Android驱动开发-- 1.内核driver层

Android系统要控制硬件设备,首先需要在驱动层写一个驱动,android的底层是Linux,写设备驱动和Linux下写设备驱动一样。
在Linux系统中,设备驱动是以文件的形式表现,可以使用和操作普通文件相同的操作命令对设备文件进行操作,例如打开、关闭、读、写等。
写字符设备驱动步骤:
1. 定义一个file_operations结构体,并在结构体里面定义设备的打开、关闭、读、写、控制等操作函数。
2. 在结构体外实现结构体里面定义的操作函数。
3. 向内核注册和卸载驱动模块。

为了方便测试,这里采取控制LED点灯的方式测试,测试的硬件是Cubieboard和扩展板DVK522(见下图)。

Android驱动开发-- 1.内核driver层_第1张图片


一、在lichee/linux-3.4/driver/char目录下新建myled文件夹,新建一个LEDdriver.c文件,代码如下:

#include 
#include 
#include 
#include 
#include 
#include  
#include 

static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
 
//配置GPIO为输出口  
static int cubieboard2_leds_open(struct inode *inode, struct file *file)
{
    printk(KERN_ALERT "cubieboard2_leds_open\n");
    /* 设置PE4, PE5, PE6 为输出*/
    *gpfcon &= ~ ((0x7<<( 4*4)) | (0x7<<( 5*4)) | (0x7<<( 6*4)) );
    *gpfcon |= ((0x1<<( 4*4)) | (0x1<<( 5*4)) | (0x1<<( 6*4)) );

     return 0;
}

//GPIO输出高低电平以控制LED灯亮灭  
static int cubieboard2_leds_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    int val_dev;

    //将用户空间的数据拷贝内核
    copy_from_user(&val_dev, buf, count);   //copy_to _user();
    
    if (val_dev == 1)
    {
        //LED on
        printk(KERN_ALERT "write val==1, LED on\n");
        *gpfdat&= ~(1<<4) | (1<<5) | (1<<6);        
    }
    if (val_dev == 0)
    {
        //LED off
        printk(KERN_ALERT "write val==0, LED off\n");
        *gpfdat|= (1<<4) | (1<<5) | (1<<6);
    }
    
    return 0;
}

static struct file_operations cubieboard2_leds_ops = {
    .owner = THIS_MODULE,
    .open = cubieboard2_leds_open,
    .write = cubieboard2_leds_write,
};

int major;

int cubieboard2_leds_init(void)
{
    //注册file_operations结构体
    //动态分配驱动号
    major=register_chrdev(0,"myled",&cubieboard2_leds_ops); 

    //注册设备类
    firstdrv_class=class_create(THIS_MODULE, "myled");
    if(IS_ERR(firstdrv_class))
        return RTP_ERR(firstdrv_class);

    //创建设备节点
    firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(major,0), NULL, "myled");
    if (unlikely(IS_ERR(firstdrv_class_dev)))
        return RTP_ERR(firstdrv_class_dev);

     //映射寄存器,寄存器地址查阅A20规格书
    gpfcon = (volatile unsigned long *)ioremap(0x01c20890,16);
    gpfdat = (volatile unsigned long *)ioremap(0x01c208a0,16);
    
    return 0;
}

void cubieboard2_leds_exit(void)
{
    //卸载驱动号
    unregister_chrdev(major,"myled");  
    //释放设备节点
    device_unregister(firstdrv_class_dev);
    //销毁设备类
     class_destroy(firstdrv_class);

    //释放GPIO寄存器映射
    iounmap(gpfcon);
    iounmap(gpfdat);
   
    return 0;
}

module_init(cubieboard2_leds_init);
module_exit(cubieboard2_leds_exit);

MODULE_AUTHOR("Ken Qiu");
MODULE_DESCRIPTION("LED driver");
MODULE_LICENSE("GPL");


Cubieboard DVK522扩展板上的LED0-LED2对应CPU的PE4-PE6引脚,CPU A20的GPIO寄存器地址如下图表。

Android驱动开发-- 1.内核driver层_第2张图片

二、编译代码,在Ubuntu操作系统完成。

linux驱动的加载方式,可以采用系统启动后动态加载驱动模块的方式,在这里将驱动直接编译进内核中,系统启动过程加载模块驱动。

1. 在myled文件夹新建Kconfig和Makefile两个文件,Kconfig是在编译前执行配置命令make menuconfig时用到的,而Makefile是执行编译命令make是用到的。

2. 在Kconfig中添加如下内容:
       config MYLED
           tristate "My LEDdriver Driver"
           default y
           help
           This is the first android LEDdriver.

default y表示默认将该驱动编译进内核。

3. 在makefile中添加如下内容:
obj-$(CONFIG_MYLED)        += myled.o  

4. 修改drivers/kconfig文件,在endmenu前添加:
 source "drivers/myled/Kconfig"

5. 修改drivers/Makefile文件,添加:
obj-$(CONFIG_MYLED) += myled/

6. 修改arch/arm/kconfig文件,添加:
source "drivers/myled/Kconfig"

7. 在linux-3.4目录下执行make ARCH=arm menuconfig,查看是否有My LEDdriver Driver,勾选上*号。

8. 在lichee目录下编译,编译成功后,在drivers/myled可以看到myled.ko。


9. 重新编译打包android系统,烧录到SD卡或开发板的NAND Flash。


三、测试。

上电开机启动android系统,连接开发板串口登录进系统,可以在/dev和/sys/class下看到myled节点,说明编译的myled驱动加载到设备驱动成功。


内核驱动完成后,需要写一个测试程序在用户空间测试一下驱动能否控制到LED灯,请见下一节:Android驱动开发-- 2.测试驱动程序。


你可能感兴趣的:(android系统)