设为首页 收藏本站
查看: 448|回复: 0

[经验分享] Linux设备驱动工程师之路——硬件访问及混杂设备LED驱动

  [复制链接]

尚未签到

发表于 2016-3-17 12:09:36 | 显示全部楼层 |阅读模式
  Linux设备驱动工程师之路——硬件访问及混杂设备LED驱动
  K-Style
  转载请注明来自于衡阳师范学院08电2 K-Style http://blog.csdn.net/ayangke,QQ:843308498 邮箱:yangkeemail@qq.com
  
  一、重要知识点
  
  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端口:
  structresource *request_region(unsigned long first, unsigned long n, const char *name)
  申请从first开始的n个端口。参数name为设备名称。如果分配成功则返回非NULL值。
  释放I/O端口:
  voidrelease_region(unsigned long start, unsigned long n)
  读写I/O端口:
  读写一个字节
  unsignedinb(unsigned port)
  voidoutb(usigned char byte, unsigned port)
  读写二个字节
  unsignedinb(unsigned port)
  voidoutb(usigned short byte, unsigned port)
  读写四个字节
  unsignedinb(unsigned port)
  voidoutb(usigned long byte, unsigned port)
  
  b.I/O内存
  通过将外设寄存器映射到内存空间来进行访问叫做I/O内存,嵌入式大多只支持这种操作。
  访问I/O内存有三步:1.申请I/O内存区域2.映射I/O内存区域3.读写I/O内存
  申请I/O内存区域
  structresource *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内存区域
  voidrelease_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个字节:
  unsignedint read8(void *addr);
  unsignedint read16(void *addr)
  unsignedint read32(void *addr)
  写读1、2、4个字节:
  voidiowrite8(u8 value, void *addr)
  voidiowrite16(u8 value, void *addr)
  voidiowrite32(u8 value, void *addr)
  
  2.混杂设备驱动
  在Linux系统中,存在一类字符设备,他们共享一个主设备号(10),但此设备号不同,我们称这类设备为混杂设备(miscdeivce),查看/proc/device中可以看到一个名为misc的主设备号为10。所有的混杂设备形成一个链表,对设备访问时内存根据次设备号找到对应的miscdevice设备。
  Linux内核使用structmiscdeivce来描述一个混杂设备
  structmiscdevice{
int minor;
  conststruct file_opreations *fops;
  structlist_head list;
  structdevice *parent;
  structdevice *this_device;
  }
  使用时只需填写minor次设备号,*name设备名,*fops文件操作函数集即可。
  Linux内核使用misc_register函数注册一个混杂设备。注册成功后,linux内核为自动为该设备创建设备文件。
  intmisc_register(struct miscdevice *misc)
  
  二、驱动代码
  
  1.驱动代码一
  这段LED驱动代码采用手动I/O内存映射的方式访问。没有使用申请内存区域函数,这样使不安全的。直接访问I/O内存地址而不是通过读写函数访问也是不安全。同时驱动代码包含硬件相关代码也是移植性不好的。写这段代码是为了帮助理解I/O内存映射的过程。
  
#include <linux/miscdevice.h>#include <linux/delay.h>#include <asm/irq.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/ioctl.h>#include <linux/cdev.h>#include <linux/string.h>#include <linux/list.h>#include <linux/pci.h>#include <asm/uaccess.h>#include <asm/atomic.h>#include <asm/unistd.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>#include <linux/ioport.h>volatile unsigned int long *gpb_con = NULL;volatile unsigned int long *gpb_data = NULL;static int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){if((cmd>1) |(arg>3))return-EINVAL;switch(cmd){case 0:*gpb_data&= ~(1<<arg+5);break;case 1:*gpb_data|= (1<<arg+5);break;default:return-EINVAL;}return 0;}static const struct file_operations leds_fops = {.owner = THIS_MODULE,.ioctl = leds_ioctl,};static struct miscdevice misc = {.minor =MISC_DYNAMIC_MINOR,.name ="my_leds",.fops =&leds_fops,};static int __init leds_init(void){int ret;//注册混杂设备ret =misc_register(&misc);//映射I/O内存gpb_con = (volatileunsigned long *)ioremap(0x56000010, 16); //0x56000010为GPIOB控制寄存器的物理地址gpb_data = gpb_con+1;//配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭*gpb_con |=(1<<5*2)|(1<<6*2)|(1<<7*2)|(1<<8*2);*gpb_data |=(1<<5) | (1<<6) | (1<<7) | (1<<8);printk("ledsinit.\n");return ret;}static void leds_exit(void){misc_deregister(&misc);        printk("leds_exit\n");}module_init(leds_init);module_exit(leds_exit);MODULE_AUTHOR("Y-Kee");MODULE_LICENSE("GPL");

  
  2.驱动代码二:
  采用内核定义好的GPIO接口(S3C2410_GPB5和S3C2410_GPB5_OUTP)和GPIO操作函数(s3c2410_gpio_setpin和s3c2410_gpio_cfgpin)。可移植性好,也是正确的做法。内核的GPIO操作函数也是通过一些的运算将GPIO接口换算成虚拟内存地址然后进行访问的。
  
#include <linux/delay.h>#include <asm/irq.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/ioctl.h>#include <linux/cdev.h>#include <linux/string.h>#include <linux/list.h>#include <linux/pci.h>#include <asm/uaccess.h>#include <asm/atomic.h>#include <asm/unistd.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>static unsigned long led_table[] =   {S3C2410_GPB5,S3C2410_GPB6,S3C2410_GPB7,S3C2410_GPB8,};static unsigned long led_cfg_table[] =    {S3C2410_GPB5_OUTP,S3C2410_GPB6_OUTP,S3C2410_GPB7_OUTP,S3C2410_GPB8_OUTP,};static int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){if((cmd>1) |(arg>3))return-EINVAL;switch(cmd){case 0:s3c2410_gpio_setpin(led_table[arg],0);break;case 1:s3c2410_gpio_setpin(led_table[arg],1);break;default:return-EINVAL;}return 0;}static const struct file_operations leds_fops = {.owner = THIS_MODULE,.ioctl = leds_ioctl,};static struct miscdevice misc = {.minor =MISC_DYNAMIC_MINOR,.name ="my_leds",.fops =&leds_fops,};static int __init leds_init(void){int ret, i;//注册混杂设备ret =misc_register(&misc);//配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭for(i=0; i<4; i++){s3c2410_gpio_cfgpin(led_table,led_cfg_table);s3c2410_gpio_setpin(led_table,1);}printk("ledsinit.\n");return ret;}static void leds_exit(void){misc_deregister(&misc);printk("leds_exit\n");}module_init(leds_init);module_exit(leds_exit);MODULE_AUTHOR("Y-Kee");MODULE_LICENSE("GPL");

  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-192032-1-1.html 上篇帖子: Linux下如何使用ftp命令对文件传输进行操作 下篇帖子: linux系统获取基础硬件信息的shell
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

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