由於之后的觸摸屏驅動分析中使用到了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 }
下一章 14、i2c子系統
