`
mmdev
  • 浏览: 12881322 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

Linux设备驱动工程师之路——硬件访问及混杂设备LED驱动

 
阅读更多

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[i],led_cfg_table[i]);
                   s3c2410_gpio_setpin(led_table[i],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");


分享到:
评论

相关推荐

    linux混杂设备驱动讲解

    该文档主要讲述了linux混杂设备的驱动编程。混杂设备可以理解为一种主设备号固定为10的一种字符设备。但是编程的过程要比字符设备简单很多。可以达到和字符设备一样的效果。有兴趣可以了解一下。

    OK6410 linux混杂设备驱动LED

    OK6410 linux混杂设备驱动LED程序,使用3.0.1内核编译,还有驱动源码、Makefile和测试程序

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

     本书是linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深入探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如pcmcia、i2c和usb等外部总线以及视频、音频、...

    Linux常见驱动源码分析(kernel hacker修炼之道全集)--李万鹏

    kernel hacker修炼之道之驱动-混杂设备.pdf kernel hacker修炼之道之驱动-按键.pdf kernel hacker修炼之道之PCI subsystem(五).pdf kernel hacker修炼之道之PCI subsystem(四).pdf kernel hacker修炼之...

    精通LINUX设备驱动程序开发

    291 14.1 存储技术 291 14.2 linux块i/o层 295 14.3 i/o调度器 295 14.4 块驱动程序数据结构和方法 296 14.5 设备实例:简单存储控制器 298 14.5.1 初始化 299 14.5.2 块设备操作 301 14.5.3 磁盘访问 ...

    国嵌培训课件Linux驱动程序设计

    3. 混杂设备驱动 4. LED驱动程序设计 第四天 1. Linux总线、设备、驱动模型 2. Linux platform驱动 3. 中断处理 4. 按键驱动程序 第五天 1.Linux网络体系架构 2. Linux网卡驱动程序设计 3. Dm9000网卡驱动程序分析 ...

    字符设备LED驱动程序

    所有的驱动程序都应该对应一个具体的设备,这个LED驱动当然设备应该是LED。但是linux将它分成了一类叫做混杂设备。这类设备共享一个主设备号,但次设备号不同所有混杂设备形成一个链表,要访问一个设备时根据次设备...

    linux混杂设备

    本文档主要是本人在工作中对于混杂设备的一些总结及个人见解,从中分析了应该如何应用混杂设备,并且比较了linux2.6和linux3.0版本在混杂设备上的区别,方便移植linux2.6驱动到linux3.0版本上。

    Linux驱动编程思维导图

    包含内核模块设计 硬件访问技术 字符设备驱动编程,字符设备控制,混杂设备驱动编程,工作队列驱动编程 linux中断处理程序等

    128*64点阵LCD在linux下驱动程序

    在S3C2440底板上,linux操作系统的128*64点阵LCD驱动程序,LCD使用的是ST7565R控制器,驱动中使用混杂设备驱动模型

    Linux驱动编程(含源码)

    linux驱动编程包括模块、字符设备、互斥与同步处理、I/O内存处理、中断处理、混杂设备、块设备等。含源码,每个驱动代码都有详细注释说明

    Linux混杂设备驱动(watchdog)

    Misc(或miscellaneous)驱动是一些拥有着共同特性的简单字符设备驱动。内核抽象出这些特性而形成一些API(在文件drivers/char/misc.c中实现),以简化这些设备驱动程序的初始化。所有的misc设备被分配同一个主设备...

    混杂字符设备之LED设备驱动设计

    混杂字符设备之LED设备驱动设计及CPU寄存器虚拟内存映射分析:混杂字符设备的主要特点是主设备号(10)公用,通过一个链表将各个设备关联起来,设备的识别主要依靠次设备号。

    混杂按键驱动

    linux平台混杂按键驱动 混杂设备驱动 按键驱动 linux

    linux驱动编程速成

    详细讲解linux驱动程序的编写,及相关技术,包括内核模块、混杂设备、IO端口、阻塞与非阻塞、系统中断等

    S5PV210-Key_misc.zip Key设备驱动

    S5PV210_Key Linux设备驱动,采用MISC混杂设备模型+中断处理模式

    三种方式下编写的按键驱动

    这是本人试验编写的按键驱动、 包含基于字符设备的按键驱动 基于混杂模式下编写的按键驱动 基于platform平台编写的按键驱动

    基于Linux系统局域网混杂模式网卡的检测与应用.pdf

    基于Linux系统局域网混杂模式网卡的检测与应用.pdf

    Linux下双网卡绑定技术实现负载均衡

    本文介绍的Linux双网卡绑定实现就是使用两块网卡虚拟成为一块网卡,这个聚合起来的 设备看起来是一个单独的以太网接口设备,通俗点讲就是两块网卡具有相同的IP地址而并行链接聚合成一个逻辑链路工作。其实这项技术在...

Global site tag (gtag.js) - Google Analytics