ambassador.c
来自「linux 内核源代码」· C语言 代码 · 共 2,334 行 · 第 1/5 页
C
2,334 行
static inline void tx_complete (amb_dev * dev, tx_out * tx) { tx_simple * tx_descr = bus_to_virt (tx->handle); struct sk_buff * skb = tx_descr->skb; PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx); // VC layer stats atomic_inc(&ATM_SKB(skb)->vcc->stats->tx); // free the descriptor kfree (tx_descr); // free the skb amb_kfree_skb (skb); dev->stats.tx_ok++; return;}/********** RX completion **********/static void rx_complete (amb_dev * dev, rx_out * rx) { struct sk_buff * skb = bus_to_virt (rx->handle); u16 vc = be16_to_cpu (rx->vc); // unused: u16 lec_id = be16_to_cpu (rx->lec_id); u16 status = be16_to_cpu (rx->status); u16 rx_len = be16_to_cpu (rx->length); PRINTD (DBG_FLOW|DBG_RX, "rx_complete %p %p (len=%hu)", dev, rx, rx_len); // XXX move this in and add to VC stats ??? if (!status) { struct atm_vcc * atm_vcc = dev->rxer[vc]; dev->stats.rx.ok++; if (atm_vcc) { if (rx_len <= atm_vcc->qos.rxtp.max_sdu) { if (atm_charge (atm_vcc, skb->truesize)) { // prepare socket buffer ATM_SKB(skb)->vcc = atm_vcc; skb_put (skb, rx_len); dump_skb ("<<<", vc, skb); // VC layer stats atomic_inc(&atm_vcc->stats->rx); __net_timestamp(skb); // end of our responsability atm_vcc->push (atm_vcc, skb); return; } else { // someone fix this (message), please! PRINTD (DBG_INFO|DBG_RX, "dropped thanks to atm_charge (vc %hu, truesize %u)", vc, skb->truesize); // drop stats incremented in atm_charge } } else { PRINTK (KERN_INFO, "dropped over-size frame"); // should we count this? atomic_inc(&atm_vcc->stats->rx_drop); } } else { PRINTD (DBG_WARN|DBG_RX, "got frame but RX closed for channel %hu", vc); // this is an adapter bug, only in new version of microcode } } else { dev->stats.rx.error++; if (status & CRC_ERR) dev->stats.rx.badcrc++; if (status & LEN_ERR) dev->stats.rx.toolong++; if (status & ABORT_ERR) dev->stats.rx.aborted++; if (status & UNUSED_ERR) dev->stats.rx.unused++; } dev_kfree_skb_any (skb); return;}/* Note on queue handling. Here "give" and "take" refer to queue entries and a queue (pair) rather than frames to or from the host or adapter. Empty frame buffers are given to the RX queue pair and returned unused or containing RX frames. TX frames (well, pointers to TX fragment lists) are given to the TX queue pair, completions are returned. *//********** command queue **********/// I really don't like this, but it's the best I can do at the moment// also, the callers are responsible for byte order as the microcode// sometimes does 16-bit accesses (yuk yuk yuk)static int command_do (amb_dev * dev, command * cmd) { amb_cq * cq = &dev->cq; volatile amb_cq_ptrs * ptrs = &cq->ptrs; command * my_slot; PRINTD (DBG_FLOW|DBG_CMD, "command_do %p", dev); if (test_bit (dead, &dev->flags)) return 0; spin_lock (&cq->lock); // if not full... if (cq->pending < cq->maximum) { // remember my slot for later my_slot = ptrs->in; PRINTD (DBG_CMD, "command in slot %p", my_slot); dump_command (cmd); // copy command in *ptrs->in = *cmd; cq->pending++; ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit); // mail the command wr_mem (dev, offsetof(amb_mem, mb.adapter.cmd_address), virt_to_bus (ptrs->in)); if (cq->pending > cq->high) cq->high = cq->pending; spin_unlock (&cq->lock); // these comments were in a while-loop before, msleep removes the loop // go to sleep // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout); msleep(cq->pending); // wait for my slot to be reached (all waiters are here or above, until...) while (ptrs->out != my_slot) { PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out); set_current_state(TASK_UNINTERRUPTIBLE); schedule(); } // wait on my slot (... one gets to its slot, and... ) while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) { PRINTD (DBG_CMD, "wait: command slot completion"); set_current_state(TASK_UNINTERRUPTIBLE); schedule(); } PRINTD (DBG_CMD, "command complete"); // update queue (... moves the queue along to the next slot) spin_lock (&cq->lock); cq->pending--; // copy command out *cmd = *ptrs->out; ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit); spin_unlock (&cq->lock); return 0; } else { cq->filled++; spin_unlock (&cq->lock); return -EAGAIN; } }/********** TX queue pair **********/static inline int tx_give (amb_dev * dev, tx_in * tx) { amb_txq * txq = &dev->txq; unsigned long flags; PRINTD (DBG_FLOW|DBG_TX, "tx_give %p", dev); if (test_bit (dead, &dev->flags)) return 0; spin_lock_irqsave (&txq->lock, flags); if (txq->pending < txq->maximum) { PRINTD (DBG_TX, "TX in slot %p", txq->in.ptr); *txq->in.ptr = *tx; txq->pending++; txq->in.ptr = NEXTQ (txq->in.ptr, txq->in.start, txq->in.limit); // hand over the TX and ring the bell wr_mem (dev, offsetof(amb_mem, mb.adapter.tx_address), virt_to_bus (txq->in.ptr)); wr_mem (dev, offsetof(amb_mem, doorbell), TX_FRAME); if (txq->pending > txq->high) txq->high = txq->pending; spin_unlock_irqrestore (&txq->lock, flags); return 0; } else { txq->filled++; spin_unlock_irqrestore (&txq->lock, flags); return -EAGAIN; }}static inline int tx_take (amb_dev * dev) { amb_txq * txq = &dev->txq; unsigned long flags; PRINTD (DBG_FLOW|DBG_TX, "tx_take %p", dev); spin_lock_irqsave (&txq->lock, flags); if (txq->pending && txq->out.ptr->handle) { // deal with TX completion tx_complete (dev, txq->out.ptr); // mark unused again txq->out.ptr->handle = 0; // remove item txq->pending--; txq->out.ptr = NEXTQ (txq->out.ptr, txq->out.start, txq->out.limit); spin_unlock_irqrestore (&txq->lock, flags); return 0; } else { spin_unlock_irqrestore (&txq->lock, flags); return -1; }}/********** RX queue pairs **********/static inline int rx_give (amb_dev * dev, rx_in * rx, unsigned char pool) { amb_rxq * rxq = &dev->rxq[pool]; unsigned long flags; PRINTD (DBG_FLOW|DBG_RX, "rx_give %p[%hu]", dev, pool); spin_lock_irqsave (&rxq->lock, flags); if (rxq->pending < rxq->maximum) { PRINTD (DBG_RX, "RX in slot %p", rxq->in.ptr); *rxq->in.ptr = *rx; rxq->pending++; rxq->in.ptr = NEXTQ (rxq->in.ptr, rxq->in.start, rxq->in.limit); // hand over the RX buffer wr_mem (dev, offsetof(amb_mem, mb.adapter.rx_address[pool]), virt_to_bus (rxq->in.ptr)); spin_unlock_irqrestore (&rxq->lock, flags); return 0; } else { spin_unlock_irqrestore (&rxq->lock, flags); return -1; }}static inline int rx_take (amb_dev * dev, unsigned char pool) { amb_rxq * rxq = &dev->rxq[pool]; unsigned long flags; PRINTD (DBG_FLOW|DBG_RX, "rx_take %p[%hu]", dev, pool); spin_lock_irqsave (&rxq->lock, flags); if (rxq->pending && (rxq->out.ptr->status || rxq->out.ptr->length)) { // deal with RX completion rx_complete (dev, rxq->out.ptr); // mark unused again rxq->out.ptr->status = 0; rxq->out.ptr->length = 0; // remove item rxq->pending--; rxq->out.ptr = NEXTQ (rxq->out.ptr, rxq->out.start, rxq->out.limit); if (rxq->pending < rxq->low) rxq->low = rxq->pending; spin_unlock_irqrestore (&rxq->lock, flags); return 0; } else { if (!rxq->pending && rxq->buffers_wanted) rxq->emptied++; spin_unlock_irqrestore (&rxq->lock, flags); return -1; }}/********** RX Pool handling **********//* pre: buffers_wanted = 0, post: pending = 0 */static inline void drain_rx_pool (amb_dev * dev, unsigned char pool) { amb_rxq * rxq = &dev->rxq[pool]; PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pool %p %hu", dev, pool); if (test_bit (dead, &dev->flags)) return; /* we are not quite like the fill pool routines as we cannot just remove one buffer, we have to remove all of them, but we might as well pretend... */ if (rxq->pending > rxq->buffers_wanted) { command cmd; cmd.request = cpu_to_be32 (SRB_FLUSH_BUFFER_Q); cmd.args.flush.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT); while (command_do (dev, &cmd)) schedule(); /* the pool may also be emptied via the interrupt handler */ while (rxq->pending > rxq->buffers_wanted) if (rx_take (dev, pool)) schedule(); } return;}static void drain_rx_pools (amb_dev * dev) { unsigned char pool; PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pools %p", dev); for (pool = 0; pool < NUM_RX_POOLS; ++pool) drain_rx_pool (dev, pool);}static inline void fill_rx_pool (amb_dev * dev, unsigned char pool, gfp_t priority){ rx_in rx; amb_rxq * rxq; PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pool %p %hu %x", dev, pool, priority); if (test_bit (dead, &dev->flags)) return; rxq = &dev->rxq[pool]; while (rxq->pending < rxq->maximum && rxq->pending < rxq->buffers_wanted) { struct sk_buff * skb = alloc_skb (rxq->buffer_size, priority); if (!skb) { PRINTD (DBG_SKB|DBG_POOL, "failed to allocate skb for RX pool %hu", pool); return; } if (check_area (skb->data, skb->truesize)) { dev_kfree_skb_any (skb); return; } // cast needed as there is no %? for pointer differences PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li", skb, skb->head, (long) (skb_end_pointer(skb) - skb->head)); rx.handle = virt_to_bus (skb); rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); if (rx_give (dev, &rx, pool)) dev_kfree_skb_any (skb); } return;}// top up all RX pools (can also be called as a bottom half)static void fill_rx_pools (amb_dev * dev) { unsigned char pool; PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pools %p", dev); for (pool = 0; pool < NUM_RX_POOLS; ++pool) fill_rx_pool (dev, pool, GFP_ATOMIC); return;}/********** enable host interrupts **********/static inline void interrupts_on (amb_dev * dev) { wr_plain (dev, offsetof(amb_mem, interrupt_control), rd_plain (dev, offsetof(amb_mem, interrupt_control)) | AMB_INTERRUPT_BITS);}/********** disable host interrupts **********/static inline void interrupts_off (amb_dev * dev) { wr_plain (dev, offsetof(amb_mem, interrupt_control), rd_plain (dev, offsetof(amb_mem, interrupt_control)) &~ AMB_INTERRUPT_BITS);}/********** interrupt handling **********/static irqreturn_t interrupt_handler(int irq, void *dev_id) { amb_dev * dev = dev_id; PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id); { u32 interrupt = rd_plain (dev, offsetof(amb_mem, interrupt)); // for us or someone else sharing the same interrupt if (!interrupt) { PRINTD (DBG_IRQ, "irq not for me: %d", irq); return IRQ_NONE; } // definitely for us PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt); wr_plain (dev, offsetof(amb_mem, interrupt), -1); } { unsigned int irq_work = 0; unsigned char pool; for (pool = 0; pool < NUM_RX_POOLS; ++pool) while (!rx_take (dev, pool)) ++irq_work; while (!tx_take (dev)) ++irq_work; if (irq_work) {#ifdef FILL_RX_POOLS_IN_BH schedule_work (&dev->bh);#else fill_rx_pools (dev);#endif PRINTD (DBG_IRQ, "work done: %u", irq_work); } else { PRINTD (DBG_IRQ|DBG_WARN, "no work done"); } } PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id); return IRQ_HANDLED;}/********** make rate (not quite as much fun as Horizon) **********/static int make_rate (unsigned int rate, rounding r, u16 * bits, unsigned int * actual) { unsigned char exp = -1; // hush gcc unsigned int man = -1; // hush gcc PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate); // rates in cells per second, ITU format (nasty 16-bit floating-point) // given 5-bit e and 9-bit m: // rate = EITHER (1+m/2^9)*2^e OR 0 // bits = EITHER 1<<14 | e<<9 | m OR 0 // (bit 15 is "reserved", bit 14 "non-zero") // smallest rate is 0 (special representation) // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1) // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0) // simple algorithm: // find position of top bit, this gives e // remove top bit and shift (rounding if feeling clever) by 9-e // ucode bug: please don't set bit 14! so 0 rate not representable if (rate > 0xffc00000U) { // larger than largest representable rate
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?