[카테고리:] 생활코딩

  • bash로 vlookup 구현

    https://sancs.tistory.com/6
    https://unix.stackexchange.com/questions/88550/vlookup-function-in-unix
    BEGIN{FS=OFS=","}
    FNR==NR{
      a[$1]=$2
      next
    }
    { if ($1 in a) {print $1, a[$1]} else {print $1, "NA"}  }
  • xbox 360 드라이버 만지기1

    xbox 360 드라이버 만지기1

    내 라즈베리 파이에 usb 타입 xbox360 패드를 붙여 보고싶다. 리눅스 커널 xpad.c에 마이크로소프트 거의 모든 제품 usb vendor, product id가 등록되어 있다. xpad.c가 조금 복작하여 usb 인터럽트 사용 방법을 잘 이해할 수 없다. 키 입력 설정을 어떻게 할지는 나중 문제고… usb-skeletion.c 문서 역시 어렵다. 여기는 인터럽트 대신 대용량 데이터 전송을 목표로 했다.

    그 중 가장 비슷한 usb 마우스 드라이버를 커널에서 찾았다. 커널이 성경과 같아 모든 물음에 대한 답을 한다. 200줄 약간 넘어가는 코드로 정말 인터럽트 핵심만 작성했다.

    대략 구성은 아래와 같이 된다.

    • usb 구조체 선언.
    • 사용가능한 usb 번호 찾음, 등록.
    • 입력 설정.
    • 인터럽트 설정.

    usb_fill_int_urb로 call 함수를 등록하여 인터럽트를 사용하면 된다. usb 인터럽트가 일정 주기로 발생한다. 계속 실행하기 위해 인터럽트로 실행할 함수 안에 usb_submit_urb를 넣는다. 알고보면 참 쉬운데 처음 이해하기 어렵다. xpad.c도 같은 방식이다. 처음에 왜 안보였는지…아직 키 입력을 설정하지 않았고, 커널에 test를 출력하도록 했다.

    usb 구조체는 여러 정보를 쉽게 사용하려 군더더기를 많이 붙이는 듯 하다.

    #include <linux/kernel.h>
    #include <linux/input.h>
    #include <linux/module.h>
    #include <linux/usb/input.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/usb/input.h>
    #include <linux/slab.h>
    #define USB_XBOX_MINOR_BASE	1
    		
    //구조체 선언.
    struct usb_xpad{
    	struct input_dev *dev;		/* input device interface */
    	struct usb_device *udev;	/* usb device */
    	struct usb_interface *intf;	/* usb interface */
    	__u8    irq_in_endpointAddr;    /* interrupt in address*/
    	__u8    irq_out_endpointAddr;   /* interrupt out address*/
    	struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
    	signed char *data;		/* input data */
    	struct urb *irq_in;		/* urb for interrupt in report*/
    	struct work_struct work;	/* init/remove device from callback */
    };
    static struct usb_xpad *myPad;
    static int xpad_init_input(struct usb_xpad *xpad);
    static void xpad_deinit_input(struct usb_xpad *xpad);
    static void usb_xpad_irq(struct urb *urb){
    	pr_info("test\n");
    	usb_submit_urb(urb, GFP_KERNEL);
    }
    static int xpad_init_urb(struct usb_interface *intf, struct usb_xpad *xpad);
    static ssize_t xbox_read(struct file *file, char *buffer, size_t count,
    			 loff_t *ppos)
    {
    	pr_info("device was read.\n");
    	return 0;
    }
    static ssize_t xbox_write(struct file *file, const char *user_buffer,
    			  size_t count, loff_t *ppos)
    {
    	return 0;
    }
    static int xbox_open(struct inode *inode, struct file *file)
    {
    	int retval;
    	struct urb *ptr_urb;
    	ptr_urb = usb_alloc_urb(0, GFP_KERNEL);
    	pr_info("device was opened.\n");
    	if (!ptr_urb) {
    		retval = -ENOMEM;
    		goto error;
    	}
    	return 0;
    error:
    	if(ptr_urb)
    	{
    		usb_free_urb(ptr_urb);
    	}
    	return retval;
    }
    static int xbox_release(struct inode *inode, struct file *file)
    {
    	pr_info("xbox was released\n");
    	return 0;
    }
    static int xbox_flush(struct file *file, fl_owner_t id)
    {
    	return 0;
    }
    static const struct file_operations xboxtest_fops = {
    	.owner =	THIS_MODULE,
    	.read =		xbox_read,
    	.write =	xbox_write,
    	.open =		xbox_open,
    	.release =	xbox_release,
    	.flush =	xbox_flush,
    	.llseek =	noop_llseek,
    };
    /*
     * usb class driver info in order to get a minor number from the usb core,
     * and to have the device registered with the driver core
     */
    static struct usb_class_driver xbox_class = {
    	.name =		"xbox%d",
    	.fops =		&xboxtest_fops,
    	.minor_base =	USB_XBOX_MINOR_BASE,
    };
    static struct usb_driver xpad_test_driver;
    static const struct usb_device_id xpad_table[] = {
    	//{ USB_INTERFACE_INFO('X', 'B', 0) },	/* X-Box USB-IF not approved class */
    	//XPAD_XBOX360_VENDOR(0x045e),		/* Microsoft X-Box 360 controllers */
    	{USB_DEVICE(0x045e, 0x028e)},
    	{ }
    };
    static const signed short xpad_common_btn[] = {
    	BTN_A, BTN_B, BTN_X, BTN_Y,			/* "analog" buttons */
    	BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR,	/* start/back/sticks */
    	-1						/* terminating entry */
    };
    MODULE_DEVICE_TABLE(usb, xpad_table);
    static int xpad_init_input(struct usb_xpad *xpad)
    {
    	struct input_dev *input_dev;
    	int i, error;
    	int pipe, maxp;
    	input_dev = input_allocate_device();
    	if (!input_dev)
    		return -ENOMEM;
    	xpad->dev = input_dev;
    	usb_to_input_id(xpad->udev, &input_dev->id);
    	input_set_drvdata(input_dev, xpad);
    	/* set up standard buttons */
    	for (i = 0; xpad_common_btn[i] >= 0; i++)
    		input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]);
    	pipe = usb_rcvintpipe(xpad->udev, xpad->irq_in_endpointAddr);
    	maxp = usb_maxpacket(xpad->udev, pipe, usb_pipeout(pipe));
    	usb_fill_int_urb(xpad->irq_in, xpad->udev,
    			pipe, xpad->data,
    			(maxp > 8 ? 8 : maxp),
    			usb_xpad_irq, xpad, 
    			xpad->endpoint_in->bInterval);
    	error = input_register_device(input_dev);
    	if (error)
    		goto err_free_dev;
    	pr_info("usb input was registered\n");
    	usb_submit_urb(xpad->irq_in, GFP_KERNEL);
    	return 0;	//return ok;
    err_free_dev:
    	input_free_device(input_dev);
    	return error;
    }
    static void xpad_deinit_input(struct usb_xpad *xpad)
    {
    	pr_info("xpad is %p, ->dev is %p.\n",xpad, xpad->dev);
    	if(xpad->dev)
    		input_unregister_device(xpad->dev);
    }
    static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
    	struct usb_xpad *xpad;
    	struct usb_device *udev;
    	struct usb_endpoint_descriptor *ep_irq_in,*ep_irq_out;
    	struct usb_host_interface *intf_tmp;
    	int retval;
    	udev = interface_to_usbdev(intf);
    	xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
    	if (!xpad)
    		return -ENOMEM;
    	//초기화
    	//kzallocd으로 0으로 초기화
    	//xpad->odata_serial = 0;
    	xpad->udev = udev;
    	xpad->intf = intf;
    	myPad = xpad;
    	pr_info("xpad is %p, myPad is %p\n", xpad->intf, myPad->intf);
    	pr_info("interface is %p\n", intf);
    	//if (intf->cur_altsetting->desc.bNumEndpoints != 2)
    	//	return -ENODEV;
    	intf_tmp = intf->cur_altsetting;
    	// find common usb endpoint helper 사용
    	//https://lkml.org/lkml/2020/9/21/1239
    	
    	/* set up the endpoint information */
    	/* use only the first bulk-in and bulk-out endpoints */
    	retval = usb_find_common_endpoints(intf_tmp,
    			NULL, NULL, &ep_irq_in, &ep_irq_out);
    	if (retval) {
    		dev_err(&intf->dev,
    			"Could not find both irq-in and irq-out endpoints\n");
    		goto error;
    	}
    	xpad->irq_in_endpointAddr = ep_irq_in->bEndpointAddress;
    	xpad->irq_out_endpointAddr = ep_irq_out->bEndpointAddress;
    	xpad->endpoint_in = ep_irq_in;
    	xpad->endpoint_out = ep_irq_out;
    	xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
    	if (!xpad->irq_in) {
    		retval = -ENOMEM;
    		goto err_free_in_urb;
    	}
    	usb_set_intfdata(intf, xpad);
    	pr_info("[info]: found interrupt end point, in: %d, out: %d", ep_irq_in->bEndpointAddress, ep_irq_out->bEndpointAddress);
    	pr_info("probe \n");
    	//pr_info("%04X:%04X pluged \n", id->idVendor, id->idProduct);
    	retval = usb_register_dev(intf, &xbox_class);
    	if (retval< 0) {
    		pr_err("usb_register failed for the "__FILE__ "driver."
    				"Error number %d", retval);
    		usb_set_intfdata(intf, NULL);
    		goto error;
    	}
    	/* let the user know what node this device is now attached to */
    	dev_info(&intf->dev,
    		 "USB Skeleton device now attached to USBSkel-%d",
    		 intf->minor);
    	//input으로 등록.
    	xpad_init_input(xpad);
    	
    	return 0;
    error:
    	kfree(xpad);
    	return retval;
    err_free_in_urb:
    	usb_free_urb(xpad->irq_in);
    	return retval;
    }
    static void xpad_disconnect(struct usb_interface *intf)
    {
    	struct usb_xpad *xpad;
    	xpad = usb_get_intfdata(intf);
    	pr_info("xpad address is %p\n", xpad);
    	xpad_deinit_input(xpad);
    	usb_deregister_dev(intf, &xbox_class);
    	usb_set_intfdata(intf, NULL);
    	kfree(xpad);
    	pr_info("disconnected\n");
    }
    static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
    {
    	pr_info("suspendes\n");
    	return 0;
    }
    static int xpad_resume(struct usb_interface *intf)
    {
    	pr_info("resumed\n");
    	return 0;
    }
    static struct usb_driver xpad_test_driver = {
    	.name		= "xbox360_test",
    	.probe		= xpad_probe,
    	.disconnect	= xpad_disconnect,
    	.suspend	= xpad_suspend,
    	.resume		= xpad_resume,
    	.id_table	= xpad_table,
    };
    static int xpad_init_urb(struct usb_interface *intf, struct usb_xpad *xpad)
    {
    	int retval;
    	xpad->irq_in= usb_alloc_urb(0, GFP_KERNEL);
    	if (!xpad->irq_in) {
    		retval = -ENOMEM;
    		goto error;
    	}
    	return 0;
    error:
    	return retval;
    }
    	
    // 모듈 loading, unloading 테스트를 위해 과거 방식으로 사용
    //module_init(usb_xboxtest_init);
    //module_exit(usb_xboxtest_exit);
    //Macro
    module_usb_driver(xpad_test_driver);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("now0930");
    MODULE_DESCRIPTION("Hello, xbox pad!");
    
    [45180.903315] xpad is ea6ce566, myPad is ea6ce566
    [45180.903332] interface is ea6ce566
    [45180.903349] [info]: found interrupt end point, in: 129, out: 1
    [45180.903360] probe 
    [45180.903636] xbox360_test 1-1.2:1.0: USB Skeleton device now attached to USBSkel-1
    [45180.903815] input: Unspecified device as /devices/virtual/input/input8
    [45180.904290] usb input was registered
    [45180.904475] xpad is 50c63d58, myPad is 50c63d58
    [45180.904489] interface is 50c63d58
    [45180.904506] [info]: found interrupt end point, in: 130, out: 2
    [45180.904518] probe 
    [45180.904685] xbox360_test 1-1.2:1.1: USB Skeleton device now attached to USBSkel-2
    [45180.904841] input: Unspecified device as /devices/virtual/input/input9
    [45180.905155] usb input was registered
    [45180.905310] xpad is 3bfd76ed, myPad is 3bfd76ed
    [45180.905323] interface is 3bfd76ed
    [45180.905342] xbox360_test 1-1.2:1.2: Could not find both irq-in and irq-out endpoints
    [45180.905432] xpad is 07d8d175, myPad is 07d8d175
    [45180.905451] interface is 07d8d175
    [45180.905469] xbox360_test 1-1.2:1.3: Could not find both irq-in and irq-out endpoints
    [45180.905585] usbcore: registered new interface driver xbox360_test
    [45180.909177] test
    [45180.913170] test
    [45210.971462] test
    [45210.984592] test
    [45210.997466] test
    [45211.010595] test
    [45211.075485] test
    [45211.088606] test
    [45211.109483] test
    [45211.130612] test
    [45211.151488] test
    [45211.168617] test
    [45211.181493] test
    [45211.754713] test
    [45211.799598] test
    [45211.812711] test
    [45212.329679] test
    [45217.545918] usbcore: deregistering interface driver xbox360_test
    [45217.546248] test
    [45217.546378] xpad address is 99d70f22
    [45217.546411] xpad is 99d70f22, ->dev is 8784f33d.
    [45217.699130] disconnected
    [45217.699578] test
    [45217.699674] xpad address is 0764c1af
    [45217.699692] xpad is 0764c1af, ->dev is 379dff77.
    [45217.779137] disconnected
  • docker cgoup issue

    docker cgoup issue

    데비안 11로 업데이트 후 에러로 docker를 실행할 수 없다.

    docker: Error response from daemon: cgroups: cgroup mountpoint does not exist: unknown.
    ERRO[0000] error waiting for container: context canceled 

    인터넷에 역시 해결 방법이 있었다.

    I ran into similar issue found a temp fix on another place.
    Temp fix:
    sudo mkdir /sys/fs/cgroup/systemd
    sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

    /etc/fstab에 기록하면 된다.

    $ cat /etc/fstab | tail -10
    #docker error
    #docker: Error response from daemon: cgroups: cgroup mountpoint does not exist: unknown.
    #mkdir /sys/fs/cgroup/systemd
    #mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
    cgroup	/sys/fs/cgroup/systemd	cgroup	none,name=systemd	0	0

    https://waspro.tistory.com/556

    docker root directory 변경

    https://fliedcat.tistory.com/113

  • spinlock, mutex 실습

    모듈을 로딩하면 kernel thread부터 만들어야 한다. 같은 책 초반에 나왔는데, 다시 보니 기억할 수 없다. 스레드를 만든 후 함수와 데이터를 넘겨야 하는데, 데이터가 void 포인터다. struct로 캐스팅 하고 싶은데, 에러가 났다. kernel 코드를 보고 괄호를 몇 번 붙였다. 아! ㅅㅂ. 구글 찾아보기보다 시(간)성비가 더 좋다.

    pi@raspberrypi:~/linux $ grep -wn "kthread_create" -r ./drivers
    ./drivers/usb/usbip/usbip_common.h:285:		= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    ./drivers/usb/atm/ueagle-atm.c:2228:	sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm");
    ./drivers/usb/atm/usbatm.c:977:	t = kthread_create(usbatm_do_heavy_init, instance, "%s",
    ./drivers/usb/gadget/file_storage.c:3527:	fsg->thread_task = kthread_create(fsg_main_thread, fsg,
    ./drivers/usb/gadget/function/u_serial.c:1059:	info->console_thread = kthread_create(gs_console_thread,
    ./drivers/usb/gadget/function/f_mass_storage.c:2924:			kthread_create(fsg_main_thread, common, "file-storage");
    ./drivers/usb/host/dwc_common_port/dwc_common_fbsd.c:981:	retval = kthread_create((void (*)(void *))func, data, &thread->proc,
    ./drivers/iio/adc/ina2xx-adc.c:840:	task = kthread_create(ina2xx_capture_thread, (void *)indio_dev,
    
    pi@raspberrypi:~/linux $ vi ./drivers/usb/host/dwc_common_port/dwc_common_fbsd.c
    dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
    {
            int retval;
            dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
    
            if (!thread) {
                    return NULL;
            }
    
            thread->abort = 0; 
            retval = kthread_create((void (*)(void *))func, data, &thread->proc,
                                    RFPROC | RFNOWAIT, 0, "%s", name);
            if (retval) {
                    DWC_FREE(thread);
                    return NULL;
            }
    
            return thread;
    }

    spinlock을 만든 후 초기화 하지 않으면, 사용할 수 없다. kernel에 메모리를 할당 받으면 초기화는 꼭 해줘야 하는 느낌이다.

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/irq.h>
    #include <linux/interrupt.h>
    #include <linux/gpio.h>     //GPIO
    
    
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>
    #include <linux/fcntl.h>
    
    
    #define GPIO_10_OUT (10)
    #define DEVICE_DATA_MAX 256
    
    #include <linux/kthread.h>
    #include <linux/delay.h>
    #include <linux/spinlock.h>
    
    
    unsigned int GPIO_irqNumber;
    
    /*사용자 데이터 부분*/
    struct my_device_data{
    	struct cdev cdev;
    	int index;
    	char my_string[DEVICE_DATA_MAX];
    	struct timer_list simple_timer;
    	spinlock_t lock;
    	struct task_struct *kthread1;
    	struct task_struct *kthread2;
    } ;
    
    int run_this_thread1(void* data);
    int run_this_thread2(void* data);
    
    int run_this_thread1(void* data)
    {
    	//while(1)로 하면 kthread_stop을 할 수 없음.
    	//루프가 없으면 kthread_stop을 불렀을 경우, segment error.
    	//thread가 없다면, 종료를 할 수 없어 보임..
    	while(!kthread_should_stop())
    	{
    	//data is my_device_data
    	struct my_device_data *ptr_main_data;
    	//ptr_main_data = data;
    	ptr_main_data = (struct my_device_data(*))data;
    
    	//공통이 구조체 접근.
    	spin_lock(&ptr_main_data->lock);
    	ptr_main_data->index++;
    	//pr_info("spin lock is %0x\n",ptr_main_data->lock);
    	pr_info("==============\n");
    	pr_info("[+]index is %d\n", ptr_main_data->index);
    	pr_info("==============\n");
    	spin_unlock(&ptr_main_data->lock);
    	//pr_info("spin lock is %0x\n",ptr_main_data->lock);
    
    	msleep(500);
    	}
    	return 0;
    
    }
    
    int run_this_thread2(void* data)
    {
    	while(!kthread_should_stop())
    	{
    	//data is my_device_data
    	struct my_device_data *ptr_main_data;
    	ptr_main_data = (struct my_device_data(*))data;
    	//
    	//공통이 구조체 접근.
    	spin_lock(&ptr_main_data->lock);
    	ptr_main_data->index++;
    	pr_info("==============\n");
    	pr_info("[+]index is %d\n", ptr_main_data->index);
    	pr_info("==============\n");
    	spin_unlock(&ptr_main_data->lock);
    	msleep(500);
    	}
    
    	return 0;
    
    }
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling work queue*/
    	return IRQ_HANDLED;
    
    }
    
    
    //device driver 작성 부분.
    /*************드라이버 함수 ******************/
    static int mydriver_open(struct inode *inode, struct file *file);
    static int mydriver_release(struct inode *inode, struct file *file);
    static ssize_t mydriver_read(struct file *flip,
    		char *buf, size_t len, loff_t *off);
    static ssize_t mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off);
    /********************************************/
    
    
    
    //file operation structure
    
    static struct file_operations fops = 
    {
    	.owner = THIS_MODULE,
    	.read = mydriver_read,
    	.write = mydriver_write,
    	.open = mydriver_open,
    	.release = mydriver_release,
    };
    
    static int mydriver_open(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was opend.\n");
    	return 0;
    
    }
    
    static int mydriver_release(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was closed.\n");
    	return 0;
    }
    
    
    static int mydriver_read(struct file *file,
    		char *buf, size_t len, loff_t *off)
    {
    	pr_info("read\n");
    	return 0;
    }
    
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	return 0;
    
    }
    
    dev_t dev = 0;
    static struct cdev my_cdev;
    static struct class *dev_class;
    struct my_device_data *main_data;
    static int __init init_hw(void)
    {
    	//디바이스 등록
    	if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0))
    	{
    		pr_err("[!]character device was not allocated\n");
    		goto r_unreg;
    
    	}
    	pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev));
    
    
    	//초기화
    	cdev_init(&my_cdev, &fops);
    	pr_info("[=]driver was initialized\n");
    
    
    	//시스템에 추가
    	if((cdev_add(&my_cdev, dev, 1)) < 0)
    	{
    		pr_err("[!]cannot add device to kernel\n");
    		goto r_del;
    
    	}
    
    
    	//class 만듦.
    	if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL)
    	{
    		pr_err("[!]cannot add class\n");
    		goto r_class;
    	}
    
    
    	if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL)
    	{
    
    		pr_err("[!]cannot create device\n");
    		goto r_device;
    	}
    
    
    	//gpio 10번을 사용.
    	//export하여 간단히 사용.
    	//입력은 값을 써 넣을 수 없음. 출력으로 설정.
    	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
    	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);
    
    	//interrupt 등록 필요
    	if (request_irq(GPIO_irqNumber,
    				(void*)gpio_irq_handler,
    				IRQF_TRIGGER_RISING,
    				"my_device",
    				NULL))
    	{
    		pr_err("[!]my_device: cannot register IRQ\n");
    		goto r_gpio;
    	}
    	pr_info("[=]module was installed\n");
    
    
    	//메모리 공간 할당.
    	main_data = kmalloc(sizeof(struct my_device_data), GFP_KERNEL);
    
    
    	//spin lock init
    	spin_lock_init(&main_data->lock);
    	main_data->index=0;
    	if(!main_data)
    	{
    		pr_err("[!]cannot alloc memory\n");
    		goto r_memory;
    	}
    	pr_info("[=]got memory\n");
    
    	//thread create.
    	main_data->kthread1 = kthread_create(run_this_thread1, main_data, "my_thread1");
    	main_data->kthread2 = kthread_create(run_this_thread2, main_data, "my_thread2");
    
    	if(main_data->kthread1)
    	{
    		wake_up_process(main_data->kthread1);
    		pr_info("wake up thread1 at %p\n", main_data->kthread1);
    	}
    	if(main_data->kthread2)
    	{
    		wake_up_process(main_data->kthread2);
    		pr_info("wake up thread2 at %p\n", main_data->kthread2);
    	}
    
    	return 0;
    r_gpio:
    	gpio_free(GPIO_10_OUT);
    r_device:
    	device_destroy(dev_class,dev);
    
    r_class:
    	class_destroy(dev_class);
    r_del:
    	cdev_del(&my_cdev);
    
    r_unreg:
    	unregister_chrdev_region(dev,1);
    r_memory:
    	;
    
    	return -1;
    }
    
    static void __exit exit_hw(void) {
    	free_irq(GPIO_irqNumber, NULL);
    	gpio_free(GPIO_10_OUT);
    	//flush_work(struct work_struct *work);
    	device_destroy(dev_class,dev);
    	//class_unregister(dev_class);
    	class_destroy(dev_class);
    	cdev_del(&my_cdev);
    	unregister_chrdev_region(dev,1);
    	if(main_data->kthread1)
    		kthread_stop(main_data->kthread1);
    	if(main_data->kthread2)
    		kthread_stop(main_data->kthread2);
    	pr_info("kthread was stopped\n");
    	pr_info("wake up thread1 at %p\n", main_data->kthread1);
    	pr_info("wake up thread2 at %p\n", main_data->kthread2);
    
    
    	kfree(main_data);
    	printk(KERN_INFO "module was removed\n");
    }
    
    
    module_init(init_hw);
    module_exit(exit_hw);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("noname");
    MODULE_DESCRIPTION("Hello, world!");
    

    spin lock을 적용하지 않을 경우, 겹쳐쓴다. 적용하면 순서대로 실행된다 믿는다.

    [ 1749.848372] ==============
    [ 1749.848376] [+]index is 108
    [ 1749.848380] ==============
    [ 1750.378381] ==============
    [ 1750.378392] [+]index is 109
    [ 1750.378399] ==============
    [ 1750.378414] ==============
    [ 1750.378421] [+]index is 110
    [ 1750.378428] ==============
    [ 1750.898416] ==============
    [ 1750.898431] [+]index is 111
    [ 1750.898439] ==============
    [ 1750.898461] ==============
    [ 1750.898470] [+]index is 112
    [ 1750.898478] ==============
    [ 1751.418404] ==============
    [ 1751.418417] [+]index is 113
    [ 1751.418426] ==============
    [ 1751.418446] ==============
    [ 1751.418455] [+]index is 114
    [ 1751.418463] ==============
    [ 1751.938485] ==============
    [ 1751.938501] [+]index is 115
    [ 1751.938512] ==============
    [ 1751.938537] ==============
    [ 1751.938547] [+]index is 116
    [ 1751.938556] ==============
    [ 1752.458509] ==============
    [ 1752.458529] [+]index is 117
    [ 1752.458538] ==============
    [ 1752.458560] ==============
    [ 1752.458570] [+]index is 118
    [ 1752.458578] ==============
    [ 1752.978388] ==============
    [ 1752.978394] [+]index is 119
    [ 1752.978398] ==============
    [ 1753.498497] kthread was stopped
    [ 1753.498515] wake up thread1 at 39208d2a
    [ 1753.498524] wake up thread2 at 5bdf5278
    [ 1753.498536] module was removed
    [ 1773.404079] [=]236-0, was allocated
    [ 1773.404089] [=]driver was initialized
    [ 1773.406890] [=]irq 54 was assinged
    [ 1773.406942] [=]module was installed
    [ 1773.406952] [=]got memory
    [ 1773.410837] wake up thread1 at 6acc0d97
    [ 1773.410859] wake up thread2 at 9613521a
    [ 1773.411132] ==============
    [ 1773.411143] [+]index is 1
    [ 1773.411151] ==============
    [ 1773.411794] ==============
    [ 1773.411805] [+]index is 2
    [ 1773.411812] ==============
    [ 1773.528622] ==============
    [ 1773.528626] ==============
    [ 1773.528636] [+]index is 4
    [ 1773.528637] [+]index is 4
    [ 1773.528640] ==============
    [ 1773.528641] ==============
    [ 1773.648584] ==============
    [ 1773.648585] ==============
    [ 1773.648589] [+]index is 6
    [ 1773.648590] [+]index is 6
    [ 1773.648593] ==============
    [ 1773.648594] ==============
    [ 1773.768583] ==============
    [ 1773.768584] ==============
    [ 1773.768588] [+]index is 8
    [ 1773.768589] [+]index is 8
    [ 1773.768591] ==============
    [ 1773.768594] ==============
    [ 1773.888583] ==============
    [ 1773.888587] [+]index is 9
    [ 1773.888589] ==============
    [ 1773.888591] ==============
    [ 1773.888595] [+]index is 10
    [ 1773.888598] ==============
    [ 1774.008583] ==============
    [ 1774.008585] ==============
    [ 1774.008588] [+]index is 12
    [ 1774.008591] [+]index is 12
    [ 1774.008594] ==============
    [ 1774.008596] ==============
    [ 1774.128592] ==============
    [ 1774.128593] ==============
    [ 1774.128597] [+]index is 14
    [ 1774.128599] [+]index is 14
    [ 1774.128601] ==============
    [ 1774.128604] ==============
    [ 1774.248635] ==============
    [ 1774.248638] ==============
    [ 1774.248648] [+]index is 16
    [ 1774.248652] [+]index is 16
    [ 1774.248657] ==============
    [ 1774.248663] ==============
    [ 1774.368631] ==============
    [ 1774.368642] [+]index is 17
    [ 1774.368649] ==============
    [ 1774.378613] ==============
    [ 1774.378622] [+]index is 18
    https://embetronicx.com/tutorials/linux/device-drivers/linux-device-drivers-tutorial-kernel-thread/

    https://embetronicx.com/tutorials/linux/device-drivers/spinlock-in-linux-kernel-1/

    https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-mutex-in-linux-kernel/

  • timer 실습

    kernel timer를 실습했다. callback 함수 argrument로 timer를 가지고 있는 구조체 데이터를 전달할 수 있다. work queue와 같은 방식이다. 커널 특정 버전부터 이런 식으로 변경되었는 듯 하다. 타이머가 만료되면 다시 등록하도록 했다.

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/irq.h>
    #include <linux/interrupt.h>
    #include <linux/gpio.h>     //GPIO
    
    
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>
    #include <linux/fcntl.h>
    
    #include <linux/timer.h>
    #include <linux/jiffies.h>
    
    #define GPIO_10_OUT (10)
    #define DEVICE_DATA_MAX 256
    
    unsigned int GPIO_irqNumber;
    
    /*\uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \ubd80\ubd84*/
    static struct my_device_data{
    	struct cdev cdev;
    	int index;
    	char my_string[DEVICE_DATA_MAX];
    	struct timer_list simple_timer;
    } my_data1;
    
    
    void timer_action_fn(struct timer_list *t);
    
    void timer_action_fn(struct timer_list *t)
    {
    	struct my_device_data *ptr1;
    	pr_info("%ld: timer function was activated\n", jiffies);
    	ptr1=from_timer(ptr1, t, simple_timer);
    	ptr1->index++;
    	pr_info("index is %d\n", ptr1->index);
    	mod_timer(&my_data1.simple_timer, jiffies+msecs_to_jiffies(1000));
    
    
    }
    
    
    static int init_timer(void){
    	timer_setup(&my_data1.simple_timer, timer_action_fn, 0);
    	pr_info("%ld: timer was setup\n", jiffies);
    	mod_timer(&my_data1.simple_timer, jiffies+msecs_to_jiffies(1000));
    	return 0;
    
    }
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling work queue*/
    	return IRQ_HANDLED;
    
    }
    
    
    //device driver \uc791\uc131 \ubd80\ubd84.
    /*************\ub4dc\ub77c\uc774\ubc84 \ud568\uc218 ******************/
    static int mydriver_open(struct inode *inode, struct file *file);
    static int mydriver_release(struct inode *inode, struct file *file);
    static ssize_t mydriver_read(struct file *flip,
    		char *buf, size_t len, loff_t *off);
    static ssize_t mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off);
    /********************************************/
    
    
    
    //file operation structure
    
    static struct file_operations fops = 
    {
    	.owner = THIS_MODULE,
    	.read = mydriver_read,
    	.write = mydriver_write,
    	.open = mydriver_open,
    	.release = mydriver_release,
    };
    
    static int mydriver_open(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was opend.\n");
    	return 0;
    
    }
    
    static int mydriver_release(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was closed.\n");
    	return 0;
    }
    
    
    static int mydriver_read(struct file *file,
    		char *buf, size_t len, loff_t *off)
    {
    	pr_info("read\n");
    	return 0;
    }
    
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	return 0;
    
    }
    
    dev_t dev = 0;
    static struct cdev my_cdev;
    static struct class *dev_class;
    static int __init init_hw(void)
    {
    	//\ub514\ubc14\uc774\uc2a4 \ub4f1\ub85d
    	if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0))
    	{
    		pr_err("[!]character device was not allocated\n");
    		goto r_unreg;
    
    	}
    	pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev));
    
    
    	//\ucd08\uae30\ud654
    	cdev_init(&my_cdev, &fops);
    	pr_info("[=]driver was initialized\n");
    
    
    	//\uc2dc\uc2a4\ud15c\uc5d0 \ucd94\uac00
    	if((cdev_add(&my_cdev, dev, 1)) < 0)
    	{
    		pr_err("[!]cannot add device to kernel\n");
    		goto r_del;
    
    	}
    
    
    	//class \ub9cc\ub4e6.
    	if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL)
    	{
    		pr_err("[!]cannot add class\n");
    		goto r_class;
    	}
    
    
    	if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL)
    	{
    
    		pr_err("[!]cannot create device\n");
    		goto r_device;
    	}
    
    
    	//gpio 10\ubc88\uc744 \uc0ac\uc6a9.
    	//export\ud558\uc5ec \uac04\ub2e8\ud788 \uc0ac\uc6a9.
    	//\uc785\ub825\uc740 \uac12\uc744 \uc368 \ub123\uc744 \uc218 \uc5c6\uc74c. \ucd9c\ub825\uc73c\ub85c \uc124\uc815.
    	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
    	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);
    
    	//interrupt \ub4f1\ub85d \ud544\uc694
    	if (request_irq(GPIO_irqNumber,
    				(void*)gpio_irq_handler,
    				IRQF_TRIGGER_RISING,
    				"my_device",
    				NULL))
    	{
    		pr_err("[!]my_device: cannot register IRQ\n");
    		goto r_gpio;
    	}
    	pr_info("[=]module was installed\n");
    
    	//timer setup
    	init_timer();
    	return 0;
    r_gpio:
    	gpio_free(GPIO_10_OUT);
    r_device:
    	device_destroy(dev_class,dev);
    
    r_class:
    	class_destroy(dev_class);
    r_del:
    	cdev_del(&my_cdev);
    
    r_unreg:
    	unregister_chrdev_region(dev,1);
    
    	return -1;
    }
    
    static void __exit exit_hw(void) {
    	free_irq(GPIO_irqNumber, NULL);
    	gpio_free(GPIO_10_OUT);
    	//flush_work(struct work_struct *work);
    	device_destroy(dev_class,dev);
    	//class_unregister(dev_class);
    	class_destroy(dev_class);
    	cdev_del(&my_cdev);
    	unregister_chrdev_region(dev,1);
    	//timer delete
    	del_timer(&my_data1.simple_timer);
    	printk(KERN_INFO "module was removed\n");
    }
    
    
    module_init(init_hw);
    module_exit(exit_hw);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("noname");
    MODULE_DESCRIPTION("Hello, world!");
    
    [146685.757495] [=]236-0, was allocated
    [146685.757504] [=]driver was initialized
    [146685.757960] [=]irq 54 was assinged
    [146685.757984] [=]module was installed
    [146685.757991] 14638325: timer was setup
    [146686.818385] 14638432: timer function was activated
    [146686.818398] index is 1
    [146687.858398] 14638536: timer function was activated
    [146687.858402] index is 2
    [146688.898415] 14638640: timer function was activated
    [146688.898428] index is 3
    [146689.938432] 14638744: timer function was activated
    [146689.938445] index is 4
    [146690.978452] 14638848: timer function was activated
    [146690.978457] index is 5
    [146692.018469] 14638952: timer function was activated
    [146692.018483] index is 6
    [146693.058494] 14639056: timer function was activated
    [146693.058500] index is 7
    [146694.098512] 14639160: timer function was activated
    [146694.098548] index is 8
    [146695.138529] 14639264: timer function was activated
    [146695.138563] index is 9
    [146696.178545] 14639368: timer function was activated
    [146696.178573] index is 10
    [146697.218575] 14639472: timer function was activated
    [146697.218607] index is 11
    [146698.258571] 14639576: timer function was activated
    [146698.258587] index is 12

    https://embetronicx.com/tutorials/linux/device-drivers/using-kernel-timer-in-linux-device-driver/

    https://stackoverflow.com/questions/14953871/how-to-pass-custom-argument-to-linux-timers-function