in eni.c [338:485]
static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb,
unsigned long skip,unsigned long size,unsigned long eff)
{
struct eni_dev *eni_dev;
struct eni_vcc *eni_vcc;
u32 dma_rd,dma_wr;
u32 dma[RX_DMA_BUF*2];
dma_addr_t paddr;
unsigned long here;
int i,j;
eni_dev = ENI_DEV(vcc->dev);
eni_vcc = ENI_VCC(vcc);
paddr = 0; /* GCC, shut up */
if (skb) {
paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len,
DMA_FROM_DEVICE);
if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr))
goto dma_map_error;
ENI_PRV_PADDR(skb) = paddr;
if (paddr & 3)
printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has "
"mis-aligned RX data (0x%lx)\n",vcc->dev->number,
vcc->vci,(unsigned long) paddr);
ENI_PRV_SIZE(skb) = size+skip;
/* PDU plus descriptor */
ATM_SKB(skb)->vcc = vcc;
}
j = 0;
if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */
here = (eni_vcc->descr+skip) & (eni_vcc->words-1);
dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci
<< MID_DMA_VCI_SHIFT) | MID_DT_JK;
dma[j++] = 0;
}
here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1);
if (!eff) size += skip;
else {
unsigned long words;
if (!size) {
DPRINTK("strange things happen ...\n");
EVENT("strange things happen ... (skip=%ld,eff=%ld)\n",
size,eff);
}
words = eff;
if (paddr & 15) {
unsigned long init;
init = 4-((paddr & 15) >> 2);
if (init > words) init = words;
dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
(vcc->vci << MID_DMA_VCI_SHIFT);
dma[j++] = paddr;
paddr += init << 2;
words -= init;
}
#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */
if (words & ~15) {
dma[j++] = MID_DT_16W | ((words >> 4) <<
MID_DMA_COUNT_SHIFT) | (vcc->vci <<
MID_DMA_VCI_SHIFT);
dma[j++] = paddr;
paddr += (words & ~15) << 2;
words &= 15;
}
#endif
#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */
if (words & ~7) {
dma[j++] = MID_DT_8W | ((words >> 3) <<
MID_DMA_COUNT_SHIFT) | (vcc->vci <<
MID_DMA_VCI_SHIFT);
dma[j++] = paddr;
paddr += (words & ~7) << 2;
words &= 7;
}
#endif
#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */
if (words & ~3) {
dma[j++] = MID_DT_4W | ((words >> 2) <<
MID_DMA_COUNT_SHIFT) | (vcc->vci <<
MID_DMA_VCI_SHIFT);
dma[j++] = paddr;
paddr += (words & ~3) << 2;
words &= 3;
}
#endif
#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */
if (words & ~1) {
dma[j++] = MID_DT_2W | ((words >> 1) <<
MID_DMA_COUNT_SHIFT) | (vcc->vci <<
MID_DMA_VCI_SHIFT);
dma[j++] = paddr;
paddr += (words & ~1) << 2;
words &= 1;
}
#endif
if (words) {
dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT)
| (vcc->vci << MID_DMA_VCI_SHIFT);
dma[j++] = paddr;
}
}
if (size != eff) {
dma[j++] = (here << MID_DMA_COUNT_SHIFT) |
(vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK;
dma[j++] = 0;
}
if (!j || j > 2*RX_DMA_BUF) {
printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n");
goto trouble;
}
dma[j-2] |= MID_DMA_END;
j = j >> 1;
dma_wr = eni_in(MID_DMA_WR_RX);
dma_rd = eni_in(MID_DMA_RD_RX);
/*
* Can I move the dma_wr pointer by 2j+1 positions without overwriting
* data that hasn't been read (position of dma_rd) yet ?
*/
if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */
printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n",
vcc->dev->number);
goto trouble;
}
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)
dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len,
DMA_FROM_DEVICE);
dma_map_error:
if (skb) dev_kfree_skb_irq(skb);
return -1;
}