为了方便测试,这里采取控制LED点灯的方式测试,测试的硬件是Cubieboard和扩展板DVK522(见下图)。
一、在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寄存器地址如下图表。
二、编译代码,在Ubuntu操作系统完成。
linux驱动的加载方式,可以采用系统启动后动态加载驱动模块的方式,在这里将驱动直接编译进内核中,系统启动过程加载模块驱动。
1. 在myled文件夹新建Kconfig和Makefile两个文件,Kconfig是在编译前执行配置命令make menuconfig时用到的,而Makefile是执行编译命令make是用到的。
2. 在Kconfig中添加如下内容:8. 在lichee目录下编译,编译成功后,在drivers/myled可以看到myled.ko。
9. 重新编译打包android系统,烧录到SD卡或开发板的NAND Flash。
三、测试。
上电开机启动android系统,连接开发板串口登录进系统,可以在/dev和/sys/class下看到myled节点,说明编译的myled驱动加载到设备驱动成功。
内核驱动完成后,需要写一个测试程序在用户空间测试一下驱动能否控制到LED灯,请见下一节:Android驱动开发-- 2.测试驱动程序。