您的位置:首页 > 其它

编写一个字符驱动

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下面的文件,也就是对设备的虚拟。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设备驱动