Tom Rini | 10e4779 | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Simon Glass | 1e9961d | 2011-02-16 11:14:33 -0800 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (c) 2011 The Chromium OS Authors. |
Simon Glass | 1e9961d | 2011-02-16 11:14:33 -0800 | [diff] [blame] | 4 | */ |
| 5 | |
| 6 | #include <common.h> |
Simon Glass | 33b66e8 | 2015-03-25 12:22:43 -0600 | [diff] [blame] | 7 | #include <dm.h> |
Simon Glass | e96ba56 | 2015-07-07 20:53:35 -0600 | [diff] [blame] | 8 | #include <errno.h> |
Simon Glass | 0f2af88 | 2020-05-10 11:40:05 -0600 | [diff] [blame] | 9 | #include <log.h> |
Simon Glass | 5b026f9 | 2015-07-06 16:47:50 -0600 | [diff] [blame] | 10 | #include <malloc.h> |
Simon Glass | 274e0b0 | 2020-05-10 11:39:56 -0600 | [diff] [blame] | 11 | #include <net.h> |
Simon Glass | 1e9961d | 2011-02-16 11:14:33 -0800 | [diff] [blame] | 12 | #include <usb.h> |
Simon Glass | 274e0b0 | 2020-05-10 11:39:56 -0600 | [diff] [blame] | 13 | #include <asm/cache.h> |
Simon Glass | 33b66e8 | 2015-03-25 12:22:43 -0600 | [diff] [blame] | 14 | #include <dm/device-internal.h> |
Simon Glass | 1e9961d | 2011-02-16 11:14:33 -0800 | [diff] [blame] | 15 | |
| 16 | #include "usb_ether.h" |
| 17 | |
Simon Glass | 5b026f9 | 2015-07-06 16:47:50 -0600 | [diff] [blame] | 18 | #define USB_BULK_RECV_TIMEOUT 500 |
| 19 | |
| 20 | int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize) |
| 21 | { |
Simon Glass | de44acf | 2015-09-28 23:32:01 -0600 | [diff] [blame] | 22 | struct usb_device *udev = dev_get_parent_priv(dev); |
Simon Glass | 5b026f9 | 2015-07-06 16:47:50 -0600 | [diff] [blame] | 23 | struct usb_interface_descriptor *iface_desc; |
| 24 | bool ep_in_found = false, ep_out_found = false; |
| 25 | struct usb_interface *iface; |
| 26 | const int ifnum = 0; /* Always use interface 0 */ |
| 27 | int ret, i; |
| 28 | |
| 29 | iface = &udev->config.if_desc[ifnum]; |
| 30 | iface_desc = &udev->config.if_desc[ifnum].desc; |
| 31 | |
| 32 | /* Initialize the ueth_data structure with some useful info */ |
| 33 | ueth->ifnum = ifnum; |
| 34 | ueth->subclass = iface_desc->bInterfaceSubClass; |
| 35 | ueth->protocol = iface_desc->bInterfaceProtocol; |
| 36 | |
| 37 | /* |
| 38 | * We are expecting a minimum of 3 endpoints - in, out (bulk), and int. |
| 39 | * We will ignore any others. |
| 40 | */ |
| 41 | for (i = 0; i < iface_desc->bNumEndpoints; i++) { |
| 42 | int ep_addr = iface->ep_desc[i].bEndpointAddress; |
| 43 | |
| 44 | /* is it an BULK endpoint? */ |
| 45 | if ((iface->ep_desc[i].bmAttributes & |
| 46 | USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { |
| 47 | if (ep_addr & USB_DIR_IN && !ep_in_found) { |
| 48 | ueth->ep_in = ep_addr & |
| 49 | USB_ENDPOINT_NUMBER_MASK; |
| 50 | ep_in_found = true; |
| 51 | } else if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { |
| 52 | ueth->ep_out = ep_addr & |
| 53 | USB_ENDPOINT_NUMBER_MASK; |
| 54 | ep_out_found = true; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | /* is it an interrupt endpoint? */ |
| 59 | if ((iface->ep_desc[i].bmAttributes & |
| 60 | USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { |
| 61 | ueth->ep_int = iface->ep_desc[i].bEndpointAddress & |
| 62 | USB_ENDPOINT_NUMBER_MASK; |
| 63 | ueth->irqinterval = iface->ep_desc[i].bInterval; |
| 64 | } |
| 65 | } |
| 66 | debug("Endpoints In %d Out %d Int %d\n", ueth->ep_in, ueth->ep_out, |
| 67 | ueth->ep_int); |
| 68 | |
| 69 | /* Do some basic sanity checks, and bail if we find a problem */ |
| 70 | if (!ueth->ep_in || !ueth->ep_out || !ueth->ep_int) { |
| 71 | debug("%s: %s: Cannot find endpoints\n", __func__, dev->name); |
| 72 | return -ENXIO; |
| 73 | } |
| 74 | |
| 75 | ueth->rxsize = rxsize; |
Stephen Warren | dfdfe6f | 2016-02-12 13:56:01 -0700 | [diff] [blame] | 76 | ueth->rxbuf = memalign(ARCH_DMA_MINALIGN, rxsize); |
Simon Glass | 5b026f9 | 2015-07-06 16:47:50 -0600 | [diff] [blame] | 77 | if (!ueth->rxbuf) |
| 78 | return -ENOMEM; |
| 79 | |
| 80 | ret = usb_set_interface(udev, iface_desc->bInterfaceNumber, ifnum); |
| 81 | if (ret) { |
| 82 | debug("%s: %s: Cannot set interface: %d\n", __func__, dev->name, |
| 83 | ret); |
| 84 | return ret; |
| 85 | } |
| 86 | ueth->pusb_dev = udev; |
| 87 | |
| 88 | return 0; |
| 89 | } |
| 90 | |
| 91 | int usb_ether_deregister(struct ueth_data *ueth) |
| 92 | { |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | int usb_ether_receive(struct ueth_data *ueth, int rxsize) |
| 97 | { |
| 98 | int actual_len; |
| 99 | int ret; |
| 100 | |
| 101 | if (rxsize > ueth->rxsize) |
| 102 | return -EINVAL; |
| 103 | ret = usb_bulk_msg(ueth->pusb_dev, |
| 104 | usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), |
| 105 | ueth->rxbuf, rxsize, &actual_len, |
| 106 | USB_BULK_RECV_TIMEOUT); |
| 107 | debug("Rx: len = %u, actual = %u, err = %d\n", rxsize, actual_len, ret); |
| 108 | if (ret) { |
| 109 | printf("Rx: failed to receive: %d\n", ret); |
| 110 | return ret; |
| 111 | } |
| 112 | if (actual_len > rxsize) { |
| 113 | debug("Rx: received too many bytes %d\n", actual_len); |
| 114 | return -ENOSPC; |
| 115 | } |
| 116 | ueth->rxlen = actual_len; |
| 117 | ueth->rxptr = 0; |
| 118 | |
| 119 | return actual_len ? 0 : -EAGAIN; |
| 120 | } |
| 121 | |
| 122 | void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes) |
| 123 | { |
| 124 | ueth->rxptr += num_bytes; |
| 125 | if (num_bytes < 0 || ueth->rxptr >= ueth->rxlen) |
| 126 | ueth->rxlen = 0; |
| 127 | } |
| 128 | |
| 129 | int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp) |
| 130 | { |
| 131 | if (!ueth->rxlen) |
| 132 | return 0; |
| 133 | |
| 134 | *ptrp = &ueth->rxbuf[ueth->rxptr]; |
| 135 | |
| 136 | return ueth->rxlen - ueth->rxptr; |
| 137 | } |