由于之后的触摸屏驱动分析中使用到了GPIO子系统和i2c子系统,因此在分析触摸屏驱动之前我准备把这两个子系统进行简单分析。
之前我们使用GPIO引脚的方式并不是推荐的方式,当我们更改某一bit时,很有可能导致另外的bit值发生更改。而GPIO子系统进行了封装,确保每次只对一个GPIO引脚操作,而不会影响到别的GPIO引脚。
下面这段代码是我从驱动程序中摘出来的,它首先获取GPIO引脚,之后设置为输出模式。
1 ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN"); 2 if (ret) { 3 printk(KERN_ERR "failed to request TP1_EN for I2C control\n"); 4 //return err; 5 } 6 7 gpio_direction_output(EXYNOS4_GPL0(2), 1); 8 9 s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT); 10 gpio_free(EXYNOS4_GPL0(2)); 11 12 mdelay(5);
一、gpio_direction_output()分析
gpio_direction_output()方便我们操作GPIO引脚,我们来看一下gpio_direction_output()函数会做些什么。
1 int gpio_direction_output(unsigned gpio, int value) 2 { 3 unsigned long flags; 4 struct gpio_chip *chip; /* 不同单板对应不同chip,由芯片厂实现 */ 5 struct gpio_desc *desc = &gpio_desc[gpio]; 6 int status = -EINVAL; 7 8 spin_lock_irqsave(&gpio_lock, flags); 9 /* 判断gpio是否可用 */ 10 if (!gpio_is_valid(gpio)) 11 goto fail; 12 chip = desc->chip; 13 if (!chip || !chip->set || !chip->direction_output) 14 goto fail; 15 gpio -= chip->base; 16 if (gpio >= chip->ngpio) 17 goto fail; 18 status = gpio_ensure_requested(desc, gpio); 19 if (status < 0) 20 goto fail; 21 22 /* now we know the gpio is valid and chip won't vanish */ 23 24 spin_unlock_irqrestore(&gpio_lock, flags); 25 26 might_sleep_if(chip->can_sleep); 27 28 if (status) { 29 status = chip->request(chip, gpio); 30 if (status < 0) { 31 pr_debug("GPIO-%d: chip request fail, %d\n", 32 chip->base + gpio, status); 33 /* and it's not available to anyone else ... 34 * gpio_request() is the fully clean solution. 35 */ 36 goto lose; 37 } 38 } 39 /* 调用chip的direction_output()函数 */ 40 status = chip->direction_output(chip, gpio, value); 41 if (status == 0) 42 set_bit(FLAG_IS_OUT, &desc->flags); 43 trace_gpio_value(chip->base + gpio, 0, value); 44 trace_gpio_direction(chip->base + gpio, 0, status); 45 lose: 46 return status; 47 fail: 48 spin_unlock_irqrestore(&gpio_lock, flags); 49 if (status) 50 pr_debug("%s: gpio-%d status %d\n", 51 __func__, gpio, status); 52 return status; 53 } 54 EXPORT_SYMBOL_GPL(gpio_direction_output);
通过代码第4行struct gpio_chip,我们可以推断出此结构体是内核提供给各个板商的,用于其实现不同GPIO引脚操作。
在drivers/gpio/目录下,有跟平台相关的文件,这些文件就是由厂商提供的GPIO引脚操作函数。在此我以gpio-omap.c为例进行讨论
我们先来查看probe()函数:
1 static int __devinit omap_gpio_probe(struct platform_device *pdev) 2 { 3 static int gpio_init_done; 4 struct omap_gpio_platform_data *pdata; 5 struct resource *res; 6 int id; 7 struct gpio_bank *bank; 8 ... 9 pdata = pdev->dev.platform_data; 10 ... 11 ret = init_gpio_info(pdev); 12 ... 13 id = pdev->id; 14 bank = &gpio_bank[id]; 15 16 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 17 ... 18 bank->irq = res->start; 19 bank->virtual_irq_start = pdata->virtual_irq_start; 20 bank->method = pdata->bank_type; 21 bank->dev = &pdev->dev; 22 bank->dbck_flag = pdata->dbck_flag; 23 bank->stride = pdata->bank_stride; 24 bank_width = pdata->bank_width; 25 26 spin_lock_init(&bank->lock); 27 28 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 29 30 bank->base = ioremap(res->start, resource_size(res)); 31 ... 32 omap_gpio_mod_init(bank, id); 33 omap_gpio_chip_init(bank); 34 omap_gpio_show_rev(bank); 35 36 if (!gpio_init_done) 37 gpio_init_done = 1; 38 39 return 0; 40 }
此函数首先填充了struct gpio_bank,之后使用ioremap()映射,最后调用omap_gpio_chip_init()初始化并注册chip。
1 static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) 2 { 3 int j; 4 static int gpio; 5 /* 填充chip成员函数 */ 6 bank->mod_usage = 0; 7 bank->chip.request = omap_gpio_request; 8 bank->chip.free = omap_gpio_free; 9 bank->chip.direction_input = gpio_input; 10 bank->chip.get = gpio_get; 11 bank->chip.direction_output = gpio_output; 12 bank->chip.set_debounce = gpio_debounce; 13 bank->chip.set = gpio_set; 14 bank->chip.to_irq = gpio_2irq; 15 if (bank_is_mpuio(bank)) { 16 bank->chip.label = "mpuio"; 17 #ifdef CONFIG_ARCH_OMAP16XX 18 bank->chip.dev = &omap_mpuio_device.dev; 19 #endif 20 bank->chip.base = OMAP_MPUIO(0); 21 } else { 22 bank->chip.label = "gpio"; 23 bank->chip.base = gpio; 24 gpio += bank_width; 25 } 26 bank->chip.ngpio = bank_width; 27 28 gpiochip_add(&bank->chip); 29 ... 30 irq_set_chained_handler(bank->irq, gpio_irq_handler); 31 irq_set_handler_data(bank->irq, bank); 32 }
接下来我们来查看chip是如何注册到内核中的:
1 int gpiochip_add(struct gpio_chip *chip) 2 { 3 unsigned long flags; 4 int status = 0; 5 unsigned id; 6 int base = chip->base; 7 /* 判断gpio是否可用 */ 8 if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) && base >= 0) { 9 ... 10 } 11 ... 12 if (status == 0) { 13 for (id = base; id < base + chip->ngpio; id++) { 14 gpio_desc[id].chip = chip; /* 放入全局变量中 */ 15 16 gpio_desc[id].flags = !chip->direction_input 17 ? (1 << FLAG_IS_OUT) 18 : 0; 19 } 20 } 21 ... 22 unlock: 23 spin_unlock_irqrestore(&gpio_lock, flags); 24 25 status = gpiochip_export(chip); 26 ... 27 return status; 28 } 29 EXPORT_SYMBOL_GPL(gpiochip_add);
重新回到gpio_direction_output()函数,我们继续向下分析。代码第40行:chip->direction_output(chip, gpio, value);最终会调用chip的direction_output()函数。我们来看一下gpio-omap.c中的direction_output()函数是怎么实现的:
1 static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) 2 { 3 void __iomem *reg = bank->base; 4 u32 l; 5 6 switch (bank->method) { 7 #ifdef CONFIG_ARCH_OMAP1 8 case METHOD_MPUIO: 9 reg += OMAP_MPUIO_IO_CNTL / bank->stride; 10 break; 11 #endif 12 #ifdef CONFIG_ARCH_OMAP15XX 13 case METHOD_GPIO_1510: 14 reg += OMAP1510_GPIO_DIR_CONTROL; 15 break; 16 #endif 17 #ifdef CONFIG_ARCH_OMAP16XX 18 case METHOD_GPIO_1610: 19 reg += OMAP1610_GPIO_DIRECTION; 20 break; 21 #endif 22 #if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) 23 case METHOD_GPIO_7XX: 24 reg += OMAP7XX_GPIO_DIR_CONTROL; 25 break; 26 #endif 27 #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) 28 case METHOD_GPIO_24XX: 29 reg += OMAP24XX_GPIO_OE; 30 break; 31 #endif 32 #if defined(CONFIG_ARCH_OMAP4) 33 case METHOD_GPIO_44XX: 34 reg += OMAP4_GPIO_OE; 35 break; 36 #endif 37 default: 38 WARN_ON(1); 39 return; 40 } 41 l = __raw_readl(reg); 42 if (is_input) 43 l |= 1 << gpio; 44 else 45 l &= ~(1 << gpio); 46 __raw_writel(l, reg); 47 }
最终它会通过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源代码:
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5 #include <linux/slab.h> 6 #include <linux/device.h> 7 #include <linux/gpio.h> 8 #include <linux/io.h> 9 10 #include <asm/uaccess.h> 11 #include <asm/io.h> 12 13 #include <plat/gpio-cfg.h> 14 15 /* 定义文件内私有结构体 */ 16 struct led_device { 17 struct cdev cdev; 18 int stat; /* 用于保存LED状态,0为灭,1为亮 */ 19 }; 20 21 static int g_major; 22 module_param(g_major, int, S_IRUGO); 23 24 static struct led_device* dev; 25 static struct class* scls; 26 static struct device* sdev; 27 28 /* LED write()函数 */ 29 static ssize_t led_write(struct file *filep, const char __user * buf, size_t len, loff_t *ppos) 30 { 31 struct led_device *dev = filep->private_data; 32 33 if (copy_from_user(&(dev->stat), buf, 1)) 34 return -EFAULT; 35 36 if (dev->stat == 1) 37 gpio_set_value(EXYNOS4_GPK1(1), 1); 38 else 39 gpio_set_value(EXYNOS4_GPK1(1), 0); 40 41 return 1; 42 } 43 44 /* LED open()函数 */ 45 static int led_open(struct inode *inodep, struct file *filep) 46 { 47 struct led_device *dev; 48 int ret = -1; 49 50 dev = container_of(inodep->i_cdev, struct led_device, cdev); 51 // 放入私有数据中 52 filep->private_data = dev; 53 54 // 设为输出引脚,灭灯 55 ret = gpio_request(EXYNOS4_GPK1(1), "LED3"); 56 if (ret) 57 printk(KERN_ERR "failed to request LED3\n"); 58 59 gpio_direction_output(EXYNOS4_GPK1(1), 1); 60 s3c_gpio_cfgpin(EXYNOS4_GPK1(1), S3C_GPIO_OUTPUT); 61 62 gpio_set_value(EXYNOS4_GPK1(1), 1); 63 64 return 0; 65 } 66 67 static int led_close(struct inode *inodep, struct file *filep) 68 { 69 gpio_free(EXYNOS4_GPK1(1)); 70 71 return 0; 72 } 73 74 /* 把定义的函数接口集合起来,方便系统调用 */ 75 static const struct file_operations led_fops = { 76 .write = led_write, 77 .open = led_open, 78 .release = led_close, 79 }; 80 81 static int __init led_init(void) 82 { 83 int ret; 84 dev_t devt; 85 86 /* 1. 申请设备号 */ 87 if (g_major) { 88 devt = MKDEV(g_major, 0); 89 ret = register_chrdev_region(devt, 1, "led"); 90 } 91 else { 92 ret = alloc_chrdev_region(&devt, 0, 1, "led"); 93 g_major = MAJOR(devt); 94 } 95 if (ret) 96 return ret; 97 98 /* 2. 申请文件内私有结构体 */ 99 dev = kzalloc(sizeof(struct led_device), GFP_KERNEL); 100 if (dev == NULL) { 101 ret = -ENOMEM; 102 goto fail_malloc; 103 } 104 105 /* 3. 注册字符设备驱动 */ 106 cdev_init(&dev->cdev, &led_fops); /* 初始化cdev并链接file_operations和cdev */ 107 ret = cdev_add(&dev->cdev, devt, 1); /* 注册cdev */ 108 if (ret) 109 return ret; 110 111 /* 4. 创建类设备,insmod后会生成/dev/led设备文件 */ 112 scls = class_create(THIS_MODULE, "led"); 113 sdev = device_create(scls, NULL, devt, NULL, "led"); 114 115 return 0; 116 117 fail_malloc: 118 unregister_chrdev_region(devt, 1); 119 120 return ret; 121 } 122 123 static void __exit led_exit(void) 124 { 125 /* 镜像注销 */ 126 dev_t devt = MKDEV(g_major, 0); 127 128 device_destroy(scls, devt); 129 class_destroy(scls); 130 131 cdev_del(&(dev->cdev)); 132 kfree(dev); 133 134 unregister_chrdev_region(devt, 1); 135 } 136 137 /* 声明段属性 */ 138 module_init(led_init); 139 module_exit(led_exit); 140 141 MODULE_LICENSE("GPL");
Makefile:
1 KERN_DIR = /work/itop4412/tools/linux-3.5 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 make -C $(KERN_DIR) M=`pwd` modules clean 8 rm -rf modules.order 9 10 obj-m += led.o
测试文件:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <string.h> 7 8 int main(int argc, char** argv) 9 { 10 if (argc != 2) { 11 printf("Usage: \n"); 12 printf("%s <on|off>\n", argv[0]); 13 return -1; 14 } 15 16 int fd; 17 fd = open("/dev/led", O_RDWR); 18 if (fd < 0) { 19 printf("can't open /dev/led\n"); 20 return -1; 21 } 22 23 char stat; 24 if (0 == strcmp(argv[1], "off")) { 25 stat = 0; 26 write(fd, &stat, 1); 27 } else { 28 stat = 1; 29 write(fd, &stat, 1); 30 } 31 close(fd); 32 33 return 0; 34 }