int (*devctl) (resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb )
struct _io_devctl { uint16_t type; uint16_t combine_len; int32_t dcmd; uint32_t nbytes; int32_t zero; }; struct _io_devctl_reply { uint32_t zero; int32_t ret_val; uint32_t nbytes; int32_t zero2; }; typedef union { struct _io_devctl i; struct _io_devctl_reply o; } io_devctl_t;
to field | from field | Meaning |
---|---|---|
0 | 0 | No data transfer |
0 | 1 | Transfer from driver to client |
1 | 0 | Transfer from client to driver |
1 | 1 | Transfer bidirectionally |
In the case of no data transfer, the driver is expected to simply perform the command given in dcmd.
If the client provides data, it immediately follows the io_devctl_t header provided by the msg parameter. Before you access any of the data, it is necessary to validate its length.
To be strictly correct, the length of the message from the client is ctp->size if ctp->rcvid is 0; otherwise, it is (ctp->info.srcmsglen – ctp->offset). In practice, you can simplify length checking based on whether your handler will deal with short messages only:
1) If the handler will deal with short messages only (less than 1500 bytes), you can always use ctp->size. This type of handler is slightly simpler.
2) If the handler has to handle large messages, you can always use (ctp->info.srcmsglen – ctp->offset).
If your handler supports multiple device control operations, you might have operations from both categories.
You need to verify that the actual message length is at least as large as the expected length. If you need to access any part of the message to determine the expected size, you need to ensure that ctp->size is large enough to include this data.
After you’ve verified the message length, you need to access the data. If your device control operation is handling short messages only (category 1 above), the message payload is contained entirely in the message buffer. If your operation has to handle large messages (category 2), you probably have to use resmgr_msgreadv() to read at least part of the payload.
If you can, before you perform a request, you should validate that the amount of data that will be returned plus the size of the io_devctl_t header is less than or equal to ctp->info.dstmsglen. Otherwise, the client will not receive all the data that you send. Although, it is conventional to set nbytes in the io_devctl_t output header to the length of the payload being returned, nbytes is not accessible to a client using devctl() or devctlv(). If the amount of data your device control operation returns is variable, it is a good idea that the returned data include the size or a means to calculate it.
Note that the input and output data structures are zero-padded so that they align with each other. This means that the implicit data area begins at the same address in the input and output structures.
It's up to your function to check the open mode against the operation; no checking is done anywhere in either the client's devctl() library or in the resource manager library. For example, it's possible to open a resource manager read-only and then issue a devctl() to it telling it to format the hard disk (which is very much a write operation). It would be prudent to verify the open mode first before proceeding with the operation.
For more information on checking the security and validity of messages, see Permission checking.
Note that the range of dcmd values you can use is limited (0x0000 through 0x0FFF inclusive is reserved for QNX Software Systems). Other values may be in use; take a look through the include files that have the name <sys/dcmd_*.h>.
For an example, take a look at A simple device control I/O function handler example, below.
The default implementation iofunc_devctl_default() handler does not perform permission checking because this handler does not implement any functionality that needs protecting. A resource manager that implements devctl() must apply permission checking according to the command to be executed. This is complicated because of the flexible nature of devctl() operations. For example, when the baud rate of a serial device is changed, it is acceptable to open the device for read-only access and then perform a devctl() to change the baud rate. Although this operation performs a modification, it does not require the file descriptor to have been opened for write. Consequently, each individual devctl() command often needs to have appropriate permission checks.
Possible methods for dealing with devctl() include the following strategies:
Enforce a global permission check based on the file open mode. For example, it might be acceptable for a resource manager to insist that no devctl() operation be performed at all if the OCB has not been opened for write access, as shown in the following code (placed at the beginning of the devctl() handler function):
if (!(ocb->ioflag & _IO_FLAG_WR)) { return EPERM; }
if ( (ocb->ioflag & _IO_FLAG_RD) == 0 ) { return EPERM; }
For an example handler that includes permission and length checking, see A simple device control I/O function handler example.