Android下led控制(中)--Linux部分

来源:互联网 发布:pc手机淘宝链接转换 编辑:程序博客网 时间:2024/05/20 05:06

首先声明一下我的实验平台,是全志CQA83T,成都启划信息的板子。上面一篇博客介绍了Android下led控制的Android部分。这一篇我想说说Linux下的部分,从上一篇我们可以知道,jni通过打开led设备/dev/led,进而使用ioctl函数来控制led的亮和灭和蜂鸣器的发声。那么在Linux下面,为什么会接受ioctl控制,ioctl函数是怎么控制led的?当然,其实到这个地步,已经和Android完全没有关系了,纯属于Linux驱动的事情了。

最初,我以为板子上的led驱动是动态驱动模块(*.ko),在系统启动后进行加载的,可是当我查看系统配置文件的时候才发现,完全不是这个样子的。我们看一下Android代码里初始化文件对led的配置,在CQA83TAndroid_v2.1.0_bv3/android/device/softwinner/octopus-f1/init.sun8i.rc里面,如下图


这里仅仅是更改设备的权限,这里也说明的当Android部分启动时,led的驱动已经加载到Linux内核。如果还不清楚,来看一下,初始化文件对其他设备的配置。如下图


到这个地方我们能说明,led驱动是在内核中加载完成的。那么它究竟是在何时加载的?这个问题我们先不去探究。下面我们先看一下led驱动的源文件。

我们知道,led驱动属于字符设备,那么其源码位置在Linux内核源码的drivers/char/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_ON1#define LED_IOCTL_SET_OFF0static script_item_uled_val[5];static script_item_value_type_eled_type;static struct semaphore lock;//led_openstatic int led_open(struct inode *inode, struct file *file){if (!down_trylock(&lock))return 0;elsereturn -EBUSY;}//led_closestatic int  led_close(struct inode *inode, struct file *file){up(&lock);return 0;}//led_ioctlstatic 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;}//led_gpiostatic 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", 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;}//file_operationsstatic struct file_operations leds_ops = {.owner= THIS_MODULE,.open= led_open,.release= led_close, .unlocked_ioctl= led_ioctl,};//miscdevicestatic struct miscdevice leds_dev = {.minor = MISC_DYNAMIC_MINOR,.name = "led",.fops = &leds_ops,};//led_removestatic int __devexit led_remove(struct platform_device *pdev){return 0;}//led_probestatic int __devinit led_probe(struct platform_device *pdev){int led_used;script_item_uval;script_item_value_type_e  type;int err;    printk("led_para!\n");type = script_get_item("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;}//platform_devicestruct platform_device led_device = {.name= "led",};//platform_driverstatic struct platform_driver led_driver = {.probe= led_probe,.remove= __devexit_p(led_remove),.driver= {.name= "led",.owner= THIS_MODULE,},};//led_initstatic 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;}//led_exitstatic 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");

前面我们已经知道,jni是通过ioctl来控制led和蜂鸣器的动作,那么源码里的led_ioctl函数就是与此相对应的。那我们重点来看一下led_ioctl函数:

//led_ioctlstatic 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;}

函数内前两行是定义了变量n,并且把星灿arg赋值给n,这样n就代表led的标号。下面就是一个switch-case语句了,条件是形参cmd的值,用到LED_IOCTL_SET_ON和LED_IOCTL_SET_OFF两个宏,这两个宏是在源文件开头定义的,LED_IOCTL_SET_ON的值为1,LED_IOCTL_SET_OFF的值为0。很显然,这个cmd是用用来标示电路中的led是灭还亮的,如果cmd的值等于LED_IOCTL_SET_ON则使led亮,如果cmd的值等于LED_IOCTL_SET_OFF则使led灭。这里调用了,__gpio_set_value这个函数,我们先不分析这个函数,先说为什么写1 led亮,而写0 led灭。这个问题要从硬件电路来说明,我们来看一下,led和蜂鸣器的电路,如下图:


这样就一目了然,它们是共地的,只有IO口输出高电平时,led才能亮,蜂鸣器才能响。

下面我们看一下上面说的__gpio_set_value这个函数,这个函数的实现是在drivers/gpio/gpiolib.c这个文件里面。我们看一下这个函数的实现,

/** * __gpio_set_value() - assign a gpio's value * @gpio: gpio whose value will be assigned * @value: value to assign * Context: any * * This is used directly or indirectly to implement gpio_set_value(). * It invokes the associated gpio_chip.set() method. */void __gpio_set_value(unsigned gpio, int value){struct gpio_chip*chip;chip = gpio_to_chip(gpio);/* Should be using gpio_set_value_cansleep() */WARN_ON(chip->can_sleep);trace_gpio_value(gpio, 0, value);if (test_bit(FLAG_OPEN_DRAIN,  &gpio_desc[gpio].flags))_gpio_set_open_drain_value(gpio, chip, value);else if (test_bit(FLAG_OPEN_SOURCE,  &gpio_desc[gpio].flags))_gpio_set_open_source_value(gpio, chip, value);elsechip->set(chip, gpio - chip->base, value);}EXPORT_SYMBOL_GPL(__gpio_set_value);

我们逐行分析这个函数:

struct gpio_chip *chip;

对于结构体gpio_chip牵涉到了Linux gpio驱动模型,这里简单说一下gpio驱动模型:

GPIO是嵌入式系统最简单、最常用的资源了,比如点亮LED,控制蜂鸣器,输出高低电平,检测按键,等等。GPIO分输入和输出,在davinci linux中,有关GPIO的最底层的寄存器驱动,\arch\arm\mach-davinci目录下的gpio.c,这个是寄存器级的驱动,搞过单片机MCU的朋友应该比较熟悉寄存器级的驱动。

GPIO的驱动主要就是读取GPIO口的状态,或者设置GPIO口的状态。就是这么简单,但是为了能够写好的这个驱动,在LINUX上作了一些软件上的分层。为了让其它驱动可以方便的操作到GPIO,在LINUX里实现了对GPIO操作的统一接口,这个接口实则上就是GPIO驱动的框架,具体的实现文件为gpiolib.c在配置内核的时候,我们必须使用CONFIG_GENERIC_GPIO这个宏来支持GPIO驱动。

    GPIO是与硬件体系密切相关的,linux提供一个模型来让驱动统一处理GPIO,即各个板卡都有实现自己的gpio_chip控制模块:request, free, input,output, get,set,irq...然后把控制模块注册到内核中,这时会改变全局gpio数组:gpio_desc[]. 当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。gpio实现为一组可用的 gpio_chip, 由驱动传入对应 gpio的全局序号去 request, dataout ,datain, free. 这时会调用gpio_chip中具体的实现。

    gpio是一组可控件的脚,由多个寄存器同时控制。通过设置对应的寄存器可以达到设置GPIO口对应状态与功能。数据状态,输入输出方向,清零,中断(那个边沿触发), 一般是一组(bank)一组的。寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()

(上面引用自:http://blog.csdn.net/bytxl/article/details/50337091)大家看这篇博文了解更详细的GPIO驱动模型。

结构体gpio_chip的定义在include/asm-generic/gpio.h文件中,具体内容是:

struct gpio_chip {const char*label;struct device*dev;struct module*owner;int(*request)(struct gpio_chip *chip,unsigned offset);void(*free)(struct gpio_chip *chip,unsigned offset);int(*direction_input)(struct gpio_chip *chip,unsigned offset);int(*get)(struct gpio_chip *chip,unsigned offset);int(*direction_output)(struct gpio_chip *chip,unsigned offset, int value);int(*set_debounce)(struct gpio_chip *chip,unsigned offset, unsigned debounce);void(*set)(struct gpio_chip *chip,unsigned offset, int value);int(*to_irq)(struct gpio_chip *chip,unsigned offset);void(*dbg_show)(struct seq_file *s,struct gpio_chip *chip);intbase;u16ngpio;const char*const *names;unsignedcan_sleep:1;unsignedexported:1;#if defined(CONFIG_OF_GPIO)/* * If CONFIG_OF is enabled, then all GPIO controllers described in the * device tree automatically may have an OF translation */struct device_node *of_node;int of_gpio_n_cells;int (*of_xlate)(struct gpio_chip *gc,        const struct of_phandle_args *gpiospec, u32 *flags);#endif#ifdef CONFIG_PINCTRL/* * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally * describe the actual pin range which they serve in an SoC. This * information would be used by pinctrl subsystem to configure * corresponding pins for gpio usage. */struct list_head pin_ranges;#endif};

下面来看:

chip = gpio_to_chip(gpio);

在相同文件下的函数实现为:

/* caller holds gpio_lock *OR* gpio is marked as requested */struct gpio_chip *gpio_to_chip(unsigned gpio){return gpio_desc[gpio].chip;}
这个函数很简单,我们来看gpio描述结构体gpio_desc,该结构体在同文件下,内容如下:

struct gpio_desc {struct gpio_chip*chip;unsigned longflags;<span style="white-space:pre"></span>/* flag symbols are bit numbers */<span style="white-space:pre"></span>#define FLAG_REQUESTED0<span style="white-space:pre"></span>#define FLAG_IS_OUT1<span style="white-space:pre"></span>#define FLAG_RESERVED2<span style="white-space:pre"></span>#define FLAG_EXPORT3/* protected by sysfs_lock */<span style="white-space:pre"></span>#define FLAG_SYSFS4/* exported via /sys/class/gpio/control */<span style="white-space:pre"></span>#define FLAG_TRIG_FALL5/* trigger on falling edge */<span style="white-space:pre"></span>#define FLAG_TRIG_RISE6/* trigger on rising edge */<span style="white-space:pre"></span>#define FLAG_ACTIVE_LOW7/* sysfs value has active low */<span style="white-space:pre"></span>#define FLAG_OPEN_DRAIN8/* Gpio is open drain type */<span style="white-space:pre"></span>#define FLAG_OPEN_SOURCE 9/* Gpio is open source type */<span style="white-space:pre"></span>#define ID_SHIFT16/* add new flags before this one */<span style="white-space:pre"></span>#define GPIO_FLAGS_MASK((1 << ID_SHIFT) - 1)<span style="white-space:pre"></span>#define GPIO_TRIGGER_MASK(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))<span style="white-space:pre"></span>#ifdef CONFIG_DEBUG_FSconst char*label;<span style="white-space:pre"></span>#endif};static struct gpio_desc <span style="color:#ff0000;">gpio_desc</span>[ARCH_NR_GPIOS];

继续看:

WARN_ON(chip->can_sleep); 

这句是设置gpio值,gpio可休眠,同gpio_set_value_cansleep()函数。

trace_gpio_value(gpio, 0, value);

根据查资料,对gpio的值添加追踪事件,我推测是应该是获取你要操作的gpio的当前状态。(没有查到确切资料,如果哪位知道,请共享一下)


if (test_bit(FLAG_OPEN_DRAIN,  &gpio_desc[gpio].flags))
_gpio_set_open_drain_value(gpio, chip, value);
else if (test_bit(FLAG_OPEN_SOURCE,  &gpio_desc[gpio].flags))
_gpio_set_open_source_value(gpio, chip, value);
else
chip->set(chip, gpio - chip->base, value);

这三句就是给IO口写值了,这里牵涉到了硬件上GPIO控制器的GPIO的控制模式,test_bit函数是用来做位测试,test_bit(FLAG_OPEN_DRAIN,  &gpio_desc[gpio].flags),这里就是要测试gpio_desc[gpio].flags)的第FLAG_OPEN_DRAIN位是否为1。意思就是,该GPIO控制器是否支持开漏控制方式。

我们再进入到_gpio_set_open_drain_value(gpio, chip, value)和_gpio_set_open_source_value(gpio, chip, value)这个函数:

/* *  _gpio_set_open_drain_value() - Set the open drain gpio's value. * @gpio: Gpio whose state need to be set. * @chip: Gpio chip. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */static void _gpio_set_open_drain_value(unsigned gpio,struct gpio_chip *chip, int value){int err = 0;if (value) {err = chip->direction_input(chip, gpio - chip->base);if (!err)clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);} else {err = chip->direction_output(chip, gpio - chip->base, 0);if (!err)set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);}trace_gpio_direction(gpio, value, err);if (err < 0)pr_err("%s: Error in set_value for open drain gpio%d err %d\n",__func__, gpio, err);}/* *  _gpio_set_open_source() - Set the open source gpio's value. * @gpio: Gpio whose state need to be set. * @chip: Gpio chip. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */static void _gpio_set_open_source_value(unsigned gpio,struct gpio_chip *chip, int value){int err = 0;if (value) {err = chip->direction_output(chip, gpio - chip->base, 1);if (!err)set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);} else {err = chip->direction_input(chip, gpio - chip->base);if (!err)clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);}trace_gpio_direction(gpio, !value, err);if (err < 0)pr_err("%s: Error in set_value for open source gpio%d err %d\n",__func__, gpio, err);}

从这两个函数可以看出,到这里基本上都是直接对GPIO的直接操作了,包括输入输出控制。细心点可以发现,如果我们假设,value等于1,也就是我们打算让GPIO口输出高,两个函数里使用的函数是不一样的,_gpio_set_open_drain_value(gpio, chip, value)里面使用的是chip->direction_input(chip, gpio - chip->base),而_gpio_set_open_source_value(gpio, chip, value)里面使用的是chip->direction_output(chip, gpio - chip->base, 1),这里不怎么看的懂,我的直观感觉是和开漏电路有关系,希望知道的朋友能够共享。

到这里,基本上是把Linux下GPIO驱动模型马马虎虎的了解了一点点,下载就一个感觉Linux好复杂。下面还是回到我们的驱动函数led.c里面,从里面不难发现,这个驱动使用了Linux的platform机制。

关于plantform先不在这里分析,专门写文章来分析。先给大家推荐几篇博文:

http://blog.csdn.net/yuanlulu/article/details/6184266

http://blog.csdn.net/weiqing1981127/article/details/8245665

http://blog.csdn.net/ufo714/article/details/8595021
http://blog.csdn.net/liuhaoyutz/article/details/15504127

http://blog.csdn.net/yaozhenguo2006/article/details/6784895


在此非常感谢大神们的分享。







0 0
原创粉丝点击