做系统用什么网站好,工程材料价格查询,网络广告的特点,电脑编程入门自学简介
一个字符设备驱动通常会实现常规的open、release、read和write接口#xff0c;但是如果需要扩展新的功能#xff0c;通常以ioctl接口的方式实现。 #mermaid-svg-uY8EyPklf5e4ZMQo {font-family:trebuchet ms,verdana,arial,sans-serif;font-size:16px;fill…简介
一个字符设备驱动通常会实现常规的open、release、read和write接口但是如果需要扩展新的功能通常以ioctl接口的方式实现。 #mermaid-svg-uY8EyPklf5e4ZMQo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo .error-icon{fill:#552222;}#mermaid-svg-uY8EyPklf5e4ZMQo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uY8EyPklf5e4ZMQo .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-uY8EyPklf5e4ZMQo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uY8EyPklf5e4ZMQo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uY8EyPklf5e4ZMQo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uY8EyPklf5e4ZMQo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uY8EyPklf5e4ZMQo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uY8EyPklf5e4ZMQo .marker.cross{stroke:#333333;}#mermaid-svg-uY8EyPklf5e4ZMQo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uY8EyPklf5e4ZMQo .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uY8EyPklf5e4ZMQo text.actortspan{fill:black;stroke:none;}#mermaid-svg-uY8EyPklf5e4ZMQo .actor-line{stroke:grey;}#mermaid-svg-uY8EyPklf5e4ZMQo .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo .sequenceNumber{fill:white;}#mermaid-svg-uY8EyPklf5e4ZMQo #sequencenumber{fill:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo .messageText{fill:#333;stroke:#333;}#mermaid-svg-uY8EyPklf5e4ZMQo .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uY8EyPklf5e4ZMQo .labelText,#mermaid-svg-uY8EyPklf5e4ZMQo .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-uY8EyPklf5e4ZMQo .loopText,#mermaid-svg-uY8EyPklf5e4ZMQo .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-uY8EyPklf5e4ZMQo .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uY8EyPklf5e4ZMQo .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-uY8EyPklf5e4ZMQo .noteText,#mermaid-svg-uY8EyPklf5e4ZMQo .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-uY8EyPklf5e4ZMQo .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uY8EyPklf5e4ZMQo .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uY8EyPklf5e4ZMQo .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uY8EyPklf5e4ZMQo .actorPopupMenu{position:absolute;}#mermaid-svg-uY8EyPklf5e4ZMQo .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-uY8EyPklf5e4ZMQo .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uY8EyPklf5e4ZMQo .actor-man circle,#mermaid-svg-uY8EyPklf5e4ZMQo line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-uY8EyPklf5e4ZMQo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} user space vfs driver ioctl() sys_ioctl() ulocked_ioctl()或compat_ioctl() user space vfs driver 用户空间的ioctl
#include sys/ioctl.h
int ioctl(int fd, int cmd, ...) ;参数描述fd文件描述符cmd交互协议设备驱动将根据cmd执行对应操作…可变参数arg依赖cmd指定的长度以及类型返回值执行成功时返回0失败则返回-1
驱动中的ioctl
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);在新版内核中unlocked_ioctl()与compat_ioctl()取代了ioctl()。unlocked_ioctl()顾名思义应该在无大内核锁BKL的情况下调用compat_ioctl()compat全称compatible兼容的主要目的是为64位系统提供32位ioctl的兼容方法也是在无大内核锁的情况下调用。
在字符设备驱动开发中一般情况下只要实现unlocked_ioctl()即可因为在vfs层的代码是直接调用unlocked_ioctl()。
ioctl命令用户与驱动之间的协议
前文提到ioctl方法第二个参数cmd为用户与驱动的“协议”理论上可以为任意int型数据可以为0、1、2、3……但是为了确保该“协议”的唯一性ioctl命令应该使用更科学严谨的方法赋值在linux中提供了一种ioctl命令的统一格式将32位int型数据划分为四个位段如下图所示 在内核中提供了宏接口以生成上述格式的ioctl命令
// include/uapi/asm-generic/ioctl.h#define _IOC(dir,type,nr,size) \(((dir) _IOC_DIRSHIFT) | \((type) _IOC_TYPESHIFT) | \((nr) _IOC_NRSHIFT) | \((size) _IOC_SIZESHIFT))dir(direction)ioctl命令访问模式数据传输方向占据2bit可以为_IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE分别指示了四种访问模式无数据、读数据、写数据、读写数据type(device type)设备类型占据8bit在一些文献中翻译为“幻数”或者“魔数”可以为任意char型字符例如‘a’、‘b’、‘c’等等其主要作用是使ioctl命令有唯一的设备标识。**tips**Documentions/ioctl-number.txt记录了在内核中已经使用的“魔数”字符为避免冲突在自定义ioctl命令之前应该先查阅该文档。nr(number)命令编号/序数占据8bit可以为任意unsigned char型数据取值范围0~255如果定义了多个ioctl命令通常从0开始编号递增size涉及到ioctl第三个参数arg占据13bit或者14bit体系相关arm架构一般为14位指定了arg的数据类型及长度如果在驱动的ioctl实现中不检查通常可以忽略该参数。
通常而言为了方便会使用宏_IOC()衍生的接口来直接定义ioctl命令
// include/uapi/asm-generic/ioctl.h/* used to create numbers */
#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)))同时内核还提供了反向解析ioctl命令的宏接口
// include/uapi/asm-generic/ioctl.h/* used to decode ioctl numbers */
#define _IOC_DIR(cmd) (((cmd) _IOC_DIRSHIFT) _IOC_DIRMASK)
#define _IOC_TYPE(cmd) (((cmd) _IOC_TYPESHIFT) _IOC_TYPEMASK)
#define _IOC_NR(cmd) (((cmd) _IOC_NRSHIFT) _IOC_NRMASK)
#define _IOC_SIZE(cmd) (((cmd) _IOC_SIZESHIFT) _IOC_SIZEMASK)示例
#ifndef __HAPTCIS_IOCTL_H__
#define __HAPTCIS_IOCTL_H__/** Date: 2024-10-12 15:53:37* LastEditors: zdk* LastEditTime: 2024-10-14 11:47:49* FilePath: \kernel\drivers\haptics\haptics_ioctl.h*/#include linux/ioctl.h
// #include sys/ioctl.h // 用户空间/*这里使用ioctl定义两个协议读寄存器和写寄存器
*用户空间和内核空间共用的头文件包含ioctl命令及相关宏定义可以理解为一份“协议”文件
*/
//cmd中的type
#define DEVICE_TYPE H
#define HAPTICS_READ_REG_NR (0)
#define HAPTICS_WRITE_REG_NR (1)
#define HAPTICS_READ_REG _IOR(DEVICE_TYPE,HAPTICS_READ_REG_NR,ioctl_protocol_t *)
#define HAPTCIS_WRITE_REG _IOW(DEVICE_TYPE,HAPTICS_WRITE_REG_NR,ioctl_protocol_t *)typedef struct
{uint8_t reg_addr;uint8_t reg_value;
}ioctl_protocol_t;#endif这个头文件是共用的因为他定义了协议的具体内容。
/** Silicon Integrated Co., Ltd haptic sih688x haptic driver file** Copyright (c) 2021 heater daokuan.zhusi-in.com** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include linux/init.h //包含宏定义的头文件
#include linux/module.h //包含初始化加载模块的头文件
#include linux/fs.h
#include linux/kdev_t.h
#include linux/miscdevice.h
#include linux/device.h
#include haptics_ioctl.h#define HAPTICS_MISC_DEV_NAME haptics//打开设备
static int haptics_open(struct inode* inode,struct file * filp)
{printk(%s\n,__FUNCTION__);return 0;
}//关闭设备
static int haptics_release(struct inode* inode ,struct file* filp)
{printk(%s\n,__FUNCTION__);return 0;
}//ioctl
static long haptics_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{int ret 0;ioctl_protocol_t msg;//反解cmd中的字段int type _IOC_TYPE(cmd);int dir _IOC_DIR(cmd);int nr _IOC_NR(cmd);int size _IOC_SIZE(cmd);printk(dir%d size%d\n,dir,size);//检验cmd是否正确//1.校验cmd_typeif(DEVICE_TYPE ! type){printk(KERN_ERR cmd type error\n);ret -1;return ret;}if(HAPTICS_READ_REG cmd)//读寄存器{//校验nrif(HAPTICS_READ_REG_NR nr){ret copy_from_user(msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk(read_reg:%#02x\n,msg.reg_addr);msg.reg_value0xff;//这里模拟读寄存器的值//将读取到的值传给用户空间ret copy_to_user((ioctl_protocol_t __user *)arg, msg, sizeof(ioctl_protocol_t));}}else if(HAPTCIS_WRITE_REG cmd)//写寄存器{//校验nrif(HAPTICS_WRITE_REG_NR nr){ret copy_from_user(msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk(write_reg:%#02x%#02x\n,msg.reg_addr,msg.reg_value);//模拟写寄存器的值}}else//{printk(KERN_ERR unknown cmd\n);}return ret;
}static struct file_operations haptics_fops
{.owner THIS_MODULE,.open haptics_open,.release haptics_release,.unlocked_ioctl haptics_ioctl,
};struct miscdevice mdev
{.minor MISC_DYNAMIC_MINOR,.name HAPTICS_MISC_DEV_NAME,.fops haptics_fops,
};//定义一个杂项设备结构体static int __init haptics_init(void)
{int ret 0;//内核层只能使用printk不能使用printfprintk(KERN_EMERG %s\n,__FUNCTION__); //输出等级为0ret misc_register(mdev);if(0 ret){printk(KERN_EMERG misc_register ok minor%d\n,mdev.minor);}return 0;
}static void __exit haptics_exit(void)
{misc_deregister(mdev);printk(KERN_EMERG %s\n,__FUNCTION__); //输出等级为0
}module_init(haptics_init);//驱动入口
module_exit(haptics_exit);//驱动出口MODULE_AUTHOR(daokuan.zhugsi-in.com);//声明作者信息
MODULE_DESCRIPTION(Haptics Driver V1.0.0); //对这个模块作一个简单的描述
MODULE_LICENSE(GPL v2);//声明开源许可证// GPL 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本上面是驱动代码负责解析ioctl命令。
#include sys/types.h
#include sys/stat.h
#include stdio.h
#include stdlib.h
#include string.h
#include fcntl.h
#include unistd.h
#include haptics_ioctl.h
/*
argc:应用程序参数个数包括应用程序本身
argv[]:具体的参数内容字符串形式
./main filename r:w r表示读 w表示写
*/int main(int argc,char * argv[])
{ioctl_protocol_t msg;char* filename;int fd0;if(argc!3){printf(error usage\n);return -1;}filenameargv[1];fd open(filename,O_RDWR);if(fd0){printf(can not open file %s\n,filename);return -2;}if(0 strcmp(argv[2],r)){msg.reg_addr 0x01;ioctl(fd,HAPTICS_READ_REG,msg);printf(read %#02x%#02x\n, msg.reg_addr,msg.reg_value);}else if(0 strcmp(argv[2],w)){msg.reg_addr 0x01;msg.reg_value 0xff;ioctl(fd,HAPTCIS_WRITE_REG,msg);}else{printf(error usage\n);}close(fd);
}
上面是应用层代码负责发送ioctl消息。 注意
_IOR中第三个参数size是参数的大小用于驱动中解析使用但是如果是固定长度这个参数也没有用到。理论上使用_IOR 、_IOW 或_IO是没有严格区别的所以我们上述代码中可以改为\#define HAPTICS_READ_REG _IO(DEVICE_TYPE,HAPTICS_READ_REG_NR) ioctl函数中第三个参数表示传入驱动的实际参数可以是一个整型值也可以是一个结构体指针。我们示例中是传入的结构体指针。