Android下led控制(下)--Linux驱动部分--script与gpio(全志)

前面写了三篇关于全志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>


简单解释一下这几项,里面的led_used和下面几个led_gpioN(N可以是1、2、3、4、5)称为子健。后面的值是这样的格式:

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;


这是一个枚举数据类型,里面包含了IO口的类型。我们知道这个函数是来查询子健的类型type的,那么是怎么查询的呢?先获取主键的哈希码,然后再主键的列表中查询哈希码和主键名称,只有两者都能够匹配才继续去查询它下面的子健如果子健的哈希码和子健的名称都匹配则返回自己的类型type。这里说一下, 这里的主键和子健是和上面看的sys_config.fex里对应的。

现在我们知道怎查的了,但是存储主键信息和子健信息的链表是怎么来的呢?我们看上面代码里面绿色的那一行,一切的对比数据都来自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查询是否可用。













  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值