📄 eni.c
字号:
for (i = 0; i < j; i++) { writel(dma[i*2],eni_dev->rx_dma+dma_wr*8); writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*8+4); dma_wr = (dma_wr+1) & (NR_DMA_RX-1); } if (skb) { ENI_PRV_POS(skb) = eni_vcc->descr+size+1; skb_queue_tail(&eni_dev->rx_queue,skb); eni_vcc->last = skb;rx_enqueued++; } eni_vcc->descr = here; eni_out(dma_wr,MID_DMA_WR_RX); return 0;trouble: if (paddr) pci_unmap_single(eni_dev->pci_dev,paddr,skb->len, PCI_DMA_FROMDEVICE); if (skb) dev_kfree_skb_irq(skb); return -1;}static void discard(struct atm_vcc *vcc,unsigned long size){ struct eni_vcc *eni_vcc; eni_vcc = ENI_VCC(vcc); EVENT("discard (size=%ld)\n",size,0); while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0); /* could do a full fallback, but that might be more expensive */ if (eni_vcc->rxing) ENI_PRV_POS(eni_vcc->last) += size+1; else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1);}/* * TODO: should check whether direct copies (without DMA setup, dequeuing on * interrupt, etc.) aren't much faster for AAL0 */static int rx_aal0(struct atm_vcc *vcc){ struct eni_vcc *eni_vcc; unsigned long descr; unsigned long length; struct sk_buff *skb; DPRINTK(">rx_aal0\n"); eni_vcc = ENI_VCC(vcc); descr = readl(eni_vcc->recv+eni_vcc->descr*4); if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { rx_ident_err(vcc); return 1; } if (descr & MID_RED_T) { DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", vcc->dev->number); length = 0; atomic_inc(&vcc->stats->rx_err); } else { length = ATM_CELL_SIZE-1; /* no HEC */ } skb = length ? atm_alloc_charge(vcc,length,GFP_ATOMIC) : NULL; if (!skb) { discard(vcc,length >> 2); return 0; } skb_put(skb,length); skb->stamp = eni_vcc->timestamp; DPRINTK("got len %ld\n",length); if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1; eni_vcc->rxing++; return 0;}static int rx_aal5(struct atm_vcc *vcc){ struct eni_vcc *eni_vcc; unsigned long descr; unsigned long size,eff,length; struct sk_buff *skb; EVENT("rx_aal5\n",0,0); DPRINTK(">rx_aal5\n"); eni_vcc = ENI_VCC(vcc); descr = readl(eni_vcc->recv+eni_vcc->descr*4); if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { rx_ident_err(vcc); return 1; } if (descr & (MID_RED_T | MID_RED_CRC_ERR)) { if (descr & MID_RED_T) { EVENT("empty cell (descr=0x%lx)\n",descr,0); DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", vcc->dev->number); size = 0; } else { static unsigned long silence = 0; if (time_after(jiffies, silence) || silence == 0) { printk(KERN_WARNING DEV_LABEL "(itf %d): " "discarding PDU(s) with CRC error\n", vcc->dev->number); silence = (jiffies+2*HZ)|1; } size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr, size); } eff = length = 0; atomic_inc(&vcc->stats->rx_err); } else { size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); DPRINTK("size=%ld\n",size); length = readl(eni_vcc->recv+(((eni_vcc->descr+size-1) & (eni_vcc->words-1)))*4) & 0xffff; /* -trailer(2)+header(1) */ if (length && length <= (size << 2)-8 && length <= ATM_MAX_AAL5_PDU) eff = (length+3) >> 2; else { /* ^ trailer length (8) */ EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr, length); printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU " "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n", vcc->dev->number,vcc->vci,length,size << 2,descr); length = eff = 0; atomic_inc(&vcc->stats->rx_err); } } skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL; if (!skb) { discard(vcc,size); return 0; } skb_put(skb,length); DPRINTK("got len %ld\n",length); if (do_rx_dma(vcc,skb,1,size,eff)) return 1; eni_vcc->rxing++; return 0;}static inline int rx_vcc(struct atm_vcc *vcc){ unsigned long vci_dsc,tmp; struct eni_vcc *eni_vcc; eni_vcc = ENI_VCC(vcc); vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*16; EVENT("rx_vcc(1)\n",0,0); while (eni_vcc->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT)) { EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n", eni_vcc->descr,tmp); DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT)); if (ENI_VCC(vcc)->rx(vcc)) return 1; } /* clear IN_SERVICE flag */ writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc); /* * If new data has arrived between evaluating the while condition and * clearing IN_SERVICE, we wouldn't be notified until additional data * follows. So we have to loop again to be sure. */ EVENT("rx_vcc(3)\n",0,0); while (ENI_VCC(vcc)->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT)) { EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n", eni_vcc->descr,tmp); DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT)); if (ENI_VCC(vcc)->rx(vcc)) return 1; } return 0;}static void poll_rx(struct atm_dev *dev){ struct eni_dev *eni_dev; struct atm_vcc *curr; eni_dev = ENI_DEV(dev); while ((curr = eni_dev->fast)) { EVENT("poll_rx.fast\n",0,0); if (rx_vcc(curr)) return; eni_dev->fast = ENI_VCC(curr)->next; ENI_VCC(curr)->next = ENI_VCC_NOS; barrier(); ENI_VCC(curr)->servicing--; } while ((curr = eni_dev->slow)) { EVENT("poll_rx.slow\n",0,0); if (rx_vcc(curr)) return; eni_dev->slow = ENI_VCC(curr)->next; ENI_VCC(curr)->next = ENI_VCC_NOS; barrier(); ENI_VCC(curr)->servicing--; }}static void get_service(struct atm_dev *dev){ struct eni_dev *eni_dev; struct atm_vcc *vcc; unsigned long vci; DPRINTK(">get_service\n"); eni_dev = ENI_DEV(dev); while (eni_in(MID_SERV_WRITE) != eni_dev->serv_read) { vci = readl(eni_dev->service+eni_dev->serv_read*4); eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1); vcc = eni_dev->rx_map[vci & 1023]; if (!vcc) { printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not " "found\n",dev->number,vci); continue; /* nasty but we try to go on anyway */ /* @@@ nope, doesn't work */ } EVENT("getting from service\n",0,0); if (ENI_VCC(vcc)->next != ENI_VCC_NOS) { EVENT("double service\n",0,0); DPRINTK("Grr, servicing VCC %ld twice\n",vci); continue; } ENI_VCC(vcc)->timestamp = xtime; ENI_VCC(vcc)->next = NULL; if (vcc->qos.rxtp.traffic_class == ATM_CBR) { if (eni_dev->fast) ENI_VCC(eni_dev->last_fast)->next = vcc; else eni_dev->fast = vcc; eni_dev->last_fast = vcc; } else { if (eni_dev->slow) ENI_VCC(eni_dev->last_slow)->next = vcc; else eni_dev->slow = vcc; eni_dev->last_slow = vcc; }putting++; ENI_VCC(vcc)->servicing++; }}static void dequeue_rx(struct atm_dev *dev){ struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; struct atm_vcc *vcc; struct sk_buff *skb; unsigned long vci_dsc; int first; eni_dev = ENI_DEV(dev); first = 1; while (1) { skb = skb_dequeue(&eni_dev->rx_queue); if (!skb) { if (first) { DPRINTK(DEV_LABEL "(itf %d): RX but not " "rxing\n",dev->number); EVENT("nothing to dequeue\n",0,0); } break; } EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb), ENI_PRV_POS(skb));rx_dequeued++; vcc = ATM_SKB(skb)->vcc; eni_vcc = ENI_VCC(vcc); first = 0; vci_dsc = eni_dev->vci+vcc->vci*16; if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb), (readl(vci_dsc+4) & MID_VCI_READ) >> MID_VCI_READ_SHIFT, eni_vcc->words)) { EVENT("requeuing\n",0,0); skb_queue_head(&eni_dev->rx_queue,skb); break; } eni_vcc->rxing--; eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1); pci_unmap_single(eni_dev->pci_dev,ENI_PRV_PADDR(skb),skb->len, PCI_DMA_TODEVICE); if (!skb->len) dev_kfree_skb_irq(skb); else { EVENT("pushing (len=%ld)\n",skb->len,0); if (vcc->qos.aal == ATM_AAL0) *(unsigned long *) skb->data = ntohl(*(unsigned long *) skb->data); memset(skb->cb,0,sizeof(struct eni_skb_prv)); vcc->push(vcc,skb); pushed++; } atomic_inc(&vcc->stats->rx); } wake_up(&eni_dev->rx_wait);}static int open_rx_first(struct atm_vcc *vcc){ struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; unsigned long size; DPRINTK("open_rx_first\n"); eni_dev = ENI_DEV(vcc->dev); eni_vcc = ENI_VCC(vcc); eni_vcc->rx = NULL; if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0; size = vcc->qos.rxtp.max_sdu*eni_dev->rx_mult/100; if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <= MID_MAX_BUF_SIZE) size = MID_MAX_BUF_SIZE; eni_vcc->recv = eni_alloc_mem(eni_dev,&size); DPRINTK("rx at 0x%lx\n",eni_vcc->recv); eni_vcc->words = size >> 2; if (!eni_vcc->recv) return -ENOBUFS; eni_vcc->rx = vcc->qos.aal == ATM_AAL5 ? rx_aal5 : rx_aal0; eni_vcc->descr = 0; eni_vcc->rx_pos = 0; eni_vcc->rxing = 0; eni_vcc->servicing = 0; eni_vcc->next = ENI_VCC_NOS; return 0;}static int open_rx_second(struct atm_vcc *vcc){ unsigned long here; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; unsigned long size; int order; DPRINTK("open_rx_second\n"); eni_dev = ENI_DEV(vcc->dev); eni_vcc = ENI_VCC(vcc); if (!eni_vcc->rx) return 0; /* set up VCI descriptor */ here = eni_dev->vci+vcc->vci*16; DPRINTK("loc 0x%x\n",(unsigned) (eni_vcc->recv-eni_dev->ram)/4); size = eni_vcc->words >> 8; for (order = -1; size; order++) size >>= 1; writel(0,here+4); /* descr, read = 0 */ writel(0,here+8); /* write, state, count = 0 */ if (eni_dev->rx_map[vcc->vci]) printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already " "in use\n",vcc->dev->number,vcc->vci); eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */ writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) << MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE | (((eni_vcc->recv-eni_dev->ram) >> (MID_LOC_SKIP+2)) << MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT),here); return 0;}static void close_rx(struct atm_vcc *vcc){ DECLARE_WAITQUEUE(wait,current); unsigned long here; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; eni_vcc = ENI_VCC(vcc); if (!eni_vcc->rx) return; eni_dev = ENI_DEV(vcc->dev); if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) { here = eni_dev->vci+vcc->vci*16; /* block receiver */ writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH << MID_VCI_MODE_SHIFT),here); /* wait for receiver to become idle */ udelay(27); /* discard pending cell */ writel(readl(here) & ~MID_VCI_IN_SERVICE,here); /* don't accept any new ones */ eni_dev->rx_map[vcc->vci] = NULL; /* wait for RX queue to drain */ DPRINTK("eni_close: waiting for RX ...\n"); EVENT("RX closing\n",0,0); add_wait_queue(&eni_dev->rx_wait,&wait); set_current_state(TASK_UNINTERRUPTIBLE); barrier(); for (;;) { /* transition service->rx: rxing++, servicing-- */ if (!eni_vcc->servicing) { barrier(); if (!eni_vcc->rxing) break; } EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing, eni_vcc->servicing); printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing, eni_vcc->rxing); schedule(); set_current_state(TASK_UNINTERRUPTIBLE); } for (;;) { int at_end; u32 tmp; tasklet_disable(&eni_dev->task); tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ; at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT; tasklet_enable(&eni_dev->task); if (at_end) break; EVENT("drain discard (host 0x%lx, nic 0x%lx)\n", eni_vcc->rx_pos,tmp); printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n", eni_vcc->rx_pos,tmp); schedule(); set_current_state(TASK_UNINTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&eni_dev->rx_wait,&wait); } eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2); eni_vcc->rx = NULL;}static int start_rx(struct atm_dev *dev){ struct eni_dev *eni_dev; eni_dev = ENI_DEV(dev); eni_dev->rx_map = (struct atm_vcc **) get_free_page(GFP_KERNEL); if (!eni_dev->rx_map) { printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", dev->number); free_page((unsigned long) eni_dev->free_list); return -ENOMEM; } memset(eni_dev->rx_map,0,PAGE_SIZE); eni_dev->rx_mult = DEFAULT_RX_MULT; eni_dev->fast = eni_dev->last_fast = NULL; eni_dev->slow = eni_dev->last_slow = NULL; init_waitqueue_head(&eni_dev->rx_wait); skb_queue_head_init(&eni_dev->rx_queue); eni_dev->serv_read = eni_in(MID_SERV_WRITE); eni_out(0,MID_DMA_WR_RX); return 0;}/*----------------------------------- TX ------------------------------------*/enum enq_res { enq_ok,enq_next,enq_jam };static inline void put_dma(int chan,u32 *dma,int *j,dma_addr_t paddr, u32 size){ u32 init,words; DPRINTK("put_dma: 0x%lx+0x%x\n",(unsigned long) paddr,size);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -