首先,先理清一下简单字符设备驱动程序的思路:
(1)申请设备号
动态申请:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
静态申请:int register_chrdev_region(dev_t from, unsigned count, const char *name)
成功返回0,失败返回负数,并置于errno
(2)分配cdev ,可以使用struct cdev *cdev_alloc(void) ,或者静态定义全局cdev变量
(3)初始化cdev
若使用动态分配,则需要进行初始化:void cdev_init(struct cdev *cdev, const struct file_operations *fops) ,mem_cdev.owner = THIS_MODULE;
若动态内存定义初始化:struct cdev *mem_cdev = cdev_alloc(); mem_cdev->ops = &fops; mem_cdev->owner = THIS_MODULE
(4)添加cdev
int cdev_add(struct cdev *p, dev_t dev,unsigned count)
若使用内存模拟字符设备,则还需申请空间:
mem_devp = kmalloc( 2 * sizeof(struct mem_dev), GFP_KERNEL);
if(!mem_devp){
result = -ENOMEM;
goto fail_malloc;
}
memset(mem_devp, 0, sizeof(struct mem_dev));
for(i = 0; i < 2; i++)
{
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
memset(mem_devp[i].data, 0, MEMDEV_SIZE);
}
申请失败情况下,记得注销设备号,使用void unregister_chrdev_region(dev_t from, unsigned count)
(5)构造file_operations结构体(结构体字段的初始化)
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,
};
(6)实现file_operations支持的函数
int mem_open(struct inode *inode, struct file *filp)
{
struct mem_dev *dev;
int num = MINOR(inode->i_rdev);
if(num >= MEMDEV_NR_DEVS)
return -ENODEV;
dev = &mem_devp[num];
filp->private_data = dev;
return 0;
}
static ssize_t mem_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 mem_dev *dev = filp->private_data;
if(p > MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
if(copy_to_user(buf, (void *)(dev->data + p), count)){
ret = -EFAULT;
}
else{
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes from %ld", count, p);
}
return ret;
}
static ssize_t mem_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 mem_dev *dev = filp->private_data;
if(p > MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
if(copy_from_user(dev->data + p, buf, count)){
ret = -EFAULT;
}
else{
*ppos += count;
ret = count;
printk(KERN_INFO "writen %d bytes from %ld", count, p);
}
return ret;
}
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{
loff_t newpos;
switch(whence){
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos+offset;
break;
case 2:
newpos = MEMDEV_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos < 0) || (newpos > MEMDEV_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
int mem_release(struct inode *inode, struct file *filp)
{
return 0;
}
测试代码:
#include <stdio.h>
int main()
{
FILE *fp = NULL;
char Buf[4096];
strcpy(Buf, "mem is char dev!");
printf("Buf:%s\n",Buf);
fp = fopen("/dev/memdev1", "r+");
if(fp == NULL){
printf("open memdev1 error!\n");
}
fwrite(Buf, sizeof(Buf), 1, fp);
fseek(fp, 0, SEEK_SET);
strcpy(Buf,"Buf is NULL!");
printf("Buf: %s\n",Buf);
fread(Buf, sizeof(Buf), 1, fp);
printf("Buf: %s\n",Buf);
return 0;
}