(本节笔记的实验代码,在这里)
一. 字符设备控制理论
1. 概述
大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力,例如设置波特率等。
在用户空间中,使用ioctl系统调用控制设备,原型为:
int ioctl(int fd, unsigned longcmd, ...)
fd:要控制的设备文件描述符。
cmd:发送给设备的控制命令。
...:第三个参数是可选的参数,存在与否依赖于控制命令(cmd)。
当应用程序使用ioctl系统调用时,驱动程序将由如下函数来响应:
在2.6.36之前的内核为:
long (*ioctl)(struct inode*node,struct file *filp,unsigned int cmd,unsigned long arg)
在2.6.36之后的内核为:
long (*unlocked_ioctl)(structfile *filp,unsigned int cmd,unsigned long arg)
/*驱动程序模型学习方法*/
1)先参照应用程序的实现方法,查看函数名和参数
2)查看file_operations设备方法中的对应函数
3)实现该对应函数的设备方法
2. 设备控制方法的实现
2.1 定义命令
命令从是指而言就是一个整数,但为了让这个整数具备更好的可读性,一般会把这个整数分为几个段:类型(8位),序号,参数传送方向,参数长度。
type(类型/幻数):表明这是属于哪个设备的命令。
number(序号):用来区分同一设备的不同命令。
direction:参数传送的方向,可能的值为:_IOC_NONE(没有数据传输),_IOC_READ(从设备读出数据),_IOC_WRITE(向设备写入数据)。
size:参数长度。
Linux系统提供下面的宏来定义命令:
_IO(type,num):不带参数的命令
_IOR(type,num,datatype):从设备中读参数的命令
_IOW(type,num,datatype):向设备写入参数的命令
例如:#define MEM_MAGEC 'm' //定义幻数
#define MEM_SET_IOW(MEM_MAGEIC,0,int)
2.2 实现设备方法
unlocked_ioctl函数的实现通常是根据命令执行的一个switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时,将返回-EINVAL
编程模型为:switch (cmd)
case 命令A:
//执行A对应的操作
case 命令B:
//执行B对应的操作
default:
return -EINVAL
3. 范例代码
3.1 touch memdev.h
『
#define MEM_MAGIC 'm'
#define MEM_RESTART _IO(MEM_MAGIC ,0)
#define MEM_SET _IOW(MEM_MAGIC,1,int)
』
3.2修改memdev.c
1)包含 #include "memdev.h"
在file_operations里面加入:
.unlocked_ioctl = mem_ioctl,
定义mem_ioctl
long mem_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
{
switch (cmd)
case MEM_RESTART:
printk("restartdevice!\n");
return 0;
case MEM_SET:
printk("arg is%d\n",arg);
return 0;
default:
return -EINVAL;
}
3.2 touch mem_ctl.c
『
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include "memdev.h"
int main()
{
int fd;
fd = open("/dev/memdev0",O_RDWR);
ioctl(fd, MEM_SET,115200);
ioctl(fd, MEM_RESTART);
return 0;
}
』
/* 编译时记得加-static选项 */
二. LED设备程序设计步骤
1.函数学习
unsigned int *led_config =ioremap(GPKCON);
writel(0x11110000,led_config);
2.代码实现
touch led.c
『
#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<mach/gpio-bank-k.h>
#include"led.h"
#defineLEDCON 0x7f008800
#defineLEDDAT 0x7f008808
unsignedint *led_config;
unsignedint *led_data;
structcdev cdev;
dev_tdevno;
intled_open(struct inode *node, struct file *filp)
{
led_config = ioremap(LEDCON,4);
writel(0x11110000,led_config);
led_data = ioremap(LEDDAT,4);
return 0;
}
longled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case LED_ON:
writel(0x00,led_data);
return 0;
case LED_OFF:
writel(0xff,led_data);
return 0;
default:
return -EINVAL;
}
}
staticstruct file_operations led_fops =
{
.open = led_open,
.unlocked_ioctl = led_ioctl,
};
staticint led_init()
{
cdev_init(&cdev,&led_fops);
alloc_chrdev_region(&devno, 0 , 1 ,"myled");
cdev_add(&cdev, devno, 1);
return 0;
}
staticvoid led_exit()
{
cdev_del(&cdev);
unregister_chrdev_region(devno,1);
}
module_init(led_init);
module_exit(led_exit); 』
touch led_app.c
『
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include"led.h"
intmain(int argc, char *argv[])
{
int fd;
int cmd;
if (argc <2 )
{
printf("please enter the secondpara!\n");
return 0;
}
cmd = atoi(argv[1]);
fd = open("/dev/myled",O_RDWR);
if (cmd == 1)
ioctl(fd,LED_ON);
else
ioctl(fd,LED_OFF);
return 0;
}
』