Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3234,6 +3234,7 @@ dev/usb/usb_device.c optional usb dev/usb/usb_dynamic.c optional usb dev/usb/usb_error.c optional usb +dev/usb/usb_fdt_support.c optional usb fdt dev/usb/usb_generic.c optional usb dev/usb/usb_handle_request.c optional usb dev/usb/usb_hid.c optional usb Index: sys/dev/usb/net/if_smsc.c =================================================================== --- sys/dev/usb/net/if_smsc.c +++ sys/dev/usb/net/if_smsc.c @@ -97,6 +97,7 @@ #include #include #include +#include #endif #include @@ -1559,148 +1560,7 @@ return (rc); } -#ifdef FDT -/* - * This is FreeBSD-specific compatibility strings for RPi/RPi2 - */ -static phandle_t -smsc_fdt_find_eth_node(phandle_t start) -{ - phandle_t child, node; - - /* Traverse through entire tree to find usb ethernet nodes. */ - for (node = OF_child(start); node != 0; node = OF_peer(node)) { - if ((ofw_bus_node_is_compatible(node, "net,ethernet") && - ofw_bus_node_is_compatible(node, "usb,device")) || - ofw_bus_node_is_compatible(node, "usb424,ec00")) - return (node); - child = smsc_fdt_find_eth_node(node); - if (child != -1) - return (child); - } - - return (-1); -} - -/* - * Check if node's path is <*>/usb/hub/ethernet - */ -static int -smsc_fdt_is_usb_eth(phandle_t node) -{ - char name[16]; - int len; - - memset(name, 0, sizeof(name)); - len = OF_getprop(node, "name", name, sizeof(name)); - if (len <= 0) - return (0); - - if (strcmp(name, "ethernet")) - return (0); - - node = OF_parent(node); - if (node == -1) - return (0); - len = OF_getprop(node, "name", name, sizeof(name)); - if (len <= 0) - return (0); - - if (strcmp(name, "hub")) - return (0); - - node = OF_parent(node); - if (node == -1) - return (0); - len = OF_getprop(node, "name", name, sizeof(name)); - if (len <= 0) - return (0); - - if (strcmp(name, "usb")) - return (0); - - return (1); -} - -static phandle_t -smsc_fdt_find_eth_node_by_path(phandle_t start) -{ - phandle_t child, node; - - /* Traverse through entire tree to find usb ethernet nodes. */ - for (node = OF_child(start); node != 0; node = OF_peer(node)) { - if (smsc_fdt_is_usb_eth(node)) - return (node); - child = smsc_fdt_find_eth_node_by_path(node); - if (child != -1) - return (child); - } - - return (-1); -} - -/* - * Look through known names that can contain mac address - * return 0 if valid MAC address has been found - */ -static int -smsc_fdt_read_mac_property(phandle_t node, unsigned char *mac) -{ - int len; - - /* Check if there is property */ - if ((len = OF_getproplen(node, "local-mac-address")) > 0) { - if (len != ETHER_ADDR_LEN) - return (EINVAL); - - OF_getprop(node, "local-mac-address", mac, - ETHER_ADDR_LEN); - return (0); - } - - if ((len = OF_getproplen(node, "mac-address")) > 0) { - if (len != ETHER_ADDR_LEN) - return (EINVAL); - - OF_getprop(node, "mac-address", mac, - ETHER_ADDR_LEN); - return (0); - } - - return (ENXIO); -} - /** - * Get MAC address from FDT blob. Firmware or loader should fill - * mac-address or local-mac-address property. Returns 0 if MAC address - * obtained, error code otherwise. - */ -static int -smsc_fdt_find_mac(unsigned char *mac) -{ - phandle_t node, root; - - root = OF_finddevice("/"); - node = smsc_fdt_find_eth_node(root); - if (node != -1) { - if (smsc_fdt_read_mac_property(node, mac) == 0) - return (0); - } - - /* - * If it's not FreeBSD FDT blob for RPi, try more - * generic .../usb/hub/ethernet - */ - node = smsc_fdt_find_eth_node_by_path(root); - - if (node != -1) - return smsc_fdt_read_mac_property(node, mac); - - return (ENXIO); -} -#endif - -/** * smsc_attach_post - Called after the driver attached to the USB interface * @ue: the USB ethernet device * @@ -1748,7 +1608,7 @@ err = smsc_eeprom_read(sc, 0x01, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); #ifdef FDT if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) - err = smsc_fdt_find_mac(sc->sc_ue.ue_eaddr); + err = usb_fdt_get_mac_addr(sc->sc_ue.ue_dev, &sc->sc_ue); #endif if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) { read_random(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); Index: sys/dev/usb/usb_fdt_support.h =================================================================== --- sys/dev/usb/usb_fdt_support.h +++ sys/dev/usb/usb_fdt_support.h @@ -0,0 +1,48 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Ian Lepore + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _USB_FDT_SUPPORT_H_ +#define _USB_FDT_SUPPORT_H_ + +struct usb_device; +struct usb_ether; + +/* + * Get the device's MAC address from the FDT data. Fills in ue->ue_eaddr and + * returns 0 on success, otherwise leaves ue_eaddr untouched and returns + * non-zero. This first attempts to get the address from the "mac-address" + * property, and if that's not valid it tries the "local-mac-address" property; + * this matches the linux interpretation of the precedence of those properties. + */ +int usb_fdt_get_mac_addr(device_t dev, struct usb_ether* ue); + +/* Get the FDT node for dev. Returns -1 if dev is not in the FDT data. */ +phandle_t usb_fdt_get_node(device_t dev, struct usb_device* udev); + +#endif Index: sys/dev/usb/usb_fdt_support.c =================================================================== --- sys/dev/usb/usb_fdt_support.c +++ sys/dev/usb/usb_fdt_support.c @@ -0,0 +1,160 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Ian Lepore + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static phandle_t +find_udev_in_children(phandle_t parent, struct usb_device *udev) +{ + phandle_t child; + ssize_t proplen; + uint32_t port; + char compat[16]; /* big enough for "usb1234,abcd" */ + + /* + * USB device nodes in FDT have a compatible string of "usb" followed by + * the vendorId,productId rendered in hex. The port number is encoded + * in the standard 'reg' property; it is one-based in the FDT data, but + * usb_device.port_index is zero-based. To uniquely identify a device, + * both the compatible string and the port number must match. + */ + snprintf(compat, sizeof(compat), "usb%x,%x", + UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct)); + for (child = OF_child(parent); child != 0; child = OF_peer(child)) { + if (!ofw_bus_node_is_compatible(child, compat)) + continue; + proplen = OF_getencprop(child, "reg", &port, sizeof(port)); + if (proplen != sizeof(port)) + continue; + if (port == (udev->port_index + 1)) + return (child); + } + return (-1); +} + +static bool +is_valid_mac_addr(uint8_t *addr) +{ + + /* + * All-bits-zero and all-bits-one are a couple common cases of what + * might get read from unprogrammed eeprom or OTP data, weed them out. + */ + if ((addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]) == 0x00) + return (false); + if ((addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff) + return (false); + return (true); +} + +int +usb_fdt_get_mac_addr(device_t dev, struct usb_ether* ue) +{ + phandle_t node; + ssize_t i, proplen; + uint8_t mac[sizeof(ue->ue_eaddr)]; + static const char *properties[] = { + "mac-address", + "local-mac-address" + }; + + if ((node = usb_fdt_get_node(ue->ue_dev, ue->ue_udev)) == -1) + return (ENXIO); + for (i = 0; i < nitems(properties); ++i) { + proplen = OF_getprop(node, properties[i], mac, sizeof(mac)); + if (proplen == sizeof(mac) && is_valid_mac_addr(mac)) { + memcpy(ue->ue_eaddr, mac, sizeof(ue->ue_eaddr)); + return (0); + } + } + return (ENXIO); +} + +phandle_t +usb_fdt_get_node(device_t dev, struct usb_device *udev) +{ + struct usb_device *ud; + struct usb_device *udev_stack[7]; /* USB spec max nesting depth */ + phandle_t controller_node, node; + int idx; + + /* + * Start searching at the controller node. The usb_device links to the + * bus, and its parent is the controller. If we can't get the + * controller node, the requesting device cannot be in the fdt data. + */ + if ((controller_node = ofw_bus_get_node(udev->bus->parent)) == -1) + return (-1); + + /* + * Walk up the usb hub ancestor hierarchy, building a stack of devices + * that begins with the requesting device and includes all the hubs + * between it and the controller, NOT including the root hub (the FDT + * bindings treat the controller and root hub as the same thing). + */ + for (ud = udev, idx = 0; ud->parent_hub != NULL; ud = ud->parent_hub) { + KASSERT(idx < nitems(udev_stack), ("Too many hubs")); + udev_stack[idx++] = ud; + } + + /* + * Now walk down the stack of udevs from the controller to the + * requesting device, and also down the hierarchy of nested children of + * the controller node in the fdt data. At each nesting level of fdt + * data look for a child node whose properties match the vID,pID,portIdx + * tuple for the udev at the corresponding layer of the udev stack. As + * long as we keep matching up child nodes with udevs, loop and search + * within the children of the just-found child for the next-deepest hub. + * If at any level we fail to find a matching node, stop searching and + * return. When we hit the end of the stack (the requesting device) we + * return whatever the result was for the search at that nesting level. + */ + for (node = controller_node;;) { + node = find_udev_in_children(node, udev_stack[--idx]); + if (idx == 0 || node == -1) + break; + } + return (node); +} Index: sys/modules/usb/usb/Makefile =================================================================== --- sys/modules/usb/usb/Makefile +++ sys/modules/usb/usb/Makefile @@ -39,4 +39,8 @@ usb_msctest.c usb_parse.c usb_pf.c usb_process.c usb_request.c \ usb_transfer.c usb_util.c +.if !empty(OPT_FDT) +SRCS+= usb_fdt_support.c ofw_bus_if.h +.endif + .include