在平常的硬件驱动过程中,很多的硬件都要用到GPIO,GPIO会复用,如果同一时刻GPIO被两个驱动同时控制了,那么就会出现bug,所以内核提供了gpiolib来统一管理系统中所有的GPIO,gpiolib本身就是属于驱动框架的一部分!
在这部分的学习过程中,我们会分析gpiolib应该如何去学习,如何编程实现gpiolib,并且通过上一节中对LED的程序进行扩展,增加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!也是比较重要的一部分!
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设置成输出模式的那个函数!
第一步:使用gpio_request申请要是用的一个GPIO。
第二步:gpio_direction_input/gpio_direction_output设置成输入/输出模式。
第三步:设置输出值gpio_set_value,获取gpio_get_value.
通过以上三步和上一章节LED驱动框架编写LED的驱动程序!
- #include
// module_init module_exit - #include
// __init __exit - #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- ///****此驱动使用的是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"); // 描述模块的别名信息