[카테고리:] 생활코딩

  • workqueue 실습

    workqueue 실습

    리눅스가 work queue를 다양하게 사용한다. 나는 초짜라 DECLARE_WORK와 INIT_WORK를 구분할 수 없었다. DECLARE_WORK가 work struct를 전역 변수로 선언한다. 여러 work로 같은 데이터에 접근할 수 있다. workqueue funtion() 파라미터로 work struct를 넣는데, 여기로 work를 전달하면 된다. 코드가 넝마조각이 되고 있다.

    #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/workqueue.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 work_struct mywork;
    	struct work_struct work_read;
    } my_data_global;
    
    struct my_device_data *work_ptr;
    struct my_device_data *work_ptr_read;
    struct my_device_data *my_data;
    
    /*workque initiate*/
    
    void workqueue_fn(struct work_struct *work);
    
    //defien workqueue_fn//
    
    void workqueue_fn(struct work_struct *work_ptr)
    {
    	/*work_ptr\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uc811\uadfc*/
    	struct my_device_data *my_pointer;
    	//container of \ub85c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \uc811\uadfc.
    	my_pointer = container_of(work_ptr, struct my_device_data, mywork);
    	my_pointer->index++;
    	pr_info("Executing workqueue function\n");
    	pr_info("index is %d\n",my_pointer->index);
    
    }
    
    
    void workqueue_read(struct work_struct *work_ptr)
    {
    	/*work_ptr\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uc811\uadfc*/
    	struct my_device_data *my_pointer;
    	//container of \ub85c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \uc811\uadfc.
    	my_pointer = container_of(work_ptr, struct my_device_data, work_read);
    	my_pointer->index++;
    	pr_info("opened, index is %d\n", my_pointer->index);
    
    }
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling work queue*/
    	schedule_work(&work_ptr->mywork);
    	pr_info("workqueue scheduled \n");
    	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");
    	//my_data_global.index++;
    	
    	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)
    {
    	struct my_device_data *my_data;
    	size_t datalen, read_len;
    	my_data = (struct my_device_data*)&my_data_global;
    	datalen = strlen(my_data->my_string);
    	//\ucd5c\ub300\uac12\uc73c\ub85c \uac15\uc81c \uc124\uc815
    	if(len > datalen)
    	{
    		len = datalen;
    	}
    	read_len = len - *off;
    	if (read_len <= 0)
    		return 0;
    	pr_info("start %p, offset is %lld, read_len is %d\n", my_data->my_string, *off, read_len);
    	if(copy_to_user(buf, my_data->my_string+*off, read_len))
    		return -EFAULT;
    	//read_len = datalen-*off;
    	*off += read_len;
    	pr_info("kernel has %d, read %d characters from kernel\n", datalen, read_len);
    	//zero\ub97c \ubc18\ud658\ud560 \ub54c\uae4c\uc9c0 \ubc18\ubcf5.
    	schedule_work(&work_ptr->work_read);
    	return read_len;
    }
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	size_t datalen, write_len, start_pos;
    	my_data = &my_data_global;
    	start_pos = strlen(my_data->my_string);
    	datalen = strlen(my_data->my_string);
    	if(len > datalen)
    	{
    		len = DEVICE_DATA_MAX;
    	}
    
    	write_len = len - *off;
    
    	if(copy_from_user(my_data->my_string + start_pos + *off, buf, write_len))
    		return -EFAULT;
    	*off += write_len;
    	return write_len;
    }
    
    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;
    	}
    	work_ptr = kmalloc(sizeof(*work_ptr), GFP_KERNEL);
    	if(work_ptr == NULL)
    	{
    		pr_err("[!]cannot allocate memory\n");
    		goto r_work;
    	}
    	INIT_WORK(&work_ptr->mywork, workqueue_fn);
    
    
    
    	//work_ptr_read = kmalloc(sizeof(*work_ptr), GFP_KERNEL);
    	INIT_WORK(&work_ptr->work_read, workqueue_read);
    
    	//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");
    	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_work:
    	kfree(work_ptr);
    
    	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);
    	flush_work(&work_ptr->mywork);
    	kfree(work_ptr);
    	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!");
    

    모듈을 로딩하고 인터럽트를 만들면 schedule_work로 등록한 workqueue_fn을 실행한다.

    [   52.456926] [=]236-0, was allocated
    [   52.456935] [=]driver was initialized
    [   52.457621] [=]irq 54 was assinged
    [   52.457693] [=]module was installed
    [   68.542830] workqueue scheduled 
    [   68.542855] Executing workqueue function
    [   68.542866] index is 1
    [   69.549436] workqueue scheduled 
    [   69.549460] Executing workqueue function
    [   69.549466] index is 2
    [   70.552638] workqueue scheduled 
    [   70.552659] Executing workqueue function
    [   70.552663] index is 3

    파일을 오픈하면 같은 데이터를 다른 work로 index를 접근할 수 있다. schedule_work(&work_ptr->work_read)를 디바이스를 읽을 때마다 실행한다.

    [   86.495196] Deviced file was opend.
    [   86.495242] Deviced file was closed.
    [   91.476276] Deviced file was opend.
    [   91.476318] start 9f3ac859, offset is 0, read_len is 5
    [   91.476327] kernel has 5, read 5 characters from kernel
    [   91.476367] opened, index is 4
    [   91.476430] Deviced file was closed.
    [  105.664089] Deviced file was opend.
    [  105.664138] start 9f3ac859, offset is 0, read_len is 5
    [  105.664149] kernel has 5, read 5 characters from kernel
    [  105.664197] opened, index is 5
    [  105.664275] Deviced file was closed.

    https://www.programmersought.com/article/61671813037/

    https://stackoverflow.com/questions/7937245/how-to-use-linux-work-queue/7938990

    https://github.com/fervagar/kernel_modules/blob/master/workQueue.c

  • character device를 tasklet으로 테스트

    tasklet을 배우고 실습하다 보니, 캐릭터 디바이스를 활용한 예제를 찾았다. 하다보니 강제로 character device를 등록, 열고, 닫고, 읽고, 쓰는 방법을 배웠다. 대충 이해한 다음 아래 동작을 구성했다.

    • read: 앞쪽부터 읽음.
    • write: 데이터가 있다면 뒤로 채워 넣음.
    • 인터럽트: gpio를 시물레이션하여 앞에서 한 글자씩 지움.
    • 버퍼 용량은 char 256개 사용.

    전에 사용한 파일을 수정했고, 모듈을 만들기 위한 makefile을 그대로 사용했다.

    #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
    
    unsigned int GPIO_irqNumber;
    
    void my_tasklet_fn(unsigned long); 
    
    /*\uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \ubd80\ubd84*/
    static struct my_device_data{
    	struct cdev cdev;
    	int index;
    	char my_string[DEVICE_DATA_MAX];
    }my_data_global;
    
    void my_tasklet_fn(unsigned long data) 
    {
    	/*taskelet \ubc1c\uc0dd\ud558\uba74 \uc55e\uc5d0\uc11c\ubd80\ud130 1\uac1c \ubb38\uc790 \uc0ad\uc81c*/
    	struct my_device_data *my_data;
    	int i;
    	/*address\ub85c data\uac00 \ub118\uc5b4\uc624\uae30 \ub54c\ubb38\uc5d0 \ud3ec\uc778\ud130\ub85c casting*/
    	my_data = (struct my_device_data*)data;
    	//\uc55e\uc5d0\uc11c\ubd80\ud130 \ud55c \ubb38\uc790 \uc0ad\uc81c
    	for (i=0;i<DEVICE_DATA_MAX;i++)
    	{
    		my_data->my_string[i]=my_data->my_string[i+1];
    
    	}
    	my_data->my_string[(DEVICE_DATA_MAX-1)]=(char)0;
    	pr_info("taskelet: deleted 1st character.\n");
    
    }
    
    /* Init the Tasklet by Static Method */
    DECLARE_TASKLET(my_tasklet,my_tasklet_fn, (unsigned long)&my_data_global);
    
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling Task to Tasklet*/
            tasklet_schedule(&my_tasklet); 
    	//pr_info("interrupt occured\n");
    	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)
    {
    	struct my_device_data *my_data;
    	size_t datalen, read_len;
    	my_data = (struct my_device_data*)&my_data_global;
    	datalen = strlen(my_data->my_string);
    	//\ucd5c\ub300\uac12\uc73c\ub85c \uac15\uc81c \uc124\uc815
    	if(len > datalen)
    	{
    		len = datalen;
    	}
    	read_len = len - *off;
    	if (read_len <= 0)
    		return 0;
    	pr_info("start %p, offset is %lld, read_len is %d\n", my_data->my_string, *off, read_len);
    	if(copy_to_user(buf, my_data->my_string+*off, read_len))
    		return -EFAULT;
    	//read_len = datalen-*off;
    	*off += read_len;
    	pr_info("kernel has %d, read %d characters from kernel\n", datalen, read_len);
    	//zero\ub97c \ubc18\ud658\ud560 \ub54c\uae4c\uc9c0 \ubc18\ubcf5.
    	return read_len;
    }
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	struct my_device_data *my_data;
    	size_t datalen, write_len, start_pos;
    	my_data = &my_data_global;
    	start_pos = strlen(my_data->my_string);
    	datalen = strlen(my_data->my_string);
    	if(len > datalen)
    	{
    		len = DEVICE_DATA_MAX;
    	}
    
    	write_len = len - *off;
    
    	if(copy_from_user(my_data->my_string + start_pos + *off, buf, write_len))
    		return -EFAULT;
    	*off += write_len;
    	return write_len;
    }
    
    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");
    	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);
    	device_destroy(dev_class,dev);
    	//class_unregister(dev_class);
    	class_destroy(dev_class);
    	cdev_del(&my_cdev);
    	unregister_chrdev_region(dev,1);
    	printk(KERN_INFO "module was removed\n");
    }
    
    
    module_init(init_hw);
    module_exit(exit_hw);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Eunseong Park (esp-ark.com)");
    MODULE_DESCRIPTION("Hello, world!");
    

    make 후 모듈을 로딩한다. 캐릭터 디바이스가 236, 0으로 등록되었다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ sudo insmod my_driver.ko 
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ lsmod | grep my_driver
    my_driver              16384  0
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -4
    [42918.717112] [=]236-0, was allocated
    [42918.717123] [=]driver was initialized
    [42918.717332] [=]irq 54 was assinged
    [42918.717363] [=]module was installed
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ ls /dev/my_device -l
    crw------- 1 root root 236, 0  7\uc6d4 14 12:45 /dev/my_device

    쓰기를 위해 권한을 부여했다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ sudo chmod 666 /dev/my_device ;ls -l /dev/my_device 
    crw-rw-rw- 1 root root 236, 0  7\uc6d4 14 12:45 /dev/my_device

    로딩 후 읽어보면 아무런 데이터를 읽을 수 없다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ head -c10 /dev/my_device
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -2
    [43123.405028] Deviced file was opend.
    [43123.405091] Deviced file was closed.

    원하는 데이터를 디바이스에 기록한다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ echo "I need more space to delete characters" > /dev/my_device 
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ echo "I need more space to delete characters" > /dev/my_device

    내용을 확인한다. 최대 길이를 넘기면 잘못 읽는데 어떻게 해결할 지 모르겠다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ head -c100 /dev/my_device 
    I need more space to delete characters
    I need more space to delete characters
    head: '/dev/my_device를 읽는 도중 오류 발생: 주소가 잘못됨
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -10
    [43264.763161] Deviced file was closed.
    [43309.683908] Deviced file was opend.
    [43309.683961] start 18f123ee, offset is 0, read_len is 10
    [43309.683972] kernel has 78, read 10 characters from kernel
    [43309.684006] Deviced file was closed.
    [43316.934115] Deviced file was opend.
    [43316.934165] start 18f123ee, offset is 0, read_len is 78
    [43316.934183] kernel has 78, read 78 characters from kernel
    [43316.934260] start 18f123ee, offset is 78, read_len is -56
    [43316.934813] Deviced file was closed.

    인터럽트를 설정 한 다음 발생시킨다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ sudo ./config_gpio.sh 
    1
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ ./make_gpio.sh 
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ cat config_gpio.sh 
    #!/bin/bash
    echo "10" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio10/direction 
    gpio read 10
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ cat make_gpio.sh 
    #!/bin/bash
    #echo "10" > /sys/class/gpio/export
    #sleep(1)
    #echo "out" > /sys/class/gpio/gpio10/direction 
    #sleep(1)
    #gpio read 10
    #sleep(1)
    echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
    sleep 1
    echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
    sleep 1
    echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -10
    [43309.683972] kernel has 78, read 10 characters from kernel
    [43309.684006] Deviced file was closed.
    [43316.934115] Deviced file was opend.
    [43316.934165] start 18f123ee, offset is 0, read_len is 78
    [43316.934183] kernel has 78, read 78 characters from kernel
    [43316.934260] start 18f123ee, offset is 78, read_len is -56
    [43316.934813] Deviced file was closed.
    [43694.677107] taskelet: deleted 1st character.
    [43695.686488] taskelet: deleted 1st character.
    [43696.693794] taskelet: deleted 1st character.

    다시 읽어본다. 앞 캐릭터 3개가 지워졌다.

    pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ head -c100 /dev/my_device 
    eed more space to delete characters
    I need more space to delete characters
    head: '/dev/my_device를 읽는 도중 오류 발생: 주소가 잘못됨

    https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/

    https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html

    https://stackoverflow.com/questions/1330284/how-might-i-learn-to-write-char-device-drivers-for-linux

    https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch03s04.html

    https://pr0gr4m.tistory.com/entry/Linux-Kernel-5-Character-Device-Driver

    http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/

    https://stackoverflow.com/questions/12124628/endlessly-looping-when-reading-from-character-device

  • linux driver, softIrq 실습

    linux driver, softIrq 실습

    인터럽트 후반부를 처리하는 soft irq를 배웠다. 자 이제 사용해 보자. 불행히도 요즘은 일반화된 tasklet을 사용하고 low level인 soft irq를 사용하지 않는다고 한다. 그래도 아래 사이트에서 강제로?? 사용할 수 있는 방법을 찾았다. 실습하고 쓰지 말아야 한다. 드라이버를 로딩 후 언로딩하면 시스템이 죽어버린다.

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

    요점은 kernel에서 export_symbol로 raise_softirq, open_softiq 등 3개 함수를 내보내 사용할 수 있게한다. interrrupt.h에 사용자 정의 softirq를 설정해야 한다. 기존 등록된 이벤를 사용하면 시스템이 죽는다.

    https://stackoverflow.com/questions/39691131/undefined-functions-while-compiling-linux-kernel-module

    튜토리얼이 시스템에 장치를 설치하여 인터럽트를 하는 방법으로 했다. 딱히 장치를 추가할 이유가 없는데 왜 했는지 모르겠으나, 아래 사이트에서 간략한 규칙을 찾았다.

    https://temp123.tistory.com/16?category=877924

    https://hyeyoo.com/85

    인터럽트를 만들어야 확인할 수 있다. 리눅스가 제공하는 명령어로 gpio를 쉽게 컨트롤 할 수 있다. 당연하게도 출력으로 설정하고 값을 써야한다.

    https://infoarts.tistory.com/21

    돌고 돌아 장치를 추가하고, 인터럽트를 만들어 softirq를 실행했다. tutorial site에 잘 설명되어 있다. 나중에 디바이스 read, write를 인터럽트로 설정할 수도 있을 듯 하다.

    i@raspberrypi:~/RaspberryDebug/my_driver $ cat Makefile
    obj-m := my_driver.o
    #obj-m := sample_driver.o
    all:
    	make -C /lib/modules/`uname -r`/build M=`pwd` modules
    clean:
    	rm *.o *.ko *.mod  *.mod.c *.mod.o
    
    #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/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    
    #define GPIO_10_OUT (10)
    unsigned int GPIO_irqNumber;
    
    //softirq handler
    static void pesudo_interrupt_softirq_handler(struct softirq_action *action)
    {
    	pr_info("pesudo interreupt occured\n");
    }
    
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/* Raise the softirq */
    	raise_softirq(TEST_SOFT_IRQ);
    	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)
    {
    	return 0;
    }
    
    
    static int mydriver_read(struct file *flip,
    		char *buf, size_t len, loff_t *off)
    {
    
    	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;
    	}
    	
    	open_softirq(TEST_SOFT_IRQ, pesudo_interrupt_softirq_handler);
    	pr_info("[=]module was installed\n");
    	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) {
    	device_destroy(dev_class,dev);
    	class_destroy(dev_class);
    	cdev_del(&my_cdev);
    	unregister_chrdev_region(dev,1);
    	printk(KERN_INFO "module was removed\n");
    }
    
    
    module_init(init_hw);
    module_exit(exit_hw);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Eunseong Park (esp-ark.com)");
    MODULE_DESCRIPTION("Hello, world!");
    
    pi@raspberrypi:~/RaspberryDebug/my_driver $ echo "10" > /sys/class/gpio/export
    pi@raspberrypi:~/RaspberryDebug/my_driver $ echo "out" > /sys/class/gpio/gpio10/direction 
    pi@raspberrypi:~/RaspberryDebug/my_driver $ gpio read 10
    1
    pi@raspberrypi:~/RaspberryDebug/my_driver $ echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
    pi@raspberrypi:~/RaspberryDebug/my_driver $ echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
    pi@raspberrypi:~/RaspberryDebug/my_driver $ echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
    
    [  281.496011] my_driver: loading out-of-tree module taints kernel.
    [  281.496218] my_driver: no symbol version for raise_softirq
    [  281.496487] [=]236-0, was allocated
    [  281.496492] [=]driver was initialized
    [  281.496679] [=]irq 54 was assinged
    [  281.496713] [=]module was installed
    [  439.643072] pesudo interreupt occured
    pi@raspberrypi:/sys/class/gpio $ dmesg | tail -10;cat /proc/softirqs 
    [   21.513881] input: Keyboard K480 Consumer Control as /devices/platform/soc/fe201000.serial/tty/ttyAMA0/hci0/hci0:11/0005:046D:B33C.0003/input/input5
    [   21.514017] input: Keyboard K480 System Control as /devices/platform/soc/fe201000.serial/tty/ttyAMA0/hci0/hci0:11/0005:046D:B33C.0003/input/input6
    [   21.514160] hid-generic 0005:046D:B33C.0003: input,hidraw2: BLUETOOTH HID v28.02 Keyboard [Keyboard K480] on e4:5f:01:06:e9:01
    [  281.496011] my_driver: loading out-of-tree module taints kernel.
    [  281.496218] my_driver: no symbol version for raise_softirq
    [  281.496487] [=]236-0, was allocated
    [  281.496492] [=]driver was initialized
    [  281.496679] [=]irq 54 was assinged
    [  281.496713] [=]module was installed
    [  439.643072] pesudo interreupt occured
                        CPU0       CPU1       CPU2       CPU3       
              HI:          1          0          0          0
           TIMER:      18713      21705      20854      19911
          NET_TX:         95         49        315         19
          NET_RX:       1125        324       1051        398
           BLOCK:          0          0          0          0
        IRQ_POLL:          0          0          0          0
         TASKLET:      44263        453        587        288
           SCHED:      17064      20646      19843      18418
         HRTIMER:          0          0          0          0
             RCU:      19242      23191      20816      20212
          (null):          1          0          0          0
    pi@raspberrypi:/sys/class/gpio $ dmesg | tail -10;cat /proc/softirqs 
    [   21.514017] input: Keyboard K480 System Control as /devices/platform/soc/fe201000.serial/tty/ttyAMA0/hci0/hci0:11/0005:046D:B33C.0003/input/input6
    [   21.514160] hid-generic 0005:046D:B33C.0003: input,hidraw2: BLUETOOTH HID v28.02 Keyboard [Keyboard K480] on e4:5f:01:06:e9:01
    [  281.496011] my_driver: loading out-of-tree module taints kernel.
    [  281.496218] my_driver: no symbol version for raise_softirq
    [  281.496487] [=]236-0, was allocated
    [  281.496492] [=]driver was initialized
    [  281.496679] [=]irq 54 was assinged
    [  281.496713] [=]module was installed
    [  439.643072] pesudo interreupt occured
    [  471.316929] pesudo interreupt occured
                        CPU0       CPU1       CPU2       CPU3       
              HI:          1          0          0          0
           TIMER:      18828      21849      21047      20104
          NET_TX:         95         49        321         19
          NET_RX:       1125        324       1063        398
           BLOCK:          0          0          0          0
        IRQ_POLL:          0          0          0          0
         TASKLET:      44459        453        609        288
           SCHED:      17173      20815      20069      18623
         HRTIMER:          0          0          0          0
             RCU:      19350      23343      20917      20386
          (null):          2          0          0          0
    pi@raspberrypi:/sys/class/gpio $ dmesg | tail -10;cat /proc/softirqs 
    [   21.514160] hid-generic 0005:046D:B33C.0003: input,hidraw2: BLUETOOTH HID v28.02 Keyboard [Keyboard K480] on e4:5f:01:06:e9:01
    [  281.496011] my_driver: loading out-of-tree module taints kernel.
    [  281.496218] my_driver: no symbol version for raise_softirq
    [  281.496487] [=]236-0, was allocated
    [  281.496492] [=]driver was initialized
    [  281.496679] [=]irq 54 was assinged
    [  281.496713] [=]module was installed
    [  439.643072] pesudo interreupt occured
    [  471.316929] pesudo interreupt occured
    [  477.954727] pesudo interreupt occured
                        CPU0       CPU1       CPU2       CPU3       
              HI:          1          0          0          0
           TIMER:      18963      22041      21297      20379
          NET_TX:         95         50        325         19
          NET_RX:       1128        326       1068        409
           BLOCK:          0          0          0          0
        IRQ_POLL:          0          0          0          0
         TASKLET:      44749        453        647        288
           SCHED:      17324      21034      20344      18910
         HRTIMER:          0          0          0          0
             RCU:      19496      23548      21158      20547
          (null):          3          0          0          0
    pi@raspberrypi:/sys/class/gpio $ sudo rmmod my_driver 
    pi@raspberrypi:/sys/class/gpio $ dmesg | tail -10;cat /proc/softirqs 
    [  281.496011] my_driver: loading out-of-tree module taints kernel.
    [  281.496218] my_driver: no symbol version for raise_softirq
    [  281.496487] [=]236-0, was allocated
    [  281.496492] [=]driver was initialized
    [  281.496679] [=]irq 54 was assinged
    [  281.496713] [=]module was installed
    [  439.643072] pesudo interreupt occured
    [  471.316929] pesudo interreupt occured
    [  477.954727] pesudo interreupt occured
    [  497.174610] module was removed
                        CPU0       CPU1       CPU2       CPU3       
              HI:          1          0          0          0
           TIMER:      19416      22394      21661      20809
          NET_TX:         95         50        335         19
          NET_RX:       1141        331       1087        409
           BLOCK:          0          0          0          0
        IRQ_POLL:          0          0          0          0
         TASKLET:      45416        479        678        288
           SCHED:      17800      21462      20753      19347
         HRTIMER:          0          0          0          0
             RCU:      19908      23925      21541      20832
          (null):          3          0          0          0
    pi@raspberrypi:/sys/class/gpio $ cat /proc/interrupts 
               CPU0       CPU1       CPU2       CPU3       
     17:          0          0          0          0     GICv2  29 Level     arch_timer
     18:      13237       9026       7126      13623     GICv2  30 Level     arch_timer
     23:        352          0          0          0     GICv2 114 Level     DMA IRQ
     31:       3111          0          0          0     GICv2  65 Level     fe00b880.mailbox
     34:       8655          0          0          0     GICv2 153 Level     uart-pl011
     35:          0          0          0          0     GICv2 169 Level     brcmstb_thermal
     36:      33671          0          0          0     GICv2 158 Level     mmc1, mmc0
     37:      17923          0          0          0     GICv2 144 Level     vc4 firmware kms
     38:          0          0          0          0     GICv2  48 Level     arm-pmu
     39:          0          0          0          0     GICv2  49 Level     arm-pmu
     40:          0          0          0          0     GICv2  50 Level     arm-pmu
     41:          0          0          0          0     GICv2  51 Level     arm-pmu
     42:       8559          0          0          0     GICv2 106 Level     v3d
     44:       1557          0          0          0     GICv2 189 Level     eth0
     45:         52          0          0          0     GICv2 190 Level     eth0
     51:        707          0          0          0     GICv2  66 Level     VCHIQ doorbell
     52:          0          0          0          0     GICv2 175 Level     PCIe PME, aerdrv
     53:       1053          0          0          0  Brcm_MSI 524288 Edge      xhci_hcd
     54:          2          0          0          0  pinctrl-bcm2835  10 Edge      my_device
    IPI0:          0          0          0          0  CPU wakeup interrupts
    IPI1:          0          0          0          0  Timer broadcast interrupts
    IPI2:      12130      21051      27290      15651  Rescheduling interrupts
    IPI3:        785       1597       1554       1937  Function call interrupts
    IPI4:          0          0          0          0  CPU stop interrupts
    IPI5:        793        414        433        246  IRQ work interrupts
    IPI6:          0          0          0          0  completion interrupts
    Err:          0
    

    자 이제 커널을 다시 돌리고 tasklet으로 가자. 드라이버를 언로딩하고 cat /proc/interrupt를 하면 시스템이 어쩃든 죽는다. softirq 문제가 아니라, 인터럽트 핸들러를 해제하지 않아서 죽었다.

    pi@raspberrypi:~/RaspberryDebug/my_driver $ diff my_driver.c my_driver.c.bak
    164d163
    < 	free_irq(GPIO_irqNumber, NULL);
    
  • 6장 IRQ thread ftrace log

    코드를 분석한 내용을 자기의 것으로 만들기 위한 과정으로 실습이 중요합니다.

    p432. 디버깅을 통해 배우는 리눅스 커널의 구조와 원리.

    프로그램을 디버깅을 통해 배워야 된다 생각했는데, 저자역시 같은 생각이다. 따라서 왜 코드만 보고 이해할 수 없었는지 명확하다. 사람이 코드만 보고 어떻게 동작하는지 이해할 수 없다.

              <idle>-0     [000] d.h. 43045.401470: irq_handler_exit: irq=37 ret=handled
              <idle>-0     [000] d.h. 43045.417578: irq_handler_entry: irq=36 name=mmc1
              <idle>-0     [000] d.h. 43045.417581: __handle_irq_event_percpu+0x88/0x294: [+] irq:[36] handler: bcm2835_mmc_irq+0x0/0x6e8 caller: (handle_irq_event_percpu+0x3c/0x90) 
              <idle>-0     [000] d.h. 43045.417583: bcm2835_mmc_irq+0x14/0x6e8 <-__handle_irq_event_percpu+0xb8/0x294
              <idle>-0     [000] d.h. 43045.417629: <stack trace>
     => bcm2835_mmc_irq+0x18/0x6e8
     => __handle_irq_event_percpu+0xb8/0x294
     => handle_irq_event_percpu+0x3c/0x90
     => handle_irq_event+0x54/0x78
     => handle_fasteoi_irq+0xd4/0x18c
     => generic_handle_irq+0x34/0x44
     => __handle_domain_irq+0x6c/0xc4
     => gic_handle_irq+0x4c/0x88
     => __irq_svc+0x5c/0x7c
     => arch_cpu_idle+0x34/0x4c
     => arch_cpu_idle+0x34/0x4c
     => default_idle_call+0x40/0x48
     => do_idle+0x124/0x164
     => cpu_startup_entry+0x28/0x2c
     => rest_init+0xb8/0xbc
     => start_kernel+0x4b0/0x4e4
              <idle>-0     [000] d.h. 43045.417638: irq_handler_exit: irq=36 ret=handled
              <idle>-0     [000] dnh. 43045.417648: sched_wakeup: comm=irq/36-mmc1 pid=82 prio=49 target_cpu=000
              <idle>-0     [000] dnh. 43045.417651: irq_handler_entry: irq=36 name=mmc0
              <idle>-0     [000] dnh. 43045.417653: __handle_irq_event_percpu+0x88/0x294: [+] irq:[36] handler: sdhci_irq+0x0/0xdc0 caller: (handle_irq_event_percpu+0x3c/0x90) 
              <idle>-0     [000] dnh. 43045.417656: irq_handler_exit: irq=36 ret=unhandled
              <idle>-0     [000] dnh. 43045.417668: sched_wakeup: comm=kworker/0:4 pid=5757 prio=120 target_cpu=000
              <idle>-0     [000] d... 43045.417684: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=irq/36-mmc1 next_pid=82 next_prio=49
         irq/36-mmc1-82    [000] .... 43045.417690: bcm2835_mmc_thread_irq+0x10/0x98 <-irq_thread_fn+0x2c/0x88
         irq/36-mmc1-82    [000] .... 43045.417704: <stack trace>
     => bcm2835_mmc_thread_irq+0x14/0x98
     => irq_thread_fn+0x2c/0x88
     => irq_thread+0x164/0x240
     => kthread+0x16c/0x174
     => ret_from_fork+0x14/0x28
     => 0
         irq/36-mmc1-82    [000] d.h. 43045.417731: irq_handler_entry: irq=36 name=mmc1

    인터럽트가 bcm2835_mmc_irq를 먼저 실행하고, thread로 bcm2835_mmc_thread_irq를 깨워 동작함을 알 수 있다.

  • 5장 인터럽트 실습2

    msi가 xhci 드라이버를 잘못 만들었는지 인터럽트를 너무 많이 발생시킨다.

    왼쪽부터 36: 인터럽트 번호, 40080: cpu0 발생 횟수, 0: cpu 1, 2, 3 발생횟수, mmc1, mmc0 인터럽트 이름 정도? 모든 인터럽트가 충분하게 많이 일어난다.

    pi@raspberrypi:~ $ cat /proc/interrupts 
               CPU0       CPU1       CPU2       CPU3       
     ...
     36:      40080          0          0          0     GICv2 158 Level     mmc1, mmc0
     37:      11075          0          0          0     GICv2 144 Level     vc4 firmware kms
    ...
     51:        707          0          0          0     GICv2  66 Level     VCHIQ doorbell
     52:          0          0          0          0     GICv2 175 Level     PCIe PME, aerdrv
     53:       2927          0          0          0  Brcm_MSI 524288 Edge      xhci_hcd

    인터럽트 53이 524,288번 발생했다. 53번 인터럽트가 있을 때 printk로 메세지를 출력하려 했으나, 횟수가 많아서인지 부팅을 할 수 없다. 횟수가 적은 31번 인터럽트를 보기로 했고, sd카드 읽고 쓰는 이벤트로 짐작된다. dump_stack을 보면 __sdhci_add_host가 request_threaded_irq로 등록한다.

    31번으로 바꾸면 잘 출력이 되는데, 아마 msi가 드라이버를 이상하게 만들어서 그런가 싶다. 인터럽트를 한번 등록하고 말아야 하는데, xhci_run으로 계속 등록하는 느낌이 든다.

    [    0.824266] [+][irq_debug] irq_num: 36, func: request_threaded_irq, line: 1888, caller: __sdhci_add_host+0xcc/0x354
    [    0.827401] CPU: 0 PID: 32 Comm: kworker/0:1 Not tainted 4.19.127-v7l+ #91
    [    0.830475] Hardware name: BCM2835
    [    0.833547] Workqueue: events deferred_probe_work_func
    [    0.836652] [<c0212dd8>] (unwind_backtrace) from [<c020d3ec>] (show_stack+0x20/0x24)
    [    0.839740] [<c020d3ec>] (show_stack) from [<c09c01dc>] (dump_stack+0xd8/0x11c)
    [    0.842903] [<c09c01dc>] (dump_stack) from [<c0285ac8>] (request_threaded_irq+0x19c/0x1a0)
    [    0.846078] [<c0285ac8>] (request_threaded_irq) from [<c0843ba0>] (__sdhci_add_host+0xcc/0x354)
    [    0.849277] [<c0843ba0>] (__sdhci_add_host) from [<c0844a60>] (sdhci_add_host+0x34/0x48)
    [    0.852508] [<c0844a60>] (sdhci_add_host) from [<c084e8e0>] (sdhci_iproc_probe+0x11c/0x150)
    [    0.855711] [<c084e8e0>] (sdhci_iproc_probe) from [<c071ad64>] (platform_drv_probe+0x58/0xa8)
    [    0.858963] [<c071ad64>] (platform_drv_probe) from [<c0718f24>] (really_probe+0x20c/0x2cc)
    [    0.862234] [<c0718f24>] (really_probe) from [<c07191b8>] (driver_probe_device+0x70/0x188)
    [    0.865503] [<c07191b8>] (driver_probe_device) from [<c071947c>] (__device_attach_driver+0xac/0xd0)
    [    0.868788] [<c071947c>] (__device_attach_driver) from [<c0716f2c>] (bus_for_each_drv+0x90/0xd4)
    [    0.872033] [<c0716f2c>] (bus_for_each_drv) from [<c0718c90>] (__device_attach+0xe0/0x148)
    [    0.875327] [<c0718c90>] (__device_attach) from [<c07194fc>] (device_initial_probe+0x1c/0x20)
    [    0.878644] [<c07194fc>] (device_initial_probe) from [<c0717f50>] (bus_probe_device+0x94/0x9c)
    [    0.881988] [<c0717f50>] (bus_probe_device) from [<c07183e0>] (deferred_probe_work_func+0x70/0x9c)
    [    0.885363] [<c07183e0>] (deferred_probe_work_func) from [<c023da28>] (process_one_work+0x23c/0x518)
    [    0.888693] [<c023da28>] (process_one_work) from [<c023eb00>] (worker_thread+0x60/0x5b8)
    [    0.892070] [<c023eb00>] (worker_thread) from [<c02445a4>] (kthread+0x16c/0x174)
    [    0.895476] [<c02445a4>] (kthread) from [<c02010ac>] (ret_from_fork+0x14/0x28)
    [    0.898876] Exception stack(0xef3e3fb0 to 0xef3e3ff8)

    __sdhci_add_host 함수를 찾아 책 예제를 추가했다. linux/drivers/mmc/host/sdhci.c에 정의되어 있다. 함수를 추가하면, 에러난다. 어디에 정의되어 있는지 모르는 듯 하다.

    /*210611, interrupt_debug_irq_desc 추가  */
    static void interrupt_debug_irq_desc(int irq_num)
    {
        struct irqaction *action;
        struct irq_desc *desc;
    
        desc = irq_to_desc(irq_num);
    
        if (!desc ) {
            pr_err("invalid desc at %s line: %d\n", __func__, __LINE__);
            return;
        }    
    
        action = desc->action;
    
        if (!action ) {
            pr_err("invalid action at %s line:%d \n", __func__, __LINE__);
            return;
        }    
    
        printk("[+] irq_desc debug start \n");
    
        printk("irq num: %d name: %8s \n", action->irq , action->name);
        printk("dev_id:0x%x \n", (unsigned int)action->dev_id);
    
        if (action->handler) {
            printk("interrupt handler: %pF \n", action->handler);
        }    
    
        printk("[-] irq_desc debug end \n");
    }
    int __sdhci_add_host(struct sdhci_host *host)
    {
        struct mmc_host *mmc = host->mmc;
        int ret; 
    
        /*   
         * Init tasklets.
         */
        tasklet_init(&host->finish_tasklet,
            sdhci_tasklet_finish, (unsigned long)host);
    
        timer_setup(&host->timer, sdhci_timeout_timer, 0);
        timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
    
        init_waitqueue_head(&host->buf_ready_int);
    
        sdhci_init(host, 0);
    
        ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
                       IRQF_SHARED, mmc_hostname(mmc), host);
        if (ret) {
            pr_err("%s: Failed to request IRQ %d: %d\n",
                   mmc_hostname(mmc), host->irq, ret);
            goto untasklet;
        }
        //210611 irq 실습
        interrupt_debug_irq_desc(host->irq);
    ...

    usb 드라이버는 irq_to_desc를 잘 사용했는데, sdhci가 에러내는게 이상하다. 헤더에 extern으로 다른 파일에서 쓸 수 있도록 했음을 알았고, 앞쪽에 통쨰로 복사하여 붙였다.

    /*
     *  linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver
     *
     *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or (at
     * your option) any later version.
     *
     * Thanks to the following companies for their support:
     *
     *     - JMicron (hardware and technical support)
     */
    ...
    //210618. irq_to_desc 실습
    #include <linux/irq.h>
    #include <linux/slab.h>
    #include <linux/export.h>
    #include <linux/interrupt.h>
    #include <linux/kernel_stat.h>
    #include <linux/radix-tree.h>
    #include <linux/bitmap.h>
    #include <linux/irqdomain.h>
    #include <linux/sysfs.h>
    #include "../kernel/irq/internals.h"

    부팅 메세지를 보면 정상적으로 출력된다.

    [    0.912941] [+] irq_desc debug start 
    [    0.918446] irq num: 36 name:     mmc1 
    [    0.921869] dev_id:0xef827b40 
    [    0.925260] interrupt handler: bcm2835_mmc_irq+0x0/0x6e8 
    [    0.928658] [-] irq_desc debug end