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