【linux驱动开发】-gpiolib概念与实践

1.什么是gpiolib?

       在平常的硬件驱动过程中,很多的硬件都要用到GPIO,GPIO会复用,如果同一时刻GPIO被两个驱动同时控制了,那么就会出现bug,所以内核提供了gpiolib来统一管理系统中所有的GPIO,gpiolib本身就是属于驱动框架的一部分!

在这部分的学习过程中,我们会分析gpiolib应该如何去学习,如何编程实现gpiolib,并且通过上一节中对LED的程序进行扩展,增加gpiolib控制来实现灯的变化!

2.如何学习gpiolib

gpiolib的架构(建立)

在内核启动,静态映射自动建立的时候,有一个函数xxx_gpiolib_init()会被自动加载!

然后进入到xxx_gpiolib_init()这个函数中,依次进行分析!

接下来就顺着主线分析s3c_gpio_chip这个结构体,首先可以看出来,这个结构体是一个gpio端口的抽象,这个结构体的一个变量就可以完全的描述一个IO端口!

这里区分端口和IO口: 一个Soc有很多的IO口,这些IO口首先被分成N个端口,然后每个端口有包含了M个IO口。譬如GPA0是一个端口,里面包含了8个IO口,我们一般记作GPA0_0(GPA0.0)、GPA0_1;

这个东西是一个结构体数组,数组里包含了很多个struct s3c_gpio_chip类型的变量!一个变量对应一个端口,重点是描述.chip这个元素!

其中.base 、.ngpio、.label成了GPIO中的一些属性:

 通过上面的分析,我们已经大概知道了如何分析一个gpiolib的建立,并且在init函数的最后一句,我们有向内核建立gpiolib的函数。

这个函数才是具体进行gpiolib的注册的。这个函数接受的参数是我们当前文件定义好的结构体数组xxx_gpio_4bit(其实两个参数分别是数组名和数组元素个数),这个数组包含了当前系统中所有IO端口的信息。

gpiolib的使用方法

        在这里我们不考虑,内核部分是如何实现的,只考虑如果我们作为厂商的驱动工程师,我们应该怎样使用gpiolib!也是比较重要的一部分!

gpiochip_add:是框架开出来的接口,给厂商驱动工程师用,用于向内核注册我们的gpiolib。

gpio_request:是框架开出来的接口,给使用gpiolib来编写自己的驱动的驱动工程师用的,驱动中想使用某一个gpio,就必须先调用gpio_request接口来向内核的gpiolib部分申请,得到允许后才能使用这个gpio。

gpio_free:对应gpio_request,用来释放申请后用完了的gpio。

gpio_request_one/gpio_request_array:这两个是gpio_request的变种。

gpiochip_is_requested:接口用来判断某一个gpio是否已经被申请了。

gpio_direction_input/gpio_direction_output:接口用来设置GPIO为输入/输出模式,注意该函数内部实际并没有对硬件进行操作,只是通过chip结构体变量的函数指针调用了将来Soc厂商的驱动工程师真正写的操作gpio设置成输出模式的那个函数!

3.通过编程实践测试gpiolib

第一步:使用gpio_request申请要是用的一个GPIO。

第二步:gpio_direction_input/gpio_direction_output设置成输入/输出模式。

第三步:设置输出值gpio_set_value,获取gpio_get_value.

通过以上三步和上一章节LED驱动框架编写LED的驱动程序!

#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>

///****此驱动使用的是led框架和gpiolib框架进行编写***

#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);
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (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);
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (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);
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (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")) //申请内存,如果成功返回0,失败返回1  如果返回0 说明成功,直接跳到else执行
	{
		printk(KERN_ERR "gpio_request failed\n");//返回1 说明失败,打印此句
	} 
	else 
	{
		// 设置为输出模式,并且默认输出1让LED灯灭
		gpio_direction_output(GPIO_LED1, 1);
	}
	
	
	
	// led1
	mydev1.name = "led1";
	mydev1.brightness = 0;	
	mydev1.brightness_set = s5pv210_led1_set;
	
	ret = led_classdev_register(NULL, &mydev1);//led_classdev_register就是注册设备
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register failed\n");
		return ret;
	}
	
	// led2
	mydev2.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;
	}
	
	// led3
	mydev3.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");					// 描述模块的别名信息

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值