1184610Salfred/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ 2184610Salfred 3184610Salfred#include <sys/cdefs.h> 4184610Salfred__FBSDID("$FreeBSD$"); 5186885Stakawata#define UFOMA_HANDSFREE 6184610Salfred/*- 7184610Salfred * Copyright (c) 2005, Takanori Watanabe 8189002Sed * Copyright (c) 2003, M. Warner Losh <imp@FreeBSD.org>. 9184610Salfred * All rights reserved. 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 * 20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30184610Salfred * SUCH DAMAGE. 31184610Salfred */ 32184610Salfred 33184610Salfred/*- 34184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc. 35184610Salfred * All rights reserved. 36184610Salfred * 37184610Salfred * This code is derived from software contributed to The NetBSD Foundation 38184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at 39184610Salfred * Carlstedt Research & Technology. 40184610Salfred * 41184610Salfred * Redistribution and use in source and binary forms, with or without 42184610Salfred * modification, are permitted provided that the following conditions 43184610Salfred * are met: 44184610Salfred * 1. Redistributions of source code must retain the above copyright 45184610Salfred * notice, this list of conditions and the following disclaimer. 46184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 47184610Salfred * notice, this list of conditions and the following disclaimer in the 48184610Salfred * documentation and/or other materials provided with the distribution. 49184610Salfred * 50184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 51184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 52184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 54184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 57184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 58184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 60184610Salfred * POSSIBILITY OF SUCH DAMAGE. 61184610Salfred */ 62184610Salfred 63184610Salfred/* 64184610Salfred * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf 65184610Salfred * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf 66184610Salfred */ 67184610Salfred 68184610Salfred/* 69184610Salfred * TODO: 70184610Salfred * - Implement a Call Device for modems without multiplexed commands. 71184610Salfred */ 72184610Salfred 73184610Salfred/* 74184610Salfred * NOTE: all function names beginning like "ufoma_cfg_" can only 75184610Salfred * be called from within the config thread function ! 76184610Salfred */ 77184610Salfred 78194677Sthompsa#include <sys/stdint.h> 79194677Sthompsa#include <sys/stddef.h> 80194677Sthompsa#include <sys/param.h> 81194677Sthompsa#include <sys/queue.h> 82194677Sthompsa#include <sys/types.h> 83194677Sthompsa#include <sys/systm.h> 84194677Sthompsa#include <sys/kernel.h> 85194677Sthompsa#include <sys/bus.h> 86194677Sthompsa#include <sys/module.h> 87194677Sthompsa#include <sys/lock.h> 88194677Sthompsa#include <sys/mutex.h> 89194677Sthompsa#include <sys/condvar.h> 90194677Sthompsa#include <sys/sysctl.h> 91194677Sthompsa#include <sys/sx.h> 92194677Sthompsa#include <sys/unistd.h> 93194677Sthompsa#include <sys/callout.h> 94194677Sthompsa#include <sys/malloc.h> 95194677Sthompsa#include <sys/priv.h> 96194677Sthompsa#include <sys/sbuf.h> 97194677Sthompsa 98188942Sthompsa#include <dev/usb/usb.h> 99194677Sthompsa#include <dev/usb/usbdi.h> 100194677Sthompsa#include <dev/usb/usbdi_util.h> 101188942Sthompsa#include <dev/usb/usb_cdc.h> 102194677Sthompsa#include "usbdevs.h" 103184610Salfred 104194228Sthompsa#define USB_DEBUG_VAR usb_debug 105188942Sthompsa#include <dev/usb/usb_debug.h> 106188942Sthompsa#include <dev/usb/usb_process.h> 107184610Salfred 108188942Sthompsa#include <dev/usb/serial/usb_serial.h> 109184610Salfred 110184610Salfredtypedef struct ufoma_mobile_acm_descriptor { 111184610Salfred uint8_t bFunctionLength; 112184610Salfred uint8_t bDescriptorType; 113184610Salfred uint8_t bDescriptorSubtype; 114184610Salfred uint8_t bType; 115184610Salfred uint8_t bMode[1]; 116194228Sthompsa} __packed usb_mcpc_acm_descriptor; 117184610Salfred 118184610Salfred#define UISUBCLASS_MCPC 0x88 119184610Salfred 120184610Salfred#define UDESC_VS_INTERFACE 0x44 121184610Salfred#define UDESCSUB_MCPC_ACM 0x11 122184610Salfred 123184610Salfred#define UMCPC_ACM_TYPE_AB1 0x1 124184610Salfred#define UMCPC_ACM_TYPE_AB2 0x2 125184610Salfred#define UMCPC_ACM_TYPE_AB5 0x5 126184610Salfred#define UMCPC_ACM_TYPE_AB6 0x6 127184610Salfred 128184610Salfred#define UMCPC_ACM_MODE_DEACTIVATED 0x0 129184610Salfred#define UMCPC_ACM_MODE_MODEM 0x1 130184610Salfred#define UMCPC_ACM_MODE_ATCOMMAND 0x2 131184610Salfred#define UMCPC_ACM_MODE_OBEX 0x60 132184610Salfred#define UMCPC_ACM_MODE_VENDOR1 0xc0 133184610Salfred#define UMCPC_ACM_MODE_VENDOR2 0xfe 134184610Salfred#define UMCPC_ACM_MODE_UNLINKED 0xff 135184610Salfred 136184610Salfred#define UMCPC_CM_MOBILE_ACM 0x0 137184610Salfred 138184610Salfred#define UMCPC_ACTIVATE_MODE 0x60 139184610Salfred#define UMCPC_GET_MODETABLE 0x61 140184610Salfred#define UMCPC_SET_LINK 0x62 141184610Salfred#define UMCPC_CLEAR_LINK 0x63 142184610Salfred 143184610Salfred#define UMCPC_REQUEST_ACKNOWLEDGE 0x31 144184610Salfred 145184610Salfred#define UFOMA_MAX_TIMEOUT 15 /* standard says 10 seconds */ 146184610Salfred#define UFOMA_CMD_BUF_SIZE 64 /* bytes */ 147184610Salfred 148184610Salfred#define UFOMA_BULK_BUF_SIZE 1024 /* bytes */ 149184610Salfred 150187299Stakawataenum { 151187299Stakawata UFOMA_CTRL_ENDPT_INTR, 152187299Stakawata UFOMA_CTRL_ENDPT_READ, 153187299Stakawata UFOMA_CTRL_ENDPT_WRITE, 154188413Sthompsa UFOMA_CTRL_ENDPT_MAX, 155187299Stakawata}; 156184610Salfred 157187299Stakawataenum { 158187299Stakawata UFOMA_BULK_ENDPT_WRITE, 159187299Stakawata UFOMA_BULK_ENDPT_READ, 160188413Sthompsa UFOMA_BULK_ENDPT_MAX, 161187299Stakawata}; 162187299Stakawata 163184610Salfredstruct ufoma_softc { 164192984Sthompsa struct ucom_super_softc sc_super_ucom; 165192984Sthompsa struct ucom_softc sc_ucom; 166184610Salfred struct cv sc_cv; 167189265Sthompsa struct mtx sc_mtx; 168184610Salfred 169192984Sthompsa struct usb_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX]; 170192984Sthompsa struct usb_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX]; 171184610Salfred uint8_t *sc_modetable; 172184610Salfred device_t sc_dev; 173192984Sthompsa struct usb_device *sc_udev; 174184610Salfred 175184610Salfred uint32_t sc_unit; 176184610Salfred 177184610Salfred uint16_t sc_line; 178184610Salfred 179184610Salfred uint8_t sc_num_msg; 180187579Stakawata uint8_t sc_nobulk; 181184610Salfred uint8_t sc_ctrl_iface_no; 182184610Salfred uint8_t sc_ctrl_iface_index; 183184610Salfred uint8_t sc_data_iface_no; 184184610Salfred uint8_t sc_data_iface_index; 185184610Salfred uint8_t sc_cm_cap; 186184610Salfred uint8_t sc_acm_cap; 187184610Salfred uint8_t sc_lsr; 188184610Salfred uint8_t sc_msr; 189184610Salfred uint8_t sc_modetoactivate; 190184610Salfred uint8_t sc_currentmode; 191184610Salfred}; 192184610Salfred 193184610Salfred/* prototypes */ 194184610Salfred 195184610Salfredstatic device_probe_t ufoma_probe; 196184610Salfredstatic device_attach_t ufoma_attach; 197184610Salfredstatic device_detach_t ufoma_detach; 198239299Shselaskystatic void ufoma_free_softc(struct ufoma_softc *); 199184610Salfred 200193045Sthompsastatic usb_callback_t ufoma_ctrl_read_callback; 201193045Sthompsastatic usb_callback_t ufoma_ctrl_write_callback; 202193045Sthompsastatic usb_callback_t ufoma_intr_callback; 203193045Sthompsastatic usb_callback_t ufoma_bulk_write_callback; 204193045Sthompsastatic usb_callback_t ufoma_bulk_read_callback; 205184610Salfred 206192984Sthompsastatic void *ufoma_get_intconf(struct usb_config_descriptor *, 207192984Sthompsa struct usb_interface_descriptor *, uint8_t, uint8_t); 208185948Sthompsastatic void ufoma_cfg_link_state(struct ufoma_softc *); 209185948Sthompsastatic void ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t); 210239180Shselaskystatic void ufoma_free(struct ucom_softc *); 211192984Sthompsastatic void ufoma_cfg_open(struct ucom_softc *); 212192984Sthompsastatic void ufoma_cfg_close(struct ucom_softc *); 213192984Sthompsastatic void ufoma_cfg_set_break(struct ucom_softc *, uint8_t); 214192984Sthompsastatic void ufoma_cfg_get_status(struct ucom_softc *, uint8_t *, 215185948Sthompsa uint8_t *); 216192984Sthompsastatic void ufoma_cfg_set_dtr(struct ucom_softc *, uint8_t); 217192984Sthompsastatic void ufoma_cfg_set_rts(struct ucom_softc *, uint8_t); 218192984Sthompsastatic int ufoma_pre_param(struct ucom_softc *, struct termios *); 219192984Sthompsastatic void ufoma_cfg_param(struct ucom_softc *, struct termios *); 220185948Sthompsastatic int ufoma_modem_setup(device_t, struct ufoma_softc *, 221192984Sthompsa struct usb_attach_arg *); 222192984Sthompsastatic void ufoma_start_read(struct ucom_softc *); 223192984Sthompsastatic void ufoma_stop_read(struct ucom_softc *); 224192984Sthompsastatic void ufoma_start_write(struct ucom_softc *); 225192984Sthompsastatic void ufoma_stop_write(struct ucom_softc *); 226197570Sthompsastatic void ufoma_poll(struct ucom_softc *ucom); 227184610Salfred 228186885Stakawata/*sysctl stuff*/ 229186885Stakawatastatic int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS); 230186885Stakawatastatic int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS); 231186885Stakawatastatic int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS); 232186885Stakawata 233192984Sthompsastatic const struct usb_config 234184610Salfred ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { 235184610Salfred 236187299Stakawata [UFOMA_CTRL_ENDPT_INTR] = { 237184610Salfred .type = UE_INTERRUPT, 238184610Salfred .endpoint = UE_ADDR_ANY, 239184610Salfred .direction = UE_DIR_IN, 240190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 241192984Sthompsa .bufsize = sizeof(struct usb_cdc_notification), 242190734Sthompsa .callback = &ufoma_intr_callback, 243184610Salfred }, 244184610Salfred 245187299Stakawata [UFOMA_CTRL_ENDPT_READ] = { 246184610Salfred .type = UE_CONTROL, 247184610Salfred .endpoint = 0x00, /* Control pipe */ 248184610Salfred .direction = UE_DIR_ANY, 249192984Sthompsa .bufsize = (sizeof(struct usb_device_request) + UFOMA_CMD_BUF_SIZE), 250190734Sthompsa .flags = {.short_xfer_ok = 1,}, 251190734Sthompsa .callback = &ufoma_ctrl_read_callback, 252190734Sthompsa .timeout = 1000, /* 1 second */ 253184610Salfred }, 254184610Salfred 255187299Stakawata [UFOMA_CTRL_ENDPT_WRITE] = { 256184610Salfred .type = UE_CONTROL, 257184610Salfred .endpoint = 0x00, /* Control pipe */ 258184610Salfred .direction = UE_DIR_ANY, 259192984Sthompsa .bufsize = (sizeof(struct usb_device_request) + 1), 260190734Sthompsa .callback = &ufoma_ctrl_write_callback, 261190734Sthompsa .timeout = 1000, /* 1 second */ 262184610Salfred }, 263184610Salfred}; 264184610Salfred 265192984Sthompsastatic const struct usb_config 266184610Salfred ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = { 267184610Salfred 268187299Stakawata [UFOMA_BULK_ENDPT_WRITE] = { 269184610Salfred .type = UE_BULK, 270184610Salfred .endpoint = UE_ADDR_ANY, 271184610Salfred .direction = UE_DIR_OUT, 272190734Sthompsa .bufsize = UFOMA_BULK_BUF_SIZE, 273190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 274190734Sthompsa .callback = &ufoma_bulk_write_callback, 275184610Salfred }, 276184610Salfred 277187299Stakawata [UFOMA_BULK_ENDPT_READ] = { 278184610Salfred .type = UE_BULK, 279184610Salfred .endpoint = UE_ADDR_ANY, 280184610Salfred .direction = UE_DIR_IN, 281190734Sthompsa .bufsize = UFOMA_BULK_BUF_SIZE, 282190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 283190734Sthompsa .callback = &ufoma_bulk_read_callback, 284184610Salfred }, 285184610Salfred}; 286184610Salfred 287192984Sthompsastatic const struct ucom_callback ufoma_callback = { 288194228Sthompsa .ucom_cfg_get_status = &ufoma_cfg_get_status, 289194228Sthompsa .ucom_cfg_set_dtr = &ufoma_cfg_set_dtr, 290194228Sthompsa .ucom_cfg_set_rts = &ufoma_cfg_set_rts, 291194228Sthompsa .ucom_cfg_set_break = &ufoma_cfg_set_break, 292194228Sthompsa .ucom_cfg_param = &ufoma_cfg_param, 293194228Sthompsa .ucom_cfg_open = &ufoma_cfg_open, 294194228Sthompsa .ucom_cfg_close = &ufoma_cfg_close, 295194228Sthompsa .ucom_pre_param = &ufoma_pre_param, 296194228Sthompsa .ucom_start_read = &ufoma_start_read, 297194228Sthompsa .ucom_stop_read = &ufoma_stop_read, 298194228Sthompsa .ucom_start_write = &ufoma_start_write, 299194228Sthompsa .ucom_stop_write = &ufoma_stop_write, 300197570Sthompsa .ucom_poll = &ufoma_poll, 301239180Shselasky .ucom_free = &ufoma_free, 302184610Salfred}; 303184610Salfred 304184610Salfredstatic device_method_t ufoma_methods[] = { 305184610Salfred /* Device methods */ 306184610Salfred DEVMETHOD(device_probe, ufoma_probe), 307184610Salfred DEVMETHOD(device_attach, ufoma_attach), 308184610Salfred DEVMETHOD(device_detach, ufoma_detach), 309239180Shselasky DEVMETHOD_END 310184610Salfred}; 311184610Salfred 312184610Salfredstatic devclass_t ufoma_devclass; 313184610Salfred 314184610Salfredstatic driver_t ufoma_driver = { 315184610Salfred .name = "ufoma", 316184610Salfred .methods = ufoma_methods, 317184610Salfred .size = sizeof(struct ufoma_softc), 318184610Salfred}; 319184610Salfred 320189275SthompsaDRIVER_MODULE(ufoma, uhub, ufoma_driver, ufoma_devclass, NULL, 0); 321188942SthompsaMODULE_DEPEND(ufoma, ucom, 1, 1, 1); 322188942SthompsaMODULE_DEPEND(ufoma, usb, 1, 1, 1); 323212122SthompsaMODULE_VERSION(ufoma, 1); 324184610Salfred 325223515Shselaskystatic const STRUCT_USB_HOST_ID ufoma_devs[] = { 326223515Shselasky {USB_IFACE_CLASS(UICLASS_CDC), 327223515Shselasky USB_IFACE_SUBCLASS(UISUBCLASS_MCPC),}, 328223515Shselasky}; 329223515Shselasky 330184610Salfredstatic int 331184610Salfredufoma_probe(device_t dev) 332184610Salfred{ 333192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 334192984Sthompsa struct usb_interface_descriptor *id; 335192984Sthompsa struct usb_config_descriptor *cd; 336194228Sthompsa usb_mcpc_acm_descriptor *mad; 337223515Shselasky int error; 338184610Salfred 339223515Shselasky if (uaa->usb_mode != USB_MODE_HOST) 340184610Salfred return (ENXIO); 341223515Shselasky 342223515Shselasky error = usbd_lookup_id_by_uaa(ufoma_devs, sizeof(ufoma_devs), uaa); 343223515Shselasky if (error) 344223515Shselasky return (error); 345223515Shselasky 346194228Sthompsa id = usbd_get_interface_descriptor(uaa->iface); 347194228Sthompsa cd = usbd_get_config_descriptor(uaa->device); 348184610Salfred 349223515Shselasky if (id == NULL || cd == NULL) 350184610Salfred return (ENXIO); 351223515Shselasky 352184610Salfred mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); 353223515Shselasky if (mad == NULL) 354184610Salfred return (ENXIO); 355223515Shselasky 356184610Salfred#ifndef UFOMA_HANDSFREE 357184610Salfred if ((mad->bType == UMCPC_ACM_TYPE_AB5) || 358223515Shselasky (mad->bType == UMCPC_ACM_TYPE_AB6)) 359184610Salfred return (ENXIO); 360184610Salfred#endif 361223515Shselasky return (BUS_PROBE_GENERIC); 362184610Salfred} 363184610Salfred 364184610Salfredstatic int 365184610Salfredufoma_attach(device_t dev) 366184610Salfred{ 367192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 368184610Salfred struct ufoma_softc *sc = device_get_softc(dev); 369192984Sthompsa struct usb_config_descriptor *cd; 370192984Sthompsa struct usb_interface_descriptor *id; 371186885Stakawata struct sysctl_ctx_list *sctx; 372186885Stakawata struct sysctl_oid *soid; 373186885Stakawata 374194228Sthompsa usb_mcpc_acm_descriptor *mad; 375184610Salfred uint8_t elements; 376184610Salfred int32_t error; 377184610Salfred 378184610Salfred sc->sc_udev = uaa->device; 379184610Salfred sc->sc_dev = dev; 380184610Salfred sc->sc_unit = device_get_unit(dev); 381184610Salfred 382189265Sthompsa mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF); 383239180Shselasky ucom_ref(&sc->sc_super_ucom); 384194227Sthompsa cv_init(&sc->sc_cv, "CWAIT"); 385184610Salfred 386194228Sthompsa device_set_usb_desc(dev); 387184610Salfred 388184610Salfred DPRINTF("\n"); 389184610Salfred 390184610Salfred /* setup control transfers */ 391184610Salfred 392194228Sthompsa cd = usbd_get_config_descriptor(uaa->device); 393194228Sthompsa id = usbd_get_interface_descriptor(uaa->iface); 394184610Salfred sc->sc_ctrl_iface_no = id->bInterfaceNumber; 395184610Salfred sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex; 396184610Salfred 397194228Sthompsa error = usbd_transfer_setup(uaa->device, 398184610Salfred &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer, 399189265Sthompsa ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &sc->sc_mtx); 400184610Salfred 401184610Salfred if (error) { 402184610Salfred device_printf(dev, "allocating control USB " 403199816Sthompsa "transfers failed\n"); 404184610Salfred goto detach; 405184610Salfred } 406184610Salfred mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); 407184610Salfred if (mad == NULL) { 408184610Salfred goto detach; 409184610Salfred } 410184610Salfred if (mad->bFunctionLength < sizeof(*mad)) { 411184610Salfred device_printf(dev, "invalid MAD descriptor\n"); 412184610Salfred goto detach; 413184610Salfred } 414184610Salfred if ((mad->bType == UMCPC_ACM_TYPE_AB5) || 415184610Salfred (mad->bType == UMCPC_ACM_TYPE_AB6)) { 416187579Stakawata sc->sc_nobulk = 1; 417184610Salfred } else { 418187579Stakawata sc->sc_nobulk = 0; 419184610Salfred if (ufoma_modem_setup(dev, sc, uaa)) { 420184610Salfred goto detach; 421184610Salfred } 422184610Salfred } 423184610Salfred 424184610Salfred elements = (mad->bFunctionLength - sizeof(*mad) + 1); 425184610Salfred 426184610Salfred /* initialize mode variables */ 427184610Salfred 428184610Salfred sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK); 429184610Salfred 430184610Salfred if (sc->sc_modetable == NULL) { 431184610Salfred goto detach; 432184610Salfred } 433184610Salfred sc->sc_modetable[0] = (elements + 1); 434227461Shselasky memcpy(&sc->sc_modetable[1], mad->bMode, elements); 435184610Salfred 436184610Salfred sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED; 437184610Salfred sc->sc_modetoactivate = mad->bMode[0]; 438184610Salfred 439188413Sthompsa /* clear stall at first run, if any */ 440189265Sthompsa mtx_lock(&sc->sc_mtx); 441194677Sthompsa usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); 442194677Sthompsa usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); 443189265Sthompsa mtx_unlock(&sc->sc_mtx); 444184610Salfred 445194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 446189265Sthompsa &ufoma_callback, &sc->sc_mtx); 447184610Salfred if (error) { 448194228Sthompsa DPRINTF("ucom_attach failed\n"); 449184610Salfred goto detach; 450184610Salfred } 451214843Sn_hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 452214843Sn_hibma 453186885Stakawata /*Sysctls*/ 454186885Stakawata sctx = device_get_sysctl_ctx(dev); 455186885Stakawata soid = device_get_sysctl_tree(dev); 456186885Stakawata 457186885Stakawata SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode", 458186885Stakawata CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support, 459186885Stakawata "A", "Supporting port role"); 460186885Stakawata 461186885Stakawata SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode", 462186885Stakawata CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current, 463186885Stakawata "A", "Current port role"); 464186885Stakawata 465186885Stakawata SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode", 466186885Stakawata CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open, 467186885Stakawata "A", "Mode to transit when port is opened"); 468187113Stakawata SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit", 469214761Sn_hibma CTLFLAG_RD, &(sc->sc_super_ucom.sc_unit), 0, 470187113Stakawata "Unit number as USB serial"); 471186885Stakawata 472184610Salfred return (0); /* success */ 473184610Salfred 474184610Salfreddetach: 475184610Salfred ufoma_detach(dev); 476184610Salfred return (ENXIO); /* failure */ 477184610Salfred} 478184610Salfred 479184610Salfredstatic int 480184610Salfredufoma_detach(device_t dev) 481184610Salfred{ 482184610Salfred struct ufoma_softc *sc = device_get_softc(dev); 483184610Salfred 484214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 485194228Sthompsa usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); 486194228Sthompsa usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); 487184610Salfred 488184610Salfred if (sc->sc_modetable) { 489184610Salfred free(sc->sc_modetable, M_USBDEV); 490184610Salfred } 491194227Sthompsa cv_destroy(&sc->sc_cv); 492184610Salfred 493239299Shselasky device_claim_softc(dev); 494239299Shselasky 495239299Shselasky ufoma_free_softc(sc); 496239299Shselasky 497184610Salfred return (0); 498184610Salfred} 499184610Salfred 500239180ShselaskyUCOM_UNLOAD_DRAIN(ufoma); 501239180Shselasky 502239180Shselaskystatic void 503239299Shselaskyufoma_free_softc(struct ufoma_softc *sc) 504239180Shselasky{ 505239180Shselasky if (ucom_unref(&sc->sc_super_ucom)) { 506239299Shselasky mtx_destroy(&sc->sc_mtx); 507239299Shselasky device_free_softc(sc); 508239180Shselasky } 509239180Shselasky} 510239180Shselasky 511239180Shselaskystatic void 512239180Shselaskyufoma_free(struct ucom_softc *ucom) 513239180Shselasky{ 514239299Shselasky ufoma_free_softc(ucom->sc_parent); 515239180Shselasky} 516239180Shselasky 517184610Salfredstatic void * 518192984Sthompsaufoma_get_intconf(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id, 519184610Salfred uint8_t type, uint8_t subtype) 520184610Salfred{ 521192984Sthompsa struct usb_descriptor *desc = (void *)id; 522184610Salfred 523194228Sthompsa while ((desc = usb_desc_foreach(cd, desc))) { 524184610Salfred 525184610Salfred if (desc->bDescriptorType == UDESC_INTERFACE) { 526184610Salfred return (NULL); 527184610Salfred } 528184610Salfred if ((desc->bDescriptorType == type) && 529184610Salfred (desc->bDescriptorSubtype == subtype)) { 530184610Salfred break; 531184610Salfred } 532184610Salfred } 533184610Salfred return (desc); 534184610Salfred} 535184610Salfred 536184610Salfredstatic void 537184610Salfredufoma_cfg_link_state(struct ufoma_softc *sc) 538184610Salfred{ 539192984Sthompsa struct usb_device_request req; 540184610Salfred int32_t error; 541184610Salfred 542184610Salfred req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; 543184610Salfred req.bRequest = UMCPC_SET_LINK; 544184610Salfred USETW(req.wValue, UMCPC_CM_MOBILE_ACM); 545184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 546184610Salfred USETW(req.wLength, sc->sc_modetable[0]); 547184610Salfred 548194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 549188413Sthompsa &req, sc->sc_modetable, 0, 1000); 550184610Salfred 551194227Sthompsa error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz); 552184610Salfred 553184610Salfred if (error) { 554184610Salfred DPRINTF("NO response\n"); 555184610Salfred } 556184610Salfred} 557184610Salfred 558184610Salfredstatic void 559184610Salfredufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state) 560184610Salfred{ 561192984Sthompsa struct usb_device_request req; 562184610Salfred int32_t error; 563184610Salfred 564184610Salfred req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; 565184610Salfred req.bRequest = UMCPC_ACTIVATE_MODE; 566184610Salfred USETW(req.wValue, state); 567184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 568184610Salfred USETW(req.wLength, 0); 569184610Salfred 570194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 571188413Sthompsa &req, NULL, 0, 1000); 572184610Salfred 573194227Sthompsa error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, 574184610Salfred (UFOMA_MAX_TIMEOUT * hz)); 575184610Salfred if (error) { 576184610Salfred DPRINTF("No response\n"); 577184610Salfred } 578184610Salfred} 579184610Salfred 580184610Salfredstatic void 581194677Sthompsaufoma_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) 582184610Salfred{ 583194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 584192984Sthompsa struct usb_device_request req; 585194677Sthompsa struct usb_page_cache *pc0, *pc1; 586194677Sthompsa int len, aframes, nframes; 587184610Salfred 588194677Sthompsa usbd_xfer_status(xfer, NULL, NULL, &aframes, &nframes); 589194677Sthompsa 590184610Salfred switch (USB_GET_STATE(xfer)) { 591184610Salfred case USB_ST_TRANSFERRED: 592184610Salfredtr_transferred: 593194677Sthompsa if (aframes != nframes) 594184610Salfred goto tr_setup; 595194677Sthompsa pc1 = usbd_xfer_get_frame(xfer, 1); 596194682Sthompsa len = usbd_xfer_frame_len(xfer, 1); 597194677Sthompsa if (len > 0) 598194677Sthompsa ucom_put_data(&sc->sc_ucom, pc1, 0, len); 599194677Sthompsa /* FALLTHROUGH */ 600184610Salfred case USB_ST_SETUP: 601184610Salfredtr_setup: 602184610Salfred if (sc->sc_num_msg) { 603184610Salfred sc->sc_num_msg--; 604184610Salfred 605184610Salfred req.bmRequestType = UT_READ_CLASS_INTERFACE; 606184610Salfred req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; 607184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 608184610Salfred USETW(req.wValue, 0); 609184610Salfred USETW(req.wLength, UFOMA_CMD_BUF_SIZE); 610184610Salfred 611194677Sthompsa pc0 = usbd_xfer_get_frame(xfer, 0); 612194677Sthompsa usbd_copy_in(pc0, 0, &req, sizeof(req)); 613184610Salfred 614194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 615194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, UFOMA_CMD_BUF_SIZE); 616194677Sthompsa usbd_xfer_set_frames(xfer, 2); 617194228Sthompsa usbd_transfer_submit(xfer); 618184610Salfred } 619184610Salfred return; 620184610Salfred 621184610Salfred default: /* Error */ 622184610Salfred DPRINTF("error = %s\n", 623194677Sthompsa usbd_errstr(error)); 624184610Salfred 625194677Sthompsa if (error == USB_ERR_CANCELLED) { 626184610Salfred return; 627184610Salfred } 628184610Salfred goto tr_transferred; 629184610Salfred } 630184610Salfred} 631184610Salfred 632184610Salfredstatic void 633194677Sthompsaufoma_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 634184610Salfred{ 635194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 636192984Sthompsa struct usb_device_request req; 637194677Sthompsa struct usb_page_cache *pc; 638184610Salfred uint32_t actlen; 639184610Salfred 640184610Salfred switch (USB_GET_STATE(xfer)) { 641184610Salfred case USB_ST_TRANSFERRED: 642184610Salfredtr_transferred: 643184610Salfred case USB_ST_SETUP: 644194677Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 645194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 1, &actlen)) { 646184610Salfred 647184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 648184610Salfred req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; 649184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 650184610Salfred USETW(req.wValue, 0); 651184610Salfred USETW(req.wLength, 1); 652184610Salfred 653194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 654194677Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 655184610Salfred 656194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 657194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, 1); 658194677Sthompsa usbd_xfer_set_frames(xfer, 2); 659184610Salfred 660194228Sthompsa usbd_transfer_submit(xfer); 661184610Salfred } 662184610Salfred return; 663184610Salfred 664184610Salfred default: /* Error */ 665194677Sthompsa DPRINTF("error = %s\n", usbd_errstr(error)); 666184610Salfred 667194677Sthompsa if (error == USB_ERR_CANCELLED) { 668184610Salfred return; 669184610Salfred } 670184610Salfred goto tr_transferred; 671184610Salfred } 672184610Salfred} 673184610Salfred 674184610Salfredstatic void 675194677Sthompsaufoma_intr_callback(struct usb_xfer *xfer, usb_error_t error) 676184610Salfred{ 677194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 678192984Sthompsa struct usb_cdc_notification pkt; 679194677Sthompsa struct usb_page_cache *pc; 680184610Salfred uint16_t wLen; 681184610Salfred uint16_t temp; 682184610Salfred uint8_t mstatus; 683194677Sthompsa int actlen; 684184610Salfred 685194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 686194677Sthompsa 687184610Salfred switch (USB_GET_STATE(xfer)) { 688184610Salfred case USB_ST_TRANSFERRED: 689194677Sthompsa if (actlen < 8) { 690184610Salfred DPRINTF("too short message\n"); 691184610Salfred goto tr_setup; 692184610Salfred } 693233774Shselasky if (actlen > (int)sizeof(pkt)) { 694184610Salfred DPRINTF("truncating message\n"); 695194677Sthompsa actlen = sizeof(pkt); 696184610Salfred } 697194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 698194677Sthompsa usbd_copy_out(pc, 0, &pkt, actlen); 699184610Salfred 700194677Sthompsa actlen -= 8; 701184610Salfred 702184610Salfred wLen = UGETW(pkt.wLength); 703194677Sthompsa if (actlen > wLen) { 704194677Sthompsa actlen = wLen; 705184610Salfred } 706184610Salfred if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) && 707184610Salfred (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) { 708184610Salfred temp = UGETW(pkt.wValue); 709184610Salfred sc->sc_currentmode = (temp >> 8); 710184610Salfred if (!(temp & 0xff)) { 711184610Salfred DPRINTF("Mode change failed!\n"); 712184610Salfred } 713194227Sthompsa cv_signal(&sc->sc_cv); 714184610Salfred } 715184610Salfred if (pkt.bmRequestType != UCDC_NOTIFICATION) { 716184610Salfred goto tr_setup; 717184610Salfred } 718184610Salfred switch (pkt.bNotification) { 719184610Salfred case UCDC_N_RESPONSE_AVAILABLE: 720187579Stakawata if (!(sc->sc_nobulk)) { 721184610Salfred DPRINTF("Wrong serial state!\n"); 722184610Salfred break; 723184610Salfred } 724184610Salfred if (sc->sc_num_msg != 0xFF) { 725184610Salfred sc->sc_num_msg++; 726184610Salfred } 727194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); 728184610Salfred break; 729184610Salfred 730184610Salfred case UCDC_N_SERIAL_STATE: 731187579Stakawata if (sc->sc_nobulk) { 732184610Salfred DPRINTF("Wrong serial state!\n"); 733184610Salfred break; 734184610Salfred } 735184610Salfred /* 736184610Salfred * Set the serial state in ucom driver based on 737184610Salfred * the bits from the notify message 738184610Salfred */ 739194677Sthompsa if (actlen < 2) { 740184610Salfred DPRINTF("invalid notification " 741194677Sthompsa "length, %d bytes!\n", actlen); 742184610Salfred break; 743184610Salfred } 744184610Salfred DPRINTF("notify bytes = 0x%02x, 0x%02x\n", 745184610Salfred pkt.data[0], pkt.data[1]); 746184610Salfred 747184610Salfred /* currently, lsr is always zero. */ 748184610Salfred sc->sc_lsr = 0; 749184610Salfred sc->sc_msr = 0; 750184610Salfred 751184610Salfred mstatus = pkt.data[0]; 752184610Salfred 753184610Salfred if (mstatus & UCDC_N_SERIAL_RI) { 754184610Salfred sc->sc_msr |= SER_RI; 755184610Salfred } 756184610Salfred if (mstatus & UCDC_N_SERIAL_DSR) { 757184610Salfred sc->sc_msr |= SER_DSR; 758184610Salfred } 759184610Salfred if (mstatus & UCDC_N_SERIAL_DCD) { 760184610Salfred sc->sc_msr |= SER_DCD; 761184610Salfred } 762194228Sthompsa ucom_status_change(&sc->sc_ucom); 763184610Salfred break; 764184610Salfred 765184610Salfred default: 766184610Salfred break; 767184610Salfred } 768184610Salfred 769184610Salfred case USB_ST_SETUP: 770184610Salfredtr_setup: 771194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 772194228Sthompsa usbd_transfer_submit(xfer); 773184610Salfred return; 774184610Salfred 775184610Salfred default: /* Error */ 776194677Sthompsa if (error != USB_ERR_CANCELLED) { 777188413Sthompsa /* try to clear stall first */ 778194677Sthompsa usbd_xfer_set_stall(xfer); 779188413Sthompsa goto tr_setup; 780184610Salfred } 781184610Salfred return; 782184610Salfred } 783184610Salfred} 784184610Salfred 785184610Salfredstatic void 786194677Sthompsaufoma_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 787184610Salfred{ 788194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 789194677Sthompsa struct usb_page_cache *pc; 790184610Salfred uint32_t actlen; 791184610Salfred 792184610Salfred switch (USB_GET_STATE(xfer)) { 793184610Salfred case USB_ST_SETUP: 794184610Salfred case USB_ST_TRANSFERRED: 795188413Sthompsatr_setup: 796194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 797194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 798184610Salfred UFOMA_BULK_BUF_SIZE, &actlen)) { 799194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, actlen); 800194228Sthompsa usbd_transfer_submit(xfer); 801184610Salfred } 802184610Salfred return; 803184610Salfred 804184610Salfred default: /* Error */ 805194677Sthompsa if (error != USB_ERR_CANCELLED) { 806188413Sthompsa /* try to clear stall first */ 807194677Sthompsa usbd_xfer_set_stall(xfer); 808188413Sthompsa goto tr_setup; 809184610Salfred } 810184610Salfred return; 811184610Salfred } 812184610Salfred} 813184610Salfred 814184610Salfredstatic void 815194677Sthompsaufoma_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 816184610Salfred{ 817194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 818194677Sthompsa struct usb_page_cache *pc; 819194677Sthompsa int actlen; 820184610Salfred 821194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 822194677Sthompsa 823184610Salfred switch (USB_GET_STATE(xfer)) { 824184610Salfred case USB_ST_TRANSFERRED: 825194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 826194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 827184610Salfred 828184610Salfred case USB_ST_SETUP: 829188413Sthompsatr_setup: 830194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 831194228Sthompsa usbd_transfer_submit(xfer); 832184610Salfred return; 833184610Salfred 834184610Salfred default: /* Error */ 835194677Sthompsa if (error != USB_ERR_CANCELLED) { 836188413Sthompsa /* try to clear stall first */ 837194677Sthompsa usbd_xfer_set_stall(xfer); 838188413Sthompsa goto tr_setup; 839184610Salfred } 840184610Salfred return; 841184610Salfred } 842184610Salfred} 843184610Salfred 844184610Salfredstatic void 845192984Sthompsaufoma_cfg_open(struct ucom_softc *ucom) 846184610Salfred{ 847184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 848184610Salfred 849184610Salfred /* empty input queue */ 850184610Salfred 851184610Salfred if (sc->sc_num_msg != 0xFF) { 852184610Salfred sc->sc_num_msg++; 853184610Salfred } 854184610Salfred if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) { 855184610Salfred ufoma_cfg_link_state(sc); 856184610Salfred } 857184610Salfred if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) { 858184610Salfred ufoma_cfg_activate_state(sc, sc->sc_modetoactivate); 859184610Salfred } 860184610Salfred} 861184610Salfred 862184610Salfredstatic void 863192984Sthompsaufoma_cfg_close(struct ucom_softc *ucom) 864184610Salfred{ 865184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 866184610Salfred 867184610Salfred ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED); 868184610Salfred} 869184610Salfred 870184610Salfredstatic void 871192984Sthompsaufoma_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 872184610Salfred{ 873184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 874192984Sthompsa struct usb_device_request req; 875184610Salfred uint16_t wValue; 876184610Salfred 877187579Stakawata if (sc->sc_nobulk || 878186885Stakawata (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { 879184610Salfred return; 880184610Salfred } 881184610Salfred if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) { 882184610Salfred return; 883184610Salfred } 884184610Salfred wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; 885184610Salfred 886184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 887184610Salfred req.bRequest = UCDC_SEND_BREAK; 888184610Salfred USETW(req.wValue, wValue); 889184610Salfred req.wIndex[0] = sc->sc_ctrl_iface_no; 890184610Salfred req.wIndex[1] = 0; 891184610Salfred USETW(req.wLength, 0); 892184610Salfred 893194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 894188413Sthompsa &req, NULL, 0, 1000); 895184610Salfred} 896184610Salfred 897184610Salfredstatic void 898192984Sthompsaufoma_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 899184610Salfred{ 900184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 901184610Salfred 902184610Salfred *lsr = sc->sc_lsr; 903184610Salfred *msr = sc->sc_msr; 904184610Salfred} 905184610Salfred 906184610Salfredstatic void 907184610Salfredufoma_cfg_set_line_state(struct ufoma_softc *sc) 908184610Salfred{ 909192984Sthompsa struct usb_device_request req; 910184610Salfred 911184610Salfred /* Don't send line state emulation request for OBEX port */ 912184610Salfred if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) { 913184610Salfred return; 914184610Salfred } 915184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 916184610Salfred req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 917184610Salfred USETW(req.wValue, sc->sc_line); 918184610Salfred req.wIndex[0] = sc->sc_ctrl_iface_no; 919184610Salfred req.wIndex[1] = 0; 920184610Salfred USETW(req.wLength, 0); 921184610Salfred 922194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 923188413Sthompsa &req, NULL, 0, 1000); 924184610Salfred} 925184610Salfred 926184610Salfredstatic void 927192984Sthompsaufoma_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 928184610Salfred{ 929184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 930184610Salfred 931187579Stakawata if (sc->sc_nobulk) { 932184610Salfred return; 933184610Salfred } 934184610Salfred if (onoff) 935184610Salfred sc->sc_line |= UCDC_LINE_DTR; 936184610Salfred else 937184610Salfred sc->sc_line &= ~UCDC_LINE_DTR; 938184610Salfred 939184610Salfred ufoma_cfg_set_line_state(sc); 940184610Salfred} 941184610Salfred 942184610Salfredstatic void 943192984Sthompsaufoma_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 944184610Salfred{ 945184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 946184610Salfred 947187579Stakawata if (sc->sc_nobulk) { 948184610Salfred return; 949184610Salfred } 950184610Salfred if (onoff) 951184610Salfred sc->sc_line |= UCDC_LINE_RTS; 952184610Salfred else 953184610Salfred sc->sc_line &= ~UCDC_LINE_RTS; 954184610Salfred 955184610Salfred ufoma_cfg_set_line_state(sc); 956184610Salfred} 957184610Salfred 958184610Salfredstatic int 959192984Sthompsaufoma_pre_param(struct ucom_softc *ucom, struct termios *t) 960184610Salfred{ 961184610Salfred return (0); /* we accept anything */ 962184610Salfred} 963184610Salfred 964184610Salfredstatic void 965192984Sthompsaufoma_cfg_param(struct ucom_softc *ucom, struct termios *t) 966184610Salfred{ 967184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 968192984Sthompsa struct usb_device_request req; 969192984Sthompsa struct usb_cdc_line_state ls; 970184610Salfred 971187579Stakawata if (sc->sc_nobulk || 972184610Salfred (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { 973184610Salfred return; 974184610Salfred } 975184610Salfred DPRINTF("\n"); 976184610Salfred 977227461Shselasky memset(&ls, 0, sizeof(ls)); 978184610Salfred 979184610Salfred USETDW(ls.dwDTERate, t->c_ospeed); 980184610Salfred 981184610Salfred if (t->c_cflag & CSTOPB) { 982184610Salfred ls.bCharFormat = UCDC_STOP_BIT_2; 983184610Salfred } else { 984184610Salfred ls.bCharFormat = UCDC_STOP_BIT_1; 985184610Salfred } 986184610Salfred 987184610Salfred if (t->c_cflag & PARENB) { 988184610Salfred if (t->c_cflag & PARODD) { 989184610Salfred ls.bParityType = UCDC_PARITY_ODD; 990184610Salfred } else { 991184610Salfred ls.bParityType = UCDC_PARITY_EVEN; 992184610Salfred } 993184610Salfred } else { 994184610Salfred ls.bParityType = UCDC_PARITY_NONE; 995184610Salfred } 996184610Salfred 997184610Salfred switch (t->c_cflag & CSIZE) { 998184610Salfred case CS5: 999184610Salfred ls.bDataBits = 5; 1000184610Salfred break; 1001184610Salfred case CS6: 1002184610Salfred ls.bDataBits = 6; 1003184610Salfred break; 1004184610Salfred case CS7: 1005184610Salfred ls.bDataBits = 7; 1006184610Salfred break; 1007184610Salfred case CS8: 1008184610Salfred ls.bDataBits = 8; 1009184610Salfred break; 1010184610Salfred } 1011184610Salfred 1012184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 1013184610Salfred req.bRequest = UCDC_SET_LINE_CODING; 1014184610Salfred USETW(req.wValue, 0); 1015184610Salfred req.wIndex[0] = sc->sc_ctrl_iface_no; 1016184610Salfred req.wIndex[1] = 0; 1017184610Salfred USETW(req.wLength, UCDC_LINE_STATE_LENGTH); 1018184610Salfred 1019194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 1020188413Sthompsa &req, &ls, 0, 1000); 1021184610Salfred} 1022184610Salfred 1023184610Salfredstatic int 1024184610Salfredufoma_modem_setup(device_t dev, struct ufoma_softc *sc, 1025192984Sthompsa struct usb_attach_arg *uaa) 1026184610Salfred{ 1027192984Sthompsa struct usb_config_descriptor *cd; 1028192984Sthompsa struct usb_cdc_acm_descriptor *acm; 1029192984Sthompsa struct usb_cdc_cm_descriptor *cmd; 1030192984Sthompsa struct usb_interface_descriptor *id; 1031192984Sthompsa struct usb_interface *iface; 1032184610Salfred uint8_t i; 1033184610Salfred int32_t error; 1034184610Salfred 1035194228Sthompsa cd = usbd_get_config_descriptor(uaa->device); 1036194228Sthompsa id = usbd_get_interface_descriptor(uaa->iface); 1037184610Salfred 1038184610Salfred cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 1039184610Salfred 1040184610Salfred if ((cmd == NULL) || 1041184610Salfred (cmd->bLength < sizeof(*cmd))) { 1042184610Salfred return (EINVAL); 1043184610Salfred } 1044184610Salfred sc->sc_cm_cap = cmd->bmCapabilities; 1045184610Salfred sc->sc_data_iface_no = cmd->bDataInterface; 1046184610Salfred 1047184610Salfred acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); 1048184610Salfred 1049184610Salfred if ((acm == NULL) || 1050184610Salfred (acm->bLength < sizeof(*acm))) { 1051184610Salfred return (EINVAL); 1052184610Salfred } 1053184610Salfred sc->sc_acm_cap = acm->bmCapabilities; 1054184610Salfred 1055184610Salfred device_printf(dev, "data interface %d, has %sCM over data, " 1056184610Salfred "has %sbreak\n", 1057184610Salfred sc->sc_data_iface_no, 1058184610Salfred sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", 1059184610Salfred sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); 1060184610Salfred 1061184610Salfred /* get the data interface too */ 1062184610Salfred 1063184610Salfred for (i = 0;; i++) { 1064184610Salfred 1065194228Sthompsa iface = usbd_get_iface(uaa->device, i); 1066184610Salfred 1067184610Salfred if (iface) { 1068184610Salfred 1069194228Sthompsa id = usbd_get_interface_descriptor(iface); 1070184610Salfred 1071184610Salfred if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { 1072184610Salfred sc->sc_data_iface_index = i; 1073194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 1074184610Salfred break; 1075184610Salfred } 1076184610Salfred } else { 1077199816Sthompsa device_printf(dev, "no data interface\n"); 1078184610Salfred return (EINVAL); 1079184610Salfred } 1080184610Salfred } 1081184610Salfred 1082194228Sthompsa error = usbd_transfer_setup(uaa->device, 1083184610Salfred &sc->sc_data_iface_index, sc->sc_bulk_xfer, 1084189265Sthompsa ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &sc->sc_mtx); 1085184610Salfred 1086184610Salfred if (error) { 1087184610Salfred device_printf(dev, "allocating BULK USB " 1088199816Sthompsa "transfers failed\n"); 1089184610Salfred return (EINVAL); 1090184610Salfred } 1091184610Salfred return (0); 1092184610Salfred} 1093184610Salfred 1094184610Salfredstatic void 1095192984Sthompsaufoma_start_read(struct ucom_softc *ucom) 1096184610Salfred{ 1097184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1098184610Salfred 1099184610Salfred /* start interrupt transfer */ 1100194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]); 1101184610Salfred 1102184610Salfred /* start data transfer */ 1103187579Stakawata if (sc->sc_nobulk) { 1104194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); 1105184610Salfred } else { 1106194228Sthompsa usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); 1107184610Salfred } 1108184610Salfred} 1109184610Salfred 1110184610Salfredstatic void 1111192984Sthompsaufoma_stop_read(struct ucom_softc *ucom) 1112184610Salfred{ 1113184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1114184610Salfred 1115184610Salfred /* stop interrupt transfer */ 1116194228Sthompsa usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]); 1117184610Salfred 1118184610Salfred /* stop data transfer */ 1119187579Stakawata if (sc->sc_nobulk) { 1120194228Sthompsa usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); 1121184610Salfred } else { 1122194228Sthompsa usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); 1123184610Salfred } 1124184610Salfred} 1125184610Salfred 1126184610Salfredstatic void 1127192984Sthompsaufoma_start_write(struct ucom_softc *ucom) 1128184610Salfred{ 1129184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1130184610Salfred 1131187579Stakawata if (sc->sc_nobulk) { 1132194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); 1133184610Salfred } else { 1134194228Sthompsa usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); 1135184610Salfred } 1136184610Salfred} 1137184610Salfred 1138184610Salfredstatic void 1139192984Sthompsaufoma_stop_write(struct ucom_softc *ucom) 1140184610Salfred{ 1141184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1142184610Salfred 1143187579Stakawata if (sc->sc_nobulk) { 1144194228Sthompsa usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); 1145184610Salfred } else { 1146194228Sthompsa usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); 1147184610Salfred } 1148184610Salfred} 1149186885Stakawata 1150194099Sthompsastatic struct umcpc_modetostr_tab{ 1151186885Stakawata int mode; 1152186885Stakawata char *str; 1153186885Stakawata}umcpc_modetostr_tab[]={ 1154186885Stakawata {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"}, 1155186885Stakawata {UMCPC_ACM_MODE_MODEM, "modem"}, 1156186885Stakawata {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"}, 1157186885Stakawata {UMCPC_ACM_MODE_OBEX, "obex"}, 1158186885Stakawata {UMCPC_ACM_MODE_VENDOR1, "vendor1"}, 1159186885Stakawata {UMCPC_ACM_MODE_VENDOR2, "vendor2"}, 1160186885Stakawata {UMCPC_ACM_MODE_UNLINKED, "unlinked"}, 1161186885Stakawata {0, NULL} 1162186885Stakawata}; 1163186885Stakawata 1164186885Stakawatastatic char *ufoma_mode_to_str(int mode) 1165186885Stakawata{ 1166186885Stakawata int i; 1167186885Stakawata for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ 1168186885Stakawata if(umcpc_modetostr_tab[i].mode == mode){ 1169186885Stakawata return umcpc_modetostr_tab[i].str; 1170186885Stakawata } 1171186885Stakawata } 1172186885Stakawata return NULL; 1173186885Stakawata} 1174186885Stakawata 1175186885Stakawatastatic int ufoma_str_to_mode(char *str) 1176186885Stakawata{ 1177186885Stakawata int i; 1178186885Stakawata for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ 1179186885Stakawata if(strcmp(str, umcpc_modetostr_tab[i].str)==0){ 1180186885Stakawata return umcpc_modetostr_tab[i].mode; 1181186885Stakawata } 1182186885Stakawata } 1183186885Stakawata return -1; 1184186885Stakawata} 1185186885Stakawata 1186186885Stakawatastatic int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS) 1187186885Stakawata{ 1188186885Stakawata struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; 1189186885Stakawata struct sbuf sb; 1190186885Stakawata int i; 1191186885Stakawata char *mode; 1192186885Stakawata 1193186885Stakawata sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND); 1194186885Stakawata for(i = 1; i < sc->sc_modetable[0]; i++){ 1195186885Stakawata mode = ufoma_mode_to_str(sc->sc_modetable[i]); 1196186885Stakawata if(mode !=NULL){ 1197186885Stakawata sbuf_cat(&sb, mode); 1198186885Stakawata }else{ 1199186885Stakawata sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]); 1200186885Stakawata } 1201186885Stakawata if(i < (sc->sc_modetable[0]-1)) 1202186885Stakawata sbuf_cat(&sb, ","); 1203186885Stakawata } 1204186885Stakawata sbuf_trim(&sb); 1205186885Stakawata sbuf_finish(&sb); 1206186885Stakawata sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 1207186885Stakawata sbuf_delete(&sb); 1208186885Stakawata 1209186885Stakawata return 0; 1210186885Stakawata} 1211186885Stakawatastatic int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS) 1212186885Stakawata{ 1213186885Stakawata struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; 1214186885Stakawata char *mode; 1215186885Stakawata char subbuf[]="(XXX)"; 1216186885Stakawata mode = ufoma_mode_to_str(sc->sc_currentmode); 1217186885Stakawata if(!mode){ 1218186885Stakawata mode = subbuf; 1219186885Stakawata snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode); 1220186885Stakawata } 1221186885Stakawata sysctl_handle_string(oidp, mode, strlen(mode), req); 1222186885Stakawata 1223186885Stakawata return 0; 1224186885Stakawata 1225186885Stakawata} 1226186885Stakawatastatic int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS) 1227186885Stakawata{ 1228186885Stakawata struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; 1229186885Stakawata char *mode; 1230186885Stakawata char subbuf[40]; 1231186885Stakawata int newmode; 1232186885Stakawata int error; 1233186885Stakawata int i; 1234186885Stakawata 1235186885Stakawata mode = ufoma_mode_to_str(sc->sc_modetoactivate); 1236186885Stakawata if(mode){ 1237186885Stakawata strncpy(subbuf, mode, sizeof(subbuf)); 1238186885Stakawata }else{ 1239186885Stakawata snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate); 1240186885Stakawata } 1241186885Stakawata error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req); 1242186885Stakawata if(error != 0 || req->newptr == NULL){ 1243186885Stakawata return error; 1244186885Stakawata } 1245186885Stakawata 1246186885Stakawata if((newmode = ufoma_str_to_mode(subbuf)) == -1){ 1247186885Stakawata return EINVAL; 1248186885Stakawata } 1249186885Stakawata 1250186885Stakawata for(i = 1 ; i < sc->sc_modetable[0] ; i++){ 1251186885Stakawata if(sc->sc_modetable[i] == newmode){ 1252186885Stakawata sc->sc_modetoactivate = newmode; 1253186885Stakawata return 0; 1254186885Stakawata } 1255186885Stakawata } 1256186885Stakawata 1257186885Stakawata return EINVAL; 1258186885Stakawata} 1259197570Sthompsa 1260197570Sthompsastatic void 1261197570Sthompsaufoma_poll(struct ucom_softc *ucom) 1262197570Sthompsa{ 1263197570Sthompsa struct ufoma_softc *sc = ucom->sc_parent; 1264197570Sthompsa usbd_transfer_poll(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); 1265197570Sthompsa usbd_transfer_poll(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); 1266197570Sthompsa} 1267