Does the second block have the proper amount of data?
Is the "to" device in range?
Is the "to" device open?
Is the current stream disconnected?
Is the "to" stream disconnected?
If these checks pass, the read queue pointers for the two streams are
stored in the respective oqptr fields. This cross-connects
the two streams indirectly, through loop_loop.
The put procedure incorporates canonical flush handling.
loopwput queues all other messages (for example, M_DATA or M_PROTO) for processing by its service procedure. A check is made that the stream is connected.
If not, M_ERROR is sent to the stream head. Certain message types can be sent upstream by drivers and modules to the
stream head where they are translated into actions detectable by user processes.
These messages may also modify the state of the stream head:
M_ERROR | Causes
the stream head to lock up. Message transmission between stream and user processes
is terminated. All subsequent system calls except close(2) and poll(2) fail. Also causes M_FLUSH, clearing all message queues, to be sent downstream by the
stream head.
|
M_HANGUP | Terminates
input from a user process to the stream. All subsequent system calls that
would send messages downstream fail. Once the stream head read message queue
is empty, EOF is returned on reads. This can also result
in SIGHUP being sent to the process group's session leader.
|
M_SIG/M_PCSIG | Causes a specified signal to be sent to the process group
associated with the stream.
|
putnextctl(9F) and putnextctl1(9F) allocate a nondata (that is, not M_DATA, M_DELAY, M_PROTO,
or M_PCPROTO) type message, place one byte in the message
(for putnextctl1(9F)),
and call the put(9E)
procedure of the specified queue.
Example 9-12 Use of ioctl to Copy Data From User Space to Kernel
Space
static int loopwput(queue_t *q, mblk_t *mp)
{
struct loop *loop;
int to;
loop = (struct loop *)q->q_ptr;
switch (mp->b_datap->db_type) {
case M_IOCTL: {
struct iocblk *iocp;
int error=0;
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case LOOP_SET: {
/*
* if this is a transparent ioctl return an error;
* the complete solution is to convert the message
* into an M_COPYIN message so that the data is
* ultimately copied from user space
* to kernel space.
*/
if (iocp->ioc_count == TRANSPARENT) {
error = EINVAL;
goto iocnak;
}
/* fetch other minor device number */
to = *(int *)mp->b_cont->b_rptr;
/*
* Sanity check. ioc_count contains the amount
* of user supplied data which must equal the
* size of an int.
*/
if (iocp->ioc_count != sizeof(int)) {
error = EINVAL;
goto iocnak;
}
/* Is the minor device number in range? */
if (to >= loop_cnt || to < 0) {
error = ENXIO;
goto iocnak;
}
/* Is the other device open? */
if (!loop_loop[to].qptr) {
error = ENXIO;
goto iocnak;
}
/* Check if either dev is currently connected */
if (loop->oqptr || loop_loop[to].oqptr) {
error = EBUSY;
goto iocnak;
}
/* Cross connect the streams through
* the loopstruct
*/
loop->oqptr = RD(loop_loop[to].qptr);
loop_loop[to].oqptr = RD(q);
/*
* Return successful ioctl. Set ioc_count
* to zero, since no data is returned.
*/
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
qreply(q, mp);
break;
}
default:
error = EINVAL;
iocnak:
/*
* Bad ioctl. Setting ioc_error causes
* the ioctl call to return that particular errno.
* By default, ioctl returns EINVAL on failure.
*/
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = error;
qreply(q, mp);
break;
}
break;
}
case M_FLUSH: {
if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHALL); /* write */
if (loop->oqptr)
flushq(loop->oqptr, FLUSHALL);
/* read on other side equals write on this side */
}
if (*mp->b_rptr & FLUSHR) {
flushq(RD(q), FLUSHALL);
if (loop->oqptr != NULL)
flushq(WR(loop->oqptr), FLUSHALL);
}
switch(*mp->b_rptr) {
case FLUSHW:
*mp->b_rptr = FLUSHR;
break;
case FLUSHR:
*mp->b_rptr = FLUSHW;
break;
}
if (loop->oqptr != NULL)
(void) putnext(loop->oqptr, mp);
break;
}
default: /* If this Stream isn't connected, send
* M_ERROR upstream.
*/
if (loop->oqptr == NULL) {
freemsg(mp);
(void) putnextctl1(RD(q), M_ERROR, ENXIO);
break;
}
(void) putq(q, mp);
}
return (0);
}
|