1.设备控制理论
1.1 作用
大部分驱 动程 序除了需要提供 读写设备 的 能 力 外, 还需要具 备 控制设备的 能 力 。 比如: 改变波特率
1.2 应用程序接口
在用 户空间 ,使用ioctl 系统调 用来控制设备, 原型如下:
int ioctl(int fd,unsigned long cmd,...)
fd: 要 要 控制的设备 文 件 描 述符
cmd: 发送给 设备的控制 命令
…: 第3个 个 参数是可选 的 参数 , 存 在 与否是依赖于 控制 命令(第 第 2 个 个数 参数 )。
1.3 设备驱动方法
当 应用程 序 使用ioctl 系统调 用 时 , 驱 动程 序 将 由如下函数 来 响 应:
1: 2.6.36 之前 的内 核
long (*ioctl) (struct inode* node ,struct file* filp, unsigned int cmd,unsigned long arg)
2 :2.6.36 之后 的内 核
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)
参数cmd: 通过 应用 函数ioctl 传递下 来的 命令
2.设备控制实现
2.1 定义命令
命令 从其 实质而言就是一 个 整数, 但为了让这 个 整数具 备 更好 的可读性 ,我们 通常会把这 个 整数分为几 个 段 : 类 型(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’ // 定义 幻 数
#define MEM_SET _IOW(MEM_MAGIC, 0, int)
2.2 实现设备方法
unlocked_ioctl 函数 的 实现通常是 根据 命令 执行 的 一 个switch 语句 。 但是 , 当命令号 不 能 匹配 任何 一 个设备所支持 的 命令时 , 返回-EINVAL.
编 程 模 型 :
Switch cmd
Case 命令A: :
// 执行A对 对 应的 操 作
Case 命令B: :
// 执行B对 对 应的 操 作
Default:
// return -EINVAL
编 程 模 型 :
Switch cmd
Case 命令A: :
// 执行A对 对 应的 操 作
Case 命令B: :
// 执行B对 对 应的 操 作
Default:
// return -EINVAL
3.自己动手写驱动
3.1 memdev.c
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include "memdev.h"
int dev1_registers[5];
int dev2_registers[5];
struct cdev cdev;
dev_t devno;
/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
if (num==0)
filp->private_data = dev1_registers;
else if(num == 1)
filp->private_data = dev2_registers;
else
return -ENODEV; //无效的次设备号
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
/*判断读位置是否有效*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*读数据到用户空间*/
if (copy_to_user(buf, register_addr+p, count))
{
ret = -EFAULT;
}
else
{
*ppos += count;
ret = count;
}
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
/*分析和获取有效的写长度*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*从用户空间写入数据*/
if (copy_from_user(register_addr + p, buf, count))
ret = -EFAULT;
else
{
*ppos += count;
ret = count;
}
return ret;
}
/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{
loff_t newpos;
switch(whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = filp->f_pos + offset;
break;
case SEEK_END:
newpos = 5*sizeof(int)-1 + offset;
break;
default:
return -EINVAL;
}
if ((newpos<0) || (newpos>5*sizeof(int)))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
long mem_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case MEM_RESTART:
printk("restart device \n");
return 0;
case MEM_SET:
printk("arg is %d\n",arg);
return 0;
default:
return -EINVAL;
}
}
/*文件操作结构体*/
static const struct file_operations mem_fops =
{
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
.unlocked_ioctl = mem_ioctl;
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
/*初始化cdev结构*/
cdev_init(&cdev, &mem_fops);
/* 注册字符设备 */
alloc_chrdev_region(&devno, 0, 2, "memdev");
cdev_add(&cdev, devno, 2);
}
/*模块卸载函数*/
static void memdev_exit(void)
{
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(devno, 2); /*释放设备号*/
}
MODULE_LICENSE("GPL");
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include "memdev.h"
int dev1_registers[5];
int dev2_registers[5];
struct cdev cdev;
dev_t devno;
/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
if (num==0)
filp->private_data = dev1_registers;
else if(num == 1)
filp->private_data = dev2_registers;
else
return -ENODEV; //无效的次设备号
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
/*判断读位置是否有效*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*读数据到用户空间*/
if (copy_to_user(buf, register_addr+p, count))
{
ret = -EFAULT;
}
else
{
*ppos += count;
ret = count;
}
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
/*分析和获取有效的写长度*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*从用户空间写入数据*/
if (copy_from_user(register_addr + p, buf, count))
ret = -EFAULT;
else
{
*ppos += count;
ret = count;
}
return ret;
}
/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{
loff_t newpos;
switch(whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = filp->f_pos + offset;
break;
case SEEK_END:
newpos = 5*sizeof(int)-1 + offset;
break;
default:
return -EINVAL;
}
if ((newpos<0) || (newpos>5*sizeof(int)))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
long mem_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case MEM_RESTART:
printk("restart device \n");
return 0;
case MEM_SET:
printk("arg is %d\n",arg);
return 0;
default:
return -EINVAL;
}
}
/*文件操作结构体*/
static const struct file_operations mem_fops =
{
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
.unlocked_ioctl = mem_ioctl;
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
/*初始化cdev结构*/
cdev_init(&cdev, &mem_fops);
/* 注册字符设备 */
alloc_chrdev_region(&devno, 0, 2, "memdev");
cdev_add(&cdev, devno, 2);
}
/*模块卸载函数*/
static void memdev_exit(void)
{
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(devno, 2); /*释放设备号*/
}
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
3.2 memdev.h
#define MEM_MAGIC 'm'
#define MEM_RESTART _IO(MEM_MAGIC,0)
#define MEM_SET _IOW(MEM_MAGIC,1,int)
#define MEM_RESTART _IO(MEM_MAGIC,0)
#define MEM_SET _IOW(MEM_MAGIC,1,int)
3.3 mem_ctl.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.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;
}
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.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;
}
253 memdev
mknod /dev/memdev0 c 253 0