字符设备驱动
===========第一个驱动程序:点灯=========================
编写驱动函数
1)定义file_operation结构体,填充(用户定义什么接口,结构体对应有什么成员)
2)在内核中注册设备
早期的办法:register_chrdev(unsigned int major,const char *name,structfile_operations *fops)
3)驱动的入口函数
int first_drv_init(void){
上面定义的注册设备驱动程序的方法
return0;
}
内核如何区分入口函数
4)修饰入口函数
module_init(first_drv_init);
示例代码如下:
/****************************************
*第一个驱动程序
*****************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME"first_led_dev"
Int major;
static int first_led_dev_open(struct inode*inode, struct file *file)
{
printk("first_led_dev_open!\n");
return0;
}
static ssize_t first_led_dev_write(structfile *file, const char *buffer, size_t count, loff_t * ppos)
{
printk("first_led_dev_write!\n");
return0;
}
static struct file_operationsfirst_led_dev_fops = {
.owner = THIS_MODULE,
.open = first_led_dev_open,
.write = first_led_dev_write,
};
/*注册驱动程序*/
static int __init first_led_dev_init(void){
/*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/
//如果主设备号为0,系统会自动分配
Major=register_chrdev(0,DEVICE_NAME,&first_led_dev_fops);
return0;
}
static void __exitfirst_led_dev_exit(void){
/*major和name必须和注册时的值一致*/
unregister_chrdev(major,DEVICE_NAME);
return;
}
module_init(first_led_dev_init);
module_exit(first_led_dev_exit);
MODULE_AUTHOR("RETACN");
MODULE_DESCRIPTION("FIRST LEDdriver");
MODULE_LICENSE("GPL");
5)编译makefile文件
示例代码如下:
KERN_DIR= /mnt/sda3/FriendlyARM/mini6410/linux/linux-2.6.38
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_led_dev.o
6)测试驱动模块 (linux环境下测试)
查看系统设备号 cat /proc/devices
上传模块到开发板
在开发板上先进入/tmp 目录,然后输入 rz。先择文件后上传
装载到系统中
insmodfirst_led_dev.ko
查看
方法一:lsmod | grep word_count
/data/local/tmp # lsmod | grep first_led_dev
first_led_dev 1093 0 - Live 0xbf1c7000
方法二:cat /proc/devices
[root@FriendlyARM /tmp]# cat/proc/devices
Character devices:
...
108 ppp
111 first_led_dev
...
编写测试程序
示例代码如下:
#include
#include
#include
#include
//测试
int main(int argc,char **argv){
intfd;
intval=1;
fd=open("/dev/xxx",O_RDWR);
if(fd<0){
printf("can not open!\n");
}
write(fd,&val,4);
return0;
}
运行程序查看结果如下:
[root@FriendlyARM /tmp]# ./Firstdrvtest
-/bin/sh: ./Firstdrvtest: Permissiondenied
[root@FriendlyARM /tmp]# chmod u+xFirstdrvtest
[root@FriendlyARM /tmp]# ./Firstdrvtest
can not open!
创设备:
[root@FriendlyARM /tmp]# mknod /dev/xxx c111 0
[root@FriendlyARM /tmp]# ./Firstdrvtest
first_led_dev_open!
first_led_dev_write!
====自动创建设备文件==============================
示例代码如下:
/****************************************
*第一个驱动程序
*****************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME"first_led_dev"
static struct class *first_led_dev_class;
static struct device *first_led_dev_class_dev;
int major;
static int first_led_dev_open(struct inode*inode, struct file *file)
{
printk("first_dev_open!\n");
return0;
}
static ssize_t first_led_dev_write(structfile *file, const char *buffer, size_t count, loff_t * ppos)
{
printk("first_led_dev_write!\n");
return0;
}
static struct file_operationsfirst_led_dev_fops = {
.owner = THIS_MODULE,
.open = first_led_dev_open,
.write = first_led_dev_write,
};
/*注册驱动程序*/
static int __init first_led_dev_init(void){
/*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/
//如果主设备号为0,系统会自动分配
major=register_chrdev(0,DEVICE_NAME,&first_led_dev_fops);
first_led_dev_class= class_create(THIS_MODULE, DEVICE_NAME);
//创建设备节点
first_led_dev_class_dev=device_create(first_led_dev_class,//
NULL,//
MKDEV(major,0),//
NULL,//
DEVICE_NAME);//
return0;
}
static void __exitfirst_led_dev_exit(void){
//删除设备节点
device_destroy(first_led_dev_class,MKDEV(major,0));
if(first_led_dev_class){
class_destroy(first_led_dev_class);
}
/*major和name必须和注册时的值一致*/
unregister_chrdev(major,DEVICE_NAME);
return;
}
module_init(first_led_dev_init);
module_exit(first_led_dev_exit);
MODULE_AUTHOR("RETACN");
MODULE_DESCRIPTION("FIRST LEDdriver");
MODULE_LICENSE("GPL");
重新装载,显示如下内容
[root@FriendlyARM /tmp]# insmod first_led_dev.ko
first_drv: Unknown symbol __class_create(err 0)
first_drv: Unknown symbol class_destroy(err 0)
first_drv: Unknown symbol device_create(err 0)
first_drv: Unknown symbol device_destroy(err 0)
insmod: can't insert 'first_led_dev.ko':unknown symbol in module or invalid
parameter
在代码中添加
MODULE_LICENSE("GPL");
重新加载,查看已生成设备节点
[root@FriendlyARM /tmp]# ls /dev/first_led_dev
/dev/first_led_dev
===========完善点灯程序===================
看原理图
GPK4-7
查看芯片手册
GPKCON0 0x7F008800 R/W Port K ConfigurationRegister 0 0x22222222
GPKDAT 0x7F008808 R/W Port K Data RegisterUndefined
GPK4 [19:16] 0000 = Input 0001 = Output
0010= Host I/F DATA[4] 0011 = HSI TX READY
0100= Reserved 0101 = DATA_CF[4]
0110= Reserved 0111 = Reserved
0010
编写代码:
单板的物理地址
驱动中操作的是虚拟地址(ioremap映射)
配置引角:GPKCON0
设置(高低电平):GPKDAT
示例代码如上:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstdrv_class;
static struct device *firstdrv_class_dev;
volatile unsigned long *gpkcon = NULL;
volatile unsigned long *gpkdat = NULL;
static int first_drv_open(struct inode*inode,struct file *file)
{
printk("first_drv_open!\n");
/*配置GPK4-7为输出*/
/*清零*/
*gpkcon&= ~(0xFFFF);
*gpkcon|= 0x11110000;
printk("gpkcon:%u",gpkcon);
return0;
}
static ssize_t first_drv_write(struct file*file,const char __user *buf,size_t count,loff_t * ppos)
{
intval;
copy_from_user(&val,buf,count);
if(val==1){
//点灯
*gpkdat &=~((1<<4)|(1<<5)|(1<<6)|(1<<7));
printk("on gpkdat:%u",gpkdat);
}else{
//灭灯
*gpkdat |=(1<<4)|(1<<5)|(1<<6)|(1<<7);
printk("off gpkdat:%u",gpkdat);
}
printk("first_drv_write!\n");
return0;
}
static struct file_operationsfirst_drv_fops={
.owner= THIS_MODULE,/*这是一个宏,推向编译模块时自动创建__this_module*/
.open= first_drv_open,
.write= first_drv_write,
};
/*入口函数,注册驱动徎序,早期的办法*/
int major;
int first_drv_init(void)
{
/*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/
//设备号写0由系统自动分配
major=register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class= class_create(THIS_MODULE, "firstdrv");
//创建设备节点
firstdrv_class_dev=device_create(firstdrv_class,firstdrv_class_dev,MKDEV(major,0),NULL,"firstdrv-%s","firstdrv");
/*映射虚拟地址*/
gpkcon=(volatile unsigned long *)ioremap(0x7F008800,32);
gpkdat=(volatileunsigned long *)ioremap(0x7F008808,32);
printk("initmethod of gpkcon: %u,gpkdata: %u",gpkcon,gpkdat);
return0;
}
/*出口函数,卸载驱动*/
void first_drv_exit()
{
/*major和name必须和注册时的值一致*/
unregister_chrdev(major,"first_drv");
//删除设备节点
//class_device_unregister(firstdrv_class_dev);
device_destroy(major,firstdrv_class_dev);
class_destroy(firstdrv_class);
/*取消映射*/
iounmap(gpkcon);
iounmap(gpkdat);
}
/*修饰入口函数*/
module_init(first_drv_init);
/*修饰出口函数*/
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
重新编译,下载到开发板
装载驱动,检查是否安装成功
编写测试程序:
示例代码如下:
#include
#include
#include
#include
/**
*firstdrvtest on 点灯
*firstdrvtest off 灭灯
**/
int main(int argc,char **argv){
intfd;
intval=1;
fd=open("/dev/firstdrv-firstdrv",O_RDWR);
if(fd<0){
printf("can not open!\n");
}
if(argc!=2){
printf("Usage: \n");
printf("%s
}
if(strcmp(argv[1],"on")==0){
val=1;
}else{
val=0;
}
write(fd,&val,4);
return0;
}