编写一个字符驱动
2015-06-02 14:39
309 查看
由于没有编写过设备驱动,所以先看看别人是再怎么写驱动的,这是我在网上找的一个开发板上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 分配设备号变量
struct cdev dev; //2.0 分配dev结构体
/*这个函数是用来控制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;
}
}
struct file_operations fops={ //2.0 分配file_operations结构体
.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<stdio.h>
#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 num;
int statue;
int fd;
sscanf(argv[1],"%d",&num);
sscanf(argv[2],"%d",&statue);
fd = open("/dev/luciensong-led",O_RDWR);
if(fd<0)
{
perror("open dev/luciensong-led faile!\n");
return (-1);
}
printf("open /dev/luciensong-led success!\n");
/*ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的参数个数如下:int
ioctl(int fd, int cmd, …);其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就能在用户程序中使用ioctl函数控制设备的I/O通道。*/
ioctl(fd,statue,num); //对应驱动里的mini2440_ioctl控制函数。
printf("statue=%d,num=%d\n",statue,num);
close(fd);
return (0);
}
然后再编译运行就可以控制led灯了。
看了一遍之后,设备节点到底是个什么东西?
Linux
中的设备有2种类型:字符设备(无缓冲且只能顺序存取)、块设备(有缓冲且可以随机存取)。每个字符设备和块设备都必须有主、次设备号,主设备号相同的设备是同类设备(使用同一个驱动程序)。这些设备中,有些设备是对实际存在的物理硬件的抽象,而有些设备则是内核自身提供的功能(不依赖于特定的物理硬件,又称为"虚拟设备")。每个设备在 /dev 目录下都有一个对应的文件(节点)。可以通过 cat /proc/devices 命令查看当前已经加载的设备驱动程序的主设备号。内核能够识别的所有设备都记录在原码树下的 Documentation/devices.txt
文件中。在 /dev 目录下除了字符设备和块设备节点之外还通常还会存在:FIFO管道、Socket、软/硬连接、目录。这些东西没有主/次设备号
设备节点好像就是./dev下面的文件,也就是对设备的虚拟。
#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 分配设备号变量
struct cdev dev; //2.0 分配dev结构体
/*这个函数是用来控制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;
}
}
struct file_operations fops={ //2.0 分配file_operations结构体
.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<stdio.h>
#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 num;
int statue;
int fd;
sscanf(argv[1],"%d",&num);
sscanf(argv[2],"%d",&statue);
fd = open("/dev/luciensong-led",O_RDWR);
if(fd<0)
{
perror("open dev/luciensong-led faile!\n");
return (-1);
}
printf("open /dev/luciensong-led success!\n");
/*ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的参数个数如下:int
ioctl(int fd, int cmd, …);其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就能在用户程序中使用ioctl函数控制设备的I/O通道。*/
ioctl(fd,statue,num); //对应驱动里的mini2440_ioctl控制函数。
printf("statue=%d,num=%d\n",statue,num);
close(fd);
return (0);
}
然后再编译运行就可以控制led灯了。
看了一遍之后,设备节点到底是个什么东西?
Linux
中的设备有2种类型:字符设备(无缓冲且只能顺序存取)、块设备(有缓冲且可以随机存取)。每个字符设备和块设备都必须有主、次设备号,主设备号相同的设备是同类设备(使用同一个驱动程序)。这些设备中,有些设备是对实际存在的物理硬件的抽象,而有些设备则是内核自身提供的功能(不依赖于特定的物理硬件,又称为"虚拟设备")。每个设备在 /dev 目录下都有一个对应的文件(节点)。可以通过 cat /proc/devices 命令查看当前已经加载的设备驱动程序的主设备号。内核能够识别的所有设备都记录在原码树下的 Documentation/devices.txt
文件中。在 /dev 目录下除了字符设备和块设备节点之外还通常还会存在:FIFO管道、Socket、软/硬连接、目录。这些东西没有主/次设备号
设备节点好像就是./dev下面的文件,也就是对设备的虚拟。
相关文章推荐
- S3C6410 看门狗驱动程序代码
- ARM11 paltform驱动代码完成,最简单的测试直接在装载设备中运行,实现秒读
- 字符设备驱动程序中重要的三个数据结构file_operations、inode、file
- Linux设备驱动并发控制详解(自旋锁,信号量)
- Platform设备驱动
- 关于linux模块编程
- 基于S3C2440芯片linux系统下的ds18b20设备驱动
- 基于S3C2440芯片linux系统下的pwm波驱动蜂鸣器
- 设备驱动简介
- linux设备驱动中file_operations结…
- 【android】binder机制-servicemanager
- alsa音频架构1
- alsa音频架构4-声卡控制
- Linux驱动 device 的probe函数是怎么被调用的
- 第四篇:两利相权取其重-多个USB设备通用驱动程序的综合比较
- 设备驱动(一)
- 设备驱动(二)
- 设备驱动(三)
- 设备驱动(四)
- 设备驱动(五)