杂学杂记(三)关于ioctl设备控制的一些分析

        大部分驱动除了需要具备读写设备的能力外,还需要具备对硬件控制的能力。例如,要求设备报告错误信息,改变波特率,这些操作常常通过ioctl方法来实现。

         在用户空间打开一个设备, 如I/O设备可用open()打开,网络协议可用socket()打开等,获取一个文件描述符后,就可以在这个描述符上调用ioctl()来向内核交换数据。

         要定义自己的ioctl操作,可以有两个方式,一种是在现有的内核代码中直接添加相关代码进行支持,比如想通过socket描述符进行 ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函数中添加自己定义的命令和相关的处理函数,重新编译内核即可, 不过这种方法一般不推荐;第二种方法是定义自己的内核设备,通过设备的ioctl()来操作,可以编成模块,这样不影响原有的内核,这是最通常的做法。

 

◇用户使用方法:

在用户空间,使用ioctl系统调用来控制设备,原型如下:

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

原型中的点表示这是一个可选的参数,存在与否依赖于控制命令(第二个参数)是否涉及到与设备的数据交互,如果cmd命令不涉及数据传输则第3 个参数arg的值无任何意义.

 

◇驱动ioctl方法:

ioctl(I/O control)驱动方法有和用户空间版本不同的原型:

/*2.6.36版本以前:*/

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

/*2.6.36版本以后:*/

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

*inode是物理信息,*filp对应一个打开的文件(照写),cmd参数从用户空间传下来,可选参数arg以一个unsigned long的形式传递,不管它是一个整数或一个指针,如果cmd命令不涉及数据传输,则第三个参数arg的值无任何意义。

 

◇ioctl实现步骤:

1. 定义命令:

         在编写unlock_ioctl代码之前,首先需要定义命令:int ioctl(int fd, int cmd, void *data)

第一个参数是文件描述符;第三个参数是数据起始位置指针;第二个参数cmd是操作命令,一般分为GET、SET以及其他类型命令,GET是用户空间进程从内核读数据,SET是用户空间进程向内核写数据,cmd虽然是一个整数,但是有一定的参数格式的,为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的。ioctl命令参数是个32位整数,分为四部分,dir(2b) size(14b) type(8b) nr(8b) ,分别代表:传递方向参数的大小类型(幻数)序号。使用如_IOC_NR(cmd)来获取命令的序号参数。

        详细定义cmd要包括这4个部分时可使用宏_IOC(dir,type,nr,size)来定义,而最简单情况下使用_IO(type, nr)来定义就可以了,这些宏都在include/asm/ioctl.h中定义

i。Documentation/ioctl-number.txt文件中罗列了在内核中已经使用了的幻数。

定义ioctl命令的正确方法是使用4个位段,这个列表中介绍的符号定义在<linux/ioctl.h>中:

◇Type:幻数(类型),表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽。

◇Number:序号,表明设备命令中的第几个,8位宽。

◇Direction:数据传送的方向,可能的值是_IOC_NONE(没有数据传输),          _IOC_READ,_IOC_WRITE。数据传送是从应用程序的观点来看待的,_IOC_READ意思是从设备读。

◇Size:用户数据的大小。(13/14位宽,视处理器而定)

内核提供了下列宏来帮助定义命令:(nr:序号)

◎_IO(type,nr):没有参数的命令

◎_IOR(type,nr,datatype):从驱动中读数据

◎_IOW(type,nr,datatype):写数据到驱动

◎_IOWR(type,nr,datatype):双向传送,type和number作为参数传递。

范例:

#define MEM_IOC_MAGIC         'm'                //定义幻数

#define MEM_IOCSET                _IOW(MEM_IOC_MAGIC,0,int)

#define MEM_IOCGQSET        _IOR(MEM_IOC_MAGIC,1,int)

 

2. Ioctl函数实现:

定义好了命令,下一步就是要实现unlock_ioctl函数了,unlock_ioctl函数的实现包括如下3个技术环节:

1) 返回值:

unlock_ioctl函数的实现通常是根据命令执行的一个switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时,通常返回-EINVAL(“非法参数”)。

2) 参数使用:

如何使用unlock_ioctl中的参数arg?如果是一个整数,可以直接使用。如果是指针,我们必须确保这个用户地址是有效的,因此使用前需进行正确的检查。

不需要检查:copy_from_user、copy_to_user、get_user、put_user

需要检查:__get_user、__put_user

参数检查:

                                       int access_ok(int type,const void *addr,unsigned long size)

第一个参数是VERIFY_READ或者VERIFY_WRITE,用来表明是读用户内存还是写用户内存。addr参数是要操作的用户内存地址,size是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数等于sizeof(int)。

access_ok返回一个布尔值:1是成功(存取没问题),0是失败(存取有问题),如果该函数返回失败,则Ioctl应当返回-EFAULT。 

 

Ioctl驱动实现代码:

/* 定义幻数*/

#define MEMDEV_IOC_MAGIC 'k'

/* 定义命令*/

#define MEMDEV_IOCPRINT  _IO(MEMDEV_IOC_MAGIC, 1)

#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)

#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)

/*IO操作*/

intmemdev_ioctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg)

{

    interr = 0;

    intret = 0;

    intioarg = 0;  

    /*检测命令的有效性*/

    if(_IOC_TYPE(cmd)!=MEMDEV_IOC_MAGIC)

        return -EINVAL;

    if(_IOC_NR(cmd)>MEMDEV_IOC_MAXNR)

        return -EINVAL;

    /*根据命令类型,检测参数空间是否可以访问*/

    if(_IOC_DIR(cmd)&_IOC_READ)

        err = !access_ok(VERIFY_WRITE,(void*)arg,_IOC_SIZE(cmd));

    elseif(_IOC_DIR(cmd)&_IOC_WRITE)

        err = !access_ok(VERIFY_READ,(void*)arg,_IOC_SIZE(cmd));

    if(err)

        return -EFAULT; 

    /*根据命令,执行相应的操作*/

    switch(cmd){

      /*打印当前设备信息*/

      caseMEMDEV_IOCPRINT:

      printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");

        break;   

      /*获取参数*/

      caseMEMDEV_IOCGETDATA:

        ioarg = 1101;

        ret = __put_user(ioarg,(int*)arg);

        break;    

      /*设置参数*/

      caseMEMDEV_IOCSETDATA:

        ret = __get_user(ioarg,(int*)arg);

        printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);

        break;

      default: 

        return -EINVAL;

    }

    return ret;

}

 


============================

简单实现思路:

在app.c和driver.c都定义如下命令:

#define FB_CTRL_CMD_INIT        _IOW('v', 3, int)


driver.c中:

static long fb_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {

    case FB_CTRL_CMD_INIT:
        fb_ctrl_io_cfg();
        break;

   case ...

}


并在操作函数结构体中指定:

static struct file_operations fb_ctrl_fops = {
    .unlocked_ioctl    = fb_ctrl_ioctl,
};


app.c:

int fd = open("/dev/fb_ctrl", O_RDWR);

int flag = 0;

ioctl(fd, FB_CTRL_CMD_INIT, &flag);


即可在app.c中向驱动读写数据

嗨!很高兴回答你关于Python的NumPy库的问题。NumPy是一个强大的科学计算库,提供了高性能的数组对象和各种功能丰富的数学函数。 首先,你可以使用以下命令安装NumPy库: ```python pip install numpy ``` 一旦安装完成,你可以在Python脚本中导入NumPy库: ```python import numpy as np ``` 现在让我们来看一些NumPy的常用功能。 1. 创建NumPy数组: - 使用`np.array()`函数从Python列表或元组创建数组。 - 使用`np.zeros()`创建一个元素全为0的数组。 - 使用`np.ones()`创建一个元素全为1的数组。 - 使用`np.random`模块生成随机数组。 2. 数组操作: - 通过索引访问和修改数组的元素。 - 使用切片操作提取子数组。 - 使用数组的形状、大小和维度等属性。 3. 数学函数: - NumPy提供了丰富的数学函数,例如平方根(`np.sqrt()`)、指数函数(`np.exp()`)、对数函数(`np.log()`)等。 - 通过在数组上应用这些函数,可以进行元素级别的数学操作。 4. 数组运算: - NumPy支持基本的数组运算,如加法、减法、乘法和除法。 - 这些运算可以在两个数组之间进行,也可以在数组和标量之间进行。 5. 线性代数: - NumPy提供了许多线性代数操作的函数,如矩阵乘法(`np.dot()`)、矩阵求逆(`np.linalg.inv()`)、特征值和特征向量(`np.linalg.eig()`)等。 这只是NumPy库的一小部分功能,但对于进行科学计算和数据分析来说非常重要。你可以参考NumPy官方文档以了解更多详细信息:https://numpy.org/doc/ 希望这些信息能帮助你开始学习NumPy库!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值