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 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * NOTE: all function names beginning like "ugensa_cfg_" can only 35 * be called from within the config thread function ! 36 */ 37 38 #include <sys/stdint.h> 39 #include <sys/param.h> 40 #include <sys/queue.h> 41 #include <sys/types.h> 42 #include <sys/systm.h> 43 #include <sys/kernel.h> 44 #include <sys/bus.h> 45 #include <sys/module.h> 46 #include <sys/lock.h> 47 #include <sys/condvar.h> 48 #include <sys/sysctl.h> 49 #include <sys/unistd.h> 50 #include <sys/callout.h> 51 #include <sys/malloc.h> 52 #include <sys/priv.h> 53 54 #include <bus/u4b/usb.h> 55 #include <bus/u4b/usbdi.h> 56 #include "usbdevs.h" 57 58 #define USB_DEBUG_VAR usb_debug 59 #include <bus/u4b/usb_debug.h> 60 #include <bus/u4b/usb_process.h> 61 62 #include <bus/u4b/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 ucom_softc *sc_ucom_ptr; 77 struct usb_xfer *sc_xfer[UGENSA_N_TRANSFER]; 78 }; 79 80 struct ugensa_softc { 81 struct ucom_super_softc sc_super_ucom; 82 struct ucom_softc sc_ucom[UGENSA_IFACE_MAX]; 83 struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX]; 84 85 struct lock sc_lock; 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 usb_callback_t ugensa_bulk_write_callback; 96 static usb_callback_t ugensa_bulk_read_callback; 97 98 static void ugensa_start_read(struct ucom_softc *); 99 static void ugensa_stop_read(struct ucom_softc *); 100 static void ugensa_start_write(struct ucom_softc *); 101 static void ugensa_stop_write(struct ucom_softc *); 102 static void ugensa_poll(struct ucom_softc *ucom); 103 104 static const struct usb_config 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 .bufsize = UGENSA_BUF_SIZE, 111 .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 112 .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 .bufsize = UGENSA_BUF_SIZE, 120 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 121 .callback = &ugensa_bulk_read_callback, 122 }, 123 }; 124 125 static const struct ucom_callback ugensa_callback = { 126 .ucom_start_read = &ugensa_start_read, 127 .ucom_stop_read = &ugensa_stop_read, 128 .ucom_start_write = &ugensa_start_write, 129 .ucom_stop_write = &ugensa_stop_write, 130 .ucom_poll = &ugensa_poll, 131 }; 132 133 static device_method_t ugensa_methods[] = { 134 /* Device methods */ 135 DEVMETHOD(device_probe, ugensa_probe), 136 DEVMETHOD(device_attach, ugensa_attach), 137 DEVMETHOD(device_detach, ugensa_detach), 138 DEVMETHOD_END 139 }; 140 141 static devclass_t ugensa_devclass; 142 143 static driver_t ugensa_driver = { 144 .name = "ugensa", 145 .methods = ugensa_methods, 146 .size = sizeof(struct ugensa_softc), 147 }; 148 149 DRIVER_MODULE(ugensa, uhub, ugensa_driver, ugensa_devclass, NULL, NULL); 150 MODULE_DEPEND(ugensa, ucom, 1, 1, 1); 151 MODULE_DEPEND(ugensa, usb, 1, 1, 1); 152 MODULE_VERSION(ugensa, 1); 153 154 static const STRUCT_USB_HOST_ID ugensa_devs[] = { 155 {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)}, 156 {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)}, 157 {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)}, 158 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)}, 159 {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)}, 160 }; 161 162 static int 163 ugensa_probe(device_t dev) 164 { 165 struct usb_attach_arg *uaa = device_get_ivars(dev); 166 167 if (uaa->usb_mode != USB_MODE_HOST) { 168 return (ENXIO); 169 } 170 if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { 171 return (ENXIO); 172 } 173 if (uaa->info.bIfaceIndex != 0) { 174 return (ENXIO); 175 } 176 return (usbd_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); 177 } 178 179 static int 180 ugensa_attach(device_t dev) 181 { 182 struct usb_attach_arg *uaa = device_get_ivars(dev); 183 struct ugensa_softc *sc = device_get_softc(dev); 184 struct ugensa_sub_softc *ssc; 185 struct usb_interface *iface; 186 int32_t error; 187 uint8_t iface_index; 188 int x, cnt; 189 190 device_set_usb_desc(dev); 191 lockinit(&sc->sc_lock, "ugensa", 0, LK_CANRECURSE); 192 193 /* Figure out how many interfaces this device has got */ 194 for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) { 195 if ((usbd_get_endpoint(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) || 196 (usbd_get_endpoint(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) { 197 /* we have reached the end */ 198 break; 199 } 200 } 201 202 if (cnt == 0) { 203 device_printf(dev, "No interfaces\n"); 204 goto detach; 205 } 206 for (x = 0; x < cnt; x++) { 207 iface = usbd_get_iface(uaa->device, x); 208 if (iface->idesc->bInterfaceClass != UICLASS_VENDOR) 209 /* Not a serial port, most likely a SD reader */ 210 continue; 211 212 ssc = sc->sc_sub + sc->sc_niface; 213 ssc->sc_ucom_ptr = sc->sc_ucom + sc->sc_niface; 214 215 iface_index = (UGENSA_IFACE_INDEX + x); 216 error = usbd_transfer_setup(uaa->device, 217 &iface_index, ssc->sc_xfer, ugensa_xfer_config, 218 UGENSA_N_TRANSFER, ssc, &sc->sc_lock); 219 220 if (error) { 221 device_printf(dev, "allocating USB " 222 "transfers failed\n"); 223 goto detach; 224 } 225 /* clear stall at first run */ 226 lockmgr(&sc->sc_lock, LK_EXCLUSIVE); 227 usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 228 usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 229 lockmgr(&sc->sc_lock, LK_RELEASE); 230 231 /* initialize port number */ 232 ssc->sc_ucom_ptr->sc_portno = sc->sc_niface; 233 sc->sc_niface++; 234 if (x != uaa->info.bIfaceIndex) 235 usbd_set_parent_iface(uaa->device, x, 236 uaa->info.bIfaceIndex); 237 } 238 device_printf(dev, "Found %d interfaces.\n", sc->sc_niface); 239 240 error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc, 241 &ugensa_callback, &sc->sc_lock); 242 if (error) { 243 DPRINTF("attach failed\n"); 244 goto detach; 245 } 246 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 247 248 return (0); /* success */ 249 250 detach: 251 ugensa_detach(dev); 252 return (ENXIO); /* failure */ 253 } 254 255 static int 256 ugensa_detach(device_t dev) 257 { 258 struct ugensa_softc *sc = device_get_softc(dev); 259 uint8_t x; 260 261 ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); 262 263 for (x = 0; x < sc->sc_niface; x++) { 264 usbd_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); 265 } 266 lockuninit(&sc->sc_lock); 267 268 return (0); 269 } 270 271 static void 272 ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 273 { 274 struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); 275 struct usb_page_cache *pc; 276 uint32_t actlen; 277 278 switch (USB_GET_STATE(xfer)) { 279 case USB_ST_SETUP: 280 case USB_ST_TRANSFERRED: 281 tr_setup: 282 pc = usbd_xfer_get_frame(xfer, 0); 283 if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0, 284 UGENSA_BUF_SIZE, &actlen)) { 285 usbd_xfer_set_frame_len(xfer, 0, actlen); 286 usbd_transfer_submit(xfer); 287 } 288 return; 289 290 default: /* Error */ 291 if (error != USB_ERR_CANCELLED) { 292 /* try to clear stall first */ 293 usbd_xfer_set_stall(xfer); 294 goto tr_setup; 295 } 296 return; 297 } 298 } 299 300 static void 301 ugensa_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 302 { 303 struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); 304 struct usb_page_cache *pc; 305 int actlen; 306 307 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 308 309 switch (USB_GET_STATE(xfer)) { 310 case USB_ST_TRANSFERRED: 311 pc = usbd_xfer_get_frame(xfer, 0); 312 ucom_put_data(ssc->sc_ucom_ptr, pc, 0, actlen); 313 314 case USB_ST_SETUP: 315 tr_setup: 316 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 317 usbd_transfer_submit(xfer); 318 return; 319 320 default: /* Error */ 321 if (error != USB_ERR_CANCELLED) { 322 /* try to clear stall first */ 323 usbd_xfer_set_stall(xfer); 324 goto tr_setup; 325 } 326 return; 327 } 328 } 329 330 static void 331 ugensa_start_read(struct ucom_softc *ucom) 332 { 333 struct ugensa_softc *sc = ucom->sc_parent; 334 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 335 336 usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 337 } 338 339 static void 340 ugensa_stop_read(struct ucom_softc *ucom) 341 { 342 struct ugensa_softc *sc = ucom->sc_parent; 343 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 344 345 usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 346 } 347 348 static void 349 ugensa_start_write(struct ucom_softc *ucom) 350 { 351 struct ugensa_softc *sc = ucom->sc_parent; 352 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 353 354 usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 355 } 356 357 static void 358 ugensa_stop_write(struct ucom_softc *ucom) 359 { 360 struct ugensa_softc *sc = ucom->sc_parent; 361 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 362 363 usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 364 } 365 366 static void 367 ugensa_poll(struct ucom_softc *ucom) 368 { 369 struct ugensa_softc *sc = ucom->sc_parent; 370 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 371 372 usbd_transfer_poll(ssc->sc_xfer, UGENSA_N_TRANSFER); 373 } 374