硬件访问及混杂设备LED驱动《转》

1103阅读 0评论2012-07-27 gjf05_05
分类:

转载:衡阳师范学院08电2  K-Style  http://blog.csdn.net/ayangke,QQ:843308498 邮箱:

http://blog.chinaunix.net/link.php?url=http://blog.csdn.net%2Fayangke%2Farticle%2Fdetails%2F6804917

 

一、重要知识点

   

    1.I/O端口和I/O内存

寄存器和常规内存的区别:寄存器和RAM主要不同在于寄存器有边际效果,读取某个地址时可能导致该地址的内容发生变化,比如说很多设备的中断状态寄存器只要一读取,便自动清0。所以硬件寄存器不能直接访问,而要通过I/O端口和I/O内存两种方式访问。

       在硬件层,I/O内存区域和I/O端口区域没有概念上的区别:它们都是通过向地址总线和控制总线发生电平信号进行访问,再通过数据总线读写数据。

       a.I/O端口:

       一些CPU制造厂在它们的芯片中使用单一的地址空间,而一些则为外设保留独立的地址空间,以便和内存区间分开来,这段独立与内存地址空间的地址空间就叫I/O端口。在/proc/ioport中可以看到。嵌入式处理器大部分不支持I/O端口。

       访问I/O端口有两步:1.申请I/O端口2.读写I/O端口

       申请I/O端口:

       struct resource *request_region(unsigned long first, unsigned long n, const char *name)

       申请从first开始的n个端口。参数name为设备名称。如果分配成功则返回非NULL值。

       释放I/O端口:

       void release_region(unsigned long start, unsigned long n)

       读写I/O端口:

读写一个字节

       unsigned inb(unsigned port)

       void outb(usigned char byte, unsigned port)

       读写二个字节

       unsigned inb(unsigned port)

       void outb(usigned short byte, unsigned port)

       读写四个字节

       unsigned inb(unsigned port)

       void outb(usigned long byte, unsigned port)

 

       b.I/O内存

       通过将外设寄存器映射到内存空间来进行访问叫做I/O内存,嵌入式大多只支持这种操作。

       访问I/O内存有三步:1.申请I/O内存区域2.映射I/O内存区域3.读写I/O内存

       申请I/O内存区域

       struct resource *request_mem_region(unsigned long start, unsigned long len, char *name)

       申请访问从start(I/O物理地址)开始的len长度的I/O内存区域,如成功则返回非NULL值。在/proc/iomem中可可以查看到已经被申请的I/O内存区域。在后面的我写的驱动程序中并没用使用申请这一步,是因为我使用的GPIO内存区域已经被申请,如果在申请会导致失败。但是这样做法是不安全的做法,因为同一I/O内存区域域被多个模块使用。

       释放I/O内存区域

       void release_mem_region(unsigned long start, unsigned long len)

       映射I/O内存区域

       void *ioremap(unsigned long phy_addr, unsigned long size)

       映射从物理地址phy_addr开始的size长度的的地址空间。返回可以访问I/O内存地址。由ioreamp返回的地址不应该直接引用,必须通过下面一些列的读写函数完成。

       读写操作I/O内存

       读1、2、4个字节:

       unsigned int read8(void *addr);

       unsigned int read16(void *addr)

       unsigned int read32(void *addr)

       写读1、2、4个字节:

       voidio write8(u8 value, void *addr)

       voidio write16(u8 value, void *addr)

       voidio write32(u8 value, void *addr)

 

    2.混杂设备驱动

         在Linux系统中,存在一类字符设备,他们共享一个主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdeivce),查看/proc/device中可以看到一个名为misc的主设备号为10。所有的混杂设备形成一个链表,对设备访问时内存根据次设备号找到对应的miscdevice设备。

         Linux内核使用structmiscdeivce来描述一个混杂设备

         struct miscdevice{
         int minor;

         const struct file_opreations *fops;

         struct list_head list;

         struct device *parent;

         struct device *this_device;     

}

使用时只需填写minor次设备号,*name设备名,*fops文件操作函数集即可。

Linux内核使用misc_register函数注册一个混杂设备。注册成功后,linux内核为自动为该设备创建设备文件。

int misc_register(struct miscdevice *misc)

 

二、驱动代码

 

1.驱动代码一

这段LED驱动代码采用手动I/O内存映射的方式访问。没有使用申请内存区域函数,这样使不安全的。直接访问I/O内存地址而不是通过读写函数访问也是不安全。同时驱动代码包含硬件相关代码也是移植性不好的。写这段代码是为了帮助理解I/O内存映射的过程。

  #include <linux/miscdevice.h>  
  1. #include <linux/delay.h>  
  2. #include <asm/irq.h>  
  3. #include <mach/regs-gpio.h>  
  4. #include <mach/hardware.h>  
  5. #include <linux/kernel.h>  
  6. #include <linux/module.h>  
  7. #include <linux/init.h>  
  8. #include <linux/mm.h>  
  9. #include <linux/fs.h>  
  10. #include <linux/types.h>  
  11. #include <linux/delay.h>  
  12. #include <linux/moduleparam.h>  
  13. #include <linux/slab.h>  
  14. #include <linux/errno.h>  
  15. #include <linux/ioctl.h>  
  16. #include <linux/cdev.h>  
  17. #include <linux/string.h>  
  18. #include <linux/list.h>  
  19. #include <linux/pci.h>  
  20. #include <asm/uaccess.h>  
  21. #include <asm/atomic.h>  
  22. #include <asm/unistd.h>  
  23. #include <asm/io.h>  
  24. #include <asm/system.h>  
  25. #include <asm/uaccess.h>  
  26. #include <linux/ioport.h>  
  27.    
  28. volatile unsigned int long *gpb_con = NULL;  
  29. volatile unsigned int long *gpb_data = NULL;  
  30.    
  31. static int leds_ioctl(struct inode *inode, struct file *file,  
  32.                     unsigned int cmd, unsigned long arg)  
  33. {  
  34.    
  35.          if((cmd>1) |(arg>3))  
  36.                    return-EINVAL;  
  37.                     
  38.          switch(cmd)  
  39.          {  
  40.                    case 0:  
  41.                             *gpb_data&= ~(1<<arg+5);  
  42.                             break;  
  43.                    case 1:  
  44.                             *gpb_data|= (1<<arg+5);  
  45.                             break;  
  46.                              
  47.                    default:  
  48.                             return-EINVAL;  
  49.                              
  50.          }  
  51.           
  52.          return 0;  
  53. }  
  54.    
  55. static const struct file_operations leds_fops = {  
  56.          .owner = THIS_MODULE,  
  57.          .ioctl = leds_ioctl,  
  58. };  
  59.    
  60. static struct miscdevice misc = {  
  61.          .minor =MISC_DYNAMIC_MINOR,  
  62.          .name ="my_leds",  
  63.          .fops =&leds_fops,  
  64. };  
  65.    
  66. static int __init leds_init(void)  
  67. {  
  68.          int ret;  
  69.    
  70.          //注册混杂设备  
  71.          ret =misc_register(&misc);  
  72.           
  73.          //映射I/O内存  
  74.          gpb_con = (volatileunsigned long *)ioremap(0x56000010, 16); //0x56000010为GPIOB控制寄存器的物理地址  
  75.          gpb_data = gpb_con+1;  
  76.           
  77.          //配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭  
  78.          *gpb_con |=(1<<5*2)|(1<<6*2)|(1<<7*2)|(1<<8*2);  
  79.          *gpb_data |=(1<<5) | (1<<6) | (1<<7) | (1<<8);  
  80.           
  81.          printk("ledsinit.\n");  
  82.          return ret;  
  83. }  
  84.    
  85. static void leds_exit(void)  
  86. {  
  87.    
  88.          misc_deregister(&misc);          
  89.           
  90.          printk("leds_exit\n");  
  91. }  
  92.    
  93. module_init(leds_init);  
  94. module_exit(leds_exit);  
  95.    
  96. MODULE_AUTHOR("Y-Kee");  
  97. MODULE_LICENSE("GPL");  
  98.    

 

2.驱动代码二:

采用内核定义好的GPIO接口(S3C2410_GPB5和S3C2410_GPB5_OUTP)和GPIO操作函数(s3c2410_gpio_setpin和s3c2410_gpio_cfgpin)。可移植性好,也是正确的做法。内核的GPIO操作函数也是通过一些的运算将GPIO接口换算成虚拟内存地址然后进行访问的。

     #include <linux/delay.h>  

  1. #include <asm/irq.h>  
  2. #include <mach/regs-gpio.h>  
  3. #include <mach/hardware.h>  
  4. #include <linux/kernel.h>  
  5. #include <linux/module.h>  
  6. #include <linux/init.h>  
  7. #include <linux/mm.h>  
  8. #include <linux/fs.h>  
  9. #include <linux/types.h>  
  10. #include <linux/delay.h>  
  11. #include <linux/moduleparam.h>  
  12. #include <linux/slab.h>  
  13. #include <linux/errno.h>  
  14. #include <linux/ioctl.h>  
  15. #include <linux/cdev.h>  
  16. #include <linux/string.h>  
  17. #include <linux/list.h>  
  18. #include <linux/pci.h>  
  19. #include <asm/uaccess.h>  
  20. #include <asm/atomic.h>  
  21. #include <asm/unistd.h>  
  22. #include <asm/io.h>  
  23. #include <asm/system.h>  
  24. #include <asm/uaccess.h>  
  25.    
  26. static unsigned long led_table[] =   {  
  27.          S3C2410_GPB5,  
  28.          S3C2410_GPB6,  
  29.          S3C2410_GPB7,  
  30.          S3C2410_GPB8,  
  31. };  
  32.    
  33. static unsigned long led_cfg_table[] =    {  
  34.          S3C2410_GPB5_OUTP,  
  35.          S3C2410_GPB6_OUTP,  
  36.          S3C2410_GPB7_OUTP,  
  37.          S3C2410_GPB8_OUTP,  
  38. };  
  39.    
  40.    
  41. static int leds_ioctl(struct inode *inode, struct file *file,  
  42.                     unsigned int cmd, unsigned long arg)  
  43. {  
  44.          if((cmd>1) |(arg>3))  
  45.                    return-EINVAL;  
  46.                     
  47.          switch(cmd)  
  48.          {  
  49.                    case 0:  
  50.                             s3c2410_gpio_setpin(led_table[arg],0);  
  51.                             break;  
  52.                    case 1:  
  53.                             s3c2410_gpio_setpin(led_table[arg],1);  
  54.                             break;  
  55.                              
  56.                    default:  
  57.                             return-EINVAL;  
  58.                              
  59.          }  
  60.           
  61.          return 0;  
  62. }  
  63.    
  64. static const struct file_operations leds_fops = {  
  65.          .owner = THIS_MODULE,  
  66.          .ioctl = leds_ioctl,  
  67. };  
  68.    
  69. static struct miscdevice misc = {  
  70.          .minor =MISC_DYNAMIC_MINOR,  
  71.          .name ="my_leds",  
  72.          .fops =&leds_fops,  
  73. };  
  74.    
  75. static int __init leds_init(void)  
  76. {  
  77.          int ret, i;  
  78.    
  79.          //注册混杂设备  
  80.          ret =misc_register(&misc);  
  81.           
  82.          //配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭  
  83.          for(i=0; i<4; i++)  
  84.          {  
  85.                    s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);  
  86.                    s3c2410_gpio_setpin(led_table[i],1);  
  87.          }  
  88.           
  89.          printk("ledsinit.\n");  
  90.          return ret;  
  91. }  
  92.    
  93. static void leds_exit(void)  
  94. {  
  95.    
  96.          misc_deregister(&misc);  
  97.          printk("leds_exit\n");  
  98. }  
  99.    
  100. module_init(leds_init);  
  101. module_exit(leds_exit);  
  102.    
  103. MODULE_AUTHOR("Y-Kee");  
  104. MODULE_LICENSE("GPL");  

 

上一篇:TCP/IP传送方式有三种:单播,广播,组播
下一篇:Linux多线程编程 [转载]