您的位置:首页 > 移动开发 > Android开发

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

2015-12-09 14:25 369 查看
Android系统要控制硬件设备,首先需要在驱动层写一个驱动,android的底层是Linux,写设备驱动和Linux下写设备驱动一样。

在Linux系统中,设备驱动是以文件的形式表现,可以使用和操作普通文件相同的操作命令对设备文件进行操作,例如打开、关闭、读、写等。

写字符设备驱动步骤:

1. 定义一个file_operations结构体,并在结构体里面定义设备的打开、关闭、读、写、控制等操作函数。

2. 在结构体外实现结构体里面定义的操作函数。

3. 向内核注册和卸载驱动模块。

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



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

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include  <asm/uaccess.h>
#include <linux/io.h>

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寄存器地址如下图表。



二、编译代码,在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.测试驱动程序。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息