Chinaunix首页 | 论坛 | 博客
  • 博客访问: 282017
  • 博文数量: 59
  • 博客积分: 235
  • 博客等级: 二等列兵
  • 技术积分: 409
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-12 15:11
文章分类
文章存档

2013年(4)

2012年(55)

分类: LINUX

2012-12-26 23:34:50

VMware 版本:
    VMware-workstation-7-english
Linux内核版本:
    Linux ubuntu 2.6.35-22-generic #33-Ubuntu SMP Sun Sep 19 20:34:50 UTC 2010 i686 GNU/Linux
附:
1、查看内核版本命令:
1) cat /proc/version
2) uname -a
3) uname -r
2、查看发行版本命令
1) lsb_release -a
2) 用命令找到/etc目录下的issue文件release文
 
驱动的大致理解:介于硬件和操作系统之间的一层软件接口,按照操作系统规范要求,写成,Linux下主要就是把对硬件的操作封装成对文件的操作,对硬件的操作也就是高低电平的设置读取,也就是读写01。
 
源码,主要是照例子编写:

点击(此处)折叠或打开

  1. #include <linux/module.h>
  2. #include <linux/types.h>
  3. #include <linux/fs.h>
  4. #include <linux/errno.h>
  5. #include <linux/mm.h>
  6. #include <linux/sched.h>
  7. #include <linux/init.h>
  8. #include <linux/cdev.h>
  9. #include <asm/io.h>
  10. #include <asm/system.h>
  11. #include <asm/uaccess.h>
  12. #include <linux/slab.h>

  13. #define GLOBALMEM_SIZE 0x1000 /*Global memory size:4kb*/
  14. #define MEM_CLEAR 0x1 /*Global memory clear 0*/
  15. #define GLOBALMEM_MAJOR 250 /*Suggested globalmem major Number*/

  16. #define _user
  17. static globalmem_major = GLOBALMEM_MAJOR;

  18. /*cdev_init(),the globalmem's cdev linked file_operations struct*/
  19. /*struct of device globalmem_dev*/
  20. struct globalmem_dev
  21. {
  22.    struct cdev cdev; /*cdev struct */
  23.    unsigned char mem[GLOBALMEM_SIZE]; /*global memory */
  24. };

  25. struct globalmem_dev *globalmem_devp; /*the pointer of device struct*/

  26. /* the file open function */
  27. int globalmem_open(struct inode *inode,struct file *filp)
  28. {
  29. /*put the struct of device date to file private data pointer */
  30. filp->private_data= globalmem_devp;
  31. return 0;
  32. }
  33. /* the function of release file */
  34. int globalmem_release(struct inode *inode,struct file *filp)
  35. {
  36.   return 0;
  37. }


  38. static ssize_t globalmem_read(struct file *filp,char _user *buf,size_t size,loff_t *ppos)
  39. {
  40.  unsigned long p = *ppos;
  41.  unsigned int count = size;
  42.  int ret =0;

  43. struct globalmem_dev *dev = filp->private_data;/*get the device pointer*/
  44. /*analyse and get usefull read length*/
  45. if(p >= GLOBALMEM_SIZE) //offset position over boundary*/
  46. {
  47.   return count ? - ENXIO:0;
  48. }
  49. if(count >GLOBALMEM_SIZE -p) //to read word number too long
  50. {
  51.  count =GLOBALMEM_SIZE -p;
  52. }
  53.   /*kernel space -> user space*/
  54. if(copy_to_user(buf,(void*)(dev->mem +p),count))
  55. {
  56.  ret = -EFAULT;
  57. }
  58. else
  59. {
  60.  *ppos += count;
  61.   ret = count;

  62. printk(KERN_INFO"read %d bytes(s) from %d \n",count,p);
  63. }
  64.  return ret;
  65. }

  66. static ssize_t globalmem_write(struct file *filp,const char _user *buf,size_t size, loff_t *ppos)
  67. {
  68.   unsigned long p = *ppos;
  69.   unsigned int count = size;
  70.   int ret =0;

  71.   struct globalmem_dev *dev = filp->private_data;/*get device pointer */
  72. /*analyse and get valid writing length */
  73. if(p >= GLOBALMEM_SIZE) // to write offset position over boundry*/
  74. {
  75.    return count? -ENXIO:0;
  76. }

  77. /*user space -> kernel space */
  78. if(copy_from_user(dev->mem +p,buf,count))
  79. {
  80.    ret = -EFAULT;
  81. }
  82. else
  83. {
  84.   *ppos += count;
  85.   ret = count;
  86.   
  87.   printk(KERN_INFO "written %d bytes(s) from %d \n",count,p);
  88. }
  89.  return ret;
  90. }

  91. static loff_t globalmem_llseek(struct file *filp,loff_t offset, int orig)
  92. {
  93.  loff_t ret;
  94.  switch (orig)
  95.  {
  96.    case 0: /*offset from the file head*/
  97.     if(offset < 0)
  98.     {
  99.       ret = - EINVAL;
  100.       break;
  101.     }
  102.    
  103.    if((unsigned int)offset>GLOBALMEM_SIZE) // offset overstep the boundary
  104.    {
  105.      ret = -EINVAL;
  106.      break;
  107.    }
  108.    filp->f_pos = (unsigned int)offset;
  109.    ret =filp->f_pos;
  110.    break;
  111.    
  112.    case 1: /* offset from the current position */
  113.       if((filp->f_pos +offset) >GLOBALMEM_SIZE) //offset overstep the boundary
  114.       {
  115.         ret = -EINVAL;
  116.         break;
  117.       }
  118.       if((filp->f_pos +offset)<0)
  119.       {
  120.         ret = -EINVAL;
  121.         break;
  122.       }
  123.      filp->f_pos += offset;
  124.      ret = filp->f_pos;
  125.      break;
  126.      default:
  127.         ret = -EINVAL;
  128. }
  129.     return ret;
  130. }

  131.  static int globalmem_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
  132. {
  133.   struct globalmem_dev *dev = filp->private_data;/* get the struct of device pointer */
  134.   switch(cmd)
  135.   {
  136.    case MEM_CLEAR: //clear global meme
  137.       memset(dev->mem,0,GLOBALMEM_SIZE);
  138.       printk(KERN_INFO "globalmem is set to zero \n");
  139.       break;
  140.     default:
  141.        return - EINVAL; //other order not support
  142.   }
  143.      return 0;
  144. }
  145. static const struct file_operations globalmem_fops=
  146. {
  147.    .owner = THIS_MODULE,
  148.    .llseek = globalmem_llseek,
  149.    .read = globalmem_read,
  150.    .write = globalmem_write,
  151.    .ioctl = globalmem_ioctl,
  152.    .open = globalmem_open,
  153.    .release= globalmem_release,
  154. };

  155. /*init and add cdev struct*/
  156. static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
  157. {
  158.   int err,devno = MKDEV(globalmem_major,0);
  159.   cdev_init(&dev->cdev,&globalmem_fops);
  160.   dev->cdev.owner = THIS_MODULE;
  161.   dev->cdev.ops = &globalmem_fops;
  162.   err = cdev_add(&dev->cdev,devno,1);
  163.   if(err)
  164.   {
  165.     printk(KERN_NOTICE"Error %d adding globalmem",err);
  166.   }
  167. }


  168. /* the function of globalmem driver module insmod */
  169. int globalmem_init(void)
  170. {
  171.    int result;
  172.    dev_t devno = MKDEV(globalmem_major,0);
  173.    
  174.  /*apply for char device driver region */
  175. if(globalmem_major)
  176. {
  177.   result = register_chrdev_region(devno,1,"globalmem");
  178. }else{ /*dynamic get major device number*/
  179.   result = alloc_chrdev_region(&devno,0,1,"globalmem");
  180.   globalmem_major = MAJOR(devno);
  181. }

  182. if (result < 0)
  183. {
  184.   return result;
  185. }

  186. /*dynamic apply for memery of the device struct */
  187. globalmem_devp = kmalloc(sizeof (struct globalmem_dev),GFP_KERNEL);
  188. if(!globalmem_devp)/* apply failed*/
  189. {
  190.   result = -ENOMEM;
  191.   goto fail_malloc;
  192. }
  193. memset(globalmem_devp,0,sizeof(struct globalmem_dev));
  194. globalmem_setup_cdev(globalmem_devp,0);
  195. return 0;
  196. fail_malloc:unregister_chrdev_region(devno,1);
  197. return result;
  198. }

  199. /*rsmod module function*/
  200. void globalmem_exit(void)
  201. {
  202.   cdev_del(&globalmem_devp->cdev); /* unregister cdev*/
  203.   kfree(globalmem_devp); /* release device struct memery*/
  204.   unregister_chrdev_region(MKDEV(globalmem_major,0),1);/* release device number*/
  205. }
  206. MODULE_AUTHOR("Learner gsw");
  207. MODULE_LICENSE("Dual BSD/GPL");

  208. module_param(globalmem_major,int,S_IRUGO);
  209. module_init(globalmem_init);
  210. module_exit(globalmem_exit);
Makefile:

点击(此处)折叠或打开

  1. obj-m = globalmem_char.o
  2. KVERSION = $(shell uname -r)
  3. all:
  4.     make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
  5. clean:
  6.     make -C /lib/modules/$(KVERSION)build M=$(PWD) clean
注:对应的.c 文件应为globalmem_char.c
 
 若报错:implicit declaration of function ‘kmalloc’
在网上找了一下,是缺少了一个头文件
#include

驱动在用户空间的验证方法,
   1 简单的手动验证:
在生成的.ko 目录下,
1)加载驱动:
insmod globalmem_char.ko
若报错:insmod: error inserting './globalmem.ko': -1 Device or resource busy的问题是因为设备号冲突了,用cat /proc/devices查看设备号。 .c文件里面的宏定义那里改为其他未占用的值。
2)lsmod 查看驱动是否加载:
 
3)cat /proc/devices  命令查看,主设备号为250的“globalmem_char”字符设备驱动,

 
3)创建  /dev/globalmem 设备节点: mknod /dev/globalmem c 250 0 命令,
4)并通过 echo 'hello world' > /dev/globalmem 命令和 cat /dev/globalmem 命
令分别验证设备的写和读.
 
cat 报错:
ANSWER:
<1>
你把globalmem_read函数中的if (p > GLOBALMEM_SIZE)改为if (p > GLOBALMEM_SIZE)就好了。具体原因是一次调用cat会读两次,每次读取4096个字节,此时文件读指针ppos就是4096了,在第二次读的时候,到if (p > GLOBALMEM_SIZE)这里条件为真,globalmem_read就返回ENXIO错误了,所以才会出现No such device or address"错误。不知道在宋老师的机子上为什么就是正确的?
<2>
宋宝华 :返回错误值才是正确的,因为读的位置已经越界,所以要返回错误。该返回错误的时候返回正确,那就是错误。改成if (p > GLOBALMEM_SIZE)是错误的。

<3> 根据上面两点可以小结书上是没有错误的。输出的错误信息是正常的debug信息,千不该万不该就是cat调用了read两次。

 
  2.c编程用户空间验证,待续:
   工作较忙,抽点时间续上,注释的英文,由于水平有限,会有语法错误,尽量使用专业词汇。
  c文件:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <fcntl.h>

  5. int main()
  6. {
  7. char string[40],reads[40];
  8. int length,ret;

  9. system("rmmod /dev/globalmem_char");
  10. system("rm /dev/globalmem");
  11. system("insmod /home/gsw/DriverLearn/globalMemory/globalmem_char.ko");
  12. system("mknod /dev/globalmem c 250 0");

  13. int fd = open("/dev/globalmem",O_RDWR);
  14. if(fd<0){
  15.     printf("Open error!\n");
  16.     return -1;
  17. }

  18. strcpy(string,"globalTest_driver Learner");
  19. length = strlen(string);
  20. if((ret=write(fd,string,length))!=length){
  21.    printf("Write failed!\n");
  22.    return -1;
  23. }

  24. lseek(fd,0,SEEK_SET);
  25. memset(reads,0,sizeof(reads));
  26. ret = read(fd,reads,length);
  27. printf("The read is:%s\n",reads);
  28. close(fd);

  29. }

      system("rmmod /dev/globalmem_char");system("rm /dev/globalmem");
      这两句是为了使下面的两句能够执行成功。

      system("insmod /home/gsw/DriverLearn/globalMemory/globalmem_char.ko");
      system("mknod /dev/globalmem c 250 0");

      系统运行后一般只用这两句加载ko,建立节点就好了,通常都是在初始化相关的shell脚本里面写的。


 对应的Makefile 文件:


点击(此处)折叠或打开

  1. #define command variant
  2. CC = gcc
  3. AR = ar
  4. RM = rm
  5. CP = cp

  6. #set parametric variation
  7. INCLUDES =
  8. CFLAGS += -c $(INCLUDES)
  9. LKFLAGS +=

  10. #define local variable
  11. LIBS =
  12. OBJS = gloBalmemeTest.o
  13. TARGET = gloBalmemTest
  14. INSTALLDIR = ./

  15. all:$(TARGET)
  16. $(TARGET):$(OBJS)$(LIBS)
  17.     $(CC) $(LKFLAGS) $(OBJS) $(LIBS) -o $@
  18. %.o:%.c
  19.     $(CC) $(CFLAGS) $< -o $@
  20. install:
  21. #    $(CP)$(TARGET)$(INSTALLDIR)
  22. clean:$(SUBDIR_CLEAN)
  23.     -$(RM) *.o $(TARGET)
  24. $(SUBDIR_CLEAN):
  25. #    $(RM) -C mylib clean

程序运行截图:
  
至此,简单的字符驱动模块已经基本成形,但需要学习的东西依然很多。
 
 
阅读(2531) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~