📄 ethernet_smsc91c111.c
字号:
}#if DEBUG_LEVEL >= 5 /* * For some reason we must read OFFSET_FPR here or else * OFFSET_EPHSR doesn't always set BIT_EPHSR_TX_SUC below * when BIT_CR_AUTO_RELEASE is being used */ SETUP_BANK(BANK_FPR); READ_REG16(OFFSET_FPR, _temp); /* * Do a sanity check on the packet number. * Not reliable when we are using BIT_CR_AUTO_RELEASE */ if (!(_temp & BIT_FPR_TEMPTY) && (_temp & MASK_FPR_PACKET_NUM) != packet_number) { ERROR ("Packet # mismatch %d,%d\r\n", _temp & MASK_FPR_PACKET_NUM, packet_number); }#endif /* * Check transmission status. It might be better to check * it from the TX Packet itself if we intended to queue up a * bunch, send and so on. (Currently, a tx packet must finish * before another is queued.) */ SETUP_BANK(BANK_EPHSR); READ_REG16(OFFSET_EPHSR, _temp); if (_temp & BIT_EPHSR_TX_SUC) { DEBUG_4 ("Tx OK\r\n"); // Release memory here because BIT_CR_AUTO_RELEASE is unset release_tx_packet (packet_number); // Clear the TX interrupt bit SETUP_BANK (BANK_ISR); WRITE_REG8 (OFFSET_IRQAR, BIT_ISR_TX_INT); /* * Enable Receive */ SETUP_BANK(BANK_RCR); READ_REG16(OFFSET_RCR, _temp); _temp |= BIT_RCR_RXEN; WRITE_REG16(OFFSET_RCR, _temp); return 1; } else { DEBUG_2 ("Tx unsuccessful %x\r\n", _temp); DEBUG_2 ("ISR is %x\r\n", _temp8); release_tx_packet (packet_number); // Clear the TX interrupt bit SETUP_BANK (BANK_ISR); WRITE_REG8 (OFFSET_IRQAR, BIT_ISR_TX_INT); if (retry++ < MAX_RETRANSMIT) { DEBUG_2 ("Retry Tx\r\n"); goto retransmit; } else { return 0; } } return 1;} // End of tx_packet_ethernetint rx_packet_ethernet(u8 *data, u16 *size, char bcast_enable){ unsigned char packet_number; unsigned short _temp; const unsigned short start_size=*size; int retry=0; int success; /* * Enable the SMSC to Receive Packets if it is not already able to * do so. */ SETUP_BANK(BANK_RCR); if (!(REG16(OFFSET_RCR) & BIT_RCR_RXEN)) { REG16(OFFSET_RCR) |= BIT_RCR_RXEN; }restart_rx: /* * Wait for the RX Packet to Come in */ SETUP_BANK(BANK_ISR); if (!(REG8(OFFSET_ISR) & BIT_ISR_RCV_INT)) { return 0; } /* * Double check that we have Received Packets Waiting * This doesn't seem necessary with the BIT_ISR_RCV_INT check above, * but we need the packet number from this register regardless. */ SETUP_BANK(BANK_FPR); READ_REG16(OFFSET_FPR, _temp); if (_temp & BIT_FPR_REMPTY) { return 0; } DEBUG_4 ("Rx..."); /* * Grab the Packet Number out of the Fifo Ports Register */ packet_number = _temp >> 8; *size = start_size; if (!(success = read_mmu_data (&packet_number, data, size))) { DEBUG_2 ("%s: read_mmu_data Failed\r\n", __FUNCTION__); } if (!release_rx_packet ()) { ERROR ("%s: Failed to Release Received Packet\r\n", __FUNCTION__); } if (!success) { return 0; } /* * To allow this to work better, we send it whether we want broadcast * packets or not. ARP and DHCP both need Broadcast, Bootp and tftp * don't. This will make bootp really, really quick and same with * tftp. It also makes tftp quick because it drops the packets on * the floor. Remember that we must honour ARP requests at all times * because the host's ARP cache might be invalidated at any time. * * If we want even more performance, we should do this in * read_mmu_data however, that is not currently done that way because * it is not clean. */ if (!bcast_enable) { if ((data[0]== 0xFF) && (data[1] == 0xFF) && (data[2] == 0xFF) && (data[3]== 0xFF) && (data[4] == 0xFF) && (data[5] == 0xFF) && (data[ETHER_TYPE_OFFSET] != (ETHER_TYPE_ARP & 0xff) || data[ETHER_TYPE_OFFSET+1] != ETHER_TYPE_ARP >> 8)) { DEBUG_3 ("Dropping b'cast RX\r\n"); if (retry++ > MAX_RX_RETRY) { return 0; } else { goto restart_rx; } } } // There seems to be a bug somewhere that causes the occasional // packet we transmit to show up here as an incoming packet. // Until we figure out why, detect those packets and throw them away. SETUP_BANK(BANK_IAR); if ((REG16(OFFSET_IAR01) == *(u16 *)(data + ETHER_SRC_OFFSET)) && (REG16(OFFSET_IAR23) == *(u16 *)(data + ETHER_SRC_OFFSET + 2)) && (REG16(OFFSET_IAR45) == *(u16 *)(data + ETHER_SRC_OFFSET + 4))) { DEBUG_2("Ignoring bogus received packet from myself\r\n"); return 0; } DEBUG_4 ("Rx OK\r\n"); return 1;} // End of rx_packet_ethernet////////////////////////////////////////////////////////////////////////////////// read_mac_ethernet// PURPOSE: Reads the MAC address.// PARAMS: (OUT) u16 * macaddr - An array to return the MAC address.// RETURNS: 1 for success, 0 for failure.////////////////////////////////////////////////////////////////////////////////int read_mac_ethernet(u16 *macaddr, unsigned short num){ u16 mac_size=sizeof(short) * 3;#ifdef TAGGED_EEPROM char tag[7]; // Try to read the tagged format MAC address. If that fails (likely // because the EEPROM isn't in tagged format) fall back to the old format. if (check_eeprom_header()) { itc_strcpy(tag, "MACADx"); tag[5] = num + '0'; if (eeprom_get_item(tag, sizeof(tag)-1, (u8 *)macaddr, &mac_size) <= 0) { DEBUG_3("Could not find EEPROM tag %s\r\n", tag); return 0; } return 1; } // If the EEPROM isn't in tagged format yet, fall through to old-style // reading routine (this is needed in order to read old addresses in // order to upgrade the EEPROM format).#endif // Read the MAC address from the old-style fixed location in EEPROM // THIS IS OBSOLETE but needed for reading an old-style MAC address return read_data_eeprom (EEPROM_MAC_ADDR + (num * mac_size), (unsigned char*)macaddr, &mac_size);} // End of read_mac_ethernet////////////////////////////////////////////////////////////////////////////////// write_mac_ethernet// PURPOSE: Writes a new MAC address.// PARAMS: (IN) u16 * macaddr - An array containing the new MAC address.// RETURNS: 1 for success, 0 for failure.////////////////////////////////////////////////////////////////////////////////int write_mac_ethernet(u16 *macaddr, unsigned short num){ u16 mac_size=sizeof(short) * 3;#ifdef TAGGED_EEPROM char tag[7]; itc_strcpy(tag, "MACADx"); tag[5] = num + '0'; if (eeprom_write_item(tag, sizeof(tag)-1, (u8 *)macaddr, mac_size) < 0) { ERROR("Could not write to EEPROM\r\n"); return 0; } return 1;#else // Write the MAC address into the old-style fixed location in EEPROM // THIS IS OBSOLETE! return write_data_eeprom (EEPROM_MAC_ADDR + (num * mac_size), (unsigned char*)macaddr, &mac_size);#endif} // End of write_mac_ethernet/* * Here starts local functions */static int probe_smsc91c111 (unsigned int *address){ unsigned short data; SETUP_BANK(BANK_BSR); READ_REG16(OFFSET_BSR, data); if ((data & 0xff00) == BSR_UNIQUE) { itc_printf ("SMSC LAN91C111 Found at Address 0x%x\r\n", (unsigned int) address);#if DEBUG_LEVEL >= 2 SETUP_BANK(BANK_RR); READ_REG16(OFFSET_RR, data); /* * Print out the Chip ID and Revision */ itc_printf ("SMSC Chip ID: 0x%x\r\n", ((data >> 4) & 0xF)); itc_printf ("SMSC Chip Revision: 0x%x\r\n", (data & 0xF));#endif return 1; } return 0;} // End of probe_smsc91c111/* * NOTES: * When you write data to the SMSC the first 16 bits must be status * bytes (0's are good) and the second 16 bits is suppose to be the * size of the ethernet frame. * * The size of the ethernet frame should be the size of the data + 6 * because the size should include the 4 bytes (status and size) and * 2 bytes for the trailer (described later). Also, the SMSC always * ignores ODD counts (you send it 11 bytes it reads it as 10 bytes.) * * The trailer is how the SMSC figures out if you are sending an ODD * number of bytes or not. The trailer is the last 2 bytes of data. * If the second byte in the trailer has bit 5 set then the first * byte in the trailer in a valid byte and should be included in the * transmission, but if bit 5 in the trailer is 0 then the first byte * in the trailer is just a pad and is ignored. * * Also note that the auto-increment only works in 32-bit mode, so * all data is re-aligned on the fly if its not 32-bit aligned already. */static int write_mmu_data (unsigned char packet_number, unsigned char *data, unsigned int data_size){ unsigned short pointer_offset; unsigned int *data32=NULL, _temp32; unsigned short *data16=NULL; unsigned short _temp; int counter,i; unsigned int alignment, leftovers; if (data_size > ((1 << 11) - 1)) return 0; /* * We set up the pointer register to auto-increment. This is the * fastest way to do it. */ SETUP_BANK(BANK_PR); counter=MAX_PR_COUNTER; do { READ_REG16(OFFSET_PR, _temp); } while ((_temp & BIT_PR_NOTEMPTY) && --counter); if (!counter) { ERROR("TX FIFO not empty\r\n"); } WRITE_REG16(OFFSET_PR, BIT_PR_AUTO_INC); SETUP_BANK(BANK_DR); REG32(OFFSET_DR) = ((data_size) + 6) << 16; /* * Leftovers has 2 purposes, to figure out our alignment, and send the * remainder of the data at the end. */ leftovers = ((unsigned int)data % 4); if (leftovers == 0) { alignment = 4; data32 = (unsigned int *)data; } else if (leftovers == 2) { alignment = 2; data16 = (unsigned short *)data; } else { alignment = 1; } /* * This was changed when it became apparent that auto-increment may * not have been working right in the 16 and 8 bit write cases. If * you know it works, this can be redone so that the leftovers * gets data_size % alignment and changes are done in the respective * if statements below (so one REG8 or REG16 is done). * * Order was right, but I saw the MMU increment 32 bits after a 16 bit * access. (FFEEDDCC) Would become (FFEE0000 DDCC0000). */ leftovers = (data_size % 4); for (pointer_offset=0;pointer_offset < (data_size - leftovers); pointer_offset += 4) { if (alignment == 4) { REG32(OFFSET_DR) = data32[pointer_offset/4]; } else if (alignment == 2) { _temp32 = data16[pointer_offset/2]; _temp32 |= data16[pointer_offset/2 + 1] << 16; REG32(OFFSET_DR) = _temp32; } else { _temp32 = data[pointer_offset]; _temp32 |= data[pointer_offset + 1] << 8; _temp32 |= data[pointer_offset + 2] << 16; _temp32 |= data[pointer_offset + 3] << 24; REG32(OFFSET_DR) = _temp32; } } /* * Here we send out the remaining bytes and setup the ODD Byte if * we have to. */ if (leftovers) { SETUP_BANK(BANK_PR); counter=MAX_PR_COUNTER; do { READ_REG16(OFFSET_PR, _temp); } while ((_temp & BIT_PR_NOTEMPTY) && --counter); if (!counter) { ERROR("TX FIFO not empty, but was\r\n"); } /* * This is necessary as the pointer offset for the DR * is different than our data pointer. Don't forget that * the first 32-bit word in the DR is the size and status */ WRITE_REG16(OFFSET_PR, pointer_offset + 4); _temp32 = 0; SETUP_BANK(BANK_DR); for (i=0;i<leftovers;i++) { _temp32 |= data[pointer_offset + i] << (i*8); } if ((data_size & 1) == 1) { /* * We have an odd number of bytes */ _temp32 |= 0x20 << (i*8); } REG32(OFFSET_DR) = _temp32; } return 1;} // End of write_mmu_data/* * Apparently, the SMSC has difficulty with the ODD Byte on receive if you * get one that is Rev 0. Although, there is not documentation on this on * the SMSC Website (Errata could not be found) the Linux Driver does this. * * Also, it only hurts if the upper-levels are unable to figure out that * the size is one larger, so the Revision Register (Chip ID and Rev) are * checked and if the appropriate Rev and Chip ID is found, the ODD Byte * is read regardless of the status. * * Chip ID == 9 and Rev 0 apparently is the problem chip. * * Additionally, the auto-increment does not work for anything other than * 32-bit accesses (see write_mmu_data comment for more info) */static int read_mmu_data (unsigned char *packet_number, unsigned char *data, unsigned short *data_size){ unsigned short pointer_offset; unsigned short size; unsigned int *data32, _temp32, data_count; int counter; unsigned short *data16; unsigned short _temp; int leftovers,i; if (*data_size > ((1 << 11) -1)) { ERROR("%s: Data buffer > max allowable rx packet\r\n", __FUNCTION__); } /* * Setup the pointer register to auto-increment our addresses so * that we don't have to worry about it. This should not be a big * deal on hardware that works */ SETUP_BANK(BANK_PR); counter=MAX_PR_COUNTER; do { READ_REG16(OFFSET_PR, _temp); } while ((_temp & BIT_PR_NOTEMPTY) && --counter); WRITE_REG16(OFFSET_PR, BIT_PR_RCV | BIT_PR_READ | BIT_PR_AUTO_INC); SETUP_BANK(BANK_DR); _temp32 = REG32(OFFSET_DR); //stat = _temp32 & 0xFFFF; size = ((_temp32 >> 16) & 0x7FF); // Size includes the status, byte count and control words // Ignore the ODD bit for now and read the extra odd byte regardless. data_count = size - 6 + 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -