// // smsc951x.c // // Information to implement this driver was taken // from the Linux smsc95xx driver which is: // Copyright (C) 2007-2008 SMSC // and the Embedded Xinu SMSC LAN9512 USB driver which is: // Copyright (c) 2008, Douglas Comer and Dennis Brylow // See the file lib/README for details! // // USPi - An USB driver for Raspberry Pi written in C // Copyright (C) 2014-2016 R. Stange // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include // USB vendor requests #define WRITE_REGISTER 0xA0 #define READ_REGISTER 0xA1 // Registers #define ID_REV 0x00 #define INT_STS 0x08 #define RX_CFG 0x0C #define TX_CFG 0x10 #define TX_CFG_ON 0x00000004 #define HW_CFG 0x14 #define HW_CFG_BIR 0x00001000 #define RX_FIFO_INF 0x18 #define PM_CTRL 0x20 #define LED_GPIO_CFG 0x24 #define LED_GPIO_CFG_SPD_LED 0x01000000 #define LED_GPIO_CFG_LNK_LED 0x00100000 #define LED_GPIO_CFG_FDX_LED 0x00010000 #define GPIO_CFG 0x28 #define AFC_CFG 0x2C #define E2P_CMD 0x30 #define E2P_DATA 0x34 #define BURST_CAP 0x38 #define GPIO_WAKE 0x64 #define INT_EP_CTL 0x68 #define BULK_IN_DLY 0x6C #define MAC_CR 0x100 #define MAC_CR_RCVOWN 0x00800000 #define MAC_CR_MCPAS 0x00080000 #define MAC_CR_PRMS 0x00040000 #define MAC_CR_BCAST 0x00000800 #define MAC_CR_TXEN 0x00000008 #define MAC_CR_RXEN 0x00000004 #define ADDRH 0x104 #define ADDRL 0x108 #define HASHH 0x10C #define HASHL 0x110 #define MII_ADDR 0x114 #define MII_BUSY 0x01 #define MII_WRITE 0x02 #define PHY_ID_MASK 0x1F #define PHY_ID_INTERNAL 0x01 #define REG_NUM_MASK 0x1F #define MII_DATA 0x118 #define FLOW 0x11C #define VLAN1 0x120 #define VLAN2 0x124 #define WUFF 0x128 #define WUCSR 0x12C #define COE_CR 0x130 // TX commands (first two 32-bit words in buffer) #define TX_CMD_A_DATA_OFFSET 0x001F0000 #define TX_CMD_A_FIRST_SEG 0x00002000 #define TX_CMD_A_LAST_SEG 0x00001000 #define TX_CMD_A_BUF_SIZE 0x000007FF #define TX_CMD_B_CSUM_ENABLE 0x00004000 #define TX_CMD_B_ADD_CRC_DISABLE 0x00002000 #define TX_CMD_B_DISABLE_PADDING 0x00001000 #define TX_CMD_B_PKT_BYTE_LENGTH 0x000007FF // RX status (first 32-bit word in buffer) #define RX_STS_FF 0x40000000 // Filter Fail #define RX_STS_FL 0x3FFF0000 // Frame Length #define RX_STS_FRAMELEN(reg) (((reg) & RX_STS_FL) >> 16) #define RX_STS_ES 0x00008000 // Error Summary #define RX_STS_BF 0x00002000 // Broadcast Frame #define RX_STS_LE 0x00001000 // Length Error #define RX_STS_RF 0x00000800 // Runt Frame #define RX_STS_MF 0x00000400 // Multicast Frame #define RX_STS_TL 0x00000080 // Frame too long #define RX_STS_CS 0x00000040 // Collision Seen #define RX_STS_FT 0x00000020 // Frame Type #define RX_STS_RW 0x00000010 // Receive Watchdog #define RX_STS_ME 0x00000008 // Mii Error #define RX_STS_DB 0x00000004 // Dribbling #define RX_STS_CRC 0x00000002 // CRC Error #define RX_STS_ERROR ( RX_STS_FF \ | RX_STS_ES \ | RX_STS_LE \ | RX_STS_TL \ | RX_STS_CS \ | RX_STS_RW \ | RX_STS_ME \ | RX_STS_DB \ | RX_STS_CRC) static const char FromSMSC951x[] = "smsc951x"; static unsigned s_nDeviceNumber = 0; boolean SMSC951xDeviceWriteReg (TSMSC951xDevice *pThis, u32 nIndex, u32 nValue); boolean SMSC951xDeviceReadReg (TSMSC951xDevice *pThis, u32 nIndex, u32 *pValue); #ifndef NDEBUG void SMSC951xDeviceDumpReg (TSMSC951xDevice *pThis, const char *pName, u32 nIndex); void SMSC951xDeviceDumpRegs (TSMSC951xDevice *pThis); #endif void SMSC951xDevice (TSMSC951xDevice *pThis, TUSBDevice *pDevice) { assert (pThis != 0); USBDeviceCopy (&pThis->m_USBDevice, pDevice); pThis->m_USBDevice.Configure = SMSC951xDeviceConfigure; pThis->m_pEndpointBulkIn = 0; pThis->m_pEndpointBulkOut = 0; pThis->m_pTxBuffer = 0; pThis->m_pTxBuffer = malloc (FRAME_BUFFER_SIZE); assert (pThis->m_pTxBuffer != 0); } void _SMSC951xDevice (TSMSC951xDevice *pThis) { assert (pThis != 0); if (pThis->m_pTxBuffer != 0) { free (pThis->m_pTxBuffer); pThis->m_pTxBuffer = 0; } if (pThis->m_pEndpointBulkOut != 0) { _USBEndpoint (pThis->m_pEndpointBulkOut); free (pThis->m_pEndpointBulkOut); pThis->m_pEndpointBulkOut = 0; } if (pThis->m_pEndpointBulkIn != 0) { _USBEndpoint (pThis->m_pEndpointBulkIn); free (pThis->m_pEndpointBulkIn); pThis->m_pEndpointBulkIn = 0; } _USBDevice (&pThis->m_USBDevice); } boolean SMSC951xDeviceConfigure (TUSBDevice *pUSBDevice) { TSMSC951xDevice *pThis = (TSMSC951xDevice *) pUSBDevice; assert (pThis != 0); u8 MACAddress[MAC_ADDRESS_SIZE]; if (GetMACAddress (MACAddress)) { MACAddressSet (&pThis->m_MACAddress, MACAddress); } else { LogWrite (FromSMSC951x, LOG_ERROR, "Cannot get MAC address"); return FALSE; } TString MACString; String (&MACString); MACAddressFormat (&pThis->m_MACAddress, &MACString); LogWrite (FromSMSC951x, LOG_DEBUG, "MAC address is %s", StringGet (&MACString)); const TUSBConfigurationDescriptor *pConfigDesc = (TUSBConfigurationDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_CONFIGURATION); if ( pConfigDesc == 0 || pConfigDesc->bNumInterfaces != 1) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x); _String (&MACString); return FALSE; } const TUSBInterfaceDescriptor *pInterfaceDesc = (TUSBInterfaceDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_INTERFACE); if ( pInterfaceDesc == 0 || pInterfaceDesc->bInterfaceNumber != 0x00 || pInterfaceDesc->bAlternateSetting != 0x00 || pInterfaceDesc->bNumEndpoints != 3) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x); _String (&MACString); return FALSE; } const TUSBEndpointDescriptor *pEndpointDesc; while ((pEndpointDesc = (TUSBEndpointDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_ENDPOINT)) != 0) { if ((pEndpointDesc->bmAttributes & 0x3F) == 0x02) // Bulk { if ((pEndpointDesc->bEndpointAddress & 0x80) == 0x80) // Input { if (pThis->m_pEndpointBulkIn != 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x); _String (&MACString); return FALSE; } pThis->m_pEndpointBulkIn = (TUSBEndpoint *) malloc (sizeof (TUSBEndpoint)); assert (pThis->m_pEndpointBulkIn); USBEndpoint2 (pThis->m_pEndpointBulkIn, &pThis->m_USBDevice, pEndpointDesc); } else // Output { if (pThis->m_pEndpointBulkOut != 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x); _String (&MACString); return FALSE; } pThis->m_pEndpointBulkOut = (TUSBEndpoint *) malloc (sizeof (TUSBEndpoint)); assert (pThis->m_pEndpointBulkOut); USBEndpoint2 (pThis->m_pEndpointBulkOut, &pThis->m_USBDevice, pEndpointDesc); } } } if ( pThis->m_pEndpointBulkIn == 0 || pThis->m_pEndpointBulkOut == 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x); _String (&MACString); return FALSE; } if (!USBDeviceConfigure (&pThis->m_USBDevice)) { LogWrite (FromSMSC951x, LOG_ERROR, "Cannot set configuration"); _String (&MACString); return FALSE; } u8 MACAddressBuffer[MAC_ADDRESS_SIZE]; MACAddressCopyTo (&pThis->m_MACAddress, MACAddressBuffer); u16 usMACAddressHigh = (u16) MACAddressBuffer[4] | (u16) MACAddressBuffer[5] << 8; u32 nMACAddressLow = (u32) MACAddressBuffer[0] | (u32) MACAddressBuffer[1] << 8 | (u32) MACAddressBuffer[2] << 16 | (u32) MACAddressBuffer[3] << 24; if ( !SMSC951xDeviceWriteReg (pThis, ADDRH, usMACAddressHigh) || !SMSC951xDeviceWriteReg (pThis, ADDRL, nMACAddressLow)) { LogWrite (FromSMSC951x, LOG_ERROR, "Cannot set MAC address"); _String (&MACString); return FALSE; } if ( !SMSC951xDeviceWriteReg (pThis, LED_GPIO_CFG, LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | LED_GPIO_CFG_FDX_LED) || !SMSC951xDeviceWriteReg (pThis, MAC_CR, MAC_CR_RCVOWN //| MAC_CR_PRMS // promiscous mode | MAC_CR_TXEN | MAC_CR_RXEN) || !SMSC951xDeviceWriteReg (pThis, TX_CFG, TX_CFG_ON)) { LogWrite (FromSMSC951x, LOG_ERROR, "Cannot start device"); _String (&MACString); return FALSE; } // TODO: check if PHY is up (wait for it) TString DeviceName; String (&DeviceName); StringFormat (&DeviceName, "eth%u", s_nDeviceNumber++); DeviceNameServiceAddDevice (DeviceNameServiceGet (), StringGet (&DeviceName), pThis, FALSE); _String (&DeviceName); _String (&MACString); return TRUE; } TMACAddress *SMSC951xDeviceGetMACAddress (TSMSC951xDevice *pThis) { assert (pThis != 0); return &pThis->m_MACAddress; } boolean SMSC951xDeviceSendFrame (TSMSC951xDevice *pThis, const void *pBuffer, unsigned nLength) { assert (pThis != 0); if (nLength >= FRAME_BUFFER_SIZE-8) { return FALSE; } assert (pThis->m_pTxBuffer != 0); assert (pBuffer != 0); memcpy (pThis->m_pTxBuffer+8, pBuffer, nLength); *(u32 *) &pThis->m_pTxBuffer[0] = TX_CMD_A_FIRST_SEG | TX_CMD_A_LAST_SEG | nLength; *(u32 *) &pThis->m_pTxBuffer[4] = nLength; assert (pThis->m_pEndpointBulkOut != 0); return DWHCIDeviceTransfer (USBDeviceGetHost (&pThis->m_USBDevice), pThis->m_pEndpointBulkOut, pThis->m_pTxBuffer, nLength+8) >= 0; } boolean SMSC951xDeviceReceiveFrame (TSMSC951xDevice *pThis, void *pBuffer, unsigned *pResultLength) { assert (pThis != 0); assert (pThis->m_pEndpointBulkIn != 0); assert (pBuffer != 0); TUSBRequest URB; USBRequest (&URB, pThis->m_pEndpointBulkIn, pBuffer, FRAME_BUFFER_SIZE, 0); if (!DWHCIDeviceSubmitBlockingRequest (USBDeviceGetHost (&pThis->m_USBDevice), &URB)) { _USBRequest (&URB); return FALSE; } u32 nResultLength = USBRequestGetResultLength (&URB); if (nResultLength < 4) // should not happen with HW_CFG_BIR set { _USBRequest (&URB); return FALSE; } u32 nRxStatus = *(u32 *) pBuffer; if (nRxStatus & RX_STS_ERROR) { LogWrite (FromSMSC951x, LOG_WARNING, "RX error (status 0x%X)", nRxStatus); _USBRequest (&URB); return FALSE; } u32 nFrameLength = RX_STS_FRAMELEN (nRxStatus); assert (nFrameLength == nResultLength-4); assert (nFrameLength > 4); if (nFrameLength <= 4) { _USBRequest (&URB); return FALSE; } nFrameLength -= 4; // ignore CRC //LogWrite (FromSMSC951x, LOG_DEBUG, "Frame received (status 0x%X)", nRxStatus); memcpy (pBuffer, (u8 *) pBuffer + 4, nFrameLength); // overwrite RX status assert (pResultLength != 0); *pResultLength = nFrameLength; _USBRequest (&URB); return TRUE; } boolean SMSC951xDeviceWriteReg (TSMSC951xDevice *pThis, u32 nIndex, u32 nValue) { assert (pThis != 0); return DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_OUT | REQUEST_VENDOR, WRITE_REGISTER, 0, nIndex, &nValue, sizeof nValue) >= 0; } boolean SMSC951xDeviceReadReg (TSMSC951xDevice *pThis, u32 nIndex, u32 *pValue) { assert (pThis != 0); return DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_IN | REQUEST_VENDOR, READ_REGISTER, 0, nIndex, pValue, sizeof *pValue) == (int) sizeof *pValue; } #ifndef NDEBUG void SMSC951xDeviceDumpReg (TSMSC951xDevice *pThis, const char *pName, u32 nIndex) { assert (pThis != 0); u32 nValue; if (!SMSC951xDeviceReadReg (pThis, nIndex, &nValue)) { LogWrite (FromSMSC951x, LOG_ERROR, "Cannot read register 0x%X", nIndex); return; } LogWrite (FromSMSC951x, LOG_DEBUG, "%08X %s", nValue, pName); } void SMSC951xDeviceDumpRegs (TSMSC951xDevice *pThis) { assert (pThis != 0); SMSC951xDeviceDumpReg (pThis, "ID_REV", ID_REV); SMSC951xDeviceDumpReg (pThis, "INT_STS", INT_STS); SMSC951xDeviceDumpReg (pThis, "RX_CFG", RX_CFG); SMSC951xDeviceDumpReg (pThis, "TX_CFG", TX_CFG); SMSC951xDeviceDumpReg (pThis, "HW_CFG", HW_CFG); SMSC951xDeviceDumpReg (pThis, "RX_FIFO_INF", RX_FIFO_INF); SMSC951xDeviceDumpReg (pThis, "PM_CTRL", PM_CTRL); SMSC951xDeviceDumpReg (pThis, "LED_GPIO_CFG", LED_GPIO_CFG); SMSC951xDeviceDumpReg (pThis, "GPIO_CFG", GPIO_CFG); SMSC951xDeviceDumpReg (pThis, "AFC_CFG", AFC_CFG); SMSC951xDeviceDumpReg (pThis, "BURST_CAP", BURST_CAP); SMSC951xDeviceDumpReg (pThis, "INT_EP_CTL", INT_EP_CTL); SMSC951xDeviceDumpReg (pThis, "BULK_IN_DLY", BULK_IN_DLY); SMSC951xDeviceDumpReg (pThis, "MAC_CR", MAC_CR); SMSC951xDeviceDumpReg (pThis, "ADDRH", ADDRH); SMSC951xDeviceDumpReg (pThis, "ADDRL", ADDRL); SMSC951xDeviceDumpReg (pThis, "HASHH", HASHH); SMSC951xDeviceDumpReg (pThis, "HASHL", HASHL); SMSC951xDeviceDumpReg (pThis, "FLOW", FLOW); SMSC951xDeviceDumpReg (pThis, "WUCSR", WUCSR); } #endif