字符驱动

来源:互联网 发布:编写汇编语言的软件 编辑:程序博客网 时间:2024/06/05 18:22
#include <linux/init.h>
#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/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
//设备驱动应该包含的头文件
#define MYMODE_SIZE 0x1000
#define MYMODE_MAJOR 240
#define MEM_CLEAR 0x1

static int mymode_major = MYMODE_MAJOR; //设备主节点号

struct mymode_dev{ //定义 设备结构体
struct cdev cdev;
unsigned char mem[MYMODE_SIZE];
};

struct mymode_dev *mymode; //设备结构体指针

static ssize_t mymode_read(struct file * filp,char __user *buf,size_t size,loff_t *ppos) //设备读操作函数
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mymode_dev *modedev = filp->private_data;
if(p>MYMODE_SIZE)
{
printk(KERN_INFO"err of poss %d\n",p);
return 0;
}

if(count > (MYMODE_SIZE-p))
count = MYMODE_SIZE-p;
if(copy_to_user(buf,(void *)(modedev->mem+p),count)){
ret = - EFAULT;
}else{
*ppos +=count;
ret = count;
printk(KERN_INFO"read %d bytes from %d\n",count,p);
}
return ret;
}
static ssize_t mymode_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos) //设备写操作函数
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mymode_dev *modedev = filp->private_data;

if(count > (MYMODE_SIZE-p))
count = MYMODE_SIZE-p;
if(copy_from_user((modedev->mem+p),buf,count)){
ret = - EFAULT;
}else{
*ppos +=count;
ret = count;
printk(KERN_INFO"write %d bytes from %d\n",count,p);
}
return ret;
}
static loff_t mymode_llseek(struct file *filep,loff_t offset,int orig) //设置偏移定位操作函数
{
loff_t ret = 0;
switch(orig)
{
case 0:
if(offset < 0 )
{
printk(KERN_INFO"ERR OFFSET\n");
ret = - EINVAL;
break;
}
if(offset >= MYMODE_SIZE)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;
}
filep->f_pos = offset;
ret = offset;

break;
case 1:
if(filep->f_pos + offset > MYMODE_SIZE-1)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;

}else if(filep->f_pos + offset <0){
filep->f_pos = 0;
ret = 0 ;
break;


}
filep->f_pos += offset;
ret = offset;
break;
case 2:
if(filep->f_pos + offset > MYMODE_SIZE-1)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;

}else if(filep->f_pos + offset <0){
filep->f_pos = 0;
ret = 0 ;
break;


}
filep->f_pos += offset;
ret = offset;
break;
default:
break;
}
}
static int mymode_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd ,unsigned long arg) //设备ioctl 函数只简单的实现了对内存区域的清0 当然你也可以自己添加一些你想要实现的功能。不管怎样你得电脑你做主。
{
struct mymode_dev *modedev = filep->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(modedev->mem,0,MYMODE_SIZE);
printk(KERN_INFO"Clear the mem to be zero\n");
break;
default:
printk(KERN_INFO"Clear the mem to be zero\n");
break;
}
return 0;
}
static int mymode_open(struct inode *inodep,struct file *filep)
{
filep->private_data = mymode;
return 0;
;
}
static int mymode_release(struct inode *inodep,struct file *filep)
{
return 0;
}
static const struct file_operations mymode_fops={ // cdev 设备的file_operations 结构体 主要实现了 开关 读写 设置偏移量 和ioctl 函数
.owner = THIS_MODULE,
.llseek = mymode_llseek,
.read = mymode_read,
.write = mymode_write,
.ioctl = mymode_ioctl,
.open = mymode_open,
.release = mymode_release,
};
int mymode_init(void) //模块加载初始化函数。设备的入口函数
{
int err,res;
dev_t devno = MKDEV(mymode_major,0);
if(mymode_major)
res = register_chrdev_region(&devno,1,"mymode");//register the cdev no
else{
res = alloc_chrdev_region(&devno,0,1,"mymode");
mymode_major = MAJOR(devno);
}
if(res < 0)
return res;
mymode = kmalloc(sizeof(struct mymode_dev),GFP_KERNEL);
if(mymode == NULL)
{
printk(KERN_INFO"kmalloc failed\n");
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
return -ENOMEM;
}
memset(mymode,0,sizeof(struct mymode_dev));
cdev_init(&mymode->cdev,&mymode_fops);
mymode->cdev.owner = THIS_MODULE;
err = cdev_add(&mymode->cdev,devno,1);
if(err){
printk("Error %d add mymode ",err);
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);

}
printk("mymode insert\n");
return 0;
}
void mymode_exit(void)//模块卸载函数 其主要内容是相对于设备初始化函数的反操作。
{
cdev_del(&mymode->cdev);
kfree(mymode);
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
printk("mymode exit\n");
}


module_init(mymode_init);
module_exit(mymode_exit);

MODULE_AUTHOR("pink hou ");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("First Module");
MODULE_ALIAS("A simplest module");

这个是完整的C代码 具体的makefile 参考我的blog借鉴一下吧。这是我通过验证的 绝对没问题。
原文地址:http://www.ebhou.com/post/chrcdev.html
参考资料:http://www.ebhou.com/post/chrcdev.html
 
obj-m :=mymode.o
 #我的开发板内核目录,如果要在主机上验证。使用‘#’注释下面语句
KERNELDIR:=/home/pink/linux-2.6.28.6
 #我的主机内核头文件位置,如果需要在注意上验证,取消对下面语句的注释。
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
 
modules :
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) module_install
 
clean :
rm -rf *.o *.ko *.mod.c *.cmd *.depend .tmp_versions *.core *.order *.symvers .mymode.*
模块编译:#make modules 或者直接make
插入模块 #insmod mymode.ko
模块移除 #rmmod mymode
手动添加设备节点 #mknod mymode c 240 0 //在当前目录添加一个字符设备节点。
模块验证:可以自己写一个简单的程序,对该节点打开 读写 ioctl