u3g.c revision 188413
1/* 2 * Copyright (c) 2008 AnyWi Technologies 3 * Author: Andrea Guzzo <aguzzo@anywi.com> 4 * * based on uark.c 1.1 2006/08/14 08:30:22 jsg * 5 * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk * 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 * 19 * $FreeBSD: head/sys/dev/usb2/serial/u3g2.c 188413 2009-02-09 22:05:25Z thompsa $ 20 */ 21 22/* 23 * NOTE: 24 * 25 * - The detour through the tty layer is ridiculously expensive wrt 26 * buffering due to the high speeds. 27 * 28 * We should consider adding a simple r/w device which allows 29 * attaching of PPP in a more efficient way. 30 * 31 * NOTE: 32 * 33 * - The device ID's are stored in "core/usb2_msctest.c" 34 */ 35 36#include <dev/usb2/include/usb2_devid.h> 37#include <dev/usb2/include/usb2_standard.h> 38#include <dev/usb2/include/usb2_mfunc.h> 39#include <dev/usb2/include/usb2_error.h> 40#include <dev/usb2/include/usb2_defs.h> 41 42#define USB_DEBUG_VAR u3g_debug 43 44#include <dev/usb2/core/usb2_core.h> 45#include <dev/usb2/core/usb2_debug.h> 46#include <dev/usb2/core/usb2_process.h> 47#include <dev/usb2/core/usb2_request.h> 48#include <dev/usb2/core/usb2_lookup.h> 49#include <dev/usb2/core/usb2_util.h> 50#include <dev/usb2/core/usb2_busdma.h> 51#include <dev/usb2/core/usb2_msctest.h> 52#include <dev/usb2/core/usb2_dynamic.h> 53#include <dev/usb2/core/usb2_device.h> 54 55#include <dev/usb2/serial/usb2_serial.h> 56 57#if USB_DEBUG 58static int u3g_debug = 0; 59 60SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB u3g"); 61SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW, 62 &u3g_debug, 0, "u3g debug level"); 63#endif 64 65#define U3G_MAXPORTS 4 66#define U3G_CONFIG_INDEX 0 67#define U3G_BSIZE 2048 68 69#define U3GSP_GPRS 0 70#define U3GSP_EDGE 1 71#define U3GSP_CDMA 2 72#define U3GSP_UMTS 3 73#define U3GSP_HSDPA 4 74#define U3GSP_HSUPA 5 75#define U3GSP_HSPA 6 76#define U3GSP_MAX 7 77 78#define U3GFL_NONE 0x00 /* No flags */ 79#define U3GFL_HUAWEI_INIT 0x01 /* Init command required */ 80#define U3GFL_SCSI_EJECT 0x02 /* SCSI eject command required */ 81#define U3GFL_SIERRA_INIT 0x04 /* Init command required */ 82 83struct u3g_speeds_s { 84 uint32_t ispeed; 85 uint32_t ospeed; 86}; 87 88enum { 89 U3G_BULK_WR, 90 U3G_BULK_RD, 91 U3G_N_TRANSFER, 92}; 93 94struct u3g_softc { 95 struct usb2_com_super_softc sc_super_ucom; 96 struct usb2_com_softc sc_ucom[U3G_MAXPORTS]; 97 98 struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER]; 99 struct usb2_device *sc_udev; 100 101 uint8_t sc_lsr; /* local status register */ 102 uint8_t sc_msr; /* U3G status register */ 103 uint8_t sc_numports; 104}; 105 106static device_probe_t u3g_probe; 107static device_attach_t u3g_attach; 108static device_detach_t u3g_detach; 109 110static usb2_callback_t u3g_write_callback; 111static usb2_callback_t u3g_read_callback; 112 113static void u3g_start_read(struct usb2_com_softc *ucom); 114static void u3g_stop_read(struct usb2_com_softc *ucom); 115static void u3g_start_write(struct usb2_com_softc *ucom); 116static void u3g_stop_write(struct usb2_com_softc *ucom); 117 118static int u3g_driver_loaded(struct module *mod, int what, void *arg); 119 120static const struct usb2_config u3g_config[U3G_N_TRANSFER] = { 121 122 [U3G_BULK_WR] = { 123 .type = UE_BULK, 124 .endpoint = UE_ADDR_ANY, 125 .direction = UE_DIR_OUT, 126 .mh.bufsize = U3G_BSIZE,/* bytes */ 127 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 128 .mh.callback = &u3g_write_callback, 129 }, 130 131 [U3G_BULK_RD] = { 132 .type = UE_BULK, 133 .endpoint = UE_ADDR_ANY, 134 .direction = UE_DIR_IN, 135 .mh.bufsize = U3G_BSIZE,/* bytes */ 136 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 137 .mh.callback = &u3g_read_callback, 138 }, 139}; 140 141static const struct usb2_com_callback u3g_callback = { 142 .usb2_com_start_read = &u3g_start_read, 143 .usb2_com_stop_read = &u3g_stop_read, 144 .usb2_com_start_write = &u3g_start_write, 145 .usb2_com_stop_write = &u3g_stop_write, 146}; 147 148#if 0 149static const struct u3g_speeds_s u3g_speeds[U3GSP_MAX] = { 150 [U3GSP_GPRS] = {64000, 64000}, 151 [U3GSP_EDGE] = {384000, 64000}, 152 [U3GSP_CDMA] = {384000, 64000}, 153 [U3GSP_UMTS] = {384000, 64000}, 154 [U3GSP_HSDPA] = {1200000, 384000}, 155 [U3GSP_HSUPA] = {1200000, 384000}, 156 [U3GSP_HSPA] = {7200000, 384000}, 157}; 158#endif 159 160static device_method_t u3g_methods[] = { 161 DEVMETHOD(device_probe, u3g_probe), 162 DEVMETHOD(device_attach, u3g_attach), 163 DEVMETHOD(device_detach, u3g_detach), 164 {0, 0} 165}; 166 167static devclass_t u3g_devclass; 168 169static driver_t u3g_driver = { 170 .name = "u3g", 171 .methods = u3g_methods, 172 .size = sizeof(struct u3g_softc), 173}; 174 175DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0); 176MODULE_DEPEND(u3g, usb2_serial, 1, 1, 1); 177MODULE_DEPEND(u3g, usb2_core, 1, 1, 1); 178 179/* Huawei specific defines */ 180 181#define U3GINFO(flag,speed) ((flag)|((speed) * 256)) 182#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256) 183 184/* 185 * NOTE: The entries marked with XXX should be checked for the correct 186 * speed indication to set the buffer sizes. 187 */ 188static const struct usb2_device_id u3g_devs[] = { 189 /* OEM: Option */ 190 {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 191 {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 192 {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 193 {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, 194 {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, 195 {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 196 /* OEM: Qualcomm, Inc. */ 197 {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, 198 {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, 199 /* OEM: Huawei */ 200 {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))}, 201 {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))}, 202 /* OEM: Novatel */ 203 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, 204 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 205 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, 206 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 207 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 208 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 209 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 210 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 211 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 212 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 213 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 214 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 215 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, 216 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 217 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, 218 {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 219 /* OEM: Merlin */ 220 {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 221 /* OEM: Sierra Wireless: */ 222 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 223 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 224 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 225 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 226 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 227 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 228 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 229 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 230 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 231 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 232 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 233 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 234 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 235 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 236 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 237 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 238 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 239 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 240 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 241 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 242 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 243 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 244 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, /* XXX */ 245 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, 246 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 247 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 248 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSPA, U3GFL_NONE))}, /* XXX */ 249 /* Sierra TruInstaller device ID */ 250 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))}, 251}; 252 253static void 254u3g_sierra_init(struct usb2_device *udev) 255{ 256 struct usb2_device_request req; 257 258 DPRINTFN(0, "\n"); 259 260 req.bmRequestType = UT_VENDOR; 261 req.bRequest = UR_SET_INTERFACE; 262 USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 263 USETW(req.wIndex, UHF_PORT_CONNECTION); 264 USETW(req.wLength, 0); 265 266 if (usb2_do_request_flags(udev, NULL, &req, 267 NULL, 0, NULL, USB_MS_HZ)) { 268 /* ignore any errors */ 269 } 270 return; 271} 272 273static void 274u3g_huawei_init(struct usb2_device *udev) 275{ 276 struct usb2_device_request req; 277 278 DPRINTFN(0, "\n"); 279 280 req.bmRequestType = UT_WRITE_DEVICE; 281 req.bRequest = UR_SET_FEATURE; 282 USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 283 USETW(req.wIndex, UHF_PORT_SUSPEND); 284 USETW(req.wLength, 0); 285 286 if (usb2_do_request_flags(udev, NULL, &req, 287 NULL, 0, NULL, USB_MS_HZ)) { 288 /* ignore any errors */ 289 } 290 return; 291} 292 293static int 294u3g_lookup_huawei(struct usb2_attach_arg *uaa) 295{ 296 /* Calling the lookup function will also set the driver info! */ 297 return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); 298} 299 300/* 301 * The following function handles 3G modem devices (E220, Mobile, 302 * etc.) with auto-install flash disks for Windows/MacOSX on the first 303 * interface. After some command or some delay they change appearance 304 * to a modem. 305 */ 306static usb2_error_t 307u3g_test_huawei_autoinst(struct usb2_device *udev, 308 struct usb2_attach_arg *uaa) 309{ 310 struct usb2_interface *iface; 311 struct usb2_interface_descriptor *id; 312 uint32_t flags; 313 314 if (udev == NULL) { 315 return (USB_ERR_INVAL); 316 } 317 iface = usb2_get_iface(udev, 0); 318 if (iface == NULL) { 319 return (USB_ERR_INVAL); 320 } 321 id = iface->idesc; 322 if (id == NULL) { 323 return (USB_ERR_INVAL); 324 } 325 if (id->bInterfaceClass != UICLASS_MASS) { 326 return (USB_ERR_INVAL); 327 } 328 if (u3g_lookup_huawei(uaa)) { 329 /* no device match */ 330 return (USB_ERR_INVAL); 331 } 332 flags = USB_GET_DRIVER_INFO(uaa); 333 334 if (flags & U3GFL_HUAWEI_INIT) { 335 u3g_huawei_init(udev); 336 } else if (flags & U3GFL_SCSI_EJECT) { 337 return (usb2_test_autoinstall(udev, 0, 1)); 338 } else if (flags & U3GFL_SIERRA_INIT) { 339 u3g_sierra_init(udev); 340 } else { 341 /* no quirks */ 342 return (USB_ERR_INVAL); 343 } 344 return (0); /* success */ 345} 346 347static int 348u3g_driver_loaded(struct module *mod, int what, void *arg) 349{ 350 switch (what) { 351 case MOD_LOAD: 352 /* register our autoinstall handler */ 353 usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst; 354 break; 355 case MOD_UNLOAD: 356 usb2_test_huawei_unload(NULL); 357 break; 358 default: 359 return (EOPNOTSUPP); 360 } 361 return (0); 362} 363 364static int 365u3g_probe(device_t self) 366{ 367 struct usb2_attach_arg *uaa = device_get_ivars(self); 368 369 if (uaa->usb2_mode != USB_MODE_HOST) { 370 return (ENXIO); 371 } 372 if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) { 373 return (ENXIO); 374 } 375 if (uaa->info.bInterfaceClass != UICLASS_VENDOR) { 376 return (ENXIO); 377 } 378 return (u3g_lookup_huawei(uaa)); 379} 380 381static int 382u3g_attach(device_t dev) 383{ 384 struct usb2_config u3g_config_tmp[U3G_N_TRANSFER]; 385 struct usb2_attach_arg *uaa = device_get_ivars(dev); 386 struct u3g_softc *sc = device_get_softc(dev); 387 struct usb2_interface *iface; 388 struct usb2_interface_descriptor *id; 389 uint8_t m; 390 uint8_t n; 391 uint8_t i; 392 uint8_t x; 393 int error; 394 395 DPRINTF("sc=%p\n", sc); 396 397 /* copy in USB config */ 398 for (n = 0; n != U3G_N_TRANSFER; n++) 399 u3g_config_tmp[n] = u3g_config[n]; 400 401 device_set_usb2_desc(dev); 402 403 sc->sc_udev = uaa->device; 404 405 x = 0; /* interface index */ 406 i = 0; /* endpoint index */ 407 m = 0; /* number of ports */ 408 409 while (m != U3G_MAXPORTS) { 410 411 /* update BULK endpoint index */ 412 for (n = 0; n != U3G_N_TRANSFER; n++) 413 u3g_config_tmp[n].ep_index = i; 414 415 iface = usb2_get_iface(uaa->device, x); 416 if (iface == NULL) { 417 if (m != 0) 418 break; /* end of interfaces */ 419 DPRINTF("did not find any modem endpoints\n"); 420 goto detach; 421 } 422 423 id = usb2_get_interface_descriptor(iface); 424 if ((id == NULL) || 425 (id->bInterfaceClass != UICLASS_VENDOR)) { 426 /* next interface */ 427 x++; 428 i = 0; 429 continue; 430 } 431 432 /* try to allocate a set of BULK endpoints */ 433 error = usb2_transfer_setup(uaa->device, &x, 434 sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER, 435 &sc->sc_ucom[m], &Giant); 436 if (error) { 437 /* next interface */ 438 x++; 439 i = 0; 440 continue; 441 } 442 443 /* grab other interface, if any */ 444 if (x != uaa->info.bIfaceIndex) 445 usb2_set_parent_iface(uaa->device, x, 446 uaa->info.bIfaceIndex); 447 448 /* set stall by default */ 449 usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]); 450 usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]); 451 452 m++; /* found one port */ 453 i++; /* next endpoint index */ 454 } 455 456 sc->sc_numports = m; 457 458 error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, 459 sc->sc_numports, sc, &u3g_callback, &Giant); 460 if (error) { 461 DPRINTF("usb2_com_attach failed\n"); 462 goto detach; 463 } 464 if (sc->sc_numports != 1) { 465 /* be verbose */ 466 device_printf(dev, "Found %u ports.\n", 467 (unsigned int)sc->sc_numports); 468 } 469 return (0); 470 471detach: 472 u3g_detach(dev); 473 return (ENXIO); 474} 475 476static int 477u3g_detach(device_t dev) 478{ 479 struct u3g_softc *sc = device_get_softc(dev); 480 uint8_t m; 481 482 DPRINTF("sc=%p\n", sc); 483 484 /* NOTE: It is not dangerous to detach more ports than attached! */ 485 usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS); 486 487 for (m = 0; m != U3G_MAXPORTS; m++) 488 usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER); 489 490 return (0); 491} 492 493static void 494u3g_start_read(struct usb2_com_softc *ucom) 495{ 496 struct u3g_softc *sc = ucom->sc_parent; 497 498 /* start read endpoint */ 499 usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]); 500 return; 501} 502 503static void 504u3g_stop_read(struct usb2_com_softc *ucom) 505{ 506 struct u3g_softc *sc = ucom->sc_parent; 507 508 /* stop read endpoint */ 509 usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]); 510 return; 511} 512 513static void 514u3g_start_write(struct usb2_com_softc *ucom) 515{ 516 struct u3g_softc *sc = ucom->sc_parent; 517 518 usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]); 519 return; 520} 521 522static void 523u3g_stop_write(struct usb2_com_softc *ucom) 524{ 525 struct u3g_softc *sc = ucom->sc_parent; 526 527 usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]); 528 return; 529} 530 531static void 532u3g_write_callback(struct usb2_xfer *xfer) 533{ 534 struct usb2_com_softc *ucom = xfer->priv_sc; 535 uint32_t actlen; 536 537 switch (USB_GET_STATE(xfer)) { 538 case USB_ST_TRANSFERRED: 539 case USB_ST_SETUP: 540tr_setup: 541 if (usb2_com_get_data(ucom, xfer->frbuffers, 0, 542 U3G_BSIZE, &actlen)) { 543 xfer->frlengths[0] = actlen; 544 usb2_start_hardware(xfer); 545 } 546 break; 547 548 default: /* Error */ 549 if (xfer->error != USB_ERR_CANCELLED) { 550 /* do a builtin clear-stall */ 551 xfer->flags.stall_pipe = 1; 552 goto tr_setup; 553 } 554 break; 555 } 556 return; 557} 558 559static void 560u3g_read_callback(struct usb2_xfer *xfer) 561{ 562 struct usb2_com_softc *ucom = xfer->priv_sc; 563 564 switch (USB_GET_STATE(xfer)) { 565 case USB_ST_TRANSFERRED: 566 usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen); 567 568 case USB_ST_SETUP: 569tr_setup: 570 xfer->frlengths[0] = xfer->max_data_length; 571 usb2_start_hardware(xfer); 572 break; 573 574 default: /* Error */ 575 if (xfer->error != USB_ERR_CANCELLED) { 576 /* do a builtin clear-stall */ 577 xfer->flags.stall_pipe = 1; 578 goto tr_setup; 579 } 580 break; 581 } 582 return; 583} 584