/* * Copyright (c) 2022, aozima * * SPDX-License-Identifier: Apache-2.0 */ /* * Change Logs * Date Author Notes * 2022-04-17 aozima the first version for CherryUSB. */ #include #include "usbh_core.h" #include "axusbnet.h" static const char *DEV_FORMAT = "/dev/u%d"; #define USE_RTTHREAD (1) // #define RX_DUMP // #define TX_DUMP // #define DUMP_RAW #if USE_RTTHREAD #include #include #include #endif /* USE_RTTHREAD */ #define MAX_ADDR_LEN 6 #define ETH_ALEN MAX_ADDR_LEN #define mdelay rt_thread_delay #define msleep rt_thread_delay #define deverr(dev, fmt, ...) USB_LOG_ERR(fmt, ##__VA_ARGS__) #define cpu_to_le16(a) (a) #define le32_to_cpus(a) (a) /* Generic MII registers. */ #define MII_BMCR 0x00 /* Basic mode control register */ #define MII_BMSR 0x01 /* Basic mode status register */ #define MII_PHYSID1 0x02 /* PHYS ID 1 */ #define MII_PHYSID2 0x03 /* PHYS ID 2 */ #define MII_ADVERTISE 0x04 /* Advertisement control reg */ #define MII_LPA 0x05 /* Link partner ability reg */ #define MII_EXPANSION 0x06 /* Expansion register */ #define MII_CTRL1000 0x09 /* 1000BASE-T control */ #define MII_STAT1000 0x0a /* 1000BASE-T status */ #define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ #define MII_MMD_DATA 0x0e /* MMD Access Data Register */ #define MII_ESTATUS 0x0f /* Extended Status */ #define MII_DCOUNTER 0x12 /* Disconnect counter */ #define MII_FCSCOUNTER 0x13 /* False carrier counter */ #define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ #define MII_RERRCOUNTER 0x15 /* Receive error counter */ #define MII_SREVISION 0x16 /* Silicon revision */ #define MII_RESV1 0x17 /* Reserved... */ #define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ #define MII_PHYADDR 0x19 /* PHY address */ #define MII_RESV2 0x1a /* Reserved... */ #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ #define MII_NCONFIG 0x1c /* Network interface config */ /* Basic mode control register. */ #define BMCR_RESV 0x003f /* Unused... */ #define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ #define BMCR_CTST 0x0080 /* Collision test */ #define BMCR_FULLDPLX 0x0100 /* Full duplex */ #define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ #define BMCR_ISOLATE 0x0400 /* Isolate data paths from MII */ #define BMCR_PDOWN 0x0800 /* Enable low power state */ #define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ #define BMCR_SPEED100 0x2000 /* Select 100Mbps */ #define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ #define BMCR_RESET 0x8000 /* Reset to default state */ #define BMCR_SPEED10 0x0000 /* Select 10Mbps */ /* Advertisement control register. */ #define ADVERTISE_SLCT 0x001f /* Selector bits */ #define ADVERTISE_CSMA 0x0001 /* Only selector supported */ #define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ #define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ #define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ #define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ #define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ #define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ #define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ #define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ #define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ #define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ #define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ #define ADVERTISE_RESV 0x1000 /* Unused... */ #define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ #define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ #define ADVERTISE_NPAGE 0x8000 /* Next page bit */ #define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ ADVERTISE_CSMA) #define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ ADVERTISE_100HALF | ADVERTISE_100FULL) struct mii_if_info { int phy_id; }; struct usbnet { #if USE_RTTHREAD /* inherit from ethernet device */ struct eth_device parent; #endif /* USE_RTTHREAD */ struct usbh_axusbnet *class; uint8_t dev_addr[MAX_ADDR_LEN]; uint8_t internalphy:1; uint8_t PhySelect:1; uint8_t OperationMode:1; struct mii_if_info mii; }; typedef struct usbnet * usbnet_t; static struct usbnet usbh_axusbnet_eth_device; #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') static void dump_hex(const void *ptr, uint32_t buflen) { unsigned char *buf = (unsigned char*)ptr; int i, j; for (i=0; itot_len); #ifdef DUMP_RAW const struct pbuf* q; rt_uint32_t i,j; rt_uint8_t *ptr; // rt_kprintf("%s %d byte\n", msg, p->tot_len); i=0; for(q=p; q != RT_NULL; q= q->next) { ptr = q->payload; for(j=0; jlen; j++) { if( (i%8) == 0 ) { rt_kprintf(" "); } if( (i%16) == 0 ) { rt_kprintf("\r\n"); } rt_kprintf("%02X ", *ptr); i++; ptr++; } } rt_kprintf("\n\n"); #endif /* DUMP_RAW */ } #else #define packet_dump(...) #endif /* dump */ static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data) { int ret = 0; struct usbh_hubport *hport = dev->class->hport; struct usb_setup_packet *setup = hport->setup; setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = cmd; setup->wValue = value; setup->wIndex = index; setup->wLength = size; ret = usbh_control_transfer(hport->ep0, setup, (uint8_t *)data); if (ret != 0) { USB_LOG_ERR("%s cmd=%d ret: %d\r\n", __FUNCTION__, cmd, ret); return ret; } _exit: return ret; } static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data) { int ret = 0; struct usbh_hubport *hport = dev->class->hport; struct usb_setup_packet *setup = hport->setup; setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = cmd; setup->wValue = value; setup->wIndex = index; setup->wLength = size; ret = usbh_control_transfer(hport->ep0, setup, (uint8_t *)data); if (ret != 0) { USB_LOG_ERR("%s cmd=%d ret: %d\r\n", __FUNCTION__, cmd, ret); return ret; } _exit: return ret; } static int ax8817x_mdio_read(struct usbnet *dev, int phy_id, int loc) { // struct usbnet *dev = netdev_priv(netdev); u16 res, ret; u8 smsr; int i = 0; // res = kmalloc(2, GFP_ATOMIC); // if (!res) // return 0; do { ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL); msleep(1); // smsr = (u8 *)&ret; ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, &smsr); } while (!(smsr & AX_HOST_EN) && (i++ < 30)); ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (uint16_t)loc, 2, &res); ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL); // ret = *res & 0xffff; // kfree(res); return res; } /* same as above, but converts resulting value to cpu byte order */ static int ax8817x_mdio_read_le(struct usbnet *netdev, int phy_id, int loc) { return (ax8817x_mdio_read(netdev, phy_id, loc)); } static void ax8817x_mdio_write(struct usbnet *dev, int phy_id, int loc, int val) { // struct usbnet *dev = netdev_priv(netdev); u16 res; u8 smsr; int i = 0; // res = kmalloc(2, GFP_ATOMIC); // if (!res) // return; // smsr = (u8 *) res; do { ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL); msleep(1); ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, &smsr); } while (!(smsr & AX_HOST_EN) && (i++ < 30)); // *res = val; res = val; ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (uint16_t)loc, 2, &res); ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL); // kfree(res); } static void ax88772b_mdio_write(struct usbnet *dev, int phy_id, int loc, int val) { // struct usbnet *dev = netdev_priv(netdev); u16 res = val; // res = kmalloc(2, GFP_ATOMIC); // if (!res) // return; // *res = val; ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL); ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (uint16_t)loc, 2, &res); if (loc == MII_ADVERTISE) { res = cpu_to_le16(BMCR_ANENABLE | BMCR_ANRESTART); ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (uint16_t)MII_BMCR, 2, &res); } ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL); // kfree(res); } /* same as above, but converts new value to le16 byte order before writing */ static void ax8817x_mdio_write_le(struct usbnet *netdev, int phy_id, int loc, int val) { ax8817x_mdio_write(netdev, phy_id, loc, cpu_to_le16(val)); } static int access_eeprom_mac(struct usbnet *dev, u8 *buf, u8 offset, bool wflag) { int ret = 0, i; u16 *tmp = (u16 *)buf; if (wflag) { ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_EN, 0, 0, 0, NULL); if (ret < 0) return ret; mdelay(15); } for (i = 0; i < (ETH_ALEN >> 1); i++) { if (wflag) { // u16 wd = cpu_to_le16(*(tmp + i)); u16 wd = (*(tmp + i)); ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM, offset + i, wd, 0, NULL); if (ret < 0) break; mdelay(15); } else { ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, offset + i, 0, 2, tmp + i); if (ret < 0) break; } } if (!wflag) { if (ret < 0) { // #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) // netdev_dbg(dev->net, "Failed to read MAC address from EEPROM: %d\n", ret); // #else // devdbg(dev, "Failed to read MAC address from EEPROM: %d\n", ret); // #endif USB_LOG_ERR("Failed to read MAC address from EEPROM: %d\n", ret); return ret; } // memcpy(dev->net->dev_addr, buf, ETH_ALEN); } else { ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_DIS, 0, 0, 0, NULL); if (ret < 0) return ret; /* reload eeprom data */ ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, AXGPIOS_RSE, 0, 0, NULL); if (ret < 0) return ret; } return 0; } static int ax88772a_phy_powerup(struct usbnet *dev) { int ret; /* set the embedded Ethernet PHY in power-down state */ ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD | AX_SWRESET_IPRL, 0, 0, NULL); if (ret < 0) { deverr(dev, "Failed to power down PHY: %d", ret); return ret; } msleep(10); /* set the embedded Ethernet PHY in power-up state */ ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL, 0, 0, NULL); if (ret < 0) { deverr(dev, "Failed to reset PHY: %d", ret); return ret; } msleep(600); /* set the embedded Ethernet PHY in reset state */ ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR, 0, 0, NULL); if (ret < 0) { deverr(dev, "Failed to power up PHY: %d", ret); return ret; } /* set the embedded Ethernet PHY in power-up state */ ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL, 0, 0, NULL); if (ret < 0) { deverr(dev, "Failed to reset PHY: %d", ret); return ret; } return 0; } static int ax88772b_reset(struct usbnet *dev) { int ret; ret = ax88772a_phy_powerup(dev); if (ret < 0) return ret; /* Set the MAC address */ ret = ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, dev->dev_addr); if (ret < 0) { deverr(dev, "set MAC address failed: %d", ret); } /* stop MAC operation */ ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP, 0, 0, NULL); if (ret < 0){ deverr(dev, "Reset RX_CTL failed: %d", ret); } ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, AX88772_MEDIUM_DEFAULT, 0, 0, NULL); if (ret < 0){ deverr(dev, "Write medium mode register: %d", ret); } return ret; } static int ax8817x_get_mac(struct usbnet *dev, u8 *buf) { int ret, i; ret = access_eeprom_mac(dev, buf, 0x04, 0); if (ret < 0) goto out; // if (ax8817x_check_ether_addr(dev)) { // ret = access_eeprom_mac(dev, dev->net->dev_addr, 0x04, 1); // if (ret < 0) { // deverr(dev, "Failed to write MAC to EEPROM: %d", ret); // goto out; // } // msleep(5); // ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID, // 0, 0, ETH_ALEN, buf); // if (ret < 0) { // deverr(dev, "Failed to read MAC address: %d", ret); // goto out; // } // for (i = 0; i < ETH_ALEN; i++) // if (*(dev->net->dev_addr + i) != *((u8 *)buf + i)) { // devwarn(dev, "Found invalid EEPROM part or non-EEPROM"); // break; // } // } // memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); // /* Set the MAC address */ // ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID, 0, 0, // ETH_ALEN, dev->net->dev_addr); // if (ret < 0) { // deverr(dev, "Failed to write MAC address: %d", ret); // goto out; // } return 0; out: return ret; } #if USE_RTTHREAD static rt_err_t rt_rndis_eth_init(rt_device_t dev) { return RT_EOK; } static rt_err_t rt_rndis_eth_open(rt_device_t dev, rt_uint16_t oflag) { return RT_EOK; } static rt_err_t rt_rndis_eth_close(rt_device_t dev) { return RT_EOK; } static rt_size_t rt_rndis_eth_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { rt_set_errno(-RT_ENOSYS); return 0; } static rt_size_t rt_rndis_eth_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { rt_set_errno(-RT_ENOSYS); return 0; } static rt_err_t rt_rndis_eth_control(rt_device_t dev, int cmd, void *args) { usbnet_t rndis_eth_dev = (usbnet_t)dev; USB_LOG_INFO("%s L%d\r\n", __FUNCTION__, __LINE__); switch(cmd) { case NIOCTL_GADDR: /* get mac address */ if(args) { USB_LOG_INFO("%s L%d NIOCTL_GADDR\r\n", __FUNCTION__, __LINE__); rt_memcpy(args, rndis_eth_dev->dev_addr, MAX_ADDR_LEN); } else { return -RT_ERROR; } break; default : break; } return RT_EOK; } /* reception packet. */ static struct pbuf *rt_rndis_eth_rx(rt_device_t dev) { struct pbuf* p = RT_NULL; // USB_LOG_INFO("%s L%d\r\n", __FUNCTION__, __LINE__); return p; } /* transmit packet. */ static rt_err_t rt_rndis_eth_tx(rt_device_t dev, struct pbuf* p) { int ret = 0; rt_err_t result = RT_EOK; uint8_t *tmp_buf = RT_NULL; usbnet_t rndis_eth = (usbnet_t)dev; struct usbh_axusbnet *class = rndis_eth->class; rt_tick_t tick_start, tick_end; uint8_t int_notify_buf[8]; #ifdef TX_DUMP packet_dump("TX", p); #endif /* TX_DUMP */ tmp_buf = (uint8_t *)rt_malloc(16 + p->tot_len ); if (!tmp_buf) { USB_LOG_INFO("[%s L%d], no memory for pbuf, len=%d.", __FUNCTION__, __LINE__, p->tot_len); goto _exit; } uint32_t slen = p->tot_len; uint32_t head = slen; head = ((head ^ 0x0000ffff) << 16) + (head); tmp_buf[0] = head & 0xFF; tmp_buf[1] = (head >> 8) & 0xFF; tmp_buf[2] = (head >> 16) & 0xFF; tmp_buf[3] = (head >> 24) & 0xFF; slen += 4; int padlen = ((p->tot_len + 4) % 512) ? 0 : 4; if (padlen) { tmp_buf[4 + slen + 0] = 0x00; tmp_buf[4 + slen + 1] = 0x00; tmp_buf[4 + slen + 2] = 0xFF; tmp_buf[4 + slen + 3] = 0xFF; slen += 4; } pbuf_copy_partial(p, tmp_buf + 4, p->tot_len, 0); tick_start = rt_tick_get(); ret = usbh_ep_bulk_transfer(class->bulkout, tmp_buf, slen, 500); if (ret < 0) { result = -RT_EIO; USB_LOG_ERR("%s L%d send over ret:%d\r\n", __FUNCTION__, __LINE__, ret); goto _exit; } tick_end = rt_tick_get(); _exit: if(tmp_buf) { rt_free(tmp_buf); } return result; } #ifdef RT_USING_DEVICE_OPS const static struct rt_device_ops rndis_device_ops = { rt_rndis_eth_init, rt_rndis_eth_open, rt_rndis_eth_close, rt_rndis_eth_read, rt_rndis_eth_write, rt_rndis_eth_control } #endif /* RT_USING_DEVICE_OPS */ #endif /* USE_RTTHREAD */ static void rt_thread_axusbnet_entry(void *parameter) { int ret; struct usbh_hubport *hport; uint8_t intf; uint8_t buf[2+8]; USB_LOG_INFO("%s L%d\r\n", __FUNCTION__, __LINE__); rt_thread_delay(200); USB_LOG_INFO("%s L%d\r\n\r\n\r\n\r\n", __FUNCTION__, __LINE__); const char *dname = "/dev/u0"; struct usbh_axusbnet *class = (struct usbh_axusbnet *)usbh_find_class_instance(dname); if (class == NULL) { USB_LOG_ERR("do not find %s\r\n", dname); return; } USB_LOG_INFO("axusbnet=%p\r\n", dname); usbh_axusbnet_eth_device.class = class; struct usbnet *dev = &usbh_axusbnet_eth_device; ret = ax8817x_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf); if (ret < 0) { USB_LOG_ERR("AX_CMD_SW_PHY_STATUS ret=%d\r\n", ret); return; } u8 tempphyselect = buf[0]; if (tempphyselect == AX_PHYSEL_SSRMII) { USB_LOG_ERR("%s L%d AX_PHYSEL_SSRMII\r\n", __FUNCTION__, __LINE__); dev->internalphy = false; return; // dev->OperationMode = OPERATION_MAC_MODE; // dev->PhySelect = 0x00; } else if (tempphyselect == AX_PHYSEL_SSRRMII) { USB_LOG_ERR("%s L%d AX_PHYSEL_SSRRMII\r\n", __FUNCTION__, __LINE__); dev->internalphy = true; return; // dev->OperationMode = OPERATION_PHY_MODE; // dev->PhySelect = 0x00; } else if (tempphyselect == AX_PHYSEL_SSMII) { USB_LOG_INFO("%s L%d internalphy AX_PHYSEL_SSMII & OPERATION_MAC_MODE\r\n", __FUNCTION__, __LINE__); dev->internalphy = true; dev->OperationMode = OPERATION_MAC_MODE; dev->PhySelect = 0x01; } else { // deverr(dev, "Unknown MII type\n"); USB_LOG_INFO("%s L%d Unknown MII type\r\n", __FUNCTION__, __LINE__); return; } /* reload eeprom data */ ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, AXGPIOS_RSE, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("reload eeprom data ret=%d\r\n", ret); return; } /* Get the EEPROM data: power saving configuration*/ ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x18, 0, 2, buf); if (ret < 0) { USB_LOG_ERR("read SROM address 18h failed: %d\r\n", ret); goto err_out; } USB_LOG_INFO("reading AX88772C psc: %02x %02x\r\n", buf[0], buf[1]); // le16_to_cpus(tmp16); // ax772b_data->psc = *tmp16 & 0xFF00; /* End of get EEPROM data */ ret = ax8817x_get_mac(dev, buf); if (ret < 0) { USB_LOG_ERR("Get HW address failed: %d\r\n", ret); return; } dump_hex(buf, ETH_ALEN); memcpy(dev->dev_addr, buf, ETH_ALEN); uint16_t chipcode = 0xFFFF; { uint16_t smsr; // asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &chipcode, 0); ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, &chipcode); USB_LOG_ERR("AX_CMD_READ_STATMNGSTS_REG ret: %d %04X\r\n", ret, chipcode); // #define AX_CHIPCODE_MASK 0x70 // #define AX_AX88772_CHIPCODE 0x00 // #define AX_AX88772A_CHIPCODE 0x10 // #define AX_AX88772B_CHIPCODE 0x20 // #define AX_HOST_EN 0x01 chipcode &= 0x70;//AX_CHIPCODE_MASK; AX_AX88772_CHIPCODE if(chipcode == 0x00) { USB_LOG_ERR("AX_CMD_READ_STATMNGSTS_REG AX_AX88772_CHIPCODE\r\n"); } else if(chipcode == 0x10) { USB_LOG_ERR("AX_CMD_READ_STATMNGSTS_REG AX_AX88772A_CHIPCODE\r\n"); } else if(chipcode == 0x20) { USB_LOG_ERR("AX_CMD_READ_STATMNGSTS_REG AX_AX88772B_CHIPCODE\r\n"); } } /* Get the PHY id: E0 10 */ ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); if (ret < 0) { USB_LOG_ERR("Error reading PHY ID: %02x\r\n", ret); return; } if (dev->internalphy) { dev->mii.phy_id = *((u8 *)buf + 1); } else { dev->mii.phy_id = *((u8 *)buf); } USB_LOG_INFO("reading %s PHY ID: %02x\r\n", dev->internalphy?"internal":"external", dev->mii.phy_id); ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, dev->PhySelect, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("Select PHY #1 failed: %d", ret); return; } ret = ax88772a_phy_powerup(dev); if (ret < 0) { USB_LOG_ERR("ax88772a_phy_powerup failed: %d", ret); return; } /* stop MAC operation */ ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("Reset RX_CTL failed: %d", ret); goto err_out; } /* make sure the driver can enable sw mii operation */ ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("Enabling software MII failed: %d\r\n", ret); goto err_out; } if ((dev->OperationMode == OPERATION_MAC_MODE) && (dev->PhySelect == 0x00)) { USB_LOG_ERR("not support the external phy\r\n"); goto err_out; } if (dev->OperationMode == OPERATION_PHY_MODE) { ax8817x_mdio_write_le(dev, dev->mii.phy_id, MII_BMCR, 0x3900); } if (dev->mii.phy_id != 0x10) { USB_LOG_ERR("not support phy_id != 0x10\r\n"); // ax8817x_mdio_write_le(dev->net, 0x10, MII_BMCR, 0x3900); } if (dev->mii.phy_id == 0x10 && dev->OperationMode != OPERATION_PHY_MODE) { u16 tmp16 = ax8817x_mdio_read_le(dev, dev->mii.phy_id, 0x12); ax8817x_mdio_write_le(dev, dev->mii.phy_id, 0x12, ((tmp16 & 0xFF9F) | 0x0040)); } ax8817x_mdio_write_le(dev, dev->mii.phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); // mii_nway_restart(&dev->mii); { /* if autoneg is off, it's an error */ uint16_t bmcr = ax8817x_mdio_read_le(dev, dev->mii.phy_id, MII_BMCR); if (bmcr & BMCR_ANENABLE) { bmcr |= BMCR_ANRESTART; USB_LOG_ERR("BMCR_ANENABLE ==> BMCR_ANRESTART\r\n"); ax8817x_mdio_write_le(dev, dev->mii.phy_id, MII_BMCR, bmcr); } else { USB_LOG_ERR("not BMCR_ANENABLE BMCR=%04X\r\n", bmcr); } } ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("Failed to write medium mode: %d", ret); goto err_out; } ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772A_IPG0_DEFAULT | AX88772A_IPG1_DEFAULT << 8, AX88772A_IPG2_DEFAULT, 0, NULL); if (ret < 0) { USB_LOG_ERR("Failed to write interframe gap: %d", ret); goto err_out; } memset(buf, 0, 4); ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf); *((u8 *)buf + 3) = 0x00; if (ret < 0) { USB_LOG_ERR("Failed to read IPG,IPG1,IPG2 failed: %d", ret); goto err_out; } else { uint32_t tmp32 = *((u32*)buf); le32_to_cpus(&tmp32); if (tmp32 != (AX88772A_IPG2_DEFAULT << 16 | AX88772A_IPG1_DEFAULT << 8 | AX88772A_IPG0_DEFAULT)) { USB_LOG_ERR("Non-authentic ASIX product\nASIX does not support it\n"); // ret = -ENODEV; goto err_out; } } // TODO: optimized for high speed. ret = ax8817x_write_cmd(dev, 0x2A, 0x8000, 0x8001, 0, NULL); if (ret < 0) { USB_LOG_ERR("Reset RX_CTL failed: %d", ret); goto err_out; } ret = ax88772b_reset(dev); if (ret < 0) { USB_LOG_ERR("ax88772b_reset failed: %d", ret); goto err_out; } // OUT 29 0 0 0 AX_CMD_WRITE_MONITOR_MODE ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, 0, 0, 0, NULL); if (ret < 0) { deverr(dev, "AX_CMD_WRITE_MONITOR_MODE failed: %d", ret); } /* Set the MAC address */ ret = ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, dev->dev_addr); if (ret < 0) { deverr(dev, "set MAC address failed: %d", ret); } // update Multicast AX_CMD_WRITE_MULTI_FILTER. const uint8_t multi_filter[] = {0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x40}; ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, AX_MCAST_FILTER_SIZE, (void *)multi_filter); if (ret < 0) { USB_LOG_ERR("Reset RX_CTL failed: %d", ret); goto err_out; } /* Configure RX header type */ // u16 rx_reg = (AX_RX_CTL_PRO | AX_RX_CTL_AMALL | AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_HEADER_DEFAULT); u16 rx_reg = (AX_RX_CTL_AB | AX_RX_CTL_AM | AX_RX_CTL_START); ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, rx_reg, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("Reset RX_CTL failed: %d", ret); goto err_out; } /* set the embedded Ethernet PHY in power-up state */ // ax772b_data->psc = *tmp16 & 0xFF00; // psc: 15 5a AX88772C psc: %02x %02x\r\n", buf[0], buf[1]); ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | (0x5a00 & 0x7FFF), 0, 0, NULL); if (ret < 0) { deverr(dev, "Failed to reset PHY: %d", ret); // return ret; } rt_thread_delay(1000); u16 mode = AX88772_MEDIUM_DEFAULT; ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); if (ret < 0) { USB_LOG_ERR("AX_CMD_WRITE_MEDIUM_MODE failed: %d", ret); goto err_out; } #if USE_RTTHREAD #ifdef RT_USING_DEVICE_OPS usbh_axusbnet_eth_device.parent.parent.ops = &rndis_device_ops; #else usbh_axusbnet_eth_device.parent.parent.init = rt_rndis_eth_init; usbh_axusbnet_eth_device.parent.parent.open = rt_rndis_eth_open; usbh_axusbnet_eth_device.parent.parent.close = rt_rndis_eth_close; usbh_axusbnet_eth_device.parent.parent.read = rt_rndis_eth_read; usbh_axusbnet_eth_device.parent.parent.write = rt_rndis_eth_write; usbh_axusbnet_eth_device.parent.parent.control = rt_rndis_eth_control; #endif usbh_axusbnet_eth_device.parent.parent.user_data = RT_NULL; usbh_axusbnet_eth_device.parent.eth_rx = rt_rndis_eth_rx; usbh_axusbnet_eth_device.parent.eth_tx = rt_rndis_eth_tx; usbh_axusbnet_eth_device.class = class; eth_device_init(&usbh_axusbnet_eth_device.parent, "u0"); eth_device_linkchange(&usbh_axusbnet_eth_device.parent, RT_FALSE); #endif /* USE_RTTHREAD */ // check link status. { u16 bmcr = ax8817x_mdio_read_le(dev, dev->mii.phy_id, MII_BMCR); u16 mode = AX88772_MEDIUM_DEFAULT; USB_LOG_ERR("%s L%d MII_BMCR=%04X\r\n", __FUNCTION__, __LINE__, bmcr); if (!(bmcr & BMCR_FULLDPLX)) { mode &= ~AX88772_MEDIUM_FULL_DUPLEX; USB_LOG_ERR("%s L%d not AX88772_MEDIUM_FULL_DUPLEX\r\n", __FUNCTION__, __LINE__); } if (!(bmcr & BMCR_SPEED100)) { mode &= ~AX88772_MEDIUM_100MB; USB_LOG_ERR("%s L%d not AX88772_MEDIUM_100MB\r\n", __FUNCTION__, __LINE__); } ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); } while (1) { // USB_LOG_INFO("%s L%d\r\n", __FUNCTION__, __LINE__); ret = usbh_ep_bulk_transfer(class->bulkin, class->bulkin_buf, sizeof(class->bulkin_buf), 1000); if (ret < 0) { if (ret != -2) { USB_LOG_ERR("%s L%d bulk in error ret=%d\r\n", __FUNCTION__, __LINE__, ret); } continue; } { const uint8_t *data = class->bulkin_buf; uint16_t len1, len2; len1 = data[0] | ((uint16_t)(data[1])<<8); len2 = data[2] | ((uint16_t)(data[3])<<8); // USB_LOG_INFO("transfer bulkin len1:%04X, len2:%04X, len2':%04X.\r\n", len1, len2, ~len2); len1 &= 0x07ff; if (data[0] != ((uint8_t)(~data[2]))) { USB_LOG_ERR("transfer bulkin len1:%04X, len2:%04X, len2':%04X.\r\n", len1, len2, ~len2); dump_hex(data, 32); continue; } #if !USE_RTTHREAD { static uint32_t count = 0; USB_LOG_INFO("recv: #%d, len=%d\r\n", count, ret); dump_hex(data+4, 32); if ((count % 10) == 0) { // 192.168.89.14 ==> 255.255.255.255:7 echo hello world! const uint8_t packet_bytes[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x36, 0xb0, 0xfd, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x59, 0x0e, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0x00, 0x07, 0x00, 0x22, 0x53, 0x06, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x41, 0x58, 0x38, 0x38, 0x37, 0x37, 0x32, 0x43, 0x2e }; uint8_t *send_buf = (uint8_t *)class->bulkin_buf; send_buf[0] = sizeof(packet_bytes); send_buf[1] = sizeof(packet_bytes) >> 8; send_buf[2] = ~send_buf[0]; send_buf[3] = ~send_buf[1]; memcpy(send_buf+4, packet_bytes, sizeof(packet_bytes)); memcpy(send_buf+4+6, dev->dev_addr, 6);// update src mac. ret = usbh_ep_bulk_transfer(class->bulkout, send_buf, 4 + sizeof(packet_bytes), 500); USB_LOG_INFO("bulkout, ret=%d\r\n", ret); dump_hex(send_buf, 64); } count++; } #endif /* RT-Thread */ #if USE_RTTHREAD { static uint32_t count = 0; if (count == 0) { eth_device_linkchange(&usbh_axusbnet_eth_device.parent, RT_TRUE); } count++; } /* allocate buffer */ struct pbuf *p = RT_NULL; p = pbuf_alloc(PBUF_LINK, len1, PBUF_RAM); if (p != NULL) { pbuf_take(p, data + 4, len1); #ifdef RX_DUMP packet_dump("RX", p); #endif /* RX_DUMP */ struct eth_device *eth_dev = &usbh_axusbnet_eth_device.parent; if ((eth_dev->netif->input(p, eth_dev->netif)) != ERR_OK) { USB_LOG_INFO("F:%s L:%d IP input error\r\n", __FUNCTION__, __LINE__); pbuf_free(p); p = RT_NULL; } // USB_LOG_INFO("%s L%d input OK\r\n", __FUNCTION__, __LINE__); } else { USB_LOG_ERR("%s L%d pbuf_alloc NULL\r\n", __FUNCTION__, __LINE__); } #endif /* RT-Thread */ } } // while (1) err_out: out2: return; } static int axusbnet_startup(void) { const char *tname = "axusbnet"; usb_osal_thread_t usb_thread; usb_thread = usb_osal_thread_create(tname, 1024 * 6, CONFIG_USBHOST_PSC_PRIO, rt_thread_axusbnet_entry, NULL); if (usb_thread == NULL) { return -1; } return 0; } static int usbh_axusbnet_connect(struct usbh_hubport *hport, uint8_t intf) { int ret = 0; struct usbh_endpoint_cfg ep_cfg = { 0 }; struct usb_endpoint_descriptor *ep_desc; USB_LOG_INFO("%s %d\r\n", __FUNCTION__, __LINE__); struct usbh_axusbnet *class = usb_malloc(sizeof(struct usbh_axusbnet)); if (class == NULL) { USB_LOG_ERR("Fail to alloc class\r\n"); return -ENOMEM; } memset(class, 0, sizeof(struct usbh_axusbnet)); class->hport = hport; class->intf = intf; snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, intf); USB_LOG_INFO("Register axusbnet Class:%s\r\n", hport->config.intf[intf].devname); hport->config.intf[intf].priv = class; #if 1 USB_LOG_INFO("hport=%p, intf=%d, intf_desc.bNumEndpoints:%d\r\n", hport, intf, hport->config.intf[intf].intf_desc.bNumEndpoints); for (uint8_t i = 0; i < hport->config.intf[intf].intf_desc.bNumEndpoints; i++) { ep_desc = &hport->config.intf[intf].ep[i].ep_desc; USB_LOG_INFO("ep[%d] bLength=%d, type=%d\r\n", i, ep_desc->bLength, ep_desc->bDescriptorType); USB_LOG_INFO("ep_addr=%02X, attr=%02X\r\n", ep_desc->bEndpointAddress, ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK); USB_LOG_INFO("wMaxPacketSize=%d, bInterval=%d\r\n\r\n", ep_desc->wMaxPacketSize, ep_desc->bInterval); } #endif for (uint8_t i = 0; i < hport->config.intf[intf].intf_desc.bNumEndpoints; i++) { ep_desc = &hport->config.intf[intf].ep[i].ep_desc; ep_cfg.ep_addr = ep_desc->bEndpointAddress; ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; ep_cfg.ep_mps = ep_desc->wMaxPacketSize; ep_cfg.ep_interval = ep_desc->bInterval; ep_cfg.hport = hport; if(ep_cfg.ep_type == USB_ENDPOINT_TYPE_BULK) { if (ep_desc->bEndpointAddress & 0x80) { usbh_pipe_alloc(&class->bulkin, &ep_cfg); } else { usbh_pipe_alloc(&class->bulkout, &ep_cfg); } } else { usbh_pipe_alloc(&class->int_notify, &ep_cfg); } } axusbnet_startup(); return ret; } static int usbh_axusbnet_disconnect(struct usbh_hubport *hport, uint8_t intf) { int ret = 0; USB_LOG_ERR("TBD: %s %d\r\n", __FUNCTION__, __LINE__); return ret; } // Class:0xff,Subclass:0xff,Protocl:0x00 static const struct usbh_class_driver axusbnet_class_driver = { .driver_name = "axusbnet", .connect = usbh_axusbnet_connect, .disconnect = usbh_axusbnet_disconnect }; CLASS_INFO_DEFINE const struct usbh_class_info axusbnet_class_info = { .match_flags = USB_CLASS_MATCH_VENDOR | USB_CLASS_MATCH_PRODUCT | USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, .class = USB_DEVICE_CLASS_VEND_SPECIFIC, .subclass = 0xff, .protocol = 0x00, .vid = 0x0b95, .pid = 0x772b, .class_driver = &axusbnet_class_driver };