static void driverInterruptHandler(kernelNetworkDevice *adapter)
{
	// This is the 'body' of the interrupt handler for lance devices.  Called
	// from the netorkInterrupt() function in kernelNetwork.c

	unsigned csr0 = 0;
	lanceDevice *lance = NULL;
	int head = 0;
	int *tail = NULL;
	unsigned short flags;

	// Check params
	if (!adapter)
		return;

	lance = adapter->data;

	// Get the contents of the status register
	csr0 = readCSR(lance, LANCE_CSR_STATUS);

	// Disable lance interrupts and clear interrupt status
	csr0 &= ~LANCE_CSR_STATUS_IENA;
	writeCSR(lance, LANCE_CSR_STATUS, csr0); // Should this be done later?

	// Check for collision errors
	if (csr0 & LANCE_CSR_STATUS_CERR)
		adapter->device.collisions += 1;

	// Why the interrupt, bub?
	if (csr0 & LANCE_CSR_STATUS_RINT)
	{
		// Received

		// If there were general errors in reception, update the error
		// statistics
		if (csr0 & LANCE_CSR_STATUS_ERR)
		{
			adapter->device.recvErrors += 1;
			if (csr0 & LANCE_CSR_STATUS_MISS)
				adapter->device.recvOverruns += 1;
		}

		// Count the number of queued receive packets
		head = lance->recvRing.head;
		while ((adapter->device.recvQueued < adapter->device.recvQueueLen) &&
			!(lance->recvRing.desc.recv[head].flags & LANCE_DESCFLAG_OWN))
		{
			flags = lance->recvRing.desc.recv[head].flags;

			// Check for receive errors with this packet
			if (flags & LANCE_DESCFLAG_ERR)
			{
				adapter->device.recvErrors += 1;
				if ((flags & LANCE_DESCFLAG_RECV_FRAM) ||
					(flags & LANCE_DESCFLAG_RECV_OFLO) ||
					(flags & LANCE_DESCFLAG_RECV_CRC))
				{
					adapter->device.recvDropped += 1;
				}
			}

			// Increase the count of packets queued for receiving
			adapter->device.recvQueued += 1;

			// Move to the next receive descriptor
			head += 1;
			if (head >= adapter->device.recvQueueLen)
				head = 0;

			if (head == lance->recvRing.head)
				// We wrapped all the way around.
				break;
		}
	}

	if (csr0 & LANCE_CSR_STATUS_TINT)
	{
		// Transmitted

		// If there were general errors in tranmission, update the error
		// statistics
		if (csr0 & LANCE_CSR_STATUS_ERR)
		{
			adapter->device.transErrors += 1;
			if (csr0 & LANCE_CSR_STATUS_MISS)
				adapter->device.transOverruns += 1;
		}

		// Loop for each transmitted packet
		tail = &(lance->transRing.tail);
		while (adapter->device.transQueued &&
			!(lance->transRing.desc.trans[*tail].flags & LANCE_DESCFLAG_OWN))
		{
			flags = lance->transRing.desc.trans[*tail].flags;

			// Check for transmit errors with this packet
			if (flags & LANCE_DESCFLAG_ERR)
			{
				adapter->device.transErrors += 1;
				if ((flags & LANCE_DESCFLAG_TRANS_UFLO) ||
					(flags & LANCE_DESCFLAG_TRANS_LCOL) ||
					(flags & LANCE_DESCFLAG_TRANS_LCAR) ||
					(flags & LANCE_DESCFLAG_TRANS_RTRY))
				{
					adapter->device.transDropped += 1;
				}
			}

			// Reduce the counter of packets queued for transmission
			adapter->device.transQueued -= 1;

			// Move to the next transmit descriptor
			*tail += 1;
			if (*tail >= adapter->device.transQueueLen)
				*tail = 0;
		}
	}

	// Reenable lance interrupts
	modifyCSR(lance, LANCE_CSR_STATUS, LANCE_CSR_STATUS_IENA, op_or);

	return;
}
Beispiel #2
0
uint64 init_nic_pcnet(struct pci_dev *d)
{
    uint32 io = d->bars[0].addr;
    uint16 t16;
    uint32 i;
    struct pcnet_private *priv = NULL;
    struct eth_dev *eth;

    priv = (struct pcnet_private *)kmalloc_align(sizeof(struct pcnet_private), "pcnet_private", NULL);
    if(priv == NULL) {
        printf("init_nic: cannot allocate pcnet_private\n");
        goto fail;
    }

    priv->dev = d;

    priv->init = (struct pcnet_init_32 *)kmalloc_align(sizeof(struct pcnet_init_32), "pcnet_init",
                 NULL);
    if(priv->init == NULL) {
        printf("init_nic: cannot allocate pcnet_init\n");
        goto fail_free_private;
    }

    priv->rx = (struct pcnet_rx_32 *)kmalloc_align(sizeof(struct pcnet_rx_32) * DRE_COUNT,
               "pcnet_rx", NULL);
    if(priv->rx == NULL) {
        printf("init_nic: cannot allocate pcnet_rx\n");
        goto fail_free_init;
    }

    priv->tx = (struct pcnet_tx_32 *)kmalloc_align(sizeof(struct pcnet_tx_32) * DRE_COUNT,
               "pcnet_tx", NULL);
    if(priv->tx == NULL) {
        printf("init_nic: cannot allocate pcnet_tx\n");
        goto fail_free_rx;
    }

    eth = eth_alloc(priv, &pcnet_ops);
    if(eth == NULL) {
        printf("init_nic: failed to allocate eth\n");
        goto fail_free_tx;
    }
    priv->eth = eth;

    t16 = pci_read_conf16(d->bus, d->dev, d->func, PCI_CMD_REG);
    if(!(t16 & PCI_CMD_MASTER)) {
        t16 |= PCI_CMD_MASTER|PCI_CMD_IO|PCI_CMD_MEMORY;
        pci_write_conf16(d->bus, d->dev, d->func, PCI_CMD_REG, t16);
        t16 = pci_read_conf16(d->bus, d->dev, d->func, PCI_CMD_REG);
        printf("init_nic: enabling PCI master bit\n");
    }

    //writeCSR(io, 0, 0x04); // this switches to 32bit too early
    printf("init_nic: eth%x mac_addr=", eth->unit);
    for (i=0; i<6; i++) {
        printf("%x", eth->addr[i] = priv->init->PADR[i] = inportb(io+i));
        if(i!=5) printf(":");
        //else printf("\n");
    }

    // Put the NIC in STOP
    outportw(io + 0x12, 0);
    outportw(io + 0x10, CSR0_STOP);
    // Reset the NIC
    inportw(io + 0x14);
    // Switch to DWORD mode
    outportl(io + 0x10, 0x00);
    // Switch to 32bit and PCNET_PCI_II style
    writeBCR(io, 20, CSR58_SSIZE32|CSR58_PCNET_PCII);

    // Obtain the chip version(s)
    priv->chip_version_lo = readCSR(io, 88);
    printf(" ver=%x:", priv->chip_version_lo);
    priv->chip_version_up = (readCSR(io, 89) & 0x0000ffff);
    printf("%x", priv->chip_version_up);

    // Set-up the initialisation block
    priv->init->MODE = 0;
    priv->init->RLEN = TX_TLEN;
    priv->init->TLEN = RX_RLEN;
    priv->init->LADRF = 0x0;
    priv->init->RDRA = (uint32)(uint64)priv->rx;
    priv->init->TDRA = (uint32)(uint64)priv->tx;

    for(i=0; i<DRE_COUNT; i++) {
        priv->rx[i].RBADR = (uint32)(uint64)kmalloc_align(1544, "pcnet rx", NULL);
        priv->rx[i].BCNT = SECOND_COMP(1544);
        priv->rx[i].ones = 0xf;
        priv->rx[i].OWN = 1;
        priv->tx[i].TBADR = 0;
        priv->tx[i].ones = 0xf;
    }

    // Tell the NIC where the init block is
    writeCSR(io, 1, ((uint32)(uint64)priv->init) & 0x0000ffff);
    writeCSR(io, 2, (((uint32)(uint64)priv->init) & 0xffff0000) >> 16);
    // Switch NIC state to INIT
    writeCSR(io, 0, (readCSR(io, 0) | CSR0_INIT) & ~ CSR0_STOP);
    printf(" INIT");

    writeCSR(io, 4, (readCSR(io, 4)|CSR4_DMA_PLUSA|CSR4_APAD_XMIT|CSR4_TXSTRTM)
             & ~CSR4_DPOLL);
    writeBCR(io, 2, readBCR(io, 2)|BCR2_ASEL);

    //writeCSR(io, 3, (readCSR(io, 3)) & ~CSR3_ALL_INTS);
    //writeCSR(io, 4, (readCSR(io, 4)) & ~CSR4_ALL_INTS);
    // Switch NIC state to START, enable RX/TX, disable STOP and enable interrupts
    writeCSR(io, 0, (readCSR(io, 0)|CSR0_STRT|/*CSR0_IENA|*/CSR0_RXON|CSR0_TXON) & ~CSR0_STOP);

    printf(" STRT\n");
    return 0;

    //fail_free_eth:
    //	eth_free(eth);
fail_free_tx:
    kfree(priv->tx);
fail_free_rx:
    kfree(priv->rx);
fail_free_init:
    kfree(priv->init);
fail_free_private:
    kfree(priv);
fail:
    return -1;
}