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 * 3. All advertising materials mentioning features or use of this software 50184610Salfred * must display the following acknowledgement: 51184610Salfred * This product includes software developed by the NetBSD 52184610Salfred * Foundation, Inc. and its contributors. 53184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its 54184610Salfred * contributors may be used to endorse or promote products derived 55184610Salfred * from this software without specific prior written permission. 56184610Salfred * 57184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 58184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 59184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 60184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 61184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 62184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 63184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 64184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 65184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 66184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 67184610Salfred * POSSIBILITY OF SUCH DAMAGE. 68184610Salfred */ 69184610Salfred 70184610Salfred/* 71184610Salfred * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf 72184610Salfred * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf 73184610Salfred */ 74184610Salfred 75184610Salfred/* 76184610Salfred * TODO: 77184610Salfred * - Implement a Call Device for modems without multiplexed commands. 78184610Salfred */ 79184610Salfred 80184610Salfred/* 81184610Salfred * NOTE: all function names beginning like "ufoma_cfg_" can only 82184610Salfred * be called from within the config thread function ! 83184610Salfred */ 84184610Salfred 85194677Sthompsa#include <sys/stdint.h> 86194677Sthompsa#include <sys/stddef.h> 87194677Sthompsa#include <sys/param.h> 88194677Sthompsa#include <sys/queue.h> 89194677Sthompsa#include <sys/types.h> 90194677Sthompsa#include <sys/systm.h> 91194677Sthompsa#include <sys/kernel.h> 92194677Sthompsa#include <sys/bus.h> 93194677Sthompsa#include <sys/module.h> 94194677Sthompsa#include <sys/lock.h> 95194677Sthompsa#include <sys/mutex.h> 96194677Sthompsa#include <sys/condvar.h> 97194677Sthompsa#include <sys/sysctl.h> 98194677Sthompsa#include <sys/sx.h> 99194677Sthompsa#include <sys/unistd.h> 100194677Sthompsa#include <sys/callout.h> 101194677Sthompsa#include <sys/malloc.h> 102194677Sthompsa#include <sys/priv.h> 103194677Sthompsa#include <sys/sbuf.h> 104194677Sthompsa 105188942Sthompsa#include <dev/usb/usb.h> 106194677Sthompsa#include <dev/usb/usbdi.h> 107194677Sthompsa#include <dev/usb/usbdi_util.h> 108188942Sthompsa#include <dev/usb/usb_cdc.h> 109194677Sthompsa#include "usbdevs.h" 110184610Salfred 111194228Sthompsa#define USB_DEBUG_VAR usb_debug 112188942Sthompsa#include <dev/usb/usb_debug.h> 113188942Sthompsa#include <dev/usb/usb_process.h> 114184610Salfred 115188942Sthompsa#include <dev/usb/serial/usb_serial.h> 116184610Salfred 117184610Salfredtypedef struct ufoma_mobile_acm_descriptor { 118184610Salfred uint8_t bFunctionLength; 119184610Salfred uint8_t bDescriptorType; 120184610Salfred uint8_t bDescriptorSubtype; 121184610Salfred uint8_t bType; 122184610Salfred uint8_t bMode[1]; 123194228Sthompsa} __packed usb_mcpc_acm_descriptor; 124184610Salfred 125184610Salfred#define UISUBCLASS_MCPC 0x88 126184610Salfred 127184610Salfred#define UDESC_VS_INTERFACE 0x44 128184610Salfred#define UDESCSUB_MCPC_ACM 0x11 129184610Salfred 130184610Salfred#define UMCPC_ACM_TYPE_AB1 0x1 131184610Salfred#define UMCPC_ACM_TYPE_AB2 0x2 132184610Salfred#define UMCPC_ACM_TYPE_AB5 0x5 133184610Salfred#define UMCPC_ACM_TYPE_AB6 0x6 134184610Salfred 135184610Salfred#define UMCPC_ACM_MODE_DEACTIVATED 0x0 136184610Salfred#define UMCPC_ACM_MODE_MODEM 0x1 137184610Salfred#define UMCPC_ACM_MODE_ATCOMMAND 0x2 138184610Salfred#define UMCPC_ACM_MODE_OBEX 0x60 139184610Salfred#define UMCPC_ACM_MODE_VENDOR1 0xc0 140184610Salfred#define UMCPC_ACM_MODE_VENDOR2 0xfe 141184610Salfred#define UMCPC_ACM_MODE_UNLINKED 0xff 142184610Salfred 143184610Salfred#define UMCPC_CM_MOBILE_ACM 0x0 144184610Salfred 145184610Salfred#define UMCPC_ACTIVATE_MODE 0x60 146184610Salfred#define UMCPC_GET_MODETABLE 0x61 147184610Salfred#define UMCPC_SET_LINK 0x62 148184610Salfred#define UMCPC_CLEAR_LINK 0x63 149184610Salfred 150184610Salfred#define UMCPC_REQUEST_ACKNOWLEDGE 0x31 151184610Salfred 152184610Salfred#define UFOMA_MAX_TIMEOUT 15 /* standard says 10 seconds */ 153184610Salfred#define UFOMA_CMD_BUF_SIZE 64 /* bytes */ 154184610Salfred 155184610Salfred#define UFOMA_BULK_BUF_SIZE 1024 /* bytes */ 156184610Salfred 157187299Stakawataenum { 158187299Stakawata UFOMA_CTRL_ENDPT_INTR, 159187299Stakawata UFOMA_CTRL_ENDPT_READ, 160187299Stakawata UFOMA_CTRL_ENDPT_WRITE, 161188413Sthompsa UFOMA_CTRL_ENDPT_MAX, 162187299Stakawata}; 163184610Salfred 164187299Stakawataenum { 165187299Stakawata UFOMA_BULK_ENDPT_WRITE, 166187299Stakawata UFOMA_BULK_ENDPT_READ, 167188413Sthompsa UFOMA_BULK_ENDPT_MAX, 168187299Stakawata}; 169187299Stakawata 170184610Salfredstruct ufoma_softc { 171192984Sthompsa struct ucom_super_softc sc_super_ucom; 172192984Sthompsa struct ucom_softc sc_ucom; 173184610Salfred struct cv sc_cv; 174189265Sthompsa struct mtx sc_mtx; 175184610Salfred 176192984Sthompsa struct usb_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX]; 177192984Sthompsa struct usb_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX]; 178184610Salfred uint8_t *sc_modetable; 179184610Salfred device_t sc_dev; 180192984Sthompsa struct usb_device *sc_udev; 181184610Salfred 182184610Salfred uint32_t sc_unit; 183184610Salfred 184184610Salfred uint16_t sc_line; 185184610Salfred 186184610Salfred uint8_t sc_num_msg; 187187579Stakawata uint8_t sc_nobulk; 188184610Salfred uint8_t sc_ctrl_iface_no; 189184610Salfred uint8_t sc_ctrl_iface_index; 190184610Salfred uint8_t sc_data_iface_no; 191184610Salfred uint8_t sc_data_iface_index; 192184610Salfred uint8_t sc_cm_cap; 193184610Salfred uint8_t sc_acm_cap; 194184610Salfred uint8_t sc_lsr; 195184610Salfred uint8_t sc_msr; 196184610Salfred uint8_t sc_modetoactivate; 197184610Salfred uint8_t sc_currentmode; 198184610Salfred}; 199184610Salfred 200184610Salfred/* prototypes */ 201184610Salfred 202184610Salfredstatic device_probe_t ufoma_probe; 203184610Salfredstatic device_attach_t ufoma_attach; 204184610Salfredstatic device_detach_t ufoma_detach; 205239299Shselaskystatic void ufoma_free_softc(struct ufoma_softc *); 206184610Salfred 207193045Sthompsastatic usb_callback_t ufoma_ctrl_read_callback; 208193045Sthompsastatic usb_callback_t ufoma_ctrl_write_callback; 209193045Sthompsastatic usb_callback_t ufoma_intr_callback; 210193045Sthompsastatic usb_callback_t ufoma_bulk_write_callback; 211193045Sthompsastatic usb_callback_t ufoma_bulk_read_callback; 212184610Salfred 213192984Sthompsastatic void *ufoma_get_intconf(struct usb_config_descriptor *, 214192984Sthompsa struct usb_interface_descriptor *, uint8_t, uint8_t); 215185948Sthompsastatic void ufoma_cfg_link_state(struct ufoma_softc *); 216185948Sthompsastatic void ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t); 217239180Shselaskystatic void ufoma_free(struct ucom_softc *); 218192984Sthompsastatic void ufoma_cfg_open(struct ucom_softc *); 219192984Sthompsastatic void ufoma_cfg_close(struct ucom_softc *); 220192984Sthompsastatic void ufoma_cfg_set_break(struct ucom_softc *, uint8_t); 221192984Sthompsastatic void ufoma_cfg_get_status(struct ucom_softc *, uint8_t *, 222185948Sthompsa uint8_t *); 223192984Sthompsastatic void ufoma_cfg_set_dtr(struct ucom_softc *, uint8_t); 224192984Sthompsastatic void ufoma_cfg_set_rts(struct ucom_softc *, uint8_t); 225192984Sthompsastatic int ufoma_pre_param(struct ucom_softc *, struct termios *); 226192984Sthompsastatic void ufoma_cfg_param(struct ucom_softc *, struct termios *); 227185948Sthompsastatic int ufoma_modem_setup(device_t, struct ufoma_softc *, 228192984Sthompsa struct usb_attach_arg *); 229192984Sthompsastatic void ufoma_start_read(struct ucom_softc *); 230192984Sthompsastatic void ufoma_stop_read(struct ucom_softc *); 231192984Sthompsastatic void ufoma_start_write(struct ucom_softc *); 232192984Sthompsastatic void ufoma_stop_write(struct ucom_softc *); 233197570Sthompsastatic void ufoma_poll(struct ucom_softc *ucom); 234184610Salfred 235186885Stakawata/*sysctl stuff*/ 236186885Stakawatastatic int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS); 237186885Stakawatastatic int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS); 238186885Stakawatastatic int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS); 239186885Stakawata 240192984Sthompsastatic const struct usb_config 241184610Salfred ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { 242184610Salfred 243187299Stakawata [UFOMA_CTRL_ENDPT_INTR] = { 244184610Salfred .type = UE_INTERRUPT, 245184610Salfred .endpoint = UE_ADDR_ANY, 246184610Salfred .direction = UE_DIR_IN, 247190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 248192984Sthompsa .bufsize = sizeof(struct usb_cdc_notification), 249190734Sthompsa .callback = &ufoma_intr_callback, 250184610Salfred }, 251184610Salfred 252187299Stakawata [UFOMA_CTRL_ENDPT_READ] = { 253184610Salfred .type = UE_CONTROL, 254184610Salfred .endpoint = 0x00, /* Control pipe */ 255184610Salfred .direction = UE_DIR_ANY, 256192984Sthompsa .bufsize = (sizeof(struct usb_device_request) + UFOMA_CMD_BUF_SIZE), 257190734Sthompsa .flags = {.short_xfer_ok = 1,}, 258190734Sthompsa .callback = &ufoma_ctrl_read_callback, 259190734Sthompsa .timeout = 1000, /* 1 second */ 260184610Salfred }, 261184610Salfred 262187299Stakawata [UFOMA_CTRL_ENDPT_WRITE] = { 263184610Salfred .type = UE_CONTROL, 264184610Salfred .endpoint = 0x00, /* Control pipe */ 265184610Salfred .direction = UE_DIR_ANY, 266192984Sthompsa .bufsize = (sizeof(struct usb_device_request) + 1), 267190734Sthompsa .callback = &ufoma_ctrl_write_callback, 268190734Sthompsa .timeout = 1000, /* 1 second */ 269184610Salfred }, 270184610Salfred}; 271184610Salfred 272192984Sthompsastatic const struct usb_config 273184610Salfred ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = { 274184610Salfred 275187299Stakawata [UFOMA_BULK_ENDPT_WRITE] = { 276184610Salfred .type = UE_BULK, 277184610Salfred .endpoint = UE_ADDR_ANY, 278184610Salfred .direction = UE_DIR_OUT, 279190734Sthompsa .bufsize = UFOMA_BULK_BUF_SIZE, 280190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 281190734Sthompsa .callback = &ufoma_bulk_write_callback, 282184610Salfred }, 283184610Salfred 284187299Stakawata [UFOMA_BULK_ENDPT_READ] = { 285184610Salfred .type = UE_BULK, 286184610Salfred .endpoint = UE_ADDR_ANY, 287184610Salfred .direction = UE_DIR_IN, 288190734Sthompsa .bufsize = UFOMA_BULK_BUF_SIZE, 289190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 290190734Sthompsa .callback = &ufoma_bulk_read_callback, 291184610Salfred }, 292184610Salfred}; 293184610Salfred 294192984Sthompsastatic const struct ucom_callback ufoma_callback = { 295194228Sthompsa .ucom_cfg_get_status = &ufoma_cfg_get_status, 296194228Sthompsa .ucom_cfg_set_dtr = &ufoma_cfg_set_dtr, 297194228Sthompsa .ucom_cfg_set_rts = &ufoma_cfg_set_rts, 298194228Sthompsa .ucom_cfg_set_break = &ufoma_cfg_set_break, 299194228Sthompsa .ucom_cfg_param = &ufoma_cfg_param, 300194228Sthompsa .ucom_cfg_open = &ufoma_cfg_open, 301194228Sthompsa .ucom_cfg_close = &ufoma_cfg_close, 302194228Sthompsa .ucom_pre_param = &ufoma_pre_param, 303194228Sthompsa .ucom_start_read = &ufoma_start_read, 304194228Sthompsa .ucom_stop_read = &ufoma_stop_read, 305194228Sthompsa .ucom_start_write = &ufoma_start_write, 306194228Sthompsa .ucom_stop_write = &ufoma_stop_write, 307197570Sthompsa .ucom_poll = &ufoma_poll, 308239180Shselasky .ucom_free = &ufoma_free, 309184610Salfred}; 310184610Salfred 311184610Salfredstatic device_method_t ufoma_methods[] = { 312184610Salfred /* Device methods */ 313184610Salfred DEVMETHOD(device_probe, ufoma_probe), 314184610Salfred DEVMETHOD(device_attach, ufoma_attach), 315184610Salfred DEVMETHOD(device_detach, ufoma_detach), 316239180Shselasky DEVMETHOD_END 317184610Salfred}; 318184610Salfred 319184610Salfredstatic devclass_t ufoma_devclass; 320184610Salfred 321184610Salfredstatic driver_t ufoma_driver = { 322184610Salfred .name = "ufoma", 323184610Salfred .methods = ufoma_methods, 324184610Salfred .size = sizeof(struct ufoma_softc), 325184610Salfred}; 326184610Salfred 327189275SthompsaDRIVER_MODULE(ufoma, uhub, ufoma_driver, ufoma_devclass, NULL, 0); 328188942SthompsaMODULE_DEPEND(ufoma, ucom, 1, 1, 1); 329188942SthompsaMODULE_DEPEND(ufoma, usb, 1, 1, 1); 330212122SthompsaMODULE_VERSION(ufoma, 1); 331184610Salfred 332223515Shselaskystatic const STRUCT_USB_HOST_ID ufoma_devs[] = { 333223515Shselasky {USB_IFACE_CLASS(UICLASS_CDC), 334223515Shselasky USB_IFACE_SUBCLASS(UISUBCLASS_MCPC),}, 335223515Shselasky}; 336223515Shselasky 337184610Salfredstatic int 338184610Salfredufoma_probe(device_t dev) 339184610Salfred{ 340192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 341192984Sthompsa struct usb_interface_descriptor *id; 342192984Sthompsa struct usb_config_descriptor *cd; 343194228Sthompsa usb_mcpc_acm_descriptor *mad; 344223515Shselasky int error; 345184610Salfred 346223515Shselasky if (uaa->usb_mode != USB_MODE_HOST) 347184610Salfred return (ENXIO); 348223515Shselasky 349223515Shselasky error = usbd_lookup_id_by_uaa(ufoma_devs, sizeof(ufoma_devs), uaa); 350223515Shselasky if (error) 351223515Shselasky return (error); 352223515Shselasky 353194228Sthompsa id = usbd_get_interface_descriptor(uaa->iface); 354194228Sthompsa cd = usbd_get_config_descriptor(uaa->device); 355184610Salfred 356223515Shselasky if (id == NULL || cd == NULL) 357184610Salfred return (ENXIO); 358223515Shselasky 359184610Salfred mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); 360223515Shselasky if (mad == NULL) 361184610Salfred return (ENXIO); 362223515Shselasky 363184610Salfred#ifndef UFOMA_HANDSFREE 364184610Salfred if ((mad->bType == UMCPC_ACM_TYPE_AB5) || 365223515Shselasky (mad->bType == UMCPC_ACM_TYPE_AB6)) 366184610Salfred return (ENXIO); 367184610Salfred#endif 368223515Shselasky return (BUS_PROBE_GENERIC); 369184610Salfred} 370184610Salfred 371184610Salfredstatic int 372184610Salfredufoma_attach(device_t dev) 373184610Salfred{ 374192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 375184610Salfred struct ufoma_softc *sc = device_get_softc(dev); 376192984Sthompsa struct usb_config_descriptor *cd; 377192984Sthompsa struct usb_interface_descriptor *id; 378186885Stakawata struct sysctl_ctx_list *sctx; 379186885Stakawata struct sysctl_oid *soid; 380186885Stakawata 381194228Sthompsa usb_mcpc_acm_descriptor *mad; 382184610Salfred uint8_t elements; 383184610Salfred int32_t error; 384184610Salfred 385184610Salfred sc->sc_udev = uaa->device; 386184610Salfred sc->sc_dev = dev; 387184610Salfred sc->sc_unit = device_get_unit(dev); 388184610Salfred 389189265Sthompsa mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF); 390239180Shselasky ucom_ref(&sc->sc_super_ucom); 391194227Sthompsa cv_init(&sc->sc_cv, "CWAIT"); 392184610Salfred 393194228Sthompsa device_set_usb_desc(dev); 394184610Salfred 395184610Salfred DPRINTF("\n"); 396184610Salfred 397184610Salfred /* setup control transfers */ 398184610Salfred 399194228Sthompsa cd = usbd_get_config_descriptor(uaa->device); 400194228Sthompsa id = usbd_get_interface_descriptor(uaa->iface); 401184610Salfred sc->sc_ctrl_iface_no = id->bInterfaceNumber; 402184610Salfred sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex; 403184610Salfred 404194228Sthompsa error = usbd_transfer_setup(uaa->device, 405184610Salfred &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer, 406189265Sthompsa ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &sc->sc_mtx); 407184610Salfred 408184610Salfred if (error) { 409184610Salfred device_printf(dev, "allocating control USB " 410199816Sthompsa "transfers failed\n"); 411184610Salfred goto detach; 412184610Salfred } 413184610Salfred mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); 414184610Salfred if (mad == NULL) { 415184610Salfred goto detach; 416184610Salfred } 417184610Salfred if (mad->bFunctionLength < sizeof(*mad)) { 418184610Salfred device_printf(dev, "invalid MAD descriptor\n"); 419184610Salfred goto detach; 420184610Salfred } 421184610Salfred if ((mad->bType == UMCPC_ACM_TYPE_AB5) || 422184610Salfred (mad->bType == UMCPC_ACM_TYPE_AB6)) { 423187579Stakawata sc->sc_nobulk = 1; 424184610Salfred } else { 425187579Stakawata sc->sc_nobulk = 0; 426184610Salfred if (ufoma_modem_setup(dev, sc, uaa)) { 427184610Salfred goto detach; 428184610Salfred } 429184610Salfred } 430184610Salfred 431184610Salfred elements = (mad->bFunctionLength - sizeof(*mad) + 1); 432184610Salfred 433184610Salfred /* initialize mode variables */ 434184610Salfred 435184610Salfred sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK); 436184610Salfred 437184610Salfred if (sc->sc_modetable == NULL) { 438184610Salfred goto detach; 439184610Salfred } 440184610Salfred sc->sc_modetable[0] = (elements + 1); 441227461Shselasky memcpy(&sc->sc_modetable[1], mad->bMode, elements); 442184610Salfred 443184610Salfred sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED; 444184610Salfred sc->sc_modetoactivate = mad->bMode[0]; 445184610Salfred 446188413Sthompsa /* clear stall at first run, if any */ 447189265Sthompsa mtx_lock(&sc->sc_mtx); 448194677Sthompsa usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); 449194677Sthompsa usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); 450189265Sthompsa mtx_unlock(&sc->sc_mtx); 451184610Salfred 452194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 453189265Sthompsa &ufoma_callback, &sc->sc_mtx); 454184610Salfred if (error) { 455194228Sthompsa DPRINTF("ucom_attach failed\n"); 456184610Salfred goto detach; 457184610Salfred } 458214843Sn_hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 459214843Sn_hibma 460186885Stakawata /*Sysctls*/ 461186885Stakawata sctx = device_get_sysctl_ctx(dev); 462186885Stakawata soid = device_get_sysctl_tree(dev); 463186885Stakawata 464186885Stakawata SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode", 465186885Stakawata CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support, 466186885Stakawata "A", "Supporting port role"); 467186885Stakawata 468186885Stakawata SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode", 469186885Stakawata CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current, 470186885Stakawata "A", "Current port role"); 471186885Stakawata 472186885Stakawata SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode", 473186885Stakawata CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open, 474186885Stakawata "A", "Mode to transit when port is opened"); 475187113Stakawata SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit", 476214761Sn_hibma CTLFLAG_RD, &(sc->sc_super_ucom.sc_unit), 0, 477187113Stakawata "Unit number as USB serial"); 478186885Stakawata 479184610Salfred return (0); /* success */ 480184610Salfred 481184610Salfreddetach: 482184610Salfred ufoma_detach(dev); 483184610Salfred return (ENXIO); /* failure */ 484184610Salfred} 485184610Salfred 486184610Salfredstatic int 487184610Salfredufoma_detach(device_t dev) 488184610Salfred{ 489184610Salfred struct ufoma_softc *sc = device_get_softc(dev); 490184610Salfred 491214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 492194228Sthompsa usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); 493194228Sthompsa usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); 494184610Salfred 495184610Salfred if (sc->sc_modetable) { 496184610Salfred free(sc->sc_modetable, M_USBDEV); 497184610Salfred } 498194227Sthompsa cv_destroy(&sc->sc_cv); 499184610Salfred 500239299Shselasky device_claim_softc(dev); 501239299Shselasky 502239299Shselasky ufoma_free_softc(sc); 503239299Shselasky 504184610Salfred return (0); 505184610Salfred} 506184610Salfred 507239180ShselaskyUCOM_UNLOAD_DRAIN(ufoma); 508239180Shselasky 509239180Shselaskystatic void 510239299Shselaskyufoma_free_softc(struct ufoma_softc *sc) 511239180Shselasky{ 512239180Shselasky if (ucom_unref(&sc->sc_super_ucom)) { 513239299Shselasky mtx_destroy(&sc->sc_mtx); 514239299Shselasky device_free_softc(sc); 515239180Shselasky } 516239180Shselasky} 517239180Shselasky 518239180Shselaskystatic void 519239180Shselaskyufoma_free(struct ucom_softc *ucom) 520239180Shselasky{ 521239299Shselasky ufoma_free_softc(ucom->sc_parent); 522239180Shselasky} 523239180Shselasky 524184610Salfredstatic void * 525192984Sthompsaufoma_get_intconf(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id, 526184610Salfred uint8_t type, uint8_t subtype) 527184610Salfred{ 528192984Sthompsa struct usb_descriptor *desc = (void *)id; 529184610Salfred 530194228Sthompsa while ((desc = usb_desc_foreach(cd, desc))) { 531184610Salfred 532184610Salfred if (desc->bDescriptorType == UDESC_INTERFACE) { 533184610Salfred return (NULL); 534184610Salfred } 535184610Salfred if ((desc->bDescriptorType == type) && 536184610Salfred (desc->bDescriptorSubtype == subtype)) { 537184610Salfred break; 538184610Salfred } 539184610Salfred } 540184610Salfred return (desc); 541184610Salfred} 542184610Salfred 543184610Salfredstatic void 544184610Salfredufoma_cfg_link_state(struct ufoma_softc *sc) 545184610Salfred{ 546192984Sthompsa struct usb_device_request req; 547184610Salfred int32_t error; 548184610Salfred 549184610Salfred req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; 550184610Salfred req.bRequest = UMCPC_SET_LINK; 551184610Salfred USETW(req.wValue, UMCPC_CM_MOBILE_ACM); 552184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 553184610Salfred USETW(req.wLength, sc->sc_modetable[0]); 554184610Salfred 555194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 556188413Sthompsa &req, sc->sc_modetable, 0, 1000); 557184610Salfred 558194227Sthompsa error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz); 559184610Salfred 560184610Salfred if (error) { 561184610Salfred DPRINTF("NO response\n"); 562184610Salfred } 563184610Salfred} 564184610Salfred 565184610Salfredstatic void 566184610Salfredufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state) 567184610Salfred{ 568192984Sthompsa struct usb_device_request req; 569184610Salfred int32_t error; 570184610Salfred 571184610Salfred req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; 572184610Salfred req.bRequest = UMCPC_ACTIVATE_MODE; 573184610Salfred USETW(req.wValue, state); 574184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 575184610Salfred USETW(req.wLength, 0); 576184610Salfred 577194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 578188413Sthompsa &req, NULL, 0, 1000); 579184610Salfred 580194227Sthompsa error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, 581184610Salfred (UFOMA_MAX_TIMEOUT * hz)); 582184610Salfred if (error) { 583184610Salfred DPRINTF("No response\n"); 584184610Salfred } 585184610Salfred} 586184610Salfred 587184610Salfredstatic void 588194677Sthompsaufoma_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) 589184610Salfred{ 590194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 591192984Sthompsa struct usb_device_request req; 592194677Sthompsa struct usb_page_cache *pc0, *pc1; 593194677Sthompsa int len, aframes, nframes; 594184610Salfred 595194677Sthompsa usbd_xfer_status(xfer, NULL, NULL, &aframes, &nframes); 596194677Sthompsa 597184610Salfred switch (USB_GET_STATE(xfer)) { 598184610Salfred case USB_ST_TRANSFERRED: 599184610Salfredtr_transferred: 600194677Sthompsa if (aframes != nframes) 601184610Salfred goto tr_setup; 602194677Sthompsa pc1 = usbd_xfer_get_frame(xfer, 1); 603194682Sthompsa len = usbd_xfer_frame_len(xfer, 1); 604194677Sthompsa if (len > 0) 605194677Sthompsa ucom_put_data(&sc->sc_ucom, pc1, 0, len); 606194677Sthompsa /* FALLTHROUGH */ 607184610Salfred case USB_ST_SETUP: 608184610Salfredtr_setup: 609184610Salfred if (sc->sc_num_msg) { 610184610Salfred sc->sc_num_msg--; 611184610Salfred 612184610Salfred req.bmRequestType = UT_READ_CLASS_INTERFACE; 613184610Salfred req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; 614184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 615184610Salfred USETW(req.wValue, 0); 616184610Salfred USETW(req.wLength, UFOMA_CMD_BUF_SIZE); 617184610Salfred 618194677Sthompsa pc0 = usbd_xfer_get_frame(xfer, 0); 619194677Sthompsa usbd_copy_in(pc0, 0, &req, sizeof(req)); 620184610Salfred 621194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 622194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, UFOMA_CMD_BUF_SIZE); 623194677Sthompsa usbd_xfer_set_frames(xfer, 2); 624194228Sthompsa usbd_transfer_submit(xfer); 625184610Salfred } 626184610Salfred return; 627184610Salfred 628184610Salfred default: /* Error */ 629184610Salfred DPRINTF("error = %s\n", 630194677Sthompsa usbd_errstr(error)); 631184610Salfred 632194677Sthompsa if (error == USB_ERR_CANCELLED) { 633184610Salfred return; 634184610Salfred } 635184610Salfred goto tr_transferred; 636184610Salfred } 637184610Salfred} 638184610Salfred 639184610Salfredstatic void 640194677Sthompsaufoma_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 641184610Salfred{ 642194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 643192984Sthompsa struct usb_device_request req; 644194677Sthompsa struct usb_page_cache *pc; 645184610Salfred uint32_t actlen; 646184610Salfred 647184610Salfred switch (USB_GET_STATE(xfer)) { 648184610Salfred case USB_ST_TRANSFERRED: 649184610Salfredtr_transferred: 650184610Salfred case USB_ST_SETUP: 651194677Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 652194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 1, &actlen)) { 653184610Salfred 654184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 655184610Salfred req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; 656184610Salfred USETW(req.wIndex, sc->sc_ctrl_iface_no); 657184610Salfred USETW(req.wValue, 0); 658184610Salfred USETW(req.wLength, 1); 659184610Salfred 660194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 661194677Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 662184610Salfred 663194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 664194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, 1); 665194677Sthompsa usbd_xfer_set_frames(xfer, 2); 666184610Salfred 667194228Sthompsa usbd_transfer_submit(xfer); 668184610Salfred } 669184610Salfred return; 670184610Salfred 671184610Salfred default: /* Error */ 672194677Sthompsa DPRINTF("error = %s\n", usbd_errstr(error)); 673184610Salfred 674194677Sthompsa if (error == USB_ERR_CANCELLED) { 675184610Salfred return; 676184610Salfred } 677184610Salfred goto tr_transferred; 678184610Salfred } 679184610Salfred} 680184610Salfred 681184610Salfredstatic void 682194677Sthompsaufoma_intr_callback(struct usb_xfer *xfer, usb_error_t error) 683184610Salfred{ 684194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 685192984Sthompsa struct usb_cdc_notification pkt; 686194677Sthompsa struct usb_page_cache *pc; 687184610Salfred uint16_t wLen; 688184610Salfred uint16_t temp; 689184610Salfred uint8_t mstatus; 690194677Sthompsa int actlen; 691184610Salfred 692194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 693194677Sthompsa 694184610Salfred switch (USB_GET_STATE(xfer)) { 695184610Salfred case USB_ST_TRANSFERRED: 696194677Sthompsa if (actlen < 8) { 697184610Salfred DPRINTF("too short message\n"); 698184610Salfred goto tr_setup; 699184610Salfred } 700233774Shselasky if (actlen > (int)sizeof(pkt)) { 701184610Salfred DPRINTF("truncating message\n"); 702194677Sthompsa actlen = sizeof(pkt); 703184610Salfred } 704194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 705194677Sthompsa usbd_copy_out(pc, 0, &pkt, actlen); 706184610Salfred 707194677Sthompsa actlen -= 8; 708184610Salfred 709184610Salfred wLen = UGETW(pkt.wLength); 710194677Sthompsa if (actlen > wLen) { 711194677Sthompsa actlen = wLen; 712184610Salfred } 713184610Salfred if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) && 714184610Salfred (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) { 715184610Salfred temp = UGETW(pkt.wValue); 716184610Salfred sc->sc_currentmode = (temp >> 8); 717184610Salfred if (!(temp & 0xff)) { 718184610Salfred DPRINTF("Mode change failed!\n"); 719184610Salfred } 720194227Sthompsa cv_signal(&sc->sc_cv); 721184610Salfred } 722184610Salfred if (pkt.bmRequestType != UCDC_NOTIFICATION) { 723184610Salfred goto tr_setup; 724184610Salfred } 725184610Salfred switch (pkt.bNotification) { 726184610Salfred case UCDC_N_RESPONSE_AVAILABLE: 727187579Stakawata if (!(sc->sc_nobulk)) { 728184610Salfred DPRINTF("Wrong serial state!\n"); 729184610Salfred break; 730184610Salfred } 731184610Salfred if (sc->sc_num_msg != 0xFF) { 732184610Salfred sc->sc_num_msg++; 733184610Salfred } 734194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); 735184610Salfred break; 736184610Salfred 737184610Salfred case UCDC_N_SERIAL_STATE: 738187579Stakawata if (sc->sc_nobulk) { 739184610Salfred DPRINTF("Wrong serial state!\n"); 740184610Salfred break; 741184610Salfred } 742184610Salfred /* 743184610Salfred * Set the serial state in ucom driver based on 744184610Salfred * the bits from the notify message 745184610Salfred */ 746194677Sthompsa if (actlen < 2) { 747184610Salfred DPRINTF("invalid notification " 748194677Sthompsa "length, %d bytes!\n", actlen); 749184610Salfred break; 750184610Salfred } 751184610Salfred DPRINTF("notify bytes = 0x%02x, 0x%02x\n", 752184610Salfred pkt.data[0], pkt.data[1]); 753184610Salfred 754184610Salfred /* currently, lsr is always zero. */ 755184610Salfred sc->sc_lsr = 0; 756184610Salfred sc->sc_msr = 0; 757184610Salfred 758184610Salfred mstatus = pkt.data[0]; 759184610Salfred 760184610Salfred if (mstatus & UCDC_N_SERIAL_RI) { 761184610Salfred sc->sc_msr |= SER_RI; 762184610Salfred } 763184610Salfred if (mstatus & UCDC_N_SERIAL_DSR) { 764184610Salfred sc->sc_msr |= SER_DSR; 765184610Salfred } 766184610Salfred if (mstatus & UCDC_N_SERIAL_DCD) { 767184610Salfred sc->sc_msr |= SER_DCD; 768184610Salfred } 769194228Sthompsa ucom_status_change(&sc->sc_ucom); 770184610Salfred break; 771184610Salfred 772184610Salfred default: 773184610Salfred break; 774184610Salfred } 775184610Salfred 776184610Salfred case USB_ST_SETUP: 777184610Salfredtr_setup: 778194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 779194228Sthompsa usbd_transfer_submit(xfer); 780184610Salfred return; 781184610Salfred 782184610Salfred default: /* Error */ 783194677Sthompsa if (error != USB_ERR_CANCELLED) { 784188413Sthompsa /* try to clear stall first */ 785194677Sthompsa usbd_xfer_set_stall(xfer); 786188413Sthompsa goto tr_setup; 787184610Salfred } 788184610Salfred return; 789184610Salfred } 790184610Salfred} 791184610Salfred 792184610Salfredstatic void 793194677Sthompsaufoma_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 794184610Salfred{ 795194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 796194677Sthompsa struct usb_page_cache *pc; 797184610Salfred uint32_t actlen; 798184610Salfred 799184610Salfred switch (USB_GET_STATE(xfer)) { 800184610Salfred case USB_ST_SETUP: 801184610Salfred case USB_ST_TRANSFERRED: 802188413Sthompsatr_setup: 803194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 804194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 805184610Salfred UFOMA_BULK_BUF_SIZE, &actlen)) { 806194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, actlen); 807194228Sthompsa usbd_transfer_submit(xfer); 808184610Salfred } 809184610Salfred return; 810184610Salfred 811184610Salfred default: /* Error */ 812194677Sthompsa if (error != USB_ERR_CANCELLED) { 813188413Sthompsa /* try to clear stall first */ 814194677Sthompsa usbd_xfer_set_stall(xfer); 815188413Sthompsa goto tr_setup; 816184610Salfred } 817184610Salfred return; 818184610Salfred } 819184610Salfred} 820184610Salfred 821184610Salfredstatic void 822194677Sthompsaufoma_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 823184610Salfred{ 824194677Sthompsa struct ufoma_softc *sc = usbd_xfer_softc(xfer); 825194677Sthompsa struct usb_page_cache *pc; 826194677Sthompsa int actlen; 827184610Salfred 828194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 829194677Sthompsa 830184610Salfred switch (USB_GET_STATE(xfer)) { 831184610Salfred case USB_ST_TRANSFERRED: 832194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 833194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 834184610Salfred 835184610Salfred case USB_ST_SETUP: 836188413Sthompsatr_setup: 837194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 838194228Sthompsa usbd_transfer_submit(xfer); 839184610Salfred return; 840184610Salfred 841184610Salfred default: /* Error */ 842194677Sthompsa if (error != USB_ERR_CANCELLED) { 843188413Sthompsa /* try to clear stall first */ 844194677Sthompsa usbd_xfer_set_stall(xfer); 845188413Sthompsa goto tr_setup; 846184610Salfred } 847184610Salfred return; 848184610Salfred } 849184610Salfred} 850184610Salfred 851184610Salfredstatic void 852192984Sthompsaufoma_cfg_open(struct ucom_softc *ucom) 853184610Salfred{ 854184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 855184610Salfred 856184610Salfred /* empty input queue */ 857184610Salfred 858184610Salfred if (sc->sc_num_msg != 0xFF) { 859184610Salfred sc->sc_num_msg++; 860184610Salfred } 861184610Salfred if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) { 862184610Salfred ufoma_cfg_link_state(sc); 863184610Salfred } 864184610Salfred if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) { 865184610Salfred ufoma_cfg_activate_state(sc, sc->sc_modetoactivate); 866184610Salfred } 867184610Salfred} 868184610Salfred 869184610Salfredstatic void 870192984Sthompsaufoma_cfg_close(struct ucom_softc *ucom) 871184610Salfred{ 872184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 873184610Salfred 874184610Salfred ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED); 875184610Salfred} 876184610Salfred 877184610Salfredstatic void 878192984Sthompsaufoma_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 879184610Salfred{ 880184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 881192984Sthompsa struct usb_device_request req; 882184610Salfred uint16_t wValue; 883184610Salfred 884187579Stakawata if (sc->sc_nobulk || 885186885Stakawata (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { 886184610Salfred return; 887184610Salfred } 888184610Salfred if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) { 889184610Salfred return; 890184610Salfred } 891184610Salfred wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; 892184610Salfred 893184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 894184610Salfred req.bRequest = UCDC_SEND_BREAK; 895184610Salfred USETW(req.wValue, wValue); 896184610Salfred req.wIndex[0] = sc->sc_ctrl_iface_no; 897184610Salfred req.wIndex[1] = 0; 898184610Salfred USETW(req.wLength, 0); 899184610Salfred 900194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 901188413Sthompsa &req, NULL, 0, 1000); 902184610Salfred} 903184610Salfred 904184610Salfredstatic void 905192984Sthompsaufoma_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 906184610Salfred{ 907184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 908184610Salfred 909184610Salfred *lsr = sc->sc_lsr; 910184610Salfred *msr = sc->sc_msr; 911184610Salfred} 912184610Salfred 913184610Salfredstatic void 914184610Salfredufoma_cfg_set_line_state(struct ufoma_softc *sc) 915184610Salfred{ 916192984Sthompsa struct usb_device_request req; 917184610Salfred 918184610Salfred /* Don't send line state emulation request for OBEX port */ 919184610Salfred if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) { 920184610Salfred return; 921184610Salfred } 922184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 923184610Salfred req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 924184610Salfred USETW(req.wValue, sc->sc_line); 925184610Salfred req.wIndex[0] = sc->sc_ctrl_iface_no; 926184610Salfred req.wIndex[1] = 0; 927184610Salfred USETW(req.wLength, 0); 928184610Salfred 929194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 930188413Sthompsa &req, NULL, 0, 1000); 931184610Salfred} 932184610Salfred 933184610Salfredstatic void 934192984Sthompsaufoma_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 935184610Salfred{ 936184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 937184610Salfred 938187579Stakawata if (sc->sc_nobulk) { 939184610Salfred return; 940184610Salfred } 941184610Salfred if (onoff) 942184610Salfred sc->sc_line |= UCDC_LINE_DTR; 943184610Salfred else 944184610Salfred sc->sc_line &= ~UCDC_LINE_DTR; 945184610Salfred 946184610Salfred ufoma_cfg_set_line_state(sc); 947184610Salfred} 948184610Salfred 949184610Salfredstatic void 950192984Sthompsaufoma_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 951184610Salfred{ 952184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 953184610Salfred 954187579Stakawata if (sc->sc_nobulk) { 955184610Salfred return; 956184610Salfred } 957184610Salfred if (onoff) 958184610Salfred sc->sc_line |= UCDC_LINE_RTS; 959184610Salfred else 960184610Salfred sc->sc_line &= ~UCDC_LINE_RTS; 961184610Salfred 962184610Salfred ufoma_cfg_set_line_state(sc); 963184610Salfred} 964184610Salfred 965184610Salfredstatic int 966192984Sthompsaufoma_pre_param(struct ucom_softc *ucom, struct termios *t) 967184610Salfred{ 968184610Salfred return (0); /* we accept anything */ 969184610Salfred} 970184610Salfred 971184610Salfredstatic void 972192984Sthompsaufoma_cfg_param(struct ucom_softc *ucom, struct termios *t) 973184610Salfred{ 974184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 975192984Sthompsa struct usb_device_request req; 976192984Sthompsa struct usb_cdc_line_state ls; 977184610Salfred 978187579Stakawata if (sc->sc_nobulk || 979184610Salfred (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { 980184610Salfred return; 981184610Salfred } 982184610Salfred DPRINTF("\n"); 983184610Salfred 984227461Shselasky memset(&ls, 0, sizeof(ls)); 985184610Salfred 986184610Salfred USETDW(ls.dwDTERate, t->c_ospeed); 987184610Salfred 988184610Salfred if (t->c_cflag & CSTOPB) { 989184610Salfred ls.bCharFormat = UCDC_STOP_BIT_2; 990184610Salfred } else { 991184610Salfred ls.bCharFormat = UCDC_STOP_BIT_1; 992184610Salfred } 993184610Salfred 994184610Salfred if (t->c_cflag & PARENB) { 995184610Salfred if (t->c_cflag & PARODD) { 996184610Salfred ls.bParityType = UCDC_PARITY_ODD; 997184610Salfred } else { 998184610Salfred ls.bParityType = UCDC_PARITY_EVEN; 999184610Salfred } 1000184610Salfred } else { 1001184610Salfred ls.bParityType = UCDC_PARITY_NONE; 1002184610Salfred } 1003184610Salfred 1004184610Salfred switch (t->c_cflag & CSIZE) { 1005184610Salfred case CS5: 1006184610Salfred ls.bDataBits = 5; 1007184610Salfred break; 1008184610Salfred case CS6: 1009184610Salfred ls.bDataBits = 6; 1010184610Salfred break; 1011184610Salfred case CS7: 1012184610Salfred ls.bDataBits = 7; 1013184610Salfred break; 1014184610Salfred case CS8: 1015184610Salfred ls.bDataBits = 8; 1016184610Salfred break; 1017184610Salfred } 1018184610Salfred 1019184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 1020184610Salfred req.bRequest = UCDC_SET_LINE_CODING; 1021184610Salfred USETW(req.wValue, 0); 1022184610Salfred req.wIndex[0] = sc->sc_ctrl_iface_no; 1023184610Salfred req.wIndex[1] = 0; 1024184610Salfred USETW(req.wLength, UCDC_LINE_STATE_LENGTH); 1025184610Salfred 1026194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 1027188413Sthompsa &req, &ls, 0, 1000); 1028184610Salfred} 1029184610Salfred 1030184610Salfredstatic int 1031184610Salfredufoma_modem_setup(device_t dev, struct ufoma_softc *sc, 1032192984Sthompsa struct usb_attach_arg *uaa) 1033184610Salfred{ 1034192984Sthompsa struct usb_config_descriptor *cd; 1035192984Sthompsa struct usb_cdc_acm_descriptor *acm; 1036192984Sthompsa struct usb_cdc_cm_descriptor *cmd; 1037192984Sthompsa struct usb_interface_descriptor *id; 1038192984Sthompsa struct usb_interface *iface; 1039184610Salfred uint8_t i; 1040184610Salfred int32_t error; 1041184610Salfred 1042194228Sthompsa cd = usbd_get_config_descriptor(uaa->device); 1043194228Sthompsa id = usbd_get_interface_descriptor(uaa->iface); 1044184610Salfred 1045184610Salfred cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 1046184610Salfred 1047184610Salfred if ((cmd == NULL) || 1048184610Salfred (cmd->bLength < sizeof(*cmd))) { 1049184610Salfred return (EINVAL); 1050184610Salfred } 1051184610Salfred sc->sc_cm_cap = cmd->bmCapabilities; 1052184610Salfred sc->sc_data_iface_no = cmd->bDataInterface; 1053184610Salfred 1054184610Salfred acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); 1055184610Salfred 1056184610Salfred if ((acm == NULL) || 1057184610Salfred (acm->bLength < sizeof(*acm))) { 1058184610Salfred return (EINVAL); 1059184610Salfred } 1060184610Salfred sc->sc_acm_cap = acm->bmCapabilities; 1061184610Salfred 1062184610Salfred device_printf(dev, "data interface %d, has %sCM over data, " 1063184610Salfred "has %sbreak\n", 1064184610Salfred sc->sc_data_iface_no, 1065184610Salfred sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", 1066184610Salfred sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); 1067184610Salfred 1068184610Salfred /* get the data interface too */ 1069184610Salfred 1070184610Salfred for (i = 0;; i++) { 1071184610Salfred 1072194228Sthompsa iface = usbd_get_iface(uaa->device, i); 1073184610Salfred 1074184610Salfred if (iface) { 1075184610Salfred 1076194228Sthompsa id = usbd_get_interface_descriptor(iface); 1077184610Salfred 1078184610Salfred if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { 1079184610Salfred sc->sc_data_iface_index = i; 1080194228Sthompsa usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 1081184610Salfred break; 1082184610Salfred } 1083184610Salfred } else { 1084199816Sthompsa device_printf(dev, "no data interface\n"); 1085184610Salfred return (EINVAL); 1086184610Salfred } 1087184610Salfred } 1088184610Salfred 1089194228Sthompsa error = usbd_transfer_setup(uaa->device, 1090184610Salfred &sc->sc_data_iface_index, sc->sc_bulk_xfer, 1091189265Sthompsa ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &sc->sc_mtx); 1092184610Salfred 1093184610Salfred if (error) { 1094184610Salfred device_printf(dev, "allocating BULK USB " 1095199816Sthompsa "transfers failed\n"); 1096184610Salfred return (EINVAL); 1097184610Salfred } 1098184610Salfred return (0); 1099184610Salfred} 1100184610Salfred 1101184610Salfredstatic void 1102192984Sthompsaufoma_start_read(struct ucom_softc *ucom) 1103184610Salfred{ 1104184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1105184610Salfred 1106184610Salfred /* start interrupt transfer */ 1107194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]); 1108184610Salfred 1109184610Salfred /* start data transfer */ 1110187579Stakawata if (sc->sc_nobulk) { 1111194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); 1112184610Salfred } else { 1113194228Sthompsa usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); 1114184610Salfred } 1115184610Salfred} 1116184610Salfred 1117184610Salfredstatic void 1118192984Sthompsaufoma_stop_read(struct ucom_softc *ucom) 1119184610Salfred{ 1120184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1121184610Salfred 1122184610Salfred /* stop interrupt transfer */ 1123194228Sthompsa usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]); 1124184610Salfred 1125184610Salfred /* stop data transfer */ 1126187579Stakawata if (sc->sc_nobulk) { 1127194228Sthompsa usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); 1128184610Salfred } else { 1129194228Sthompsa usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); 1130184610Salfred } 1131184610Salfred} 1132184610Salfred 1133184610Salfredstatic void 1134192984Sthompsaufoma_start_write(struct ucom_softc *ucom) 1135184610Salfred{ 1136184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1137184610Salfred 1138187579Stakawata if (sc->sc_nobulk) { 1139194228Sthompsa usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); 1140184610Salfred } else { 1141194228Sthompsa usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); 1142184610Salfred } 1143184610Salfred} 1144184610Salfred 1145184610Salfredstatic void 1146192984Sthompsaufoma_stop_write(struct ucom_softc *ucom) 1147184610Salfred{ 1148184610Salfred struct ufoma_softc *sc = ucom->sc_parent; 1149184610Salfred 1150187579Stakawata if (sc->sc_nobulk) { 1151194228Sthompsa usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); 1152184610Salfred } else { 1153194228Sthompsa usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); 1154184610Salfred } 1155184610Salfred} 1156186885Stakawata 1157194099Sthompsastatic struct umcpc_modetostr_tab{ 1158186885Stakawata int mode; 1159186885Stakawata char *str; 1160186885Stakawata}umcpc_modetostr_tab[]={ 1161186885Stakawata {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"}, 1162186885Stakawata {UMCPC_ACM_MODE_MODEM, "modem"}, 1163186885Stakawata {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"}, 1164186885Stakawata {UMCPC_ACM_MODE_OBEX, "obex"}, 1165186885Stakawata {UMCPC_ACM_MODE_VENDOR1, "vendor1"}, 1166186885Stakawata {UMCPC_ACM_MODE_VENDOR2, "vendor2"}, 1167186885Stakawata {UMCPC_ACM_MODE_UNLINKED, "unlinked"}, 1168186885Stakawata {0, NULL} 1169186885Stakawata}; 1170186885Stakawata 1171186885Stakawatastatic char *ufoma_mode_to_str(int mode) 1172186885Stakawata{ 1173186885Stakawata int i; 1174186885Stakawata for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ 1175186885Stakawata if(umcpc_modetostr_tab[i].mode == mode){ 1176186885Stakawata return umcpc_modetostr_tab[i].str; 1177186885Stakawata } 1178186885Stakawata } 1179186885Stakawata return NULL; 1180186885Stakawata} 1181186885Stakawata 1182186885Stakawatastatic int ufoma_str_to_mode(char *str) 1183186885Stakawata{ 1184186885Stakawata int i; 1185186885Stakawata for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ 1186186885Stakawata if(strcmp(str, umcpc_modetostr_tab[i].str)==0){ 1187186885Stakawata return umcpc_modetostr_tab[i].mode; 1188186885Stakawata } 1189186885Stakawata } 1190186885Stakawata return -1; 1191186885Stakawata} 1192186885Stakawata 1193186885Stakawatastatic int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS) 1194186885Stakawata{ 1195186885Stakawata struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; 1196186885Stakawata struct sbuf sb; 1197186885Stakawata int i; 1198186885Stakawata char *mode; 1199186885Stakawata 1200186885Stakawata sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND); 1201186885Stakawata for(i = 1; i < sc->sc_modetable[0]; i++){ 1202186885Stakawata mode = ufoma_mode_to_str(sc->sc_modetable[i]); 1203186885Stakawata if(mode !=NULL){ 1204186885Stakawata sbuf_cat(&sb, mode); 1205186885Stakawata }else{ 1206186885Stakawata sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]); 1207186885Stakawata } 1208186885Stakawata if(i < (sc->sc_modetable[0]-1)) 1209186885Stakawata sbuf_cat(&sb, ","); 1210186885Stakawata } 1211186885Stakawata sbuf_trim(&sb); 1212186885Stakawata sbuf_finish(&sb); 1213186885Stakawata sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 1214186885Stakawata sbuf_delete(&sb); 1215186885Stakawata 1216186885Stakawata return 0; 1217186885Stakawata} 1218186885Stakawatastatic int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS) 1219186885Stakawata{ 1220186885Stakawata struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; 1221186885Stakawata char *mode; 1222186885Stakawata char subbuf[]="(XXX)"; 1223186885Stakawata mode = ufoma_mode_to_str(sc->sc_currentmode); 1224186885Stakawata if(!mode){ 1225186885Stakawata mode = subbuf; 1226186885Stakawata snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode); 1227186885Stakawata } 1228186885Stakawata sysctl_handle_string(oidp, mode, strlen(mode), req); 1229186885Stakawata 1230186885Stakawata return 0; 1231186885Stakawata 1232186885Stakawata} 1233186885Stakawatastatic int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS) 1234186885Stakawata{ 1235186885Stakawata struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; 1236186885Stakawata char *mode; 1237186885Stakawata char subbuf[40]; 1238186885Stakawata int newmode; 1239186885Stakawata int error; 1240186885Stakawata int i; 1241186885Stakawata 1242186885Stakawata mode = ufoma_mode_to_str(sc->sc_modetoactivate); 1243186885Stakawata if(mode){ 1244186885Stakawata strncpy(subbuf, mode, sizeof(subbuf)); 1245186885Stakawata }else{ 1246186885Stakawata snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate); 1247186885Stakawata } 1248186885Stakawata error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req); 1249186885Stakawata if(error != 0 || req->newptr == NULL){ 1250186885Stakawata return error; 1251186885Stakawata } 1252186885Stakawata 1253186885Stakawata if((newmode = ufoma_str_to_mode(subbuf)) == -1){ 1254186885Stakawata return EINVAL; 1255186885Stakawata } 1256186885Stakawata 1257186885Stakawata for(i = 1 ; i < sc->sc_modetable[0] ; i++){ 1258186885Stakawata if(sc->sc_modetable[i] == newmode){ 1259186885Stakawata sc->sc_modetoactivate = newmode; 1260186885Stakawata return 0; 1261186885Stakawata } 1262186885Stakawata } 1263186885Stakawata 1264186885Stakawata return EINVAL; 1265186885Stakawata} 1266197570Sthompsa 1267197570Sthompsastatic void 1268197570Sthompsaufoma_poll(struct ucom_softc *ucom) 1269197570Sthompsa{ 1270197570Sthompsa struct ufoma_softc *sc = ucom->sc_parent; 1271197570Sthompsa usbd_transfer_poll(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); 1272197570Sthompsa usbd_transfer_poll(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); 1273197570Sthompsa} 1274