From patchwork Mon Jul 6 22:47:50 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 491857 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id EB0DC1402C4 for ; Tue, 7 Jul 2015 08:50:18 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b=A8GCs1Zo; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B479F4B885; Tue, 7 Jul 2015 00:49:39 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ekA4IJtBMprW; Tue, 7 Jul 2015 00:49:39 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 3B2944B889; Tue, 7 Jul 2015 00:49:10 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 01F5C4B80E for ; Tue, 7 Jul 2015 00:48:46 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2CrE13QItq_e for ; Tue, 7 Jul 2015 00:48:45 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ig0-f170.google.com (mail-ig0-f170.google.com [209.85.213.170]) by theia.denx.de (Postfix) with ESMTPS id 902C14B811 for ; Tue, 7 Jul 2015 00:48:36 +0200 (CEST) Received: by igh16 with SMTP id 16so37787158igh.0 for ; Mon, 06 Jul 2015 15:48:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=dsIVhsePzb4qHEGKxz2iQ1PUApTRfWL/R5o1YIbfcEQ=; b=A8GCs1ZoJw8ePz4eui/hR6qnAZXmoxGbcwokOqBRK62Hj3d90U12G+CdywlARQwy/D 7MY+hoOpZGxl1Acv/MdsIsKhqPuMTfFaJgWgLmp+tywbtQ8SWVG7XVjH/cCEE0PK0Boi 16ygpA5qnHmPkqJKtPiKlfI0f2934W7YAN8VU/ND1gO2NvwABcYGDkF+vfqSQRL+yKz9 E5kWu27Ti1cNdhWn382WpShW4bdoVlF/bxqJR9b7p6Znpm2ekoKhg2b8iFJfQVUQAfNE P0//GaR0KhrqdFhanxraMYYx445VHPC6WsiTD2HmS0/FqP7QOQWk2XDDwlT1VeXyWKHE x2+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=dsIVhsePzb4qHEGKxz2iQ1PUApTRfWL/R5o1YIbfcEQ=; b=EEOVSm1Pwr4YocSSr8g7BspVsX2VkTlJquBLrb6p+8QXia482T8VsQoNGhpRSarCAW SRBT79c5ypPl8pgxOP2bFk/iGHTILwwz0oA73MBvri1wLu/j0kMmMFUszlXsNBT15iBI qBY9QWkGQWp4M+WIZocawKwgxP96Ea2685+35R+2H/8r5e9IfoJQiCA+tVnfbI8DOx/C b306ysCPA/XrZUiedfJfYj2QuKQfSc1cyJqSb/Z0vCwonQWv5fcz1c+aY4sd8PCzunX2 /nu4qPVuCDZ3QKCSoj4KcyVA9yzcctzq6h0qRHSkdI3RMotqgWfHBo7KX48VrDqjvcj1 YUsg== X-Gm-Message-State: ALoCoQlQq9xEvp3dh1uNwOvRuqip694Mf5p+hc9elBmTEH/gveU4viMtpUna6x8b1XoboLvacByU X-Received: by 10.107.136.153 with SMTP id s25mr1801232ioi.65.1436222915649; Mon, 06 Jul 2015 15:48:35 -0700 (PDT) Received: from kaki.bld.corp.google.com ([172.29.216.32]) by mx.google.com with ESMTPSA id g1sm13383844iog.4.2015.07.06.15.48.32 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 06 Jul 2015 15:48:33 -0700 (PDT) Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 6A278220FC5; Mon, 6 Jul 2015 16:48:29 -0600 (MDT) From: Simon Glass To: U-Boot Mailing List Date: Mon, 6 Jul 2015 16:47:50 -0600 Message-Id: <1436222877-17548-12-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.4.3.573.g4eafbef In-Reply-To: <1436222877-17548-1-git-send-email-sjg@chromium.org> References: <1436222877-17548-1-git-send-email-sjg@chromium.org> Cc: Marek Vasut , Tom Rini , Joe Hershberger Subject: [U-Boot] [PATCH 11/18] dm: usb: eth: Support driver model with USB Ethernet X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" At present USB Ethernet does not work with CONFIG_DM_ETH. Add driver model support to this feature, so that it can work alongside other Ethernet devices with driver model. It was found that quite a bit of code is common in most of the USB Ethernet drivers. Add this code to the common layer to reduce the amount of duplicate code needed in USB Ethernet drivers when CONFIG_DM_ETH is used. Signed-off-by: Simon Glass Reviewed-by: Marek Vasut --- common/cmd_usb.c | 2 +- drivers/usb/eth/usb_ether.c | 127 ++++++++++++++++++++++++++++++++++++++++++++ include/usb_ether.h | 89 +++++++++++++++++++++++++++---- 3 files changed, 206 insertions(+), 12 deletions(-) diff --git a/common/cmd_usb.c b/common/cmd_usb.c index e1dba7a..77cb257 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -22,7 +22,7 @@ #ifdef CONFIG_USB_STORAGE static int usb_stor_curr_dev = -1; /* current device */ #endif -#ifdef CONFIG_USB_HOST_ETHER +#if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH) static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */ #endif diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index c72b7e4..07d2309 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -6,11 +6,137 @@ #include #include +#include #include #include #include "usb_ether.h" +#ifdef CONFIG_DM_ETH + +#define USB_BULK_RECV_TIMEOUT 500 + +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize) +{ + struct usb_device *udev = dev_get_parentdata(dev); + struct usb_interface_descriptor *iface_desc; + bool ep_in_found = false, ep_out_found = false; + struct usb_interface *iface; + const int ifnum = 0; /* Always use interface 0 */ + int ret, i; + + iface = &udev->config.if_desc[ifnum]; + iface_desc = &udev->config.if_desc[ifnum].desc; + + /* Initialize the ueth_data structure with some useful info */ + ueth->ifnum = ifnum; + ueth->subclass = iface_desc->bInterfaceSubClass; + ueth->protocol = iface_desc->bInterfaceProtocol; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and int. + * We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + int ep_addr = iface->ep_desc[i].bEndpointAddress; + + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (ep_addr & USB_DIR_IN && !ep_in_found) { + ueth->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = true; + } else if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ueth->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = true; + } + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ueth->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ueth->irqinterval = iface->ep_desc[i].bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", ueth->ep_in, ueth->ep_out, + ueth->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ueth->ep_in || !ueth->ep_out || !ueth->ep_int) { + debug("%s: %s: Cannot find endpoints\n", __func__, dev->name); + return -ENXIO; + } + + ueth->rxsize = rxsize; + ueth->rxbuf = memalign(rxsize, ARCH_DMA_MINALIGN); + if (!ueth->rxbuf) + return -ENOMEM; + + ret = usb_set_interface(udev, iface_desc->bInterfaceNumber, ifnum); + if (ret) { + debug("%s: %s: Cannot set interface: %d\n", __func__, dev->name, + ret); + return ret; + } + ueth->pusb_dev = udev; + + return 0; +} + +int usb_ether_deregister(struct ueth_data *ueth) +{ + return 0; +} + +int usb_ether_receive(struct ueth_data *ueth, int rxsize) +{ + int actual_len; + int ret; + + if (rxsize > ueth->rxsize) + return -EINVAL; + ret = usb_bulk_msg(ueth->pusb_dev, + usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), + ueth->rxbuf, rxsize, &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", rxsize, actual_len, ret); + if (ret) { + printf("Rx: failed to receive: %d\n", ret); + return ret; + } + if (actual_len > rxsize) { + debug("Rx: received too many bytes %d\n", actual_len); + return -ENOSPC; + } + ueth->rxlen = actual_len; + ueth->rxptr = 0; + + return actual_len ? 0 : -EAGAIN; +} + +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes) +{ + ueth->rxptr += num_bytes; + if (num_bytes < 0 || ueth->rxptr >= ueth->rxlen) + ueth->rxlen = 0; +} + +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp) +{ + if (!ueth->rxlen) + return 0; + + *ptrp = &ueth->rxbuf[ueth->rxptr]; + + return ueth->rxlen - ueth->rxptr; +} + +#else + typedef void (*usb_eth_before_probe)(void); typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum, struct ueth_data *ss); @@ -197,3 +323,4 @@ int usb_host_eth_scan(int mode) return 0; return -1; } +#endif diff --git a/include/usb_ether.h b/include/usb_ether.h index 23507e1..c6d1416 100644 --- a/include/usb_ether.h +++ b/include/usb_ether.h @@ -19,25 +19,91 @@ #define ETH_DATA_LEN 1500 /* Max. octets in payload */ #define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */ +/* TODO(sjg@chromium.org): Remove @pusb_dev when all boards use CONFIG_DM_ETH */ struct ueth_data { /* eth info */ - struct eth_device eth_dev; /* used with eth_register */ - int phy_id; /* mii phy id */ +#ifdef CONFIG_DM_ETH + uint8_t *rxbuf; + int rxsize; + int rxlen; /* Total bytes available in rxbuf */ + int rxptr; /* Current position in rxbuf */ +#else + struct eth_device eth_dev; /* used with eth_register */ + /* driver private */ + void *dev_priv; +#endif + int phy_id; /* mii phy id */ /* usb info */ struct usb_device *pusb_dev; /* this usb_device */ - unsigned char ifnum; /* interface number */ - unsigned char ep_in; /* in endpoint */ - unsigned char ep_out; /* out ....... */ - unsigned char ep_int; /* interrupt . */ - unsigned char subclass; /* as in overview */ - unsigned char protocol; /* .............. */ + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ unsigned char irqinterval; /* Intervall for IRQ Pipe */ - - /* driver private */ - void *dev_priv; }; +#ifdef CONFIG_DM_ETH +/** + * usb_ether_register() - register a new USB ethernet device + * + * This selects the correct USB interface and figures out the endpoints to use. + * + * @dev: USB device + * @ss: Place to put USB ethernet data + * @rxsize: Maximum size to allocate for the receive buffer + * @return 0 if OK, -ve on error + */ +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize); + +/** + * usb_ether_deregister() - deregister a USB ethernet device + * + * @ueth: USB Ethernet device + * @return 0 + */ +int usb_ether_deregister(struct ueth_data *ueth); + +/** + * usb_ether_receive() - recieve a packet from the bulk in endpoint + * + * The packet is stored in the internal buffer ready for processing. + * + * @ueth: USB Ethernet device + * @rxsize: Maximum size to receive + * @return 0 if a packet was received, -EAGAIN if not, -ENOSPC if @rxsize is + * larger than the size passed ot usb_ether_register(), other -ve on error + */ +int usb_ether_receive(struct ueth_data *ueth, int rxsize); + +/** + * usb_ether_get_rx_bytes() - obtain bytes from the internal packet buffer + * + * This should be called repeatedly to obtain packet data until it returns 0. + * After each packet is processed, call usb_ether_advance_rxbuf() to move to + * the next one. + * + * @ueth: USB Ethernet device + * @ptrp: Returns a pointer to the start of the next packet if there is + * one available + * @return number of bytes available, or 0 if none + */ +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp); + +/** + * usb_ether_advance_rxbuf() - Advance to the next packet in the internal buffer + * + * After processing the data returned by usb_ether_get_rx_bytes(), call this + * function to move to the next packet. You must specify the number of bytes + * you have processed in @num_bytes. + * + * @ueth: USB Ethernet device + * @num_bytes: Number of bytes to skip, or -1 to skip all bytes + */ +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes); +#else /* * Function definitions for each USB ethernet driver go here * (declaration is unconditional, compilation is conditional) @@ -65,5 +131,6 @@ int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum, struct ueth_data *ss); int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss, struct eth_device *eth); +#endif #endif /* __USB_ETHER_H__ */