设备驱动框架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");					// 描述模块的别名信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

from PyQt4 import QtGui,QtCore出错-解

from PyQt4 import QtGui,QtCore出错-解今天尝试着安装PyQt写界面&#xff0c;官网下载后发现import出错了&#xff0c;情况如下图&#xff1a;import PyQt4就可以&#xff0c;from PyQt4 import QtCore却不行提示DLL load faied找了下网上有些人说是某些dll文件丢失了&#xf…

设备驱动框架4——将驱动集成到内核中

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 驱动集成到内核的概念 驱动开发的步骤一般是&#xff1a; &#xff08;1&#xff09;以模块的形式在内核外部编写与调试 &#xff08;2&#xff09;将调试好的驱动代码集成到kernel中 之前我们编写的…

例子简单说说C# ref和out

首写从这字段看 ref 就是引用的意思 out当然就是输出了public void getRefStr(ref string str) {str"hello 你好&#xff0c;你变成了Ref了" }public void getOutStr(out string outStr){outStr "hello 你好&#xff0c;你是out输出的值";} protected…

VARIANT变体类型数据

2019独角兽企业重金招聘Python工程师标准>>> 特殊 Variant 是一种特殊的数据类型&#xff0c;除了定长String数据及用户定义类型外&#xff0c;可以包含任何种类的数据。Variant 也可以包含Empty、Error、Nothing及Null等特殊值。可以用VarType函数或TypeName函数来…

mysql修改校对集_MySQL 图文详细教程之校对集问题

软件安装&#xff1a;装机软件必备包SQL是Structured Query Language(结构化查询语言)的缩写。SQL是专为数据库而建立的操作命令集&#xff0c;是一种功能齐全的数据库语言。在使用它时&#xff0c;只需要发出“做什么”的命令&#xff0c;“怎么做”是不用使用者考虑的。SQL功…

C# winform 魔兽MH全图制作教程(1): 开发准备工作

C# winform 魔兽MH全图制作教程&#xff08;1&#xff09;: 开发准备工作 一、开发条件&#xff1a; Visual Studio 2008win xp,win 7,win 2003.C# 语言基础会调试能够运行游戏&#xff1a;《魔兽争霸3冰封王座》拥有版本魔兽客户端版本切换器1.20E,1.24E,1.24D二、设计思路&am…

从常识看中国经济社会-再续之续:套利

2019独角兽企业重金招聘Python工程师标准>>> 《全球化掠夺》提及财富流转的路径&#xff0c;世界仍旧是个丛林&#xff0c;每个人、每个族群都在争夺自己的利益。在一个经济体的内部&#xff0c;财富是垂直流动的&#xff1b;在全球化的经济体中&#xff0c;财富是纵…

MYSQL中的BlackHole引擎

MYSQL中的BlackHole引擎 http://blog.csdn.net/ylspirit/article/details/7234021 http://blog.chinaunix.net/uid-22646981-id-3271711.html MySQL在5.x系列提供了Blackhole引擎–“黑洞”. 其作用正如其名字一样&#xff1a;任何写入到此引擎的数据均会被丢弃掉&#xff0c;…

《DIY四轴飞行器》读书笔记1

内容整理于黄和悦的《DIY四轴飞行器》。 一、四轴飞行器概述 1、四轴飞行器的现状 &#xff08;1&#xff09;研究内容 多级协作&#xff0c;自主飞行倾斜&#xff1b;最优控制理论&#xff0c;飞行器自主飞行和避障&#xff1b;主要是飞控部分。 &#xff08;2&#xff09…

脚本输出当前 “yyyy-MM-dd WeakDay Festval”

ylbtech-JavaScript: 脚本输出当前 “yyyy-MM-dd WeakDay Festval”脚本输出当前 “yyyy-MM-dd WeakDay Festval” 1.A,源代码(Source Code)-脚本输出当前 “yyyy-MM-dd WeakDay Festval”返回顶部 <SCRIPT languagejavascript> <!--calendar new Date();day cal…

SecureCRT密钥远程登录Linux

一&#xff1a;环境SecureCRT版本&#xff1a;SecureCRT_5.1.3linux版本&#xff1a;[rootangelT ~]# cat /etc/redhat-release CentOS release 6.4 (Final)[rootangelT ~]# uname -r2.6.32-358.el6.x86_64linux系统的sshd_config配置文件是默认的&#xff0c;没有任何的修改。…

在Linux系统安装Nginx及配置https加密访问

2019独角兽企业重金招聘Python工程师标准>>> 1、安装nginx ①、为了确保能在 nginx 中使用正则表达式进行更灵活的配置&#xff0c;安装之前需要确定系统是否安装有 PCRE&#xff08;Perl Compatible Regular Expressions&#xff09;包。您可以到 ftp://ftp.csx.c…

和菜鸟一起学linux之bluez学习记录2

这里主要摘取对于hci&#xff0c;l2cap&#xff0c;sdp和rfcomm的一些应用编程。 关于hci 一、HCI层协议概述 1、HCI Command Packets 详见bluez源码&#xff1a;lib/hci.h /* Link Control */ #define OGF_LINK_CTL 0x01 #define OCF_INQUIRY 0x0001 #define OCF_…

AppDelegate.h

2019独角兽企业重金招聘Python工程师标准>>> #ifndef __APP_DELEGATE_H__ #define __APP_DELEGATE_H__#include "CCApplication.h" //CCApplication.h能根据平台打开对应的平台头文件 /** brief The cocos2d Application.The reason for implement as …

虚拟机上网以及互ping问题

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 虚拟机设置静态IP和上网问题 &#xff08;1&#xff09;在“虚拟机——设置——网络适配器”中选择桥接模式&#xff1b; &#xff08;2&#xff09;在“编辑——虚拟网络编辑器”中&#xff0c;选择桥接到有…

qt 飞扬青云_R语言学习——实例标识符

> patientID> age> diabetes> status> patientdata> #在上述创建的病例数据框中&#xff0c;病人编号(patientID)用于区分数据中的不同个体&#xff0c;在R中实例标识符(case identifier)可以通过数据框操作函数中的rowname选项指定&#xff0c;如下代码&…

fck java_FCkjava三种调用方法

测试FCK方法一:toolbarSet "Default" >默认内容测试方法二:FCKeditor oFCKeditor ;oFCKeditor new FCKeditor(request,"content") ;oFCKeditor.setBasePath( "/db/fckeditor/" ) ;oFCKeditor.setWidth("600");oFCKeditor.setHeig…

Spring 容器(Spring 的上下文)

为什么80%的码农都做不了架构师&#xff1f;>>> 最近在做项目时牵扯到有关父子上下文的概念。 何为父子上下文呢&#xff1f; 父上下文&#xff1a; 使用listener监听器来加载配置文件&#xff0c;如下&#xff1a; <listener> <listener-class>org.…

file_operations结构体

以下读书笔记内容摘自宋宝华《Linux设备驱动开发详解》一书。 file_operations结构体在字符设备驱动的地位 file_operations结构体的定义 此结构体定义在x210kernel/include/linux/fs.h文件中。 struct file_operations {struct module *owner;loff_t (*llseek) (struct file …

FlashCC学习札记

前段时间项目中有一个功能模块用到了FlashCC&#xff0c;将C的代码编译成swc文件&#xff0c;以便在Flash工程中使用。开发过程中遇到一些问题&#xff0c;现在记录下来&#xff0c;以便日后查阅。 一、开发环境搭建 说明:本文所使用的FlashCC版本为1.0.1&#xff0c; 操作系统…