【Linux 驱动开发】2-正点原子第五十一章 Linux中断实验 “屎山”代码改改改~
说实话,这一节的代码逻辑不清,对于初学者来说有点混乱,原因主要有:
1. 初始化设备结构体为结构体数组,只用到一个设备却用到了结构体数组,致使多处用循环,代码混乱。
2. 设备(按键)IO初始化与中断初始化混杂在一个函数中,导致逻辑不清晰。
针对以上的混乱现象,我一方面只定义一个设备结构体,一方面将 IO 初始化与中断初始化函数分开,达到清晰理解的目的。
我的结构体类型定义(与正点原子略有不同, 正点原子将 gpio编号成员放在中断结构体,我认为没有必要):
typedef struct irq_Type { unsigned int irqnum; irqreturn_t (*handler)(int, void *); unsigned char value; char name[10]; }irq_Typedef; typedef struct { dev_t devid; /* 设备号:以下三项用于申请设备号 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ struct cdev cdev; /* cdev:用于注册字符设备 */ struct class *class; /* 类 :以下两项用于自动创建设备节点 */ struct device *device; /* 设备 */ struct device_node *nd; /* 设备树中的设备节点*/ int gpio_num; /*led设备用到的GPIO管脚的编号*/ atomic_t key_value; /*LED设备的原子量*/ irq_Typedef dev_irqStruct; /*设备用到的中断相关的属性*/ }DevStruct;
定义设备结构体:
DevStruct Key_Struct;
IO 初始化、中断初始化、节点初始化三分离:
IO初始化部分:配置按键的电气属性(PAD)、设置输入输出方向等等。
1 /** 2 @brief:配置按键 GPIO 的电气属性和输入输出方向等等 3 **/ 4 static int mykey_ioinit(void) 5 { 6 //0. 找到设备树中的 firekey 结点 7 Key_Struct.nd = of_find_node_by_path("/firekey"); 8 if(Key_Struct.nd == NULL) 9 { 10 printk("firekey node cannot found!\n"); 11 return -EINVAL; 12 } 13 else 14 { 15 printk("firekey node find succeed!\n"); 16 } 17 18 //1.打印设备树中的 firekey 结点的 property 19 prp = of_find_property(Key_Struct.nd, "compatible", NULL); 20 if(prp == NULL) 21 { 22 printk("Info compatible find failed.\n"); 23 } 24 else 25 { 26 printk("Compatible:%s\r\n", (char *)prp->value); 27 } 28 29 /*2. 利用设备树的 pinctrl 和 gpio 子系统,配置 gpio 的电气属性 30 并得到 gpio 编号(以后的全部读写API要用到此 gpio 编号) 31 */ 32 Key_Struct.gpio_num = of_get_named_gpio(Key_Struct.nd, "key-gpio", 0); 33 if(Key_Struct.gpio_num < 0) 34 { 35 printk("can't get key-gpio\n"); 36 return -EINVAL; 37 } 38 else 39 { 40 printk("key-gpio num = %d\r\n",Key_Struct.gpio_num); 41 } 42 43 /* 3. 设置GPIO的读写方向,为 input */ 44 gpio_request(Key_Struct.gpio_num, "key0"); 45 gpio_direction_input(Key_Struct.gpio_num); 46 47 return 0; 48 }
中断初始化部分:配置中断号、中断处理函数,并向内核注册中断(调用系统API request_irq())。
1 static int mykey_irqinit(void) 2 { 3 int ret = 0; 4 //1-----------为 dev_irqStruct 设置 irqnum 属性 5 Key_Struct.dev_irqStruct.irqnum = irq_of_parse_and_map(Key_Struct.nd, 0); 6 7 //2-----------为 dev_irqStruct 设置 *handler 属性 8 Key_Struct.dev_irqStruct.handler = key0_handler; 9 10 //3----------为 irqkeydesc 设置 value 属性 11 Key_Struct.dev_irqStruct.value = KEY_ON; 12 13 //4----------为 irqkeydesc 设置 name 属性 14 memset(Key_Struct.dev_irqStruct.name, 0, sizeof(Key_Struct.dev_irqStruct.name)); //将 name 设为 0 15 sprintf(Key_Struct.dev_irqStruct.name, "KEY%d", 0); 16 17 //X---注册中断并使能中断 18 ret = request_irq 19 (Key_Struct.dev_irqStruct.irqnum, //中断号 20 Key_Struct.dev_irqStruct.handler, //中断处理函数 21 IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, //中断标志(中断的属性) 22 Key_Struct.dev_irqStruct.name, //用户定义的中断名字 23 &Key_Struct); //传进 handler 的 void * 参数 24 25 if(ret < 0){ 26 printk("irq request %d failed!\r\n", Key_Struct.dev_irqStruct.irqnum); 27 return -EFAULT; 28 } 29 30 return 0; 31 }
节点初始化部分:向系统申请设备号、注册设备、设置自动装载节点。也是该设备的 xxx_init()函数,包含上述 mykey_ioinit() 和 mykey_irqinit().
1 static int __init mykey_init(void) 2 { 3 //1. 由系统分配设备号 4 if(Key_Struct.major != 0) 5 { 6 Key_Struct.devid= MKDEV(Key_Struct.major, 0); 7 register_chrdev_region(Key_Struct.devid, DEV_CNT, DEV_NAME); 8 } 9 else //major == 0 10 { 11 alloc_chrdev_region(&(Key_Struct.devid),0,DEV_CNT,DEV_NAME); 12 Key_Struct.major = MAJOR(Key_Struct.devid); 13 Key_Struct.minor = MINOR(Key_Struct.devid); 14 } 15 printk("new key major:%d; minor:%d\r\n", Key_Struct.major, Key_Struct.minor); 16 17 //2. 注册设备 18 Key_Struct.cdev.owner = THIS_MODULE; 19 cdev_init(&(Key_Struct.cdev), &my_fops); 20 cdev_add(&(Key_Struct.cdev), Key_Struct.devid, DEV_CNT); 21 22 //3. 设置自动装载节点 23 //创建类 24 Key_Struct.class = class_create(THIS_MODULE, DEV_NAME); 25 if (IS_ERR(Key_Struct.class)) { 26 return PTR_ERR(Key_Struct.class); 27 } 28 //创建设备 29 Key_Struct.device = device_create(Key_Struct.class, NULL, Key_Struct.devid, NULL, DEV_NAME); 30 if (IS_ERR(Key_Struct.device)) { 31 return PTR_ERR(Key_Struct.device); 32 } 33 34 mykey_ioinit(); 35 mykey_irqinit(); 36 37 printk("key init succeed!\n"); 38 printk("KEY_ON: 1; KEY_OFF: 0;\n"); 39 return 0; 40 }