思来想去,因为涉及面过于繁杂。加之本人能力又有限。还是认为站在BSP的角度上去分析,首先简介基础的背景知识。搞懂驱动程序的意思之后,而后再着手优化移植驱动,用这样的比較有用的方式慢慢地模块看似比較难的驱动程序。
并且,很多这种设备/电路仅仅要求一位。即仅仅要有开/关两种状态就够了。比方灯亮与灭。
对这些设备/电路的控制。使用传统的串行口或并行口都不合适。
所以在微控制器芯片上一般都会提供一个“通用可编程IO接口”,即GPIO。
接口至少有两个寄存器,即“通用IO控制寄存器”与“通用IO数据寄存器”。
数据寄存器的各位都直接引到芯片外部,而对这样的寄存器中每一位的作用,即每一位的信号流通方向,则能够通过控制寄存器中相应位独立的加以设置。这样。有无GPIO接口也就成为微控制器差别于微处理器的一个特征。
static int __init sun4i_gpio_init(void) { int err; int i; int sun4i_gpio_used = 0; struct sun4i_gpio_data *gpio_i; /* include /linux/sysfs.h ------------------------------------- struct attribute { const char *name; struct module *owner; mode_t mode; }; struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); }; 这2个都是sys文件系统的中的结构体。关键点在device_attribute 中的show 和 store,事实上就是对设备的读和写 */ struct device_attribute *attr_i; char pin[16]; pr_info("sun4i gpio driver init "); /* 用来获取sys_config1.fex主键gpio_para中的子键gpio_used的值,假设gpio_used的值为0,则表示该驱动已经通过配置关闭了,这样就是实现了用配置来控制打开和关闭驱动 这里我们知道了假设我们要添加单独控制的gpio。我们仅仅须要在sys_config1.fex文件里,加入gpio_para的主键和名为gpio_used的子键 */ err = script_parser_fetch("gpio_para", "gpio_used", &sun4i_gpio_used, sizeof(sun4i_gpio_used)/sizeof(int)); if(err) { pr_err("%s script_parser_fetch "gpio_para" "gpio_used" error ", __FUNCTION__); goto exit; } if(!sun4i_gpio_used) { pr_err("%s sun4i_gpio is not used in config ", __FUNCTION__); err = -1; goto exit; } /* 用来获取sys_config1.fex主键gpio_para中的子键gpio_num的值,非常显然子键gpio_num的值。用来定义配置中一共同拥有多少个gpio */ err = script_parser_fetch("gpio_para", "gpio_num", &sun4i_gpio_num, sizeof(sun4i_gpio_num)/sizeof(int)); if(err) { pr_err("%s script_parser_fetch "gpio_para" "gpio_num" error ", __FUNCTION__); goto exit; } sun4i_gpio_dbg("sun4i_gpio_num:%d ", sun4i_gpio_num); if(!sun4i_gpio_num) { pr_err("%s sun4i_gpio_num is none ", __FUNCTION__); err = -1; goto exit; } /* 注冊一个杂项设备,主设备号是10,此设备号由系统来定义 */ err = misc_register(&sun4i_gpio_dev); if(err) { pr_err("%s register sun4i_gpio as misc device error ", __FUNCTION__); goto exit; } /* 依据gpio的个数。对每一个gpio结构体申请一块内存,用来保存从sys_config1.fex文件里读取到的每一个gpio的属性 */ psun4i_gpio = kzalloc(sizeof(struct sun4i_gpio_data) * sun4i_gpio_num, GFP_KERNEL); /* 依照gpio的个数,对每一个gpio申请一个设备属性,每一个设备属性将用来对sys文件系统中的gpio的读写 */ pattr = kzalloc(sizeof(struct device_attribute) * sun4i_gpio_num, GFP_KERNEL); if(!psun4i_gpio || !pattr) { pr_err("%s kzalloc failed ", __FUNCTION__); err = -ENOMEM; goto exit; } gpio_i = psun4i_gpio; attr_i = pattr; /* 循环对每一个gpio的在sys_config1.fex文件的值进行读取,并将解析出来的值保存到gpio_i中 */ for(i = 0; i < sun4i_gpio_num; i++) { /* 由此能够看出,子键相似于gpio_pin_1 gpio_pin_2 gpio_pin_3 ......这样的方式来命名的 */ sprintf(pin, "gpio_pin_%d", i+1); sun4i_gpio_dbg("pin:%s ", pin); err = script_parser_fetch("gpio_para", pin, (int *)&gpio_i->info, sizeof(script_gpio_set_t)); if(err) { pr_err("%s script_parser_fetch "gpio_para" "%s" error ", __FUNCTION__, pin); break; } /* ************************************************************************************************************ * 这是 CSP_GPIO_Request_EX函数的说明 * CSP_GPIO_Request_EX * * 函数名称: * * 參数说明: main_name 传进的主键名称,匹配模块(驱动名称) * * sub_name 传进的子键名称,假设是空,表示所有,否则寻找到匹配的单独GPIO * * 返回值 :0 : err * other: success * * 说明 :临时没有做冲突检查 * * ************************************************************************************************************ */ gpio_i->gpio_handler = gpio_request_ex("gpio_para", pin); sun4i_gpio_dbg("gpio handler: %d", gpio_i->gpio_handler); if(!gpio_i->gpio_handler) { pr_err("%s can not get "gpio_para" "%s" gpio handler, already used by others?", __FUNCTION__, pin); break; } sun4i_gpio_dbg("%s: port:%d, portnum:%d ", pin, gpio_i->info.port, gpio_i->info.port_num); /* Turn the name to pa1, pb2 etc... */ sprintf(gpio_i->name, "p%c%d", 'a'+gpio_i->info.port-1, gpio_i->info.port_num); sun4i_gpio_dbg("psun4i_gpio->name%s ", gpio_i->name); /* Add attributes to the group */ /* 这里将属性初始化到sys文件系统。并对device_attribute 结构体的成员赋值,这样事实上就是定义了读写IO的函数 sun4i_gpio_enable_show就是读出IO的data,而sun4i_gpio_enable_store就是往IO中写入值 */ sysfs_attr_init(&attr_i->attr); attr_i->attr.name = gpio_i->name; attr_i->attr.mode = S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH; attr_i->show = sun4i_gpio_enable_show; attr_i->store = sun4i_gpio_enable_store; sun4i_gpio_attributes[i] = &attr_i->attr; gpio_i++; attr_i++; } sysfs_create_group(&sun4i_gpio_dev.this_device->kobj, &sun4i_gpio_attribute_group); exit: return err; } static void __exit sun4i_gpio_exit(void) { sun4i_gpio_dbg("bye, sun4i_gpio exit "); sysfs_remove_group(&sun4i_gpio_dev.this_device->kobj, &sun4i_gpio_attribute_group); misc_deregister(&sun4i_gpio_dev); kfree(psun4i_gpio); kfree(pattr); } struct sun4i_gpio_data,这个结构体事实上就用来描写叙述一个gpio struct sun4i_gpio_data { int status; //当前状态,事实上就是gpio的值。0或者1 unsigned gpio_handler; //用来标识这个gpio,相当于一个唯一的id script_gpio_set_t info; char name[8]; //8个字节的字符串用来描写叙述名字 比如"PI09" } script_gpio_set_t 结构体。才是真正用来描写叙述单个的gpio typedef struct { char gpio_name[32]; int port; int port_num; int mul_sel; int pull; int drv_level; int data; } script_gpio_set_t;
gpio_name ="gpio_pin_1 "
port ='I'
port_num =09
描写叙述gpio的形式:Port:port+组内序号<功能分配><内部电阻状态><驱动能力><输出电平状态>
相应的,mul_sel就是功能分配,是Multifunction select的缩写,这个决定了属于什么功能,因为我们这里是输出功能,所以默认值为1
sysfs
终于sysfs使我们认识到一个全新的对象模型非常有利于系统,于是kobject应运而生。
从kernel在2.6引入sysfs開始,sysfs总是不可或缺的内核的一部分了。
假设採用sysfs的方式,我们的应用程序仅仅须要读写一个文件就可以操作GPIO了
static int sun4i_gpio_open(struct inode *inode, struct file *file) { pr_info("sun4i_gpio open "); return 0; } ssize_t sun4i_gpio_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { pr_info("sun4i_gpio write "); return 0; } static const struct file_operations sun4i_gpio_fops = { .open = sun4i_gpio_open, .write = sun4i_gpio_write, .release = sun4i_gpio_release };