字符驱动模块charmem.c
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #include "charmem.h" #define CHARMEM_SIZE 4096 /*设备内存大小*/ #define CHARMEM_MAJOR 261 /*驱动的主设备号*/ static int mem_major=CHARMEM_MAJOR; /*设备的主设备号*/ module_param(mem_major,int,S_IRUGO); /*将主设备号作为参数传入到模块*/ /*自定义的字符驱动存储结构体*/ struct struct_mem_dev { struct cdev cdev; /*字符设备驱动结构体(内核)*/ unsigned char mem[CHARMEM_SIZE]; /*字符设备分配的内存*/ }; struct struct_mem_dev *mem_devp; /*程序用结构体*/ /*字符驱动打开函数*/ static int mem_open(struct inode *inode,struct file *filp) { filp->private_data=mem_devp; return 0; } /*字符驱动释放函数*/ static int mem_release(struct inode *inode,struct file *filp) { return 0; } /*字符驱动特定指令函数*/ static long mem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) { //struct struct_mem_dev *dev=filp->private_data; int result=0,ioarg=0; //char * chararg; /*检查指令是否合法*/ if(_IOC_TYPE(cmd)!=MEM_MAGIC) /*如果指令类型不等于预定义的幻数*/ return -EINVAL; if(_IOC_NR(cmd)>MEM_MAXNR) /*指令序号最大值*/ return -EINVAL; /*检查权限*/ if(_IOC_DIR(cmd) & _IOC_READ) result=!access_ok(VERIFY_WRITE,(void *)arg,_IOC_SIZE(cmd)); else if(_IOC_DIR(cmd) & _IOC_WRITE) result=!access_ok(VERIFY_READ,(void *)arg,_IOC_SIZE(cmd)); if(result) return -EFAULT; /*执行指令*/ switch(cmd) { case MEM_CLEAR: /*清理命令*/ printk(KERN_INFO "Kernel Execute ‘MEM_CLEAR‘ Command!\n"); break; case MEM_GETDATA: /*获取数据ioarg-->arg*/ ioarg=2016; result=__put_user(ioarg,(int *)arg); break; case MEM_SETDATA: /*设置参数arg-->ioarg*/ result=__get_user(ioarg,(int *)arg); printk(KERN_INFO "Kernel Execute ‘MEM_SETDATA‘ Command,ioarg=%d!\n",ioarg); break; // case MEM_PRINTSTR: // result=__get_user(chararg,(char *)arg1); // printk(KERN_INFO "Kernel Execute ‘MEM_PRINTSTR‘ Command,chararg=%s\n",chararg); // break; default: return -EINVAL; /*返回错误*/ } return 0; } /*字符驱动读取函数 struct file *filp: 文件指针 char __user *buf: 用户空间内存 size_t size: 读取的数量 loff_t *poss: 文件内指针偏移量 */ static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *poss) { unsigned long p=*poss; /*文件内指针位置*/ unsigned int count=size; /*要读取的数据大小*/ int result=0; /*返回值*/ struct struct_mem_dev *dev=filp->private_data; /*获取保存在filp中的字符驱动数据*/ if(p>=CHARMEM_SIZE) /*如果当前指针大于文件内存数*/ return 0; if(count>CHARMEM_SIZE-p) /*要读取的数量大于剩余的数量*/ count=CHARMEM_SIZE-p; if(copy_to_user(buf,dev->mem+p,count)) { result=-EINVAL; } else { *poss=count+*poss; /*更新文件内指针位置*/ result=count; /*返回读取到的数据数量*/ printk(KERN_INFO "read [%d] bytes from %lu\n",count,p); } return result; } /*字符驱动写入函数*/ static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *poss) { unsigned long p=*poss; unsigned int count=size; int result=0; struct struct_mem_dev *dev=filp->private_data; /*获取保存在filp中的字符驱动数据*/ if(p>=CHARMEM_SIZE) /*如果要写入的大小超过了预设的内存大小*/ return 0; if(count>CHARMEM_SIZE-p) /*如果要写入的大小超过了剩余的内存大小,则删掉剩余额数据*/ count=CHARMEM_SIZE-p; /*将用户空间的数据写入到内核空间中*/ if(copy_from_user(dev->mem+p,buf,count)) { *poss=count+*poss; result=count; printk(KERN_INFO "write [%u] bytes from %lu\n",count,p); } return result; } /*文件内指针移动函数*/ static loff_t mem_llseek(struct file *filp,loff_t offset,int orig) { loff_t result=0; switch(orig) { case 0: /*0表示SEEK_SET,文件开头*/ if(offset<0) /*位置错误*/ { result=-EINVAL; break; } if((unsigned int)offset>CHARMEM_SIZE) /*位置超出预设内存范围*/ { result=-EINVAL; break; } filp->f_pos=(unsigned int)offset; result=filp->f_pos; break; case 1: /*1表示SEEK_CUR,文件当前位置*/ if((filp->f_pos+offset)<CHARMEM_SIZE) /*文件指针超出预设内存范围*/ { result=-EINVAL; break; } if((filp->f_pos+offset)<0) /*文件指针偏移错误*/ { result=-EINVAL; break; } filp->f_pos=filp->f_pos+offset; /*更新文件指针*/ result=filp->f_pos; break; default: /*默认操作*/ result=-EINVAL; break; } return result; } /*文件操作集*/ static const struct file_operations mem_fops= { .owner = THIS_MODULE, /*指向拥有这个模块的指针*/ .llseek = mem_llseek, /*文件内指针偏移操作*/ .read = mem_read, /*字符设备读取*/ .write = mem_write, /*字符设备写入*/ .open = mem_open, /*字符设备打开*/ .release = mem_release, /*字符设备释放*/ .unlocked_ioctl = mem_ioctl, /*向字符设备发出特定指令*/ }; /*字符设备设置函数*/ static void mem_setup_dev(struct struct_mem_dev *dev,int index) { int err,devno; devno=MKDEV(mem_major,index); /*利用MKDEV宏生成字符设备号*/ /*初始化字符设备*/ cdev_init(&dev->cdev,&mem_fops); dev->cdev.owner=THIS_MODULE; err=cdev_add(&dev->cdev,devno,1); if(err) { printk(KERN_INFO "mem_setup_dev ERROR:%d adding cdev[%d].\n",err,index); } } /*模块初始化*/ static int __init mymem_init(void) { int result; dev_t devno=MKDEV(mem_major,0); if(mem_major) /*如果主设备号已经预先设置*/ { result=register_chrdev_region(devno,1,"chardev"); /*静态分配设备号*/ } else { result=alloc_chrdev_region(&devno,0,1,"chardev"); /*动态分配设备号*/ mem_major=MAJOR(devno); /*获取分配到的主设备号*/ } if(result<0) return result; /*为驱动分配内核物理内存,大小为4K*/ mem_devp=kzalloc(sizeof(struct struct_mem_dev),GFP_KERNEL); if(!mem_devp) { result=-ENOMEM; goto fail_malloc; } mem_setup_dev(mem_devp,0); return 0; fail_malloc: unregister_chrdev_region(devno,1); /*销毁之前注册的字符设备*/ return result; } /*模块注销函数*/ static void __exit mymem_exit(void) { cdev_del(&mem_devp->cdev); /*从系统中删除掉该字符设备*/ kfree(mem_devp); /*释放分配的内存*/ unregister_chrdev_region(MKDEV(mem_major,0),1); /*销毁之前注册的字符设备*/ } module_init(mymem_init); module_exit(mymem_exit); /*模块声明*/ MODULE_AUTHOR("EDISON REN"); MODULE_LICENSE("GPL");
功能预定义charmem.h
#ifndef _CHARMEM_H #define _CHARMEM_H #include <linux/ioctl.h> /*ioctl指令*/ #define MEM_MAGIC ‘j‘ /*幻数*/ #define MEM_CLEAR _IO(MEM_MAGIC,0x1a) /*清理指令*/ #define MEM_SETDATA _IOW(MEM_MAGIC,0x1b,int) /*设置指令*/ #define MEM_GETDATA _IOR(MEM_MAGIC,0x1c,int) /*读取指令*/ //#define MEM_PRINTSTR _IOW(MEM_MAGIC,0x1d,char*) /*字符串*/ #define MEM_MAXNR 0x1c /*指令序号最大值*/ #endif
应用程序charmemapp.c
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "charmem.h" /*字符设备驱动名称*/ #define DEV_PATH "/dev/chardev" int main(void) { int fd,result=0,arg; //char * chararg="hello world!"; fd=open(DEV_PATH,O_RDWR); if(fd<0) { printf("打开字符设备文件失败!\n"); } else { printf("打开字符设备文件成功!\n"); printf("*****************************\n"); /*执行MEM_CLEAR指令*/ result=ioctl(fd,MEM_CLEAR); if(result<0) printf("执行‘MEM_CLEAR‘指令失败!\n"); else printf("执行指令‘MEM_CLEAR‘成功!\n"); printf("*****************************\n"); /*执行MEM_GETDATA指令*/ result=ioctl(fd,MEM_GETDATA,&arg); if(result<0) printf("执行‘MEM_GETDATA‘指令失败!\n"); else printf("执行‘MEM_GETDATA‘指令成功.读取的数据:%d!\n",arg); printf("*****************************\n"); /*执行MEM_SETDATA指令*/ arg=2016; result=ioctl(fd,MEM_SETDATA,&arg); if(result<0) printf("执行‘MEM_SETDATA‘指令失败!\n"); else printf("执行‘MEM_SETDATA‘指令成功!\n"); /*执行MEM_PRINTSTR指令*/ // result=ioctl(fd,MEM_PRINTSTR,&chararg); // if(result<0) // printf("执行‘MEM_PRINTSTR‘指令失败!\n"); // else // printf("执行‘MEM_PRINTSTR‘指令成功!\n"); } close(fd); return 0; }
Makefile文件
obj-m := charmem.o KDIR := /usr/src/linux-headers-3.13.0-32-generic all: make -C $(KDIR) M=$(PWD) modules clean: rm -rf *.ko *.o *.mod.o *.mod.c *.symvers *.mod *.order
原文:http://www.cnblogs.com/foggia2004/p/5551258.html