[Linux driver] Chapter 6 advanced character driver operations-IOCTL

Source: Internet
Author: User
Tags socket error

I. IOCTL

1) concept: # include <sys/IOCTL. h>

2) Capability: controls the I/O device and provides a means to obtain device information and send control parameters to the device. It is used to send control and configuration commands to devices. Some commands require control parameters. The data cannot be read and written using read/write, which is called out-of-band data. That is to say, read/write reads and writes in-band data, is the main body of I/O operations, and IOCTL
Commands send control information, where data is auxiliary data.

3) usage: int IOCTL (INT handle, int cmd, [int * argdx, int argcx]);

4) Return Value: Success is 0, error is-1

Note the macro defined in usr/include/ASM-generic/IOCTL. h:

# DEFINE _ ioc_nrbits 8 // The Bit Width of the number field, 8 bits

# DEFINE _ ioc_typebits 8 // The font width of the type field, 8 bits

# DEFINE _ ioc_sizebits 14 // The font width of the size field, 14 bits

# DEFINE _ ioc_dirbits 2 // The ction field's Bit Width, 2 bits

# DEFINE _ ioc_nrmask (1 <_ ioc_nrbits)-1) // mask of the ordinal field, 0x000000ff

# DEFINE _ ioc_typemask (1 <_ ioc_typebits)-1) // mask of the magic number field, 0x000000ff

# DEFINE _ ioc_sizemask (1 <_ ioc_sizebits)-1) // mask of the size field, 0x00003fff

# DEFINE _ ioc_dirmask (1 <_ ioc_dirbits)-1) // mask of the direction field, 0x00000003

# DEFINE _ ioc_nrshift 0 // shift of the ordinal number field in the entire field, 0

# DEFINE _ ioc_typeshift (_ ioc_nrshift + _ ioc_nrbits) // displacement of the magic number field, 8

# DEFINE _ ioc_sizeshift (_ ioc_typeshift + _ ioc_typebits) // displacement of the size field, 16

# DEFINE _ ioc_dirshift (_ ioc_sizeshift + _ ioc_sizebits) // direction field displacement, 30

5) IOCTL System Call prototype
INT (* IOCTL) (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long Arg );
It should be noted that no matter whether the optional parameter Arg is given as an integer or a pointer by the user, it is transmitted in the form of an unsigned long. If the caller does not pass the ARG parameter, the ARG value received by the driver is undefined. Because the type check on the ARG parameter is disabled, if an invalid parameter is passed to ioctl, the compiler will not be able to issue an alert and any associated errors will be hard to be found.


2. Select the ioctl command

Before writing IOCTL code, you need to select numbers corresponding to different commands. Most programmers choose a group of small numbers starting from 0. To prevent the wrong device from using the correct command, the command number should be unique within the system. To help programmers create a unique IOCTL command code, each command number is divided into multiple single-digit fields. To select the ioctl command number for the driver according to the agreed method of the Linux kernel, you should first look at include/ASM/IOCTL. h
And documentation/ioctl-number.txt. (In the first version of Linux, a 16-bit serial number is used, with a height of eight bits: the magic number is as low as eight bits: The serial number)

The bit field symbols to be used are defined in:

Type (magic number): 8-Bit Width (_ioc_typebits), refer to ioctl-number.txt to select a number and use it throughout the driver.
Number (ordinal number): sequential number, 8-Bit Width (_ ioc_nrbits ).
Direction: Possible values include _ ioc_none (no data transmission), _ ioc_read, _ ioc_write, and _ ioc_read | _ ioc_write (two-way data transmission ). This field is a single bit mask (two digits). Therefore, you can use the and operation to extract _ ioc_read and _ ioc_write.
Size (data size): the width is related to the architecture. Arm is 14 bits. Values of specific systems can be found in macro _ ioc_sizebits.


<Linux/ipctl. h> contains the macros that define some command numbers in <ASM/IOCTL. h>:

_ IO (type, NR)/* command without parameters */
_ Ior (type, NR, datatype)/* read data from the driver */
_ Iow (type, NR, datatype)/* write data */
_ IOWR (type, NR, datatype)/* bidirectional transfer */
/* The type and number members are passed as parameters, and the size member is obtained by applying the sizeof to the datatype parameter */
This header file also defines macros used to unbind this field:
_ Ioc_dir (NR)
_ Ioc_type (NR)
_ Ioc_nr (NR)
_ Ioc_size (NR)


3. Return Value

POSIX standard stipulates that if an inappropriate IOCTL command number is used,-enotty should be returned. This error code is interpreted by the C library as "inappropriate device IOCTL. However, it returns-einval, which is still quite common.


4. predefined commands
Some IOCTL commands are identified by the kernel. When these commands are used on their own devices, they will be decoded before our own file operations are called. therefore, if you select an IOCTL command number and the system predefines the same, you will never see the request for this command, and because of the conflict between IOCTL numbers, the behavior of the application cannot be predicted. Predefined commands are divided into three types:
(1) commands used for any files (General, device, FIFO, and socket)
(2) commands used only for regular files
(3) commands specific to the file system type
The following IOCTL commands are predefined to any file, including a specific file of the device:
Fioclex: Set the close-on-exec flag (File IOCTL close on Exec ).
Fionclex: Clear the close-no-exec flag (File IOCTL not close on Exec ).
Fioqsize: This command returns the size of a file or directory. When used as a device file, however, it returns an enotty error.
Fionbio: "file IOCTL non-blocking I/O" (described in the "blocking and non-blocking operations" section ).
5. Use IOCTL Parameters
When the optional Arg parameter of IOCTL is used, if an integer is passed, it can be directly used. If it is a pointer, you must be careful. When using a pointer to reference a user space, we must ensure that the user address is valid, and its verification (data is not transmitted) is performed by the function access_ OK.
Implementation, defined in:
Int access_ OK (INT type, const void * ADDR, unsigned long size );

TypeIt should be verify_read (read) or verify_write (read/write );

The ADDR parameter is the user space address,

Size is the number of bytes. You can use sizeof ().

Access_ OK returns a Boolean value: 1 is successful (access is OK) and 0 is failed (access is faulty ). If it returns false, the driver should return-efault to the caller.

Note: first, access_ OK does not check the complete operation of memory access; it only checks whether the memory reference is within the memory range where the process has reasonable permissions, and ensures that the address does not point to the kernel space memory. Second, most of the driver code does not need to actually call access_ OK, but uses put_user (datum, PTR) and get_user (local, PTR) directly. They have the verification function, make sure that the process can write the given memory address. If the address is successful, 0 is returned, and-efault is returned in case of an error ..

<ASM/uaccess. h>

Put_user (datum, PTR)

_ Put_user (datum, PTR)
Get_user (local, PTR)
_ Get_user (local, PTR)
These macros are faster than copy_to_user and copy_from_user, and these macros have been written to allow passing pointer of any type, as long as they are a user space address. the size of the transmitted data depends on the type of the PRT parameter, and is determined by the macro creation in the compiler, such as sizeof and typeof, during compilation. They only transmit 1, 2, 4, or 8 bytes. If the above function is used to send a value of an unsuitable size, the result is often a strange message from the compiler, such as "coversion to non-scalar type requested ". in these cases, you must use copy_to_user
Or copy_from_user.
_ Put_user and _ get_user perform fewer checks (do not call access_ OK), but still fail. If the directed memory is not writable to the user, therefore, they should only use the access_ OK check in the memory zone. As a general rule: when implementing a read method, call _ put_user to save several cycles, or when you copy several items, so call access_ OK once before the first data transfer.


Vi. capabilities and limited operations

The Linux Kernel provides a more flexible system called capabilities ). The kernel is designed for permission management and exports two system calls capget and capset, which can be defined in the user space management. The meaningful capabilities for device driver writers are as follows:
Cap_dac_override/* is beyond the access restrictions (Data Access control or DAC) on files and directories. */
Cap_net_admin/* network management tasks, including tasks that can affect network interfaces */
Cap_sys_module/* ability to load or remove kernel modules */
Cap_sys_rawio/* is used to perform raw (raw) I/O operations. Examples include accessing the device port or directly communicating with the USB device */
Cap_sys_admin/* Interception Capability, providing a channel for many system management operations */
7. Implementation of the ioctl command

switch(cmd){   case MEM_IOCSQUANTUM:retval = _get_user(scull_quantum,(int *)arg);break;   case MEM_IOCGQUANTUM:retval = _put_user(scull_quantum,(int *)arg);break;      default:return -EINVAL;}

8. Use IOCTL instances in Linux

Program 1: inet_addr, netmask, and broad_addr of the detection interface program 2: Check whether the physical connection of the interface is normal program 3: simpler test of the physical connection program 4: adjust the volume ********* * ****************************** # include <stdio. h> # include <string. h> # include <stdlib. h> # include <errno. h> # include <unistd. h> # include <sys/types. h> # include <sys/socket. h> # include <netinet/in. h> # include <ARPA/inet. h> # include <sys/IOCTL. h> # include <net/if. h> static void usage () {printf (" Usage: ipconfig interface \ n "); exit (0);} int main (INT argc, char ** argv) {struct sockaddr_in * ADDR; struct ifreq IFR; char * Name, * address; int sockfd; If (argc! = 2) usage (); else name = argv [1]; sockfd = socket (af_inet, sock_dgram, 0); strncpy (IFR. ifr_name, name, IFNAMSIZ-1); If (IOCTL (sockfd, siocgifaddr, & IFR) =-1) perror ("IOCTL error"), exit (1 ); ADDR = (struct sockaddr_in *) & (IFR. ifr_addr); Address = inet_ntoa (ADDR-> sin_addr); printf ("Inet ADDR: % s", address); If (IOCTL (sockfd, siocgifbrdaddr, & IFR) =-1) perror ("IOCTL error"), exit (1); ADDR = (struct sockaddr_in *) & IFR. ifr_broadaddr; address = inet_ntoa (ADDR-> sin_addr); printf ("broad ADDR: % s", address); If (IOCTL (sockfd, siocgifnetmask, & IFR) =-1) perror ("IOCTL error"), exit (1); ADDR = (struct sockaddr_in *) & IFR. ifr_addr; address = inet_ntoa (ADDR-> sin_addr); printf ("Inet mask: % s", address); printf ("\ n"); exit (0 );} ****** **************************************** * ****** # inclu De <stdio. h> # include <string. h> # include <errno. h> # include <fcntl. h> # include <getopt. h> # include <sys/socket. h> # include <sys/IOCTL. h> # include <net/if. h> # include <stdlib. h> # include <unistd. h> typedef unsigned short 2010; typedef unsigned int u32; typedef unsigned char u8; # include <Linux/ethtool. h> # include <Linux/sockios. h> int detect_mii (INT skfd, char * ifname) {struct ifreq IFR; 2010* data, mii_val; unsig Ned phy_id;/* Get the vitals from the interface. */strncpy (IFR. ifr_name, ifname, ifnamsiz); If (IOCTL (skfd, siocgmiiphy, & IFR) <0) {fprintf (stderr, "siocgmiiphy on % s failed: % s \ n ", ifname, strerror (errno); (void) Close (skfd); return 2;} DATA = (2010*) (& IFR. ifr_data); phy_id = data [0]; data [1] = 1; if (IOCTL (skfd, siocgmiireg, & IFR) <0) {fprintf (stderr, "siocgmiireg on % s failed: % s \ n", IFR. I Fr_name, strerror (errno); return 2;} mii_val = data [3]; Return (mii_val & 0x0016) = 0x0004 )? 0: 1);} int detect_ethtool (INT skfd, char * ifname) {struct ifreq IFR; struct ethtool_value edata; memset (& IFR, 0, sizeof (IFR); edata. cmd = ethtool_glink; strncpy (IFR. ifr_name, ifname, sizeof (IFR. ifr_name)-1); IFR. ifr_data = (char *) & edata; If (IOCTL (skfd, siocethtool, & IFR) =-1) {printf ("ethtool_glink failed: % s \ n ", strerror (errno); return 2;} return (edata. data? 0: 1);} int main (INT argc, char ** argv) {int skfd =-1; char * ifname; int retval; If (argv [1]) ifname = argv [1]; else ifname = "eth0";/* open a socket. */If (skfd = socket (af_inet, sock_dgram, 0) <0) {printf ("socket error \ n"); exit (-1 );} retval = detect_ethtool (skfd, ifname); If (retval = 2) retval = detect_mii (skfd, ifname); close (skfd); If (retval = 2) printf ("cocould not determine status \ N "); If (retval = 1) printf (" link down \ n "); If (retval = 0) printf (" link up \ n "); return retval ;} * ******************************* Program 3 ******* **************************************** * ***** # include <stdio. h> # include <stdlib. h> # include <string. h> # include <errno. h> # include <net/if. h> # include <Linux/sockios. h> # include <sys/IOCTL. h> # define linktest_glink 0x0000000astruct linktest_value {unsigned int cmd; u Nsigned int data ;}; staticvoidusage (const char * pname) {fprintf (stderr, "Usage: % S <device> \ n", pname); fprintf (stderr, "returns: \ n "); fprintf (stderr," \ t 0: link detected \ n "); fprintf (stderr," \ t % d: % s \ n ", enodev, strerror (enodev); fprintf (stderr, "\ t % d: % s \ n", enonet, strerror (enonet); fprintf (stderr, "\ t % d: % s \ n ", eopnotsupp, strerror (eopnotsupp); exit (exit_failure);} staticintlinktest (const Char * devname) {struct ifreq IFR; struct linktest_value edata; int FD;/* setup our control structures. */memset (& IFR, 0, sizeof (IFR); strcpy (IFR. ifr_name, devname);/* Open Control socket. */FD = socket (af_inet, sock_dgram, 0); If (FD <0) {return-ecomm;} errno = 0; edata. cmd = linktest_glink; IFR. ifr_data = (caddr_t) & edata; If (! IOCTL (FD, siocethtool, & IFR) {If (edata. data) {fprintf (stdout, "link detected on % s \ n", devname); Return 0 ;}else {errno = enonet ;}} perror ("linktest "); return errno;} intmain (INT argc, char * argv []) {If (argc! = 2) {usage (argv [0]);} return linktest (argv [1]);} * ************************************ program 4 * **************************************** * *************** # include <sys/types. h> # include <sys/STAT. h> # include <fcntl. h> # include <sys/IOCTL. h> # include <sys/Soundcard. h> # include <stdio. h> # include <unistd. h> # include <math. h> # include <string. h> # include <stdlib. h> # define base_value 257int main (INT argc, char * argv []) {int mixer_fd = 0; char * Names [sound_mixer_nrdevices] = sound_device_labels; int value, I; printf ("\ nusage: % s dev_no. [0 .. 24] value [0 .. 100] \ n ", argv [0]); printf (" eg. % s 0 100 \ n ", argv [0]); printf (" will change the volume to max volume. \ n "); printf (" The dev_no. are as below: \ n "); for (I = 0; I <sound_mixer_nrdevices; I ++) {if (I % 3 = 0) printf ("\ n"); printf ("% s: % d \ t", Names [I], I );} printf ("\ n"); If (argc <3) Exit (1); If (mixer_fd = open ("/dev/mixer", o_rdwr ))) {printf ("mixer opened successfully, working... \ n "); value = base_value * atoi (argv [2]); If (IOCTL (mixer_fd, mixer_write (atoi (argv [1]), & value) = 0) printf ("successfully ..... "); else printf (" unsuccessfully ..... "); printf (" done. \ n ");} else printf (" can't open/dev/mixer error .... \ n "); exit (0 );}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.