edited on: 2022-03-17 15:47
Here's my attempt to turn Don's plan from the desired action into a fully fledged set of editing instructions, taking into account the mailing list discussion in August 2013.
All page and line numbers refer to the 2016/2018 edition.
On page xxxix section Referenced Documents, insert before "Sarwate Article":
IEEE Std 1003.26(TM)-2003, IEEE Standard for Information Technology - Portable Operating System Interface (POSIX) - Part 26: Device Control Application Program Interface (API) [C Language]
On page 7 line 180 section 1.7.1 Codes, insert a new code:
The functionality described is optional. The functionality described is also an extension to the ISO C standard.
Where applicable, functions are marked with the DC margin legend in the SYNOPSIS section. Where additional semantics apply to a function, the material is identified by use of the DC margin legend.
On page 507 line 17993 section 188.8.131.52 Cancellation Points, add posix_devctl() to the list of functions that may be a cancellation point.
On page 16 line 530 section 184.108.40.206 POSIX System Interfaces, insert:
On page 96 line 2957 section 4 General Concepts, insert a new section:
4.19 Special Device Drivers
Some devices require control operations, other than the operations that are common to most devices (such as read(), write(), open(), and close()), but because the device belongs to a class that is not present in the majority of systems, standardization of a device-specific application program interface (API) for controlling it has not been practical. The driver for such a device may respond to the write() function to transfer data to the device or the read() function to collect information from the device. The interpretation of the information is defined by the implementor of the driver.
The term special device refers to hardware, or an object that appears to the application as such; access to the driver for this hardware uses the file abstraction character special file. Implementations supporting the Device Control option shall provide the means to integrate a device driver into the system. The means available to integrate drivers into the system and the way character special files that refer to them are created are implementation defined. Character special files that have no structure defined by this standard can be accessed using the posix_devctl() function defined in the System Interfaces volume of POSIX.1-202x.
and renumber the later sections.
On page 218 insert a new <devctl.h> page:
<devctl.h> - device control
The following shall be declared as a function and may also be defined as a macro. A function prototype shall be provided.
int posix_devctl(int, int, void *restrict, size_t, int *restrict);
First released in Issue 8. Derived from POSIX.26.
On page 426 line 14745 section <unistd.h>, insert a new paragraph:
The implementation supports the device control option. If this symbol is defined in <unistd.h>, it shall be defined to be -1, 0, or 20yymmL. The value of this symbol reported by sysconf() shall either be -1 or 20yymmL.[/DC]
On page 435 line 15151 section <unistd.h>, insert:
On page 1430 insert a new posix_devctl() page:
posix_devctl - device control
int posix_devctl(int fildes, int dcmd,
void *restrict dev_data_ptr, size_t nbyte,
int *restrict dev_info_ptr);[/DC]
The posix_devctl() function shall cause the device control command dcmd to be passed to the driver identified by fildes. Associated data shall be passed to and/or from the driver depending on direction information encoded in the dcmd argument or as implied in the dcmd argument by the design and implementation of the driver.
If the dev_data_ptr argument is not a null pointer, it shall be a pointer to a buffer that is provided by the caller and that contains data bytes to be passed to the driver or provides space for receiving data bytes to be passed back from the driver, or both.
If the data bytes are to be passed to the driver, at least nbyte bytes of associated data shall be made available to the driver; if the data bytes are to be passed from the driver, no more than nbyte bytes shall be passed.
The driver may be executing in an address space different from the address space of the calling thread. Therefore, if the data bytes passed to the driver (i.e., the contents of the memory area starting at dev_data_ptr and continuing for nbyte bytes) contain pointers to memory in the address space of the calling thread and the driver uses these pointers to access that memory, the effects are unspecified.
[OB]If dev_data_ptr is not a null pointer and nbyte is zero, the amount of data passed to and/or from the driver is unspecified. This feature is obsolescent and is only provided for compatibility with existing device drivers.[/OB]
If dev_data_ptr is a null pointer, there shall be no data bytes passed between the caller and the driver other than the data specified in the rest of the arguments to posix_devctl() and in its return value.
The dev_info_ptr argument provides the opportunity to return an integer number containing additional device information, instead of just a success/failure indication. For implementation-provided dcmd values, it is implementation-defined whether each such value causes the int pointed to by dev_info_ptr to be set and, if set, what value it is set to.
For each supported device, the set of valid dcmd commands, the associated data interpretation, and the effects of the command on the device are all defined by the driver for the device identified by fildes, and are therefore implementation-defined for implementation-provided device drivers.
Upon successful completion, posix_devctl() shall return zero; otherwise an error number shall be returned to indicate the error. The value returned in the int value pointed to by dev_info_ptr is driver dependent.
The posix_devctl() function shall fail if:
The fildes argument is not a valid open file descriptor.The posix_devctl() function may fail if:
The posix_devctl() function was interrupted by a signal.[EINVAL]
The nbyte argument is negative, or exceeds an implementation-defined maximum, or is less than the minimum number of bytes required for this command.[EINVAL]
The dcmd argument is not valid for this device.[ENOTTY]
The fildes argument is not associated with a character special file that accepts control functions.[EPERM]
The requesting process does not have the appropriate privilege to request the device to perform the specified command.Driver code may detect other errors, but the error numbers returned are driver dependent. See ``Recommended Practice for Driver-Detected Errors'' in RATIONALE.
If the posix_devctl() function fails, the effect of this failed function on the device is driver dependent. Corresponding data might be transferred, partially transferred, or not transferred at all.
An interface to be included in the POSIX standard should improve source code portability of application programs. In traditional UNIX practice, ioctl() was used to handle special devices. Therefore, a general specification of its arguments cannot be written. Based on this fact, in the past many people claimed that ioctl(), or something close to it, had no place in the POSIX standards.
Against this perception stood the widespread use of ioctl() to interface to all sorts of drivers for a vast variety of hardware used in all areas of general-purpose, realtime, and embedded computing, such as analog-digital converters, counters, and video graphic devices. These devices provide a set of services that cannot be represented or used in terms of read() or write() calls.
The arguments in favor of ioctl() standardization can be summarized as follows:
Even if ioctl() addresses very different hardware, many of these devices either are actually the same, interfaced to different computer systems with different implementations of operating systems, or belong to classes of devices with rather high commonality in their functions, e.g., analog-digital converters or digital-analog converters. Growing standardization of the control and status register (CSR) space of these devices allows exploitation of a growing similarity of control codes and data for these devices. A general mechanism is needed to control these devices.
In all these cases, a standardized interface from the application program to drivers for these devices will improve source code portability.
Even if control codes and device data have to be changed when porting applications from one system to another, the definition of ioctl() largely improves readability of a program handling special devices. Changes are confined to more clearly labeled places.
A driver for a specific device normally cannot be considered portable per se, but an application that uses this driver can be made portable if all interfaces needed are well defined and standardized. Users and integrators of realtime systems often add device drivers for specific devices, and a standard interface simplifies this process. Also, device drivers often follow their special hardware from system to system.
In recognition of these reasons, The Open Group included ioctl() in the The Single UNIX Specification, Version 1, and the interface was later incorporated into POSIX.1 under the XSI STREAMS option (although that option was subsequently removed).
The posix_devctl() interface defined in this standard provides an alternative to the the various ioctl() implementations with a standard interface that captures the extensibility of ioctl(), but avoids several of its deficiencies, which is mentioned in ``Relationship to ioctl() and the Perceived Needs for Improvement'' below.
The ioctl() interface is widely used. It has provided the generality mentioned above. Existing practice encodes into the second parameter information about data size and direction in some systems. An example of such an encoding is the use in BSD 4.3 of two bits of the command word as read/write bits. However, ioctl() has definite problems with the way that its sometimes optional third parameter can be interpreted.
This practice is similar to the existing POSIX fcntl() function, in which the third parameter can be optional for F_GETFD, F_GETFL, an int when used with the F_DUPFD, F_SETFD, or F_SETFL commands or a struct flock, when used with the F_GETLK, F_SETLD, or F_SETLKW commands. However, the fcntl() interface defines two distinct and known data types as possible for the third parameter. This is not the case in the ioctl() interface, where any number of device driver specific structures and commands are used.
Relationship to ioctl() and the Perceived Needs for Improvement
[xref to XRAT A.11] briefly mentions some of the perceived deficiencies in existing implementations of the ioctl() function, in the context of those ioctl() commands used to implement terminal control. The standard developers decided that, since the set of such control operations was fairly well defined, suitable encapsulations such as tcsetattr(), tcsendbreak(), and tcdrain() could be standardized. These interfaces, while successfully standardizing portable terminal control operations, are not extensible to arbitrary user-supplied devices.
There are several perceived deficiencies with the ioctl() function that drove the development of the posix_devctl() interface as an alternative:
- The major problem with ioctl() is that the third argument (when one is passed) varies in both size and type according to the second (command) argument. It is not unprecedented in POSIX, or standards in general, for a function to accept a generic pointer; consider the ISO C function fread(), or the POSIX functions read() and mmap(). However, in all such instances, the generic pointer is accompanied by a size argument that specifies the size of the pointed-to object. Unlike the Ada language, it is, and has always been, the C programmer's responsibility to ensure that these two arguments form a consistent specification of the passed object. But traditional ioctl() implementations do not allow the user to specify the size of the pointed-to object; that size is instead fixed implicitly by the specified command (passed as another argument). The posix_devctl() interface improves upon ioctl() in that it allows the user to specify the object size, thereby restoring the familiar C paradigm for passing a generic object by pointer/size pair.
- A secondary problem with ioctl() is that the third argument is sometimes permitted to be interpreted as an integer (int). The posix_devctl() interface clearly requires the dev_data_ptr argument to be a pointer.
- A related problem with ioctl() is that the direction(s) in which data are transferred to or from the pointed-to object is neither specified explicitly as an argument (as with mmap()), nor implied by the ioctl() function (as with read()/write(), fread()/fwrite(), or fgets()/fputs()). Instead, the direction is implied by the command argument. In traditional implementations, only the device driver knows the interpretation of the commands and whether data bytes are to be transferred to or from the pointed-to object. But in networked implementations, generic portions of the operating system may need to know the direction to ensure that data bytes are passed properly between a client and a server, separately from device driver concerns. Two implementation-specific solutions to this problem are to always assume data bytes need to be transferred in both directions, or to encode the implied direction into the command word along with the fixed data size. The posix_devctl() interface already provides the implementation with an explicit size parameter. Since the direction is already known implicitly to both the application and the driver and since workable methods exist for implementations to ascertain that direction if required, this perceived problem is strictly an implementation issue and solvable without further impact on the interface.
- Finally, posix_devctl() improves upon ioctl() by adopting the new style of error return, avoiding all the problems errno brings to multi-threaded applications. Because the driver-specific information carried by the non-error return values of ioctl() still potentially needs to be passed to the application, posix_devctl() adds the dev_info_ptr argument to specify where this information should be stored.
Which Differences Between posix_devctl() and ioctl() Are Acceptable?
Any differences between the definitions of posix_devctl() and ioctl() have to be perceived as a clear improvement by the community of potential users. Drivers for normal peripherals are typically written by highly specialized professionals. Drivers for the special devices are very often written by the application developer or by the hardware designer. Any interface definition that can be seen as overly complicated will simply not be accepted.
Nevertheless, a few simple and useful improvements to ioctl() are possible, specifically the improvement of type checking, and justify the definition of a new interface.
The major difference between the two interfaces is the addition of the size of the device data. For enhanced compatibility with existing ioctl() implementations, this size can be specified as zero; in this case the amount of data passed is unspecified. (This allows a macro definition of ioctl() that converts it into a posix_devctl() call.) In any case, the data size argument does not contradict the general goal of being able to implement posix_devctl() using the existing ioctl() interfaces provided in current UNIX systems and other POSIX implementations because the standard allows but does not require checking the size of the device data. Although the third argument of the ioctl() function does not specify a size, it is implicit in the specific combination of control command and driver and, therefore, known to the driver implementation.
The method of indicating error return values differs from traditional ioctl() implementations, but it does not preclude the construction of posix_devctl() as a macro built upon ioctl(), which was one of the original design goals.
Rationale for the dev_info_ptr Argument
The POSIX.26 developers felt that it was important to preserve the current ioctl() functionality of allowing a device driver to return some arbitrary piece of information instead of just a success/failure indication. Such information might be, for example, the number of bytes received, the number of bytes that would not fit into the buffer pointed at by dev_data_ptr, the data type indication, or the device status. Current practice for device drivers and ioctl() usage allows such a device-dependent return value. Thus, the concept of an additional output argument, dev_info_ptr, was born.
Rationale for No direction Argument
The initial specification for posix_devctl() contained an additional argument that specified the direction of data flow, i.e., to the driver and/or from the driver. This argument was later removed for the following reasons:
- The argument was redundant. Most (if not all) existing implementations encode the direction data either explicitly or implicitly in the command word.
- The argument increased the probability of programming errors, since it must be made to agree with the direction information already encoded or implied in the command word or an error would occur.
- The only real use of the argument would be if new drivers were written that supported generic commands such as TRANSFER_CONTROL_DATA, which was modified by the direction argument to indicate in which direction the data should be transferred. This is contrary to current practice that uses command pairs such as GET_CONTROL_DATA and PUT_CONTROL_DATA.
- The primary purpose of the direction argument was to allow higher levels of the system to identify the direction of data transfers, particularly in the case of remote devices, without having to understand all the commands of all the devices on the system. Implementations that need to ascertain the direction of data transfer from a command word will define a consistent convention for encoding the direction into each command word, and all device drivers supplied by the user must adhere to this convention.
Thus, the data direction argument was removed.
Rationale for Not Defining the Direction Encoding in the dcmd Argument
The POSIX.26 developers gave consideration to defining the direction encoding in the dcmd argument, but decided against doing so. No particular benefit was seen to a predefined encoding, as long as the encoding was used consistently across the entire implementation and was well known to the implementation.
In addition, although only one encoding (BSD's) employed for ioctl() was known among the members of the small working group, it could not be ruled out that other encodings already existed, and no reason for precluding these encodings was seen.
Finally, system or architectural constraints might make a chosen standard encoding difficult to use on a given implementation.
Thus, this standard does not define a direction encoding. Specifying a standard encoding is actually a small part of a larger and more contentious objective, that of specifying a complete set of interfaces for portable device drivers. If a future POSIX standard specifies such interfaces, the issue of device control direction encoding will necessarily be addressed as part of that specification.
Recommended Practice for Handling Data Size Errors
In the event that the amount of data from the device is too large to fit into the specified buffer, as much data as will fit should be transferred, and the error posted. The retained data will aid in debugging, even if some of the data is lost.
Recommended Practice for nbyte == 0
The feature that permits an unspecified amount of control data to be transferred if nbyte is zero exists only for compatibility with existing device driver usage of ioctl(), i.e., when ioctl() is implemented on top of posix_devctl() and the device driver transfers an amount of data implied by the command.
Implementations in which posix_devctl() is built as a library routine on top of ioctl() may not be able to make checks on the nbyte argument. However, newly developed applications using posix_devctl() should always use an appropriate value for the nbyte argument, for portability to implementations directly supporting posix_devctl() in which the device drivers may be able to honor the application's nbyte argument or return the error [EINVAL] if the argument is an unacceptable value. Device drivers designed for those systems should interpret a zero value of nbyte as no data to be transferred.
Recommended Practice for Driver-Detected Errors
If the driver detects the following error conditions, it is recommended that the posix_devctl() function fail and return the corresponding error number:
The control operation could not complete successfully because the device was in use by another process, or the driver was unable to carry out the request due to an outstanding operation in progress.[EINVAL]
The arguments dev_dta_ptr and nbyte define a buffer too small to hold the amount of data expected by or to be returned by this driver.[EIO]
The control operation could not complete successfully because the driver detected a hardware error.
XBD 4.19 Special Device Drivers, <devctl.h>
On page 2059 line 66728 section sysconf(), insert:
On page 3440 line 117435 section A.4 General Concepts, insert a new section:
A.4.19 Special Device Drivers
POSIX systems interact with their physical environment using a variety of devices (such as analog-digital converters, digital-analog converters, counters, and video graphic equipment), which provide a set of services that cannot be fully utilized in terms of read and/or write semantics. Traditional practice uses a single function, called ioctl(), to encapsulate all the control operations on the different devices connected to the system, both special or common devices. The POSIX.1-1988 developers decided not to standardize this interface because it was not type safe, it had a variable number of parameters, and it had behaviors that could not be specified by the standard because they were driver-dependent. Instead, POSIX.1-1988 defined a device-specific application program interface (API) for a common class of drivers, Terminals. Later, The Single UNIX Specification, Version 1 included the ioctl() function, but restricted it to control of STREAMS devices.
Although the POSIX.1-1988 solution for common classes of devices is the best from the point of view of application portability, there is still a need for a way to interact with special, or even common devices, for which developing a full standard API is not practical. The device control option standardized in POSIX.26 and now included in this standard is a general method for interfacing to the widest possible range of devices, through a new service to pass control information and commands between the application and the device drivers.
A driver for a special device will normally not be portable between POSIX implementations, but an application that uses such a driver can be made portable if all functions calling the driver are well defined and standardized. Users and integrators of realtime systems often add drivers for special devices, and a standardized function format for interfacing with these devices greatly simplifies this process.
and renumber the later sections.