globalmem驱动
globalmem为“全局内存”的意思,在globalmem字符设备中会分配一片大小为GLOBALMEM_SIZE的内存空间,并在驱动中提供对这片内存的读写、控制和定位函数,供用户空间的进程能通过Linux系统调用获取和设置这片内存。
头文件、宏定义以及结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h>
#define GLOBALMEM_SIZE 0x1000 #define MEM_CLEAR 0x1 #define GLOBALMEM_MAJOR 230
static int globalmem_major = GLOBALMEM_MAJOR; module_param(globalmem_major, int, S_IRUGO);
struct globalmem_dev { struct cdev cdev; unsigned char mem[GLOBALMEM_SIZE]; };
struct globalmem_dev *globalmem_devp;
|
内核模块传入参数
内核模块中没有main函数,所以向模块内部传入参数可以通过module_param这个宏定义来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #define S_IRWXU 00700 #define S_IRUSR 00400 #define S_IWUSR 00200 #define S_IXUSR 00100
#define S_IRWXG 00070 #define S_IRGRP 00040 #define S_IWGRP 00020 #define S_IXGRP 00010
#define S_IRWXO 00007 #define S_IROTH 00004 #define S_IWOTH 00002 #define S_IXOTH 00001
|
kzalloc函数与kfree函数
用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 *,*所有申请的元素都被初始化为 0.
1 2
| #include <linux/slab.h> void *kmalloc(size_t size, int flags)
|
给 kmalloc 的第一个参数是要分配的块的大小. 第 2 个参数, 分配标志, 非常有趣, 因为它以几个方式控制 kmalloc 的行为.
最一般使用的标志, GFP_KERNEL, 代表运行在内核空间的进程而进行的. 这意味着调用函数是代表一个进程在执行一个系统调用. 使用 GFP_KENRL 意味着 kmalloc 能够使当前进程在少内存的情况下睡眠来等待一页. 一个使用 GFP_KERNEL 来分配内存的函数必须, 因此, 是可重入的并且不能在原子上下文中运行. 当当前进程睡眠, 内核采取正确的动作来定位一些空闲内存, 或者通过刷新缓存到磁盘或者交换出去一个用户进程的内存。
GFP_KERNEL 不一直是使用的正确分配标志; 有时 kmalloc 从一个进程的上下文的外部调用. 例如, 这类的调用可能发生在中断处理, tasklet, 和内核定时器中. 在这个情况下, 当前进程不应当被置为睡眠, 并且驱动应当使用一个GFP_ATOMIC 标志来代替. 内核正常地试图保持一些空闲页以便来满足原子的分配. 当使用 GFP_ATOMIC 时,kmalloc 能够使用甚至最后一个空闲页. 如果这最后一个空闲页不存在, 但是, 分配失败。
其他用来代替或者增添 GFP_KERNEL 和 GFP_ATOMIC 的标志, 尽管它们 2 个涵盖大部分设备驱动的需要. 所有的标志定义在 <linux/gfp.h>, 并且每个标志用一个双下划线做前缀, 例如 __GFP_DMA. 另外, 有符号代表常常使用的标志组合; 这些缺乏前缀并且有时被称为分配优先级。
1 2 3
| #include <linux/slab.h> void kfree(const void *objp)
|
seek函数
文件重定位函数,改变文件的读写偏移量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| static loff_t globalmem_llseek(struct file *filp, 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; } 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; }
|
文件重定位的起始地址有三种情况:同时该函数具有检测定位的合法性
①0:从文件开头进行定位
②1:从文件当前位置进行定位
③2:定位到文件末尾
ioctl函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static long globalmem_ioctl(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; }
|
ioctl()函数可以接收用户空间的MEM_CLEAR命令实现对内存的数据清0.
驱动代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
| #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/io.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linxu/init.h>
#define GLOBALMEM_SIZE 0x1000 #define MEM_CLEAR 0x01 #define GLOBALMEM_MAJOR 230
struct globalmemdev{ dev_t devid; struct cdev cdev; struct device *device; struct class *class; int major; int minor; struct device_node *nd; unsigned char mem[GLOBALMEM_SIZE]; }; struct globalmemdev *globalmem_devp; module_param(globalmem_devp->major, int , S_IRUGO);
static int globalmem_open(struct inode *inode, struct file *filp) { filp->private_data = globalmem_devp; return 0; } static int globalmem_release(struct inode *inode, struct file *filp) { return 0; } static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct globalmemdev *dev = filp -> private_data; switch(cmd) { case MEM_CLEAR: memset(dev->mem, 0, GLOBALMEM_SIZE); printk(KERN_INFO "globalmem is set to zero \n"); default : return -EINVAL; } } static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { unsigned long p = *offt; unsigned int count = cnt; int ret = 0; struct globalmemdev *dev = filp -> private_data; if(p >= GLOBALMEM_SIZE) { return 0; } if(count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE-p; if(copy_to_user(buf, dev->mem + p, count)) ret = -EFAULT; else{ *ppos += count; ret = count; printk(KERN_INFO "read %u bytes(s) from %lu\n", count , p); } return ret; } static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { unsigned long p = *offt; unsigned int count = cnt ; int ret = 0; struct globalmemdev *dev = filp -> private_data; if(p >= GLOBALMEM_SIZE) return 0; if(count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE - p; if(copy_from_user(dev->mem + p, buf, count)) ret = -EFAULT; else{ *offt += count; ret = count; printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p); } return ret; } static loff_t globalmem_llseek(struct file *filp, 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; } 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_ops = { .owner = THIS_MODULE, .llseek = globalmem_llseek, .read = globalmem_read, .write = globalmem_write, .unlocked_ioctl = globalmem_ioctl, .open = globalmem_open, .release = globalmem_release, }; static int __init globalmem_init(void) { if(globalmem_devp.major) { globalmem_devp.devid = MKDEV(globalmem_devp.major, 0); register_chrdev_region(globalmem_devp.devid, 1, "globalmem"); }else{ alloc_chrdev_region(&globalmem_devp.devid, 0, 1, "globalmem"); globalmem_devp.major = MAJOR(globalmem_devp.devid); globalmem_devp.minor = MINOR(globalmem_devp.devid); } cdev_init(&globalmem_devp.cdev, &globalmem_ops); dev->cdev.owner = THIS_MODULE; cdev_add(&globalmem_dep.cdev, globalmem_devp.devid, 1); globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL); if (!globalmem_devp) { ret = -ENOMEM; goto fail_malloc; } fail_malloc: unregister_chrdev_region(devno, 1); return ret;
} static void __exit globalmem_exit(void) { cdev_del(&globalmem_devp->cdev); kfree(globalmem_devp) unregister_chrdev_region(globalmem_devp.devid, 1); } module_init(globalmem_init); module_exit(globalmem_exit); MODULE_LICENSE("GPL);
|