ugensa.c revision 194677
1184610Salfred/* $FreeBSD: head/sys/dev/usb/serial/ugensa.c 194677 2009-06-23 02:19:59Z thompsa $ */ 2184610Salfred/* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */ 3184610Salfred 4184610Salfred/* 5184610Salfred * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc. 6184610Salfred * All rights reserved. 7184610Salfred * 8184610Salfred * This code is derived from software contributed to The NetBSD Foundation 9184610Salfred * by Roland C. Dowdeswell <elric@netbsd.org>. 10184610Salfred * 11184610Salfred * Redistribution and use in source and binary forms, with or without 12184610Salfred * modification, are permitted provided that the following conditions 13184610Salfred * are met: 14184610Salfred * 1. Redistributions of source code must retain the above copyright 15184610Salfred * notice, this list of conditions and the following disclaimer. 16184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 17184610Salfred * notice, this list of conditions and the following disclaimer in the 18184610Salfred * documentation and/or other materials provided with the distribution. 19184610Salfred * 3. All advertising materials mentioning features or use of this software 20184610Salfred * must display the following acknowledgement: 21184610Salfred * This product includes software developed by the NetBSD 22184610Salfred * Foundation, Inc. and its contributors. 23184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its 24184610Salfred * contributors may be used to endorse or promote products derived 25184610Salfred * from this software without specific prior written permission. 26184610Salfred * 27184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37184610Salfred * POSSIBILITY OF SUCH DAMAGE. 38184610Salfred */ 39184610Salfred 40184610Salfred/* 41184610Salfred * NOTE: all function names beginning like "ugensa_cfg_" can only 42184610Salfred * be called from within the config thread function ! 43184610Salfred */ 44184610Salfred 45194677Sthompsa#include <sys/stdint.h> 46194677Sthompsa#include <sys/stddef.h> 47194677Sthompsa#include <sys/param.h> 48194677Sthompsa#include <sys/queue.h> 49194677Sthompsa#include <sys/types.h> 50194677Sthompsa#include <sys/systm.h> 51194677Sthompsa#include <sys/kernel.h> 52194677Sthompsa#include <sys/bus.h> 53194677Sthompsa#include <sys/linker_set.h> 54194677Sthompsa#include <sys/module.h> 55194677Sthompsa#include <sys/lock.h> 56194677Sthompsa#include <sys/mutex.h> 57194677Sthompsa#include <sys/condvar.h> 58194677Sthompsa#include <sys/sysctl.h> 59194677Sthompsa#include <sys/sx.h> 60194677Sthompsa#include <sys/unistd.h> 61194677Sthompsa#include <sys/callout.h> 62194677Sthompsa#include <sys/malloc.h> 63194677Sthompsa#include <sys/priv.h> 64194677Sthompsa 65194677Sthompsa#include <dev/usb/usb.h> 66194677Sthompsa#include <dev/usb/usbdi.h> 67188746Sthompsa#include "usbdevs.h" 68184610Salfred 69194228Sthompsa#define USB_DEBUG_VAR usb_debug 70188942Sthompsa#include <dev/usb/usb_debug.h> 71188942Sthompsa#include <dev/usb/usb_process.h> 72184610Salfred 73188942Sthompsa#include <dev/usb/serial/usb_serial.h> 74184610Salfred 75184610Salfred#define UGENSA_BUF_SIZE 2048 /* bytes */ 76184610Salfred#define UGENSA_CONFIG_INDEX 0 77184610Salfred#define UGENSA_IFACE_INDEX 0 78184610Salfred#define UGENSA_IFACE_MAX 8 /* exclusivly */ 79184610Salfred 80187259Sthompsaenum { 81187259Sthompsa UGENSA_BULK_DT_WR, 82187259Sthompsa UGENSA_BULK_DT_RD, 83188413Sthompsa UGENSA_N_TRANSFER, 84187259Sthompsa}; 85187259Sthompsa 86184610Salfredstruct ugensa_sub_softc { 87194228Sthompsa struct ucom_softc *sc_ucom_ptr; 88192984Sthompsa struct usb_xfer *sc_xfer[UGENSA_N_TRANSFER]; 89184610Salfred}; 90184610Salfred 91184610Salfredstruct ugensa_softc { 92192984Sthompsa struct ucom_super_softc sc_super_ucom; 93192984Sthompsa struct ucom_softc sc_ucom[UGENSA_IFACE_MAX]; 94184610Salfred struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX]; 95184610Salfred 96184610Salfred struct mtx sc_mtx; 97184610Salfred uint8_t sc_niface; 98184610Salfred}; 99184610Salfred 100184610Salfred/* prototypes */ 101184610Salfred 102184610Salfredstatic device_probe_t ugensa_probe; 103184610Salfredstatic device_attach_t ugensa_attach; 104184610Salfredstatic device_detach_t ugensa_detach; 105184610Salfred 106193045Sthompsastatic usb_callback_t ugensa_bulk_write_callback; 107193045Sthompsastatic usb_callback_t ugensa_bulk_read_callback; 108184610Salfred 109192984Sthompsastatic void ugensa_start_read(struct ucom_softc *); 110192984Sthompsastatic void ugensa_stop_read(struct ucom_softc *); 111192984Sthompsastatic void ugensa_start_write(struct ucom_softc *); 112192984Sthompsastatic void ugensa_stop_write(struct ucom_softc *); 113184610Salfred 114192984Sthompsastatic const struct usb_config 115184610Salfred ugensa_xfer_config[UGENSA_N_TRANSFER] = { 116184610Salfred 117187259Sthompsa [UGENSA_BULK_DT_WR] = { 118184610Salfred .type = UE_BULK, 119184610Salfred .endpoint = UE_ADDR_ANY, 120184610Salfred .direction = UE_DIR_OUT, 121190734Sthompsa .bufsize = UGENSA_BUF_SIZE, 122190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 123190734Sthompsa .callback = &ugensa_bulk_write_callback, 124184610Salfred }, 125184610Salfred 126187259Sthompsa [UGENSA_BULK_DT_RD] = { 127184610Salfred .type = UE_BULK, 128184610Salfred .endpoint = UE_ADDR_ANY, 129184610Salfred .direction = UE_DIR_IN, 130190734Sthompsa .bufsize = UGENSA_BUF_SIZE, 131190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 132190734Sthompsa .callback = &ugensa_bulk_read_callback, 133184610Salfred }, 134184610Salfred}; 135184610Salfred 136192984Sthompsastatic const struct ucom_callback ugensa_callback = { 137194228Sthompsa .ucom_start_read = &ugensa_start_read, 138194228Sthompsa .ucom_stop_read = &ugensa_stop_read, 139194228Sthompsa .ucom_start_write = &ugensa_start_write, 140194228Sthompsa .ucom_stop_write = &ugensa_stop_write, 141184610Salfred}; 142184610Salfred 143184610Salfredstatic device_method_t ugensa_methods[] = { 144184610Salfred /* Device methods */ 145184610Salfred DEVMETHOD(device_probe, ugensa_probe), 146184610Salfred DEVMETHOD(device_attach, ugensa_attach), 147184610Salfred DEVMETHOD(device_detach, ugensa_detach), 148184610Salfred {0, 0} 149184610Salfred}; 150184610Salfred 151184610Salfredstatic devclass_t ugensa_devclass; 152184610Salfred 153184610Salfredstatic driver_t ugensa_driver = { 154184610Salfred .name = "ugensa", 155184610Salfred .methods = ugensa_methods, 156184610Salfred .size = sizeof(struct ugensa_softc), 157184610Salfred}; 158184610Salfred 159189275SthompsaDRIVER_MODULE(ugensa, uhub, ugensa_driver, ugensa_devclass, NULL, 0); 160188942SthompsaMODULE_DEPEND(ugensa, ucom, 1, 1, 1); 161188942SthompsaMODULE_DEPEND(ugensa, usb, 1, 1, 1); 162184610Salfred 163192984Sthompsastatic const struct usb_device_id ugensa_devs[] = { 164184610Salfred {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)}, 165184610Salfred {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)}, 166184610Salfred {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)}, 167184610Salfred {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)}, 168184610Salfred {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)}, 169184610Salfred}; 170184610Salfred 171184610Salfredstatic int 172184610Salfredugensa_probe(device_t dev) 173184610Salfred{ 174192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 175184610Salfred 176192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 177184610Salfred return (ENXIO); 178184610Salfred } 179184610Salfred if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { 180184610Salfred return (ENXIO); 181184610Salfred } 182184610Salfred if (uaa->info.bIfaceIndex != 0) { 183184610Salfred return (ENXIO); 184184610Salfred } 185194228Sthompsa return (usbd_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); 186184610Salfred} 187184610Salfred 188184610Salfredstatic int 189184610Salfredugensa_attach(device_t dev) 190184610Salfred{ 191192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 192184610Salfred struct ugensa_softc *sc = device_get_softc(dev); 193184610Salfred struct ugensa_sub_softc *ssc; 194192984Sthompsa struct usb_interface *iface; 195184610Salfred int32_t error; 196184610Salfred uint8_t iface_index; 197184610Salfred int x, cnt; 198184610Salfred 199194228Sthompsa device_set_usb_desc(dev); 200184610Salfred mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF); 201184610Salfred 202184610Salfred /* Figure out how many interfaces this device has got */ 203184610Salfred for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) { 204194228Sthompsa if ((usbd_get_endpoint(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) || 205194228Sthompsa (usbd_get_endpoint(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) { 206184610Salfred /* we have reached the end */ 207184610Salfred break; 208184610Salfred } 209184610Salfred } 210184610Salfred 211184610Salfred if (cnt == 0) { 212184610Salfred device_printf(dev, "No interfaces!\n"); 213184610Salfred goto detach; 214184610Salfred } 215184610Salfred for (x = 0; x < cnt; x++) { 216194228Sthompsa iface = usbd_get_iface(uaa->device, x); 217184610Salfred if (iface->idesc->bInterfaceClass != UICLASS_VENDOR) 218184610Salfred /* Not a serial port, most likely a SD reader */ 219184610Salfred continue; 220184610Salfred 221184610Salfred ssc = sc->sc_sub + sc->sc_niface; 222194228Sthompsa ssc->sc_ucom_ptr = sc->sc_ucom + sc->sc_niface; 223184610Salfred 224184610Salfred iface_index = (UGENSA_IFACE_INDEX + x); 225194228Sthompsa error = usbd_transfer_setup(uaa->device, 226184610Salfred &iface_index, ssc->sc_xfer, ugensa_xfer_config, 227184610Salfred UGENSA_N_TRANSFER, ssc, &sc->sc_mtx); 228184610Salfred 229184610Salfred if (error) { 230184610Salfred device_printf(dev, "allocating USB " 231184610Salfred "transfers failed!\n"); 232184610Salfred goto detach; 233184610Salfred } 234184610Salfred /* clear stall at first run */ 235189265Sthompsa mtx_lock(&sc->sc_mtx); 236194677Sthompsa usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 237194677Sthompsa usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 238189265Sthompsa mtx_unlock(&sc->sc_mtx); 239184610Salfred 240184610Salfred /* initialize port number */ 241194228Sthompsa ssc->sc_ucom_ptr->sc_portno = sc->sc_niface; 242184610Salfred sc->sc_niface++; 243184610Salfred if (x != uaa->info.bIfaceIndex) 244194228Sthompsa usbd_set_parent_iface(uaa->device, x, 245184610Salfred uaa->info.bIfaceIndex); 246184610Salfred } 247184610Salfred device_printf(dev, "Found %d interfaces.\n", sc->sc_niface); 248184610Salfred 249194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc, 250184610Salfred &ugensa_callback, &sc->sc_mtx); 251184610Salfred if (error) { 252184610Salfred DPRINTF("attach failed\n"); 253184610Salfred goto detach; 254184610Salfred } 255184610Salfred return (0); /* success */ 256184610Salfred 257184610Salfreddetach: 258184610Salfred ugensa_detach(dev); 259184610Salfred return (ENXIO); /* failure */ 260184610Salfred} 261184610Salfred 262184610Salfredstatic int 263184610Salfredugensa_detach(device_t dev) 264184610Salfred{ 265184610Salfred struct ugensa_softc *sc = device_get_softc(dev); 266184610Salfred uint8_t x; 267184610Salfred 268194228Sthompsa ucom_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface); 269184610Salfred 270184610Salfred for (x = 0; x < sc->sc_niface; x++) { 271194228Sthompsa usbd_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); 272184610Salfred } 273184610Salfred mtx_destroy(&sc->sc_mtx); 274184610Salfred 275184610Salfred return (0); 276184610Salfred} 277184610Salfred 278184610Salfredstatic void 279194677Sthompsaugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 280184610Salfred{ 281194677Sthompsa struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); 282194677Sthompsa struct usb_page_cache *pc; 283184610Salfred uint32_t actlen; 284184610Salfred 285184610Salfred switch (USB_GET_STATE(xfer)) { 286184610Salfred case USB_ST_SETUP: 287184610Salfred case USB_ST_TRANSFERRED: 288188413Sthompsatr_setup: 289194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 290194677Sthompsa if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0, 291184610Salfred UGENSA_BUF_SIZE, &actlen)) { 292194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, actlen); 293194228Sthompsa usbd_transfer_submit(xfer); 294184610Salfred } 295184610Salfred return; 296184610Salfred 297184610Salfred default: /* Error */ 298194677Sthompsa if (error != USB_ERR_CANCELLED) { 299188413Sthompsa /* try to clear stall first */ 300194677Sthompsa usbd_xfer_set_stall(xfer); 301188413Sthompsa goto tr_setup; 302184610Salfred } 303184610Salfred return; 304184610Salfred } 305184610Salfred} 306184610Salfred 307184610Salfredstatic void 308194677Sthompsaugensa_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 309184610Salfred{ 310194677Sthompsa struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); 311194677Sthompsa struct usb_page_cache *pc; 312194677Sthompsa int actlen; 313184610Salfred 314194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 315194677Sthompsa 316184610Salfred switch (USB_GET_STATE(xfer)) { 317184610Salfred case USB_ST_TRANSFERRED: 318194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 319194677Sthompsa ucom_put_data(ssc->sc_ucom_ptr, pc, 0, actlen); 320184610Salfred 321184610Salfred case USB_ST_SETUP: 322188413Sthompsatr_setup: 323194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 324194228Sthompsa usbd_transfer_submit(xfer); 325184610Salfred return; 326184610Salfred 327184610Salfred default: /* Error */ 328194677Sthompsa if (error != USB_ERR_CANCELLED) { 329188413Sthompsa /* try to clear stall first */ 330194677Sthompsa usbd_xfer_set_stall(xfer); 331188413Sthompsa goto tr_setup; 332184610Salfred } 333184610Salfred return; 334184610Salfred } 335184610Salfred} 336184610Salfred 337184610Salfredstatic void 338192984Sthompsaugensa_start_read(struct ucom_softc *ucom) 339184610Salfred{ 340184610Salfred struct ugensa_softc *sc = ucom->sc_parent; 341184610Salfred struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 342184610Salfred 343194228Sthompsa usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 344184610Salfred} 345184610Salfred 346184610Salfredstatic void 347192984Sthompsaugensa_stop_read(struct ucom_softc *ucom) 348184610Salfred{ 349184610Salfred struct ugensa_softc *sc = ucom->sc_parent; 350184610Salfred struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 351184610Salfred 352194228Sthompsa usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 353184610Salfred} 354184610Salfred 355184610Salfredstatic void 356192984Sthompsaugensa_start_write(struct ucom_softc *ucom) 357184610Salfred{ 358184610Salfred struct ugensa_softc *sc = ucom->sc_parent; 359184610Salfred struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 360184610Salfred 361194228Sthompsa usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 362184610Salfred} 363184610Salfred 364184610Salfredstatic void 365192984Sthompsaugensa_stop_write(struct ucom_softc *ucom) 366184610Salfred{ 367184610Salfred struct ugensa_softc *sc = ucom->sc_parent; 368184610Salfred struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 369184610Salfred 370194228Sthompsa usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 371184610Salfred} 372