#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);
|