平台设备驱动模板
2.写模板#
具体字符设备相关代码与硬件有关,与模板1一致。修改部分仅在引脚资源获取部分,包括设备树与平台设备注册方式。
(1)gpio_drv.c
修改入口函数与出口函数:注册平台驱动程序,而原先入口函数、出口函数的工作放在
platform_driver
的probe和remove函数中static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpio-demo", },{ /* sentinel */ }};static struct platform_driver gpio_platform_driver = {.driver = {.name = "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,},.probe = gpio_drv_probe,.remove = gpio_drv_remove,};/* 在入口函数 */static int __init gpio_drv_init(void){/* 注册platform_driver */return platform_driver_register(&gpio_platform_driver);}/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/static void __exit gpio_drv_exit(void){/* 注销platform_driver */platform_driver_unregister(&gpio_platform_driver);}gpio_drv_probe和gpio_drv_remove,在probe函数里面获取引脚资源
static int gpio_drv_probe(struct platform_device *pdev){int err = 0;int i;struct device_node *np = pdev->dev.of_node;struct resource *res;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 从platform_device 获得引脚信息* 1. pdev来自c文件* 2. pdev来自设备树*/if (np){/* pdev来自设备树:示例reg_usb_ltemodule: regulator@1 {compatible = "100ask,gpio-demo";gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;};*/count = of_gpio_count(np);if (!count){return -EINVAL;}gpios = kmalloc(count*sizeof(struct gpio_desc), GFP_KERNEL);for ( i = 0; i < count; i++){gpios[i].gpio = of_get_gpio(np, i);sprintf(gpios[i].name, "%s_pin_%d", np->name, i);}}else{/* pdev来自c文件static struct resource omap16xx_gpio3_resources[] = {{.start = 115,.end = 115,.flags = IORESOURCE_IRQ,},{.start = 118,.end = 118,.flags = IORESOURCE_IRQ,},};*/count = 0;while (1){res = platform_get_resource(pdev, IORESOURCE_IRQ, count);if (!res){break;}count++;}if (!count){return -EINVAL;}gpios = kmalloc(count*sizeof(struct gpio_desc), GFP_KERNEL);for ( i = 0; i < count; i++){res = platform_get_resource(pdev, IORESOURCE_IRQ, i);gpios[i].gpio = res->start;sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);}}for (i = 0; i < count; i++){gpios[i].irq = gpio_to_irq(gpios[i].gpio); //把引脚号转换为中断号,setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);//timer_setup(&gpios[i].key_timer, key_timer_expire, 0);gpios[i].key_timer.expires = ~0;add_timer(&gpios[i].key_timer);//注册中断,这里按键按下与松开时就会触发中断,进入中断处理函数gpio_key_isr(gpios[i].irq, &gpios[i])err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"100ask_gpio_key", &gpios[i]);}/* 注册file_operations */major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv); /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_gpio_key");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio"); /* /dev/100ask_gpio */kfree(gpios);return err;}之前我们是通过一个数组来管理GPIO
static struct gpio_desc gpios[] = {{131, 0, "gpio_100ask_1", 1,},{132, 0, "gpio_100ask_2", 2,},};现在是使用platform_device来获取
1.在平台platform_device下的dev结构体中有of_node,可以从中读取gpio引脚数据(设备树在内核加载时会注册)
struct platform_device {const char *name;int id;bool id_auto;struct device dev;...};struct device dev;
struct device {...struct device_node *of_node; /* associated device tree node */...};2.of_gpio_count(np);
在.dts和设备驱动不关心GPIO名字的情况下,可以直接通过of_get_gpio()获取GPIO
/*** of_gpio_count() - Count GPIOs for a device* @np: device node to count GPIOs for** Same as of_gpio_named_count, but hard coded to use the 'gpios' property*/static inline int of_gpio_count(struct device_node *np){return of_gpio_named_count(np, "gpios"); //将设备节点中名为 “gpios” 的属性用作 GPIO 数量的计数}3.of_get_gpio(np, i);
接受一个指向设备节点(device_node)的指针和 GPIO 的索引作为参数。
该函数调用的实际功能由 of_get_gpio_flags 函数执行,通过设备节点和 GPIO 索引获取 GPIO 编号,并返回该编号。
/*** of_get_gpio() - Get a GPIO number to use with GPIO API* @np: device node to get GPIO from* @index: index of the GPIO** Returns GPIO number to use with Linux generic GPIO API, or one of the errno* value on the error condition.*/static inline int of_get_gpio(struct device_node *np, int index){return of_get_gpio_flags(np, index, NULL);}参考
《Linux设备开发详解 - 宋宝华》12.2.3 platform设备资源和数据
使用.c文件注册platform_devic
struct platform_device {const char *name;int id;bool id_auto;struct device dev;u32 num_resources;struct resource *resource;...};可以使用IORESOURCE_IRQ表示GPIO资源,可以通过platform_get_resource获取资源
struct resource *platform_get_resource(struct platform_device *, unsigned int,unsigned int);//示例res = platform_get_resource(pdev, IORESOURCE_IRQ, i);gpios[i].gpio = res->start;remove函数就是之前exit函数中的工作,参数和返回值定义不同
static int gpio_drv_remove(struct platform_device *pdev){int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "100ask_gpio_key");for (i = 0; i < count; i++){free_irq(gpios[i].irq, &gpios[i]);del_timer(&gpios[i].key_timer);}return 0;}
(2)dts设备树文件和gpio_dev.c
以电机驱动为例:需要确保设备树的compatible
与platform_driver.driver.of_match_table
数组中的某一项的compatible匹配
- 修改设备树:
arch/arm/boot/dts/100ask…
编译:
make drbs
复制到开发板上
PC:cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/开发板:cp /mnt/100ask_imx6ull-14x14.dtb /boot/
重启开发板后可以在/sys/firmware/devicetree/base
看到motor节点,进入motor节点
再装载驱动程序,可以看到会多出driver目录
使用gpio_dev.c注册platform_dev
一样有入口函数和出口函数:
定义了
这里需要gpio_platform_device.named
的“100ask_gpio_plat_drv”与平台驱动的name匹配(不止一种匹配方式参考匹配方式)
开发板测试gpio_drv.c和gpio_dev.c
仅装载drv
再装载dev
请点击左侧菜单(移动端为右下角)选择要查看的所有笔记吧。