Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
9.  STREAMS Drivers STREAMS Driver Code Samples Loop-Around Driver  Previous   Contents   Next 
   
 
  • 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);
}

 
 
 
  Previous   Contents   Next