vmem驱动设备

vmem是内存多字符设备。包含vfs的open、read、write、ioctl、poll、fasync和release函数,device文件的读写。

virtual_mem.c

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/sched.h>
  4 #include <linux/types.h>
  5 #include <linux/init.h>
  6 #include <linux/cdev.h>
  7 #include <linux/device.h>
  8 #include <linux/slab.h>
  9 #include <linux/poll.h>
 10 
 11 
 12 //#undef DEBUG
 13 #define DEBUG
 14 #ifdef DEBUG
 15 #define dprintk(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
 16 #else
 17 #define dprintk(...) do { } while (0)
 18 #endif
 19 
 20 static struct class *virtualmem_class = NULL;
 21 
 22 static int virtualmem_major = 0;        //模块virtualmem_major参数,默认为0
 23 static int virtualmem_minor = 0;
 24 module_param(virtualmem_major, int, S_IRUGO);    
 25 
 26 static int dev_num = 1;            //模块dev_num参数,默认为1
 27 module_param(dev_num, int, S_IRUGO);            
 28 
 29 
 30 struct virtualmem_dev {
 31     unsigned int current_len;
 32     unsigned char mem[PAGE_SIZE];
 33     struct cdev cdev;
 34     struct mutex mutex;
 35     struct device_attribute device_attribute;
 36     wait_queue_head_t r_wait;
 37     wait_queue_head_t w_wait;
 38     struct fasync_struct *async_queue;
 39 
 40 };
 41 static struct  virtualmem_dev *virtualmem_devp;
 42 
 43 
 44 ssize_t virtual_show(struct device *dev, struct device_attribute *attr, char *buf)
 45 {
 46     struct  virtualmem_dev *devp = (struct virtualmem_dev *)dev_get_drvdata(dev);
 47     mutex_lock(&devp->mutex);
 48     snprintf(buf, devp->current_len + 1, "%s", devp->mem);        
 49     mutex_unlock(&devp->mutex);
 50     return devp->current_len + 1;
 51 }
 52 
 53 ssize_t virtual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 54 {
 55     struct  virtualmem_dev *devp = (struct virtualmem_dev *)dev_get_drvdata(dev);
 56     mutex_lock(&devp->mutex);
 57     if (PAGE_SIZE == devp->current_len) {
 58         devp->current_len = 0;
 59         return -1;
 60     }
 61     memcpy(devp->mem, buf, count);
 62     devp->current_len = count;
 63     mutex_unlock(&devp->mutex);
 64     return count;
 65 }
 66 
 67 static int virtual_open(struct inode *inode, struct file *filp)
 68 {
 69     struct  virtualmem_dev *devp;
 70     devp = container_of(inode->i_cdev, struct virtualmem_dev, cdev);
 71     filp->private_data = devp;
 72     return 0;
 73 }
 74 
 75 static ssize_t virual_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
 76 {
 77     int ret;
 78     struct virtualmem_dev *devp = filp->private_data;
 79     DECLARE_WAITQUEUE(wait, current);
 80 
 81     mutex_lock(&devp->mutex);
 82     add_wait_queue(&devp->r_wait, &wait);        //定义读队列唤醒
 83 
 84     while (!devp->current_len) {
 85         if (filp->f_flags & O_NONBLOCK) {
 86             ret = -EAGAIN;
 87             goto out1;
 88         }
 89 
 90         set_current_state(TASK_INTERRUPTIBLE);    //读阻塞休眠
 91         mutex_unlock(&devp->mutex);
 92         schedule();
 93         if (signal_pending(current)) {
 94             ret = -ERESTARTSYS;
 95             goto out2;
 96         }
 97         mutex_lock(&devp->mutex);
 98     }
 99 
100     if (count > devp->current_len)
101         count = devp->current_len;
102     if (copy_to_user(buf, devp->mem, count)) {
103         ret = -EFAULT;
104         goto out1;
105     } else {
106         devp->current_len -= count;
107         memcpy(devp->mem, devp->mem + count, devp->current_len);
108         dprintk("read %d bytes,current_len:%d\n", (int)count, devp->current_len);
109         wake_up_interruptible(&devp->w_wait);        //唤醒读阻塞休眠
110         ret = count;
111     }
112 out1:
113     mutex_unlock(&devp->mutex);
114 out2:
115     remove_wait_queue(&devp->r_wait, &wait);
116     set_current_state(TASK_RUNNING);
117     return ret;
118 }
119 
120 static ssize_t virual_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
121 {
122     int ret;
123     struct virtualmem_dev *devp = filp->private_data;
124     DECLARE_WAITQUEUE(wait, current);
125 
126     mutex_lock(&devp->mutex);
127     add_wait_queue(&devp->w_wait, &wait);        //定义写队列唤醒
128 
129     while (devp->current_len == PAGE_SIZE) {
130         if (filp->f_flags & O_NONBLOCK) {
131             ret = -EAGAIN;
132             goto out1;
133         }
134 
135         set_current_state(TASK_INTERRUPTIBLE);    //写阻塞休眠
136         mutex_unlock(&devp->mutex);
137         schedule();
138         if (signal_pending(current)) {
139             ret = -ERESTARTSYS;
140             goto out2;
141         }
142         mutex_lock(&devp->mutex);
143     }
144     if (count > PAGE_SIZE - devp->current_len)
145         count = PAGE_SIZE - devp->current_len;
146     if (copy_from_user(devp->mem + devp->current_len, buf, count)) {
147         ret = -EFAULT;
148         goto out1;
149     } else {
150         devp->current_len += count;
151         dprintk("written %d bytes,current_len:%d\n", (int)count, devp->current_len);
152         wake_up_interruptible(&devp->r_wait);        //唤醒读阻塞休眠
153 
154         if (devp->async_queue) {    //写信号进行异步通知应用
155             kill_fasync(&devp->async_queue, SIGIO, POLL_IN);
156             dprintk("%s kill SIGIO\n", __func__);
157         }
158         ret = count;
159     }
160 out1:
161     mutex_unlock(&devp->mutex);
162 out2:
163     remove_wait_queue(&devp->w_wait, &wait);
164     set_current_state(TASK_RUNNING);
165     return ret;
166 }
167 
168 //这里利用IO CMD宏定义
169 #define MEM_CLEAR        _IO('V',1)    
170 #define MEM_FULL        _IOW('V', 2, unsigned char)
171 
172 static long virtual_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
173 {
174     struct virtualmem_dev *devp = filp->private_data;
175     switch(cmd) {    
176     case MEM_CLEAR:
177         mutex_lock(&devp->mutex);
178         devp->current_len = 0;
179         memset(devp->mem, 0, PAGE_SIZE);
180         mutex_unlock(&devp->mutex);
181         dprintk("cmd = %d, clear the memory", cmd);
182         break;
183     case MEM_FULL:
184         mutex_lock(&devp->mutex);
185         devp->current_len = PAGE_SIZE;
186         memset(devp->mem, (unsigned char )arg, PAGE_SIZE);
187         mutex_unlock(&devp->mutex);
188         dprintk("cmd = %d, fill the memory using %ld", cmd, arg);
189         break;
190     default:
191         return -EINVAL;
192     }
193     return 0;
194 }
195 
196 static unsigned int virtual_poll(struct file *filp, poll_table *wait)
197 {
198     unsigned int mask = 0;
199     struct virtualmem_dev *devp = filp->private_data;
200     mutex_lock(&devp->mutex);
201     poll_wait(filp, &devp->r_wait, wait);    //声明读写队列到poll table唤醒线程
202     poll_wait(filp, &devp->w_wait, wait);
203 
204     if (devp->current_len != 0 )
205         mask |= POLLIN | POLLRDNORM;
206 
207     if (devp->current_len != PAGE_SIZE)
208         mask |= POLLOUT | POLLWRNORM;
209 
210     mutex_unlock(&devp->mutex);
211     return mask;
212 }
213 
214 static int virtual_fasync(int fd, struct file *filp, int mode)
215 {
216     struct virtualmem_dev *devp = filp->private_data;
217     return fasync_helper(fd, filp, mode, &devp->async_queue);
218 }
219 
220 static int virtual_release(struct inode *inode, struct file *filp)
221 {
222     virtual_fasync(-1, filp, 0);
223     return 0;
224 }
225 
226 
227 
228 static  struct file_operations virtual_fops = {
229     .owner = THIS_MODULE,
230     .read = virual_read,
231     .write = virual_write,
232     .unlocked_ioctl = virtual_ioctl,
233     .poll = virtual_poll,
234     .fasync = virtual_fasync,
235     .open = virtual_open,
236     .release = virtual_release,
237 };
238 
239 static int virtualmem_setup_dev(struct virtualmem_dev *devp, int index)
240 {
241     int ret;
242     dev_t devno = MKDEV(virtualmem_major, virtualmem_minor + index);
243 
244     cdev_init(&devp->cdev, &virtual_fops);
245     devp->cdev.owner = THIS_MODULE;
246     ret = cdev_add(&devp->cdev, devno, 1);
247     return ret;
248 }
249 
250 
251 static int __init virtualmem_init(void)
252 {
253     int ret;
254     int i;
255     dev_t devno = MKDEV(virtualmem_major, virtualmem_minor);
256     struct device *temp[dev_num];
257 
258     dprintk("Initializing virtualmem device.\n");
259     if(virtualmem_major)
260         ret = register_chrdev_region(devno, dev_num, "virtualmem");
261     else {
262         ret = alloc_chrdev_region(&devno, 0, dev_num, "virtualmem");
263         virtualmem_major = MAJOR(devno);
264         virtualmem_minor = MINOR(devno);
265     }
266     if(ret < 0) {
267         dprintk("Failed to alloc char dev region.\n");
268         goto err;
269     }
270 
271     virtualmem_devp = kzalloc(sizeof(struct virtualmem_dev) * dev_num, GFP_KERNEL);
272     if (!virtualmem_devp) {
273         ret = -ENOMEM;
274         dprintk("Failed to alloc virtualmem device.\n");
275         goto unregister;
276     }
277 
278     for(i = 0; i < dev_num; i++) {
279         ret = virtualmem_setup_dev(virtualmem_devp + i, i);
280         if (ret) {
281             dprintk("Failed to setup dev %d.\n", i);
282             while(--i >= 0)
283                 cdev_del(&(virtualmem_devp + i)->cdev);
284             goto kfree;
285         }
286     }
287 
288     virtualmem_class = class_create(THIS_MODULE, "virtualmem");    //建立virtualmem类
289     if (IS_ERR(virtualmem_class)) {
290         ret = PTR_ERR(virtualmem_class);
291         dprintk("Failed to create virtualmem class.\n");
292         goto destroy_cdev;
293     }
294 
295     for(i = 0; i < dev_num; i++)  {
296         //在virtualmem这个类里建立多个virtualmem文件
297         temp[i] = device_create(virtualmem_class, NULL, devno + i, (void *)(virtualmem_devp + i), "%s%d", "virtualmem", i);
298         if (IS_ERR(temp[i])) {
299             ret = PTR_ERR(temp[i]);
300             dprintk("Failed to create virtualmem device.\n");
301             while(--i >= 0)
302                 device_destroy(virtualmem_class, devno + i);
303             goto destory_class;
304         }
305     }
306     for(i = 0; i < dev_num; i++) {
307         (virtualmem_devp + i)->device_attribute.attr.name = "mem";    //对于单设备一般用宏 DEVICE_ATTR,这里多设备需要完成宏的代码
308         (virtualmem_devp + i)->device_attribute.attr.mode = S_IRUGO | S_IWUSR;
309         (virtualmem_devp + i)->device_attribute.show = virtual_show;
310         (virtualmem_devp + i)->device_attribute.store = virtual_store;
311         ret =  device_create_file(temp[i], &(virtualmem_devp + i)->device_attribute);
312         if(ret < 0) {
313             dprintk("Failed to create attribute mem.");
314             while(--i >= 0)
315                 device_remove_file(temp[i], &(virtualmem_devp + i)->device_attribute);
316             goto destroy_device;
317         }
318     }
319     for(i = 0; i < dev_num; i++)  {
320         mutex_init(&(virtualmem_devp + i)->mutex);    //初始化互斥锁
321         init_waitqueue_head(&(virtualmem_devp + i)->r_wait);        //初始化读写队列
322         init_waitqueue_head(&(virtualmem_devp + i)->w_wait);
323     }
324 
325 
326     dprintk("Succedded to Initialize virtualmem device.\n");
327     return 0;
328 
329 destroy_device:
330     for(i = 0; i < dev_num; i++)
331         device_destroy(virtualmem_class, devno + i);
332 
333 destory_class:
334     class_destroy(virtualmem_class);
335 
336 destroy_cdev:
337     for(i = 0; i < dev_num; i++)
338         cdev_del(&(virtualmem_devp + i)->cdev);
339 
340 kfree:
341     kfree(virtualmem_devp);
342 
343 unregister:
344     unregister_chrdev_region(MKDEV(virtualmem_major, virtualmem_minor), dev_num);
345 
346 err:
347     return ret;
348 }
349 
350 static void __exit virualmem_exit(void)
351 {
352     int i;
353     dprintk("Destroy virtualmem device.\n");
354     if(virtualmem_class) {
355         for(i = 0; i < dev_num; i++)
356             device_destroy(virtualmem_class, MKDEV(virtualmem_major, virtualmem_minor + i));
357         class_destroy(virtualmem_class);
358     }
359     if(virtualmem_devp) {
360         for(i = 0; i < dev_num; i++)
361             cdev_del(&(virtualmem_devp + i)->cdev);
362         kfree(virtualmem_devp);
363     }
364     unregister_chrdev_region(MKDEV(virtualmem_major, virtualmem_minor), dev_num);
365 }
366 
367 module_init(virtualmem_init);
368 module_exit(virualmem_exit);
369 
370 MODULE_AUTHOR("Kevin Hwang <kevin.hwang@live.com");
371 MODULE_LICENSE("GPL v2");

 

测试程序virtual_test.c:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <fcntl.h>
  4 #include <signal.h>
  5 #include <sys/ioctl.h>
  6 #include <sys/epoll.h>
  7 #include <sys/stat.h>
  8 
  9 //#undef EPOLL
 10 #define EPOLL
 11 #define MEM_CLEAR        _IO('V',1)
 12 #define MEM_FULL        _IOW('V', 2, unsigned char)
 13 
 14 char buff[4096];
 15 int fd;
 16 void signalio_handler(int signum)
 17 {
 18     printf("receive a signal \n");
 19     read(fd, buff, 4096);
 20     printf("%s \n", buff);
 21 }
 22 
 23 int main(int argc, char const **argv)
 24 {
 25     int oflags, input_num = 0, dev_num;
 26     char tmp[10];
 27     char path[30];
 28     
 29 #ifdef EPOLL
 30     int ret;
 31     struct epoll_event ev_virtualmem;
 32     int epfd;
 33 #endif
 34 
 35     fd = open("/sys/module/virtual_mem/parameters/dev_num", O_RDONLY, S_IRUSR);    //读取模块设备数量参数dev_num
 36     if (fd == -1) {
 37         printf("Failed to open parameters for dev_num.\n");
 38         return -1;
 39     }
 40     read(fd, tmp, 10);
 41     dev_num = atoi(tmp);    //获取dev_num
 42     if (dev_num > 1) {        //如果dev_num = 1默认不需要参数
 43         if (argc == 2)
 44             input_num = atoi(*(++argv));
 45         else {
 46             printf("please input the dev_num between 0 and %d.\n", dev_num - 1);
 47             return -1;
 48         }
 49         if (**argv < '0' || **argv > '9' || dev_num <= input_num) {        //第一个参数第一位不为0~9返回错误信息
 50             printf("please input the dev_num between 0 and %d.\n", dev_num - 1);
 51             return -1;
 52         }
 53     }
 54 
 55     snprintf(path, 30 + 1, "/dev/virtualmem%d", input_num);        //获取设备路径名字
 56     
 57     fd = open(path, O_RDWR | O_NONBLOCK, S_IRUSR | S_IWUSR);
 58     if (fd == -1) {
 59         printf("Failed to open /dev/virtualmem%d.\n", input_num);
 60         return -1;
 61     }
 62     printf("open /dev/virtualmem%d success.\n", input_num);
 63 
 64     if (ioctl(fd, MEM_FULL, 0xFF) < 0)
 65         printf("ioctl command = 0x%lx failed.\n", MEM_FULL);
 66 
 67     if (ioctl(fd, MEM_CLEAR, 0xFF) < 0)
 68         printf("ioctl command = 0x%x failed.\n", MEM_CLEAR);
 69 #ifdef EPOLL
 70     epfd = epoll_create(1);    //创建epoll
 71     if (epfd < 0) {
 72         printf("epoll_create failed.\n");
 73         return -1;
 74     }
 75     ev_virtualmem.events = EPOLLIN | EPOLLPRI;        //epoll触发事件
 76     if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev_virtualmem) < 0) {    //增加这个事件
 77         printf("epfd_ctl add failed.\n");
 78         return -1;
 79     }
 80 
 81     ret = epoll_wait(epfd, &ev_virtualmem, 1, 5000);        //等待事件,调用驱动poll
 82     if (ret < 0)
 83         printf("epoll_wait failed.\n");
 84     else if (!ret)
 85         printf("no data input in virtualmem%d\n", input_num);
 86     else {
 87         printf("receive data.\n");
 88         read(fd, buff, 4096);
 89         printf("%s \n", buff);
 90     }
 91     if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev_virtualmem)) {
 92         printf("epfd_ctl delete failed.\n");
 93         return -1;
 94     }
 95 #else
 96     signal(SIGIO, signalio_handler);        //声明signalio_handler信号处理函数
 97     fcntl(fd, F_SETOWN, getpid());        //获取程序pid
 98     oflags = fcntl(fd, F_GETFL);
 99     fcntl(fd, F_SETFL, oflags | FASYNC);    //添加FASYNC标志
100     while (1)
101         sleep(100);
102 #endif
103     close(fd);
104     return 0;
105 }

 

Makefile:

1 obj-m += virtual_mem.o
2 
3 KERN_DIR = /lib/modules/$(shell uname -r)/build
4 all:
5     make -C $(KERN_DIR) M=$(CURDIR) modules
6 
7 clean:
8     make -C $(KERN_DIR) M=$(CURDIR) clean

 

github代码地址 https://github.com/kevinhwang91/virtual_mem

 

代码在linux3.0-4.2测试过,使用方法:

make

insmod virtual_mem.ko (dev_num=具体数字,dev_num缺省默认为1)

可以直接在/dev/virtual_mem?或者/sys/class/virtualmem/virtualmem?/mem(非阻塞)进行读写

gcc -o virtual_test virtual_test.c(可以在源码预处理选择处理方法)

./virtual_test (0 - dev_num-1)其中一个设备

 

posted @ 2016-07-22 02:30  Kevin_Hwang  阅读(648)  评论(0编辑  收藏  举报