原创 ARM+LINUX字符型设备驱动编写框架

2009-4-6 12:43 4189 8 8 分类: MCU/ 嵌入式
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

最近在ARM+LINUX嵌入式环境下学习字符型设备的驱动程序的编写,现整理如下:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


字符型设备的编写其实还是有规律可循的,我自己总结了一个框图。


 驱动程序框架:


点击看大图


驱动程序里最重要的就是


module_init(XXX_init);module_exit(XXX_exit);


第一句话是告诉内核编写的这段程序是驱动程序,也就是将驱动程序以模块的形式注册进内核。后一句就是用于将驱动程序卸载掉。


 


接着就是编写static int __init XXX_init(void)函数,其中最重要的就是注册字符型设备,给它分配一个主设备号ret=register_chrdev(XXX_MAJOR,DEVICE_NAME,&XXX_fops);这个函数的第一个参数是注册的主设备号,可以自己指定,我这里就是采用外部宏定义的方式,也可以让系统自动分配,将该参数写0即可。分配的主设备号范围是0~254.第二个参数是自己指定的设备名,也就是将驱动加入内核后添加在/dev下的设备名,这个名字很重要,应用程序在调用open()函数时也要用到这个设备名。第三个就是一个结构体指针,我们的任务就是要丰满它。


static struct file operations XXX_fops =


 {


       .ioctl       =     XXX_ioctl,


};


实际上file operations这个结构体的内容非常多如下,但是使用时并不用全部使用。


       struct file_operations


 {   struct module *owner;


       loff_t (*llseek) (struct file *, loff_t, int);


        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);


        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);


        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);


        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);


       int (*readdir) (struct file *, void *, filldir_t);


       unsigned int (*poll) (struct file *, struct poll_table_struct *);


       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);


       long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);


       long (*compat_ioctl) (struct file *, unsigned int, unsigned long);


       int (*mmap) (struct file *, struct vm_area_struct *);


       int (*open) (struct inode *, struct file *);


       int (*flush) (struct file *, fl_owner_t id);


       int (*release) (struct inode *, struct file *);


       int (*fsync) (struct file *, struct dentry *, int datasync);


     int (*aio_fsync) (struct kiocb *, int datasync);


       int (*fasync) (int, struct file *, int); ...


};


可以看到里面主要的一项就是.ioctl   =     XXX_ioctl,我们对硬件的一些操作就是在XXX_ioctl这个函数里面来实现的。


 


这些就是一个字符型设备驱动的主要内容,把以上这些函数的丰满一下,就可以组成一个完整的驱动程序。下面举例说明。


 


这是一个蜂鸣器的驱动。开发板是友善之臂的mini2440


下图是蜂鸣器部分的局部电路图,可以看到只要让GPB0口输出1就可以让蜂鸣器想起来。


b8010923-a682-4629-98bf-6d6c6cd86586.JPG


蜂鸣器的驱动程序:


 


#include <linux/config.h>


#include <linux/module.h>


#include <linux/kernel.h>


#include <linux/fs.h>


#include <linux/init.h>


#include <linux/devfs_fs_kernel.h>


#include <linux/miscdevice.h>


#include <linux/delay.h>


#include <asm/irq.h>


#include <asm/arch/regs-gpio.h>


#include <asm/hardware.h>


 


#define DEVICE_NAME     "beep"  //设备名beep


#define BEEP_MAJOR 253      //主设备号253


 


 


 


static int qq2440_beep_ioctl(


       struct inode *inode,


       struct file *file,


       unsigned int cmd,


       unsigned long arg)


{


      


             


              s<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />3c2410_gpio_setpin(S3C2410_GPB0, cmd);


      


      


}


 


static struct file_operations qq2440_beep_fops = {


       .owner    =     THIS_MODULE,


       .ioctl       =     qq2440_beep_ioctl,


};


 


static int __init qq2440_beep_module_init(void)


{


       int ret;


       int i;


 


       ret = register_chrdev(BEEP_MAJOR, DEVICE_NAME, &qq2440_beep_fops);


       if (ret < 0) {


         printk(DEVICE_NAME " can't register major number\n");


         return ret;


       }


 


       devfs_mk_cdev(MKDEV(BEEP_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);


      


       s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);


       s3c2410_gpio_setpin(S3C2410_GPB0, 0);


 


       printk(DEVICE_NAME " initialized\n");


       return 0;


}


 


static void __exit qq2440_beep_module_exit(void)


{


       devfs_remove(DEVICE_NAME);


       unregister_chrdev(BEEP_MAJOR, DEVICE_NAME);


       printk(DEVICE_NAME " removed\n");


}


 


module_init(qq2440_beep_module_init);


module_exit(qq2440_beep_module_exit);


 


可以看到这里只是将我之前框图的函数丰满了一下。


1. module_init(qq2440_beep_module_init);


module_exit(qq2440_beep_module_exit);


2. static int __init qq2440_beep_module_init(void)


注册设备,初始化硬件。


3. static void __exit qq2440_beep_module_exit(void)


卸载设备的函数。


4static struct file_operations qq2440_beep_fops =


编写file operations结构体。


5. tatic int qq2440_beep_ioctl


丰满ioctl函数。


注意一点,我的设备名是beep


 


接着我们就可以把该驱动程序放在kernel-2.6.23/drivers/char的目录下。并打开Kconfig文件,添加


Config QQ2440_BEEP_MODULE  //驱动程序文件名


              tristate”QQ2440_BEEP_MODULE”


              depends on ARCH_S3C2410


              help


                     qq2440_beep_module test


这样在内核根目录下make menuconfig后就可以把QQ2440_BEEP_MODULE显示在菜单上。把它前面的状态改为【M】,我们以模块的形式加载。


 


接着我们还需要修改Makefile文件。在里面添加


obj-$(CONFIG_QQ2440_BEEP_MODULE)+=qq2440_beep_module.o


然后我们在内核根目录下make modules时,就可以生成我们所需的qq2440_beep_module.ko文件了。把它放到开发板上,通过insmod加载,rmmod卸载。


 


下面是我的测试程序:


#include <stdio.h>


#include <stdlib.h>


#include <unistd.h>


#include <sys/ioctl.h>


 


int main(int argc, char **argv)


{


      


       int fd;


      


            fd = open("/dev/beep", 0); //可以看到,open函数里调用了设备名,这也是驱动程序和


                                                      //应用程序的连接点


       if (fd < 0) {


              perror("cannt open device beep");


              exit(1);


       }


       ioctl(fd,1,0);   //这里就是我们编写的ioctl函数的具体实现


       close(fd);


       return 0;


}


 


 


 


另外,再附上LED的驱动程序和测试程序,用于比较。


驱动程序:


#include <linux/config.h>


#include <linux/module.h>


#include <linux/kernel.h>


#include <linux/fs.h>


#include <linux/init.h>


#include <linux/devfs_fs_kernel.h>


#include <linux/miscdevice.h>


#include <linux/delay.h>


#include <asm/irq.h>


#include <asm/arch/regs-gpio.h>


#include <asm/hardware.h>


 


#define DEVICE_NAME     "leds"


#define LED_MAJOR 231


 


static unsigned long led_table [] = {


       S3C2410_GPB5,


       S3C2410_GPB6,


       S3C2410_GPB7,


       S3C2410_GPB8,


};


 


static unsigned int led_cfg_table [] = {


       S3C2410_GPB5_OUTP,


       S3C2410_GPB6_OUTP,


       S3C2410_GPB7_OUTP,


       S3C2410_GPB8_OUTP,


};


 


static int qq2440_leds_ioctl(


       struct inode *inode,


       struct file *file,


       unsigned int cmd,


       unsigned long arg)


{


       switch(cmd) {


       case 0:


       case 1:


              if (arg > 4) {


                     return -EINVAL;


              }


              s3c2410_gpio_setpin(led_table[arg], !cmd);


              return 0;


       default:


              return -EINVAL;


       }


}


 


static struct file_operations qq2440_leds_fops = {


       .owner    =     THIS_MODULE,


       .ioctl       =     qq2440_leds_ioctl,


};


 


static int __init qq2440_leds_init(void)


{


       int ret;


       int i;


 


       ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &qq2440_leds_fops);


       if (ret < 0) {


         printk(DEVICE_NAME " can't register major number\n");


         return ret;


       }


 


       devfs_mk_cdev(MKDEV(LED_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);


      


       for (i = 0; i < 4; i++) {


              s3c2410_gpio_cfgpin(led_table, led_cfg_table);


              s3c2410_gpio_setpin(led_table, 1);


       }


 


       printk(DEVICE_NAME " initialized\n");


       return 0;


}


 


static void __exit qq2440_leds_exit(void)


{


       devfs_remove(DEVICE_NAME);


       unregister_chrdev(LED_MAJOR, DEVICE_NAME);


}


 


module_init(qq2440_leds_init);


module_exit(qq2440_leds_exit);


 


测试程序:


#include <stdio.h>


#include <stdlib.h>


#include <unistd.h>


#include <sys/ioctl.h>


 


int main(int argc, char **argv)


{


       int on;


       int led_no;


       int fd;


       if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||


           on < 0 || on > 1 || led_no < 0 || led_no > 3) {


              fprintf(stderr, "Usage: leds led_no 0|1\n");


              exit(1);


       }


       fd = open("/dev/leds0", 0);


       if (fd < 0) {


              perror("open device leds");


              exit(1);


       }


       ioctl(fd, on, led_no);


       close(fd);


       return 0;


}


 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
8
关闭 站长推荐上一条 /3 下一条