示例#1
0
/*
 * Manually shut down a context.  This allows the caller to force destruction
 * of all connections and state associated with the context.
 */
void
robin_shutdown_context(robin_context context)
{
	struct robin_state *s, *s_n;
	robin_context dummy = context;
	robin_con con, con_n;
	int last = 0;

	if (context->refcount == 1)
		last = 1;

	/* uncount the user's context handle, then abort states */
	ROBIN_DEL_CONTEXT(dummy, context);

	/* context was just freed */
	if (last)
		return;

	/* shutdown all the connections, before touching the context */
	for (con = TAILQ_FIRST(&context->con.list);
			con != TAILQ_END(&context->con.list);
			con = con_n) {
		con_n = TAILQ_NEXT(con, entry);
		robin_shutdown_con(context, con);
	}

	/* abort all state entries still held */
	for (s = TAILQ_FIRST(&context->state);
			s != TAILQ_END(&context->state);
			s = s_n) {
		s_n = TAILQ_NEXT(s, entry);
		robin_state_abort(s, ROBIN_DISCONNECTED);
	}
}
示例#2
0
int tl_event_node_insert(struct tl_event_node * node) {
    struct tl_event_node * insertPos;
    tl_manage_t tm;

    assert(node);
    assert(node->m_event.m_tl);
    assert(node->m_event.m_tl->m_manage);

    tm = node->m_event.m_tl->m_manage;

    insertPos = TAILQ_FIRST(&tm->m_event_queue);
    while(insertPos != TAILQ_END(&tm->m_event_queue)
          && insertPos->m_execute_time <= node->m_execute_time)
    {
        insertPos = TAILQ_NEXT(insertPos, m_next);
    }

    if (insertPos == TAILQ_END(&tm->m_event_queue)) {
        TAILQ_INSERT_TAIL(&tm->m_event_queue, node, m_next);
    }
    else {
        TAILQ_INSERT_BEFORE(insertPos, node, m_next);
    }

    node->m_state = tl_event_node_state_in_event_queue;

    return 0;
}
示例#3
0
/*
 * Free the robin_con structure.  This does NOT free/shutdown the pigeon
 * control structure.  robin_do_shutdown() should be used to safely shutdown
 * and free without looping.
 *
 * XXX USERS SHOULD NEVER CALL THIS FUNCTION DIRECTLY XXX
 */
void
robin_free_con(robin_context context, robin_con con)
{
	robin_con p;
	struct robin_state *s, *s_n;

	/* Make sure the con is in this context's list */
	TAILQ_FOREACH(p, &context->con.list, entry)
		if (p == con)
			break;
	if (p == TAILQ_END(&context->con.list))
		return;

	/* set a destruction pointer to prevent looping */
	if (con->destroying)
		return;
	else
		con->destroying = 1;

	if (robin_verbose & ROBIN_LOG_CONNECT)
		log_debug("==] CON %p free", con);

	/* take out of the connection list early */
	TAILQ_REMOVE(&context->con.list, con, entry);

	/* unset the 'master' connection */
	if (con->context->con.master == con)
		con->context->con.master = NULL;

	/* abort STATE_CONN entries that match this connection */
	for (s = TAILQ_FIRST(&context->state);
			s != TAILQ_END(&context->state);
			s = s_n) {
		s_n = TAILQ_NEXT(s, entry);
		if (s->con == con) {
			/* unreference the connection always */
			s->con = NULL;
			/* remove the state sometimes */
			if ((s->state & STATE_CONN))
				robin_state_abort(s, ROBIN_DISCONNECTED);
			else
				robin_state_resend(s);
		}
	}
	dns_abort_byarg(con);

	ROBIN_DEL_CONTEXT(con->context, context);
	free_RobinHosts(&con->connect.hosts);
	evtimer_del(&con->connect.timer);

	free(con);
}
示例#4
0
/**
 * drm_vblank_off - disable vblank events on a CRTC
 * @dev: DRM device
 * @crtc: CRTC in question
 *
 * Caller must hold event lock.
 */
void drm_vblank_off(struct drm_device *dev, int crtc)
{
	struct drmevlist *list;
	struct drm_pending_event *ev, *tmp;
	struct drm_pending_vblank_event *vev;
	struct timeval now;
	unsigned int seq;

	mtx_enter(&dev->vbl_lock);
	vblank_disable_and_save(dev, crtc);
	wakeup(&dev->vbl_queue[crtc]);

	list = &dev->vbl_events;
	/* Send any queued vblank events, lest the natives grow disquiet */
	seq = drm_vblank_count_and_time(dev, crtc, &now);

	mtx_enter(&dev->event_lock);
	for (ev = TAILQ_FIRST(list); ev != TAILQ_END(list); ev = tmp) {
		tmp = TAILQ_NEXT(ev, link);

		vev = (struct drm_pending_vblank_event *)ev;

		if (vev->pipe != crtc)
			continue;
		DRM_DEBUG("Sending premature vblank event on disable: \
			  wanted %d, current %d\n",
			  vev->event.sequence, seq);
		TAILQ_REMOVE(list, ev, link);
		drm_vblank_put(dev, vev->pipe);
		send_vblank_event(dev, vev, seq, &now);
	}
	mtx_leave(&dev->event_lock);

	mtx_leave(&dev->vbl_lock);
}
示例#5
0
void
drm_handle_vblank_events(struct drm_device *dev, int crtc)
{
	struct drmevlist		*list;
	struct drm_pending_event	*ev, *tmp;
	struct drm_pending_vblank_event	*vev;
	struct timeval			 now;
	u_int				 seq;

	list = &dev->vblank->vb_crtcs[crtc].vbl_events;
	microtime(&now);
	seq = drm_vblank_count(dev, crtc);

	mtx_enter(&dev->event_lock);
	for (ev = TAILQ_FIRST(list); ev != TAILQ_END(list); ev = tmp) {
		tmp = TAILQ_NEXT(ev, link);

		vev = (struct drm_pending_vblank_event *)ev;

		if ((seq - vev->event.sequence) > (1 << 23))
			continue;
		DPRINTF("%s: got vblank event on crtc %d, value %d\n",
		    __func__, crtc, seq);
		
		vev->event.sequence = seq;
		vev->event.tv_sec = now.tv_sec;
		vev->event.tv_usec = now.tv_usec;
		drm_vblank_put(dev, crtc);
		TAILQ_REMOVE(list, ev, link);
		TAILQ_INSERT_TAIL(&ev->file_priv->evlist, ev, link);
		wakeup(&ev->file_priv->evlist);
		selwakeup(&ev->file_priv->rsel);
	}
	mtx_leave(&dev->event_lock);
}
示例#6
0
void mem_buffer_end(struct mem_buffer_pos * pos, struct mem_buffer * buffer) {
    assert(pos);
    assert(buffer);

    pos->m_buffer = buffer;
    pos->m_trunk = TAILQ_END(&buffer->m_trunks);
    pos->m_pos_in_trunk = 0;
}
示例#7
0
void
robin_free_context(robin_context context)
{
	struct robin_state *s, *s_n;
	robin_con con, con_n;

	/* manual decrement, this should be the last one */
	context->refcount--;
	if (context->refcount > 0) {
		log_debug("context has %u operations in progress", context->refcount);
		return;
	}

	/* abort all state entries still held */
	for (s = TAILQ_FIRST(&context->state);
			s != TAILQ_END(&context->state);
			s = s_n) {
		s_n = TAILQ_NEXT(s, entry);
		robin_state_abort(s, ROBIN_DISCONNECTED);
	}

	/* shutdown all the connections, before touching the context */
	for (con = TAILQ_FIRST(&context->con.list);
			con != TAILQ_END(&context->con.list);
			con = con_n) {
		con_n = TAILQ_NEXT(con, entry);
		robin_shutdown_con(context, con);
	}

	tb_free_error_table(context->et_list);

	/* free cell (containing hosts) after connections shut down */
	free_RobinCell(&context->cell);

	if (context->con.username)
		free(context->con.username);
	if (context->con.servname)
		free(context->con.servname);
	if (context->con.ccache)
		free(context->con.ccache);

	free(context);
}
示例#8
0
static void
send_pktq(struct pktq *pktq)
{
	struct pkt *pkt, *next;
	
	for (pkt = TAILQ_FIRST(pktq); pkt != TAILQ_END(pktq); pkt = next) {
		next = TAILQ_NEXT(pkt, pkt_next);
		TAILQ_REMOVE(pktq, pkt, pkt_next);
		send_pkt(pkt);
	}
}
示例#9
0
void mem_buffer_begin(struct mem_buffer_pos * pos, struct mem_buffer * buffer) {
    assert(pos);
    assert(buffer);

    pos->m_buffer = buffer;
    pos->m_pos_in_trunk = 0;

    pos->m_trunk = TAILQ_FIRST(&buffer->m_trunks);
    while(pos->m_trunk != TAILQ_END(&buffer->m_trunks) && pos->m_trunk->m_size <= 0) {
        pos->m_trunk = TAILQ_NEXT(pos->m_trunk, m_next);
    }
}
示例#10
0
/*
 * add a packet to a stream
 */
int stream_add(struct stream_object *so, struct log_header_packet *pck, char *buf)
{
   struct so_list *pl, *tmp;

   /* skip ack packet or zero lenght packet */
   if (pck->len == 0)
      return 0;

   /* the packet is good, add it */
   SAFE_CALLOC(pl, 1, sizeof(struct so_list));

   /* create the packet object */
   memcpy(&pl->po.L3.src, &pck->L3_src, sizeof(struct ip_addr));
   memcpy(&pl->po.L3.dst, &pck->L3_dst, sizeof(struct ip_addr));
   
   pl->po.L4.src = pck->L4_src;
   pl->po.L4.dst = pck->L4_dst;
   pl->po.L4.proto = pck->L4_proto;
  
   SAFE_CALLOC(pl->po.DATA.data, pck->len, sizeof(char));
   
   memcpy(pl->po.DATA.data, buf, pck->len);
   pl->po.DATA.len = pck->len;
   
   /* set the stream direction */

   /* this is the first packet in the stream */
   if (TAILQ_FIRST(&so->so_head) == TAILQ_END(&so->so_head)) {
      pl->side = STREAM_SIDE1;
      /* init the pointer to the first packet */
      so->side1.so_curr = pl;
      so->side2.so_curr = pl;
   /* check the previous one and set it accordingly */
   } else {
      tmp = TAILQ_LAST(&so->so_head, so_list_head);
      if (!ip_addr_cmp(&tmp->po.L3.src, &pl->po.L3.src))
         /* same direction */
         pl->side = tmp->side;
      else 
         /* change detected */
         pl->side = (tmp->side == STREAM_SIDE1) ? STREAM_SIDE2 : STREAM_SIDE1;
   }
      
   /* add to the queue */
   TAILQ_INSERT_TAIL(&so->so_head, pl, next);

   return pck->len;
}
示例#11
0
static void
fragroute_process(const struct pcap_pkthdr *hdr, void *buf, size_t len, void *arg)
{
    struct pktq pktq;
    struct pkt *pkt, *next;
    
    if ((pkt = pkt_new()) == NULL) {
        warn("pkt_new");
        return;
    }
    if (ETH_HDR_LEN + len > PKT_BUF_LEN) {
        warn("dropping oversized packet");
        return;
    }
    memcpy(pkt->pkt_data + ETH_HDR_LEN, buf, len);
    pkt->pkt_end = pkt->pkt_data + ETH_HDR_LEN + len;

    pkt_decorate(pkt);
    
    if (pkt->pkt_ip == NULL) {
        warn("dropping non-IP packet");
        return;
    }
    eth_pack_hdr(pkt->pkt_eth, ctx.dmac.addr_eth,
        ctx.smac.addr_eth, ETH_TYPE_IP);
    
    pkt->pkt_ip->ip_src = ctx.src.addr_ip;
    ip_checksum(pkt->pkt_ip, len);

    /* Forward this packet along as is. */
    if(ctx.dfile && 
       eth_send(ctx.eth, pkt->pkt_data, pkt->pkt_end - pkt->pkt_data) < 0)
        warn("eth_send");

    TAILQ_INIT(&pktq);
    TAILQ_INSERT_TAIL(&pktq, pkt, pkt_next);
    
    mod_apply(&pktq);

    for (pkt = TAILQ_FIRST(&pktq); pkt != TAILQ_END(&pktq); pkt = next) {
        next = TAILQ_NEXT(pkt, pkt_next);
        _resend_outgoing(pkt);
    }

}
示例#12
0
static int tl_manage_dispatch_event(tl_manage_t tm, int maxCount) {
    int rv = 0;
    int count = 0;

    for(; rv == 0
            && maxCount > 0
            && tm->m_action_begin_pos == tm->m_action_end_pos;
        --maxCount)
    {
        struct tl_event_node * node = TAILQ_FIRST(&tm->m_event_queue);

        if (node == TAILQ_END(tm->m_event_queue)
            || node->m_execute_time > tm->m_time_current)
        {
            break;
        }

        TAILQ_REMOVE(&tm->m_event_queue, node, m_next);
        node->m_state = tl_event_node_state_free;

        tl_event_do_dispatch(&node->m_event, rv);

        if (node->m_repeatCount > 0) {
            --node->m_repeatCount;
        }

        if (node->m_repeatCount != 0) {
            node->m_execute_time += node->m_span;
            if (tl_event_node_insert(node) != 0) {
                tl_event_node_free(node);
            }
            
        }
        else {
            tl_event_node_free(node);
        }

        tl_event_queue_clear(&tm->m_event_building_queue);

        ++count;
    }

    return rv == 0 ? count : rv;
}
示例#13
0
/*
 * Robin connection shutdown.  This is user-safe, and calls the internal robin
 * callbacks for the pigeon connection, which will internally call
 * robin_free_con().  If the pigeon has already been freed, or was never init'd
 */
void
robin_shutdown_con(robin_context context, robin_con con)
{
	robin_con p;

	if (robin_verbose & ROBIN_LOG_CONNECT)
		log_debug("==] CON %p shutdown", con);

	/* Make sure the con is in this context's list */
	TAILQ_FOREACH(p, &context->con.list, entry)
		if (p == con)
			break;
	if (p == TAILQ_END(&context->con.list))
		return;

	if (con->p)
		pigeon_shutdown(&con->p);
	else
		robin_free_con(context, con);
}
示例#14
0
int
tbsymbol_set(tbsymbol_head *head, const char *name, const char *value,
             int persist)
{
    tbsymbol *sym;

    TAILQ_FOREACH(sym, head, entry)
    if (strcmp(name, sym->name) == 0)
        break;

    if (sym != TAILQ_END(head)) {
        if (sym->persist == 1)
            return TBOX_EXISTS;
        else {
            free(sym->name);
            free(sym->value);
            TAILQ_REMOVE(head, sym, entry);
            free(sym);
        }
    }
    if ((sym = calloc(1, sizeof(*sym))) == NULL)
        return TBOX_NOMEM;

    if ((sym->name = strdup(name)) == NULL) {
        free(sym);
        return TBOX_NOMEM;
    }
    if ((sym->value = strdup(value)) == NULL) {
        free(sym->name);
        free(sym);
        return TBOX_NOMEM;
    }
    sym->used = 0;
    sym->persist = persist;
    TAILQ_INSERT_TAIL(head, sym, entry);
    return TBOX_SUCCESS;
}
示例#15
0
/*
 * search a pattern into the stream 
 * returns  - NULL if not found
 *          - the packet containing the string if found
 */
struct so_list * stream_search(struct stream_object *so, u_char *buf, size_t buflen, int mode)
{
   u_char *tmpbuf = NULL, *find;
   size_t offset = 0, len = 0;
   struct so_list *so_curr = NULL, *first;
   size_t po_off = 0;
   
   /* get the values into temp variable */
   switch (mode) {
      case STREAM_SIDE1:
         so_curr = so->side1.so_curr;
         po_off = so->side1.po_off;
         break;
      case STREAM_SIDE2:
         so_curr = so->side2.so_curr;
         po_off = so->side2.po_off;
         break;
   }

   first = so_curr;
   
   /* create the buffer from the current position to the end */ 
   for ( ; so_curr != TAILQ_END(so->so_head); so_curr = TAILQ_NEXT(so_curr, next)) {
     
      /* skip packet in the wrong side */
      if (so_curr->side != mode) {
         po_off = 0;
         continue;
      }
      
      if (so_curr == first)
         len += so_curr->po.DATA.len - po_off;
      else
         len += so_curr->po.DATA.len;
         
      
      SAFE_REALLOC(tmpbuf, len);
      
      /* 
       * add the packet to the end of the buffer 
       * containing the whole conversation 
       */
      if (so_curr == first)
         memcpy(tmpbuf, so_curr->po.DATA.data + po_off, so_curr->po.DATA.len - po_off);
      else
         memcpy(tmpbuf + len - so_curr->po.DATA.len, so_curr->po.DATA.data, so_curr->po.DATA.len);
   }

   /* the buffer is found in the conversation */
   if ((find = memmem(tmpbuf, len, buf, buflen)) != NULL) {
      offset = find - tmpbuf;
     
      SAFE_FREE(tmpbuf);

      /* move the stream pointers to the buffer found */
      stream_move(so, offset, SEEK_CUR, mode);

      switch (mode) {
         case STREAM_SIDE1:
            return so->side1.so_curr;
         case STREAM_SIDE2:
            return so->side2.so_curr;
      }
   } 

   SAFE_FREE(tmpbuf);
  
   return NULL;
}
示例#16
0
/*
 * move the pointers into the stream
 */
int stream_move(struct stream_object *so, size_t offset, int whence, int mode)
{
   size_t tmp_size = 0;
   size_t move = 0;
   
   struct so_list *so_curr = NULL;
   size_t po_off = 0;

   /* get the values into temp variable */
   switch (mode) {
      case STREAM_SIDE1:
         so_curr = so->side1.so_curr;
         po_off = so->side1.po_off;
         break;
      case STREAM_SIDE2:
         so_curr = so->side2.so_curr;
         po_off = so->side2.po_off;
         break;
   }

   /* no movement */
   if (offset == 0)
      return 0;
   
   /* 
    * the offest is calculated from the beginning,
    * so move to the first packet
    */
   if (whence == SEEK_SET) {
      so_curr = TAILQ_FIRST(&so->so_head);
      po_off = 0;
   }

   /* the other mode is SEEK_CUR */

   /* search the first packet matching the selected mode */
   while (so_curr->side != mode) {
      so_curr = TAILQ_NEXT(so_curr, next);
      /* don't go after the end of the stream */
      if (so_curr == TAILQ_END(&so->pl_head))
         return 0;
   }

   while (offset) {
      /* get the lenght to jump to in the current po */
      tmp_size = (so_curr->po.DATA.len - po_off < offset) ? so_curr->po.DATA.len - po_off : offset;

      /* update the offset */
      po_off += tmp_size;

      /* decrement the total offset by the packet lenght */
      offset -= tmp_size;

      /* update the total movement */
      move += tmp_size;

      /* we have reached the end of the packet, go to the next one */
      if (po_off == so_curr->po.DATA.len) {
         /* search the next packet matching the selected mode */
         do {
            /* don't go after the end of the stream */
            if (TAILQ_NEXT(so_curr, next) != TAILQ_END(&so->pl_head))
               so_curr = TAILQ_NEXT(so_curr, next);
            else
               goto move_end;
            
         } while (so_curr->side != mode);

         /* reset the offset for the packet */
         po_off = 0;
      }
   }
   
move_end:
   /* restore the value in the real stream object */
   switch (mode) {
      case STREAM_SIDE1:
         so->side1.so_curr = so_curr;
         so->side1.po_off = po_off;
         break;
      case STREAM_SIDE2:
         so->side2.so_curr = so_curr;
         so->side2.po_off = po_off;
         break;
   }
   
   return move;
}
示例#17
0
/*
 * Allocate physical memory from the given physical address range.
 * Called by DMA-safe memory allocation methods.
 */
int
_bus_dmamem_alloc_range(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
    bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
    int flags, paddr_t low, paddr_t high)
{
	paddr_t curaddr, lastaddr;
	struct vm_page *m;
	struct pglist mlist;
	int curseg, error;

#ifdef DEBUG_DMA
	printf("alloc_range: t=%p size=%lx align=%lx boundary=%lx segs=%p nsegs=%x rsegs=%p flags=%x lo=%lx hi=%lx\n",
	    t, size, alignment, boundary, segs, nsegs, rsegs, flags, low, high);
#endif	/* DEBUG_DMA */

	/* Always round the size. */
	size = round_page(size);

	TAILQ_INIT(&mlist);
	/*
	 * Allocate pages from the VM system.
	 */
	error = uvm_pglistalloc(size, low, high, alignment, boundary,
	    &mlist, nsegs, (flags & BUS_DMA_NOWAIT) == 0);
	if (error)
		return (error);

	/*
	 * Compute the location, size, and number of segments actually
	 * returned by the VM code.
	 */
	m = TAILQ_FIRST(&mlist);
	curseg = 0;
	lastaddr = segs[curseg].ds_addr = VM_PAGE_TO_PHYS(m);
	segs[curseg].ds_len = PAGE_SIZE;
#ifdef DEBUG_DMA
		printf("alloc: page %lx\n", lastaddr);
#endif	/* DEBUG_DMA */
	m = TAILQ_NEXT(m, pageq);

	for (; m != TAILQ_END(&mlist); m = TAILQ_NEXT(m, pageq)) {
		curaddr = VM_PAGE_TO_PHYS(m);
#ifdef DIAGNOSTIC
		if (curaddr < low || curaddr >= high) {
			printf("uvm_pglistalloc returned non-sensical"
			    " address 0x%lx\n", curaddr);
			panic("_bus_dmamem_alloc_range");
		}
#endif	/* DIAGNOSTIC */
#ifdef DEBUG_DMA
		printf("alloc: page %lx\n", curaddr);
#endif	/* DEBUG_DMA */
		if (curaddr == (lastaddr + PAGE_SIZE))
			segs[curseg].ds_len += PAGE_SIZE;
		else {
			curseg++;
			segs[curseg].ds_addr = curaddr;
			segs[curseg].ds_len = PAGE_SIZE;
		}
		lastaddr = curaddr;
	}

	*rsegs = curseg + 1;

	return (0);
}
示例#18
0
/*
 * Allocate physical memory from the given physical address range.
 * Called by DMA-safe memory allocation methods.
 */
int
_dmamem_alloc_range(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
    bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
    int flags, paddr_t low, paddr_t high)
{
	paddr_t curaddr, lastaddr;
	vm_page_t m;
	struct pglist mlist;
	int curseg, error, plaflag;

	/* Always round the size. */
	size = round_page(size);

	/*
	 * Allocate pages from the VM system.
	 */
	plaflag = flags & BUS_DMA_NOWAIT ? UVM_PLA_NOWAIT : UVM_PLA_WAITOK;
	if (flags & BUS_DMA_ZERO)
		plaflag |= UVM_PLA_ZERO;

	TAILQ_INIT(&mlist);
	error = uvm_pglistalloc(size, low, high, alignment, boundary,
	    &mlist, nsegs, plaflag);
	if (error)
		return (error);

	/*
	 * Compute the location, size, and number of segments actually
	 * returned by the VM code.
	 */
	m = TAILQ_FIRST(&mlist);
	curseg = 0;
	lastaddr = segs[curseg].ds_addr =
	    (*t->_pa_to_device)(VM_PAGE_TO_PHYS(m));
	segs[curseg].ds_len = PAGE_SIZE;
	m = TAILQ_NEXT(m, pageq);

	for (; m != TAILQ_END(&mlist); m = TAILQ_NEXT(m, pageq)) {
		curaddr = VM_PAGE_TO_PHYS(m);
#ifdef DIAGNOSTIC
		if (curaddr < low || curaddr >= high) {
			printf("vm_page_alloc_memory returned non-sensical"
			    " address 0x%lx\n", curaddr);
			panic("_dmamem_alloc_range");
		}
#endif
		curaddr = (*t->_pa_to_device)(curaddr);
		if (curaddr == (lastaddr + PAGE_SIZE))
			segs[curseg].ds_len += PAGE_SIZE;
		else {
			curseg++;
			segs[curseg].ds_addr = curaddr;
			segs[curseg].ds_len = PAGE_SIZE;
		}
		lastaddr = curaddr;
	}

	*rsegs = curseg + 1;

	return (0);
}
示例#19
0
/*
 * read data from the stream
 * mode can be: 
 *    STREAM_SIDE1 reads only from the first side (usually client to server)
 *    STREAM_SIDE2 reads only from the other side
 */
int stream_read(struct stream_object *so, u_char *buf, size_t size, int mode)
{
   size_t buf_off = 0;
   size_t tmp_size = 0;
   
   struct so_list *so_curr = NULL;
   size_t po_off = 0;

   /* get the values into temp variable */
   switch (mode) {
      case STREAM_SIDE1:
         so_curr = so->side1.so_curr;
         po_off = so->side1.po_off;
         break;
      case STREAM_SIDE2:
         so_curr = so->side2.so_curr;
         po_off = so->side2.po_off;
         break;
   }
   
   /* search the first packet matching the selected mode */
   while (so_curr->side != mode) {
      so_curr = TAILQ_NEXT(so_curr, next);
      /* don't go after the end of the stream */
      if (so_curr == TAILQ_END(&so->pl_head))
         return 0;
   }

   /* size is decremented while it is copied in the buffer */
   while (size) {

      /* get the size to be copied from the current po */
      tmp_size = (so_curr->po.DATA.len - po_off < size) ? so_curr->po.DATA.len - po_off : size;

      /* fill the buffer */
      memcpy(buf + buf_off, so_curr->po.DATA.data + po_off, tmp_size);
      
      /* the offset is the portion of the data copied into the buffer */
      po_off += tmp_size;

      /* update the pointer into the buffer */
      buf_off += tmp_size;

      /* decrement the total size to be copied */
      size -= tmp_size;
      
      /* we have reached the end of the packet, go to the next one */
      if (po_off == so_curr->po.DATA.len) {
         /* search the next packet matching the selected mode */
         do {
            /* don't go after the end of the stream */
            if (TAILQ_NEXT(so_curr, next) != TAILQ_END(&so->pl_head))
               so_curr = TAILQ_NEXT(so_curr, next);
            else
               goto read_end;
            
         } while (so_curr->side != mode);

         /* reset the offset for the packet */
         po_off = 0;
      }
   }
  
read_end:   
   /* restore the value in the real stream object */
   switch (mode) {
      case STREAM_SIDE1:
         so->side1.so_curr = so_curr;
         so->side1.po_off = po_off;
         break;
      case STREAM_SIDE2:
         so->side2.so_curr = so_curr;
         so->side2.po_off = po_off;
         break;
   }
  
   /* return the total byte read */
   return buf_off;
}