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 + -
显示快捷键?