ambassador.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,331 行 · 第 1/5 页
C
2,331 行
/********** TX completion **********/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); do_gettimeofday(&skb->stamp); // 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; unsigned long timeout; 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)); // prepare to wait for cq->pending milliseconds // effectively one centisecond on i386 timeout = (cq->pending*HZ+999)/1000; if (cq->pending > cq->high) cq->high = cq->pending; spin_unlock (&cq->lock); while (timeout) { // go to sleep // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout); set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout (timeout); } // 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, int 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 - 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, struct pt_regs *pt_regs) { amb_dev * dev = amb_devs; (void) pt_regs; PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id); if (!dev_id) { PRINTD (DBG_IRQ|DBG_ERR, "irq with NULL dev_id: %d", irq); return IRQ_NONE; } // Did one of our cards generate the interrupt? while (dev) { if (dev == dev_id) break; dev = dev->prev; } // impossible - unless we add the device to our list after both // registering the IRQ handler for it and enabling interrupts, AND // the card generates an IRQ at startup - should not happen again if (!dev) { PRINTD (DBG_IRQ, "irq for unknown device: %d", irq); return IRQ_NONE; } // impossible - unless we have memory corruption of dev or kernel if (irq != dev->irq) { PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq); return IRQ_NONE; } { 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"); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?