⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 horizon.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 5 页
字号:
  rx_queue_entry_flags = rx_queue_entry & (RX_CRC_32_OK|RX_COMPLETE_FRAME|SIMONS_DODGEY_MARKER);    if (!rx_len) {    // (at least) bus-mastering breaks if we try to handle a    // zero-length frame, besides AAL5 does not support them    PRINTK (KERN_ERR, "zero-length frame!");    rx_queue_entry_flags &= ~RX_COMPLETE_FRAME;  }    if (rx_queue_entry_flags & SIMONS_DODGEY_MARKER) {    PRINTD (DBG_RX|DBG_ERR, "Simon's marker detected!");  }  if (rx_queue_entry_flags == (RX_CRC_32_OK | RX_COMPLETE_FRAME)) {    struct atm_vcc * atm_vcc;        PRINTD (DBG_RX, "got a frame on rx_channel %x len %u", rx_channel, rx_len);        atm_vcc = dev->rxer[rx_channel];    // if no vcc is assigned to this channel, we should drop the frame    // (is this what SIMONS etc. was trying to achieve?)        if (atm_vcc) {            if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {		if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {	    	  struct sk_buff * skb = atm_alloc_charge (atm_vcc, rx_len, GFP_ATOMIC);	  if (skb) {	    // remember this so we can push it later	    dev->rx_skb = skb;	    // remember this so we can flush it later	    dev->rx_channel = rx_channel;	    	    // prepare socket buffer	    skb_put (skb, rx_len);	    ATM_SKB(skb)->vcc = atm_vcc;	    	    // simple transfer	    // dev->rx_regions = 0;	    // dev->rx_iovec = 0;	    dev->rx_bytes = rx_len;	    dev->rx_addr = skb->data;	    PRINTD (DBG_RX, "RX start simple transfer (addr %p, len %d)",		    skb->data, rx_len);	    	    // do the business	    rx_schedule (dev, 0);	    return;	    	  } else {	    PRINTD (DBG_SKB|DBG_WARN, "failed to get skb");	  }	  	} else {	  PRINTK (KERN_INFO, "frame received on TX-only VC %x", rx_channel);	  // do we count this?	}	      } else {	PRINTK (KERN_WARNING, "dropped over-size frame");	// do we count this?      }          } else {      PRINTD (DBG_WARN|DBG_VCC|DBG_RX, "no VCC for this frame (VC closed)");      // do we count this?    }      } else {    // Wait update complete ? SPONG  }    // RX was aborted  YELLOW_LED_ON(dev);    FLUSH_RX_CHANNEL (dev,rx_channel);  clear_bit (rx_busy, &dev->flags);    return;}/********** interrupt handler **********/static void interrupt_handler (int irq, void * dev_id, struct pt_regs * pt_regs) {  hrz_dev * dev = hrz_devs;  u32 int_source;  unsigned int irq_ok;  (void) pt_regs;    PRINTD (DBG_FLOW, "interrupt_handler: %p", dev_id);    if (!dev_id) {    PRINTD (DBG_IRQ|DBG_ERR, "irq with NULL dev_id: %d", irq);    return;  }  // Did one of our cards generate the interrupt?  while (dev) {    if (dev == dev_id)      break;    dev = dev->prev;  }  if (!dev) {    PRINTD (DBG_IRQ, "irq not for me: %d", irq);    return;  }  if (irq != dev->irq) {    PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq);    return;  }    // definitely for us  irq_ok = 0;  while ((int_source = rd_regl (dev, INT_SOURCE_REG_OFF)	  & INTERESTING_INTERRUPTS)) {    // In the interests of fairness, the (inline) handlers below are    // called in sequence and without immediate return to the head of    // the while loop. This is only of issue for slow hosts (or when    // debugging messages are on). Really slow hosts may find a fast    // sender keeps them permanently in the IRQ handler. :(        // (only an issue for slow hosts) RX completion goes before    // rx_data_av as the former implies rx_busy and so the latter    // would just abort. If it reschedules another transfer    // (continuing the same frame) then it will not clear rx_busy.        // (only an issue for slow hosts) TX completion goes before RX    // data available as it is a much shorter routine - there is the    // chance that any further transfers it schedules will be complete    // by the time of the return to the head of the while loop        if (int_source & RX_BUS_MASTER_COMPLETE) {      ++irq_ok;      PRINTD (DBG_IRQ|DBG_BUS|DBG_RX, "rx_bus_master_complete asserted");      rx_bus_master_complete_handler (dev);    }    if (int_source & TX_BUS_MASTER_COMPLETE) {      ++irq_ok;      PRINTD (DBG_IRQ|DBG_BUS|DBG_TX, "tx_bus_master_complete asserted");      tx_bus_master_complete_handler (dev);    }    if (int_source & RX_DATA_AV) {      ++irq_ok;      PRINTD (DBG_IRQ|DBG_RX, "rx_data_av asserted");      rx_data_av_handler (dev);    }  }  if (irq_ok) {    PRINTD (DBG_IRQ, "work done: %u", irq_ok);  } else {    PRINTD (DBG_IRQ|DBG_WARN, "spurious interrupt source: %#x", int_source);  }    PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);}/********** housekeeping **********/static void set_timer (struct timer_list * timer, unsigned int delay) {  timer->expires = jiffies + delay;  add_timer (timer);  return;}static void do_housekeeping (unsigned long arg) {  // just stats at the moment  hrz_dev * dev = hrz_devs;  (void) arg;  // data is set to zero at module unload  if (housekeeping.data) {    while (dev) {      // collect device-specific (not driver/atm-linux) stats here      dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF);      dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF);      dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF);      dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF);      dev = dev->prev;    }    set_timer (&housekeeping, HZ/10);  }  return;}/********** find an idle channel for TX and set it up **********/// called with tx_busy setstatic inline 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];    u16 rd_ptr;    u16 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;}/********** send a frame **********/static int hrz_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) {  unsigned int spin_count;  int free_buffers;  hrz_dev * dev = HRZ_DEV(atm_vcc->dev);  hrz_vcc * vcc = HRZ_VCC(atm_vcc);  u16 channel = vcc->channel;    u32 buffers_required;    /* signed for error return */  short tx_channel;    PRINTD (DBG_FLOW|DBG_TX, "hrz_send vc %x data %p len %u",	  channel, skb->data, skb->len);    dump_skb (">>>", channel, skb);    if (atm_vcc->qos.txtp.traffic_class == ATM_NONE) {    PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", channel);    hrz_kfree_skb (skb);    return -EIO;  }    // don't understand this  ATM_SKB(skb)->vcc = atm_vcc;    if (skb->len > atm_vcc->qos.txtp.max_sdu) {    PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping...");    hrz_kfree_skb (skb);    return -EIO;  }    if (!channel) {    PRINTD (DBG_ERR|DBG_TX, "attempt to transmit on zero (rx_)channel");    hrz_kfree_skb (skb);    return -EIO;  }  #if 0  {    // where would be a better place for this? housekeeping?    u16 status;    pci_read_config_word (dev->pci_dev, PCI_STATUS, &status);    if (status & PCI_STATUS_REC_MASTER_ABORT) {      PRINTD (DBG_BUS|DBG_ERR, "Clearing PCI Master Abort (and cleaning up)");      status &= ~PCI_STATUS_REC_MASTER_ABORT;      pci_write_config_word (dev->pci_dev, PCI_STATUS, status);      if (test_bit (tx_busy, &dev->flags)) {	hrz_kfree_skb (dev->tx_skb);	tx_release (dev);      }    }  }#endif  #ifdef DEBUG_HORIZON  /* wey-hey! */  if (channel == 1023) {    unsigned int i;    unsigned short d = 0;    char * s = skb->data;    if (*s++ == 'D') {      for (i = 0; i < 4; ++i) {	d = (d<<4) | ((*s <= '9') ? (*s - '0') : (*s - 'a' + 10));	++s;      }      PRINTK (KERN_INFO, "debug bitmap is now %hx", debug = d);    }  }#endif    // wait until TX is free and grab lock  if (tx_hold (dev)) {    hrz_kfree_skb (skb);    return -ERESTARTSYS;  }   // Wait for enough space to be available in transmit buffer memory.    // should be number of cells needed + 2 (according to hardware docs)  // = ((framelen+8)+47) / 48 + 2  // = (framelen+7) / 48 + 3, hmm... faster to put addition inside XXX  buffers_required = (skb->len+(ATM_AAL5_TRAILER-1)) / ATM_CELL_PAYLOAD + 3;    // replace with timer and sleep, add dev->tx_buffers_queue (max 1 entry)  spin_count = 0;  while ((free_buffers = rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF)) < buffers_required) {    PRINTD (DBG_TX, "waiting for free TX buffers, got %d of %d",	    free_buffers, buffers_required);    // what is the appropriate delay? implement a timeout? (depending on line speed?)    // mdelay (1);    // what happens if we kill (current_pid, SIGKILL) ?    schedule();    if (++spin_count > 1000) {      PRINTD (DBG_TX|DBG_ERR, "spun out waiting for tx buffers, got %d of %d",	      free_buffers, buffers_required);      tx_release (dev);      hrz_kfree_skb (skb);      return -ERESTARTSYS;    }  }    // Select a channel to transmit the frame on.  if (channel == dev->last_vc) {    PRINTD (DBG_TX, "last vc hack: hit");    tx_channel = dev->tx_last;  } else {    PRINTD (DBG_TX, "last vc hack: miss");    // Are we currently transmitting this VC on one of the channels?    for (tx_channel = 0; tx_channel < TX_CHANS; ++tx_channel)      if (dev->tx_channel_record[tx_channel] == channel) {	PRINTD (DBG_TX, "vc already on channel: hit");	break;      }    if (tx_channel == TX_CHANS) {       PRINTD (DBG_TX, "vc already on channel: miss");      // Find and set up an idle channel.      tx_channel = setup_idle_tx_channel (dev, vcc);      if (tx_channel < 0) {	PRINTD (DBG_TX|DBG_ERR, "failed to get channel");	tx_release (dev);	return tx_channel;      }    }        PRINTD (DBG_TX, "got channel");    SELECT_TX_CHANNEL(dev, tx_channel);        dev->last_vc = channel;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -