static int do_rx_dma()

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;
}