前面写了三篇关于全志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");
看上面标注的地方(红字绿底),这里引用了一个script_get_item函数,我们可以查看全志的资料可以知道这个函数功能是获取配置脚本中某一项子健。通过该说明文档,我们可以知道Script这个功能里的函数是为了解析系统管脚配置脚本sys_config.fex中的内容。
通俗来说吧,就是开发板的管脚配置会写在一个叫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);
这个函数的作用是返回的是子健的type,我们来看一下子健的定义类型:
/*
* 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;
这个一个子健的数据结构体,包括名称,值,模式,哈希码,下一个节点指针。再看一下type的定义:
/*
* 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;
这个是script_main_key_t类型的指针。继续寻找这个,我们可以在这个文件夹下的 __init script_init(void)函数,在这个函数对变量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;
}
这俩有什么不一样,是子健不一样,从前面的博文知道,led_probe函数是platform机制的探测函数,也是初始化函数,这里先查led的子健led_used是否可用,然后再查具体gpio端口是否可用。也就是先查使能,如果使能则继续查具体g皮哦端口能否可用。
这里大概就是这么多,简单总结一下:在使用gpio驱动模型之前,先使用script查询是否可用。