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