static short setup_idle_tx_channel()

in horizon.c [1424:1544]


static short setup_idle_tx_channel (hrz_dev * dev, hrz_vcc * vcc) {
  unsigned short idle_channels;
  short tx_channel = -1;
  unsigned int spin_count;
  PRINTD (DBG_FLOW|DBG_TX, "setup_idle_tx_channel %p", dev);
  
  // better would be to fail immediately, the caller can then decide whether
  // to wait or drop (depending on whether this is UBR etc.)
  spin_count = 0;
  while (!(idle_channels = rd_regw (dev, TX_STATUS_OFF) & IDLE_CHANNELS_MASK)) {
    PRINTD (DBG_TX|DBG_WARN, "waiting for idle TX channel");
    // delay a bit here
    if (++spin_count > 100) {
      PRINTD (DBG_TX|DBG_ERR, "spun out waiting for idle TX channel");
      return -EBUSY;
    }
  }
  
  // got an idle channel
  {
    // tx_idle ensures we look for idle channels in RR order
    int chan = dev->tx_idle;
    
    int keep_going = 1;
    while (keep_going) {
      if (idle_channels & (1<<chan)) {
	tx_channel = chan;
	keep_going = 0;
      }
      ++chan;
      if (chan == TX_CHANS)
	chan = 0;
    }
    
    dev->tx_idle = chan;
  }
  
  // set up the channel we found
  {
    // Initialise the cell header in the transmit channel descriptor
    // a.k.a. prepare the channel and remember that we have done so.
    
    tx_ch_desc * tx_desc = &memmap->tx_descs[tx_channel];
    u32 rd_ptr;
    u32 wr_ptr;
    u16 channel = vcc->channel;
    
    unsigned long flags;
    spin_lock_irqsave (&dev->mem_lock, flags);
    
    // Update the transmit channel record.
    dev->tx_channel_record[tx_channel] = channel;
    
    // xBR channel
    update_tx_channel_config (dev, tx_channel, RATE_TYPE_ACCESS,
			      vcc->tx_xbr_bits);
    
    // Update the PCR counter preload value etc.
    update_tx_channel_config (dev, tx_channel, PCR_TIMER_ACCESS,
			      vcc->tx_pcr_bits);

#if 0
    if (vcc->tx_xbr_bits == VBR_RATE_TYPE) {
      // SCR timer
      update_tx_channel_config (dev, tx_channel, SCR_TIMER_ACCESS,
				vcc->tx_scr_bits);
      
      // Bucket size...
      update_tx_channel_config (dev, tx_channel, BUCKET_CAPACITY_ACCESS,
				vcc->tx_bucket_bits);
      
      // ... and fullness
      update_tx_channel_config (dev, tx_channel, BUCKET_FULLNESS_ACCESS,
				vcc->tx_bucket_bits);
    }
#endif

    // Initialise the read and write buffer pointers
    rd_ptr = rd_mem (dev, &tx_desc->rd_buf_type) & BUFFER_PTR_MASK;
    wr_ptr = rd_mem (dev, &tx_desc->wr_buf_type) & BUFFER_PTR_MASK;
    
    // idle TX channels should have identical pointers
    if (rd_ptr != wr_ptr) {
      PRINTD (DBG_TX|DBG_ERR, "TX buffer pointers are broken!");
      // spin_unlock... return -E...
      // I wonder if gcc would get rid of one of the pointer aliases
    }
    PRINTD (DBG_TX, "TX buffer pointers are: rd %x, wr %x.",
	    rd_ptr, wr_ptr);
    
    switch (vcc->aal) {
      case aal0:
	PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal0");
	rd_ptr |= CHANNEL_TYPE_RAW_CELLS;
	wr_ptr |= CHANNEL_TYPE_RAW_CELLS;
	break;
      case aal34:
	PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal34");
	rd_ptr |= CHANNEL_TYPE_AAL3_4;
	wr_ptr |= CHANNEL_TYPE_AAL3_4;
	break;
      case aal5:
	rd_ptr |= CHANNEL_TYPE_AAL5;
	wr_ptr |= CHANNEL_TYPE_AAL5;
	// Initialise the CRC
	wr_mem (dev, &tx_desc->partial_crc, INITIAL_CRC);
	break;
    }
    
    wr_mem (dev, &tx_desc->rd_buf_type, rd_ptr);
    wr_mem (dev, &tx_desc->wr_buf_type, wr_ptr);
    
    // Write the Cell Header
    // Payload Type, CLP and GFC would go here if non-zero
    wr_mem (dev, &tx_desc->cell_header, channel);
    
    spin_unlock_irqrestore (&dev->mem_lock, flags);
  }
  
  return tx_channel;
}