简单的linux 字符设备驱动
/*************************************************************************
> File Name: chr_dev.c
> Author: Bond
> Created Time: 2021年03月17日 星期三 13时44分36秒
************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#define DBUG true
#define DEBUG(x...) if(DBUG){printk(x);}
#define DEV_MAJOR 230
#define MAYJOR_MAGIC 'c'
#define GLOBALMEM_SIZE 0x1000
#define DEVICE_NUM 10
#define MEM_CLEAR _IOWR(MAYJOR_MAGIC,0,int)
static int devmem_major = DEV_MAJOR;
module_param(devmem_major,int,S_IRUGO);
struct chrmem_dev{
struct class *dev_class;
struct device *dev_devcie;
struct cdev c_dev;
unsigned char mem[GLOBALMEM_SIZE];
};
static struct chrmem_dev *pchrmem_dev;
static int chr_dev_open(struct inode *inode, struct file *filp){
struct chrmem_dev *dev = container_of(inode->i_cdev,struct chrmem_dev,c_dev);
filp->private_data = dev;
return 0;
}
static int chr_dev_release(struct inode *inode,struct file* filp){
return 0;
}
static long chr_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
struct chrmem_dev *dev = filp->private_data;
switch (cmd){
case MEM_CLEAR:
memset(dev->mem,0,GLOBALMEM_SIZE);
break;
default :
return -EINVAL;
break;
}
return 0;
}
static ssize_t chr_dev_read(struct file *filp, char __user *buffer, size_t size, loff_t *f_pos){
unsigned long p = *f_pos;
unsigned int count = size;
int ret;
struct chrmem_dev *dev = filp->private_data;
DEBUG("Bond read size %u byte from %lu\n",size,p);
if(p >= GLOBALMEM_SIZE){
return 0;
}
if(count > GLOBALMEM_SIZE-p){
count = GLOBALMEM_SIZE-p;
}
if(copy_to_user(buffer,dev->mem+p,count)){
ret = -EFAULT;
}else{
*f_pos += count;
ret = count;
DEBUG("Bond read %u byte from %lu\n",count,p);
}
return ret;
}
static ssize_t chr_dev_write(struct file *filp,
const char __user *buffer,
size_t size,
loff_t *f_pos) {
unsigned long p = *f_pos;
unsigned int count = size;
int ret;
struct chrmem_dev *dev = filp->private_data;
DEBUG("Bond write size %u byte from %lu\n",size,p);
if(p >= GLOBALMEM_SIZE){
return 0;
}
if(count > GLOBALMEM_SIZE-p){
count = GLOBALMEM_SIZE-p;
}
if(copy_from_user(dev->mem+p,buffer,count)){
ret = -EFAULT;
}else{
*f_pos += count;
ret = count;
DEBUG("Bond write %u byte from %lu\n",count,p);
}
return ret;
}
static loff_t chr_dev_llseek(struct file *filp, loff_t offset, int orignal){
loff_t ret = 0;
switch(orignal){
case 0:
if(offset<0 || (unsigned int)offset>GLOBALMEM_SIZE){
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1:
if((filp->f_pos +offset)>GLOBALMEM_SIZE){
ret = -EINVAL;
break;
}
if((filp->f_pos + offset) <0){
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp ->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct file_operations chr_dev_fops = {
.owner = THIS_MODULE,
.open = chr_dev_open,
.read = chr_dev_read,
.write = chr_dev_write,
.llseek = chr_dev_llseek,
.release = chr_dev_release,
.unlocked_ioctl = chr_dev_ioctl,
};
static int chr_dev_setup(struct chrmem_dev *dev,int index){
int err,devno;
char buf[10]={0};
devno = MKDEV(devmem_major,index);
cdev_init(&dev->c_dev,&chr_dev_fops);
dev->c_dev.owner = THIS_MODULE;
err = cdev_add(&dev->c_dev,devno,1);
if(err){
DEBUG("error %d add chr_dev index %d\n",err,index);
}
sprintf(buf,"chr_dev%d",index);
dev->dev_class = class_create(THIS_MODULE,buf);
if(IS_ERR(dev->dev_class)){
DEBUG("Bond ERR:failed in creat class index %d\n",index);
return -1;
}
dev->dev_devcie = device_create(dev->dev_class,NULL,devno,NULL,buf);
return err;
}
static int __init chr_dev_init(void){
int ret;
int i;
dev_t devno = MKDEV(DEV_MAJOR,0);
if(devmem_major){
ret = register_chrdev_region(devno,DEVICE_NUM,"chrdevmem");
}else{
ret = alloc_chrdev_region(&devno,0,DEVICE_NUM,"chrdevmem");
devmem_major = MAJOR(devno);
}
if(ret){
return -ENOTBLK;
}
pchrmem_dev = kzalloc(sizeof(struct chrmem_dev)*DEVICE_NUM,GFP_KERNEL);
if(!pchrmem_dev){
ret = -ENOMEM;
goto fail_kzalloc;
}
for(i=0;i<DEVICE_NUM;i++){
chr_dev_setup(pchrmem_dev+i,i);
}
return ret;
fail_kzalloc:
unregister_chrdev_region(devno,DEVICE_NUM);
return ret;
}
static void __exit chr_dev_exit(void){
int i;
for(i=0;i<DEVICE_NUM;i++){
int devno;
cdev_del(&((pchrmem_dev+i)->c_dev));
devno = MKDEV(devmem_major,i);
device_destroy(pchrmem_dev->dev_class,devno);
class_destroy(pchrmem_dev->dev_class);
}
kfree(pchrmem_dev);
unregister_chrdev_region(MKDEV(devmem_major,0),DEVICE_NUM);
}
module_init(chr_dev_init);
module_exit(chr_dev_exit);
MODULE_AUTHOR("Arctan <xxxx@163.com>");
MODULE_DESCRIPTION("char dev driver");
MODULE_LICENSE("GPL");