原创 如何编写linux的驱动程序

2011-1-5 15:43 2973 11 12 分类: MCU/ 嵌入式

如何编写Linux的驱动程序


编写linux驱动程序,应该是一件得心应手的事,因为linux是开源的,从上往下或从下往上,一切都是那么的光明磊落的呈现于眼前。只要你愿意,你可随意了解你所想知道的东西。Linux是强大的,linux的前途是光明的。。。


 


本次从编写一个简单的char(led)型设备,来解开驱动的神秘外衣。。。


环境:


内核版本:linux-kernel 2.6.30.4


操作系统:fedora 10 (小呢帽)


编译环境:gcc-version 4.3.2 20081105(Red Hat 4.3.2-7(GCC)


 


驱动是直接和硬件打交道的,一般处于操作系统的核心态,编写LED的驱动必须能够直接操作硬件的IO资源,同时提供API函数给更高一级的用户使用,以达到操作硬件资源的目的。这也就是驱动的作用吧。


OK,驱动编写,步入正题。通过源码分析,以便更好的了解什么才是驱动。


#include <linux/miscdevice.h>


#include <linux/delay.h>


#include <asm/irq.h>


#include <linux/fs.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/moduleparam.h>


#include <linux/slab.h>


#include <linux/errno.h>


#include <linux/ioctl.h>


#include <linux/cdev.h>


#include <linux/list.h>


#include <linux/string.h>


#include <asm/uaccess.h>


#include <asm/atomic.h>


#include <asm/unistd.h>


 


#define DEVICE_NAME "MY_LED_DRIVER"


//定义IO地址(也就是IO寄存器对应相应的PORT端口),放于一个数组中


static unsigned long gpio_table[]=


{


    S3C2410_GPB5,


    S3C2410_GPB6,


    S3C2410_GPB7,


    S3C2410_GPB8,


};


//IO功能选项,硬件上拉输出


static unsigned int gpio_cfg_table[] =


{


 


   S3C2410_GPB5_OUTP,


   S3C2410_GPB6_OUTP,


   S3C2410_GPB7_OUTP,


   S3C2410_GPB8_OUTP,


};


//编写一个ioctl函数,这个函数提供给用户端使用(也就是用户态使用)


static int my_ioctl(struct inode *inode,struct file* file,unsigned int cmd,


          unsigned long arg)


{


      


       if (arg > 4)


       {


          return -EINVAL;


       }


       if (cmd == 1) //led ON


       {


           s3c2410_gpio_setpin(gpio_table[arg],0);


           return 0;


       }


       if (cmd == 0) //led OFF


       {


          s3c2410_gpio_setpin(gpio_table[arg],1);


          return 0;


       }


       else


       {


           return -EINVAL;


       }


 


}


//一个和文件设备相关的结构体。


static struct file_operations dev_fops =


{


       .owner = THIS_MODULE,


       .ioctl = my_ioctl,


       //.read  = my_read,   //这个暂时屏蔽,一会我们再加入一个读操作的函数


};


//linux中设备的注册结构体


static struct miscdevice misc =


{


       .minor = MISC_DYNAMIC_MINOR,


       .name  = DEVICE_NAME,


       .fops  = &dev_fops,


};


//设备初始化(包括注册)函数


static int __init dev_init(void)


{


       int ret;


       int i;


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


       {


           s3c2410_gpio_cfgpin(gpio_table,gpio_cfg_table);


           s3c2410_gpio_setpin(gpio_table,0);


           mdelay(500);


           s3c2410_gpio_setpin(gpio_table,1);


       }


       ret = misc_register(&misc);


       printk(DEVICE_NAME"MY_LED_DRIVER init ok\n");


       return ret;


}


//设备注销函数


static void __exit dev_exit(void)


{


       misc_deregister(&misc);


}


//与模块相关的函数


module_init(dev_init);


module_exit(dev_exit);


MODULE_LICENSE("GPL");


MODULE_AUTHOR("blog.ednchina.com/itspy");



MODULE_DESCRIPTION("MY LED DRIVER");


到此,上面就完成了一个简单的驱动(别急,下面我们再会稍微增加点复杂的东西),以上代码的可以简单概括为:像自己写51单片机或者ARM的裸奔程序一样操作IO函数,然后再linux系统中进行相关必须的函数关联和注册。 为什么要关联呢,为什么注册呢? 因为这是必须的,从以下这些结构体就知道了。


stuct 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);



}


file_operations 结构体中包括了很多与设备相关的函数指针,指向了驱动所提供的函数。


struct inode{


       struct hlist_node     i_hash;


       struct list_head       i_list;


       struct list_head       i_sb_list;


       struct list_head       i_dentry;


       unsigned long         i_ino;


       atomic_t         i_count;


       unsigned int           i_nlink;


       uid_t                     i_uid;


       gid_t                     i_gid;


       dev_t                    i_rdev;


       u64                i_version;


       loff_t                    i_size;



}


   inode 原是UNIX 操作系统中的一种数据结构,它包含了与文件系统中各个文件相关的一些重要信息。在 UNIX 中创建文件系统时,同时将会创建大量的 inode 。通常,文件系统磁盘空间中大约百分之一空间分配给了 inode 表。


大略了解以上信息之后,我们只需把我们所要实现的功能和结构体关联起来。上例中已经完成IO写操作的函数,现在我们再添加一个读的函数。基于这种原理,我们想实现各种功能的驱动也就很简单了。


//添加读函数示意, 用户层可以通过 read函数来操作。


static int my_read(struct file* fp, char __user *dat,size_t cnt)


{


      size_t i;


      printk("now read the hardware...\n");


      for(i=0;i<cnt;i++)


          dat = 'A';


      dat = '\0';


      return cnt;


 


}


这样,完成驱动编写。编译之后,本驱动可以通过直接嵌入内核中,也可以以模块的嵌入的形式加载到linux内核中去。


完成了驱动,写个应用程序了验证一下吧:


int main(int argc,char ** argv)


{


 


    int on;


    int led_no;


    int fd;


    char str[10];


    int cnt =0;


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


    if (fd < 0)


    {


       printf("can't open dev\n");


       exit(1);   


    }


    printf("read process\n");


    cnt = read(fd,str,10);


    printf("get data from driver:\n%s\ncount = %d\n",str,cnt);


    printf("read process end \n");


    cnt = 0;


    printf("running...\n");


    while(cnt++<1000)


    {


      ioctl(fd,0,0);  //led off


      ioctl(fd,0,1);


      ioctl(fd,0,2);


      ioctl(fd,0,3);


      sleep(1);


 //printf("sdfdsfdsfdsfds...\n");


      ioctl(fd,1,0);  //led on


      ioctl(fd,1,1);


      ioctl(fd,1,2);


      ioctl(fd,1,3);


      sleep(1);


      printf("%d\b",cnt);


    }


    close(fd);


    return 0;


}


最后看看跑起来的结果:


a61f41e1-0149-451e-8122-e7fedfb060af.jpg

文章评论1条评论)

登录后参与讨论

xucun915_925777961 2011-1-7 13:12

正在学习这个,谢谢分享^_^
相关推荐阅读
用户1369714 2012-04-12 12:34
大家好,我是itspy,关于这个博客,请大家看过来!
大家好,我是itspy,关于这个博客...,很失望,以后不会用了 如果大家有什么问题,请到我的另一个博客去留言吧 我也很希望跟大家做交流,有什么技术问题,itspy会很乐意帮助的,新博客欢...
用户1369714 2011-08-07 14:35
uip 移植在rt-thread上的源码
*/本人在以前开发过程中移植uIP到RT-Thread实时线程系统,有需要用到项目中的朋友可以参考一下。 附件是源码包,在以太网驱动采用DM9000,驱动程序和移植文件uipif.c在源码包下(rt...
用户1369714 2011-01-13 10:32
Linux内核的社会视角--Mr. Process的一生
         Linux内核是一个无比复杂的系统,要想看清大致的脉络也非易事。其实,可以把运行中的Linux想像成一个人类的社会,当中的进程就是社会中的人。人有生老病死,进程有创建、异常、终止。人...
用户1369714 2011-01-08 12:39
RT-Thread Radio 网络播放器--初次零距离接触!
      今天很高兴, 收到了RT-Thread Radio套件,还有ffx和RT-Thread工作室写的新书《RT-Thread 实时操作系统 编程指南》。 如此令人快乐的事,如此高兴,实在是想不...
用户1369714 2010-12-28 10:12
Busybox制作Linux根文件系统
Busybox ——嵌入式Linux中的瑞士军刀利用busybox-1.13.0制作linux根文件系统(yaffs2)源码下载:http://www.busybox.net/downloads/操作...
我要评论
1
11
关闭 站长推荐上一条 /2 下一条