AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > Linux

Linux ioctl() Primer

51自学网 http://www.wanshiok.com
Vipul Gupta

In general an ioctl call in a user program looks like ioctl(int fd, int command, (char *) argstruct). For ioctl calls related to the networking code (these are the only ones we will deal with in this note), the file descriptor fd is actually a socket descriptor returned by the socket() system call. The command could be any one of those listed in /usr/include/linux/sockios.h. These commands are subdivided into a number of categories depending on what aspect of networking they deal with:

changing the routing table (e.g. SIOCADDRT, SIOCDELRT),
reading/updating the ARP/RARP caches (e.g. SIOCDARP, SIOCSRARP),
generic functions related to network interfaces (e.g. SIOCGIFNAME, SIOCSIFADDR etc)
The Goodies directory contains a number of sample programs illustrating the use of networking ioctl calls. As you look at these programs, notice how the structure used for argstruct depends on the ioctl command type. For example, routing table related ioctls use the rtentry structure defined in /usr/include/linux/route.h (see adddefault.c for an example) and ARP related ioctls use the arpreq structure defined in /usr/include/linux/if_arp.h (see arpread.c).

Network interface related ioctl commands typically look like SIOCxIFyyyy where x is either S (set, write) or G (get, read). The getifinfo.c program uses such commands to read the IP address, hardware address, broadcast address and flags associated with a network interface. For these ioctls, the third argument is an ifreq structure which is defined in /usr/include/linux/if.h. In some cases, new ioctl commands may be needed in addition to those defined in sockios.h, e.g. the WaveLAN wireless networking card maintains information about wireless signal strength which may be of use to a user program. How should user programs be allowed access to this information? Our first instinct may be to define a new ioctl command in sockios.h, e.g. SIOCGIFWVLNSS (Get WaVeLaN Signal Strength). Unfortunately, this command makes no sense at all for other interfaces (e.g. loopback) and attempts to use this ioctl command on interfaces other than WaveLAN cards should be disallowed. What we need, then, is a mechanism to define interface specific ioctl commands. Luckily, the Linux OS already has built-in hooks for this purpose. If you look at sockios.h again, you will notice that each device has a predefined SIOCDEVPRIVATE ioctl command. The implementation of this command is left totally upto the person writing the corresponding device driver.

By convention, a user program invokes a device specific ioctl command as ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr) where ifr is defined as struct ifreq ifr. It fills ifr.ifr_name with the interface name associated with the device, e.g. on tether, the WaveLAN card is named eth1. Typically, a user program will also need to exchange command arguments and results with the kernel and that is done through the ifr.ifr_data field, e.g. the signal strength infromation for the WaveLAN card could be returned in this field. The Linux source code already includes two devices de4x5 and ewrk3 that define and implement device specific ioctl commands. The source code for these drivers is in de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, (in /usr/src/linux/drivers/net/). Both drivers define their own private structures (struct ewrk3_ioctl and struct de4x5_ioctl) for exchanging information between user programs and the drivers. Before the ioctl call, the user program fills out the necessary fields in this structure and points ifr.ifr_data to it.

Before we go any further into the driver code for ewrk3 and de4x5, let us trace through various steps in the processing of an ioctl call. All interface-type ioctl requests (SIOCxIFyyyy and SIOCDEVPRIVATE) result in dev_ioctl() (in /usr/src/linux/net/core/dev.c) being called. This is just a wrapper and most of the real action is left for dev_ifsioc() (also in dev.c). About the only thing dev_ioctl() does is check whether a calling process has the appropriate permissions to issue the command (e.g. commands to alter the routing tables require root permissions). One of the first things dev_ifsioc() does is get the device structre (struct device defined in /usr/include/linux/netdevice.h) corresponding to the device named in ifr.ifr_name. This is followed by the code to implement generic interface commands (e.g. SIOCGIFADDR) inside a giant switch statement. The SIOCDEVPRIVATE command and any others with codes from 0x89F0 through 0x89FF end up in the default: branch for this switch. Here, the kernel checks to see if a device specific ioctl handler has been set up in the device structure. The handler is maintained as a function pointer in the do_ioctl field of the device structure. If the handler has been set, the kernel invokes it.

So, to implement device specific ioctls, all one needs to do is write a device specific ioctl handler and have the do_ioctl field in the corresponding device structure point to it. For the ewrk3 device, this function is called ewrk3_ioctl() (in ewrk3.c) and the corresponding device structure is initialized in ewrk3_init(). The ewrk3_ioctl() code clearly indicates the use of ifr.ifr_data for exchanging information between the device driver and user program. Note that this area of memory can be used for bidirectional information exchange. For example, in the ewrk3 driver code, the first two bytes of ifr.ifr_data are used to convey the specific action (e.g. EWRK3_SET_PROM, EWRK3_CLR_PROM) desired by the user (the driver implements multiple device specific commands that are all invoked through SIOCDEVPRIVATE). Also, and the buffer pointed to by the fifth byte in ifr.ifr_data is used to exchange other information (hardware address when using EWRK3_SET_HWADDR or EWRK3_GET_HWADDR).

As you go through ewrk3_ioctl(), keep in mind that normally a user process cannot directly access kernel memory. For this reason, two special procedures memcpy_tofs() and memcpy_fromfs() are provided to driver writers. The kernel procedure memcpy_tofs(arg1, arg2, arg3) copies arg3 bytes from starting address arg2 (in kernel space) to address arg1 (in user space). Similarly, memcpy_fromfs(arg1, arg2, arg3) copies arg3 bytes from starting address arg2 (in user space) to address arg1 (in kernel space). These procedures are preceded by calls to verify_area() to verify that the process has appropriate access permissions. Also notice the use of the printk() function to print debugging information. This function is similar to printf() but cannot handle floating point. The printf() function is not available to kernel code. The output generated by printk() is logged into /usr/adm/messages. For more information on these and related procedures, look at the section titled Supporting Functions in Michael K. Johnson's "Linux Kernel Hacker's Guide" which is accessible from the Linux Documentation Home Page.

 

 

 
上一篇:Linux库函数升级权威经验  下一篇:Linux程式设计入门-fork,pthread,and signals