设备驱动框架3——使用gpiolib完成LED驱动
以下內容源于朱有鵬嵌入式課程的學習整理,如有侵權請告知刪除。
一、前言
在實際情況中,很多硬件都要用到GPIO,因此GPIO會復用;如果同一個GPIO被2個驅動同時控制就會出現bug;因此內核提供了gpiolib來統一管理系統中所有GPIO。某個驅動需要用到GPIO時,需要申請,被許可后才可以使用,使用完后釋放,其他驅動才能使用該GPIO。
在LED驅動框架的分析文章開頭,寫到“系統資源的管控屬于驅動框架的一部分”。gpiolib體系也屬于驅動框架的一部分。
二、gpiolib體系的3條主線
第1條主線:gpiolib的建立過程。
第2條主線:gpiolib的使用方法,即申請、使用、釋放。
第3條主線:gpiolib的架構,即涉及哪些目錄的哪些文件。
三、第一條主線:gpiolib的建立過程
s5pv210_gpiolib_init()函數是gpiolib初始化的函數。
其定義在x210_kernel\arch\arm\mach-s5pv210\gpiolib.c文件中。
static void __init smdkc110_map_io(void) {s5p_init_io(NULL, 0, S5P_VA_CHIPID);s3c24xx_init_clocks(24000000);s5pv210_gpiolib_init();//這里s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs)); #ifdef CONFIG_MTD_ONENANDs5pc110_device_onenand.name = "s5pc110-onenand"; #endif #ifdef CONFIG_MTD_NANDs3c_device_nand.name = "s5pv210-nand"; #endifs5p_device_rtc.name = "smdkc110-rtc"; } __init int s5pv210_gpiolib_init(void) {struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;//結構體數組int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);//GPIO端口數目int i = 0;for (i = 0; i < nr_chips; i++, chip++) {if (chip->config == NULL)chip->config = &gpio_cfg;if (chip->base == NULL)chip->base = S5PV210_BANK_BASE(i);}samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);return 0; }1、結構體struct?s3c_gpio_chip的分析
該結構體定義在\x210_kernel\arch\arm\plat-samsung\include\plat\gpio-core.h文件中。它是一個GPIO端口的抽象。
端口和IO口是兩個概念。S5PV210的IO口約有160個,這些IO口被分成N個端口,每個端口中又包含了M個IO口。比如GPA0就是一個端口,里面包含8個IO口,一般記作GPA0_0(或GPA0.0),GPA0_1(或GPA0.1),…,GPA0_7(或GPA0.7)。
內核中為每個GPIO都分配了一個編號,編號是一個數字(比如有160個IO時,編號就可以從1到160連續分布),編號可以讓程序很方便的去識別每一個GPIO。
struct s3c_gpio_chip {struct gpio_chip chip;struct s3c_gpio_cfg *config;struct s3c_gpio_pm *pm;void __iomem *base;//端口的基地址int eint_offset;spinlock_t lock; #ifdef CONFIG_PMu32 pm_save[7]; #endif };?該結構體包括著結構體struct gpio_chip,其內容如下:
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; };2、結構體數組s5pv210_gpio_4bit
該數組定義在x210_kernel\arch\arm\mach-s5pv210\gpiolib.c文件中。
該數組的成員都是struct s3c_gpio_chip結構體類型的變量,在定義數組的同時,都填充了每個成員的chip這個元素。
結構體數組s5pv210_gpio_4bit包含了當前系統中所有的IO端口的信息,比如端口的名字、端口中所有GPIO的編號、端口操作寄存器組的虛擬地址基地址、端口中IO口的數量、端口上下拉等模式的配置函數、端口中的IO口換算其對應的中斷號的函數。
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {{.chip = {.base = S5PV210_GPA0(0), //當前端口的基礎編號.ngpio = S5PV210_GPIO_A0_NR,//當前端口擁有的IO口數量.label = "GPA0", //當前端口的名字.to_irq = s5p_gpiolib_gpioint_to_irq,//當前端口中IO口換成對應中斷號的方法},}, {.chip = {.base = S5PV210_GPA1(0),.ngpio = S5PV210_GPIO_A1_NR,.label = "GPA1",.to_irq = s5p_gpiolib_gpioint_to_irq,},}, {.chip = {.base = S5PV210_GPB(0),.ngpio = S5PV210_GPIO_B_NR,.label = "GPB",.to_irq = s5p_gpiolib_gpioint_to_irq,},}, //……省略部分代碼 } [root@xjh class]# ls backlight i2c-adapter misc regulator scsi_host //忽略部分 bdi i2c-dev mmc_host rfkill sound block ieee80211 mtd rtc spi_master firmware input net s3c_bc switch gpio lcd power_supply scsi_device timed_output graphics leds ppp scsi_disk tty hidraw mem pvr scsi_generic vc [root@xjh class]# cd gpio/ [root@xjh gpio]# ls export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 [root@xjh gpio]# cd gpiochip0 [root@xjh gpiochip0]# ls base label ngpio power subsystem uevent [root@xjh gpiochip0]# cat base 0 [root@xjh gpiochip0]# cat label GPA0 [root@xjh gpiochip0]# cat ngpio 8 [root@xjh gpiochip0]# cd ../gpiochip14 [root@xjh gpiochip14]# ls base label ngpio power subsystem uevent [root@xjh gpiochip14]# cat base 14 [root@xjh gpiochip14]# cat label GPB [root@xjh gpiochip14]# cat ngpio 8 [root@xjh gpiochip14]# cd ../gpiochip35 [root@xjh gpiochip35]# ls base label ngpio power subsystem uevent [root@xjh gpiochip35]# cat base 35 [root@xjh gpiochip35]# cat label GPD0 [root@xjh gpiochip35]# cat ngpio 4 [root@xjh gpiochip35]#另外,注意一下諸如S5PV210_GPA0()這樣的宏。這個宏的返回值是GPA0端口的某一個IO口的基礎編號值,傳參是這個IO口在GPA0端口中的局部編號。
/* S5PV210 GPIO number definitions */ #define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr)) #define S5PV210_GPA1(_nr) (S5PV210_GPIO_A1_START + (_nr)) #define S5PV210_GPB(_nr) (S5PV210_GPIO_B_START + (_nr)) #define S5PV210_GPC0(_nr) (S5PV210_GPIO_C0_START + (_nr)) #define S5PV210_GPC1(_nr) (S5PV210_GPIO_C1_START + (_nr)) …… #define S5PV210_GPIO_NEXT(__gpio) \((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)enum s5p_gpio_number {S5PV210_GPIO_A0_START = 0,S5PV210_GPIO_A1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A0),S5PV210_GPIO_B_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A1),S5PV210_GPIO_C0_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_B),S5PV210_GPIO_C1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_C0),S5PV210_GPIO_D0_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_C1),S5PV210_GPIO_D1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_D0), …… }3、samsung_gpiolib_add_4bit_chips()函數
該函數的函數名中為什么有個“4bit”?查詢原理圖得知如下。三星的CPU中2440的CON寄存器是2bit對應一個IO口,而6410和210以及之后的系列中CON寄存器是4bit對應1個IO口。所以gpiolib在操作2440和210的CON寄存器時是不同的。
?此函數用來注冊gpiolib,接收的參數是結構體數組s5pv210_gpio_4bit、該數組元素的個數。
samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);函數調用關系如下:
|…………samsung_gpiolib_add_4bit_chips()函數
|………………samsung_gpiolib_add_4bit()函數
|……………………samsung_gpiolib_4bit_input函數
|……………………samsung_gpiolib_4bit_output函數
|………………s3c_gpiolib_add()函數
void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,int nr_chips) {for (; nr_chips > 0; nr_chips--, chip++) {samsung_gpiolib_add_4bit(chip);s3c_gpiolib_add(chip);} }查閱代碼可知,samsung_gpiolib_add_4bit()函數內部沒有做gpiolib的注冊工作,而是在做填充,填充的是每一個GPIO被設置成輸入模式/輸出模式的操作方法。
static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,unsigned int offset) {struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);void __iomem *base = ourchip->base;unsigned long con;con = __raw_readl(base + GPIOCON_OFF);//讀寫三部曲con &= ~(0xf << con_4bit_shift(offset));__raw_writel(con, base + GPIOCON_OFF);gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);return 0; }查閱代碼可知,s3c_gpiolib_add()首先完善chip的direction_input/direction_ouput/set/get這4個方法,然后調用gpiochip_add方法進行真正的注冊操作。
__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) {struct gpio_chip *gc = &chip->chip;int ret;BUG_ON(!chip->base);BUG_ON(!gc->label);BUG_ON(!gc->ngpio);spin_lock_init(&chip->lock);if (!gc->direction_input)gc->direction_input = s3c_gpiolib_input;if (!gc->direction_output)gc->direction_output = s3c_gpiolib_output;if (!gc->set)gc->set = s3c_gpiolib_set;//設置data寄存器的值if (!gc->get)gc->get = s3c_gpiolib_get;//獲取data寄存器的值#ifdef CONFIG_PMif (chip->pm != NULL) {if (!chip->pm->save || !chip->pm->resume)printk(KERN_ERR "gpio: %s has missing PM functions\n",gc->label);} elseprintk(KERN_ERR "gpio: %s has no PM function\n", gc->label); #endif/* gpiochip_add() prints own failure message on error. */ret = gpiochip_add(gc);if (ret >= 0)s3c_gpiolib_track(chip); } static void s3c_gpiolib_set(struct gpio_chip *chip,unsigned offset, int value) {struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);void __iomem *base = ourchip->base;unsigned long flags;unsigned long dat;s3c_gpio_lock(ourchip, flags);dat = __raw_readl(base + 0x04);dat &= ~(1 << offset);if (value)dat |= 1 << offset;__raw_writel(dat, base + 0x04);s3c_gpio_unlock(ourchip, flags); }static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) {struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);unsigned long val;val = __raw_readl(ourchip->base + 0x04);val >>= offset;val &= 1;return val; } /* *這個注冊就是將(我們的封裝了一個GPIO端口的所有信息的)chip結構體變量, *掛接到內核(gpiolib模塊定義的一個)gpio_desc數組中的某一個格子中。 */ int gpiochip_add(struct gpio_chip *chip) {unsigned long flags;int status = 0;unsigned id;int base = chip->base;if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))&& base >= 0) {status = -EINVAL;goto fail;}spin_lock_irqsave(&gpio_lock, flags);if (base < 0) {base = gpiochip_find_base(chip->ngpio);if (base < 0) {status = base;goto unlock;}chip->base = base;}/* these GPIO numbers must not be managed by another gpio_chip */for (id = base; id < base + chip->ngpio; id++) {if (gpio_desc[id].chip != NULL) {status = -EBUSY;break;}}if (status == 0) {for (id = base; id < base + chip->ngpio; id++) {gpio_desc[id].chip = chip;/* REVISIT: most hardware initializes GPIOs as* inputs (often with pullups enabled) so power* usage is minimized. Linux code should set the* gpio direction first thing; but until it does,* we may expose the wrong direction in sysfs.*/gpio_desc[id].flags = !chip->direction_input? (1 << FLAG_IS_OUT): 0;}}unlock:spin_unlock_irqrestore(&gpio_lock, flags);if (status == 0)status = gpiochip_export(chip); fail:/* failures here can mean systems won't boot... */if (status)pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",chip->base, chip->base + chip->ngpio - 1,chip->label ? : "generic");return status; }?四、第2條主線:gpiolib的使用方法
1、從驅動框架角度分析gpiolib
目前已經清楚第1條主線,即gpiolib的建立過程,但這只是廠商驅動工程師負責的那一部分;還有另一部分是內核開發者提供的驅動框架的那一部分,即第2條主線。
drivers/gpio/gpiolib.c文件中的函數構成第2部分,即內核開發者寫的gpiolib框架部分。
(1)gpiochip_add
它是框架提供的接口,給廠商驅動工程師用(針對某個開發板GPIO的情況,對內核進行一定的修改,注冊),用于向內核注冊gpiolib(標記有多少組端口,屬性細節等,讓內核知道具體的GPIO信息)。
(2)gpio_request
它是框架提供的接口,給使用gpiolib來編寫自己的驅動的驅動工程師用的,驅動中要想使用某一個gpio,就必須先調用gpio_request接口來向內核的gpiolib部分申請,得到允許后才可以去使用這個gpio。
(3)gpio_free
對應于gpio_request,用來釋放gpio。
(4)gpio_request_one、gpio_request_array
這兩個是gpio_request的變種。
(5)gpiochip_is_requested
接口用來判斷某一個gpio是否已經被申請。
(6)gpio_direction_input、gpio_direction_output
接口用來設置GPIO為輸入/輸出模式,注意該函數內部實際并沒有對硬件進行操作,只是通過chip結構體變量的函數指針,調用了(將來SoC廠商的驅動工程師寫的)真正地操作硬件、實現gpio設置成輸出模式的那個函數。
(7)其他接口
以上的接口,是給寫其他驅動并且用到了gpiolib的人使用的。剩下的函數是gpiolib內部自己的一些功能實現的代碼。
2、gpiolib的attribute部分
(1)CONFIG_GPIO_SYSFS
在內核中很多實現方式都是通過宏來配置的;在.config文件有,則必然在menuconfig中有。
(2)GPIO的attribute演示
static ssize_t chip_label_show(struct device *dev,struct device_attribute *attr, char *buf) {const struct gpio_chip *chip = dev_get_drvdata(dev);return sprintf(buf, "%s\n", chip->label ? : ""); } static DEVICE_ATTR(label, 0444, chip_label_show, NULL); [root@xjh class]# ls backlight i2c-adapter misc regulator scsi_host //忽略部分 bdi i2c-dev mmc_host rfkill sound block ieee80211 mtd rtc spi_master firmware input net s3c_bc switch gpio lcd power_supply scsi_device timed_output graphics leds ppp scsi_disk tty hidraw mem pvr scsi_generic vc [root@xjh class]# cd gpio/ [root@xjh gpio]# ls export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 [root@xjh gpio]# cd gpiochip0 [root@xjh gpiochip0]# ls base label ngpio power subsystem uevent [root@xjh gpiochip0]# cat base 0 [root@xjh gpiochip0]# cat label GPA0 [root@xjh gpiochip0]# cat ngpio 8 [root@xjh gpiochip0]#(3)能夠cat的相關代碼分析
static int __init gpiolib_sysfs_init(void) {int status;unsigned long flags;unsigned gpio;idr_init(&pdesc_idr);status = class_register(&gpio_class);在/sys/class里定義了gpio這個類if (status < 0)return status;/* Scan and register the gpio_chips which registered very* early (e.g. before the class_register above was called).** We run before arch_initcall() so chip->dev nodes can have* registered, and so arch_initcall() can always gpio_export().*/spin_lock_irqsave(&gpio_lock, flags);for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {struct gpio_chip *chip;chip = gpio_desc[gpio].chip;if (!chip || chip->exported)continue;spin_unlock_irqrestore(&gpio_lock, flags);status = gpiochip_export(chip);//spin_lock_irqsave(&gpio_lock, flags);}spin_unlock_irqrestore(&gpio_lock, flags);return status; } postcore_initcall(gpiolib_sysfs_init); static int gpiochip_export(struct gpio_chip *chip) {int status;struct device *dev;/* Many systems register gpio chips for SOC support very early,* before driver model support is available. In those cases we* export this later, in gpiolib_sysfs_init() ... here we just* verify that _some_ field of gpio_class got initialized.*/if (!gpio_class.p)return 0;/* use chip->base for the ID; it's already known to be unique */mutex_lock(&sysfs_lock);dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,"gpiochip%d", chip->base);if (!IS_ERR(dev)) {status = sysfs_create_group(&dev->kobj,//用來創建許多attribute&gpiochip_attr_group);} elsestatus = PTR_ERR(dev);chip->exported = (status == 0);mutex_unlock(&sysfs_lock);if (status) {unsigned long flags;unsigned gpio;spin_lock_irqsave(&gpio_lock, flags);gpio = chip->base;while (gpio_desc[gpio].chip == chip)gpio_desc[gpio++].chip = NULL;spin_unlock_irqrestore(&gpio_lock, flags);pr_debug("%s: chip %s status %d\n", __func__,chip->label, status);}return status; } [root@xjh ~]# cd /sys/class [root@xjh class]# cd gpio/ [root@xjh gpio]# ls export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 [root@xjh gpio]# echo 23 >export [root@xjh gpio]# ls export gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 gpio23//多出 gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 [root@xjh gpio]# cd gpio23 [root@xjh gpio23]# ls active_low direction edge power subsystem uevent value [root@xjh gpio23]# cat value 0 [root@xjh gpio23]# cat edge none [root@xjh gpio23]# cat direction in [root@xjh gpio23]#五、使用gpiolib完成led驅動
1、流程分析
第1步:使用gpio_request申請要使用的一個GPIO。
第2步:使用gpio_direction_input、gpio_direction_output 來設置輸入、輸出模式。
第3步:使用gpio_set_value設置輸出值,或者使用gpio_get_value獲取IO口值。
2、代碼實踐
Linux中查看gpio使用情況的方法
內核中提供了虛擬文件系統debugfs,里面有一個gpio文件,提供了gpio的使用信息(比如誰被使用了誰沒有被使用)。
使用方法:
(1)使用命令“mount -t debugfs debugfs /tmp”進行掛載;
(2)之后使用“cat /tmp/gpio”即可得到gpio的所有信息;
(3)使用完后“umount /tmp”卸載掉debugfs。
[root@xjh ]# cd tmp/ [root@xjh tmp]# ls [root@xjh tmp]# mount -t debugfs debugfs /tmp [root@xjh tmp]# ls [root@xjh tmp]# cd .. [root@xjh ]# ls bin etc lib mnt root sys usr dev home linuxrc proc sbin tmp var [root@xjh ]# cd tmp/ [root@xjh tmp]# ls apanic binder ieee80211 mmc2 pmstats asoc gpio mmc0 mmc3 sched_features bdi hid mmc1 pmem_gpu1 usb [root@xjh tmp]# cat gpio GPIOs 0-7, GPA0:GPIOs 9-12, GPA1:GPIOs 14-21, GPB:GPIOs 23-27, GPC0:gpio-23 (sysfs ) in loGPIOs 29-33, GPC1:GPIOs 35-38, GPD0:GPIOs 40-45, GPD1:GPIOs 47-54, GPE0:GPIOs 56-60, GPE1:GPIOs 62-69, GPF0:GPIOs 71-78, GPF1:GPIOs 80-87, GPF2:GPIOs 89-94, GPF3:GPIOs 96-102, GPG0:GPIOs 104-110, GPG1:GPIOs 112-118, GPG2:GPIOs 120-126, GPG3:GPIOs 128-135, GPH0:gpio-129 (GPH0 ) in higpio-130 (GPH0 ) in higpio-131 (GPH0 ) in hiGPIOs 137-144, GPH1:gpio-139 (GPH1 ) in lo irq-42 level-highGPIOs 146-153, GPH2:gpio-146 (GPH2 ) in higpio-147 (GPH1 ) in higpio-148 (GPH2 ) in higpio-149 (GPH2 ) in higpio-152 (GPH2 ) in logpio-153 (GPH3 ) in hiGPIOs 155-162, GPH3:GPIOs 164-170, GPI:GPIOs 172-179, GPJ0:GPIOs 181-186, GPJ1:GPIOs 188-195, GPJ2:GPIOs 197-204, GPJ3:GPIOs 206-210, GPJ4:GPIOs 212-219, MP01:GPIOs 221-224, MP02:GPIOs 226-233, MP03:GPIOs 235-242, MP04:GPIOs 244-251, MP05:GPIOs 253-260, MP06:GPIOs 262-269, MP07:GPIOs 271-278, MP10:GPIOs 280-287, MP11:GPIOs 289-296, MP12:GPIOs 298-305, MP13:GPIOs 307-314, MP14:GPIOs 316-323, MP15:GPIOs 325-332, MP16:GPIOs 334-341, MP17:GPIOs 343-349, MP18:GPIOs 351-358, MP20:GPIOs 360-367, MP21:GPIOs 369-376, MP22:GPIOs 378-385, MP23:GPIOs 387-394, MP24:GPIOs 396-403, MP25:GPIOs 405-412, MP26:GPIOs 414-421, MP27:GPIOs 423-429, MP28:GPIOs 431-436, ETC0:GPIOs 438-445, ETC1:GPIOs 447-454, ETC2:GPIOs 456-461, ETC4: [root@xjh tmp]#代碼(該驅動只申請了LED1資源)
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <linux/leds.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/io.h> #include <linux/ioport.h> #include <mach/gpio.h>#define GPIO_LED1 S5PV210_GPJ0(3) #define GPIO_LED2 S5PV210_GPJ0(4) #define GPIO_LED3 S5PV210_GPJ0(5)#define X210_LED_OFF 1 // X210中LED是正極接電源,負極節GPIO #define X210_LED_ON 0 // 所以1是滅,0是亮static struct led_classdev mydev1; // 定義結構體變量 static struct led_classdev mydev2; // 定義結構體變量 static struct led_classdev mydev3; // 定義結構體變量// 這個函數就是要去完成具體的硬件讀寫任務的 static void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness value) {printk(KERN_INFO "s5pv210_led1_set\n");//writel(0x11111111, GPJ0CON);// 在這里根據用戶設置的值來操作硬件// 用戶設置的值就是valueif (value == LED_OFF){// 用戶給了個0,希望LED滅//writel(0x11111111, GPJ0CON);// 讀改寫三部曲//writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT);gpio_set_value(GPIO_LED1, X210_LED_OFF);}else{// 用戶給的是非0,希望LED亮//writel(0x11111111, GPJ0CON);//writel((readl(GPJ0DAT) & ~(1<<3)), GPJ0DAT);gpio_set_value(GPIO_LED1, X210_LED_ON);} }static void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness value) {printk(KERN_INFO "s5pv2102_led_set\n");//writel(0x11111111, GPJ0CON);// 在這里根據用戶設置的值來操作硬件// 用戶設置的值就是valueif (value == LED_OFF){// 用戶給了個0,希望LED滅//writel(0x11111111, GPJ0CON);// 讀改寫三部曲//writel((readl(GPJ0DAT) | (1<<4)), GPJ0DAT);}else{// 用戶給的是非0,希望LED亮//writel(0x11111111, GPJ0CON);//writel((readl(GPJ0DAT) & ~(1<<4)), GPJ0DAT);} }static void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness value) {printk(KERN_INFO "s5pv210_led3_set\n");//writel(0x11111111, GPJ0CON);// 在這里根據用戶設置的值來操作硬件// 用戶設置的值就是valueif (value == LED_OFF){// 用戶給了個0,希望LED滅//writel(0x11111111, GPJ0CON);// 讀改寫三部曲//writel((readl(GPJ0DAT) | (1<<5)), GPJ0DAT);}else{// 用戶給的是非0,希望LED亮//writel(0x11111111, GPJ0CON);//writel((readl(GPJ0DAT) & ~(1<<5)), GPJ0DAT);} }static int __init s5pv210_led_init(void) {// 用戶insmod安裝驅動模塊時會調用該函數// 該函數的主要任務就是去使用led驅動框架提供的設備注冊函數來注冊一個設備int ret = -1;// 在這里去申請驅動用到的各種資源,當前驅動中就是GPIO資源if (gpio_request(GPIO_LED1, "led1_gpj0.3")) //這里是申請失敗{printk(KERN_ERR "gpio_request failed\n");} else //申請成功后{// 設置為輸出模式,并且默認輸出1讓LED燈滅gpio_direction_output(GPIO_LED1, 1);}// led1mydev1.name = "led1";mydev1.brightness = 0; mydev1.brightness_set = s5pv210_led1_set;ret = led_classdev_register(NULL, &mydev1);if (ret < 0) {printk(KERN_ERR "led_classdev_register failed\n");return ret;}// led2mydev2.name = "led2";mydev2.brightness = 0; mydev2.brightness_set = s5pv210_led2_set;ret = led_classdev_register(NULL, &mydev2);if (ret < 0) {printk(KERN_ERR "led_classdev_register failed\n");return ret;}// led3mydev3.name = "led3";mydev3.brightness = 0; mydev3.brightness_set = s5pv210_led3_set;ret = led_classdev_register(NULL, &mydev3);if (ret < 0) {printk(KERN_ERR "led_classdev_register failed\n");return ret;}return 0; }static void __exit s5pv210_led_exit(void) {led_classdev_unregister(&mydev1);led_classdev_unregister(&mydev2);led_classdev_unregister(&mydev3);gpio_free(GPIO_LED1); }module_init(s5pv210_led_init); module_exit(s5pv210_led_exit);// MODULE_xxx這種宏作用是用來添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("aston <1264671872@qq.com>"); // 描述模塊的作者 MODULE_DESCRIPTION("s5pv210 led driver"); // 描述模塊的介紹信息 MODULE_ALIAS("s5pv210_led"); // 描述模塊的別名信息總結
以上是生活随笔為你收集整理的设备驱动框架3——使用gpiolib完成LED驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dropbear 安装配置
- 下一篇: delphi的时间Ttime,Tdate