1 /* $FreeBSD$ */ 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 "usbdevs.h" 46 #include <dev/usb/usb.h> 47 #include <dev/usb/usb_mfunc.h> 48 #include <dev/usb/usb_error.h> 49 #include <dev/usb/usb_cdc.h> 50 #include <dev/usb/usb_defs.h> 51 52 #define USB_DEBUG_VAR usb2_debug 53 54 #include <dev/usb/usb_core.h> 55 #include <dev/usb/usb_debug.h> 56 #include <dev/usb/usb_process.h> 57 #include <dev/usb/usb_request.h> 58 #include <dev/usb/usb_lookup.h> 59 #include <dev/usb/usb_util.h> 60 #include <dev/usb/usb_device.h> 61 62 #include <dev/usb/serial/usb_serial.h> 63 64 #define UGENSA_BUF_SIZE 2048 /* bytes */ 65 #define UGENSA_CONFIG_INDEX 0 66 #define UGENSA_IFACE_INDEX 0 67 #define UGENSA_IFACE_MAX 8 /* exclusivly */ 68 69 enum { 70 UGENSA_BULK_DT_WR, 71 UGENSA_BULK_DT_RD, 72 UGENSA_N_TRANSFER, 73 }; 74 75 struct ugensa_sub_softc { 76 struct usb2_com_softc *sc_usb2_com_ptr; 77 struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER]; 78 }; 79 80 struct 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 91 static device_probe_t ugensa_probe; 92 static device_attach_t ugensa_attach; 93 static device_detach_t ugensa_detach; 94 95 static usb2_callback_t ugensa_bulk_write_callback; 96 static usb2_callback_t ugensa_bulk_read_callback; 97 98 static void ugensa_start_read(struct usb2_com_softc *); 99 static void ugensa_stop_read(struct usb2_com_softc *); 100 static void ugensa_start_write(struct usb2_com_softc *); 101 static void ugensa_stop_write(struct usb2_com_softc *); 102 103 static const struct usb2_config 104 ugensa_xfer_config[UGENSA_N_TRANSFER] = { 105 106 [UGENSA_BULK_DT_WR] = { 107 .type = UE_BULK, 108 .endpoint = UE_ADDR_ANY, 109 .direction = UE_DIR_OUT, 110 .mh.bufsize = UGENSA_BUF_SIZE, 111 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 112 .mh.callback = &ugensa_bulk_write_callback, 113 }, 114 115 [UGENSA_BULK_DT_RD] = { 116 .type = UE_BULK, 117 .endpoint = UE_ADDR_ANY, 118 .direction = UE_DIR_IN, 119 .mh.bufsize = UGENSA_BUF_SIZE, 120 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 121 .mh.callback = &ugensa_bulk_read_callback, 122 }, 123 }; 124 125 static const struct usb2_com_callback ugensa_callback = { 126 .usb2_com_start_read = &ugensa_start_read, 127 .usb2_com_stop_read = &ugensa_stop_read, 128 .usb2_com_start_write = &ugensa_start_write, 129 .usb2_com_stop_write = &ugensa_stop_write, 130 }; 131 132 static device_method_t ugensa_methods[] = { 133 /* Device methods */ 134 DEVMETHOD(device_probe, ugensa_probe), 135 DEVMETHOD(device_attach, ugensa_attach), 136 DEVMETHOD(device_detach, ugensa_detach), 137 {0, 0} 138 }; 139 140 static devclass_t ugensa_devclass; 141 142 static driver_t ugensa_driver = { 143 .name = "ugensa", 144 .methods = ugensa_methods, 145 .size = sizeof(struct ugensa_softc), 146 }; 147 148 DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0); 149 MODULE_DEPEND(ugensa, ucom, 1, 1, 1); 150 MODULE_DEPEND(ugensa, usb, 1, 1, 1); 151 152 static const struct usb2_device_id ugensa_devs[] = { 153 {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)}, 154 {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)}, 155 {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)}, 156 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)}, 157 {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)}, 158 }; 159 160 static int 161 ugensa_probe(device_t dev) 162 { 163 struct usb2_attach_arg *uaa = device_get_ivars(dev); 164 165 if (uaa->usb2_mode != USB_MODE_HOST) { 166 return (ENXIO); 167 } 168 if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { 169 return (ENXIO); 170 } 171 if (uaa->info.bIfaceIndex != 0) { 172 return (ENXIO); 173 } 174 return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); 175 } 176 177 static int 178 ugensa_attach(device_t dev) 179 { 180 struct usb2_attach_arg *uaa = device_get_ivars(dev); 181 struct ugensa_softc *sc = device_get_softc(dev); 182 struct ugensa_sub_softc *ssc; 183 struct usb2_interface *iface; 184 int32_t error; 185 uint8_t iface_index; 186 int x, cnt; 187 188 device_set_usb2_desc(dev); 189 mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF); 190 191 /* Figure out how many interfaces this device has got */ 192 for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) { 193 if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) || 194 (usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) { 195 /* we have reached the end */ 196 break; 197 } 198 } 199 200 if (cnt == 0) { 201 device_printf(dev, "No interfaces!\n"); 202 goto detach; 203 } 204 for (x = 0; x < cnt; x++) { 205 iface = usb2_get_iface(uaa->device, x); 206 if (iface->idesc->bInterfaceClass != UICLASS_VENDOR) 207 /* Not a serial port, most likely a SD reader */ 208 continue; 209 210 ssc = sc->sc_sub + sc->sc_niface; 211 ssc->sc_usb2_com_ptr = sc->sc_ucom + sc->sc_niface; 212 213 iface_index = (UGENSA_IFACE_INDEX + x); 214 error = usb2_transfer_setup(uaa->device, 215 &iface_index, ssc->sc_xfer, ugensa_xfer_config, 216 UGENSA_N_TRANSFER, ssc, &sc->sc_mtx); 217 218 if (error) { 219 device_printf(dev, "allocating USB " 220 "transfers failed!\n"); 221 goto detach; 222 } 223 /* clear stall at first run */ 224 usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 225 usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 226 227 /* initialize port number */ 228 ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface; 229 sc->sc_niface++; 230 if (x != uaa->info.bIfaceIndex) 231 usb2_set_parent_iface(uaa->device, x, 232 uaa->info.bIfaceIndex); 233 } 234 device_printf(dev, "Found %d interfaces.\n", sc->sc_niface); 235 236 error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc, 237 &ugensa_callback, &sc->sc_mtx); 238 if (error) { 239 DPRINTF("attach failed\n"); 240 goto detach; 241 } 242 return (0); /* success */ 243 244 detach: 245 ugensa_detach(dev); 246 return (ENXIO); /* failure */ 247 } 248 249 static int 250 ugensa_detach(device_t dev) 251 { 252 struct ugensa_softc *sc = device_get_softc(dev); 253 uint8_t x; 254 255 usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface); 256 257 for (x = 0; x < sc->sc_niface; x++) { 258 usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); 259 } 260 mtx_destroy(&sc->sc_mtx); 261 262 return (0); 263 } 264 265 static void 266 ugensa_bulk_write_callback(struct usb2_xfer *xfer) 267 { 268 struct ugensa_sub_softc *ssc = xfer->priv_sc; 269 uint32_t actlen; 270 271 switch (USB_GET_STATE(xfer)) { 272 case USB_ST_SETUP: 273 case USB_ST_TRANSFERRED: 274 tr_setup: 275 if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0, 276 UGENSA_BUF_SIZE, &actlen)) { 277 xfer->frlengths[0] = actlen; 278 usb2_start_hardware(xfer); 279 } 280 return; 281 282 default: /* Error */ 283 if (xfer->error != USB_ERR_CANCELLED) { 284 /* try to clear stall first */ 285 xfer->flags.stall_pipe = 1; 286 goto tr_setup; 287 } 288 return; 289 } 290 } 291 292 static void 293 ugensa_bulk_read_callback(struct usb2_xfer *xfer) 294 { 295 struct ugensa_sub_softc *ssc = xfer->priv_sc; 296 297 switch (USB_GET_STATE(xfer)) { 298 case USB_ST_TRANSFERRED: 299 usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0, 300 xfer->actlen); 301 302 case USB_ST_SETUP: 303 tr_setup: 304 xfer->frlengths[0] = xfer->max_data_length; 305 usb2_start_hardware(xfer); 306 return; 307 308 default: /* Error */ 309 if (xfer->error != USB_ERR_CANCELLED) { 310 /* try to clear stall first */ 311 xfer->flags.stall_pipe = 1; 312 goto tr_setup; 313 } 314 return; 315 } 316 } 317 318 static void 319 ugensa_start_read(struct usb2_com_softc *ucom) 320 { 321 struct ugensa_softc *sc = ucom->sc_parent; 322 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 323 324 usb2_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 325 } 326 327 static void 328 ugensa_stop_read(struct usb2_com_softc *ucom) 329 { 330 struct ugensa_softc *sc = ucom->sc_parent; 331 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 332 333 usb2_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 334 } 335 336 static void 337 ugensa_start_write(struct usb2_com_softc *ucom) 338 { 339 struct ugensa_softc *sc = ucom->sc_parent; 340 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 341 342 usb2_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 343 } 344 345 static void 346 ugensa_stop_write(struct usb2_com_softc *ucom) 347 { 348 struct ugensa_softc *sc = ucom->sc_parent; 349 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 350 351 usb2_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 352 } 353