完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
timed GPIO驱动程序分析 Timed GPIO驱动程序是android系统基于linux内核新增加的一类驱动程序,这类驱动程序主要是运用了内核定时器,与内核定时器进行绑定,使得控制GPIO口的高低电平与时间打上关系,既可以实现在一定的时间实现GPIO口为高或者低电平。Timed GPIO驱动被实现为平台设备驱动,Timed GPIO驱动源码位于如下目录:kerneldriversstagingandroid Timed GPIO驱动程序主要包括如下几个文件: Timed_gpio.c Timed_gpio.h Timed_output.c Timed_output.h Timed_gpio.c文件为具体的驱动程序,Timed_output.c为向sys文件系统注册类的框架代码, 下面将具体分析每一个文件的作用及实现的具体功能。 首先分析注册类的框架代码 :Timed_output.c Timed_output.h Timed_output.h文件分析: struct timed_output_dev { const char *name; void (*enable)(struct timed_output_dev *sdev, int timeout); int (*get_time)(struct timed_output_dev *sdev); struct device *dev; int index; int state; }; extern int timed_output_dev_register(struct timed_output_dev *dev); extern void timed_output_dev_unregister(struct timed_output_dev *dev); Timed_output.h文件主要定义了一个结构体timed_output_dev设备结构体,该结构体表示一个具体的设备, Name:代表Time GPIO设备的名字,enable:为一个函数指针,主要用于设置定时器的过期时间, Enable:用于获取离过期还剩余的时间。Index:为设备索引号,代表同一名字的设备的数量,state带表当前设备的状态值。 timed_output_dev_register,timed_output_dev_unregister这两个函数声明用于timed_output设备的注册和卸载。 Timed_output.c文件分析: 该文件主要用于向系统注册timed_output驱动程序框架,其中主要实现了Timed_output.h文件中定义的结构体及函数。 timed_output框架注册函数的实现,这个函数用于将驱动程序注册到kernel中,后面分析Timed GPIO驱动的具体实现时会调用此函数向系统注册Timed GPIO驱动。 int timed_output_dev_register(struct timed_output_dev *tdev) { int ret; if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time) return -EINVAL; ret = create_timed_output_class(); 调用此函数在sys/class下生成timed_output类 if (ret < 0) return ret; tdev->index = atomic_inc_return(&device_count); tdev->dev = device_create(timed_output_class, NULL, MKDEV(0, tdev->index), NULL, tdev->name); if (IS_ERR(tdev->dev)) return PTR_ERR(tdev->dev); ret = device_create_file(tdev->dev, &dev_attr_enable); if (ret < 0) goto err_create_file; dev_set_drvdata(tdev->dev, tdev); tdev->state = 0; return 0; err_create_file: device_destroy(timed_output_class, MKDEV(0, tdev->index)); printk(KERN_ERR "timed_output: Failed to register driver %sn", tdev->name); return ret; } 此函数用于在sys/class下面创建类,类的名字为timed_output static int create_timed_output_class(void) { if (!timed_output_class) { timed_output_class = class_create(THIS_MODULE, "timed_output"); if (IS_ERR(timed_output_class)) return PTR_ERR(timed_output_class); atomic_set(&device_count, 0); } return 0; } 其中下面两个函数最为关键,是内核空间和用户空间的传值过程的具体实现函数, enable_show函数调用get_time函数并将返回的剩余时间写入buf并传递到用户空间。 static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int remaining = tdev->get_time(tdev); return sprintf(buf, "%dn", remaining); } enable_store函数用于将用户空间传递来的buf值写入内核空间。 static ssize_t enable_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int value; if (sscanf(buf, "%d", &value) != 1) return -EINVAL; tdev->enable(tdev, value); return size; } Timed_gpio.h文件分析: #ifndef _LINUX_TIMED_GPIO_H #define _LINUX_TIMED_GPIO_H #define TIMED_GPIO_NAME "timed-gpio" //Time_GPIO驱动的名字,将显示在/sys/class/ timed_output目录下 struct timed_gpio { const char *name; //GPIO的名字 unsigned gpio; //具体的GPIO管脚 int max_timeout;//最大的超时时间 u8 active_low; //IO口电平状态表示位 }; timed_gpio结构体仅用于定义单个的GPIO的相关信息 gpio_platform_data结构体用于定义一组GPIO的相关信息 struct timed_gpio_platform_data { int num_gpios; struct timed_gpio *gpios; }; #endif 下面将分析具体的Timed_gpio驱动程序 timed_gpio_driver定义如下: 该函数指明了具体的初始化函数(pore)和移除函数(remove)以及驱动的名字额模块。 static struct platform_driver timed_gpio_driver = { .probe = timed_gpio_probe, .remove = timed_gpio_remove, .driver = { .name = TIMED_GPIO_NAME, .owner = THIS_MODULE, }, }; 调用platform_driver_register函数向kernel注册平台驱动 static int __init timed_gpio_init(void) { return platform_driver_register(&timed_gpio_driver); } timed_gpio探测函数 static int timed_gpio_probe(struct platform_device *pdev) { struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; struct timed_gpio *cur_gpio; struct timed_gpio_data *gpio_data, *gpio_dat; int i, j, ret = 0; if (!pdata) return -EBUSY; //为pdata->num_gpios个GPIO分配内存空间 gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, GFP_KERNEL); if (!gpio_data) return -ENOMEM; for (i = 0; i < pdata->num_gpios; i++) { cur_gpio = &pdata->gpios; gpio_dat = &gpio_data; hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,//初始化定时器 HRTIMER_MODE_REL); gpio_dat->timer.function = gpio_timer_func;//定时器回调函数 spin_lock_init(&gpio_dat->lock); gpio_dat->dev.name = cur_gpio->name; gpio_dat->dev.get_time = gpio_get_time; gpio_dat->dev.enable = gpio_enable; ret = gpio_request(cur_gpio->gpio, cur_gpio->name);//申请GPIO if (ret >= 0) { ret = timed_output_dev_register(&gpio_dat->dev);//调用timed_output框架注册函数 if (ret < 0) gpio_free(cur_gpio->gpio); } if (ret < 0) { for (j = 0; j < i; j++) { timed_output_dev_unregister(&gpio_data.dev); gpio_free(gpio_data.gpio); } kfree(gpio_data); return ret; } gpio_dat->gpio = cur_gpio->gpio; gpio_dat->max_timeout = cur_gpio->max_timeout; gpio_dat->active_low = cur_gpio->active_low; gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low);//初始化GPIO的输出值 } platform_set_drvdata(pdev, gpio_data); return 0; } 初始化过程: 1. 首先调用kzalloc函数为 GPIO分配内存空间 2. 调用hrtimer_init函数初始化化内核定时器 3. 设置GPIO的enable函数为gpio_enable 4. 设置GPIO的get_time函数为gpio_get_time 5. 调用timed_output_dev_register函数注册设备驱动。 6. 初始化timed_gpio_data结构体 7. 调用gpio_direction_output函数设置GPIO的初始值。 GPIO驱动移除函数,调用timed_output_dev_unregister卸载驱动程序 static int timed_gpio_remove(struct platform_device *pdev) { struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev); int i; for (i = 0; i < pdata->num_gpios; i++) { timed_output_dev_unregister(&gpio_data.dev); gpio_free(gpio_data.gpio); } kfree(gpio_data); return 0; } 功能回调函数gpio_timer_func分析:定时器超时后将执行此函数,此函数根据active_low的值来设置GPIO的高低电平。 static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) { struct timed_gpio_data *data = container_of(timer, struct timed_gpio_data, timer); gpio_direction_output(data->gpio, data->active_low ? 1 : 0); return HRTIMER_NORESTART; } gpio_enable函数为关键函数接受用户空间传过来的value值用于在一定时间里控制GPIO static void gpio_enable(struct timed_output_dev *dev, int value) { struct timed_gpio_data *data = container_of(dev, struct timed_gpio_data, dev); unsigned long flags; spin_lock_irqsave(&data->lock, flags); /* cancel previous timer and set GPIO according to value */ hrtimer_cancel(&data->timer); gpio_direction_output(data->gpio, data->active_low ? !value : !!value); if (value > 0) { if (value > data->max_timeout) value = data->max_timeout; //启动定时器函数 hrtimer_start(&data->timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); } spin_unlock_irqrestore(&data->lock, flags); } 到这里相信大家对Time GPIO驱动已经用了深刻的印象和认识。下面将用一幅图来说明整个Time GPIO驱动的调用过程。以总结回顾前面的分析。
|
|
相关推荐
|
|
iTOP-3A5000主控板龙芯架构外加机箱就是一台电脑主机
859 浏览 0 评论
迅为RK3568开发板EMMC镜像导出打包update.img
909 浏览 0 评论
飞凌嵌入式-ELFBOARD 硬件知识分享-ELF 2电源电路讲解
1360 浏览 0 评论
2066 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程介绍之烧写所需镜像
1718 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-29 12:12 , Processed in 0.409152 second(s), Total 35, Slave 28 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号