+// SPDX-License-Identifier: GPL-2.0+
/*
* ether.c -- Ethernet gadget driver, with CDC and non-CDC options
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <console.h>
+#include <env.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/usb/ch9.h>
#include "rndis.h"
#include <dm.h>
+#include <dm/lists.h>
#include <dm/uclass-internal.h>
#include <dm/device-internal.h>
* RNDIS specs are ambiguous and appear to be incomplete, and are also
* needlessly complex. They borrow more from CDC ACM than CDC ECM.
*/
-#define ETH_ALEN 6 /* Octets in one ethernet addr */
-#define ETH_HLEN 14 /* Total octets in header. */
-#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
-#define ETH_DATA_LEN 1500 /* Max. octets in payload */
-#define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */
#define DRIVER_DESC "Ethernet Gadget"
/* Based on linux 2.6.27 version */
#define DRIVER_VERSION "May Day 2005"
-static const char shortname[] = "ether";
static const char driver_desc[] = DRIVER_DESC;
#define RX_EXTRA 20 /* guard against rx overflows */
struct usb_gadget *gadget;
struct usb_request *req; /* for control responses */
struct usb_request *stat_req; /* for cdc & rndis status */
-#ifdef CONFIG_DM_USB
- struct udevice *usb_udev;
-#endif
u8 config;
struct usb_ep *in_ep, *out_ep, *status_ep;
struct usb_request *tx_req, *rx_req;
+#ifndef CONFIG_DM_ETH
struct eth_device *net;
+#else
+ struct udevice *net;
+#endif
struct net_device_stats stats;
unsigned int tx_qlen;
/*-------------------------------------------------------------------------*/
struct ether_priv {
struct eth_dev ethdev;
+#ifndef CONFIG_DM_ETH
struct eth_device netdev;
+#else
+ struct udevice *netdev;
+#endif
struct usb_gadget_driver eth_driver;
};
* static ushort idProduct;
*/
-#if defined(CONFIG_USBNET_MANUFACTURER)
-static char *iManufacturer = CONFIG_USBNET_MANUFACTURER;
+#if defined(CONFIG_USB_GADGET_MANUFACTURER)
+static char *iManufacturer = CONFIG_USB_GADGET_MANUFACTURER;
#else
static char *iManufacturer = "U-Boot";
#endif
* can't really use its struct. All we do here is say that we're using
* the submode of "SAFE" which directly matches the CDC Subset.
*/
+#ifdef CONFIG_USB_ETH_SUBSET
static const u8 mdlm_detail_desc[] = {
6,
USB_DT_CS_INTERFACE,
0, /* network control capabilities (none) */
0, /* network data capabilities ("raw" encapsulation) */
};
+#endif
#endif
/* this descriptor actually adds value, surprise! */
.iMACAddress = STRING_ETHADDR,
.bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */
- .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN),
+ .wMaxSegmentSize = __constant_cpu_to_le16(PKTSIZE_ALIGN),
.wNumberMCFilters = __constant_cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
&& dev->config
&& dev->tx_qlen != 0) {
/* tx fifo is full, but we can't clear it...*/
- error("can't change configurations");
+ pr_err("can't change configurations");
return -ESPIPE;
}
eth_reset_config(dev);
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
status = rndis_msg_parser(dev->rndis_config, (u8 *) req->buf);
if (status < 0)
- error("%s: rndis parse error %d", __func__, status);
+ pr_err("%s: rndis parse error %d", __func__, status);
}
#endif /* RNDIS */
retval = usb_ep_queue(dev->out_ep, req, gfp_flags);
if (retval)
- error("rx submit --> %d", retval);
+ pr_err("rx submit --> %d", retval);
return retval;
}
req->length -= length;
req->actual -= length;
}
- if (req->actual < ETH_HLEN || ETH_FRAME_LEN < req->actual) {
+ if (req->actual < ETH_HLEN || PKTSIZE_ALIGN < req->actual) {
length_err:
dev->stats.rx_errors++;
dev->stats.rx_length_errors++;
fail2:
usb_ep_free_request(dev->in_ep, dev->tx_req);
fail1:
- error("can't alloc requests");
+ pr_err("can't alloc requests");
return -1;
}
static char rndis_resp_buf[8] __attribute__((aligned(sizeof(__le32))));
+#ifndef CONFIG_DM_ETH
static int rndis_control_ack(struct eth_device *net)
+#else
+static int rndis_control_ack(struct udevice *net)
+#endif
{
struct ether_priv *priv = (struct ether_priv *)net->priv;
struct eth_dev *dev = &priv->ethdev;
int status = -ENOMEM;
int gcnum;
u8 tmp[7];
+#ifdef CONFIG_DM_ETH
+ struct eth_pdata *pdata = dev_get_platdata(l_priv->netdev);
+#endif
/* these flags are only ever cleared; compiler take note */
#ifndef CONFIG_USB_ETH_CDC
* anything less functional on CDC-capable hardware,
* so we fail in this case.
*/
- error("controller '%s' not recognized",
+ pr_err("controller '%s' not recognized",
gadget->name);
return -ENODEV;
}
* to choose the right configuration otherwise.
*/
if (rndis) {
-#if defined(CONFIG_USB_RNDIS_VENDOR_ID) && defined(CONFIG_USB_RNDIS_PRODUCT_ID)
+#if defined(CONFIG_USB_GADGET_VENDOR_NUM) && defined(CONFIG_USB_GADGET_PRODUCT_NUM)
device_desc.idVendor =
- __constant_cpu_to_le16(CONFIG_USB_RNDIS_VENDOR_ID);
+ __constant_cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM);
device_desc.idProduct =
- __constant_cpu_to_le16(CONFIG_USB_RNDIS_PRODUCT_ID);
+ __constant_cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM);
#else
device_desc.idVendor =
__constant_cpu_to_le16(RNDIS_VENDOR_NUM);
* supporting one submode of the "SAFE" variant of MDLM.)
*/
} else {
-#if defined(CONFIG_USB_CDC_VENDOR_ID) && defined(CONFIG_USB_CDC_PRODUCT_ID)
- device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID);
- device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID);
+#if defined(CONFIG_USB_GADGET_VENDOR_NUM) && defined(CONFIG_USB_GADGET_PRODUCT_NUM)
+ device_desc.idVendor = cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM);
+ device_desc.idProduct = cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM);
#else
if (!cdc) {
device_desc.idVendor =
in_ep = usb_ep_autoconfig(gadget, &fs_source_desc);
if (!in_ep) {
autoconf_fail:
- error("can't autoconfigure on %s\n",
+ pr_err("can't autoconfigure on %s\n",
gadget->name);
return -ENODEV;
}
if (status_ep) {
status_ep->driver_data = status_ep; /* claim */
} else if (rndis) {
- error("can't run RNDIS on %s", gadget->name);
+ pr_err("can't run RNDIS on %s", gadget->name);
return -ENODEV;
#ifdef CONFIG_USB_ETH_CDC
} else if (cdc) {
/* network device setup */
+#ifndef CONFIG_DM_ETH
dev->net = &l_priv->netdev;
+#else
+ dev->net = l_priv->netdev;
+#endif
dev->cdc = cdc;
dev->zlp = zlp;
dev->out_ep = out_ep;
dev->status_ep = status_ep;
+ memset(tmp, 0, sizeof(tmp));
/*
* Module params for these addresses should come from ID proms.
* The host side address is used with CDC and RNDIS, and commonly
* host side code for the SAFE thing cares -- its original BLAN
* thing didn't, Sharp never assigned those addresses on Zaurii.
*/
+#ifndef CONFIG_DM_ETH
get_ether_addr(dev_addr, dev->net->enetaddr);
-
- memset(tmp, 0, sizeof(tmp));
memcpy(tmp, dev->net->enetaddr, sizeof(dev->net->enetaddr));
+#else
+ get_ether_addr(dev_addr, pdata->enetaddr);
+ memcpy(tmp, pdata->enetaddr, sizeof(pdata->enetaddr));
+#endif
get_ether_addr(host_addr, dev->host_mac);
if (rndis) {
status = rndis_init();
if (status < 0) {
- error("can't init RNDIS, %d", status);
+ pr_err("can't init RNDIS, %d", status);
goto fail;
}
}
status_ep ? " STATUS " : "",
status_ep ? status_ep->name : ""
);
- printf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
- dev->net->enetaddr[0], dev->net->enetaddr[1],
- dev->net->enetaddr[2], dev->net->enetaddr[3],
- dev->net->enetaddr[4], dev->net->enetaddr[5]);
+#ifndef CONFIG_DM_ETH
+ printf("MAC %pM\n", dev->net->enetaddr);
+#else
+ printf("MAC %pM\n", pdata->enetaddr);
+#endif
if (cdc || rndis)
printf("HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
return 0;
fail:
- error("%s failed, status = %d", __func__, status);
+ pr_err("%s failed, status = %d", __func__, status);
eth_unbind(gadget);
return status;
}
/*-------------------------------------------------------------------------*/
-
-#ifdef CONFIG_DM_USB
-int dm_usb_init(struct eth_dev *e_dev)
-{
- struct udevice *dev = NULL;
- int ret;
-
- ret = uclass_first_device(UCLASS_USB_DEV_GENERIC, &dev);
- if (!dev || ret) {
- error("No USB device found\n");
- return -ENODEV;
- }
-
- e_dev->usb_udev = dev;
-
- return ret;
-}
-#endif
+static void _usb_eth_halt(struct ether_priv *priv);
static int _usb_eth_init(struct ether_priv *priv)
{
struct eth_dev *dev = &priv->ethdev;
struct usb_gadget *gadget;
unsigned long ts;
+ int ret;
unsigned long timeout = USB_CONNECT_TIMEOUT;
-#ifdef CONFIG_DM_USB
- if (dm_usb_init(dev)) {
- error("USB ether not found\n");
- return -ENODEV;
- }
-#else
- board_usb_init(0, USB_INIT_DEVICE);
-#endif
+ ret = usb_gadget_initialize(0);
+ if (ret)
+ return ret;
/* Configure default mac-addresses for the USB ethernet device */
#ifdef CONFIG_USBNET_DEV_ADDR
strlcpy(host_addr, CONFIG_USBNET_HOST_ADDR, sizeof(host_addr));
#endif
/* Check if the user overruled the MAC addresses */
- if (getenv("usbnet_devaddr"))
- strlcpy(dev_addr, getenv("usbnet_devaddr"),
+ if (env_get("usbnet_devaddr"))
+ strlcpy(dev_addr, env_get("usbnet_devaddr"),
sizeof(dev_addr));
- if (getenv("usbnet_hostaddr"))
- strlcpy(host_addr, getenv("usbnet_hostaddr"),
+ if (env_get("usbnet_hostaddr"))
+ strlcpy(host_addr, env_get("usbnet_hostaddr"),
sizeof(host_addr));
if (!is_eth_addr_valid(dev_addr)) {
- error("Need valid 'usbnet_devaddr' to be set");
+ pr_err("Need valid 'usbnet_devaddr' to be set");
goto fail;
}
if (!is_eth_addr_valid(host_addr)) {
- error("Need valid 'usbnet_hostaddr' to be set");
+ pr_err("Need valid 'usbnet_hostaddr' to be set");
goto fail;
}
gadget = dev->gadget;
usb_gadget_connect(gadget);
- if (getenv("cdc_connect_timeout"))
- timeout = simple_strtoul(getenv("cdc_connect_timeout"),
+ if (env_get("cdc_connect_timeout"))
+ timeout = simple_strtoul(env_get("cdc_connect_timeout"),
NULL, 10) * CONFIG_SYS_HZ;
ts = get_timer(0);
while (!dev->network_started) {
/* Handle control-c and timeouts */
if (ctrlc() || (get_timer(ts) > timeout)) {
- error("The remote end did not respond in time.");
+ pr_err("The remote end did not respond in time.");
goto fail;
}
usb_gadget_handle_interrupts(0);
rx_submit(dev, dev->rx_req, 0);
return 0;
fail:
+ _usb_eth_halt(priv);
return -1;
}
rndis_pkt = malloc(length +
sizeof(struct rndis_packet_msg_type));
if (!rndis_pkt) {
- error("No memory to alloc RNDIS packet");
+ pr_err("No memory to alloc RNDIS packet");
goto drop;
}
rndis_add_hdr(rndis_pkt, length);
}
usb_gadget_handle_interrupts(0);
}
- if (rndis_pkt)
- free(rndis_pkt);
+ free(rndis_pkt);
return 0;
drop:
return 0;
}
-void _usb_eth_halt(struct ether_priv *priv)
+static void _usb_eth_halt(struct ether_priv *priv)
{
struct eth_dev *dev = &priv->ethdev;
}
usb_gadget_unregister_driver(&priv->eth_driver);
-#ifdef CONFIG_DM_USB
- device_remove(dev->usb_udev);
-#else
- board_usb_cleanup(0, USB_INIT_DEVICE);
-#endif
+ usb_gadget_release(0);
}
+#ifndef CONFIG_DM_ETH
static int usb_eth_init(struct eth_device *netdev, bd_t *bd)
{
struct ether_priv *priv = (struct ether_priv *)netdev->priv;
ret = _usb_eth_recv(priv);
if (ret) {
- error("error packet receive\n");
+ pr_err("error packet receive\n");
return ret;
}
net_process_received_packet(net_rx_packets[0],
dev->rx_req->length);
} else {
- error("dev->rx_req invalid");
+ pr_err("dev->rx_req invalid");
}
packet_received = 0;
rx_submit(dev, dev->rx_req, 0);
netdev->halt = usb_eth_halt;
netdev->priv = l_priv;
-#ifdef CONFIG_MCAST_TFTP
- #error not supported
-#endif
eth_register(netdev);
return 0;
}
+#else
+static int usb_eth_start(struct udevice *dev)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+
+ return _usb_eth_init(priv);
+}
+
+static int usb_eth_send(struct udevice *dev, void *packet, int length)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+
+ return _usb_eth_send(priv, packet, length);
+}
+
+static int usb_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+ struct eth_dev *ethdev = &priv->ethdev;
+ int ret;
+
+ ret = _usb_eth_recv(priv);
+ if (ret) {
+ pr_err("error packet receive\n");
+ return ret;
+ }
+
+ if (packet_received) {
+ if (ethdev->rx_req) {
+ *packetp = (uchar *)net_rx_packets[0];
+ return ethdev->rx_req->length;
+ } else {
+ pr_err("dev->rx_req invalid");
+ return -EFAULT;
+ }
+ }
+
+ return -EAGAIN;
+}
+
+static int usb_eth_free_pkt(struct udevice *dev, uchar *packet,
+ int length)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+ struct eth_dev *ethdev = &priv->ethdev;
+
+ packet_received = 0;
+
+ return rx_submit(ethdev, ethdev->rx_req, 0);
+}
+
+static void usb_eth_stop(struct udevice *dev)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+
+ _usb_eth_halt(priv);
+}
+
+static int usb_eth_probe(struct udevice *dev)
+{
+ struct ether_priv *priv = dev_get_priv(dev);
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+
+ priv->netdev = dev;
+ l_priv = priv;
+
+ get_ether_addr(CONFIG_USBNET_DEVADDR, pdata->enetaddr);
+ eth_env_set_enetaddr("usbnet_devaddr", pdata->enetaddr);
+
+ return 0;
+}
+
+static const struct eth_ops usb_eth_ops = {
+ .start = usb_eth_start,
+ .send = usb_eth_send,
+ .recv = usb_eth_recv,
+ .free_pkt = usb_eth_free_pkt,
+ .stop = usb_eth_stop,
+};
+
+int usb_ether_init(void)
+{
+ struct udevice *dev;
+ struct udevice *usb_dev;
+ int ret;
+
+ ret = uclass_first_device(UCLASS_USB_GADGET_GENERIC, &usb_dev);
+ if (!usb_dev || ret) {
+ pr_err("No USB device found\n");
+ return ret;
+ }
+
+ ret = device_bind_driver(usb_dev, "usb_ether", "usb_ether", &dev);
+ if (!dev || ret) {
+ pr_err("usb - not able to bind usb_ether device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(eth_usb) = {
+ .name = "usb_ether",
+ .id = UCLASS_ETH,
+ .probe = usb_eth_probe,
+ .ops = &usb_eth_ops,
+ .priv_auto_alloc_size = sizeof(struct ether_priv),
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif /* CONFIG_DM_ETH */