这篇文章介绍如何实现一个简单的linux字符设备驱动,在这里实现的字符设备驱动包括mydev_temp.h文件和mydev_temp.c文件,其中mydev_temp.h文件内容如下:
#ifndef _MYDEV_TEMP_H_
#define _MYDEV_TEMP_H_
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 250
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
struct mem_dev{
char *data;
unsigned long size;
};
#endif
这个头文件主要定义了字符设备驱动的主设备号,字符设备个数以及每个字符设备的大小,并且定义了字符设备结构体。而mydev_temp.c文件内如如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "mydev_temp.h"
#ifdef CONFIG_MODVERSIONS
#define MODVERSIONS
#include <linux/version.h>
#endif
static int mem_major = MEMDEV_MAJOR;
module_param(mem_major, int, S_IRUGO);
struct mem_dev *mem_devp;
struct cdev cdev;
static int mydev_temp_open(struct inode *inode, struct file *filp);
static int mydev_temp_release(struct inode *inode, struct file* filp);
static loff_t mydev_temp_llseek(struct file *filp, loff_t offset,int whence);
static ssize_t mydev_temp_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos);
static ssize_t mydev_temp_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos);
static const struct file_operations mydev_temp_fops = {
.owner = THIS_MODULE,
.llseek = mydev_temp_llseek,
.read = mydev_temp_read,
.write = mydev_temp_write,
.open = mydev_temp_open,
.release = mydev_temp_release,
};
static int mydev_temp_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 mydev_temp_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;
printk("p=%lu\n",p);
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 %u byte(s) from %lu\n",count,p);
}
return ret;
}
static ssize_t mydev_temp_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;
printk("p=%lu\n",p);
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
if(copy_from_user(dev->data+p,buf,count)){
return -EFAULT;
}else{
*ppos += count;
ret = count;
printk(KERN_INFO"written %u byte(s) from %lu\n",count,p);
}
return ret;
}
static loff_t mydev_temp_llseek(struct file *filp, loff_t offset,int whence)
{
loff_t newpos;
switch(whence){
case 0: /*SEEK_SET*/
newpos = offset;
break;
case 1: /*SEEK_CUR*/
newpos = filp->f_pos + offset;
break;
case 2: /*SEEK_END*/
newpos = MEMDEV_SIZE+offset;
break;
default:
return -EINVAL;
}
if(newpos < 0 || newpos>MEMDEV_SIZE)
return -EINVAL;
filp->f_pos = newpos;
printk("filp->f_pos=%llu\n",filp->f_pos);
return newpos;
}
static int mydev_temp_release(struct inode *inode, struct file* filp)
{
printk("The device is released!\n");
return 0;
}
static int __init mydev_temp_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major,0);
if(mem_major){
result = register_chrdev_region(devno,2,"mydev_temp");
}else{
result = alloc_chrdev_region(&devno,0,2,"mydev_temp");
mem_major = MAJOR(devno);
}
if(result < 0)
return result;
cdev_init(&cdev,&mydev_temp_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mydev_temp_fops;
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
mem_devp = kmalloc(MEMDEV_NR_DEVS*sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp){
result = -ENOMEM;
unregister_chrdev_region(devno, 1);
return result;
}
memset(mem_devp,0,sizeof(struct mem_dev));
for(i=0;i<MEMDEV_NR_DEVS;++i){
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
memset(mem_devp[i].data,0,MEMDEV_SIZE);
}
return 0;
}
static void __exit mydev_temp_exit(void)
{
printk(KERN_ALERT "Unloading...\n");
cdev_del(&cdev);
kfree(mem_devp);
unregister_chrdev_region(MKDEV(mem_major,0), 2);;
printk("unregister success!\n");
}
MODULE_AUTHOR("Fang Xieyun");
MODULE_LICENSE("GPL");
module_init(mydev_temp_init);
module_exit(mydev_temp_exit);