diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2009-04-06 19:16:47 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2009-04-07 10:56:22 +0200 |
commit | 277b605eaef1e7087492be29304c668a11e121b7 (patch) | |
tree | c1a2dac65e606c0830352edec7b73323b4db9d51 /drivers/net/usb/usbnet.c | |
parent | 90ef0ccd2cf37fc9b7630dcc91fec4c27ca3b31c (diff) | |
download | barebox-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.c | 252 |
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); +} + |