V9/sys/net/packet.c

Compare this file to the similar file:
Show the results in this format:

/*
 *	       P A C K E T
 *
 * Utilities for working with "packets".  These are groups of (stream) data
 * blocks terminated by a delimiter.  When these blocks are removed from a
 * queue, they are kept as a chain of blocks, without the final M_DELIM, and
 * are referred to as a dequeued packet.
 *
 *
 * Written by Kurt Gollhardt  (Nirvonics, Inc.)
 * Last update Wed Feb 12 21:24:10 1986
 *
 */

#include "../h/param.h"
#include "../h/stream.h"

#define PKT_COMPLETE	 0
#define PKT_PARTIAL	 1


/*
 * This routine does all the work for a server on a queue using packets.
 * Arguments provide routines to do the application-specific operations:
 *   'blocked' is called with a pointer to the queue;
 *	  it should return non-zero if the location which the first
 *	  packet on the queue would be sent to is full.  If it does
 *	  return non-zero, it must ensure that this queue will be
 *	  re-enabled when the blocking condition goes away.
 *   'allow_partial' is called with a pointer to the queue;
 *	  it should return non-zero if this queue is allowed to receive
 *	  partial packets at any time.  (Any queue may be given partial
 *	  packets if not doing so would cause a deadlock.)
 *   'process' is called with a pointer to the queue, a dequeued
 *	  packet (struct block *), and a flag which is non-zero if
 *	  this is a partial packet.
 */
packet_srvp(q, delim_count, blocked, allow_partial, process, other)
     register struct queue    *q;	/* queue being serviced */
     int	    *delim_count;	/* ptr to count of delims on queue */
     int	    (*blocked)();	/* routine to see if a queue blocks */
     int	    (*allow_partial)();	/* routine to say if partial pkts ok */
     int	    (*process)();	/* routine to process a packet */
     int	    (*other)();		/* routine to handle misc blocks */
{
     register struct block    *bp, *first_bp, *prev_bp;
     register int	 processed = 0;
     static check_other();

     check_other(q, other);
     while (*delim_count > 0) {
	  if ((*blocked) (q))
	       return;
	  if ((bp = getq(q))->type == M_DELIM)
	       first_bp = NULL;
	  else {
	       first_bp = prev_bp = bp;
	       while ((bp = getq(q))->type != M_DELIM)
		    prev_bp = bp;
	       prev_bp->next = NULL;
	  }
	  freeb(bp);
	  (*process) (q, first_bp, PKT_COMPLETE);
	  --*delim_count;
	  ++processed;
	  check_other(q, other);
     }

     if (q->first != NULL) {
	  if ((*allow_partial) (q) || /* Huge partial packet */
		    ((q->flag & QWANTW) && processed == 0)) {
	       if ((*blocked) (q))
		    return;
	       first_bp = q->first;
	       while (getq(q))
		    ;
	       (*process) (q, first_bp, PKT_PARTIAL);
	  } else
	       q->flag |= QWANTR;
     }
}

static check_other(q, other)
     register struct queue    *q;
     int	    (*other)();
{
     register struct block    *bp;

     while (q->first && q->first->type != M_DATA && q->first->type != M_DELIM) {
	  bp = getq(q);
	  if (other)
	       (*other)(q, bp);
	  else
	       freeb(bp);
     }
}


/*
 * This routine does all the work for a put routine on a queue using packets.
 * Arguments provide routines to do the application-specific operations:
 *   'ioctl' (optional) is called with a pointer to the queue, and a pointer
 *	  to an M_IOCTL block.
 *   'other' (optional) is called with a pointer to the queue, and a pointer
 *	  to a block of type other than M_DATA, M_DELIM, or M_IOCTL.
 */
packet_putp(q, bp, delim_count, ioctl, other)
     register struct queue    *q;	/* queue being put onto */
     register struct block    *bp;	/* block being put on */
     int	    *delim_count;	/* ptr to count of delims on queue */
     int	    (*ioctl)();		/* routine to handle an ioctl */
     int	    (*other)();		/* routine to handle misc blocks */
{
     register int   ps, (*func)();

     switch (bp->type) {
	  case M_DATA:
	       putq(q, bp);
	       return;
	  case M_DELIM:
	       ps = spl6();
	       putq(q, bp);
	       ++*delim_count;
	       qenable(q);
	       splx(ps);
	       return;
	  case M_IOCTL:
	       func = ioctl;
	       break;
	  default:
	       func = other;
     }

     if (func)
	  (*func) (q, bp);
     else if (q->next)
	  (*q->next->qinfo->putp) (q->next, bp);
     else
	  freeb(bp);
}


#define BLEN(bp)    ((bp)->wptr - (bp)->rptr)
#define BSZ(bp)	    ((bp)->lim - (bp)->base)
#undef MIN
#define MIN(a,b)    ((a) > (b) ? (b) : (a))

/*
 * This routine takes a dequeued packet and a length.
 * It makes sure that the specified length of data appears in the first
 * block of the packet, if possible.
 */
struct block *packet_pullup(bp, len)
     register struct block    *bp;	/* the dequeued packet */
     unsigned	    len;		/* desired length */
			 /* returns the new packet (the old one is invalid) */
{
     register struct block    *nbp;
     register unsigned	      count;


     if (bp == NULL)
          goto bad;
     if (BLEN(bp) >= len)
          return bp;

     nbp = allocb(len);
     if (nbp == NULL || BSZ(nbp) < len) {
	  free_blocks(bp);
          goto cleanup;
     }
     nbp->next = bp;

     while (bp && bp->type == M_DATA) {
	  count = BSZ(nbp) - BLEN(nbp);	     /* space left in new block */
          count = MIN(count, len);	     /* clip to desired length */
	  count = MIN(count, BLEN(bp));	     /* clip to length of this block */
          bcopy(bp->rptr, nbp->wptr, count);
          len -= count;
          bp->rptr += count;
          nbp->wptr += count;
          if (BLEN(bp) > 0)
               break;
          nbp->next = bp->next;
          freeb(bp);
	  bp = nbp->next;
     }

     if (len == 0)
          return nbp;

cleanup:
     free_blocks(nbp);
bad:
     printf("packet_pullup bad\n");
     return NULL;
}


/*
 * Free space taken by a dequeued packet.
 */
free_blocks(bp)
     register struct block    *bp;	/* dequeued packet */
{
     register struct block    *nbp;

     while (bp) {
	  nbp = bp->next;
	  freeb(bp);
	  bp = nbp;
     }
}


/*
 * This routine puts blocks from a dequeued packet onto a queue's successor.
 */
packet_put(q, bp)
     register struct queue    *q;	/* Note that we put to q->next */
     register struct block    *bp;
{
     register struct block    *bnext;

     while (bp != NULL) {     
	  bnext = bp->next;
	  if (bp->rptr >= bp->wptr)
	       freeb(bp);
	  else
	       (*q->next->qinfo->putp) (q->next, bp);
	  bp = bnext;
     }
     if (q->flag & QDELIM)
	  putctl(q->next, M_DELIM);
}