嵌入式学习-驱动开发-lesson2-LED字符设备驱动

来源:互联网 发布:ubuntu 14.04 apt 源 编辑:程序博客网 时间:2024/04/29 13:33

一、设备控制

1)设备控制应用函数

在用户空间,主要是使用ioctl系统调用来控制设备。
int ioctl(int fd,unsigned long cmd,…)
fd: 要控制的设备文件描述符
cmd: 发送给设备的控制命令
…: 第3个参数是可选的参数,存在与否是依赖于控制命令(第 2 个参数 )。

2)设备控制驱动函数

应用程序使用ioctl系统调用时,驱动程序会响应ioctl系统调用,主要通过下面的函数:

2.6.36 之前的内核

long (*ioctl) (struct inode* node ,struct file* filp, unsigned int cmd,unsigned long arg)

2.6.36之后的内核

long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)

参数cmd:便是通过ioctl系统调用传递下来的命令

3)设备控制实现

ioctl系统调用的cmd命令从其实质而言就是一个整数, 但为了让这个整数具备更好的可读性,我们通常会把这个整数分为几个段:类型(8位),序号,参数传送方向,参数长度。
Type(类型/幻数): 表明这是属于哪个设备的命令,在使用的时候可以详细的了解一下,防止和内核冲突,下文有介绍。
Number(序号),用来区分同一设备的不同命令
Direction:参数传送的方向,可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE(向设备写入参数)
Size: 参数长度

Linux系统提供了下面的宏来帮助定义命令:

_IO(type,nr):不带参数的命令_IOR(type,nr,datatype):从设备中读参数的命令_IOW(type,nr,datatype):向设备写入参数的命令

例:

#define MEM_MAGIC ‘m’ //定义幻数  8位#define MEM_SET _IOW(MEM_MAGIC, 0, int)

MEM_MAGIC 命令的类型
0 命令的序号 第一个命令
int 传输的参数的类型

关于ioctl系统调用和幻数更详细的情况,请参考:
1.ioctl的实现
2.构造IOCTL命令的学习心得—–_IO, _IOR, _IOW, _IOWR 幻数的理解

二、led字符驱动编写

LED字符驱动的编写完全适用于上一课中中的字符驱动模型,因此按照上一课的模型进行代码的编写。

1)分配cdev结构

//静态分配cdev结构struct cdev mdev;

2)初始化cdev结构

    //初始化cdev    cdev_init(&mdev,&led_ops);

led_ops为我们定义的操作函数集

3)操作函数集的定义与使用

在本次中设计中主要在操作函数集中实现了两个函数,一个函数对led进行初始化操作,另外一个函数则对ioctl系统调用进行响应。

1.操作函数集原型

//操作函数集const struct file_operations led_ops ={    .open = led_open,    .unlocked_ioctl = led_ctl,};

2.led_open

这个函数的调用是在我们打开设备文件的时候,经过一些的内核之间的XX,而调用的。
这个函数其实就是初始化led,对LED的寄存器进行配置,但是在配置的时候,要注意物理地址与虚拟地址的转换。

//led的物理地址#define GPKCON  0x7F008820#define GPKDATA 0x7F008824//转化后的虚拟地址unsigned int *ledcon;unsigned int *leddata;//openstatic int led_open(struct inode *inode, struct file *file){    //LED控制寄存器    ledcon = ioremap(GPKCON,4);    writel(0x1111,ledcon);    //LED数据寄存器    leddata = ioremap(GPKDATA,4);    return 0;   }

3.led_ctl

本函数主要用来对ioctl系统调用进行响应。

如下,所示:

//ioctllong led_ctl (struct file *file, unsigned int cmd, unsigned long arg){    switch(cmd)    {        case(LED_OFF):            writel(0xff,leddata);            return 0;        case(LED_ON):            writel(0x00,leddata);            return 0;           default:        return -EINVAL;     }}

4)注册cdev结构

在注册前,需要分配一个主设备号

//设备号dev_t devno;alloc_chrdev_region(&devno,0,1,"leddev"); //动态分配主设备号

注册

cdev_add(&mdev,devno,1);

5)销毁

cdev_del(&mdev);

贴上代码:

/**********************************************File name :led.c*Author    :stone*Date      :2016/07/27*Function  :led驱动代码*********************************************/#include <linux/init.h>#include <linux/module.h>#include <linux/cdev.h>#include <linux/fs.h>#include<linux/io.h>#include <mach/gpio-bank-k.h>#include <linux/device.h>#include"led.h"//静态分配cdev结构struct cdev mdev;//设备号dev_t devno;//led的物理地址#define GPKCON  0x7F008820#define GPKDATA 0x7F008824//转化后的虚拟地址unsigned int *ledcon;unsigned int *leddata;//自动创建设备文件static struct class *my_class;//openstatic int led_open(struct inode *inode, struct file *file){    //LED控制寄存器    ledcon = ioremap(GPKCON,4);    writel(0x1111,ledcon);    //LED数据寄存器    leddata = ioremap(GPKDATA,4);    return 0;   }//ioctllong led_ctl (struct file *file, unsigned int cmd, unsigned long arg){    switch(cmd)    {        case(LED_OFF):            writel(0xff,leddata);            return 0;        case(LED_ON):            writel(0x00,leddata);            return 0;           default:        return -EINVAL;     }}//操作函数集const struct file_operations led_ops ={    .open = led_open,    .unlocked_ioctl = led_ctl,};//入口函数int led_init(){    //初始化cdev    cdev_init(&mdev,&led_ops);    //注册cdev        alloc_chrdev_region(&devno,0,1,"leddev"); //动态分配主设备号    cdev_add(&mdev,devno,1);    //自动创建设备文件    my_class = class_create(THIS_MODULE, "my_class1");      device_create(my_class, NULL, devno, NULL, "myled" "%d", MINOR(devno));      return 0;}//退出函数void led_exit(){    cdev_del(&mdev);    //注销设备号    unregister_chrdev_region(devno,1);    device_destroy(my_class, devno);     class_destroy(my_class);}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");

三、应用程序的编写

驱动程序编写之后,需要使用应用程序对其测试,

/**********************************************File name :led_app.c*Author    :stone*Date      :2016/07/27*Function  :对LED驱动尽心测试*********************************************/#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>#include <sys/ioctl.h>#include "led.h"int main(int argc,char **argv){    int fd,num;    fd = open("/dev/myled0",O_RDWR);    num = atoi(argv[1]);    if(num == 0)    {       printf("led off\n");       ioctl(fd,LED_OFF);       }      if(num == 1)    {       printf("led on\n");       ioctl(fd,LED_ON);    }         return 0;}
/**********************************************File name :led.c*Author    :stone*Date      :2016/07/27*Function  :ioctl幻数的定义*********************************************/#define LED_MAGIC 'L'#define LED_ON  _IO(LED_MAGIC,0)#define LED_OFF _IO(LED_MAGIC,1)

菜鸟一枚,如有错误,多多指教。。。

0 0
原创粉丝点击