前情说明:本文为正点原子I.MX6U 驱动开发篇代码总结,摘录了部分例程中主要的代码框架,完整代码及教程请参考正点原子官方。
Linux驱动开发——正点原子I.MX6U驱动开发例程速通(一)
Linux驱动开发——正点原子I.MX6U驱动开发例程速通(二)
Linux驱动开发——正点原子I.MX6U驱动开发例程速通(三)
本文对应《正点原子I.MX6U嵌入式Linux驱动开发指南》中的以下章节:
18_dtsplatform19_miscbeep20_input为减少篇幅,部分框架重复内容使用...表示。
platform驱动的probe函数,当驱动与设备匹配后此函数就会执行
static int led_probe(struct platform_device *dev){ printk("led driver and device was matched!\r\n"); /* 1、设置设备号 */ if (leddev.major) { leddev.devid = MKDEV(leddev.major, 0); register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME); } else { alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); leddev.major = MAJOR(leddev.devid); } /* 2、注册设备 */ cdev_init(&leddev.cdev, &led_fops); cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT); /* 3、创建类 */ leddev.class = class_create(THIS_MODULE, LEDDEV_NAME); if (IS_ERR(leddev.class)) { return PTR_ERR(leddev.class); } /* 4、创建设备 */ leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME); if (IS_ERR(leddev.device)) { return PTR_ERR(leddev.device); } /* 5、初始化IO */ leddev.node = of_find_node_by_path("/gpioled"); if (leddev.node == NULL){ printk("gpioled node nost find!\r\n"); return -EINVAL; } leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0); if (leddev.led0 < 0) { printk("can't get led-gpio\r\n"); return -EINVAL; } gpio_request(leddev.led0, "led0"); gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出,默认高电平 */ return 0;}platform驱动的remove函数,移除platform驱动的时此函数会执行
static int led_remove(struct platform_device *dev){ gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭LED */ gpio_free(leddev.led0); /* 释放IO */ cdev_del(&leddev.cdev); /* 删除cdev */ unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */ device_destroy(leddev.class, leddev.devid); class_destroy(leddev.class); return 0;}static conststruct of_device_id led_of_match[] = { { .compatible = "atkalpha-gpioled" }, { /* Sentinel */ }};staticstruct platform_driver led_driver = { .driver = { .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */ .of_match_table = led_of_match, /* 设备树匹配表 */ }, .probe = led_probe, .remove = led_remove,};static int __init leddriver_init(void){ return platform_driver_register(&led_driver);}static void __exit leddriver_exit(void){ platform_driver_unregister(&led_driver);}struct miscbeep_dev{ dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */struct device_node *nd; /* 设备节点 */ int beep_gpio; /* beep所使用的GPIO编号 */};staticstruct file_operations miscbeep_fops = { .owner = THIS_MODULE, .open = miscbeep_open, .write = miscbeep_write,};staticstruct miscdevice beep_miscdev = { .minor = MISCBEEP_MINOR, .name = MISCBEEP_NAME, .fops = &miscbeep_fops,};platform驱动的probe函数,当驱动与设备匹配以后此函数就会执行
static int miscbeep_probe(struct platform_device *dev){ int ret = 0; printk("beep driver and device was matched!\r\n"); /* 设置BEEP所使用的GPIO */ /* 1、获取设备节点:beep */ miscbeep.nd = of_find_node_by_path("/beep"); if(miscbeep.nd == NULL) { printk("beep node not find!\r\n"); return -EINVAL; } /* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */ miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0); if(miscbeep.beep_gpio < 0) { printk("can't get beep-gpio"); return -EINVAL; } /* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */ ret = gpio_direction_output(miscbeep.beep_gpio, 1); if(ret < 0) { printk("can't set gpio!\r\n"); } /* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备 * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可 */ ret = misc_register(&beep_miscdev); if(ret < 0){ printk("misc device register failed!\r\n"); return -EFAULT; } return 0;}static int miscbeep_remove(struct platform_device *dev){ /* 注销设备的时候关闭LED灯 */ gpio_set_value(miscbeep.beep_gpio, 1); /* 注销misc设备 */ misc_deregister(&beep_miscdev); return 0;} static conststruct of_device_id beep_of_match[] = { { .compatible = "atkalpha-beep" }, { /* Sentinel */ } };staticstruct platform_driver beep_driver = { .driver = { .name = "imx6ul-beep", /* 驱动名字,用于和设备匹配 */ .of_match_table = beep_of_match, /* 设备树匹配表 */ }, .probe = miscbeep_probe, .remove = miscbeep_remove,};static int __init miscbeep_init(void){ return platform_driver_register(&beep_driver);}static void __exit miscbeep_exit(void){ platform_driver_unregister(&beep_driver);}struct keyinput_dev { ...struct input_dev *inputdev; /* input结构体 */};void timer_function(unsigned long arg){ unsigned char value; unsigned char num;struct irq_keydesc *keydesc;struct keyinput_dev *dev = (struct keyinput_dev *)arg; num = dev->curkeynum; keydesc = &dev->irqkeydesc[num]; value = gpio_get_value(keydesc->gpio); /* 读取IO值 */ if(value == 0){ /* 按下按键 */ /* 上报按键值 */ //input_event(dev->inputdev, EV_KEY, keydesc->value, 1); input_report_key(dev->inputdev, keydesc->value, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */ input_sync(dev->inputdev); } else { /* 按键松开 */ //input_event(dev->inputdev, EV_KEY, keydesc->value, 0); input_report_key(dev->inputdev, keydesc->value, 0); input_sync(dev->inputdev); } }static int keyio_init(void){ unsigned char i = 0; char name[10]; int ret = 0; keyinputdev.nd = of_find_node_by_path("/key"); if (keyinputdev.nd== NULL){ printk("key node not find!\r\n"); return -EINVAL; } /* 提取GPIO */ for (i = 0; i < KEY_NUM; i++) { keyinputdev.irqkeydesc[i].gpio = of_get_named_gpio(keyinputdev.nd ,"key-gpio", i); if (keyinputdev.irqkeydesc[i].gpio < 0) { printk("can't get key%d\r\n", i); } } /* 初始化key所使用的IO,并且设置成中断模式 */ for (i = 0; i < KEY_NUM; i++) { memset(keyinputdev.irqkeydesc[i].name, 0, sizeof(name)); /* 缓冲区清零 */ sprintf(keyinputdev.irqkeydesc[i].name, "KEY%d", i); /* 组合名字 */ gpio_request(keyinputdev.irqkeydesc[i].gpio, name); gpio_direction_input(keyinputdev.irqkeydesc[i].gpio); keyinputdev.irqkeydesc[i].irqnum = irq_of_parse_and_map(keyinputdev.nd, i); } /* 申请中断 */ keyinputdev.irqkeydesc[0].handler = key0_handler; keyinputdev.irqkeydesc[0].value = KEY_0; for (i = 0; i < KEY_NUM; i++) { ret = request_irq(keyinputdev.irqkeydesc[i].irqnum, keyinputdev.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, keyinputdev.irqkeydesc[i].name, &keyinputdev); if(ret < 0){ printk("irq %d request failed!\r\n", keyinputdev.irqkeydesc[i].irqnum); return -EFAULT; } } /* 创建定时器 */ init_timer(&keyinputdev.timer); keyinputdev.timer.function = timer_function; /* 申请input_dev */ keyinputdev.inputdev = input_allocate_device(); keyinputdev.inputdev->name = KEYINPUT_NAME;#if 0 /* 初始化input_dev,设置产生哪些事件 */ __set_bit(EV_KEY, keyinputdev.inputdev->evbit); /* 设置产生按键事件 */ __set_bit(EV_REP, keyinputdev.inputdev->evbit); /* 重复事件,比如按下去不放开,就会一直输出信息 */ /* 初始化input_dev,设置产生哪些按键 */ __set_bit(KEY_0, keyinputdev.inputdev->keybit);#endif#if 0 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);#endif keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); /* 注册输入设备 */ ret = input_register_device(keyinputdev.inputdev); if (ret) { printk("register input device failed!\r\n"); return ret; } return 0;}static int __init keyinput_init(void){ keyio_init(); return 0;}static void __exit keyinput_exit(void){ unsigned int i = 0; /* 删除定时器 */ del_timer_sync(&keyinputdev.timer); /* 删除定时器 */ /* 释放中断 */ for (i = 0; i < KEY_NUM; i++) { free_irq(keyinputdev.irqkeydesc[i].irqnum, &keyinputdev); } /* 释放input_dev */ input_unregister_device(keyinputdev.inputdev); input_free_device(keyinputdev.inputdev);}