您的位置:首页 > 其它

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

2015-06-26 10:35 316 查看
前几天搞定了树莓派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命令行。有兴趣的朋友可以加关注。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: