📄 eni.c
字号:
EVENT("put_dma: 0x%lx+0x%lx\n",(unsigned long) paddr,size);#if 0 /* don't complain anymore */ if (paddr & 3) printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr); if (size & 3) printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size);#endif if (paddr & 3) { init = 4-(paddr & 3); if (init > size || size < 7) init = size; DPRINTK("put_dma: %lx DMA: %d/%d bytes\n", (unsigned long) paddr,init,size); dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += init; size -= init; } words = size >> 2; size &= 3; if (words && (paddr & 31)) { init = 8-((paddr & 31) >> 2); if (init > words) init = words; DPRINTK("put_dma: %lx DMA: %d/%d words\n", (unsigned long) paddr,init,words); dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += init << 2; words -= init; }#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */ if (words & ~15) { DPRINTK("put_dma: %lx DMA: %d*16/%d words\n", (unsigned long) paddr,words >> 4,words); dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += (words & ~15) << 2; words &= 15; }#endif#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */ if (words & ~7) { DPRINTK("put_dma: %lx DMA: %d*8/%d words\n", (unsigned long) paddr,words >> 3,words); dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += (words & ~7) << 2; words &= 7; }#endif#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */ if (words & ~3) { DPRINTK("put_dma: %lx DMA: %d*4/%d words\n", (unsigned long) paddr,words >> 2,words); dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += (words & ~3) << 2; words &= 3; }#endif#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */ if (words & ~1) { DPRINTK("put_dma: %lx DMA: %d*2/%d words\n", (unsigned long) paddr,words >> 1,words); dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += (words & ~1) << 2; words &= 1; }#endif if (words) { DPRINTK("put_dma: %lx DMA: %d words\n",(unsigned long) paddr, words); dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; paddr += words << 2; } if (size) { DPRINTK("put_dma: %lx DMA: %d bytes\n",(unsigned long) paddr, size); dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) | (chan << MID_DMA_CHAN_SHIFT); dma[(*j)++] = paddr; }}static enum enq_res do_tx(struct sk_buff *skb){ struct atm_vcc *vcc; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; struct eni_tx *tx; dma_addr_t paddr; u32 dma_rd,dma_wr; u32 size; /* in words */ int aal5,dma_size,i,j; DPRINTK(">do_tx\n"); NULLCHECK(skb); EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len); vcc = ATM_SKB(skb)->vcc; NULLCHECK(vcc); eni_dev = ENI_DEV(vcc->dev); NULLCHECK(eni_dev); eni_vcc = ENI_VCC(vcc); tx = eni_vcc->tx; NULLCHECK(tx);#if 0 /* Enable this for testing with the "align" program */ { unsigned int hack = *((char *) skb->data)-'0'; if (hack < 8) { skb->data += hack; skb->len -= hack; } }#endif#if 0 /* should work now */ if ((unsigned long) skb->data & 3) printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned " "TX data\n",vcc->dev->number,vcc->vci);#endif /* * Potential future IP speedup: make hard_header big enough to put * segmentation descriptor directly into PDU. Saves: 4 slave writes, * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-) */ aal5 = vcc->qos.aal == ATM_AAL5; /* check space in buffer */ if (!aal5) size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE; /* cell without HEC plus segmentation header (includes four-byte cell header) */ else { size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1; /* add AAL5 trailer */ size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE; /* add segmentation header */ } /* * Can I move tx_pos by size bytes without getting closer than TX_GAP * to the read pointer ? TX_GAP means to leave some space for what * the manual calls "too close". */ if (!NEPMOK(tx->tx_pos,size+TX_GAP, eni_in(MID_TX_RDPTR(tx->index)),tx->words)) { DPRINTK(DEV_LABEL "(itf %d): TX full (size %d)\n", vcc->dev->number,size); return enq_next; } /* check DMA */ dma_wr = eni_in(MID_DMA_WR_TX); dma_rd = eni_in(MID_DMA_RD_TX); dma_size = 3; /* JK for descriptor and final fill, plus final size mis-alignment fix */DPRINTK("iovcnt = %d\n",ATM_SKB(skb)->iovcnt); if (!ATM_SKB(skb)->iovcnt) dma_size += 5; else dma_size += 5*ATM_SKB(skb)->iovcnt; if (dma_size > TX_DMA_BUF) { printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries " "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF); } DPRINTK("dma_wr is %d, tx_pos is %ld\n",dma_wr,tx->tx_pos); if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) < dma_size) { printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n", vcc->dev->number); return enq_jam; } paddr = pci_map_single(eni_dev->pci_dev,skb->data,skb->len, PCI_DMA_TODEVICE); ENI_PRV_PADDR(skb) = paddr; /* prepare DMA queue entries */ j = 0; eni_dev->dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) << MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | MID_DT_JK; j++; if (!ATM_SKB(skb)->iovcnt) if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len); else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4); else {DPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */ for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) put_dma(tx->index,eni_dev->dma,&j,(unsigned long) ((struct iovec *) skb->data)[i].iov_base, ((struct iovec *) skb->data)[i].iov_len); } if (skb->len & 3) put_dma(tx->index,eni_dev->dma,&j,zeroes,4-(skb->len & 3)); /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */ eni_dev->dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) << MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | MID_DMA_END | MID_DT_JK; j++; DPRINTK("DMA at end: %d\n",j); /* store frame */ writel((MID_SEG_TX_ID << MID_SEG_ID_SHIFT) | (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) | (tx->resolution << MID_SEG_RATE_SHIFT) | (size/(ATM_CELL_PAYLOAD/4)),tx->send+tx->tx_pos*4);/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/ writel((vcc->vci << MID_SEG_VCI_SHIFT) | (aal5 ? 0 : (skb->data[3] & 0xf)) | (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0), tx->send+((tx->tx_pos+1) & (tx->words-1))*4); DPRINTK("size: %d, len:%d\n",size,skb->len); if (aal5) writel(skb->len,tx->send+ ((tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1))*4); j = j >> 1; for (i = 0; i < j; i++) { writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*8); writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*8+4); dma_wr = (dma_wr+1) & (NR_DMA_TX-1); } ENI_PRV_POS(skb) = tx->tx_pos; ENI_PRV_SIZE(skb) = size; ENI_VCC(vcc)->txing += size; tx->tx_pos = (tx->tx_pos+size) & (tx->words-1); DPRINTK("dma_wr set to %d, tx_pos is now %ld\n",dma_wr,tx->tx_pos); eni_out(dma_wr,MID_DMA_WR_TX); skb_queue_tail(&eni_dev->tx_queue,skb);queued++; return enq_ok;}static void poll_tx(struct atm_dev *dev){ struct eni_tx *tx; struct sk_buff *skb; enum enq_res res; int i; DPRINTK(">poll_tx\n"); for (i = NR_CHAN-1; i >= 0; i--) { tx = &ENI_DEV(dev)->tx[i]; if (tx->send) while ((skb = skb_dequeue(&tx->backlog))) { res = do_tx(skb); if (res == enq_ok) continue; DPRINTK("re-queuing TX PDU\n"); skb_queue_head(&tx->backlog,skb);requeued++; if (res == enq_jam) return; break; } }}static void dequeue_tx(struct atm_dev *dev){ struct eni_dev *eni_dev; struct atm_vcc *vcc; struct sk_buff *skb; struct eni_tx *tx; NULLCHECK(dev); eni_dev = ENI_DEV(dev); NULLCHECK(eni_dev); while ((skb = skb_dequeue(&eni_dev->tx_queue))) { vcc = ATM_SKB(skb)->vcc; NULLCHECK(vcc); tx = ENI_VCC(vcc)->tx; NULLCHECK(ENI_VCC(vcc)->tx); DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb), (unsigned) eni_in(MID_TX_DESCRSTART(tx->index))); if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) == eni_in(MID_TX_DESCRSTART(tx->index))) { skb_queue_head(&eni_dev->tx_queue,skb); break; } ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); pci_unmap_single(eni_dev->pci_dev,ENI_PRV_PADDR(skb),skb->len, PCI_DMA_TODEVICE); if (vcc->pop) vcc->pop(vcc,skb); else dev_kfree_skb_irq(skb); atomic_inc(&vcc->stats->tx); wake_up(&eni_dev->tx_wait);dma_complete++; }}static struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr){ int i; for (i = !ubr; i < NR_CHAN; i++) if (!eni_dev->tx[i].send) return eni_dev->tx+i; return NULL;}static int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre, int *res,int unlimited){ static const int pre_div[] = { 4,16,128,2048 }; /* 2^(((x+2)^2-(x+2))/2+1) */ if (unlimited) *pre = *res = 0; else { if (*pcr > 0) { int div; for (*pre = 0; *pre < 3; (*pre)++) if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break; div = pre_div[*pre]**pcr; DPRINTK("min div %d\n",div); *res = TS_CLOCK/div-1; } else { int div; if (!*pcr) *pcr = eni_dev->tx_bw+reserved; for (*pre = 3; *pre >= 0; (*pre)--) if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break; if (*pre < 3) (*pre)++; /* else fail later */ div = pre_div[*pre]*-*pcr; DPRINTK("max div %d\n",div); *res = (TS_CLOCK+div-1)/div-1; } if (*res < 0) *res = 0; if (*res > MID_SEG_MAX_RATE) *res = MID_SEG_MAX_RATE; } *pcr = TS_CLOCK/pre_div[*pre]/(*res+1); DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res); return 0;}static int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, int set_rsv,int set_shp){ struct eni_dev *eni_dev = ENI_DEV(vcc->dev); struct eni_vcc *eni_vcc = ENI_VCC(vcc); struct eni_tx *tx; unsigned long size,mem; int rate,ubr,unlimited,new_tx; int pre,res,order; int error; rate = atm_pcr_goal(txtp); ubr = txtp->traffic_class == ATM_UBR; unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || rate >= ATM_OC3_PCR); if (!unlimited) { size = txtp->max_sdu*eni_dev->tx_mult/100; if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <= MID_MAX_BUF_SIZE) size = MID_MAX_BUF_SIZE; } else { if (eni_dev->ubr) { eni_vcc->tx = eni_dev->ubr; txtp->pcr = ATM_OC3_PCR; return 0; } size = UBR_BUFFER; } new_tx = !eni_vcc->tx; mem = 0; /* for gcc */ if (!new_tx) tx = eni_vcc->tx; else { mem = eni_alloc_mem(eni_dev,&size); if (!mem) return -ENOBUFS; tx = alloc_tx(eni_dev,unlimited); if (!tx) { eni_free_mem(eni_dev,mem,size); return -EBUSY; } DPRINTK("got chan %d\n",tx->index); tx->reserved = tx->shaping = 0; tx->send = mem; tx->words = size >> 2; skb_queue_head_init(&tx->backlog); for (order = 0; size > (1 << (order+10)); order++); eni_out((order << MID_SIZE_SHIFT) | ((tx->send-eni_dev->ram) >> (MID_LOC_SKIP+2)), MID_TX_PLACE(tx->index)); tx->tx_pos = eni_in(MID_TX_DESCRSTART(tx->index)) & MID_DESCR_START; } error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited); if (!error && txtp->min_pcr > rate) error = -EINVAL; if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && txtp->max_pcr < rate) error = -EINVAL; if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) error = -EINVAL; if (!error && set_rsv && !set_shp && rate < tx->shaping) error = -EINVAL; if (!error && !set_rsv && rate > tx->reserved && !ubr) error = -EINVAL; if (error) { if (new_tx) { tx->send = 0; eni_free_mem(eni_dev,mem,size); } return error; } txtp->pcr = rate; if (set_rsv && !ubr) { eni_dev->tx_bw += tx->reserved; tx->reserved = rate; eni_dev->tx_bw -= rate; } if (set_shp || (unlimited && new_tx)) { if (unlimited && new_tx) eni_dev->ubr = tx; tx->prescaler = pre; tx->resolution = res; tx->shaping = rate; } if (set_shp) eni_vcc->tx = tx; DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping); return 0;}static int open_tx_first(struct atm_vcc *vcc){ ENI_VCC(vcc)->tx = NULL; if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; ENI_VCC(vcc)->txing = 0; return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1);}static int open_tx_second(struct atm_vcc *vcc){ return 0; /* nothing to do */}static void close_tx(struct atm_vcc *vcc){ DECLARE_WAITQUEUE(wait,current); struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; eni_vcc = ENI_VCC(vcc); if (!eni_vcc->tx) return; eni_dev = ENI_DEV(vcc->dev); /* wait for TX queue to drain */ DPRINTK("eni_close: waiting for TX ...\n"); add_wait_queue(&eni_dev->tx_wait,&wait); set_current_state(TASK_UNINTERRUPTIBLE); for (;;) { int txing; tasklet_disable(&eni_dev->task); txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing; tasklet_enable(&eni_dev->task); if (!txing) break; DPRINTK("%d TX left\n",eni_vcc->txing); schedule(); set_current_state(TASK_UNINTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&eni_dev->tx_wait,&wait); if (eni_vcc->tx != eni_dev->ubr) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -