如何编写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;
}
最后看看跑起来的结果:
xucun915_925777961 2011-1-7 13:12