【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;
View Code

定义设备结构体:

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 }

 

posted @ 2023-07-03 10:53  FBshark  阅读(24)  评论(0编辑  收藏  举报