现在的位置: 首页 > 综合 > 正文

基于ARM9开发板的按键字符设备驱动实现

2013年10月06日 ⁄ 综合 ⁄ 共 3871字 ⁄ 字号 评论关闭

基于ARM9开发板的按键字符设备驱动实现(1)

李亚锋
2009-06-16 16:53

基于ARM9
开发板的按键

字符
设备驱动实现

摘要:

该驱动程序实现4
个按键设备在
Linux
系统中基于

QT2410E开发板

的工作情况,通过该实例可以了解ARM
平台
Linux
系统下的
GPIO
程序控制,以及硬件中断程序的工作机制。另外还可以熟悉
Linux 2.6
内核的模块加载和测试方法。

1.了解硬件原理图

由于该设备驱动是针对具体硬件设备的,所以一般需要了解它的硬件原理图(如图1
),该模块有四个按键分别是
S2

S3

S4

S5
,其中这四个按键分别对应的外部中断为:
EINT0

EINT2

KBDINT

EINT1
)和
KBDSPIMISO

EINT13
)。工作原理很简单,当系统正常工作时,按这四个任意键会产生相应的中断信号,从而系统会知道哪个按键被触发,在该驱动实现中当有哪个按键被触发时会有相应的打印信息产生。



按键模块的电路图

2.设计驱动程序框架

字符设备指那些必须以串行顺序依次访问的设备
,并且不需要
缓冲
,通常用于不需要大量数据请求传送的设备类型,所以对于该按键设备适合用字符设备类型来实现。对于Linux
设备驱动,通常根据设备类型的不同所以实现框架会有所不同,比如字符设备、块设备和网络设备分别都有自己的实现框架和相应的内核
API
函数。通常字符设备驱动实现的内容有:模块的加载(字符设备的注册,硬件的初始化,中断的注册等);字符设备的操作函数实现(
open

close

read

write

ioctl
等);中断程序的实现;模块的卸载(注销字符设备,注销其他申请的资源)。下面来分析一下该按键设备驱动的具体实现。


3.
按键模块的加载

     1  static int __init buttons_init(void)

     2  {

     3          int ret,devno;

     4          dev_t dev;

     5          ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME); //在系统中申请一个字符设备区域,主设备号由系统动态分配。

     6          buttons_major_number = MAJOR(dev); //摘取出主设备号

     7          printk(KERN_INFO "Initial QT2410E Board Buttons driver!/n");

     8          if (ret<0) {

     9                  printk(KERN_WARNING "button:can't get major number %d/n",buttons_major_number);

    10                  return ret;

    11          }

    12          ret = request_irqs(); //注册中断,下文会具体分析该函数的实现

    13          if (ret) { //如果注册中断失败,则注销上面申请的字符设备区域。

    14                  unregister_chrdev_region(dev,1);

    15                  printk(KERN_WARNING "button:can't request irqs/n");

    16                  return ret;

    17          }

    18          devno = MKDEV(buttons_major_number,0);

    19          cdev_init(&buttons_dev,&buttons_fops); //初始化
buttons_dev
字符设备结构

    20          buttons_dev.owner = THIS_MODULE;

    21          

    22

    23          ret = cdev_add(&buttons_dev,devno,1);// 将字符设备加入到内核中

    24          if (ret) { //如果添加失败,则做上述注册的释放操作。

    25                  free_irqs();

    26                  unregister_chrdev_region(dev,1);

    27                  printk(KERN_NOTICE "Error %d adding buttons device/n",ret);

    28                  return ret;

    29          }

    30

    31  #ifdef CONFIG_DEVFS_FS //如果定义
devfs
,系统会自动创建
/dev
目录下的字符设备节点,比如这里会自动创建
/dev/buttons
字符设备节点

    32          devfs_mk_cdev(MKDEV(buttons_major_number,0), S_IFCHR | S_IRUSR | S_IWUSR,DEVICE_NAME);

    33          printk(KERN_INFO"/dev/%s has been added to your system./n",DEVICE_NAME);

    34  #else //否则需要执行 
mknod
命令手动创建字符设备节点。

    35          printk(DEVICE_NAME "Initialized/n");

    36          printk(KERN_INFO "You must create the dev file manually./n");

    37          printk(KERN_INFO "Todo: mknod c /dev/%s %d 0/n",DEVICE_NAME,buttons_major_number);

    38  #endif

    39          return 0;

    40  }

分析上述代码,
buttons_init
是该按键模块的驱动入口函数,也是该内核模块的加载函数,主要用于注册资源、申请资源和初始化设备等工作。第5
行,

alloc_chrdev_region
()是内核提供的申请字符设备号函数,它会动态的为设备申请一个主设备号,并且根据输入参数申请多个次设备。第19
行,

cdev_init
()是用于初始化一个
cdev
结构,这里初始化的是buttons_dev
全局变量。第
32
行,

devfs_mk_cdev
()函数会在/dev
目录下自动创建一个设备节点,早期的内核版本是需要手动通过
mknod
工具创建设备节点。

4.字符设备的操作函数

由于该设备功能单一,所以这里只实现了read
操作,关于
read
操作的定义如下:


static struct file_operations buttons_fops = {

.owner

=

THIS_MODULE,

.read

=

buttons_read,

};

其中read
操作是被定义在

file_operations
的对象中,由
buttons_read
函数具体实现。

     1  static ssize_t buttons_read(struct file *filp,char __user *buffer,size_t count,loff_t *ppos)

     2  {       

     3          static int key;

     4          unsigned long  flags;

     5          if (!ready) { 

     6                  return -EAGAIN;

     7          }

     8          if (count != sizeof key_value)

     9                  return -EINVAL;

    10          local_irq_save(flags);

    11          key = key_value;

    12

    13          local_irq_restore(flags);

    14          copy_to_user(buffer, &key, sizeof key);

    15          ready = 0;

    16

    17          return sizeof key_value;

    18  }

以上代码中最重要的实现是由
copy_to_user
()函数实现的,它是内核提供的用于将内核空间数据拷贝到用户空间中去API
,它是内核空间与用户空间通信的重要实现函数。

5. 中断实现函数

     1  static int request_irqs(void)

     2  {

抱歉!评论已关闭.