九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4297|回复: 0
打印 上一主题 下一主题

x4412&ibox项目实战29-移植globalmem设备驱动

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-4 21:27:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
编写一个字符设备驱动,在内核空间申请4KB的内存空间,供整个用户空间共享使用。应用程序通过read和write函数读取和写入数据,通过IOCTL可以清除4KB的内存空间。
在kernel/drivers/char/x4412目录下新建x4412-globalmem.c文件,编辑内容如下:
  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/fs.h>
  4. #include <linux/errno.h>
  5. #include <linux/cdev.h>
  6. #include <linux/slab.h>
  7. #include <linux/device.h>
  8. #include <asm/uaccess.h>
  9. #define GLOBALMEM_SIZE 0x1000 /*全局内存最大4KB*/
  10. #define MEM_CLEAR 0x1                    /*清零全局内存*/
  11. static int globalmem_major;
  12. static struct class *cdev_class;
  13. struct globalmem_dev
  14. {
  15.          struct cdev cdev; /*cdev 结构体*/
  16.          unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/
  17. };
  18. struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
  19. int globalmem_open(struct inode *inode, struct file *filp)
  20. {
  21.          /*将设备结构体指针赋值给文件私有数据指针*/
  22.          filp->private_data = globalmem_devp;
  23.          return 0;
  24. }
  25. int globalmem_release(struct inode *inode, struct file *filp)
  26. {
  27.          return 0;
  28. }
  29. static long globalmem_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
  30. {
  31.          struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/
  32.          switch(cmd)
  33.          {
  34.                    case MEM_CLEAR:
  35.                             memset(dev->mem, 0, GLOBALMEM_SIZE);
  36.                             printk(KERN_INFO "globalmem is set to zero\n");
  37.                    break;
  38.                    default:
  39.                             return -EINVAL;
  40.          }
  41.          return 0;
  42. }
  43. static ssize_t globalmem_read(struct file *filp, char __user *buf,size_t size,loff_t *ppos)
  44. {
  45.          unsigned long p = *ppos;
  46.          unsigned int count = size;
  47.          int ret = 0;
  48.          struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  49.          /*分析和获取有效的写长度*/
  50.          if (p >= GLOBALMEM_SIZE)
  51.                    return count ? -ENXIO: 0;
  52.          if (count > GLOBALMEM_SIZE - p)
  53.                    count = GLOBALMEM_SIZE - p;
  54.          /*内核空间→用户空间*/
  55.          if (copy_to_user(buf, (void*)(dev->mem + p), count))
  56.          {
  57.                    ret = - EFAULT;
  58.          }
  59.          else
  60.          {
  61.                    ret = count;
  62.          }
  63.          return ret;
  64. }
  65. static ssize_t globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos)
  66. {
  67.          unsigned long p = *ppos;
  68.          unsigned int count = size;
  69.          int ret = 0;
  70.          struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  71.          if (p >= GLOBALMEM_SIZE)
  72.                    return count ? - ENXIO: 0;
  73.          if (count > GLOBALMEM_SIZE - p)
  74.                    count = GLOBALMEM_SIZE - p;
  75.           if (copy_from_user(dev->mem + p, buf, count))
  76.                    ret = - EFAULT;
  77.           else
  78.           {
  79.                     ret = count;
  80.           }
  81.           return ret;
  82. }
  83. static const struct file_operations globalmem_fops =
  84. {
  85.          .owner = THIS_MODULE,
  86.          .read = globalmem_read,
  87.          .write = globalmem_write,
  88.          .unlocked_ioctl = globalmem_ioctl,
  89.          .open = globalmem_open,
  90.          .release = globalmem_release,
  91. };
  92. static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
  93. {
  94.          int err, devno = MKDEV(globalmem_major, index);
  95.          cdev_init(&dev->cdev, &globalmem_fops);
  96.          dev->cdev.owner = THIS_MODULE;
  97.          dev->cdev.ops = &globalmem_fops;
  98.          err = cdev_add(&dev->cdev, devno, 1);
  99.          if (err)
  100.                    printk(KERN_NOTICE "Error %d adding cdev%d", err, index);
  101. }
  102. int globalmem_init(void)
  103. {
  104.          int result;
  105.          dev_t devno;
  106.          result = alloc_chrdev_region(&devno, 0, 1, "x4412-globalmem");
  107.          if(result < 0)
  108.                    return result;
  109.          globalmem_major = MAJOR(devno);
  110.          globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
  111.          if (!globalmem_devp)
  112.          {
  113.                    result = -ENOMEM;
  114.                    goto fail_malloc;
  115.          }
  116.          memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
  117.          globalmem_setup_cdev(globalmem_devp, 0);
  118.          cdev_class = class_create(THIS_MODULE,"x4412-globalmem");
  119.          if(IS_ERR(cdev_class))
  120.                    goto fail_class;
  121.          device_create(cdev_class,NULL,devno,NULL,"x4412-globalmem");
  122.          return 0;
  123. fail_class:
  124.          class_destroy(cdev_class);
  125. fail_malloc:
  126.          unregister_chrdev_region(devno, 1);
  127.          return result;
  128. }
  129. void globalmem_exit(void)
  130. {
  131.          device_destroy(cdev_class,MKDEV(globalmem_major,0));
  132.          class_destroy(cdev_class);
  133.          cdev_del(&globalmem_devp->cdev); /*注销 cdev*/
  134.          kfree(globalmem_devp); /*释放设备结构体内存*/
  135.          unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
  136. }

  137. MODULE_AUTHOR("www.9tripod.com");
  138. MODULE_LICENSE("GPL");
  139. module_init(globalmem_init);
  140. module_exit(globalmem_exit);
复制代码
       在模块初始化函数globalmem_init中,通过alloc_chrdev_region函数动态申请设备号。驱动的最开始封装了结构体globalmem_dev,包含了cdevmem两个成员,cdev为字符设备驱动的基本骨架,mem为驱动预留的4KB内存空间,用于应用程序的读写访问。globalmem_devp为定义的全局结构体指针,指向通过kmalloc函数申请的内存地址,供整个驱动调用。globalmem_setup_cdev函数通过调用cdev_initcdev_add函数注册字符设备驱动,并关联file_operations结构体。class_createdevice_create函数用于在驱动中自动创建设备节点,驱动加载后,在/dev目录下将会自动生成x4412-globalmem设备节点。如果通过模块形式加载驱动时没有自动生成节点,执行mdev –s指令即可。
在读函数globalmem_read中,首先判断读取文件的有效性。驱动限制了读取范围,只允许读取4096字节的数据,因此当读取的指针偏移ppos大于等于4096时继续读取,读函数将返回错误。当有效性校验通过后,通过copy_to_user函数将需要读取的字节数上报给上层应用。
在写函数globalmem_write中,同样首先判断写入文件的有效性。如果写入长度超过了4096个字节,也将会返回错误。当有效性校验通过后,通过copy_from_user函数从应用中读取需要写入的数据,并保存到4KB字节的内存中。
在ioctl函数globalmem_ioctl中,通过memset函数将预留的4K空间清零,即完成了清除工作。
在kernel/drivers/char/x4412/Kconfig中添加如下语句:
  1. config X4412_GLOBALMEM_DRIVER
  2.          tristate "x4412 global mem driver"
  3.          default m
  4.          help
  5.          compile for x4412 global mem driver,y for kernel,m for module.
复制代码
在kernel/drivers/char/x4412/Makefile中添加如下语句:
  1. obj-$(CONFIG_X4412_GLOBALMEM_DRIVER) += x4412-globalmem.o
复制代码
编译内核,将会在kernel/drivers/char/x4412目录下生成目标文件x4412-globalmem.ko文件。
使用指令测试驱动模块:
  1. [root@x4412 mnt]# insmod x4412-globalmem.ko
  2. [root@x4412 mnt]# echo 'www.9tripod.com' > /dev/x4412-globalmem
  3. [root@x4412 mnt]# cat /dev/x4412-globalmem
  4. www.9tripod.com
复制代码
也可以编写应用程序测试驱动模块,参考源码如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>
  4. #define DEVICE_NAME  "/dev/x4412-globalmem"
  5. #define MEM_CLEAR 0x1

  6. int main(int argc,char **argv)
  7. {
  8.         int fd;
  9.            char buf[30];
  10.            char str[]="hello,x4412!";

  11.         fd = open(DEVICE_NAME,O_RDWR);
  12.         if(fd == -1)
  13.         {
  14.                   printf("open device %s error \n",DEVICE_NAME);
  15.         }
  16.         else
  17.         {
  18.                    write(fd,str,12);
  19.                    usleep(5000);
  20.                    read(fd,buf,12);
  21.                    printf("read from /dev/x4412-gloalmem:\r\n");
  22.                    printf("%s\r\n",buf);
  23.                    usleep(5000);
  24.                    ioctl(fd,MEM_CLEAR);
  25.                    read(fd,buf,12);
  26.                    printf("read from /dev/x4412-gloalmem later:\r\n");
  27.                    printf("%s\r\n",buf);
  28.                    close(fd);
  29.         }
  30.         return 0;
  31. }
复制代码
       应用程序首先向驱动模块写入字符串“hello,x4412!”,再将它读取出来。接着调用ioctl函数将驱动模块的4KB内存清零,然后再读取4KB内存模块的前12个字节数据,判断内存是否已经被清零。
       测试界面如下:
  1. [root@x4412 mnt]# ./x4412-globalmem-app
  2. read from /dev/x4412-gloalmem:
  3. hello,x4412!
  4. [  502.251039] globalmem is set to zero
  5. read from /dev/x4412-gloalmem later:

  6. [root@x4412 mnt]#
复制代码
可直接在x4412开发板或ibox卡片电脑上运行的驱动模块和应用程序映像:
x4412-globalmem.ko (4.16 KB, 下载次数: 11)
x4412-globalmem-app (6.58 KB, 下载次数: 11)


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-6-23 11:24 , Processed in 0.020176 second(s), 20 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表