//================================================================================================================================= [1] 一些常用函数接口/结构体 [ MKDEV(); #获得设备号 #32位 = major高12bit+次设备号低20bit copy_to_user() #用户层 --> 内核层 copy_from_user() #内核层 --> 用户层 ioremap(); kmalloc(); readl(); #读寄存器 writel(); #写寄存器 MODULE_LICENSE() printk() struct class struct device struct file_operations register_chrdev() class_create() device_create() device_destroy() class_destroy() unregister_chrdev() kfree() iounmap() module_init() module_exit() open() write() read() close() ] //================================================================================================================================= [2] 一些设备驱动常识 [ 1>>/proc/devices #注册设备 ]
字符设备实验
cdevice.c
字符设备实验 1:用户app.c输入"1"/"0"开关灯功能 ===================================================== ======================cdevice.c开始====================== ===================================================== #include<linux/init.h> #include<linux/module.h> #include<linux/fs.h> #include<linux/device.h> #include <asm/errno.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/slab.h> #define GPL2CON 0x11000100 //寄存器物理地址 #define GPL2DAT 0x11000104 #define CON_RESET 0x00000000 #define DAT_RESET 0x00 #define GPL2_0_OUT 0x1//输出模式 #define GPL2_0_DATSET 0x1//高电平 MODULE_LICENSE("GPL"); //结构体存储零碎变量信息 struct cdev_led1 { volatile unsigned long *gpl2con; //控制寄存器映射地址 volatile unsigned long *gpl2dat; //数据寄存器映射地址 char *name; //设备节点文件名字 unsigned int major; //主设备号 unsigned int minor; //次设备号 struct class *cdev_led1_class; //类结构体 struct device *cdev_led1_node; //设备信息结构体 ##############注意结构体里面只能是指针 }; struct cdev_led1 *cled1; //创建结构体变量 ssize_t cdev_led1_read (struct file *, char __user *, size_t , loff_t *); //对接应用层的read函数 ssize_t cdev_led1_write (struct file *, const char __user *, size_t , loff_t *);//对接应用层的write函数 int cdev_led1_open (struct inode *, struct file *); //对接应用层的open函数 int cdev_led1_close (struct inode *, struct file *); //对接应用层的close函数 struct file_operations fops = { //文件操作结构体,注意赋值有"=",成员赋值完后是","最后的成员复制完不写";" .read = cdev_led1_read, .write= cdev_led1_write, .open= cdev_led1_open, .release= cdev_led1_close }; /*cdev_led1_read() <--> read() //读取驱动文件数据 * struct file //文件路径 * char __user //接收用户数据的缓存 * size_t //数据个数 * loff_t //偏移量 */ ssize_t cdev_led1_read (struct file *cdev1_file, char __user *cdev1_buf, size_t N, loff_t *offset) { int tem = 0; copy_to_user(cdev1_buf, &tem, 1); //内核层---数据---> 用户层 printk("\n__kernel__ :%s () is %d\n",__FUNCTION__,tem); //打印向用户层发送的数据 return 0; } /*cdev_led1_write() <--> write() //向驱动文件写数据 * struct file //文件路径 * char __user //接收用户数据的缓存 * size_t //数据个数 * loff_t //偏移量 */ ssize_t cdev_led1_write (struct file *cdev1_file, const char __user *cdev1_buf, size_t N, loff_t *offset) { printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__); unsigned int reg_vlu = 0; //保存寄存器映射地址的值 char tem = ' '; __copy_from_user(&tem, cdev1_buf,1); //用户层 ---数据---> 内核 printk("\n__kernel__ :cdev_led1_write() is %d\n",tem); //打印从用户层接收的数据 if(tem == '0') //关灯 { reg_vlu = readl(cled1->gpl2con); //读取映射地址数据 reg_vlu |= (GPL2_0_OUT << 0); writel(reg_vlu,cled1->gpl2con); //向映射地址写数据 reg_vlu = readl(cled1->gpl2dat); //读取映射地址数据 reg_vlu &= ~(0x1<<0); writel(reg_vlu,cled1->gpl2dat); //向映射地址写数据 printk("\n__kernel__ :%s:close led\n",__FUNCTION__); } if(tem == '1') //开灯 { reg_vlu = readl(cled1->gpl2con); //读取映射地址数据 reg_vlu |= (GPL2_0_OUT << 0); writel(reg_vlu,cled1->gpl2con); //向映射地址写数据 reg_vlu = readl(cled1->gpl2dat); //读取映射地址数据 reg_vlu |= (1<<0); writel(reg_vlu,cled1->gpl2dat); //向映射地址写数据 } printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__); return 0; } /*cdev_led1_open() <--> open() * struct inode //文件文件描述符 * struct file //文件路径 */ int cdev_led1_open (struct inode *cdev1_inode, struct file *cdev1_file) { printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__); printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__); return 0; } /*cdev_led1_close() <--> close() * struct inode //文件文件描述符 * struct file //文件路径 */ int cdev_led1_close (struct inode *cdev1_inode, struct file *cdev1_file) { printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__); printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__); return 0; } //模块加载函数 static int cde_led1_init(void) { printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__); int ret = 0; //第一步:实例化结构体变量 // 分配堆内存 ; 返回值为地址,所以cled1是结构体指针 cled1 = kmalloc(sizeof(struct cdev_led1), GFP_KERNEL); if(cled1 == NULL) { printk("\n kmalloc error\n"); goto err0; //错误标签0 } //第二步:结构体变量赋值 cled1->major = 100; cled1->minor = 1; cled1->name = "cdev1"; cled1->gpl2con = ioremap(GPL2CON,8); cled1->gpl2dat = cled1->gpl2con +1 ; //第三步:注册设备,申请设备内存资源:驱动操作函数结构体,主设备号,设备名 ret = register_chrdev(cled1->major,cled1->name,&fops); if(ret == -ENOMEM) { printk("\n register_chrdev error\n"); goto err1; } //第四步:申请类 cled1->cdev_led1_class = class_create(THIS_MODULE, "cdev_led1_class"); if(cled1->cdev_led1_class == NULL) { printk("\n class_create error\n"); goto err2; } //第五步:自动创建驱动节点 = 驱动文件 /*参数2:父亲,一般NULL ; ;参数4:私有数据填NULL ;参数5:设备节点名字*/ cled1->cdev_led1_node = device_create(cled1->cdev_led1_class,NULL,MKDEV(cled1->major,cled1->minor), NULL, "cdev1"); if(cled1->cdev_led1_node == NULL) { printk("\n device_create error\n"); goto err3; } printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__); return 0; //错误处理(释放与回收动作) err3: //第一步:卸载驱动节点 device_destroy(cled1->cdev_led1_class,MKDEV(cled1->major,cled1->minor)); err2: //第二步:卸载类 class_destroy(cled1->cdev_led1_class); err1: //第三步:释放驱动占用的内存 unregister_chrdev(cled1->major,cled1->name); err0: //第四步:释放自定义的结构体对象 kfree(cled1); } //模块卸载函数 static void cde_led1_exit(void) { printk("\n=====__kernel__ :%s in=====\n",__FUNCTION__); //第一步:卸载驱动节点 device_destroy(cled1->cdev_led1_class,MKDEV(cled1->major,cled1->minor)); //第二步:卸载类 class_destroy(cled1->cdev_led1_class); //第三步:注销设备,释放驱动占用的内存 unregister_chrdev(cled1->major,cled1->name); //第四步:解除寄存器物理地址映射 iounmap(cled1->gpl2con); //第五步:释放自定义的结构体对象 kfree(cled1); printk("\n=====__kernel__ :%s out=====\n",__FUNCTION__); } module_init(cde_led1_init); //定位模块初始化函数,用户层insmod指令使用该函数跳转到cde_led1_init module_exit(cde_led1_exit);//定位模块卸载函数,用户层rmmod指令使用该函数跳转到cde_led1_exit ===================================================== ======================cdevice.c结束====================== =====================================================
app.c
===================================================== ======================app.c====================== ===================================================== #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> //用户层函数接收main传参 void main(int argc,char*argv[]) { printf("\n=====__app__:in=====\n"); int ret,tem,fd = 0; char ledstate; //第一步:接收用户开关指令,"1" = 开 ; "0" = 关 ledstate = *(char*)argv[1]; //第二步:打开驱动节点 fd = open("/dev/cdev1",O_RDWR); if(fd == -1) { perror("\nopen error\n"); } //第三步:向驱动写"0" / "1"指令 ret = write(fd,&ledstate,1); if(ret == -1) { perror("\nwrite error\n"); } //第三步:读取驱动数据,并打印 ret = read(fd,&tem,1); printf("\n__app__:read() is %d\n",tem); if(ret == -1) { perror("\nread error\n"); } close(fd); printf("\n=====__app__:out=====\n"); }
Makefile
obj-m += cdevice.o #项目模块添加cdevice.o
KERNEL = /home/topeet/iTop4412_Kernel_3.0 #内核源码路径
EXE = app
GCC = /home/topeet/arm-2009q3/bin/arm-none-linux-gnueabi-gcc #编译工具路径
PWD = $(shell pwd) #当前路径
all: make -C $(KERNEL) M=$(PWD) modules #生成驱动模块
$(GCC) app.c -o $(EXE) #编译应用层app.c
install: cp cdevice.ko $(EXE) /home/topeet/minilinux/module_pro2/ #移动.ko 与app.exe到nfs共享目录
运行效果:
[root@iTOP-4412]# insmod cdevice.ko //加载模块 [13858.429220] [13858.429232] =====__kernel__ :cde_led1_init in===== [13858.451033] [13858.451045] =====__kernel__ :cde_led1_init out===== [root@iTOP-4412]# ./app 1 //开灯 =====__app__:in[13875.837426] [13875.837445] =====__kernel__ :cdev_led1_open in===== [13875.843701] [13875.843715] =====__kernel__ :cdev_led1_open out===== [13875.850172] [13875.850187] =====__kernel__ :cdev_led1_write in===== [13875.856456] [13875.856468] __kernel__ :cdev_led1_write() is 49 [13875.862441] [13875.862455] =====__kernel__ :cdev_led1_write out===== [13875.869107] [13875.869121] __kernel__ :cdev_led1_read () is 0 [13875.875346] [13875.875362] =====__kernel__ :cdev_led1_close in===== [13875.881371] [13875.881385] =====__kernel__ :cdev_led1_close out===== ===== __app__:read() is 0 =====__app__:out===== [root@iTOP-4412]# ./app 0 //关灯 =====__app__:in[13884.293018] [13884.293038] =====__kernel__ :cdev_led1_open in===== ===== [13884.304336] [13884.304353] =====__kernel__ :cdev_led1_open out===== [13884.310212] [13884.310229] =====__kernel__ :cdev_led1_write in===== [13884.315987] [13884.316000] __kernel__ :cdev_led1_write() is 48 [13884.321859] [13884.321872] __kernel__ :cdev_led1_write:close led [13884.328097] [13884.328111] =====__kernel__ :cdev_led1_write out===== [13884.334619] [13884.334632] __kernel__ :cdev_led1_read () is 0 __app__:read() is[13884.350304] [13884.350320] =====__kernel__ :cdev_led1_close in===== 0 [13884.365071] [13884.365086] =====__kernel__ :cdev_led1_close out===== =====__app__:out===== [root@iTOP-4412]# rmmod cdevice //卸载驱动 [13917.257985] [13917.258003] =====__kernel__ :cde_led1_exit in===== [13917.277571] [13917.277588] =====__kernel__ :cde_led1_exit out===== [root@iTOP-4412]#