✅ 操作成功!

ioctl函数

发布时间:2023-06-06 作者:admin 来源:文学

ioctl函数

ioctl函数

管理信息系统课程设计-现代远程教育研究

2023年2月19日发(作者:李德胜是谁)

【Linux】【驱动】ioctl介绍和应⽤场景

1.什么是ioctl

ioctl是⽤于设备(⽂件或者套接字)控制的公共接⼝,除了读取和写⼊设备之外,⼤部分驱动程序还需要另外⼀种能⼒,即通过设备驱动程

序执⾏各种类型的硬件控制。简单数据传输外,⼤部分设备可以执⾏其他⼀些操作,⽐如,⽤户空间经常会请求设备状态,弹出设备,报告

错误信息,改变波特率或者执⾏⾃破坏,等。这些操作通常通过ioctl⽅法执⾏,该⽅法实现了同名的系统调⽤。

2.1函数原型

在⽤户空间,ioctl系统调⽤原型如下:

#include

intioctl(intd,intrequest,...);

参数中使⽤的…表⽰可变数码的参数,但是在实际系统找中,系统调⽤不会真正使⽤可变数⽬的参数,⽽是必须具有精确定义的原型。

2.2参数说明

该参数是SOCK_DGRAM类型的套接字,⼀般初始化⽅式为:

sock=socket(AF_INET,SOCK_DGRAM,0);

uest

该参数是请求命令编号。这些编号⼀部分是已经编订好的,另外⼀部分是⽤户⾃⼰通过调⽤系统接⼝宏编制的。早期的Linux版本,为

了⽅便程序员创建唯⼀的ioctl命令号,每⼀个命令号划分为多个位字段,Linux第⼀个版本使⽤⼀个16bit的⽆符号整数。⾼8bit是

与设备相关的幻数,地8位是⼀个序列号嘛,在该设备内唯⼀。

已经编码好的命令,可以参考⽂件kernel/Documentation/ioctl/中的说明,下⾯列举出对私有设备的编码:

0x8900-06arch/x86/include/asm/sockios.h

0x890B-DFlinux/sockios.h

0x89E0-EFlinux/ROTOPRIVATErange

0x89E0-EFlinux/PRIVATErange

0x89F0-FFlinux/EVPRIVATErange

对于⽐较新的设备的请求命令编码,或者是⽤户⾃⼰添加的某类设备(⽐如gpio)命令编码,则使⽤32bit的请求码,这32位的编码

有对应的规则:

bitsmeaning

31-3000-noparameters:uses_IOmacro

10-read:_IOR

01-write:_IOW

11-read/write:_IOWR

29-16sizeofarguments

15-8asciicharactersupposedly,uniquetoeachdriver

7-0function

#kernel中提供宏接⼝⽤于计算出唯⼀的命令请求码

#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))

常见的16bit的命令编码在⽂件kernelincludelinuxsocketios.h中定义:

/*Socketconfigurationcontrols.*/

#defineSIOCGIFNAME0x8910/*getifacename*/

#defineSIOCSIFLINK0x8911/*setifacechannel*/

#defineSIOCGIFCONF0x8912/*getifacelist*/

#defineSIOCGIFFLAGS0x8913/*getflags*/

#defineSIOCGIFFLAGS0x8913/*getflags*/

#defineSIOCSIFFLAGS0x8914/*setflags*/

#defineSIOCGIFADDR0x8915/*getPAaddress*/

#defineSIOCSIFADDR0x8916/*setPAaddress*/

`

3.可变参数

通常使⽤到的可变参数类型如下:

多个设备配置信息

structifconfifconf_st;/⽤于保存多个配置/

structifconf{

intifc_len;/*sizeofbuffer*/

union{

char__user*ifcu_buf;

structifreq__user*ifcu_req;

}ifc_ifcu;

};

某个接⼝的配置信息

structifreqifr;

structifreq{

#defineIFHWADDRLEN6

union

{

charifrn_name[IFNAMSIZ];/*ifname,e.g."en0"*/

}ifr_ifrn;

union{

structsockaddrifru_addr;

structsockaddrifru_dstaddr;

structsockaddrifru_broadaddr;

structsockaddrifru_netmask;

structsockaddrifru_hwaddr;

shortifru_flags;

intifru_ivalue;

intifru_mtu;

structifmapifru_map;

charifru_slave[IFNAMSIZ];/*Justfitsthesize*/

charifru_newname[IFNAMSIZ];

void__user*ifru_data;

structif_settingsifru_settings;

}ifr_ifru;

};

⽆线设备接⼝信息:

structiwreqiwr;

structiwreq

{

union{

charifrn_name[IFNAMSIZ];/*ifname,e.g."eth0"*/

}ifr_ifrn;

/*Datapart(definedjustabove)*/

unioniwreq_datau;

};

/*------------------------IOCTLREQUEST------------------------*/

/*

*Thisstructuredefinesthepayloadofanioctl,andisused

*below.

*

*Notethatthisstructureshouldfitonthememoryfootprint

*ofiwreq(whichisthesameasifreq),whichmeanamaxsizeof

*16octets=g,pointersmightbe64bitswide...

*Youshouldcheckthiswhenincreasingthestructuresdefined

*aboveinthisfile...

*/

`

unioniwreq_data

{

/*Config-generic*/

charname[IFNAMSIZ];

/*Name:usedtoverifythepresenceofwirelessextensions.

*Nameoftheprotocol/provider…*/

structiw_pointessid;/*Extendednetworkname*/

structiw_paramnwid;/*networkid(ordomain-thecell)*/

structiw_freqfreq;/*frequencyorchannel:

*0-1000=channel

*>1000=frequencyinHz*/

structiw_paramsens;/*signallevelthreshold*/

structiw_parambitrate;/*defaultbitrate*/

structiw_paramtxpower;/*defaulttransmitpower*/

structiw_paramrts;/*RTSthresholdthreshold*/

structiw_paramfrag;/*Fragmentationthreshold*/

__u32mode;/*Operationmode*/

structiw_paramretry;/*Retrylimits&lifetime*/

structiw_pointencoding;/*Encodingstuff:tokens*/

structiw_parampower;/*PMduration/timeout*/

structiw_qualityqual;/*Qualitypartofstatistics*/

structsockaddrap_addr;/*Accesspointaddress*/

structsockaddraddr;/*Destinationaddress(hw/mac)*/

structiw_paramparam;/*Othersmallparameters*/

structiw_pointdata;/*Otherlargeparameters*/`

};

/*

*Foralldatalargerthan16octets,weneedtousea

*pointertomemoryallocatedinuserspace.

*/

structiw_point

{

void/*__user*/*pointer;/*Pointertothedata(inuserspace)*/

__u16length;/*numberoffieldsorsizeinbytes*/

__u16flags;/*Optionalparams*/

};

上⾯的参数中,最常⽤的是:

=FLAG;

r=(void*)(buffer);

=sizeof(buf);

⼀定要注意传⼊buffer的长度⼀定要⼤于等于和驱动中cop_to_user的长度,否则出现段错误。

时间戳

structtimevaludp_arrival;

if(ioctl(usd,SIOCGSTAMP,&udp_arrival)<0)

_ioctl函数

该函数中⽂件kernelnetsocket.c中定义:

staticlongsock_ioctl(structfile*file,unsignedcmd,unsignedlongarg)

{

structsocket*sock;

structsock*sk;

void__user*argp=(void__user*)arg;

intpid,err;

structnet*net;

sock=file->private_data;

sk=sock->sk;

net=sock_net(sk);

printk("%s%dcmd=0x%xrn",__FUNCTION__,__LINE__,cmd);

printk("%s%dSIOCDEVPRIVATE=0x%x,SIOCIWFIRST=0x%x,SIOCIWLAST=0x%xrn",__FUNCTION__,__LINE__,SIOCDEVPRIVATE,SIOCIWFIR

ST,SIOCIWLAST);

if(cmd>=SIOCDEVPRIVATE&&cmd<=(SIOCDEVPRIVATE+15)){/*SIOCDEVPRIVATE=0x8BE0*/

err=dev_ioctl(net,cmd,argp);/*查找设备的ioctl函数*/

}else

#ifdefCONFIG_WEXT_CORE

if(cmd>=SIOCIWFIRST&&cmd<=SIOCIWLAST){/*SIOCIWFIRST=0x8B00,SIOCIWLAST=0x8BFF*/

err=dev_ioctl(net,cmd,argp);/*查找设备的ioctl函数*/

}else

#endif

switch(cmd){

caseFIOSETOWN:

caseSIOCSPGRP:

err=-EFAULT;

if(get_user(pid,(int__user*)argp))

break;

err=f_setown(sock->file,pid,1);

break;

caseFIOGETOWN:

caseSIOCGPGRP:

err=put_user(f_getown(sock->file),

(int__user*)argp);

break;

caseSIOCGIFBR:

caseSIOCSIFBR:

caseSIOCBRADDBR:

caseSIOCBRDELBR:

err=-ENOPKG;

err=-ENOPKG;

if(!br_ioctl_hook)

request_module("bridge");

mutex_lock(&br_ioctl_mutex);

if(br_ioctl_hook)

err=br_ioctl_hook(net,cmd,argp);

mutex_unlock(&br_ioctl_mutex);

break;

caseSIOCGIFVLAN:

caseSIOCSIFVLAN:

err=-ENOPKG;

if(!vlan_ioctl_hook)

request_module("8021q");

mutex_lock(&vlan_ioctl_mutex);

if(vlan_ioctl_hook)

err=vlan_ioctl_hook(net,argp);

mutex_unlock(&vlan_ioctl_mutex);

break;

caseSIOCADDDLCI:

caseSIOCDELDLCI:

err=-ENOPKG;

if(!dlci_ioctl_hook)

request_module("dlci");

mutex_lock(&dlci_ioctl_mutex);

if(dlci_ioctl_hook)

err=dlci_ioctl_hook(cmd,argp);

mutex_unlock(&dlci_ioctl_mutex);

break;

default:

err=sock_do_ioctl(net,sock,cmd,arg);/*调⽤sock的ioctl函数sock->ops->ioctl(sock,cmd,arg)*/

break;

}

returnerr;

}

如果ioctl的命令不在SIOCIWFIRST和SIOCIWLAST范围内,则匹配其他命令,如果匹配不上,则进⼊

default:

err=sock_do_ioctl(net,sock,cmd,arg);

printk("%s%derr=0x%xrn",__FUNCTION__,__LINE__,err);

break;

调⽤sock的ioctl函数,⽽不是设备的ioctl函数。如果ioctl命令编码不在范围内,那么返回错误:

sock_ioctl953cmd=0x8C02

sock_ioctl954SIOCDEVPRIVATE=0x89f0,SIOCIWFIRST=0x8b00,SIOCIWLAST=0x8bff

ioctlerror::Operationnotsupported

添加命令

根据功能需求,有时候需要在现有的命令上添加新ioctl功能接⼝,根据上⾯的描述,有两种情况

第⼀:在现有的命令编码上添加新的接⼝,⽐如在⽆线驱动中要添加⼀些新的命令,考虑到预留的编码范围:

#defineSIOCIWFIRSTPRIV0x8BE0

#defineSIOCIWLASTPRIV0x8BFF

如果每⼀条命令都是⽤⼀个编码,很容易⽤完,所以,⼀般在实现时,使⽤2级编码的⽅法进⾏添加。

⽐如定义#defineMY_PRIV_IOCTL(SIOCIWFIRSTPRIV+0x01)

⽤于添加私有的ioctl命令,那么可以在此基础上添加subcmd,作为多个命令的区分。有两种⽅法:

⼀是通过请求参数中的成员,⽐如=MY_DRIVER_GET_STATION_LIST_INFO;

⼆是通过参数r指向的数据结构变量中定义subcmd进⾏区分不同的命令;

常⽤例⼦

1.获取接⼝的MAC地址:

structifreqifr;

structethhdr*ethh=(structethhdr*)pkt;

memset(&device,0,sizeof(device));

if((_ifindex=if_nametoindex(interface))==0){

return-1;

return-1;

}

if((fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)))<0){

return-1;

}

memset(&ifr,0,sizeof(ifr));

snprintf(_name,sizeof(_name),"%s",interface);

if(ioctl(fd,SIOCGIFHWADDR,&ifr)<0){

close(fd);

return(EXIT_FAILURE);

}

close(fd);

memcpy(ethh->h_source,__data,6);

2.获取⽹络设备序号

structifreqifr;

memset(&ifr,0,sizeof(ifr));

strncpy(_name,ifname,sizeof(_name));/*获取⽹络设备ifname的序号*/

if(-1==ioctl(sockfd,SIOCGIFINDEX,&ifr))

{

if(errbuf!=NULL)

{

perror("ioctlerror");

}

return-1;

}

_ifindex;

3.获取和设置设备的flags

structifreqifr;

memset(&ifr,0,sizeof(ifr));

strncpy(_name,ifname,sizeof(_name));

if(-1==ioctl(sockfd,SIOCGIFFLAGS,&ifr))

{

perror("ioctlerror");

return-1;

}

_setbits(_flags,(IFF_UP|IFF_BROADCAST|IFF_MULTICAST));

_clrbits(_flags,(IFF_ALLMULTI|IFF_PROMISC));

if(-1==ioctl(sockfd,SIOCSIFFLAGS,&ifr))

{

perror(“ioctlerror”);

return-1;

}

return0;

4.读取某个GPIO设备

#defineHARDWARE_GPIO_IOCTL_BASE0x01

#defineHARDWARE_GPIO_IOCTL_CMD1HARDWARE0_GPIO_IOCTL_BASE

#defineHARDWARE_GPIO_MAGIC0xB2

#defineHARDWARE_GPIO_BTN_READ_IOR(HARDWARE_GPIO_MAGIC,HARDWARE_GPIO_IOCTL_CMD1,int)

intfd=open("/dev/hardware_gpio_chrdev",O_RDONLY|O_CREAT);

intval=0;

rt=ioctl(fd,HARDWARE_GPIO_BTN_READ,&val);/*读取某个gpio的状态*/

5.设置⾮阻塞套接字

intsock_set_blocking(constsock_tsock)

{

intrc;

rc=fcntl(sock,F_GETFL,NULL);

if(rc>=0)

{

rc=fcntl(sock,F_SETFL,rc&(~O_NONBLOCK));

rc=fcntl(sock,F_SETFL,rc&(~O_NONBLOCK));

}

returnrc;

}

👁️ 阅读量:0