概述
首先声明一下我的实验平台,是全志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_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;
//led_open
static int led_open(struct inode *inode, struct file *file)
{
if (!down_trylock(&lock))
return 0;
else
return -EBUSY;
}
//led_close
static int led_close(struct inode *inode, struct file *file)
{
up(&lock);
return 0;
}
//led_ioctl
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;
}
//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", 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_operations
static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.unlocked_ioctl = led_ioctl,
};
//miscdevice
static struct miscdevice leds_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "led",
.fops = &leds_ops,
};
//led_remove
static int __devexit led_remove(struct platform_device *pdev)
{
return 0;
}
//led_probe
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 = script_get_item("led_para", "led_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
printk("%s script_get_item "led_para" led_used = %dn",
__FUNCTION__, val.val);
return -1;
}
led_used = val.val;
printk("%s script_get_item "led_para" led_used = %dn",
__FUNCTION__, val.val);
if(!led_used) {
printk("%s led_used is not used in config, led_used=%dn", __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_device
struct platform_device led_device = {
.name = "led",
};
//platform_driver
static struct platform_driver led_driver = {
.probe = led_probe,
.remove = __devexit_p(led_remove),
.driver = {
.name = "led",
.owner = THIS_MODULE,
},
};
//led_init
static int __init led_init(void)
{
if (platform_device_register(&led_device)) {
printk("%s: register gpio device failedn", __func__);
}
if (platform_driver_register(&led_driver)) {
printk("%s: register gpio driver failedn", __func__);
}
return 0;
}
//led_exit
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");
前面我们已经知道,jni是通过ioctl来控制led和蜂鸣器的动作,那么源码里的led_ioctl函数就是与此相对应的。那我们重点来看一下led_ioctl函数:
//led_ioctl
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;
}
函数内前两行是定义了变量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);
else
chip->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的最底层的寄存器驱动,archarmmach-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);
int base;
u16 ngpio;
const char *const *names;
unsigned can_sleep:1;
unsigned exported: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 long flags;
<span style="white-space:pre"> </span>/* flag symbols are bit numbers */
<span style="white-space:pre"> </span>#define FLAG_REQUESTED 0
<span style="white-space:pre"> </span>#define FLAG_IS_OUT 1
<span style="white-space:pre"> </span>#define FLAG_RESERVED 2
<span style="white-space:pre"> </span>#define FLAG_EXPORT 3 /* protected by sysfs_lock */
<span style="white-space:pre"> </span>#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
<span style="white-space:pre"> </span>#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
<span style="white-space:pre"> </span>#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
<span style="white-space:pre"> </span>#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
<span style="white-space:pre"> </span>#define FLAG_OPEN_DRAIN 8 /* 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_SHIFT 16 /* 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_FS
const 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 %dn",
__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 %dn",
__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
在此非常感谢大神们的分享。
最后
以上就是俊逸香水为你收集整理的Android下led控制(中)--Linux部分的全部内容,希望文章能够帮你解决Android下led控制(中)--Linux部分所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复