Chinaunix首页 | 论坛 | 博客
  • 博客访问: 630814
  • 博文数量: 75
  • 博客积分: 988
  • 博客等级: 准尉
  • 技术积分: 1269
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-10 15:44
文章分类

全部博文(75)

文章存档

2022年(1)

2019年(1)

2018年(1)

2016年(9)

2015年(7)

2013年(6)

2012年(40)

2011年(10)

分类: LINUX

2012-03-12 08:49:09

当我们着手写一个驱动的时候,并不需要从0开始,而是可以在内核源码里面找到别人成熟的类似的驱动进行修改,移植。这也是一个非常高效的过程。
首先,我们要明白自己的目标,要的是什么.
从上到下,一个软件系统可以分为:应用程序、库、操作系统(内核)、驱动程序。以一个点灯程序为例:
现在的内核都有一个VFS文件系统,会根据用户空间(应用程序)里面的设备提供的设备属性,设备号来进行填充,然后根据填充的内容注册进内核。

实现流程如下:
1,写出要实现的功能:led_open、led_write
2, 怎么告诉内核 file_operation结构体
       a,定义fileoperation并填充
       b,把这个结构告诉内核
         register_chrdev(...)
3,谁来调用 驱动入口
   int_led_drv_init(void)
4, 对驱动进行修饰
   module_init(led_drv_init);

点击(此处)折叠或打开

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/delay.h>
  6. #include <asm/uaccess.h>
  7. #include <asm/irq.h>
  8. #include <asm/io.h>
  9. #include <asm/arch/regs-gpio.h>
  10. #include <asm/hardware.h>


  11. static struct class *leddrv_class;
  12. static struct class_device *leddrv_class_dev;

  13. volatile unsigned long *gpfcon = NULL;
  14. volatile unsigned long *gpfdat = NULL;

  15. static int led_drv_open(struct inode *inode, struct file *file)
  16. {
  17.     printk("led_drv_open\n");

  18.     gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)); //清零
  19.     gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)); //输出
  20.     return 0;
  21. }

  22. static ssize_t led_drv_write(struct file *file, const char __user *buf,size_t count, loff_t * ppos)
  23. {
  24.     int val;

  25.     copy_from_user(&val, buf, count); //copy to user

  26.     if (val == 1)
  27.         {
  28.             //open_led
  29.             gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
  30.         }
  31.     else
  32.         {
  33.             // close_led
  34.             gpfdat |= (1<<4) | (1<<5) | (1<<6);
  35.         }
  36.     
  37.     printk("led_drv_write\n");
  38.     return 0;
  39. }

  40. static struct file_operations led_drv_fops = {
  41.     .owner = THIS_MODULE,
  42.     .open = led_drv_open,
  43.     .write = led_drv_write,
  44. };

  45. int major;
  46. int led_drv_init(void)
  47. {
  48.     major = register_chrdev(0,"led_drv",&led_drv_fops);

  49.     leddrv_class = class_create(THIS_MODULE, "leddrv");
  50.     if(IS_ERR(leddrv_class))
  51.         return PTR_ERR(leddrv_class);

  52.     leddrv_class_dev = class_device_creat(leddrv_class, NULL, MKDEV(major,0),NULL,"led");
  53.     if(unlikely(IS_ERR(leddrv_class_dev)));
  54.         return PTR_ERR(leddrv_class_dev);

  55.     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
  56.     gpfdat = gpfcon + 1; //加1是以(volatile unsigned long *)的长度为单位,即为4字节
  57.     return 0;
  58. }

  59. void led_drv_exit(void)
  60. {
  61.     unregister_chrdev(major,"led_drv");

  62.     class_device_unregister(leddrv_class_dev);
  63.     class_destroy(leddrv_class);
  64.     iounmap(gpfcon);
  65.     return 0;
  66. }


  67. module_init(led_drv_init);
  68. module_exit(led_drv_exit);
当然,以上代码并不是很完善,只是为了说明一些问题,
驱动程序可以自动或手动分配主设备号,上面的代码major=0,说明是自动分配主设备号,应该程序也可以自动创建或手工创建,上面的例子中通过linux udev机制,通过两个类实现自动创建。open配置引脚,wirte返回引脚状态。file_operation里面的结构有很多,根据需要找到要用到的进行填充。还有就是驱动程序里面用到的是虚拟地址,通过ioremap/iounmap来映射/释放虚拟地址。

测试代码:

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>

  5. int main (int argc, char **argv)
  6. {
  7.     int fd;
  8.     int val = 1;
  9.     fd = open ("/dev/led", O_RDWR);
  10.     if (fd<0)
  11.         printf("can't open!\n");

  12.     if (argc != 2)
  13.     {
  14.         printf("Usage : \n");
  15.         printf("%s \n", argv[0]);
  16.         return 0;
  17.     }

  18.     if (strcmp(argv[1], "on") == 0)
  19.     {
  20.         val = 1;
  21.     }
  22.     else
  23.     {
  24.         val = 0;
  25.     }

  26.     write(fd, &val, 4);

  27.     return 0;

  28. }
16,17行对输入的参数进行判断,如果输入的参数不等于2,则打印出正确的使用方法。把val写入驱动程序中的val就可以控制灯的亮灭。

Makefile文件:用来编译led_drv.c

点击(此处)折叠或打开

  1. KERN_DIR = /xxx/linux-2.6.22.6

  2. all:
  3. make -C $(KERN_DIR) M=`pwd` modules

  4. clean:
  5. make -C $(KERN_DIR) M=`pwd` modules clean
  6. rm -rf modules.order

  7. obj-m += led_drv.o
第一行是存放的编译好的内核源码目录,根据自己实际情况而定。这里要注意的是内核的源码要和驱动程序版本相同,不然可能会出现一些问题。

该系列笔记均是看韦东山视频教程的记录,基本上用的是他所提供的源码。在这里也推荐大家看下他的视频教程,真的很不错的!

以上难免有疏漏之处,今后会完善。
阅读(2024) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~