📄 horizon.c
字号:
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 + -