#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 // 设置全局内存大小为4K
#define MEM_CLEAR 0x1 // 清零全局内存
#define GLOBALMEM_MAJOR 250 // 预设golbalmem的主设备号
static int globalmem_major = GLOBALMEM_MAJOR;
// globalmem 结构体
struct globalmem_dev
{
struct cdev cdev; // cdev结构体
unsigned char mem[GLOBALMEM_SIZE]; // 全局内存
};
//******************************************************************************
//在执行insmod的时候调用init函数会进行kmalloc申请一块内存,会对这个指针进行初始化
struct globalmem_dev *globalmem_devp;
//******************************************************************************
//文件打开函数
int globalmem_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalmem_devp; //获得内存空间地址
return 0;
}
//文件释放函数
int globalmem_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct globalmem_dev *dev = filp->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(dev->mem, 0, GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
//*******************************************************************************************
//filp:文件指针 buf:缓冲区 size:读取的字节数 ppos:相对文件开头的偏移量
//*******************************************************************************************
static ssize_t globalmem_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 globalmem_dev *dev = filp->private_data;
if(p >= GLOBALMEM_SIZE) //越界
{
return count ? - ENXIO : 0;
}
if(count > (GLOBALMEM_SIZE - p)) //读取的字节数超过剩下的字节数
{
count = GLOBALMEM_SIZE - p;
}
//from kernel to user
//copy_to_user和copy_from_user函数的第一个参数都是目标,第二个参数是源
if(copy_to_user(buf, (void*)(dev->mem + p), count))
{
ret = - EFAULT;
}
else
{
*ppos += count; //指针后移
ret = count; //返回读出的字节数
printk(KERN_INFO "read %d bytes from %ld\n", count, p);
}
return ret;
}
//*******************************************************************************************
//filp:文件指针 buf:缓冲区 size:写入的字节数 ppos:相对文件开头的偏移量
//*******************************************************************************************
static ssize_t globalmem_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 globalmem_dev *dev = filp->private_data;
if(p >= GLOBALMEM_SIZE) //越界
{
return count ? - ENXIO : 0;
}
if(count > (GLOBALMEM_SIZE - p)) //溢出
{
count = GLOBALMEM_SIZE - p;
}
//from kernel to user
//copy_to_user和copy_from_user函数的第一个参数都是目标,第二个参数是源
if(copy_from_user(dev->mem + p, buf, count))
{
ret = - EFAULT;
}
else
{
*ppos += count; //指针后移
ret = count; //返回写入的字节数
printk(KERN_INFO "written %d bytes from %ld\n", count, p);
}
return ret;
}
//************************************************************************
// llseek函数对文件定位的起始地址可以是:
// 文件开头(SEEK_SET,0)
// 当前位置 (SEEK_CUR,1)
// 文件尾 (SEEK_END,2)
// 函数参数:filp 文件指针 offset 偏移量 orig 偏移量的参考地址
//************************************************************************
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret;
switch(orig)
{
case 0:
if(offset < 0)
{
ret = - EINVAL;
break;
}
if((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 globalmem_fops =
{
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
int err, devno;
devno = MKDEV(globalmem_major, 0);
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 globalmem", err);
}
}
// globalmem设备驱动模块加载函数
int globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major,0); // 通过主设备号和次设备号生成dev_t
//向系统申请设备号
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;
}
//*******************************************************************************************
//在设备驱动程序里面申请动态内存不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。
//释放内存用的是kfree,或free_pages. 请注意,kmalloc等函数返回的是物理地址!
//而malloc等返回的是线性地址!要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。
//内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。
//在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址*/
//*******************************************************************************************
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if(!globalmem_devp)
{
result = - ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
globalmem_setup_cdev(globalmem_devp, 0);
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return result;
}
// globalmem设备驱动模块卸载函数
int globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev); // 删除cdev结构
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); //从系统注销设备号
}
MODULE_AUTHOR("Benson");
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
|