ugensa.c revision 184610
1/* $FreeBSD: head/sys/dev/usb2/serial/ugensa2.c 184610 2008-11-04 02:31:03Z alfred $ */ 2/* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */ 3 4/* 5 * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Roland C. Dowdeswell <elric@netbsd.org>. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40/* 41 * NOTE: all function names beginning like "ugensa_cfg_" can only 42 * be called from within the config thread function ! 43 */ 44 45#include <dev/usb2/include/usb2_devid.h> 46#include <dev/usb2/include/usb2_standard.h> 47#include <dev/usb2/include/usb2_mfunc.h> 48#include <dev/usb2/include/usb2_error.h> 49#include <dev/usb2/include/usb2_cdc.h> 50#include <dev/usb2/include/usb2_defs.h> 51 52#define USB_DEBUG_VAR usb2_debug 53 54#include <dev/usb2/core/usb2_core.h> 55#include <dev/usb2/core/usb2_debug.h> 56#include <dev/usb2/core/usb2_process.h> 57#include <dev/usb2/core/usb2_config_td.h> 58#include <dev/usb2/core/usb2_request.h> 59#include <dev/usb2/core/usb2_lookup.h> 60#include <dev/usb2/core/usb2_util.h> 61#include <dev/usb2/core/usb2_device.h> 62 63#include <dev/usb2/serial/usb2_serial.h> 64 65#define UGENSA_BUF_SIZE 2048 /* bytes */ 66#define UGENSA_N_TRANSFER 4 /* units */ 67#define UGENSA_CONFIG_INDEX 0 68#define UGENSA_IFACE_INDEX 0 69#define UGENSA_IFACE_MAX 8 /* exclusivly */ 70 71struct ugensa_sub_softc { 72 struct usb2_com_softc *sc_usb2_com_ptr; 73 struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER]; 74 75 uint8_t sc_flags; 76#define UGENSA_FLAG_BULK_READ_STALL 0x01 77#define UGENSA_FLAG_BULK_WRITE_STALL 0x02 78}; 79 80struct ugensa_softc { 81 struct usb2_com_super_softc sc_super_ucom; 82 struct usb2_com_softc sc_ucom[UGENSA_IFACE_MAX]; 83 struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX]; 84 85 struct mtx sc_mtx; 86 uint8_t sc_niface; 87}; 88 89/* prototypes */ 90 91static device_probe_t ugensa_probe; 92static device_attach_t ugensa_attach; 93static device_detach_t ugensa_detach; 94 95static usb2_callback_t ugensa_bulk_write_callback; 96static usb2_callback_t ugensa_bulk_write_clear_stall_callback; 97static usb2_callback_t ugensa_bulk_read_callback; 98static usb2_callback_t ugensa_bulk_read_clear_stall_callback; 99 100static void ugensa_start_read(struct usb2_com_softc *ucom); 101static void ugensa_stop_read(struct usb2_com_softc *ucom); 102static void ugensa_start_write(struct usb2_com_softc *ucom); 103static void ugensa_stop_write(struct usb2_com_softc *ucom); 104 105static const struct usb2_config 106 ugensa_xfer_config[UGENSA_N_TRANSFER] = { 107 108 [0] = { 109 .type = UE_BULK, 110 .endpoint = UE_ADDR_ANY, 111 .direction = UE_DIR_OUT, 112 .mh.bufsize = UGENSA_BUF_SIZE, 113 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 114 .mh.callback = &ugensa_bulk_write_callback, 115 }, 116 117 [1] = { 118 .type = UE_BULK, 119 .endpoint = UE_ADDR_ANY, 120 .direction = UE_DIR_IN, 121 .mh.bufsize = UGENSA_BUF_SIZE, 122 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 123 .mh.callback = &ugensa_bulk_read_callback, 124 }, 125 126 [2] = { 127 .type = UE_CONTROL, 128 .endpoint = 0x00, /* Control pipe */ 129 .direction = UE_DIR_ANY, 130 .mh.bufsize = sizeof(struct usb2_device_request), 131 .mh.flags = {}, 132 .mh.callback = &ugensa_bulk_write_clear_stall_callback, 133 .mh.timeout = 1000, /* 1 second */ 134 .mh.interval = 50, /* 50ms */ 135 }, 136 137 [3] = { 138 .type = UE_CONTROL, 139 .endpoint = 0x00, /* Control pipe */ 140 .direction = UE_DIR_ANY, 141 .mh.bufsize = sizeof(struct usb2_device_request), 142 .mh.flags = {}, 143 .mh.callback = &ugensa_bulk_read_clear_stall_callback, 144 .mh.timeout = 1000, /* 1 second */ 145 .mh.interval = 50, /* 50ms */ 146 }, 147}; 148 149static const struct usb2_com_callback ugensa_callback = { 150 .usb2_com_start_read = &ugensa_start_read, 151 .usb2_com_stop_read = &ugensa_stop_read, 152 .usb2_com_start_write = &ugensa_start_write, 153 .usb2_com_stop_write = &ugensa_stop_write, 154}; 155 156static device_method_t ugensa_methods[] = { 157 /* Device methods */ 158 DEVMETHOD(device_probe, ugensa_probe), 159 DEVMETHOD(device_attach, ugensa_attach), 160 DEVMETHOD(device_detach, ugensa_detach), 161 {0, 0} 162}; 163 164static devclass_t ugensa_devclass; 165 166static driver_t ugensa_driver = { 167 .name = "ugensa", 168 .methods = ugensa_methods, 169 .size = sizeof(struct ugensa_softc), 170}; 171 172DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0); 173MODULE_DEPEND(ugensa, usb2_serial, 1, 1, 1); 174MODULE_DEPEND(ugensa, usb2_core, 1, 1, 1); 175MODULE_DEPEND(ugensa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); 176 177static const struct usb2_device_id ugensa_devs[] = { 178 {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)}, 179 {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)}, 180 {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)}, 181 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)}, 182 {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E270, 0)}, 183 {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, 0)}, 184 {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, 0)}, 185 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, 0)}, 186 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, 0)}, 187 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, 0)}, 188 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, 0)}, 189 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, 0)}, 190 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, 0)}, 191 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U950D, 0)}, 192 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, 0)}, 193 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, 0)}, 194 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, 0)}, 195 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, 0)}, 196 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, 0)}, 197 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, 0)}, 198 {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, 0)}, 199 {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)}, 200 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, 0)}, 201 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, 0)}, 202 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, 0)}, 203 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, 0)}, 204 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, 0)}, 205 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, 0)}, 206 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, 0)}, 207 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, 0)}, 208 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, 0)}, 209 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, 0)}, 210 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, 0)}, 211 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, 0)}, 212 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 0)}, 213 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, 0)}, 214 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, 0)}, 215 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, 0)}, 216 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, 0)}, 217 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, 0)}, 218 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, 0)}, 219 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, 0)}, 220 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, 0)}, 221 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, 0)}, 222 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, 0)}, 223 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, 0)}, 224 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, 0)}, 225}; 226 227static int 228ugensa_probe(device_t dev) 229{ 230 struct usb2_attach_arg *uaa = device_get_ivars(dev); 231 232 if (uaa->usb2_mode != USB_MODE_HOST) { 233 return (ENXIO); 234 } 235 if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { 236 return (ENXIO); 237 } 238 if (uaa->info.bIfaceIndex != 0) { 239 return (ENXIO); 240 } 241 return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); 242} 243 244static int 245ugensa_attach(device_t dev) 246{ 247 struct usb2_attach_arg *uaa = device_get_ivars(dev); 248 struct ugensa_softc *sc = device_get_softc(dev); 249 struct ugensa_sub_softc *ssc; 250 struct usb2_interface *iface; 251 int32_t error; 252 uint8_t iface_index; 253 int x, cnt; 254 255 if (sc == NULL) 256 return (ENOMEM); 257 258 device_set_usb2_desc(dev); 259 mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF); 260 261 /* Figure out how many interfaces this device has got */ 262 for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) { 263 if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) || 264 (usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) { 265 /* we have reached the end */ 266 break; 267 } 268 } 269 270 if (cnt == 0) { 271 device_printf(dev, "No interfaces!\n"); 272 goto detach; 273 } 274 for (x = 0; x < cnt; x++) { 275 iface = usb2_get_iface(uaa->device, x); 276 if (iface->idesc->bInterfaceClass != UICLASS_VENDOR) 277 /* Not a serial port, most likely a SD reader */ 278 continue; 279 280 ssc = sc->sc_sub + sc->sc_niface; 281 ssc->sc_usb2_com_ptr = sc->sc_ucom + sc->sc_niface; 282 283 iface_index = (UGENSA_IFACE_INDEX + x); 284 error = usb2_transfer_setup(uaa->device, 285 &iface_index, ssc->sc_xfer, ugensa_xfer_config, 286 UGENSA_N_TRANSFER, ssc, &sc->sc_mtx); 287 288 if (error) { 289 device_printf(dev, "allocating USB " 290 "transfers failed!\n"); 291 goto detach; 292 } 293 /* clear stall at first run */ 294 ssc->sc_flags |= (UGENSA_FLAG_BULK_WRITE_STALL | 295 UGENSA_FLAG_BULK_READ_STALL); 296 297 /* initialize port number */ 298 ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface; 299 sc->sc_niface++; 300 if (x != uaa->info.bIfaceIndex) 301 usb2_set_parent_iface(uaa->device, x, 302 uaa->info.bIfaceIndex); 303 } 304 device_printf(dev, "Found %d interfaces.\n", sc->sc_niface); 305 306 error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc, 307 &ugensa_callback, &sc->sc_mtx); 308 if (error) { 309 DPRINTF("attach failed\n"); 310 goto detach; 311 } 312 return (0); /* success */ 313 314detach: 315 ugensa_detach(dev); 316 return (ENXIO); /* failure */ 317} 318 319static int 320ugensa_detach(device_t dev) 321{ 322 struct ugensa_softc *sc = device_get_softc(dev); 323 uint8_t x; 324 325 usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface); 326 327 for (x = 0; x < sc->sc_niface; x++) { 328 usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); 329 } 330 mtx_destroy(&sc->sc_mtx); 331 332 return (0); 333} 334 335static void 336ugensa_bulk_write_callback(struct usb2_xfer *xfer) 337{ 338 struct ugensa_sub_softc *ssc = xfer->priv_sc; 339 uint32_t actlen; 340 341 switch (USB_GET_STATE(xfer)) { 342 case USB_ST_SETUP: 343 case USB_ST_TRANSFERRED: 344 if (ssc->sc_flags & UGENSA_FLAG_BULK_WRITE_STALL) { 345 usb2_transfer_start(ssc->sc_xfer[2]); 346 return; 347 } 348 if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0, 349 UGENSA_BUF_SIZE, &actlen)) { 350 xfer->frlengths[0] = actlen; 351 usb2_start_hardware(xfer); 352 } 353 return; 354 355 default: /* Error */ 356 if (xfer->error != USB_ERR_CANCELLED) { 357 ssc->sc_flags |= UGENSA_FLAG_BULK_WRITE_STALL; 358 usb2_transfer_start(ssc->sc_xfer[2]); 359 } 360 return; 361 362 } 363} 364 365static void 366ugensa_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) 367{ 368 struct ugensa_sub_softc *ssc = xfer->priv_sc; 369 struct usb2_xfer *xfer_other = ssc->sc_xfer[0]; 370 371 if (usb2_clear_stall_callback(xfer, xfer_other)) { 372 DPRINTF("stall cleared\n"); 373 ssc->sc_flags &= ~UGENSA_FLAG_BULK_WRITE_STALL; 374 usb2_transfer_start(xfer_other); 375 } 376 return; 377} 378 379static void 380ugensa_bulk_read_callback(struct usb2_xfer *xfer) 381{ 382 struct ugensa_sub_softc *ssc = xfer->priv_sc; 383 384 switch (USB_GET_STATE(xfer)) { 385 case USB_ST_TRANSFERRED: 386 usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0, 387 xfer->actlen); 388 389 case USB_ST_SETUP: 390 if (ssc->sc_flags & UGENSA_FLAG_BULK_READ_STALL) { 391 usb2_transfer_start(ssc->sc_xfer[3]); 392 } else { 393 xfer->frlengths[0] = xfer->max_data_length; 394 usb2_start_hardware(xfer); 395 } 396 return; 397 398 default: /* Error */ 399 if (xfer->error != USB_ERR_CANCELLED) { 400 ssc->sc_flags |= UGENSA_FLAG_BULK_READ_STALL; 401 usb2_transfer_start(ssc->sc_xfer[3]); 402 } 403 return; 404 405 } 406} 407 408static void 409ugensa_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) 410{ 411 struct ugensa_sub_softc *ssc = xfer->priv_sc; 412 struct usb2_xfer *xfer_other = ssc->sc_xfer[1]; 413 414 if (usb2_clear_stall_callback(xfer, xfer_other)) { 415 DPRINTF("stall cleared\n"); 416 ssc->sc_flags &= ~UGENSA_FLAG_BULK_READ_STALL; 417 usb2_transfer_start(xfer_other); 418 } 419 return; 420} 421 422static void 423ugensa_start_read(struct usb2_com_softc *ucom) 424{ 425 struct ugensa_softc *sc = ucom->sc_parent; 426 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 427 428 usb2_transfer_start(ssc->sc_xfer[1]); 429 return; 430} 431 432static void 433ugensa_stop_read(struct usb2_com_softc *ucom) 434{ 435 struct ugensa_softc *sc = ucom->sc_parent; 436 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 437 438 usb2_transfer_stop(ssc->sc_xfer[3]); 439 usb2_transfer_stop(ssc->sc_xfer[1]); 440 return; 441} 442 443static void 444ugensa_start_write(struct usb2_com_softc *ucom) 445{ 446 struct ugensa_softc *sc = ucom->sc_parent; 447 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 448 449 usb2_transfer_start(ssc->sc_xfer[0]); 450 return; 451} 452 453static void 454ugensa_stop_write(struct usb2_com_softc *ucom) 455{ 456 struct ugensa_softc *sc = ucom->sc_parent; 457 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 458 459 usb2_transfer_stop(ssc->sc_xfer[2]); 460 usb2_transfer_stop(ssc->sc_xfer[0]); 461 return; 462} 463