由于没有编写过设备驱动,所以先看看别人是再怎么写驱动的,这是我在网上找的一个开发板上led灯的驱动程序,先来看一下这个程序。
#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 <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
/*
这些头文件有很多事不需要的,原来的作者估计是图省事直接全部包含了,但是我觉得有几个还是要说明一下的。
#include <mach/regs-gpio.h>
#include <mach/***.h> 是在linux-2.6.29/arch/arm/mach-s3c2410/include/mach下面寻找源文件。
这个头文件是对arm处理器管脚的定义,这里开发板的芯片是s3c2410,所以包含的是mach-s3c2410的资源头文件,这样我们可以直接对处理器的引脚给出高低电平,然后根据开发板芯片电路图来找到对应硬件是接在哪些引脚上的,这样我们就可以控制这些硬件的动作。
#include <asm/irq.h>
使用中断必须有的头文件,定义了系统的中断
<linux/fs.h>:文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)。
*/
static unsigned long led_table[]=
{S3C2410_GPB(5), //S3C2410_GPB为s3c2410中gpio的宏,表示端口b的第5个引脚
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
dev_t devno;//1.0 分配设备号变量
/*这个函数是用来控制led的*/
static int mini2440_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg)
{if(arg<4) //因为开发板上只有四个LED(0.1.2.3)所以限制一下操作LED的数量
{
switch(cmd)
{
case 0:
s3c2410_gpio_setpin(led_table[arg],!cmd); //s3c2410_gpio_setpin用来将相应的引脚输出为0或1
return 0;
case 1:
s3c2410_gpio_setpin(led_table[arg],!cmd); //不知道原作者为什么又写一遍
return 0;
default:
return -EINVAL; //失败返回固定的返回值,这个返回值是系统定义的
}
}
else
{
printk("<0>""the led number is faile!\n");
return -EINVAL;
}
}
.owner = THIS_MODULE,
.ioctl = mini2440_ioctl, //这里对led灯没有读写操作,只有控制操作
};
static int __init led_init(void){
int i;
printk("<0>""the led function startup!\n");
for(i=0;i<4;i++) //配置相应的LED脚为输出
{
s3c2410_gpio_cfgpin(led_table[i],S3C2410_GPIO_OUTPUT);
}
for(i=0;i<4;i++) //配置相应的LED输出高电平全部熄灭
{
s3c2410_gpio_setpin(led_table[i],1);
}
alloc_chrdev_region(&devno,0,1,"luciensong-led");//1.1 动态申请设备号
cdev_init(&dev,&fops); //2.1 初始化dev,并建立dev与fops间的连接dev.owner=THIS_MODULE; //2.1 指定dev模块所属
cdev_add(&dev,devno,1); //2.2 添加dev
return (0);
}
static void __exit led_exit(void)
{
int i;
printk("<0>""the led function end!\n");
for(i=0;i<4;i++) //
{
s3c2410_gpio_setpin(led_table[i],1);
}
cdev_del(&dev);//3.0注销设备dev
unregister_chrdev_region(devno,1);//3.1注销设备号
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lucien Song");
MODULE_VERSION("V0.1");
然后生成ko文件后insmod,这时候已经被装载到了内核中。
但是要想让上层应用程序访问它,还需要为驱动创建设备节点。
手动创建用mknod命令来创建:
mknod /dev/CDEV_ZHU c 254 0 //参数分别为文件名,类型,设备号,具体可以查看这篇文章:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076160.html
动态创建卸载可以用下面的方法:
cdev_class = class_create(owner,name) // cdev_class 为 struct class 类型
然后使用:
device_create(_cls,_parent,_devt,_device,_fmt)
当动态创建了设备节点之后,在卸载的时候需要使用:
device_destroy(_cls,_device) 和 class_destroy(struct class * cls)
来销毁设备和类。
这个led驱动用的是手动创建设备节点。mknod /dev/luciensong-led c 253 0
然后直接弄个程序调用驱动
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/ioctl.h>
int main(int argc,char *argv[])
{
int statue;
int fd;
sscanf(argv[2],"%d",&statue);
if(fd<0)
{
perror("open dev/luciensong-led faile!\n");
return (-1);
}
printf("statue=%d,num=%d\n",statue,num);
close(fd);
return (0);
看了一遍之后,设备节点到底是个什么东西?
Linux 中的设备有2种类型:字符设备(无缓冲且只能顺序存取)、块设备(有缓冲且可以随机存取)。每个字符设备和块设备都必须有主、次设备号,主设备号相同的设备是同类设备(使用同一个驱动程序)。这些设备中,有些设备是对实际存在的物理硬件的抽象,而有些设备则是内核自身提供的功能(不依赖于特定的物理硬件,又称为"虚拟设备")。每个设备在 /dev 目录下都有一个对应的文件(节点)。可以通过 cat /proc/devices 命令查看当前已经加载的设备驱动程序的主设备号。内核能够识别的所有设备都记录在原码树下的 Documentation/devices.txt 文件中。在 /dev 目录下除了字符设备和块设备节点之外还通常还会存在:FIFO管道、Socket、软/硬连接、目录。这些东西没有主/次设备号
设备节点好像就是./dev下面的文件,也就是对设备的虚拟。