Neither the transparent nor nontransparent method implements ioctl(2) in the Stream head, but in the Streams driver or module itself. I_STR ioctl(2) (also referred to as nontransparent ioctl(2)) is created when a user requests an I_STR ioctl(2) and specifies a pointer to a strioctl structure as the argument. For example, assuming that fd is an open lp Streams device and LP_CRLF is a valid option, the user could make a request by issuing the following:
struct strioctl *str; short lp_opt = LP_CRLF; str.ic_cmd = SET_OPTIONS; str.ic_timout = -1; str.ic_dp = (char *)&lp_opt; str.ic_len = sizeof (lp_opt) ioctl(fd, I_STR, &str);
On receipt of the I_STR ioctl(2) request, the Stream head creates an M_IOCTL message. ioc_cmd is set to SET_OPTIONS, ioc_count is set to the value contained in ic_len (in this example sizeof (short)). An M_DATA mblk is linked to the M_IOCTL mblk and the data pointed to by ic_dp is copied into it (in this case LP_CRLF).
Example 8-6, illustrates processing associated with an I_STR ioctl(2). lpdoioctl is called by lp write-side put or service procedure to process M_IOCTL messages:
static void lpdoioctl (queue_t *q, mblk_t *mp) { struct iocblk *iocp; struct lp *lp; lp = (struct lp *)q->q_ptr; /* 1st block contains iocblk structure */ iocp = (struct iocblk *)mp->b_rptr; switch (iocp->ioc_cmd) { case SET_OPTIONS: /* Count should be exactly one short's worth * (for this example) */ if (iocp->ioc_count != sizeof(short)) goto iocnak; if (mp->b_cont == NULL) goto lognak; /* not shown in this example */ /* Actual data is in 2nd message block */ iocp->ioc_error = lpsetopt (lp, *(short *)mp->b_cont->b_rptr) /* ACK the ioctl */ mp->b_datap->db_type = M_IOCACK; iocp->ioc_count = 0; qreply(q, mp); break; default: iocnak: /* NAK the ioctl */ mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); } }
lpdoioctl illustrates driver M_IOCTL processing, which also applies to modules. In this example, only one command is recognized, SET_OPTIONS. ioc_count contains the number of user-supplied data bytes. For this example, ioc_count must equal the size of a short.
Once the command has been verified [lines 20-24], lpsetopt (not shown here) is called to process the request [lines 26-27]. lpsetopt returns 0 if the request is satisfied, otherwise an error number is returned.
If ioc_error is nonzero, on receipt of the acknowledgment the Stream head returns -1 to the application's ioctl(2) request and sets errno to the value of ioc_error. The ioctl(2) is acknowledged [lines 30-33). This includes changing the M_IOCTL message type to M_IOCACK and setting the ioc_count field to zero to indicate that no data is to be returned to the user. Finally, the message is sent upstream using qreply(9F).
If ioc_count was left nonzero, the Stream head would copy that many bytes from the second through the nth message blocks into the user buffer. You must set ioc_count if you want to pass any data back to the user.
This example is for a driver. In the default case, for unrecognized commands, or for malformed requests, a nak is generated [lines 34-38). This is done by changing the message type to an M_IOCNAK and sending it back up stream. A module does not acknowledge (nak) an unrecognized command, but passes the message on. A module does not acknowledge (nak) a malformed request.