summaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/usbnet.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2009-04-06 19:16:47 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-04-07 10:56:22 +0200
commit277b605eaef1e7087492be29304c668a11e121b7 (patch)
treec1a2dac65e606c0830352edec7b73323b4db9d51 /drivers/net/usb/usbnet.c
parent90ef0ccd2cf37fc9b7630dcc91fec4c27ca3b31c (diff)
downloadbarebox-277b605eaef1e7087492be29304c668a11e121b7.tar.gz
barebox-277b605eaef1e7087492be29304c668a11e121b7.tar.xz
USB support
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/net/usb/usbnet.c')
-rw-r--r--drivers/net/usb/usbnet.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
new file mode 100644
index 0000000000..fff6a1710b
--- /dev/null
+++ b/drivers/net/usb/usbnet.c
@@ -0,0 +1,252 @@
+#include <common.h>
+#include <usb/usb.h>
+#include <usb/usbnet.h>
+#include <asm/byteorder.h>
+#include <errno.h>
+#include <malloc.h>
+
+static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+int usbnet_get_endpoints(struct usbnet *dev)
+{
+ struct usb_device *udev = dev->udev;
+ int tmp;
+ struct usb_interface_descriptor *alt = NULL;
+ struct usb_endpoint_descriptor *in = NULL, *out = NULL;
+ struct usb_endpoint_descriptor *status = NULL;
+
+ for (tmp = 0; tmp < udev->config.no_of_if; tmp++) {
+ unsigned ep;
+
+ in = out = status = NULL;
+ alt = &udev->config.if_desc[tmp];
+
+ /* take the first altsetting with in-bulk + out-bulk;
+ * remember any status endpoint, just in case;
+ * ignore other endpoints and altsetttings.
+ */
+ for (ep = 0; ep < alt->bNumEndpoints; ep++) {
+ struct usb_endpoint_descriptor *e;
+ int intr = 0;
+
+ e = &alt->ep_desc[ep];
+ switch (e->bmAttributes) {
+ case USB_ENDPOINT_XFER_INT:
+ if (!usb_endpoint_dir_in(e))
+ continue;
+ intr = 1;
+ /* FALLTHROUGH */
+ case USB_ENDPOINT_XFER_BULK:
+ break;
+ default:
+ continue;
+ }
+ if (usb_endpoint_dir_in(e)) {
+ if (!intr && !in)
+ in = e;
+ else if (intr && !status)
+ status = e;
+ } else {
+ if (!out)
+ out = e;
+ }
+ }
+ if (in && out)
+ break;
+ }
+
+ if (!alt || !in || !out)
+ return -EINVAL;
+
+ if (alt->bAlternateSetting != 0
+ || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+ tmp = usb_set_interface (dev->udev, alt->bInterfaceNumber,
+ alt->bAlternateSetting);
+ if (tmp < 0)
+ return tmp;
+ }
+
+ dev->in = usb_rcvbulkpipe (dev->udev,
+ in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->out = usb_sndbulkpipe (dev->udev,
+ out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev_dbg(&dev->dev, "found endpoints: IN=%d OUT=%d\n",
+ in->bEndpointAddress, out->bEndpointAddress);
+
+ return 0;
+}
+EXPORT_SYMBOL(usbnet_get_endpoints);
+
+char tx_buffer[4096];
+
+static int usbnet_send(struct eth_device *edev, void *eth_data, int data_length)
+{
+ struct usbnet *dev = edev->priv;
+ struct driver_info *info = dev->driver_info;
+ int len, alen, ret;
+
+ dev_dbg(&dev->dev, "%s\n",__func__);
+
+ /* some devices want funky USB-level framing, for
+ * win32 driver (usually) and/or hardware quirks
+ */
+ if(info->tx_fixup) {
+ if(info->tx_fixup(dev, eth_data, data_length, tx_buffer, &len)) {
+ dev_dbg(&dev->dev, "can't tx_fixup packet");
+ return 0;
+ }
+ } else {
+ len = data_length;
+ memmove(tx_buffer, (void*) eth_data, len);
+ }
+
+ /* don't assume the hardware handles USB_ZERO_PACKET
+ * NOTE: strictly conforming cdc-ether devices should expect
+ * the ZLP here, but ignore the one-byte packet.
+ */
+ if ((len % dev->maxpacket) == 0)
+ tx_buffer[len++] = 0;
+
+ ret = usb_bulk_msg(dev->udev, dev->out, tx_buffer, len, &alen, 1000);
+ dev_dbg(edev->dev, "%s: ret: %d len: %d alen: %d\n", __func__, ret, len, alen);
+
+ return 0;
+}
+
+static char rx_buf[4096];
+
+static int usbnet_recv(struct eth_device *edev)
+{
+ struct usbnet *dev = (struct usbnet*) edev->priv;
+ struct driver_info *info = dev->driver_info;
+ int len, alen = 0;
+
+ dev_dbg(edev->dev, "%s\n",__func__);
+
+ len = dev->rx_urb_size;
+
+ usb_bulk_msg(dev->udev, dev->in, rx_buf, len, &alen,
+ 1000);
+
+ if(alen) {
+ if (info->rx_fixup)
+ return info->rx_fixup(dev, rx_buf, alen);
+ else
+ NetReceive(rx_buf, alen);
+ }
+
+ return 0;
+}
+
+static int usbnet_init(struct eth_device *edev)
+{
+ struct usbnet *dev = (struct usbnet*) edev->priv;
+ struct driver_info *info = dev->driver_info;
+ int ret = 0;
+
+ dev_dbg(edev->dev, "%s\n",__func__);
+
+ /* put into "known safe" state */
+ if (info->reset)
+ ret = info->reset(dev);
+
+ if (ret) {
+ dev_info (edev->dev, "open reset fail (%d)", ret);
+ return ret;
+ }
+
+ miiphy_restart_aneg(&dev->miiphy);
+
+ return 0;
+}
+
+static int usbnet_open(struct eth_device *edev)
+{
+ struct usbnet *dev = (struct usbnet*)edev->priv;
+
+ dev_dbg(edev->dev, "%s\n",__func__);
+
+ if (miiphy_wait_aneg(&dev->miiphy))
+ return -1;
+
+ miiphy_print_status(&dev->miiphy);
+
+ return 0;
+}
+
+static void usbnet_halt(struct eth_device *edev)
+{
+ dev_dbg(edev->dev, "%s\n",__func__);
+}
+
+int usbnet_probe(struct usb_device *usbdev, const struct usb_device_id *prod)
+{
+ struct usbnet *undev;
+ struct eth_device *edev;
+ struct driver_info *info;
+ int status;
+
+ dev_dbg(edev->dev, "%s\n", __func__);
+
+ undev = xzalloc(sizeof (*undev));
+
+ usbdev->drv_data = undev;
+
+ edev = &undev->edev;
+ undev->udev = usbdev;
+
+ edev->open = usbnet_open,
+ edev->init = usbnet_init,
+ edev->send = usbnet_send,
+ edev->recv = usbnet_recv,
+ edev->halt = usbnet_halt,
+ edev->priv = undev;
+ edev->dev = &undev->dev;
+
+ get_free_deviceid(edev->dev->id, "eth");
+ sprintf(edev->dev->name, "%s", "usbnet");
+
+ info = (struct driver_info *)prod->driver_info;
+ undev->driver_info = info;
+
+ if (info->bind) {
+ status = info->bind (undev);
+ if (status < 0)
+ goto out1;
+ }
+
+ if (!undev->rx_urb_size)
+ undev->rx_urb_size = 1514; /* FIXME: What to put here? */
+ undev->maxpacket = usb_maxpacket(undev->udev, undev->out);
+
+ /* FIXME: eth layer should have the device and register it */
+ register_device(edev->dev);
+
+ eth_register(edev);
+
+ return 0;
+out1:
+ dev_dbg(edev->dev, "err: %d\n", status);
+ return status;
+}
+
+void usbnet_disconnect(struct usb_device *usbdev)
+{
+ struct usbnet *undev = usbdev->drv_data;
+ struct eth_device *edev = &undev->edev;
+ struct driver_info *info;
+
+ eth_unregister(edev);
+ unregister_device(edev->dev);
+
+ info = undev->driver_info;
+ if (info->unbind)
+ info->unbind(undev);
+
+ free(undev);
+}
+