前面写了三篇关于全志CQA83T下Android控制led的博文,但是还是有很多东西可以学习,可以写写作为学习记录。如果看源码,绕不过script这个东西,这个不是像其他系统脚本一样,这里的script应该是全志自己加上去的,不是原生系统的内容(暂时给感觉是这样)。这一篇还是要借助led驱动的源码来引入,下面再贴一下led.c的源码:
#include <linux/types.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/init.h> #include <linux/input.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/input/matrix_keypad.h> #include <linux/slab.h> #include <asm/io.h> #include <mach/irqs.h> #include <mach/hardware.h> #include <mach/sys_config.h> #include <linux/miscdevice.h> #include <linux/printk.h> #include <linux/kernel.h> #define LED_IOCTL_SET_ON 1 #define LED_IOCTL_SET_OFF 0 static script_item_u led_val[5]; static script_item_value_type_e led_type; static struct semaphore lock; static int led_open(struct inode *inode, struct file *file) { if (!down_trylock(&lock)) return 0; else return -EBUSY; } static int led_close(struct inode *inode, struct file *file) { up(&lock); return 0; } static long led_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { unsigned int n; n = (unsigned int)arg; switch (cmd) { case LED_IOCTL_SET_ON: if (n < 1) return -EINVAL; if(led_val[n-1].gpio.gpio != -1) { __gpio_set_value(led_val[n-1].gpio.gpio, 1); printk("led%d on !\n", n); } break; case LED_IOCTL_SET_OFF: default: if (n < 1) return -EINVAL; if(led_val[n-1].gpio.gpio != -1) { __gpio_set_value(led_val[n-1].gpio.gpio, 0); printk("led%d off !\n", n); } break; } return 0; } static int __devinit led_gpio(void) { int i = 0; char gpio_num[10]; for(i =1 ; i < 6; i++) { sprintf(gpio_num, "led_gpio%d", i); led_type= <span style="color:#ff0000;background-color: rgb(102, 255, 153);">script_get_item</span>("led_para", gpio_num, &led_val[i-1]); if(SCIRPT_ITEM_VALUE_TYPE_PIO != led_type) { printk("led_gpio type fail !"); // gpio_free(led_val[i-1].gpio.gpio); led_val[i-1].gpio.gpio = -1; continue; } if(0 != gpio_request(led_val[i-1].gpio.gpio, NULL)) { printk("led_gpio gpio_request fail !"); led_val[i-1].gpio.gpio = -1; continue; } if (0 != gpio_direction_output(led_val[i-1].gpio.gpio, 0)) { printk("led_gpio gpio_direction_output fail !"); // gpio_free(led_val[i-1].gpio.gpio); led_val[i-1].gpio.gpio = -1; continue; } } return 0; } static struct file_operations leds_ops = { .owner = THIS_MODULE, .open = led_open, .release = led_close, .unlocked_ioctl = led_ioctl, }; static struct miscdevice leds_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "led", .fops = &leds_ops, }; static int __devexit led_remove(struct platform_device *pdev) { return 0; } static int __devinit led_probe(struct platform_device *pdev) { int led_used; script_item_u val; script_item_value_type_e type; int err; printk("led_para!\n"); type = <span style="color:#ff0000;background-color: rgb(102, 255, 153);">script_get_item</span>("led_para", "led_used", &val); if (SCIRPT_ITEM_VALUE_TYPE_INT != type) { printk("%s script_get_item \"led_para\" led_used = %d\n", __FUNCTION__, val.val); return -1; } led_used = val.val; printk("%s script_get_item \"led_para\" led_used = %d\n", __FUNCTION__, val.val); if(!led_used) { printk("%s led_used is not used in config, led_used=%d\n", __FUNCTION__,led_used); return -1; } err = led_gpio(); if (err) return -1; sema_init(&lock, 1); err = misc_register(&leds_dev); printk("======= cqa83 led initialized ================\n"); return err; } struct platform_device led_device = { .name = "led", }; static struct platform_driver led_driver = { .probe = led_probe, .remove = __devexit_p(led_remove), .driver = { .name = "led", .owner = THIS_MODULE, }, }; static int __init led_init(void) { if (platform_device_register(&led_device)) { printk("%s: register gpio device failed\n", __func__); } if (platform_driver_register(&led_driver)) { printk("%s: register gpio driver failed\n", __func__); } return 0; } static void __exit led_exit(void) { platform_driver_unregister(&led_driver); } module_init(led_init); module_exit(led_exit); MODULE_DESCRIPTION("Led Driver"); MODULE_LICENSE("GPL v2");
通俗来说吧,就是开发板的管脚配置会写在一个叫sys_config.fex的文件中,而script的功能就是去读这个文件并进行解析保存,在程序使用某个管脚时去从这些保存的内容中查找,如果能够查找到并且可用,即是匹配成功,那么程序才能使用该管脚。Script这个内容在三个文件中,sys_config.fex,sys_config.h,sys_config.c。sys_config.fex就是系统管脚配置文件,sys_config.h声明头文件和数据结构,sys_config.c实现了接口函数。由于sys_config.fex内容比较多,这里就不全部显示了,现在说led驱动,就只贴出来led的配置内容。其文件在lichee\tools\pack\chips\sun8iw6p1\configs\f1\sys_config.fex
;---------------------------------------------------------------------------------- ;led ;---------------------------------------------------------------------------------- [led_para]<span style="white-space:pre"> </span>//主键 led_used = 1<span style="white-space:pre"> </span>//使用使能,0不能使用,1能使用 led_gpio1 = port:PL09<1><default><default><1><span style="white-space:pre"> </span> led_gpio2 = port:PL10<1><default><default><1> led_gpio3 = port:PH04<1><default><default><1> led_gpio4 = port:PH09<1><default><default><1> led_gpio5 = port:PC07<1><default><default><1>
port:<port><mux feature><pullup/down><drive capability><output level>
<port>:要设置的io口(例如PL09)
<mux feature>:io口功能配置:0是input,1是output,2到7可以看板子的具体配置
<pullup/down>:0是disable;1是pullup enable;2是pulldown enable。但是这个配置只要引脚被配置成输入才是有效的。
<drive capability>:驱动能力配置,驱动能力默认是毫安(mA)级别的,配置成0到3对应,10mA,20mA,30mA,40mA。
<output level>:设置初始化时的电平输出值,配置为0是低电平,配置为1是高电平。但是只有引脚被配置为输出时才是有有效的。
其中<pullup/down>,<drive capability>,<output level>三个的值可以配置为default,设置为default是和上次保持不变。
这些内容可以在http://linux-sunxi.org/Fex_Guide#FEX_Description这个网站上看到。
下面来看一下,script_get_item这个函数,这个函数的实现在linux-3.4\arch\arm\mach-sunxi\sys_config.c
<span style="color:#ff0000;">script_item_value_type_e</span> script_get_item(char *main_key, char *sub_key, script_item_u *item) { int main_hash, sub_hash; <span style="color:#009900;">script_main_key_t *mainkey = g_script;</span> if(!main_key || !sub_key || !item || !g_script) { return SCIRPT_ITEM_VALUE_TYPE_INVALID; } main_hash = hash(main_key); sub_hash = hash(sub_key); /* try to look for the main key from main key list */ while(mainkey) { if((mainkey->hash == main_hash) && !strcmp(mainkey->name, main_key)) { /* find the main key */ script_sub_key_t *subkey = mainkey->subkey; while(subkey) { if((subkey->hash == sub_hash) && !strcmp(subkey->name, sub_key)) { /* find the sub key */ *item = *subkey->value; return <span style="color:#ff0000;">subkey->type</span>; } subkey = subkey->next; } /* no sub key defined under the main key */ return SCIRPT_ITEM_VALUE_TYPE_INVALID; } mainkey = mainkey->next; } return SCIRPT_ITEM_VALUE_TYPE_INVALID; } EXPORT_SYMBOL(script_get_item);
/* * define script item management data * @name: item name, which is defined left of '=' * @value: value of the item * @type: type of the item, interge / string / gpio? * @hash: hash value of sub key name, for searching quickly * @next: pointer for list */ typedef struct { char name[SCRIPT_NAME_SIZE_MAX]; script_item_u *value; script_item_value_type_e type; int hash; void *next; } script_sub_key_t;
/* * define types of script item * @SCIRPT_ITEM_VALUE_TYPE_INVALID: invalid item type * @SCIRPT_ITEM_VALUE_TYPE_INT: integer item type * @SCIRPT_ITEM_VALUE_TYPE_STR: strint item type * @SCIRPT_ITEM_VALUE_TYPE_PIO: gpio item type */ typedef enum { SCIRPT_ITEM_VALUE_TYPE_INVALID = 0, SCIRPT_ITEM_VALUE_TYPE_INT, SCIRPT_ITEM_VALUE_TYPE_STR, SCIRPT_ITEM_VALUE_TYPE_PIO, } script_item_value_type_e;
现在我们知道怎查的了,但是存储主键信息和子健信息的链表是怎么来的呢?我们看上面代码里面绿色的那一行,一切的对比数据都来自g_script这个量g_script。现在可以推断,这个量里面保存了板子的管脚分配信息并且是和sys_config.fex相对应的。我们再本函数里面找这个变量,可以找到其定义:
static script_main_key_t *g_script;
/* * init script */ int __init script_init(void) { int i, j, count; script_origin_head_t *script_hdr = __va(SYS_CONFIG_MEMBASE); script_origin_main_key_t *origin_main; script_origin_sub_key_t *origin_sub; script_main_key_t *main_key; script_sub_key_t *sub_key, *tmp_sub, swap_sub; script_item_u *sub_val, *tmp_val, swap_val, *pval_temp; if (cfg_addr > PHYS_OFFSET) script_hdr = __va(cfg_addr); printk("%s enter!\n", __func__); if(!script_hdr) { printk(KERN_ERR "script buffer is NULL!\n"); return -1; } /* alloc memory for main keys */ <span style="color:#ff0000;"> g_script = SCRIPT_MALLOC(script_hdr->main_cnt*sizeof(script_main_key_t));</span> if(!g_script) { printk(KERN_ERR "try to alloc memory for main keys!\n"); return -1; } origin_main = &script_hdr->main_key; for(i=0; i<script_hdr->main_cnt; i++) { <span style="color:#ff0000;"> main_key = &g_script[i];</span> /* copy main key name */ strncpy(main_key->name, origin_main[i].name, SCRIPT_NAME_SIZE_MAX); /* calculate hash value */ main_key->hash = hash(main_key->name); if (origin_main[i].sub_cnt == 0) { /* this mainkey have no subkey, skip subkey initialize */ main_key->subkey = NULL; main_key->subkey_val = NULL; count = 0; goto next_mainkey; } /* allock memory for sub-keys */ main_key->subkey = SCRIPT_MALLOC(origin_main[i].sub_cnt*sizeof(script_sub_key_t)); main_key->subkey_val = SCRIPT_MALLOC(origin_main[i].sub_cnt*sizeof(script_item_u)); if(!main_key->subkey || !main_key->subkey_val) { printk(KERN_ERR "try alloc memory for sub keys failed!\n"); goto err_out; } sub_key = main_key->subkey; sub_val = main_key->subkey_val; origin_sub = (script_origin_sub_key_t *)((unsigned int)script_hdr + (origin_main[i].offset<<2)); /* process sub keys */ for(j=0; j<origin_main[i].sub_cnt; j++) { strncpy(sub_key[j].name, origin_sub[j].name, SCRIPT_NAME_SIZE_MAX); sub_key[j].hash = hash(sub_key[j].name); sub_key[j].type = (script_item_value_type_e)origin_sub[j].pattern.type; sub_key[j].value = &sub_val[j]; if(origin_sub[j].pattern.type == SCIRPT_PARSER_VALUE_TYPE_SINGLE_WORD) { sub_val[j].val = *(int *)((unsigned int)script_hdr + (origin_sub[j].offset<<2)); sub_key[j].type = SCIRPT_ITEM_VALUE_TYPE_INT; } else if(origin_sub[j].pattern.type == SCIRPT_PARSER_VALUE_TYPE_STRING) { sub_val[j].str = SCRIPT_MALLOC((origin_sub[j].pattern.cnt<<2) + 1); memcpy(sub_val[j].str, (char *)((unsigned int)script_hdr + (origin_sub[j].offset<<2)), origin_sub[j].pattern.cnt<<2); sub_key[j].type = SCIRPT_ITEM_VALUE_TYPE_STR; } else if(origin_sub[j].pattern.type == SCIRPT_PARSER_VALUE_TYPE_GPIO_WORD) { script_origin_gpio_t *origin_gpio = (script_origin_gpio_t *)((unsigned int)script_hdr + (origin_sub[j].offset<<2) - 32); u32 gpio_tmp = port_to_index(origin_gpio->port, origin_gpio->port_num); if(GPIO_INDEX_INVALID == gpio_tmp) printk(KERN_ERR "%s:%s->%s gpio cfg invalid, please check sys_config.fex!\n",__func__,main_key->name,sub_key[j].name); sub_val[j].gpio.gpio = gpio_tmp; sub_val[j].gpio.mul_sel = (u32)origin_gpio->mul_sel; sub_val[j].gpio.pull = (u32)origin_gpio->pull; sub_val[j].gpio.drv_level = (u32)origin_gpio->drv_level; sub_val[j].gpio.data = (u32)origin_gpio->data; sub_key[j].type = SCIRPT_ITEM_VALUE_TYPE_PIO; } else { sub_key[j].type = SCIRPT_ITEM_VALUE_TYPE_INVALID; } } /* process gpios */ tmp_sub = main_key->subkey; tmp_val = main_key->subkey_val; count = 0; for(j=0; j<origin_main[i].sub_cnt; j++) { if(sub_key[j].type == SCIRPT_ITEM_VALUE_TYPE_PIO) { /* swap sub key */ swap_sub = *tmp_sub; *tmp_sub = sub_key[j]; sub_key[j] = swap_sub; /* swap sub key value ptr */ pval_temp = tmp_sub->value; tmp_sub->value = sub_key[j].value; sub_key[j].value = pval_temp; /* swap key value */ swap_val = *tmp_val; *tmp_val = main_key->subkey_val[j]; main_key->subkey_val[j] = swap_val; tmp_sub++; tmp_val++; count++; } } /* process sub key link */ for(j=0; j<origin_main[i].sub_cnt-1; j++) { main_key->subkey[j].next = &main_key->subkey[j+1]; } /* set gpio infermation */ next_mainkey: main_key->gpio = main_key->subkey_val; main_key->gpio_cnt = count; <span style="color:#ff0000;">main_key->next = &g_script[i+1];</span> } g_script[script_hdr->main_cnt-1].next = 0; /* dump all the item */ //script_dump_mainkey(NULL); printk("%s exit!\n", __func__); return 0; err_out: /* script init failed, release resource */ printk(KERN_ERR "init sys_config script failed!\n"); if(g_script) { for(i=0; i<script_hdr->main_cnt; i++) { main_key = &g_script[i]; origin_sub = (script_origin_sub_key_t *)((unsigned int)script_hdr + (origin_main[i].offset<<2)); if(main_key->subkey_val) { for(j=0; j<origin_main[i].sub_cnt; j++) { if(main_key->subkey[j].type == SCIRPT_ITEM_VALUE_TYPE_STR) { if (main_key->subkey_val[j].str) { SCRIPT_FREE(main_key->subkey_val[j].str, (origin_sub[j].pattern.cnt<<2) + 1); } } } SCRIPT_FREE(main_key->subkey_val, sizeof(script_item_u)); } if(main_key->subkey) { SCRIPT_FREE(main_key->subkey, sizeof(script_sub_key_t)); } } SCRIPT_FREE(g_script, script_hdr->main_cnt*sizeof(script_main_key_t)); g_script = 0; } return -1; }
对g_script进行初始化之后,就能进行相应的查询了。
如果仔细看led.c两处调用的script_get_item,在led_probe函数里调用是这样的,
type = script_get_item("led_para", "<span style="color:#ff0000;">led_used</span>", &val);而在led_gpio里面调用是这样的
static int __devinit led_gpio(void) { int i = 0; char gpio_num[10]; for(i =1 ; i < 6; i++) { sprintf(gpio_num, "led_gpio%d", i); led_type= script_get_item("led_para", <span style="color:#ff0000;">gpio_num</span>, &led_val[i-1]); if(SCIRPT_ITEM_VALUE_TYPE_PIO != led_type) { printk("led_gpio type fail !"); // gpio_free(led_val[i-1].gpio.gpio); led_val[i-1].gpio.gpio = -1; continue; } if(0 != gpio_request(led_val[i-1].gpio.gpio, NULL)) { printk("led_gpio gpio_request fail !"); led_val[i-1].gpio.gpio = -1; continue; } if (0 != gpio_direction_output(led_val[i-1].gpio.gpio, 0)) { printk("led_gpio gpio_direction_output fail !"); // gpio_free(led_val[i-1].gpio.gpio); led_val[i-1].gpio.gpio = -1; continue; } } return 0; }
这里大概就是这么多,简单总结一下:在使用gpio驱动模型之前,先使用script查询是否可用。