Linux2.6.32驱动笔记(4)ioctl方法解析及mini2440-led驱动实现

摘要: 介绍了字符设备驱动的控制方式——ioctl,同时利用该方式在mini2440上实现led驱动。


驱动中,除了read,write,open,close之外,还有很多的访问方式,其中对于设备的控制,ioctl是一种重要的方式。


一、ioctl函数

    int ioctl(int fd,unsigned long cmd,…)

    fd:要控制的设备文件描述符

    cmd:发送给设备的控制命令

    …:可选参数,依赖于第二个cmd参数

    当应用程序使用ioctl的时候,驱动程序将由如下驱动函数来响应:

    2.6.36内核以前:

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

    2.6.36内核以后:

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


二、控制实现步骤

1.定义命令

命令其实是一个整数,但是为了可读性更好,分为四个段,分别是:类型(幻数)8位,序号8位,传送方向2位,参数大小14位。

type(类型/幻数):表明这是属于哪个设备的命令;

number(序号):用来区分同一个设备的不同命令;

direction:参数传递方向,可能的值是没有传输方向,读或者写;

size:参数长度。


命令宏,Linux提供了一组宏来帮助定义命令:

_IO(type,nr):不带参数的命令;

_IOR(type,nr,datatype):从设备中读参数的命令;

_IOW(type,nr,datatype):向设备写入参数的命令;

_IOWR(type,nr,datatype):双向传输。

其中命令的type 和size是通过datatype中取对应位得来。


2.实现操作

    这个模式就是switch case,我们给进去定义的命令,然后让switch去匹配,匹配到哪个就执行相应的操作,都没有匹配到就返回-EINVAL。

    switchcmd

    case命令A:

       //执行A对应的操作

    case命令B:

       //执行B对应的操作

    default:

       //return–EINVAL


三、LED字符驱动实现

    首先,mini2440上和四个led相连接的gpio口分别是PORTB的5,6,7,8四个端口。那么就需要定义寄存器的基地址,并且使用ioremap把它映射到用户空间给我们使用。

led.c如下

#include 
#include 
#include 
#include 
#include 
//#include 
#include "led.h"//step 1一些列需要的头文件
 
#define LEDCON 0x56000010
#define LEDDAT 0x56000014//GPB5,6,7,8,控制寄存器基地址,和数据寄存器基地址,一个用来设置端口模式,一个用来给数据
 
unsigned int *led_config;
unsigned int *led_data;//ioremap之后得到的虚拟地址赋给他们
 
struct cdev cdev;//定义设备的结构体
dev_t devno;//定义设备号
 
int led_open(struct inode *inode,structfile *filep)//setp 6
{
    led_config= ioremap(LEDCON,4);//把LEDCON控制寄存器物理地址映射到用户空间,映射4个字节
    writel(0x155555,led_config);//01对应输出模式,这里把所有i/o口配置成输出模式
   
    led_data= ioremap(LEDDAT,4);//数据寄存器的映射
    return0;
    }
   
long led_ioctl(struct file *filep,unsignedint cmd,unsigned long arg) //step 7
{
    switch(cmd)
    {
       caseLED_ON:
           writel(0x1ff,led_data);//具体命令实现处,给LED点灯
           return0;
      
       caseLED_OFF:
           writel(0x000,led_data);//关灯
           return0;
      
       default:
           return-EINVAL;
       }
    }
 
 
static struct file_operations led_fops=   //step 5 对应的对led的操作实现都在这里,一个打开,一个ioctl,所以下面我们就要去写实现函数
{
    .open= led_open,
    .unlocked_ioctl= led_ioctl,
    };
 
static int led_init(void)//step 3
{
    cdev_init(&cdev,&led_fops);//字符设备初始化函数,将cdev和f_ops绑定起来
    alloc_chrdev_region(&devno,0,1,"myled");//次设备号为0,一个设备,设备名称为myled,把动态分配得到的设备号给devno
    cdev_add(&cdev,devno,1);//注册设备,devno为主设备号,设备数为1
    return0;
    }
   
static void led_exit(void)//step 4
{
    cdev_del(&cdev);//删除驱动cdev
    unregister_chrdev_region(devno,1);//释放设备号
    }
 
module_init(led_init);//step 2
module_exit(led_exit);
 

led_app.c如下:

#include 
#include 
#include 
#include 
#include "led.h" //open需要的一系列的头文件以及led.h
 
int main(int argc,char *argv[])//主函数,argc保存主函数的参数个数,后面存放具体的参数
{
    intfd;//文件句柄
    intcmd;//定义cmd
    if(argc<2)
       {
           printf("please enter the secondpara!\n");
           return0;
           }//这里判断小于2是因为我们输入的时候要输入进来是0还是1,使用方法是./ledapp 0或者./ledapp 1,需要第二个参数
    cmd= atoi(argv[1]);//atoi用来把字符串转换成整形数,因为我们的cmd其实都是一个整形数
    fd= open("/dev/myled",O_RDWR);//打开文件
   
    if(cmd==1)
       ioctl(fd,LED_ON);
    else
       ioctl(fd,LED_OFF);
       //具体ioctl操作,开关灯
    return0;
    }
 

led.h如下:

#define LED_MAGIC 'L'
#define LED_ON _IO(LED_MAGIC,0)
#define LED_OFF _IO(LED_MAGIC,1)

Makefile如下:

obj-m := led.o
 
KDIR :=/home/passionbird/project/test/linux-2.6.32.2
 
all:
    make-C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
 
 
clean:
    rm-f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order


虚拟机下按照以下步骤:

#make

#chmod 777 led.ko

#cp led.ko /opt/rootfs

#arm-linux-gcc –static ledapp.c –oledapp

#chmod 777 ledapp

#cp ledapp /opt/rootfs


开发板上电:

#boot

#insmod led.ko

#cat proc/devices

#mknod /dev/myled c 251 0

#./ledapp 1

#./ledapp 0

#rmmod led


这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!

 

你可能感兴趣的:(BSP_Driver,ARM驱动开发)