#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 <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 254
static int globalmem_major = GLOBALMEM_MAJOR;
struct globalmem_dev
{
struct cdev cdev;
/***************************************
struct cdev
{
struct kobject koj; //内嵌的kobiject对象
struct module *owner;//所属模块
struct file_operations *ops;//文件操作结构体
struct list_head list;
dev_t dev;//设备号
unsigned int count;
};
******************************************/
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev *globalmem_devp;
int globalmem_open(struct inode *inode,struct file *filep)
{
filep->private_data=globalmem_devp;
return 0;
}
int globalmem_release(struct inode *inode,struct file *filep)
{
return 0;
}
static int globalmem_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd,unsigned long arg)
{
struct globalmem_dev *dev=filep->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(dev->mem,0,GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero");
break;
default:
return -EINVAL;
}
return 0;
}
static ssize_t globalmem_read(struct file *filep,char __user *buf,size_t size,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct globalmem_dev *dev=filep->private_data;
if(p>=GLOBALMEM_SIZE) //要读的偏移位置越界
return count ? - ENXIO:0;
if(count>GLOBALMEM_SIZE-p) //要读的字节数太大
count= GLOBALMEM_SIZE-p;
/*内核空间—>用户空间:将内核空间中首地址为dev->mem+p的count个字节拷贝到用户空间的首地址为buf的字符串里面去*/
if(copy_to_user(buf,(void *)(dev->mem+p),count))
ret = -EFAULT;
else
{
*ppos+=count;
ret=count;
printk(KERN_INFO "read %d bytes(s) from %d\n",(int)count,(int)p);
}
return ret;
}
static ssize_t globalmem_write(struct file *filep,const char __user *buf,size_t size,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count =size;
int ret =0;
struct globalmem_dev *dev=filep->private_data;
if(p>=GLOBALMEM_SIZE) //要写的偏移位置越界
return count ? -ENXIO:0;
if(count>GLOBALMEM_SIZE-p) //要写的字节数太大
count=GLOBALMEM_SIZE-p;
/*用户空间->内核空间:将用户空间中首地址为buf的count个字节拷贝到内核空间首地址为dev-mem+p的存储空间去*/
if(copy_from_user(dev->mem+p,buf,count))
ret=-EFAULT;
else
{
*ppos+=count;
ret=count;
printk(KERN_INFO "written %d bytes from %d\n",(int)count,(int)p);
}
return ret;
}
static loff_t globalmem_llseek(struct file *filep,loff_t offset,int orig)
{
loff_t ret=0;
switch(orig)
{
case 0: /*从文件开头开始偏移*/
if(offset<0)
{
ret = -EINVAL;
break;
}
if((unsigned int)offset>GLOBALMEM_SIZE) //偏移越界
{
ret = -EINVAL;
break;
}
filep->f_pos=(unsigned int)offset;
ret =filep->f_pos;
break;
case 1: /*从当前位置开始偏移*/
if(((filep->f_pos+offset)>GLOBALMEM_SIZE)||((filep->f_pos+offset)<0)) //偏移越界
{
ret=-EINVAL;
break;
}
filep->f_pos+=offset;
ret=filep->f_pos;
break;
default:
ret=-EINVAL;
break;
}
return ret;
}
/*
通过file_operations方法将设备类型的差异屏蔽了,
这就是linux能够将所有设备都理解为文件的原因。
*/
static const struct file_operations globalmem_fops=
{
/*指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES*/
.owner=THIS_MODULE,
/*llseek用来修改文件当前的读写位置,返回新位置*/
.llseek=globalmem_llseek,
/*从设备中同步读取数据。读取成功返回读取的字节数。设置为NULL,调用时返回-EINVAL*/
.read=globalmem_read,
/*向设备发送数据*/
.write=globalmem_write,
/*提供一种执行设备特殊命令的方法*/
.ioctl=globalmem_ioctl,
/*由VFS调用,当VFS打开一个文件,即建立了一个新的"struct file",之后调用open方法分配文件结构。*/
.open=globalmem_open,
/*file结构释放时,将调用此指针函数,release与open相同可设置为NULL*/
.release=globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{
int err,devno=MKDEV(globalmem_major,index);
/*初始化cdev,并建立cdev和file_operations之间的连接*/
cdev_init(&dev->cdev,&globalmem_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&globalmem_fops;
/*注册设备,通常发生在驱动模块的加载函数中*/
err=cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}
/*globalmem 设备驱动模块加载函数*/
int globalmem_init(void)
{ int result;
dev_t devno=MKDEV(globalmem_major,0);
/*主设备号是globalmem_major,
从设备号是0,
通过#define MKDEV(ma,mi) (((ma)<<MINORBITS)|(mi))宏定义生成设备号devno
*/
if(globalmem_major)
result = register_chrdev_region(devno,1,"globalmem");
/**/
else
{
result = alloc_chrdev_region(&devno,0,1,"globalmem");
/**/
globalmem_major = MAJOR(devno);
}
if(result<0)
return result;
globalmem_devp=kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
/*
kmalloc的特殊之处在于分配的内存在物理地址上是连续的
*/
if(!globalmem_devp)
{
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp,0,sizeof(struct globalmem_dev));
/**/
globalmem_setup_cdev(globalmem_devp,0);
/*初始化并添加cdev结构体*/
return 0;
fail_malloc:unregister_chrdev_region(devno,1);
return result;
}
/*globalmem设备驱动模块卸载函数*/
void globalmem_exit(void)
{
/*注销设备,通常发生在驱动模块的卸载函数中*/
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major,0),1);
}
MODULE_AUTHOR("Song Baohua");
/**/
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalmem_major,int,S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
《linux设备驱动开发详解》上的globalmem字符设备驱动程序
最新推荐文章于 2024-09-15 22:47:38 发布