Linux驱动程序开发 - 设备控制接口
来源:互联网 发布:js拉链是ykk 编辑:程序博客网 时间:2024/05/07 23:17
设备驱动程序的一个基本功能就是管理和控制设备,同时为用户应用程序提供管理和控制设备的接口。我们前面的“Hello World”驱动程序已经可以提供读写功能了,在这里我们将扩展我们的驱动以支持设备控制接口,在Linux中这个接口是通过ioctl函数来实现的。
设备控制接口(ioctl 函数)
回想一下我们在字符设备驱动中介绍的struct file_operations 结构,这里我们将介绍一个新的方法:
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
这是驱动程序设备控制接口函数(ioctl函数)的内核原型定义,struct inode * 和struct file* 描述了操作的文件,unsigned int 描述了ioctl命令号,这是一个重要的参数,我们稍后会对它做详细介绍。最后一个参数是unsigned long数据类型,描述了ioctl命令可能带有的参数,它可能是一个整数或指针数据。
- ioctl命令号
- dir:
- type:
- nr:
- size:
_IO(type,nr)
_IOR(type,nr,size)
_IOW(type,nr,size)
_IOWR(type,nr,size)
宏_IO用于无数据传输,宏_IOR用于从设备读数据,宏 _IOW用于向设备写数据,宏_IOWR用于同时有读写数据的IOCTL命令。相对的,Linux内核也提供了相应的宏来从ioctl命令号种解码相应的域值:
_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)
- ioctl返回值
- ioctl参数
unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
unsigned long __must_check copy_from_user(void *to,
const void __user *from, unsigned long n);
copy_from_user和copy_to_user一般用于复杂的或大数据交换,对于简单的数据类型,如int或char,内核提供了简单的宏来实现这个功能:
#define get_user(x,ptr)
#define put_user(x,ptr)
其中,x是内核空间的简单数据类型地址,ptr是用户空间地址指针。
我们需要牢记:在内核中是无法直接访问用户空间地址数据的。因此凡是从用户空间传递过来的指针数据,务必使用内核提供的函数来访问它们。
举例
好了,是时候举个例子了。我们将扩展我们的helloworld驱动添加ioctl函数。
首先,我们添加一个头文件来定义ioctl接口需要用到的数据(hello.h):
#ifndef _HELLO_H
#define _HELLO_H
#include <asm/ioctl.h>
#define MAXBUF 20
typedef struct _buf_data{
int size;
char data [MAXBUF];
}buf_data;
#define HELLO_IOCTL_NR_BASE 0
#define HELLO_IOCTL_NR_SET_DATA (HELLO_IOCTL_NR_BASE + 1)
#define HELLO_IOCTL_NR_MAX (HELLO_IOCTL_NR_GET_BUFF + 1)
#define HELLO_IOCTL_SET_DATA _IOR('h', HELLO_IOCTL_NR_SET_DATA, buf_data*)
#endif
然后为我们的驱动程序添加ioctl接口hello_ioctl,并实现这个函数:
static int hello_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int cmd_nr;
int err;
buf_data buff;
err = 0;
cmd_nr = _IOC_NR (cmd);
switch (cmd_nr){
case HELLO_IOCTL_NR_SET_DATA:
if (copy_from_user(&buff, (unsigned char *)arg, sizeof(buf_data)))
{
err = -ENOMEM;
goto error;
}
memset(hello_buf, 0, sizeof(hello_buf));
memcpy(hello_buf, buff.data, buff.size);
break;
default:
printk("hello_ioctl: Unknown ioctl command (%d)/n", cmd);
break;
}
error:
return err;
}
static struct file_operations hello_fops = {
.read = hello_read,
.write = hello_write,
.open = hello_open,
.ioctl = hello_ioctl,
.release = hello_release,
};
后记
到这里我们已经向您展示了Linux内核驱动程序的设备控制接口(ioctl接口),详细的介绍了它的使用,并给出了一个实际的例子,尽管它很简单,但已经足够了。到这里你可以写出一个标准的Linux驱动程序了。不过这里还有个问题,那就是我们不得不从/proc/devices文件里读取设备号然后手动创建设备节点。我们是否可以让系统自动的创建这个设备节点文件呢?当然可以。不过在那之前,我们必须深入了解Linux的设备驱动模型。后面的章节我们就详细的介绍Linux的设备驱动模型及Hotplug机制。
2. 基本过程
#define NEWCHAR_IOC_MAGIC 'M'
#define NEWCHAR_SET _IO(NEWCHAR_IOC_MAGIC, 0)
#define NEWCHAR_GET _IO(NEWCHAR_IOC_MAGIC, 1)
#define NEWCHAR_IOC_MAXNR 1
要 定义自己的ioctl操作,可以有两个方式,一种是在现有的内核代码中直接添加相关代码进行支持,比如想通过socket描述符进行 ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函数中添加自己定义的命令和相关的处理函数,重新编译内核即可, 不过这种方法一般不推荐;第二种方法是定义自己的内核设备,通过设备的ioctl()来操作,可以编成模块,这样不影响原有的内核,这是最通常的做法。
unsigned int cmd, unsigned long arg);
static int newchar_open(struct inode *inode, struct file *filep);
static int newchar_release(struct inode *inode, struct file *filep);
struct file_operations newchar_fops =
{
owner: THIS_MODULE,
ioctl: newchar_ioctl,
open: newchar_open,
release: newchar_release,
};
struct newchar{
int a;
int b;
};
#define DEVICE_NAME "newchar"
当然想搞得复杂的话可进行各种限制检查,如只允许指定的用户打开等:
{
MOD_INC_USE_COUNT;
关闭设备,也很简单,减模块计数器:
static int newchar_release(struct inode *inode, struct file *filep)
{
MOD_DEC_USE_COUNT;
进行ioctl调用的基本处理函数
static int newchar_ioctl(struct inode *inode, struct file *filep,
unsigned int cmd, unsigned long arg)
{
int ret;
if (_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;
if (_IOC_NR(cmd) > NEWCHAR_IOC_MAXNR) return -EINVAL;
ret = EINVAL;
{
case KNEWCHAR_SET:
// 设置操作,将数据从用户空间拷贝到内核空间
{
struct newchar nc;
if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
return -EFAULT;
ret = do_set_newchar(&nc);
}
break;
case KNEWCHAR_GET:
// GET操作通常会在数据缓冲区中先传递部分初始值作为数据查找条件,获取全部
// 数据后重新写回缓冲区
// 当然也可以根据具体情况什么也不传入直接向内核获取数据
{
struct newchar nc;
if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
return -EFAULT;
ret = do_get_newchar(&nc);
if(ret == 0){
if(copy_to_user((unsigned char *)arg, &nc, sizeof(nc))!=0)
return -EFAULT;
}
break;
}
return ret;
}
static int __init _init(void)
{
int result;
// 登记该字符设备,这是2.4以前的基本方法,到2.6后有了些变化,
// 是使用MKDEV和cdev_init()来进行,本文还是按老方法
result = register_chrdev(MAJOR_DEV_NUM, DEVICE_NAME, &newchar_fops);
if (result < 0) {
printk(KERN_WARNING __FUNCTION__ ": failed register character device for /dev/newchar/n");
return result;
}
return 0;
模块退出函数,登出字符设备
static void __exit _cleanup(void)
{
int result;
if (result < 0)
printk(__FUNCTION__ ": failed unregister character device for /dev/newchar/n");
module_exit(_cleanup);
- Linux驱动程序开发 - 设备控制接口
- Linux驱动程序开发 - 设备控制接口
- Linux驱动程序开发 - 设备控制接口
- 开发Linux设备驱动程序
- Linux设备控制接口
- Linux设备控制接口
- Linux设备驱动程序开发入门
- Linux字符设备驱动程序开发
- Linux字符设备驱动程序开发
- linux设备驱动程序开发笔记
- Linux驱动程序开发 - 设备IO
- 嵌入式linux设备驱动程序开发
- Linux驱动程序开发 - 设备IO
- Linux字符设备驱动程序开发
- Hasen的linux设备驱动开发学习之旅--增加了并发控制的设备驱动程序
- linux设备驱动开发学习之旅--增加了并发控制的设备驱动程序
- Linux字符设备驱动程序之并发控制
- Linux设备驱动程序中的并发控制
- 开发RCP程序时的问题
- HTML显示日期时间代码 - [js 特效代码]
- 该学习英语了
- AES/DES c加解密收藏
- 关于存储过程的返回包操作
- Linux驱动程序开发 - 设备控制接口
- PHP分页显示制作详细讲解
- 作为java的一个库来使用wvtool
- 不要说别人陀、菜这样的话,大家都是喜欢得到承认的
- 不同的Linux之间拷贝文件
- 泡泡堂助手V4.0
- php增删改查操作实例详解
- 喜羊羊给米老鼠做“小三”就能飞黄腾达?
- 多媒体