当前位置: 首页 > news >正文

linux驱动学习(十五)之ioctl

一般而言字符设备驱动不可能只会调用读写操作(read/write),因为字符设备还需要进行其他参数的配置(如摄像头驱动 设置摄像头的画面参数、获取摄像头的能力......),像这样的操作都不会使用读写函数来实现,一般内核都会交给ioctl函数来实现,像ioctl函数的特点就是通过发送不同命令码,然后驱动返回不同的数据。

一、应用程序中的ioctl函数

头文件

#include <sys/ioctl.h>

函数原型

int ioctl(int fd, unsigned long request,...);
参数说明:
  int fd-----文件描述符 open返回值
  unsigned long request----依赖于设备的请求代码(对设备的请求方式、命令)
  ... ----->第三个参数可以有,也可以省略,要根据参数二来确定
  
  比如:摄像头----videodev2.h--->usr/include/linux/videodev2.h4
	#define VIDIOC_G_FMT 	_IOWR('V', 4, struct v412_format)---->从底层得到v412 format数据给应用层
	#define VIDIOC_S_FMT	_IOWR('V', 5, struct v4l2_format)---->
	ioctl(fd,VIDIOC_G_FMT,要第三个参数)

常用的两种:

int ioctl(int fd, unsigned long request); //应用程序下发命令给驱动

int ioctl(int fd, unsigned long request , unsigned long args);  //应用程序下发命令和参数给驱动

二、驱动程序中的ioctl函数

long (*unlocked ioctl)(struct file *,unsigned int, unsigned long);

ioctl控制命令的形式
  ioctl命令:IO,IOR,IOW,IOWR,但是,应用程序和驱动程序也要约定同一个形式,而且由驱动程序来确定。

在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段,如下所示:

#define _IOC(dir,type,nr,size) \
	(((dir)  << _IOC_DIRSHIFT) | \
	 ((type) << _IOC_TYPESHIFT) | \
	 ((nr)   << _IOC_NRSHIFT) | \
	 ((size) << _IOC_SIZESHIFT))
名称

比特位

含义
dir31-30

命令访问模式(数据传输方向),占据 2 bit

00 - 命令不带参数
01 - 命令需要把数据写入驱动,写方向
10 - 命令需要从驱动中获取数据,读方向
11 - 命令既要写入数据又要获取数据,读写方向

type15-8设备类型,占据 8 bit,可以为任意 char 型字符,例如灯-----> 'L', beep ----> 'B'等等,其主要作用是使 ioctl 命令有唯一的设备标识
nr7-0命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增
size29-16涉及到 ioctl 函数第三个参数 arg ,占据14bit,指定了 arg 的数据类型及长度
#define _IO(type,nr)		_IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)	_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOR_BAD(type,nr,size)	_IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)		(((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)		(((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)		(((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)		(((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

/* ...and for the drivers/sound files... */

#define IOC_IN		(_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT		(_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT	((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK	(_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT	(_IOC_SIZESHIFT)

1)定义一个命令,该命令不需要参数

  #define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)

2)定义一个命令,该命令说明应用程序向驱动程序读参数

#define _IOR(type,nr,size)    _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))

3)定义一个命令,该命令说明应用程序向驱动程序写参数

  #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

4)定义一个命令,参数的传输是双向的

 #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE, (type),(nr),(_IOC_TYPECHECK(size)))

5)获得传输方向位段的值

#define _IOC_DIR(nr)  (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)

6)获得类型的值

#define _IOC_TYPE(nr)  (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)

7)获得编号的值

#define _IOC_NR(nr)  (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)

8)获得大小的值

#define _IOC_SIZE(nr)  (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)    

应用程序如果打算下发命令给驱动程序,需要设计对应的命令码,并且驱动程序也必须有对应的命令码(相当于应用程序和驱动程序必须有一致的命令码)。

三、示例

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/device.h>

static struct class *class;

struct option{
 unsigned int datab;
    unsigned int parity;
    unsigned int stopb;
};

#define VS_MAGIC 's'

#define VS_SET_BAUD _IOW(VS_MAGIC, 0, unsigned int)
#define VS_GET_BAUD _IOR(VS_MAGIC, 1, unsigned int)
#define VS_SET_FFMT _IOW(VS_MAGIC, 2, struct option)
#define VS_GET_FFMT _IOR(VS_MAGIC, 3, struct option)

#define VSER_MAJOR 256
#define VSER_MINOR 0
#define VSER_DEV_CNT 1
#define VSER_DEV_NAME "vser"

struct vser_dev{
 unsigned int baud;
    struct option opt;
    struct cdev cdev;
};

DEFINE_KFIFO(vsfifo, char, 32);
static struct vser_dev vsdev;

static int vser_open(struct inode *inode, struct file *filp)
{
 return 0;
}

static int vser_release(struct inode *inode, struct file *flip)
{
 return 0;
}

static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
 int ret;
    unsigned int copied = 0;
    ret = kfifo_to_user(&vsfifo, buf, count, &copied);
    
    return ret == 0 ? copied : ret;
}

static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
 int ret;
    unsigned int copied = 0;
    
    ret = kfifo_from_user(&vsfifo, buf, count, &copied);
    
    return ret == 0? copied : ret;
}

static long vser_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    if (_IOC_TYPE(cmd) != VS_MAGIC)
        return -ENOTTY;
 
    switch(cmd) {
        case VS_SET_BAUD:
             vsdev.baud = arg;
             break;
        case VS_GET_BAUD:
             arg = vsdev.baud;
             break;
        case VS_SET_FFMT:
             if (copy_from_user(&vsdev.opt, (struct option __user *)arg, sizeof(struct option)))
                    return -EFAULT; 
             break;
        case VS_GET_FFMT:
             if (copy_to_user((struct option __user *)arg, &vsdev.opt, sizeof(struct option)))
     return -EFAULT;
             break;
        default:
             break;

 };
    
 return 0;
}

static struct file_operations vser_ops = {
 .owner = THIS_MODULE,
    .open = vser_open,
    .release = vser_release,
    .read = vser_read,
    .write = vser_write,
    .unlocked_ioctl = vser_ioctl,
};

static int __init vser_init(void)
{
 int ret;
    dev_t dev;
    

    dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    if (ret)
         goto reg_err;
    
    cdev_init(&vsdev.cdev, &vser_ops);
    vsdev.cdev.owner = THIS_MODULE;
    vsdev.baud = 115200;
    vsdev.opt.datab = 8;
    vsdev.opt.parity = 0;
    vsdev.opt.stopb = 1;
    
    ret = cdev_add(&vsdev.cdev, dev, VSER_DEV_CNT);
    if (ret)
        goto add_err;

    /* 自动创建设备节点 */
    class = class_create(THIS_MODULE, "my_ioctl");  /* /sys/class/my_ioctl */
 device_create(class, NULL, dev, NULL, "vser0"); /* /dev/vser0 */

    return 0;
    
add_err:
    unregister_chrdev_region(dev, VSER_DEV_CNT);
reg_err:
    return ret;
}

static void __exit vser_exit(void)
{
 dev_t dev;
 dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    
    cdev_del(&vsdev.cdev);

    device_destroy(class, dev);
 class_destroy(class);


    unregister_chrdev_region(dev, VSER_DEV_CNT);
}

module_init(vser_init);
module_exit(vser_exit);
MODULE_LICENSE("GPL");

应用层:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>

struct option{
 unsigned int datab;
    unsigned int parity;
    unsigned int stopb;
};

#define VS_MAGIC 's'

#define VS_SET_BAUD _IOW(VS_MAGIC, 0, unsigned int)
#define VS_GET_BAUD _IOR(VS_MAGIC, 1, unsigned int)
#define VS_SET_FFMT _IOW(VS_MAGIC, 2, struct option)
#define VS_GET_FFMT _IOR(VS_MAGIC, 3, struct option)

int main(int argc, char *argv[])
{
 int fd;
    int ret;
    unsigned int baud;
    struct option opt = {8,1,1};
    
    fd = open("/dev/vser0", O_RDWR);
    if (fd == -1)
        goto fail;
    
    baud = 9600;
    ret = ioctl(fd, VS_SET_BAUD, baud);
    if (ret == -1)
        goto fail;
    
    ret = ioctl(fd, VS_GET_BAUD, baud);
    if (ret == -1)
        goto fail;
    
    ret = ioctl(fd, VS_SET_FFMT, &opt);
    if (ret == -1)
        goto fail;
    
 ret = ioctl(fd, VS_GET_FFMT, &opt);
    if (ret == -1)
        goto fail;
    
    printf("baud rate:%d\n", baud);
    printf("frame format: %d%d%d\n", opt.datab, opt.parity, opt.stopb);

 close(fd);
    exit(EXIT_SUCCESS);
    
fail:
     perror("ioctl test");
     exit(EXIT_FAILURE);
}

觉得有帮助的话,打赏一下呗。。

           

需要商务合作(定制程序)的欢迎私信!! 

相关文章:

  • 软件工程面试题(三十)
  • 【Android】界面布局-相对布局RelativeLayout-例子
  • 网络基础二
  • linux专题3-----禁止SSH的密码登录
  • 论文阅读笔记——RDT-1B: A DIFFUSION FOUNDATION MODEL FOR BIMANUAL MANIPULATION
  • R 语言科研绘图第 36 期 --- 饼状图-基础
  • 大厂不再招测试?软件测试左移开发合理吗?
  • C 语言排序算法:从基础到进阶的全面解析一、引言
  • Deep Reinforcement Learning for Robotics翻译解读
  • 【Python使用】嘿马云课堂web完整实战项目第3篇:增加数据,修改数据【附代码文档】
  • Python菜鸟教程(小程序)
  • UE5把动画导出为视频格式
  • CentOS 7上配置SQL Server链接其他SQL Server服务器
  • 【HTML】纯前端网页小游戏-戳破彩泡
  • 算法刷题记录——LeetCode篇(2.3) [第121~130题](持续更新)
  • 【嵌入式系统设计师】知识点:第2章 嵌入式系统硬件基础知识
  • Latex语法入门之数学公式
  • 【群晖CPU异常占用原因及解决办法】synoscgi_SYNO.Core.System.ProcessGroup_1_list
  • risc-V学习日记(3):编译与链接
  • MySQL 安全与权限管理:数据库的城堡守卫系统