/* * u_qc_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack * * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * Copyright (C) 2008 Nokia Corporation * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ #include #include #include #include #include #include #include "u_ether.h" /* * This component encapsulates the Ethernet link glue needed to provide * one (!) network link through the USB gadget stack, normally "usb0". * * The control and data models are handled by the function driver which * connects to this code; such as CDC Ethernet (ECM or EEM), * "CDC Subset", or RNDIS. That includes all descriptor and endpoint * management. * * Link level addressing is handled by this component using module * parameters; if no such parameters are provided, random link level * addresses are used. Each end of the link uses one address. The * host end address is exported in various ways, and is often recorded * in configuration databases. * * The driver which assembles each configuration using such a link is * responsible for ensuring that each configuration includes at most one * instance of is network link. (The network layer provides ways for * this single "physical" link to be used by multiple virtual links.) * * This utilities is based on Ethernet-over-USB link layer utilities and * contains MSM specific implementation. */ #define UETH__VERSION "29-May-2008" struct eth_qc_dev { /* lock is held while accessing port_usb * or updating its backlink port_usb->ioport */ spinlock_t lock; struct qc_gether *port_usb; struct net_device *net; struct usb_gadget *gadget; unsigned header_len; bool zlp; u8 host_mac[ETH_ALEN]; }; /*-------------------------------------------------------------------------*/ #undef DBG #undef VDBG #undef ERROR #undef INFO #define xprintk(d, level, fmt, args...) \ printk(level "%s: " fmt , (d)->net->name , ## args) #ifdef DEBUG #undef DEBUG #define DBG(dev, fmt, args...) \ xprintk(dev , KERN_DEBUG , fmt , ## args) #else #define DBG(dev, fmt, args...) \ do { } while (0) #endif /* DEBUG */ #ifdef VERBOSE_DEBUG #define VDBG DBG #else #define VDBG(dev, fmt, args...) \ do { } while (0) #endif /* DEBUG */ #define ERROR(dev, fmt, args...) \ xprintk(dev , KERN_ERR , fmt , ## args) #define INFO(dev, fmt, args...) \ xprintk(dev , KERN_INFO , fmt , ## args) /*-------------------------------------------------------------------------*/ /* NETWORK DRIVER HOOKUP (to the layer above this driver) */ static int ueth_qc_change_mtu(struct net_device *net, int new_mtu) { struct eth_qc_dev *dev = netdev_priv(net); unsigned long flags; int status = 0; /* don't change MTU on "live" link (peer won't know) */ spin_lock_irqsave(&dev->lock, flags); if (dev->port_usb) status = -EBUSY; else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) status = -ERANGE; else net->mtu = new_mtu; spin_unlock_irqrestore(&dev->lock, flags); return status; } static void eth_qc_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) { struct eth_qc_dev *dev = netdev_priv(net); strlcpy(p->driver, "g_qc_ether", sizeof p->driver); strlcpy(p->version, UETH__VERSION, sizeof p->version); strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); } static const struct ethtool_ops qc_ethtool_ops = { .get_drvinfo = eth_qc_get_drvinfo, .get_link = ethtool_op_get_link, }; static netdev_tx_t eth_qc_start_xmit(struct sk_buff *skb, struct net_device *net) { return NETDEV_TX_OK; } static int eth_qc_open(struct net_device *net) { struct eth_qc_dev *dev = netdev_priv(net); struct qc_gether *link; DBG(dev, "%s\n", __func__); if (netif_carrier_ok(dev->net)) { /* Force the netif to send the RTM_NEWLINK event * that in use to notify on the USB cable status. */ netif_carrier_off(dev->net); netif_carrier_on(dev->net); netif_wake_queue(dev->net); } spin_lock_irq(&dev->lock); link = dev->port_usb; if (link && link->open) link->open(link); spin_unlock_irq(&dev->lock); return 0; } static int eth_qc_stop(struct net_device *net) { struct eth_qc_dev *dev = netdev_priv(net); unsigned long flags; struct qc_gether *link = dev->port_usb; VDBG(dev, "%s\n", __func__); netif_stop_queue(net); spin_lock_irqsave(&dev->lock, flags); if (dev->port_usb && link->close) link->close(link); spin_unlock_irqrestore(&dev->lock, flags); return 0; } /*-------------------------------------------------------------------------*/ /* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ static char *qc_dev_addr; module_param(qc_dev_addr, charp, S_IRUGO); MODULE_PARM_DESC(qc_dev_addr, "QC Device Ethernet Address"); /* this address is invisible to ifconfig */ static char *qc_host_addr; module_param(qc_host_addr, charp, S_IRUGO); MODULE_PARM_DESC(qc_host_addr, "QC Host Ethernet Address"); static int get_qc_ether_addr(const char *str, u8 *dev_addr) { if (str) { unsigned i; for (i = 0; i < 6; i++) { unsigned char num; if ((*str == '.') || (*str == ':')) str++; num = hex_to_bin(*str++) << 4; num |= hex_to_bin(*str++); dev_addr[i] = num; } if (is_valid_ether_addr(dev_addr)) return 0; } random_ether_addr(dev_addr); return 1; } static const struct net_device_ops eth_qc_netdev_ops = { .ndo_open = eth_qc_open, .ndo_stop = eth_qc_stop, .ndo_start_xmit = eth_qc_start_xmit, .ndo_change_mtu = ueth_qc_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; static struct device_type qc_gadget_type = { .name = "gadget", }; void gether_qc_get_macs(u8 dev_mac[ETH_ALEN], u8 host_mac[ETH_ALEN]) { if (get_qc_ether_addr(qc_dev_addr, dev_mac)) pr_debug("using random dev_mac ethernet address\n"); if (get_qc_ether_addr(qc_host_addr, host_mac)) pr_debug("using random host_mac ethernet address\n"); } /** * gether_qc_setup - initialize one ethernet-over-usb link * @g: gadget to associated with these links * @ethaddr: NULL, or a buffer in which the ethernet address of the * host side of the link is recorded * Context: may sleep * * This sets up the single network link that may be exported by a * gadget driver using this framework. The link layer addresses are * set up using module parameters. * * Returns negative errno, or zero on success */ int gether_qc_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) { return gether_qc_setup_name(g, ethaddr, "usb"); } /** * gether_qc_setup_name - initialize one ethernet-over-usb link * @g: gadget to associated with these links * @ethaddr: NULL, or a buffer in which the ethernet address of the * host side of the link is recorded * @netname: name for network device (for example, "usb") * Context: may sleep * * This sets up the single network link that may be exported by a * gadget driver using this framework. The link layer addresses are * set up using module parameters. * * Returns negative errno, or zero on success */ int gether_qc_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], const char *netname) { struct eth_qc_dev *dev; struct net_device *net; int status; net = alloc_etherdev(sizeof *dev); if (!net) return -ENOMEM; dev = netdev_priv(net); spin_lock_init(&dev->lock); /* network device setup */ dev->net = net; snprintf(net->name, sizeof(net->name), "%s%%d", netname); if (get_qc_ether_addr(qc_dev_addr, net->dev_addr)) dev_warn(&g->dev, "using random %s ethernet address\n", "self"); if (get_qc_ether_addr(qc_host_addr, dev->host_mac)) dev_warn(&g->dev, "using random %s ethernet address\n", "host"); if (ethaddr) memcpy(ethaddr, dev->host_mac, ETH_ALEN); net->netdev_ops = ð_qc_netdev_ops; SET_ETHTOOL_OPS(net, &qc_ethtool_ops); netif_carrier_off(net); dev->gadget = g; SET_NETDEV_DEV(net, &g->dev); SET_NETDEV_DEVTYPE(net, &qc_gadget_type); status = register_netdev(net); if (status < 0) { dev_dbg(&g->dev, "register_netdev failed, %d\n", status); free_netdev(net); } else { INFO(dev, "MAC %pM\n", net->dev_addr); INFO(dev, "HOST MAC %pM\n", dev->host_mac); } return status; } /** * gether_qc_cleanup_name - remove Ethernet-over-USB device * @netname: name for network device (for example, "usb") * Context: may sleep * * This is called to free all resources allocated by @gether_qc_setup(). */ void gether_qc_cleanup_name(const char *netname) { struct net_device *net_dev; /* Extract the eth_qc_dev from the net device */ net_dev = dev_get_by_name(&init_net, netname); if (net_dev) { dev_put(net_dev); unregister_netdev(net_dev); free_netdev(net_dev); } } /** * gether_qc_connect_name - notify network layer that USB link * is active * @link: the USB link, set up with endpoints, descriptors matching * current device speed, and any framing wrapper(s) set up. * @netname: name for network device (for example, "usb") * Context: irqs blocked * @netif_enable: if true, net interface will be turned on * * This is called to let the network layer know the connection * is active ("carrier detect"). */ struct net_device *gether_qc_connect_name(struct qc_gether *link, const char *netname, bool netif_enable) { struct net_device *net_dev; struct eth_qc_dev *dev; /* Extract the eth_qc_dev from the net device */ net_dev = dev_get_by_name(&init_net, netname); if (!net_dev) return ERR_PTR(-EINVAL); dev_put(net_dev); dev = netdev_priv(net_dev); if (!dev) return ERR_PTR(-EINVAL); dev->zlp = link->is_zlp_ok; dev->header_len = link->header_len; spin_lock(&dev->lock); dev->port_usb = link; link->ioport = dev; if (netif_running(dev->net)) { if (link->open) link->open(link); } else { if (link->close) link->close(link); } spin_unlock(&dev->lock); if (netif_enable) { netif_carrier_on(dev->net); if (netif_running(dev->net)) netif_wake_queue(dev->net); } return dev->net; } /** * gether_qc_disconnect_name - notify network layer that USB * link is inactive * @link: the USB link, on which gether_connect() was called * @netname: name for network device (for example, "usb") * Context: irqs blocked * * This is called to let the network layer know the connection * went inactive ("no carrier"). * * On return, the state is as if gether_connect() had never been called. */ void gether_qc_disconnect_name(struct qc_gether *link, const char *netname) { struct net_device *net_dev; struct eth_qc_dev *dev; /* Extract the eth_qc_dev from the net device */ net_dev = dev_get_by_name(&init_net, netname); if (!net_dev) return; dev_put(net_dev); dev = netdev_priv(net_dev); if (!dev) return; DBG(dev, "%s\n", __func__); netif_stop_queue(dev->net); netif_carrier_off(dev->net); spin_lock(&dev->lock); dev->port_usb = NULL; link->ioport = NULL; spin_unlock(&dev->lock); }