由于之后的触摸屏驱动分析中使用到了GPIO子系统和i2c子系统,因此在分析触摸屏驱动之前我准备把这两个子系统进行简单分析。

之前我们使用GPIO引脚的方式并不是推荐的方式,当我们更改某一bit时,很有可能导致另外的bit值发生更改。而GPIO子系统进行了封装,确保每次只对一个GPIO引脚操作,而不会影响到别的GPIO引脚。

下面这段代码是我从驱动程序中摘出来的,它首先获取GPIO引脚,之后设置为输出模式。

 ret = gpio_request(EXYNOS4_GPL0(), "TP1_EN");
if (ret) {
printk(KERN_ERR "failed to request TP1_EN for I2C control\n");
//return err;
} gpio_direction_output(EXYNOS4_GPL0(), ); s3c_gpio_cfgpin(EXYNOS4_GPL0(), S3C_GPIO_OUTPUT);
gpio_free(EXYNOS4_GPL0()); mdelay();

一、gpio_direction_output()分析

gpio_direction_output()方便我们操作GPIO引脚,我们来看一下gpio_direction_output()函数会做些什么。

 int gpio_direction_output(unsigned gpio, int value)
{
unsigned long flags;
struct gpio_chip *chip; /* 不同单板对应不同chip,由芯片厂实现 */
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL; spin_lock_irqsave(&gpio_lock, flags);
/* 判断gpio是否可用 */
if (!gpio_is_valid(gpio))
goto fail;
chip = desc->chip;
if (!chip || !chip->set || !chip->direction_output)
goto fail;
gpio -= chip->base;
if (gpio >= chip->ngpio)
goto fail;
status = gpio_ensure_requested(desc, gpio);
if (status < )
goto fail; /* now we know the gpio is valid and chip won't vanish */ spin_unlock_irqrestore(&gpio_lock, flags); might_sleep_if(chip->can_sleep); if (status) {
status = chip->request(chip, gpio);
if (status < ) {
pr_debug("GPIO-%d: chip request fail, %d\n",
chip->base + gpio, status);
/* and it's not available to anyone else ...
* gpio_request() is the fully clean solution.
*/
goto lose;
}
}
/* 调用chip的direction_output()函数 */
status = chip->direction_output(chip, gpio, value);
if (status == )
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(chip->base + gpio, , value);
trace_gpio_direction(chip->base + gpio, , status);
lose:
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
pr_debug("%s: gpio-%d status %d\n",
__func__, gpio, status);
return status;
}
EXPORT_SYMBOL_GPL(gpio_direction_output);

通过代码第4行struct gpio_chip,我们可以推断出此结构体是内核提供给各个板商的,用于其实现不同GPIO引脚操作。

在drivers/gpio/目录下,有跟平台相关的文件,这些文件就是由厂商提供的GPIO引脚操作函数。在此我以gpio-omap.c为例进行讨论

我们先来查看probe()函数:

 static int __devinit omap_gpio_probe(struct platform_device *pdev)
{
static int gpio_init_done;
struct omap_gpio_platform_data *pdata;
struct resource *res;
int id;
struct gpio_bank *bank;
...
pdata = pdev->dev.platform_data;
...
ret = init_gpio_info(pdev);
...
id = pdev->id;
bank = &gpio_bank[id]; res = platform_get_resource(pdev, IORESOURCE_IRQ, );
...
bank->irq = res->start;
bank->virtual_irq_start = pdata->virtual_irq_start;
bank->method = pdata->bank_type;
bank->dev = &pdev->dev;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
bank_width = pdata->bank_width; spin_lock_init(&bank->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, ); bank->base = ioremap(res->start, resource_size(res));
...
omap_gpio_mod_init(bank, id);
omap_gpio_chip_init(bank);
omap_gpio_show_rev(bank); if (!gpio_init_done)
gpio_init_done = ; return ;
}

此函数首先填充了struct gpio_bank,之后使用ioremap()映射,最后调用omap_gpio_chip_init()初始化并注册chip。

 static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
{
int j;
static int gpio;
/* 填充chip成员函数 */
bank->mod_usage = ;
bank->chip.request = omap_gpio_request;
bank->chip.free = omap_gpio_free;
bank->chip.direction_input = gpio_input;
bank->chip.get = gpio_get;
bank->chip.direction_output = gpio_output;
bank->chip.set_debounce = gpio_debounce;
bank->chip.set = gpio_set;
bank->chip.to_irq = gpio_2irq;
if (bank_is_mpuio(bank)) {
bank->chip.label = "mpuio";
#ifdef CONFIG_ARCH_OMAP16XX
bank->chip.dev = &omap_mpuio_device.dev;
#endif
bank->chip.base = OMAP_MPUIO();
} else {
bank->chip.label = "gpio";
bank->chip.base = gpio;
gpio += bank_width;
}
bank->chip.ngpio = bank_width; gpiochip_add(&bank->chip);
...
irq_set_chained_handler(bank->irq, gpio_irq_handler);
irq_set_handler_data(bank->irq, bank);
}

接下来我们来查看chip是如何注册到内核中的:

 int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = ;
unsigned id;
int base = chip->base;
/* 判断gpio是否可用 */
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - )) && base >= ) {
...
}
...
if (status == ) {
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip; /* 放入全局变量中 */ gpio_desc[id].flags = !chip->direction_input
? ( << FLAG_IS_OUT)
: ;
}
}
...
unlock:
spin_unlock_irqrestore(&gpio_lock, flags); status = gpiochip_export(chip);
...
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);

重新回到gpio_direction_output()函数,我们继续向下分析。代码第40行:chip->direction_output(chip, gpio, value);最终会调用chip的direction_output()函数。我们来看一下gpio-omap.c中的direction_output()函数是怎么实现的:

 static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
void __iomem *reg = bank->base;
u32 l; switch (bank->method) {
#ifdef CONFIG_ARCH_OMAP1
case METHOD_MPUIO:
reg += OMAP_MPUIO_IO_CNTL / bank->stride;
break;
#endif
#ifdef CONFIG_ARCH_OMAP15XX
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_DIR_CONTROL;
break;
#endif
#ifdef CONFIG_ARCH_OMAP16XX
case METHOD_GPIO_1610:
reg += OMAP1610_GPIO_DIRECTION;
break;
#endif
#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
case METHOD_GPIO_7XX:
reg += OMAP7XX_GPIO_DIR_CONTROL;
break;
#endif
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_OE;
break;
#endif
#if defined(CONFIG_ARCH_OMAP4)
case METHOD_GPIO_44XX:
reg += OMAP4_GPIO_OE;
break;
#endif
default:
WARN_ON();
return;
}
l = __raw_readl(reg);
if (is_input)
l |= << gpio;
else
l &= ~( << gpio);
__raw_writel(l, reg);
}

最终它会通过readl()和writel()等操作实现对寄存器的读写操作,对于这两个函数,读者可以参考:Linux驱动函数解读第四节。

二、内核中GPIO的使用函数

1. 检测GPIO引脚是否有效

bool gpio_is_valid(int number)

2. 获取或释放GPIO引脚。如果GPIO引脚正在使用,则无法获取。原理类似于原子操作

int gpio_request(unsigned gpio, const char *label)

void gpio_free(unsigned gpio)

3. 设置GPIO引脚为输入或输出

int gpio_direction_input(unsigned gpio)    /* 输入 */

int gpio_direction_output(unsigned gpio, int value)    /* 输出 */

4. 获取或设置GPIO引脚的值

int gpio_get_value(unsigned gpio)

void gpio_set_value(unsigned gpio, int value)

5. 设置GPIO引脚为中断引脚

int gpio_to_irq(unsigned gpio)    /* 返回值为request_irq()使用的中断号 */

6. 导出或撤销GPIO引脚到用户空间

int gpio_export(unsigned gpio, bool direction_may_change)
/* 参数direction_may_change表示用户程序是否可以修改GPIO的方向,
如果可以,则参数direction_may_change为真 */ void gpio_unexport(unsigned gpio) /* 撤销GPIO */

下面我们可以使用GPIO子系统优化第二章的LED程序。

三、LED驱动优化

led源代码:

 #include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <plat/gpio-cfg.h> /* 定义文件内私有结构体 */
struct led_device {
struct cdev cdev;
int stat; /* 用于保存LED状态,0为灭,1为亮 */
}; static int g_major;
module_param(g_major, int, S_IRUGO); static struct led_device* dev;
static struct class* scls;
static struct device* sdev; /* LED write()函数 */
static ssize_t led_write(struct file *filep, const char __user * buf, size_t len, loff_t *ppos)
{
struct led_device *dev = filep->private_data; if (copy_from_user(&(dev->stat), buf, ))
return -EFAULT; if (dev->stat == )
gpio_set_value(EXYNOS4_GPK1(), );
else
gpio_set_value(EXYNOS4_GPK1(), ); return ;
} /* LED open()函数 */
static int led_open(struct inode *inodep, struct file *filep)
{
struct led_device *dev;
int ret = -; dev = container_of(inodep->i_cdev, struct led_device, cdev);
// 放入私有数据中
filep->private_data = dev; // 设为输出引脚,灭灯
ret = gpio_request(EXYNOS4_GPK1(), "LED3");
if (ret)
printk(KERN_ERR "failed to request LED3\n"); gpio_direction_output(EXYNOS4_GPK1(), );
s3c_gpio_cfgpin(EXYNOS4_GPK1(), S3C_GPIO_OUTPUT); gpio_set_value(EXYNOS4_GPK1(), ); return ;
} static int led_close(struct inode *inodep, struct file *filep)
{
gpio_free(EXYNOS4_GPK1()); return ;
} /* 把定义的函数接口集合起来,方便系统调用 */
static const struct file_operations led_fops = {
.write = led_write,
.open = led_open,
.release = led_close,
}; static int __init led_init(void)
{
int ret;
dev_t devt; /* 1. 申请设备号 */
if (g_major) {
devt = MKDEV(g_major, );
ret = register_chrdev_region(devt, , "led");
}
else {
ret = alloc_chrdev_region(&devt, , , "led");
g_major = MAJOR(devt);
}
if (ret)
return ret; /* 2. 申请文件内私有结构体 */
dev = kzalloc(sizeof(struct led_device), GFP_KERNEL);
if (dev == NULL) {
ret = -ENOMEM;
goto fail_malloc;
} /* 3. 注册字符设备驱动 */
cdev_init(&dev->cdev, &led_fops); /* 初始化cdev并链接file_operations和cdev */
ret = cdev_add(&dev->cdev, devt, ); /* 注册cdev */
if (ret)
return ret; /* 4. 创建类设备,insmod后会生成/dev/led设备文件 */
scls = class_create(THIS_MODULE, "led");
sdev = device_create(scls, NULL, devt, NULL, "led"); return ; fail_malloc:
unregister_chrdev_region(devt, ); return ret;
} static void __exit led_exit(void)
{
/* 镜像注销 */
dev_t devt = MKDEV(g_major, ); device_destroy(scls, devt);
class_destroy(scls); cdev_del(&(dev->cdev));
kfree(dev); unregister_chrdev_region(devt, );
} /* 声明段属性 */
module_init(led_init);
module_exit(led_exit); MODULE_LICENSE("GPL");

Makefile:

 KERN_DIR = /work/itop4412/tools/linux-3.5

 all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += led.o

测试文件:

 #include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h> int main(int argc, char** argv)
{
if (argc != ) {
printf("Usage: \n");
printf("%s <on|off>\n", argv[]);
return -;
} int fd;
fd = open("/dev/led", O_RDWR);
if (fd < ) {
printf("can't open /dev/led\n");
return -;
} char stat;
if ( == strcmp(argv[], "off")) {
stat = ;
write(fd, &stat, );
} else {
stat = ;
write(fd, &stat, );
}
close(fd); return ;
}

下一章  十四、i2c子系统

十三、GPIO子系统的更多相关文章

  1. Linux内核驱动之GPIO子系统API接口概述

    1.前言 在嵌入式Linux开发中,对嵌入式SoC中的GPIO进行控制非常重要,Linux内核中提供了GPIO子系统,驱动开发者在驱动代码中使用GPIO子系统提供的API函数,便可以达到对GPIO控制 ...

  2. 【linux】gpio子系统

    目录 前言 linux子系统 gpio子系统 gpio子系统实战-系统调用 前言 目前不涉及驱动源码 参考链接 linux子系统 在 Linux 系统中 绝大多数硬件设备都有非常成熟的驱动框架 驱动工 ...

  3. Linux内核驱动之GPIO子系统(一)GPIO的使用

    转自:http://blog.csdn.net/mirkerson/article/details/8464290 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt , ...

  4. Linux内核驱动之GPIO子系统(一)GPIO的使用【转】

    转自:http://blog.csdn.net/tommy_wxie/article/details/9427047 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt  ...

  5. Linux GPIO子系统

    一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt ,dma,timer一样)驱动程序,应用程序都能够通过相应的接口使用gpio,gpio使用0-MAX_INT之间的整数 ...

  6. gpio子系统和pinctrl子系统(下)

    情景分析 打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410 Pinctrl-samsung.c为例看看pinctrl输入参数的初始化过程(最开始的 ...

  7. gpio子系统和pinctrl子系统(中)

    pinctrl子系统核心实现分析 pinctrl子系统的内容在drivers/pinctrl文件夹下,主要文件有(建议先看看pinctrl内核文档Documentation/pinctrl.txt): ...

  8. gpio子系统和pinctrl子系统(上)

    前言 随着内核的发展,linux驱动框架在不断的变化.很早很早以前,出现了gpio子系统,后来又出现了pinctrl子系统.在网上很难看到一篇讲解这类子系统的文章.就拿gpio操作来说吧,很多时候都是 ...

  9. Linux驱动之GPIO子系统和pinctrl子系统

    前期知识   1.如何编写一个简单的Linux驱动(一)--驱动的基本框架   2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations   3.如何编写一个简单的Lin ...

随机推荐

  1. 内存分析工具 MAT 的使用【转】

    转自:http://blog.csdn.net/aaa2832/article/details/19419679/ 1 内存泄漏的排查方法 Dalvik Debug Monitor Server (D ...

  2. IO流——常用IO流详解

    1:字节流 字节流:用于处理以字节为单位的二进制文件(如音乐,图片等) InputStream 是抽象类 它的对应子类FileInputStream可以被实例化 构造方法: FileInputStre ...

  3. 解决GitHub上传大于100M文件失败

    目录 问题 解决 参考 问题 push的时候遇到以下问题: remote: error: GH001: Large files detected. You may want to try Git La ...

  4. 转: mysql的取整函数

    一.ROUND()函数用法 ROUND(X) -- 表示将值 X 四舍五入为整数,无小数位    ROUND(X,D) -- 表示将值 X 四舍五入为小数点后 D 位的数值,D为小数点后小数位数.若要 ...

  5. bad object refs/remotes/origin/HEAD

    How to handle git gc fatal: bad object refs/remotes/origin/HEAD error: failed to run repack I random ...

  6. 通AI启示录,从一篇数学物理基础论文说起 原创: 关注前沿科技 量子位 今天 允中 发自 凹非寺

    通AI启示录,从一篇数学物理基础论文说起 原创: 关注前沿科技 量子位 今天 允中 发自 凹非寺

  7. 创建Bitmap之Bitmap静态方法使用示例

    package com.loaderman.customviewdemo; import android.app.Activity; import android.content.Intent; im ...

  8. Apache配置优化之开启GZip传输

    1.确保apache已经编译的模块里有mod_deflate模块 2.确保apache的配置文件里引入了压缩的模块 3.确保要开启Gzip压缩的虚拟主机配置里有如下配置,并重启apache服务:如果要 ...

  9. python安装pip方法

    1.先下载pip安装脚本: https://bootstrap.pypa.io/get-pip.py 2.执行python get-pip.py 3.安装完成.

  10. 数据结构与算法学习(二)——Master公式及其应用

    本篇文章涉及公式,由于博客园没有很好的支持,建议移步我的CSDN博客和简书进行阅读. 1. Master公式是什么? 我们在解决算法问题时,经常会用到递归.递归在较难理解的同时,其算法的复杂度也不是很 ...