/* * Attach a multiplexed serial port * Data is read/written with requests on the default control pipe. An interrupt * endpoint returns when there is new data to be read. */ static int uhso_attach_muxserial(struct uhso_softc *sc, struct usb_interface *iface, int type) { struct usb_descriptor *desc; int i, port, tty; usb_error_t uerr; /* * The class specific interface (type 0x24) descriptor subtype field * contains a bitmask that specifies which (and how many) ports that * are available through this multiplexed serial port. */ desc = usbd_find_descriptor(sc->sc_udev, NULL, iface->idesc->bInterfaceNumber, UDESC_CS_INTERFACE, 0xff, 0, 0); if (desc == NULL) { UHSO_DPRINTF(0, "Failed to find UDESC_CS_INTERFACE\n"); return (ENXIO); } UHSO_DPRINTF(1, "Mux port mask %x\n", desc->bDescriptorSubtype); if (desc->bDescriptorSubtype == 0) return (ENXIO); /* * The bitmask is one octet, loop through the number of * bits that are set and create a TTY for each. */ for (i = 0; i < 8; i++) { port = (1 << i); if ((port & desc->bDescriptorSubtype) == port) { UHSO_DPRINTF(2, "Found mux port %x (%d)\n", port, i); tty = uhso_alloc_tty(sc); if (tty < 0) return (ENOMEM); sc->sc_tty[tty].ht_muxport = i; uerr = usbd_transfer_setup(sc->sc_udev, &sc->sc_iface_index, sc->sc_tty[tty].ht_xfer, uhso_ctrl_config, UHSO_CTRL_MAX, sc, &sc->sc_mtx); if (uerr) { device_printf(sc->sc_dev, "Failed to setup control pipe: %s\n", usbd_errstr(uerr)); return (ENXIO); } } } /* Setup the intr. endpoint */ uerr = usbd_transfer_setup(sc->sc_udev, &iface->idesc->bInterfaceNumber, sc->sc_xfer, uhso_mux_config, 1, sc, &sc->sc_mtx); if (uerr) return (ENXIO); return (0); }
static int cdce_attach(device_t dev) { struct cdce_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_interface *iface; const struct usb_cdc_union_descriptor *ud; const struct usb_interface_descriptor *id; const struct usb_cdc_ethernet_descriptor *ued; const struct usb_config *pcfg; int error; uint8_t i; uint8_t data_iface_no; char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ sc->sc_flags = USB_GET_DRIVER_INFO(uaa); sc->sc_ue.ue_udev = uaa->device; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); ud = usbd_find_descriptor (uaa->device, NULL, uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1); if ((ud == NULL) || (ud->bLength < sizeof(*ud)) || (sc->sc_flags & CDCE_FLAG_NO_UNION)) { DPRINTFN(1, "No union descriptor!\n"); sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; goto alloc_transfers; } data_iface_no = ud->bSlaveInterface[0]; for (i = 0;; i++) { iface = usbd_get_iface(uaa->device, i); if (iface) { id = usbd_get_interface_descriptor(iface); if (id && (id->bInterfaceNumber == data_iface_no)) { sc->sc_ifaces_index[0] = i; sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); break; } } else { device_printf(dev, "no data interface found\n"); goto detach; } } /* * <quote> * * The Data Class interface of a networking device shall have * a minimum of two interface settings. The first setting * (the default interface setting) includes no endpoints and * therefore no networking traffic is exchanged whenever the * default interface setting is selected. One or more * additional interface settings are used for normal * operation, and therefore each includes a pair of endpoints * (one IN, and one OUT) to exchange network traffic. Select * an alternate interface setting to initialize the network * aspects of the device and to enable the exchange of * network traffic. * * </quote> * * Some devices, most notably cable modems, include interface * settings that have no IN or OUT endpoint, therefore loop * through the list of all available interface settings * looking for one with both IN and OUT endpoints. */ alloc_transfers: pcfg = cdce_config; /* Default Configuration */ for (i = 0; i != 32; i++) { error = usbd_set_alt_interface_index(uaa->device, sc->sc_ifaces_index[0], i); if (error) break; #if CDCE_HAVE_NCM if ((i == 0) && (cdce_ncm_init(sc) == 0)) pcfg = cdce_ncm_config; #endif error = usbd_transfer_setup(uaa->device, sc->sc_ifaces_index, sc->sc_xfer, pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx); if (error == 0) break; } if (error || (i == 32)) { device_printf(dev, "No valid alternate " "setting found\n"); goto detach; } ued = usbd_find_descriptor (uaa->device, NULL, uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1); if ((ued == NULL) || (ued->bLength < sizeof(*ued))) { error = USB_ERR_INVAL; } else { error = usbd_req_get_string_any(uaa->device, NULL, eaddr_str, sizeof(eaddr_str), ued->iMacAddress); } if (error) { /* fake MAC address */ device_printf(dev, "faking MAC address\n"); sc->sc_ue.ue_eaddr[0] = 0x2a; memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t)); sc->sc_ue.ue_eaddr[5] = device_get_unit(dev); } else { memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr)); for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) { char c = eaddr_str[i]; if ('0' <= c && c <= '9') c -= '0'; else if (c != 0) c -= 'A' - 10; else break; c &= 0xf; if ((i & 1) == 0) c <<= 4; sc->sc_ue.ue_eaddr[i / 2] |= c; } if (uaa->usb_mode == USB_MODE_DEVICE) { /* * Do not use the same MAC address like the peer ! */ sc->sc_ue.ue_eaddr[5] ^= 0xFF; } } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &cdce_ue_methods; error = uether_ifattach(ue); if (error) { device_printf(dev, "could not attach interface\n"); goto detach; } return (0); /* success */ detach: cdce_detach(dev); return (ENXIO); /* failure */ }
/*------------------------------------------------------------------------* * cdce_ncm_init * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t cdce_ncm_init(struct cdce_softc *sc) { struct usb_ncm_parameters temp; struct usb_device_request req; struct usb_ncm_func_descriptor *ufd; uint8_t value[8]; int err; ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL, sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0 - 1, UCDC_NCM_FUNC_DESC_SUBTYPE, 0 - 1); /* verify length of NCM functional descriptor */ if (ufd != NULL) { if (ufd->bLength < sizeof(*ufd)) ufd = NULL; else DPRINTFN(1, "Found NCM functional descriptor.\n"); } req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_ifaces_index[1]; req.wIndex[1] = 0; USETW(req.wLength, sizeof(temp)); err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, &temp, 0, NULL, 1000 /* ms */); if (err) return (1); /* Read correct set of parameters according to device mode */ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { sc->sc_ncm.rx_max = UGETDW(temp.dwNtbInMaxSize); sc->sc_ncm.tx_max = UGETDW(temp.dwNtbOutMaxSize); sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder); sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor); sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment); sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); } else { sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize); sc->sc_ncm.tx_max = UGETDW(temp.dwNtbInMaxSize); sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder); sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor); sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment); sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); } /* Verify maximum receive length */ if ((sc->sc_ncm.rx_max < 32) || (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) { DPRINTFN(1, "Using default maximum receive length\n"); sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN; } /* Verify maximum transmit length */ if ((sc->sc_ncm.tx_max < 32) || (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) { DPRINTFN(1, "Using default maximum transmit length\n"); sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN; } /* * Verify that the structure alignment is: * - power of two * - not greater than the maximum transmit length * - not less than four bytes */ if ((sc->sc_ncm.tx_struct_align < 4) || (sc->sc_ncm.tx_struct_align != ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) || (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) { DPRINTFN(1, "Using default other alignment: 4 bytes\n"); sc->sc_ncm.tx_struct_align = 4; } /* * Verify that the payload alignment is: * - power of two * - not greater than the maximum transmit length * - not less than four bytes */ if ((sc->sc_ncm.tx_modulus < 4) || (sc->sc_ncm.tx_modulus != ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) || (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) { DPRINTFN(1, "Using default transmit modulus: 4 bytes\n"); sc->sc_ncm.tx_modulus = 4; } /* Verify that the payload remainder */ if ((sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) { DPRINTFN(1, "Using default transmit remainder: 0 bytes\n"); sc->sc_ncm.tx_remainder = 0; } /* * Offset the TX remainder so that IP packet payload starts at * the tx_modulus. This is not too clear in the specification. */ sc->sc_ncm.tx_remainder = (sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) & (sc->sc_ncm.tx_modulus - 1); /* Verify max datagrams */ if (sc->sc_ncm.tx_nframe == 0 || sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) { DPRINTFN(1, "Using default max " "subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1); /* need to reserve one entry for zero padding */ sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1); } /* Additional configuration, will fail in device side mode, which is OK. */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_ifaces_index[1]; req.wIndex[1] = 0; if (ufd != NULL && (ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) { USETW(req.wLength, 8); USETDW(value, sc->sc_ncm.rx_max); USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1)); USETW(value + 6, 0); } else { USETW(req.wLength, 4); USETDW(value, sc->sc_ncm.rx_max); } err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, &value, 0, NULL, 1000 /* ms */); if (err) { DPRINTFN(1, "Setting input size " "to %u failed.\n", sc->sc_ncm.rx_max); } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_NCM_SET_CRC_MODE; USETW(req.wValue, 0); /* no CRC */ req.wIndex[0] = sc->sc_ifaces_index[1]; req.wIndex[1] = 0; USETW(req.wLength, 0); err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, NULL, 0, NULL, 1000 /* ms */); if (err) { DPRINTFN(1, "Setting CRC mode to off failed.\n"); } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_NCM_SET_NTB_FORMAT; USETW(req.wValue, 0); /* NTB-16 */ req.wIndex[0] = sc->sc_ifaces_index[1]; req.wIndex[1] = 0; USETW(req.wLength, 0); err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, NULL, 0, NULL, 1000 /* ms */); if (err) { DPRINTFN(1, "Setting NTB format to 16-bit failed.\n"); } return (0); /* success */ }