关于树莓派内核编译和驱动编写(2)

前几天搞定了树莓派2的内核编译运行工作,这几天集中研究了树莓派的gpio操作,那么现在是时候把它搞出来了


我们知道,gpio操作是驱动的基础操作,那么研究一块板子,一个版本的内核,首先要从gpio入手。

基础的gpio操作有以下:

ioremap映射寄存器地址,readl读取寄存器上的数值,writel将数据写入寄存器。

用内核提供的gpio操作宏

现在接触到了一种新的gpio操作方式:使用gpio_chip结构体,用面向对象的思维对gpio的功能进行操作。


在我使用的3.18.16内核中,树莓派的芯片bcm2836的gpio操作是由内核中的bcm2708的gpio操作进行的,大概可以想象两者的相似性很高。

从网上找到的驱动代码附上。


在最开始研究的时候,看芯片手册,使用其提供的地址进行地址映射,然后改写,剧情很狗血,就是失败,无论是总线地址还是物理地址,都是失败的。

原因不明,最后在网上找到了下面的代码,编译运行之后成功了,分析得出其使用gpio_chip结构体的结论。


#include <linux/kernel.h>  
#include <linux/module.h>
#include <linux/device.h> 
#include <mach/platform.h>       
#include <linux/platform_device.h>
#include <linux/types.h>  
#include <linux/fs.h>   
#include <linux/ioctl.h>  
#include <linux/cdev.h>  
#include <linux/delay.h>  
#include <linux/uaccess.h>
#include <linux/init.h> 
#include <linux/gpio.h>
 
//class声明内核模块驱动信息,是UDEV能够自动生成/dev下相应文件
static dev_t pi_led_devno; //设备号
static struct class *pi_led_class;
static struct cdev pi_led_class_dev;
 
struct gpio_chip *gpiochip;
 
#define led_pin 17  //gpio 4
 

static int is_right_chip(struct gpio_chip *chip, void *data) //此函数是 gpiochip_find函数调用内核,之后由内核调用的一个查找gpio_chip结构体的函数
{
 
        if (strcmp(data, chip->label) == 0)
                return 1;
        return 0;
}


//内核加载后的初始化函数.
static int __init pi_led_init(void)
{
   struct device *dev;
   int major; //自动分配主设备号
   major = alloc_chrdev_region(&pi_led_devno,0,1,DRIVER_NAME);
   //register_chrdev 注册字符设备使系统知道有LED这个模块在.
    
   cdev_init(&pi_led_class_dev, &pi_led_dev_fops);
   major = cdev_add(&pi_led_class_dev,pi_led_devno,1);
   //注册class
   pi_led_class = class_create(THIS_MODULE,DRIVER_NAME);
    
   dev = device_create(pi_led_class ,NULL,pi_led_devno,NULL,DRIVER_NAME);
      
   gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); //此函数是用来找到寄存器的gpio_chip结构体的函数,此函数由内核实现 *(1)
   gpiochip->direction_output(gpiochip, led_pin, 1);
   gpiochip->set(gpiochip, led_pin, 1); //*(2)
   printk("pi led init ok!\n");
   return 0;
}
//内核卸载后的销毁函数.
void pi_led_exit(void)
{
gpiochip->set(gpiochip, led_pin, 0);
   gpio_free(led_pin);
   device_destroy(pi_led_class,pi_led_devno);
   class_destroy(pi_led_class);
   cdev_del(&pi_led_class_dev);
   unregister_chrdev_region(pi_led_devno, 1);
   printk("pi led exit ok!\n");
    
}
 
module_init(pi_led_init);
module_exit(pi_led_exit);
 
MODULE_DESCRIPTION("Rasp Led Driver");
MODULE_AUTHOR("52pi.net");
MODULE_LICENSE("GPL");


*(1)"bcm2708_gpio"字符串是要使用的gpio_chip结构体的lable成员,可以理解为其名字,当找到这个gpio结构体之后,可以看到其在bcm2708_gpio.c文件当中,但是这个文件有两个,分别在mach-bcm2708和mach-bcm2709两个文件夹中,简单看了一下,两个文件基本相同,在里面找到:

static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value)

函数,此函数就是*(2)中调用的函数的最终目的地,其实现方式仍然是readl和writel函数,那么也就是说,这两个函数仍然能够使用,我对其进行了打印修改,重新编译内核,内核打印如下信息

[   52.190213] *********addr---0xf320001c //实际读取和写入的地址
[   52.195631] *******1****0x6770696f //读取寄存器上的数据
[   52.200735] *******2****0x6770696f


芯片手册中描述了gpio对应寄存器物理地址应该是0x2020 001c,总线地址0x7e20 001c

查看打印信息发现地址跟着两个都不同,分析可能是bcm2708的对应地址。或者是由内核映射好的寄存器地址,后者可能性更大。


那么之前使用ioremap映射的地址不能用的原因不出意外应该是内核对其寄存器进行限制,不允许随意映射了,这个我还没有找到相关证据。

于是按照0xf320001c的地址重写了自己以前用readl和writel实现的驱动,发现这次成功了,并且重启树莓派之后,这个地址还能用,那么我分析这个

地址已经被内核控制住了,不能够重新映射,或者说即使重新映射,也不能够使用。


很多朋友使用三种应用层的gpio调用方式对其进行操作,然而这并不是内核驱动,而且我分析过wiringPi的源代码,发现其是通过调用mem的驱动程序进行地址映射,从而达到操作寄存器的目的。

mem的设备节点是/dev/mem,主设备号为1,内存,属于字符设备,可以通过内核源码的major 查看主设备号为1的宏,然后搜索此宏,就能找到其驱动程序。


现在找到了一个gpio寄存器的可操作地址,那么有时间我还会分析为什么是这个地址,以及接下来可以对其进行操作,可能写一些其他的驱动程序,但可能不会再发上来,下一步要开始研究uboot,目的是希望能够启动一个bootloader命令行。有兴趣的朋友可以加关注。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值