13227Syq193411 /*
23227Syq193411 * CDDL HEADER START
33227Syq193411 *
43227Syq193411 * The contents of this file are subject to the terms of the
53227Syq193411 * Common Development and Distribution License (the "License").
63227Syq193411 * You may not use this file except in compliance with the License.
73227Syq193411 *
83227Syq193411 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93227Syq193411 * or http://www.opensolaris.org/os/licensing.
103227Syq193411 * See the License for the specific language governing permissions
113227Syq193411 * and limitations under the License.
123227Syq193411 *
133227Syq193411 * When distributing Covered Code, include this CDDL HEADER in each
143227Syq193411 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153227Syq193411 * If applicable, add the following below this CDDL HEADER, with the
163227Syq193411 * fields enclosed by brackets "[]" replaced with your own identifying
173227Syq193411 * information: Portions Copyright [yyyy] [name of copyright owner]
183227Syq193411 *
193227Syq193411 * CDDL HEADER END
203227Syq193411 */
219354STim.Marsland@Sun.COM
223227Syq193411 /*
23*12024SRaymond.Chen@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
243227Syq193411 * Use is subject to license terms.
253227Syq193411 */
263227Syq193411
273227Syq193411 /*
283227Syq193411 * USB Serial CDC ACM driver
293227Syq193411 *
303227Syq193411 * 1. General Concepts
313227Syq193411 * -------------------
323227Syq193411 *
333227Syq193411 * 1.1 Overview
343227Syq193411 * ------------
353227Syq193411 * This driver supports devices that comply with the USB Communication
363227Syq193411 * Device Class Abstract Control Model (USB CDC ACM) specification,
373227Syq193411 * which is available at http://www.usb.org. Given the broad nature
383227Syq193411 * of communication equipment, this driver supports the following
393227Syq193411 * types of devices:
405541Slg150142 * + Telecommunications devices: analog modems, mobile phones;
415541Slg150142 * + Networking devices: cable modems;
423227Syq193411 * Except the above mentioned acm devices, this driver also supports
433227Syq193411 * some devices which provide modem-like function and have pairs of
443227Syq193411 * bulk in/out pipes.
453227Syq193411 *
463227Syq193411 * There are three classes that make up the definition for communication
473227Syq193411 * devices: the Communication Device Class, the Communication Interface
483227Syq193411 * Class and the Data Interface Class. The Communication Device Class
493227Syq193411 * is a device level definition and is used by the host to properly
503227Syq193411 * identify a communication device that may present several different
513227Syq193411 * types of interfaces. The Communication Interface Class defines a
523227Syq193411 * general-purpose mechanism that can be used to enable all types of
533227Syq193411 * communication services on the Universal Serial Bus (USB). The Data
543227Syq193411 * Interface Class defines a general-purpose mechanism to enable bulk
553227Syq193411 * transfer on the USB when the data does not meet the requirements
563227Syq193411 * for any other class.
573227Syq193411 *
583227Syq193411 * 1.2 Interface Definitions
593227Syq193411 * -------------------------
603227Syq193411 * Communication Class Interface is used for device management and,
613227Syq193411 * optionally, call management. Device management includes the requests
623227Syq193411 * that manage the operational state of a device, the device responses,
633227Syq193411 * and event notifications. In Abstract Control Model, the device can
643227Syq193411 * provide an internal implementation of call management over the Data
653227Syq193411 * Class interface or the Communication Class interface.
663227Syq193411 *
673227Syq193411 * The Data Class defines a data interface as an interface with a class
683227Syq193411 * type of Data Class. Data transmission on a communication device is
693227Syq193411 * not restricted to interfaces using the Data Class. Rather, a data
703227Syq193411 * interface is used to transmit and/or receive data that is not
713227Syq193411 * defined by any other class. The data could be:
725541Slg150142 * + Some form of raw data from a communication line.
735541Slg150142 * + Legacy modem data.
745541Slg150142 * + Data using a proprietary format.
753227Syq193411 *
763227Syq193411 * 1.3 Endpoint Requirements
773227Syq193411 * -------------------------
783227Syq193411 * The Communication Class interface requires one endpoint, the management
793227Syq193411 * element. Optionally, it can have an additional endpoint, the notification
803227Syq193411 * element. The management element uses the default endpoint for all
813227Syq193411 * standard and Communication Class-specific requests. The notification
823227Syq193411 * element normally uses an interrupt endpoint.
833227Syq193411 *
843227Syq193411 * The type of endpoints belonging to a Data Class interface are restricted
853227Syq193411 * to bulk, and are expected to exist in pairs of the same type (one In and
863227Syq193411 * one Out).
873227Syq193411 *
883227Syq193411 * 1.4 ACM Function Characteristics
893227Syq193411 * --------------------------------
903227Syq193411 * With Abstract Control Model, the USB device understands standard
913227Syq193411 * V.25ter (AT) commands. The device contains a Datapump and micro-
923227Syq193411 * controller that handles the AT commands and relay controls. The
933227Syq193411 * device uses both a Data Class interface and a Communication Class.
943227Syq193411 * interface.
953227Syq193411 *
963227Syq193411 * A Communication Class interface of type Abstract Control Model will
973227Syq193411 * consist of a minimum of two pipes; one is used to implement the
983227Syq193411 * management element and the other to implement a notification element.
993227Syq193411 * In addition, the device can use two pipes to implement channels over
1003227Syq193411 * which to carry unspecified data, typically over a Data Class interface.
1013227Syq193411 *
1023227Syq193411 * 1.5 ACM Serial Emulation
1033227Syq193411 * ------------------------
1043227Syq193411 * The Abstract Control Model can bridge the gap between legacy modem
1053227Syq193411 * devices and USB devices. To support certain types of legacy applications,
1063227Syq193411 * two problems need to be addressed. The first is supporting specific
1073227Syq193411 * legacy control signals and state variables which are addressed
1083227Syq193411 * directly by the various carrier modulation standards. To support these
1093227Syq193411 * requirement, additional requests and notifications have been created.
1103227Syq193411 * Please refer to macro, beginning with USB_CDC_REQ_* and
1113227Syq193411 * USB_CDC_NOTIFICATION_*.
1123227Syq193411 *
1133227Syq193411 * The second significant item which is needed to bridge the gap between
1143227Syq193411 * legacy modem designs and the Abstract Control Model is a means to
1153227Syq193411 * multiplex call control (AT commands) on the Data Class interface.
1163227Syq193411 * Legacy modem designs are limited by only supporting one channel for
1173227Syq193411 * both "AT" commands and the actual data. To allow this type of
1183227Syq193411 * functionality, the device must have a means to specify this limitation
1193227Syq193411 * to the host.
1203227Syq193411 *
1213227Syq193411 * When describing this type of device, the Communication Class interface
1223227Syq193411 * would still specify a Abstract Control Model, but call control would
1233227Syq193411 * actually occur over the Data Class interface. To describe this
1243227Syq193411 * particular characteristic, the Call Management Functional Descriptor
1253227Syq193411 * would have bit D1 of bmCapabilities set.
1263227Syq193411 *
1273227Syq193411 * 1.6 Other Bulk In/Out Devices
1283227Syq193411 * -----------------------------
1293227Syq193411 * Some devices don't conform to USB CDC specification, but they provide
1303227Syq193411 * modem-like function and have pairs of bulk in/out pipes. This driver
1313227Syq193411 * supports this kind of device and exports term nodes by their pipes.
1323227Syq193411 *
1333227Syq193411 * 2. Implementation
1343227Syq193411 * -----------------
1353227Syq193411 *
1363227Syq193411 * 2.1 Overview
1373227Syq193411 * ------------
1383227Syq193411 * It is a device-specific driver (DSD) working with USB generic serial
1393227Syq193411 * driver (GSD). It implements the USB-to-serial device-specific driver
1403227Syq193411 * interface (DSDI) which is offered by GSD. The interface is defined
1413227Syq193411 * by ds_ops_t structure.
1423227Syq193411 *
1433227Syq193411 * 2.2 Port States
1443227Syq193411 * ---------------
1453227Syq193411 * For USB CDC ACM devices, this driver is attached to its interface,
1463227Syq193411 * and exports one port for each interface. For other modem-like devices,
1473227Syq193411 * this driver can dynamically find the ports in the current device,
1483227Syq193411 * and export one port for each pair bulk in/out pipes. Each port can
1493227Syq193411 * be operated independently.
1503227Syq193411 *
1513227Syq193411 * port_state:
1523227Syq193411 *
1533227Syq193411 * attach_ports
1543227Syq193411 * |
1553227Syq193411 * |
1563227Syq193411 * |
1573227Syq193411 * v
1583227Syq193411 * USBSACM_PORT_CLOSED
1593227Syq193411 * | ^
1603227Syq193411 * | |
1613227Syq193411 * V |
1623227Syq193411 * open_port close_port
1633227Syq193411 * | ^
1643227Syq193411 * | |
1653227Syq193411 * V |
1663227Syq193411 * USBSACM_PORT_OPEN
1673227Syq193411 *
1683227Syq193411 *
1693227Syq193411 * 2.3 Pipe States
1703227Syq193411 * ---------------
1713227Syq193411 * Each port has its own bulk in/out pipes and some ports could also have
1723227Syq193411 * its own interrupt pipes (traced by usbsacm_port structure), which are
1733227Syq193411 * opened during attach. The pipe status is as following:
1743227Syq193411 *
1753227Syq193411 * pipe_state:
1763227Syq193411 *
1773227Syq193411 * usbsacm_init_alloc_ports usbsacm_free_ports
1783227Syq193411 * | ^
1793227Syq193411 * v |
1805541Slg150142 * |---->------ USBSACM_PORT_CLOSED ------>------+
1813227Syq193411 * ^ |
1823227Syq193411 * | reconnect/resume/open_port
1833227Syq193411 * | |
1843227Syq193411 * disconnect/suspend/close_port |
1853227Syq193411 * | v
1863227Syq193411 * +------<------ USBSACM_PIPE_IDLE ------<------|
1873227Syq193411 * | |
1883227Syq193411 * V ^
1893227Syq193411 * | |
1903227Syq193411 * +-----------------+ +-----------+
1913227Syq193411 * | |
1923227Syq193411 * V ^
1933227Syq193411 * | |
1943227Syq193411 * rx_start/tx_start----->------failed------->---------|
1953227Syq193411 * | |
1963227Syq193411 * | bulkin_cb/bulkout_cb
1973227Syq193411 * V |
1983227Syq193411 * | ^
1993227Syq193411 * | |
2003227Syq193411 * +----->----- USBSACM_PIPE_BUSY ---->------+
2013227Syq193411 *
2023227Syq193411 *
2033227Syq193411 * To get its status in a timely way, acm driver can get the status
2043227Syq193411 * of the device by polling the interrupt pipe.
2053227Syq193411 *
2063227Syq193411 */
2073227Syq193411
2083227Syq193411 #include <sys/types.h>
2093227Syq193411 #include <sys/param.h>
2103227Syq193411 #include <sys/conf.h>
2113227Syq193411 #include <sys/stream.h>
2123227Syq193411 #include <sys/strsun.h>
2133227Syq193411 #include <sys/termio.h>
2143227Syq193411 #include <sys/termiox.h>
2153227Syq193411 #include <sys/ddi.h>
2163227Syq193411 #include <sys/sunddi.h>
2173227Syq193411 #include <sys/byteorder.h>
2183227Syq193411 #define USBDRV_MAJOR_VER 2
2193227Syq193411 #define USBDRV_MINOR_VER 0
2203227Syq193411 #include <sys/usb/usba.h>
2213227Syq193411 #include <sys/usb/usba/usba_types.h>
2223227Syq193411 #include <sys/usb/clients/usbser/usbser.h>
2233227Syq193411 #include <sys/usb/clients/usbser/usbser_dsdi.h>
2243227Syq193411 #include <sys/usb/clients/usbcdc/usb_cdc.h>
2253227Syq193411 #include <sys/usb/clients/usbser/usbsacm/usbsacm.h>
2263227Syq193411
2273227Syq193411 /* devops entry points */
2283227Syq193411 static int usbsacm_attach(dev_info_t *, ddi_attach_cmd_t);
2293227Syq193411 static int usbsacm_detach(dev_info_t *, ddi_detach_cmd_t);
2305541Slg150142 static int usbsacm_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
2313227Syq193411 void **);
2323227Syq193411 static int usbsacm_open(queue_t *, dev_t *, int, int, cred_t *);
2333227Syq193411
2343227Syq193411 /* DSD operations */
2353227Syq193411 static int usbsacm_ds_attach(ds_attach_info_t *);
2363227Syq193411 static void usbsacm_ds_detach(ds_hdl_t);
2373227Syq193411 static int usbsacm_ds_register_cb(ds_hdl_t, uint_t, ds_cb_t *);
2383227Syq193411 static void usbsacm_ds_unregister_cb(ds_hdl_t, uint_t);
2393227Syq193411 static int usbsacm_ds_open_port(ds_hdl_t, uint_t);
2403227Syq193411 static int usbsacm_ds_close_port(ds_hdl_t, uint_t);
2413227Syq193411
2423227Syq193411 /* standard UART operations */
2433227Syq193411 static int usbsacm_ds_set_port_params(ds_hdl_t, uint_t,
2443227Syq193411 ds_port_params_t *);
2453227Syq193411 static int usbsacm_ds_set_modem_ctl(ds_hdl_t, uint_t, int, int);
2463227Syq193411 static int usbsacm_ds_get_modem_ctl(ds_hdl_t, uint_t, int, int *);
2473227Syq193411 static int usbsacm_ds_break_ctl(ds_hdl_t, uint_t, int);
2483227Syq193411
2493227Syq193411 /* data xfer */
2503227Syq193411 static int usbsacm_ds_tx(ds_hdl_t, uint_t, mblk_t *);
2513227Syq193411 static mblk_t *usbsacm_ds_rx(ds_hdl_t, uint_t);
2523227Syq193411 static void usbsacm_ds_stop(ds_hdl_t, uint_t, int);
2533227Syq193411 static void usbsacm_ds_start(ds_hdl_t, uint_t, int);
2543227Syq193411
2553227Syq193411 /* fifo operations */
2563227Syq193411 static int usbsacm_ds_fifo_flush(ds_hdl_t, uint_t, int);
2573227Syq193411 static int usbsacm_ds_fifo_drain(ds_hdl_t, uint_t, int);
2583227Syq193411 static int usbsacm_wait_tx_drain(usbsacm_port_t *, int);
2593227Syq193411 static int usbsacm_fifo_flush_locked(usbsacm_state_t *, uint_t, int);
2603227Syq193411
2613227Syq193411 /* power management and CPR */
2623227Syq193411 static int usbsacm_ds_suspend(ds_hdl_t);
2633227Syq193411 static int usbsacm_ds_resume(ds_hdl_t);
2643227Syq193411 static int usbsacm_ds_disconnect(ds_hdl_t);
2653227Syq193411 static int usbsacm_ds_reconnect(ds_hdl_t);
2663227Syq193411 static int usbsacm_ds_usb_power(ds_hdl_t, int, int, int *);
2673227Syq193411 static int usbsacm_create_pm_components(usbsacm_state_t *);
2683227Syq193411 static void usbsacm_destroy_pm_components(usbsacm_state_t *);
2693227Syq193411 static void usbsacm_pm_set_busy(usbsacm_state_t *);
2703227Syq193411 static void usbsacm_pm_set_idle(usbsacm_state_t *);
2713227Syq193411 static int usbsacm_pwrlvl0(usbsacm_state_t *);
2723227Syq193411 static int usbsacm_pwrlvl1(usbsacm_state_t *);
2733227Syq193411 static int usbsacm_pwrlvl2(usbsacm_state_t *);
2743227Syq193411 static int usbsacm_pwrlvl3(usbsacm_state_t *);
2753227Syq193411
2763227Syq193411 /* event handling */
2773227Syq193411 /* pipe callbacks */
2783227Syq193411 static void usbsacm_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
2793227Syq193411 static void usbsacm_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
2803227Syq193411
2813227Syq193411 /* interrupt pipe */
2823227Syq193411 static void usbsacm_pipe_start_polling(usbsacm_port_t *acmp);
2833227Syq193411 static void usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
2843227Syq193411 static void usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
2853227Syq193411 static void usbsacm_parse_intr_data(usbsacm_port_t *acmp, mblk_t *data);
2863227Syq193411
2873227Syq193411 /* Utility functions */
2883227Syq193411 /* data transfer routines */
2893227Syq193411 static int usbsacm_rx_start(usbsacm_port_t *);
2903227Syq193411 static void usbsacm_tx_start(usbsacm_port_t *);
2913227Syq193411 static int usbsacm_send_data(usbsacm_port_t *, mblk_t *);
2923227Syq193411
2933227Syq193411 /* Initialize or release resources */
2943227Syq193411 static int usbsacm_init_alloc_ports(usbsacm_state_t *);
2953227Syq193411 static void usbsacm_free_ports(usbsacm_state_t *);
2963227Syq193411 static void usbsacm_cleanup(usbsacm_state_t *);
2973227Syq193411
2983227Syq193411 /* analysis functional descriptors */
2993227Syq193411 static int usbsacm_get_descriptors(usbsacm_state_t *);
3003227Syq193411
3013227Syq193411 /* hotplug */
3023227Syq193411 static int usbsacm_restore_device_state(usbsacm_state_t *);
3033227Syq193411 static int usbsacm_restore_port_state(usbsacm_state_t *);
3043227Syq193411
3053227Syq193411 /* pipe operations */
3063227Syq193411 static int usbsacm_open_port_pipes(usbsacm_port_t *);
3073227Syq193411 static void usbsacm_close_port_pipes(usbsacm_port_t *);
3083227Syq193411 static void usbsacm_close_pipes(usbsacm_state_t *);
3093227Syq193411 static void usbsacm_disconnect_pipes(usbsacm_state_t *);
3103227Syq193411 static int usbsacm_reconnect_pipes(usbsacm_state_t *);
3113227Syq193411
3123227Syq193411 /* vendor-specific commands */
3133227Syq193411 static int usbsacm_req_write(usbsacm_port_t *, uchar_t, uint16_t,
3143227Syq193411 mblk_t **);
3153227Syq193411 static int usbsacm_set_line_coding(usbsacm_port_t *,
3163227Syq193411 usb_cdc_line_coding_t *);
3173227Syq193411 static void usbsacm_mctl2reg(int mask, int val, uint8_t *);
3183227Syq193411 static int usbsacm_reg2mctl(uint8_t);
3193227Syq193411
3203227Syq193411 /* misc */
3213227Syq193411 static void usbsacm_put_tail(mblk_t **, mblk_t *);
3223227Syq193411 static void usbsacm_put_head(mblk_t **, mblk_t *);
3233227Syq193411
3243227Syq193411
3253227Syq193411 /*
3263227Syq193411 * Standard STREAMS driver definitions
3273227Syq193411 */
3283227Syq193411 struct module_info usbsacm_modinfo = {
3293227Syq193411 0, /* module id */
3303227Syq193411 "usbsacm", /* module name */
3313227Syq193411 USBSER_MIN_PKTSZ, /* min pkt size */
3323227Syq193411 USBSER_MAX_PKTSZ, /* max pkt size */
3333227Syq193411 USBSER_HIWAT, /* hi watermark */
3343227Syq193411 USBSER_LOWAT /* low watermark */
3353227Syq193411 };
3363227Syq193411
3373227Syq193411 static struct qinit usbsacm_rinit = {
3383227Syq193411 NULL,
3393227Syq193411 usbser_rsrv,
3403227Syq193411 usbsacm_open,
3413227Syq193411 usbser_close,
3423227Syq193411 NULL,
3433227Syq193411 &usbsacm_modinfo,
3443227Syq193411 NULL
3453227Syq193411 };
3463227Syq193411
3473227Syq193411 static struct qinit usbsacm_winit = {
3483227Syq193411 usbser_wput,
3493227Syq193411 usbser_wsrv,
3503227Syq193411 NULL,
3513227Syq193411 NULL,
3523227Syq193411 NULL,
3533227Syq193411 &usbsacm_modinfo,
3543227Syq193411 NULL
3553227Syq193411 };
3563227Syq193411
3573227Syq193411
3583227Syq193411 struct streamtab usbsacm_str_info = {
3593227Syq193411 &usbsacm_rinit, &usbsacm_winit, NULL, NULL
3603227Syq193411 };
3613227Syq193411
3623227Syq193411 /* cb_ops structure */
3633227Syq193411 static struct cb_ops usbsacm_cb_ops = {
3643227Syq193411 nodev, /* cb_open */
3653227Syq193411 nodev, /* cb_close */
3663227Syq193411 nodev, /* cb_strategy */
3673227Syq193411 nodev, /* cb_print */
3683227Syq193411 nodev, /* cb_dump */
3693227Syq193411 nodev, /* cb_read */
3703227Syq193411 nodev, /* cb_write */
3713227Syq193411 nodev, /* cb_ioctl */
3723227Syq193411 nodev, /* cb_devmap */
3733227Syq193411 nodev, /* cb_mmap */
3743227Syq193411 nodev, /* cb_segmap */
3753227Syq193411 nochpoll, /* cb_chpoll */
3763227Syq193411 ddi_prop_op, /* cb_prop_op */
3773227Syq193411 &usbsacm_str_info, /* cb_stream */
3783227Syq193411 (int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG) /* cb_flag */
3793227Syq193411 };
3803227Syq193411
3813227Syq193411 /* dev_ops structure */
3823227Syq193411 struct dev_ops usbsacm_ops = {
3833227Syq193411 DEVO_REV, /* devo_rev */
3843227Syq193411 0, /* devo_refcnt */
3853227Syq193411 usbsacm_getinfo, /* devo_getinfo */
3863227Syq193411 nulldev, /* devo_identify */
3873227Syq193411 nulldev, /* devo_probe */
3883227Syq193411 usbsacm_attach, /* devo_attach */
3893227Syq193411 usbsacm_detach, /* devo_detach */
3903227Syq193411 nodev, /* devo_reset */
3913227Syq193411 &usbsacm_cb_ops, /* devo_cb_ops */
3923227Syq193411 (struct bus_ops *)NULL, /* devo_bus_ops */
3937656SSherry.Moore@Sun.COM usbser_power, /* devo_power */
3948688SRaymond.Chen@Sun.COM ddi_quiesce_not_needed, /* devo_quiesce */
3953227Syq193411 };
3963227Syq193411
3973227Syq193411 extern struct mod_ops mod_driverops;
3983227Syq193411 /* modldrv structure */
3993227Syq193411 static struct modldrv modldrv = {
4003227Syq193411 &mod_driverops, /* type of module - driver */
4017425SGongtian.Zhao@Sun.COM "USB Serial CDC ACM driver",
4023227Syq193411 &usbsacm_ops,
4033227Syq193411 };
4043227Syq193411
4053227Syq193411 /* modlinkage structure */
4063227Syq193411 static struct modlinkage modlinkage = {
4073227Syq193411 MODREV_1,
4083227Syq193411 &modldrv,
4093227Syq193411 NULL
4103227Syq193411 };
4113227Syq193411
4123227Syq193411 static void *usbsacm_statep; /* soft state */
4133227Syq193411
4143227Syq193411 /*
4153227Syq193411 * DSD definitions
4163227Syq193411 */
4177767SJohn.Beck@Sun.COM static ds_ops_t usbsacm_ds_ops = {
4183227Syq193411 DS_OPS_VERSION,
4193227Syq193411 usbsacm_ds_attach,
4203227Syq193411 usbsacm_ds_detach,
4213227Syq193411 usbsacm_ds_register_cb,
4223227Syq193411 usbsacm_ds_unregister_cb,
4233227Syq193411 usbsacm_ds_open_port,
4243227Syq193411 usbsacm_ds_close_port,
4253227Syq193411 usbsacm_ds_usb_power,
4263227Syq193411 usbsacm_ds_suspend,
4273227Syq193411 usbsacm_ds_resume,
4283227Syq193411 usbsacm_ds_disconnect,
4293227Syq193411 usbsacm_ds_reconnect,
4303227Syq193411 usbsacm_ds_set_port_params,
4313227Syq193411 usbsacm_ds_set_modem_ctl,
4323227Syq193411 usbsacm_ds_get_modem_ctl,
4333227Syq193411 usbsacm_ds_break_ctl,
4343227Syq193411 NULL, /* NULL if h/w doesn't support loopback */
4353227Syq193411 usbsacm_ds_tx,
4363227Syq193411 usbsacm_ds_rx,
4373227Syq193411 usbsacm_ds_stop,
4383227Syq193411 usbsacm_ds_start,
4393227Syq193411 usbsacm_ds_fifo_flush,
4403227Syq193411 usbsacm_ds_fifo_drain
4413227Syq193411 };
4423227Syq193411
4433227Syq193411 /*
4443227Syq193411 * baud code -> baud rate (0 means unsupported rate)
4453227Syq193411 */
4463227Syq193411 static int usbsacm_speedtab[] = {
4473227Syq193411 0, /* B0 */
4483227Syq193411 50, /* B50 */
4493227Syq193411 75, /* B75 */
4503227Syq193411 110, /* B110 */
4513227Syq193411 134, /* B134 */
4523227Syq193411 150, /* B150 */
4533227Syq193411 200, /* B200 */
4543227Syq193411 300, /* B300 */
4553227Syq193411 600, /* B600 */
4563227Syq193411 1200, /* B1200 */
4573227Syq193411 1800, /* B1800 */
4583227Syq193411 2400, /* B2400 */
4593227Syq193411 4800, /* B4800 */
4603227Syq193411 9600, /* B9600 */
4613227Syq193411 19200, /* B19200 */
4623227Syq193411 38400, /* B38400 */
4633227Syq193411 57600, /* B57600 */
4643227Syq193411 76800, /* B76800 */
4653227Syq193411 115200, /* B115200 */
4663227Syq193411 153600, /* B153600 */
4673227Syq193411 230400, /* B230400 */
4683227Syq193411 307200, /* B307200 */
4699354STim.Marsland@Sun.COM 460800, /* B460800 */
4709354STim.Marsland@Sun.COM 921600 /* B921600 */
4713227Syq193411 };
4723227Syq193411
4733227Syq193411
4743227Syq193411 static uint_t usbsacm_errlevel = USB_LOG_L4;
4753227Syq193411 static uint_t usbsacm_errmask = 0xffffffff;
4763227Syq193411 static uint_t usbsacm_instance_debug = (uint_t)-1;
4773227Syq193411
4783227Syq193411
4793227Syq193411 /*
4803227Syq193411 * usbsacm driver's entry points
4813227Syq193411 * -----------------------------
4823227Syq193411 */
4833227Syq193411 /*
4843227Syq193411 * Module-wide initialization routine.
4853227Syq193411 */
4863227Syq193411 int
_init(void)4873227Syq193411 _init(void)
4883227Syq193411 {
4893227Syq193411 int error;
4903227Syq193411
4913227Syq193411 if ((error = mod_install(&modlinkage)) == 0) {
4923227Syq193411
4933227Syq193411 error = ddi_soft_state_init(&usbsacm_statep,
4943227Syq193411 usbser_soft_state_size(), 1);
4953227Syq193411 }
4963227Syq193411
4973227Syq193411 return (error);
4983227Syq193411 }
4993227Syq193411
5003227Syq193411
5013227Syq193411 /*
5023227Syq193411 * Module-wide tear-down routine.
5033227Syq193411 */
5043227Syq193411 int
_fini(void)5053227Syq193411 _fini(void)
5063227Syq193411 {
5073227Syq193411 int error;
5083227Syq193411
5093227Syq193411 if ((error = mod_remove(&modlinkage)) == 0) {
5103227Syq193411 ddi_soft_state_fini(&usbsacm_statep);
5113227Syq193411 }
5123227Syq193411
5133227Syq193411 return (error);
5143227Syq193411 }
5153227Syq193411
5163227Syq193411
5173227Syq193411 int
_info(struct modinfo * modinfop)5183227Syq193411 _info(struct modinfo *modinfop)
5193227Syq193411 {
5203227Syq193411 return (mod_info(&modlinkage, modinfop));
5213227Syq193411 }
5223227Syq193411
5233227Syq193411
5243227Syq193411 /*
5253227Syq193411 * Device configuration entry points
5263227Syq193411 */
5273227Syq193411 static int
usbsacm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5283227Syq193411 usbsacm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5293227Syq193411 {
5307767SJohn.Beck@Sun.COM return (usbser_attach(dip, cmd, usbsacm_statep, &usbsacm_ds_ops));
5313227Syq193411 }
5323227Syq193411
5333227Syq193411
5343227Syq193411 static int
usbsacm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5353227Syq193411 usbsacm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5363227Syq193411 {
5373227Syq193411 return (usbser_detach(dip, cmd, usbsacm_statep));
5383227Syq193411 }
5393227Syq193411
5403227Syq193411
5413227Syq193411 int
usbsacm_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)5423227Syq193411 usbsacm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
5433227Syq193411 void **result)
5443227Syq193411 {
5453227Syq193411 return (usbser_getinfo(dip, infocmd, arg, result, usbsacm_statep));
5463227Syq193411 }
5473227Syq193411
5483227Syq193411
5493227Syq193411 static int
usbsacm_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)5503227Syq193411 usbsacm_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
5513227Syq193411 {
5523227Syq193411 return (usbser_open(rq, dev, flag, sflag, cr, usbsacm_statep));
5533227Syq193411 }
5543227Syq193411
5553227Syq193411 /*
5563227Syq193411 * usbsacm_ds_detach:
5573227Syq193411 * attach device instance, called from GSD attach
5583227Syq193411 * initialize state and device, including:
5593227Syq193411 * state variables, locks, device node
5603227Syq193411 * device registration with system
5613227Syq193411 * power management
5623227Syq193411 */
5633227Syq193411 static int
usbsacm_ds_attach(ds_attach_info_t * aip)5643227Syq193411 usbsacm_ds_attach(ds_attach_info_t *aip)
5653227Syq193411 {
5663227Syq193411 usbsacm_state_t *acmp;
5673227Syq193411
5683227Syq193411 acmp = (usbsacm_state_t *)kmem_zalloc(sizeof (usbsacm_state_t),
5693227Syq193411 KM_SLEEP);
5703227Syq193411 acmp->acm_dip = aip->ai_dip;
5713227Syq193411 acmp->acm_usb_events = aip->ai_usb_events;
5723227Syq193411 acmp->acm_ports = NULL;
5733227Syq193411 *aip->ai_hdl = (ds_hdl_t)acmp;
5743227Syq193411
5753227Syq193411 /* registers usbsacm with the USBA framework */
5763227Syq193411 if (usb_client_attach(acmp->acm_dip, USBDRV_VERSION,
5773227Syq193411 0) != USB_SUCCESS) {
5783227Syq193411
5793227Syq193411 goto fail;
5803227Syq193411 }
5813227Syq193411
5823227Syq193411 /* Get the configuration information of device */
5833227Syq193411 if (usb_get_dev_data(acmp->acm_dip, &acmp->acm_dev_data,
5843227Syq193411 USB_PARSE_LVL_CFG, 0) != USB_SUCCESS) {
5853227Syq193411
5863227Syq193411 goto fail;
5873227Syq193411 }
5883227Syq193411 acmp->acm_def_ph = acmp->acm_dev_data->dev_default_ph;
5893227Syq193411 acmp->acm_dev_state = USB_DEV_ONLINE;
5903227Syq193411 mutex_init(&acmp->acm_mutex, NULL, MUTEX_DRIVER,
5913227Syq193411 acmp->acm_dev_data->dev_iblock_cookie);
5923227Syq193411
5933227Syq193411 acmp->acm_lh = usb_alloc_log_hdl(acmp->acm_dip, "usbsacm",
5943227Syq193411 &usbsacm_errlevel, &usbsacm_errmask, &usbsacm_instance_debug, 0);
5953227Syq193411
5963227Syq193411 /* Create power management components */
5973227Syq193411 if (usbsacm_create_pm_components(acmp) != USB_SUCCESS) {
5983227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
5993227Syq193411 "usbsacm_ds_attach: create pm components failed.");
6003227Syq193411
6013227Syq193411 goto fail;
6023227Syq193411 }
6033227Syq193411
6043227Syq193411 /* Register to get callbacks for USB events */
6053227Syq193411 if (usb_register_event_cbs(acmp->acm_dip, acmp->acm_usb_events, 0)
6063227Syq193411 != USB_SUCCESS) {
6073227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6083227Syq193411 "usbsacm_ds_attach: register event callback failed.");
6093227Syq193411
6103227Syq193411 goto fail;
6113227Syq193411 }
6123227Syq193411
6133227Syq193411 /*
6143227Syq193411 * If devices conform to acm spec, driver will attach using class id;
6153227Syq193411 * if not, using device id.
6163227Syq193411 */
6173227Syq193411 if ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
6183227Syq193411 "usbif,class2.2") == 0) ||
6193227Syq193411 ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
6203227Syq193411 "usb,class2.2.0") == 0))) {
6213227Syq193411
6223227Syq193411 acmp->acm_compatibility = B_TRUE;
6233227Syq193411 } else {
6243227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6253227Syq193411 "usbsacm_ds_attach: A nonstandard device is attaching to "
6263227Syq193411 "usbsacm driver. This device doesn't conform to "
6273227Syq193411 "usb cdc spec.");
6283227Syq193411
6293227Syq193411 acmp->acm_compatibility = B_FALSE;
6303227Syq193411 }
6313227Syq193411
6323227Syq193411 /* initialize state variables */
6333227Syq193411 if (usbsacm_init_alloc_ports(acmp) != USB_SUCCESS) {
6343227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6353227Syq193411 "usbsacm_ds_attach: initialize port structure failed.");
6363227Syq193411
6373227Syq193411 goto fail;
6383227Syq193411 }
6393227Syq193411 *aip->ai_port_cnt = acmp->acm_port_cnt;
6403227Syq193411
6413227Syq193411 /* Get max data size of bulk transfer */
6423227Syq193411 if (usb_pipe_get_max_bulk_transfer_size(acmp->acm_dip,
6433227Syq193411 &acmp->acm_xfer_sz) != USB_SUCCESS) {
6443227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6453227Syq193411 "usbsacm_ds_attach: get max size of transfer failed.");
6463227Syq193411
6473227Syq193411 goto fail;
6483227Syq193411 }
6493227Syq193411
6503227Syq193411 return (USB_SUCCESS);
6513227Syq193411 fail:
6523227Syq193411 usbsacm_cleanup(acmp);
6533227Syq193411
6543227Syq193411 return (USB_FAILURE);
6553227Syq193411 }
6563227Syq193411
6573227Syq193411
6583227Syq193411 /*
6593227Syq193411 * usbsacm_ds_detach:
6603227Syq193411 * detach device instance, called from GSD detach
6613227Syq193411 */
6623227Syq193411 static void
usbsacm_ds_detach(ds_hdl_t hdl)6633227Syq193411 usbsacm_ds_detach(ds_hdl_t hdl)
6643227Syq193411 {
6653227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
6663227Syq193411
6673227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
6683227Syq193411 "usbsacm_ds_detach:");
6693227Syq193411
6703227Syq193411 usbsacm_close_pipes(acmp);
6713227Syq193411 usbsacm_cleanup(acmp);
6723227Syq193411 }
6733227Syq193411
6743227Syq193411
6753227Syq193411 /*
6763227Syq193411 * usbsacm_ds_register_cb:
6773227Syq193411 * GSD routine call ds_register_cb to register interrupt callbacks
6783227Syq193411 * for the given port
6793227Syq193411 */
6803227Syq193411 /*ARGSUSED*/
6813227Syq193411 static int
usbsacm_ds_register_cb(ds_hdl_t hdl,uint_t port_num,ds_cb_t * cb)6823227Syq193411 usbsacm_ds_register_cb(ds_hdl_t hdl, uint_t port_num, ds_cb_t *cb)
6833227Syq193411 {
6843227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
6853227Syq193411 usbsacm_port_t *acm_port;
6863227Syq193411
6873227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
6883227Syq193411 "usbsacm_ds_register_cb: acmp = 0x%p port_num = %d",
6896898Sfb209375 (void *)acmp, port_num);
6903227Syq193411
6913227Syq193411 /* Check if port number is greater than actual port number. */
6923227Syq193411 if (port_num >= acmp->acm_port_cnt) {
6933227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6943227Syq193411 "usbsacm_ds_register_cb: port number is wrong.");
6953227Syq193411
6963227Syq193411 return (USB_FAILURE);
6973227Syq193411 }
6983227Syq193411 acm_port = &acmp->acm_ports[port_num];
6993227Syq193411 acm_port->acm_cb = *cb;
7003227Syq193411
7013227Syq193411 return (USB_SUCCESS);
7023227Syq193411 }
7033227Syq193411
7043227Syq193411
7053227Syq193411 /*
7063227Syq193411 * usbsacm_ds_unregister_cb:
7073227Syq193411 * GSD routine call ds_unregister_cb to unregister
7083227Syq193411 * interrupt callbacks for the given port
7093227Syq193411 */
7103227Syq193411 /*ARGSUSED*/
7113227Syq193411 static void
usbsacm_ds_unregister_cb(ds_hdl_t hdl,uint_t port_num)7123227Syq193411 usbsacm_ds_unregister_cb(ds_hdl_t hdl, uint_t port_num)
7133227Syq193411 {
7143227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
7153227Syq193411 usbsacm_port_t *acm_port;
7163227Syq193411
7173227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
7183227Syq193411 "usbsacm_ds_unregister_cb: ");
7193227Syq193411
7203227Syq193411 if (port_num < acmp->acm_port_cnt) {
7213227Syq193411 /* Release callback function */
7223227Syq193411 acm_port = &acmp->acm_ports[port_num];
7233227Syq193411 bzero(&acm_port->acm_cb, sizeof (acm_port->acm_cb));
7243227Syq193411 }
7253227Syq193411 }
7263227Syq193411
7273227Syq193411
7283227Syq193411 /*
7293227Syq193411 * usbsacm_ds_open_port:
7303227Syq193411 * GSD routine call ds_open_port
7313227Syq193411 * to open the given port
7323227Syq193411 */
7333227Syq193411 /*ARGSUSED*/
7343227Syq193411 static int
usbsacm_ds_open_port(ds_hdl_t hdl,uint_t port_num)7353227Syq193411 usbsacm_ds_open_port(ds_hdl_t hdl, uint_t port_num)
7363227Syq193411 {
7373227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
7383227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
7393227Syq193411
7403227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
7413227Syq193411 "usbsacm_ds_open_port: port_num = %d", port_num);
7423227Syq193411
7433227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
7443227Syq193411 /* Check the status of the given port and device */
7453227Syq193411 if ((acmp->acm_dev_state == USB_DEV_DISCONNECTED) ||
7463227Syq193411 (acm_port->acm_port_state != USBSACM_PORT_CLOSED)) {
7473227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
7483227Syq193411
7493227Syq193411 return (USB_FAILURE);
7503227Syq193411 }
7513227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
7523227Syq193411
7533227Syq193411 usbsacm_pm_set_busy(acmp);
7543227Syq193411
7553227Syq193411 /* open pipes of port */
7563227Syq193411 if (usbsacm_open_port_pipes(acm_port) != USB_SUCCESS) {
7573227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
7583227Syq193411 "usbsacm_ds_open_port: open pipes failed.");
7593227Syq193411
7603227Syq193411 return (USB_FAILURE);
7613227Syq193411 }
7623227Syq193411
7633227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
7643227Syq193411 /* data receipt */
7653227Syq193411 if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
7663227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
7673227Syq193411 "usbsacm_ds_open_port: start receive data failed.");
7683227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
7693227Syq193411
7703227Syq193411 return (USB_FAILURE);
7713227Syq193411 }
7723227Syq193411 acm_port->acm_port_state = USBSACM_PORT_OPEN;
7733227Syq193411
7743227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
7753227Syq193411
7763227Syq193411 return (USB_SUCCESS);
7773227Syq193411 }
7783227Syq193411
7793227Syq193411
7803227Syq193411 /*
7813227Syq193411 * usbsacm_ds_close_port:
7823227Syq193411 * GSD routine call ds_close_port
7833227Syq193411 * to close the given port
7843227Syq193411 */
7853227Syq193411 /*ARGSUSED*/
7863227Syq193411 static int
usbsacm_ds_close_port(ds_hdl_t hdl,uint_t port_num)7873227Syq193411 usbsacm_ds_close_port(ds_hdl_t hdl, uint_t port_num)
7883227Syq193411 {
7893227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
7903227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
7913227Syq193411 int rval = USB_SUCCESS;
7923227Syq193411
7933227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
7946898Sfb209375 "usbsacm_ds_close_port: acmp = 0x%p", (void *)acmp);
7953227Syq193411
7963227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
7973227Syq193411 acm_port->acm_port_state = USBSACM_PORT_CLOSED;
7983227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
7993227Syq193411
8003227Syq193411 usbsacm_close_port_pipes(acm_port);
8013227Syq193411
8023227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
8033227Syq193411 rval = usbsacm_fifo_flush_locked(acmp, port_num, DS_TX | DS_RX);
8043227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
8053227Syq193411
8063227Syq193411 usbsacm_pm_set_idle(acmp);
8073227Syq193411
8083227Syq193411 return (rval);
8093227Syq193411 }
8103227Syq193411
8113227Syq193411
8123227Syq193411 /*
8133227Syq193411 * usbsacm_ds_usb_power:
8143227Syq193411 * GSD routine call ds_usb_power
8153227Syq193411 * to set power level of the component
8163227Syq193411 */
8173227Syq193411 /*ARGSUSED*/
8183227Syq193411 static int
usbsacm_ds_usb_power(ds_hdl_t hdl,int comp,int level,int * new_state)8193227Syq193411 usbsacm_ds_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state)
8203227Syq193411 {
8213227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
8223227Syq193411 usbsacm_pm_t *pm = acmp->acm_pm;
8233227Syq193411 int rval = USB_SUCCESS;
8243227Syq193411
8253227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
8263227Syq193411 "usbsacm_ds_usb_power: ");
8273227Syq193411
8283227Syq193411 /* check if pm is NULL */
8293227Syq193411 if (pm == NULL) {
8303227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
8313227Syq193411 "usbsacm_ds_usb_power: pm is NULL.");
8323227Syq193411
8333227Syq193411 return (USB_FAILURE);
8343227Syq193411 }
8353227Syq193411
8363227Syq193411 mutex_enter(&acmp->acm_mutex);
8373227Syq193411 /*
8383227Syq193411 * check if we are transitioning to a legal power level
8393227Syq193411 */
8403227Syq193411 if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
8413227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
8423227Syq193411 "usbsacm_ds_usb_power: "
8433227Syq193411 "illegal power level %d, pwr_states=%x",
8443227Syq193411 level, pm->pm_pwr_states);
8453227Syq193411 mutex_exit(&acmp->acm_mutex);
8463227Syq193411
8473227Syq193411 return (USB_FAILURE);
8483227Syq193411 }
8493227Syq193411
8503227Syq193411 /*
8513227Syq193411 * if we are about to raise power and asked to lower power, fail
8523227Syq193411 */
8533227Syq193411 if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
8543227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
8553227Syq193411 "usbsacm_ds_usb_power: wrong condition.");
8563227Syq193411 mutex_exit(&acmp->acm_mutex);
8573227Syq193411
8583227Syq193411 return (USB_FAILURE);
8593227Syq193411 }
8603227Syq193411
8613227Syq193411 /*
8623227Syq193411 * Set the power status of device by request level.
8633227Syq193411 */
8643227Syq193411 switch (level) {
8653227Syq193411 case USB_DEV_OS_PWR_OFF:
8663227Syq193411 rval = usbsacm_pwrlvl0(acmp);
8673227Syq193411
8683227Syq193411 break;
8693227Syq193411 case USB_DEV_OS_PWR_1:
8703227Syq193411 rval = usbsacm_pwrlvl1(acmp);
8713227Syq193411
8723227Syq193411 break;
8733227Syq193411 case USB_DEV_OS_PWR_2:
8743227Syq193411 rval = usbsacm_pwrlvl2(acmp);
8753227Syq193411
8763227Syq193411 break;
8773227Syq193411 case USB_DEV_OS_FULL_PWR:
8783227Syq193411 rval = usbsacm_pwrlvl3(acmp);
8795541Slg150142 /*
8805541Slg150142 * If usbser dev_state is DISCONNECTED or SUSPENDED, it shows
8815541Slg150142 * that the usb serial device is disconnected/suspended while it
8825541Slg150142 * is under power down state, now the device is powered up
8835541Slg150142 * before it is reconnected/resumed. xxx_pwrlvl3() will set dev
8845541Slg150142 * state to ONLINE, we need to set the dev state back to
8855541Slg150142 * DISCONNECTED/SUSPENDED.
8865541Slg150142 */
8875541Slg150142 if ((rval == USB_SUCCESS) &&
8885541Slg150142 ((*new_state == USB_DEV_DISCONNECTED) ||
8895541Slg150142 (*new_state == USB_DEV_SUSPENDED))) {
8905541Slg150142 acmp->acm_dev_state = *new_state;
8915541Slg150142 }
8923227Syq193411
8933227Syq193411 break;
8943227Syq193411 }
8953227Syq193411
8963227Syq193411 *new_state = acmp->acm_dev_state;
8973227Syq193411 mutex_exit(&acmp->acm_mutex);
8983227Syq193411
8993227Syq193411 return (rval);
9003227Syq193411 }
9013227Syq193411
9023227Syq193411
9033227Syq193411 /*
9043227Syq193411 * usbsacm_ds_suspend:
9053227Syq193411 * GSD routine call ds_suspend
9063227Syq193411 * during CPR suspend
9073227Syq193411 */
9083227Syq193411 static int
usbsacm_ds_suspend(ds_hdl_t hdl)9093227Syq193411 usbsacm_ds_suspend(ds_hdl_t hdl)
9103227Syq193411 {
9113227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
9125541Slg150142 int state = USB_DEV_SUSPENDED;
9133227Syq193411
9143227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
9153227Syq193411 "usbsacm_ds_suspend: ");
9165541Slg150142 /*
9175541Slg150142 * If the device is suspended while it is under PWRED_DOWN state, we
9185541Slg150142 * need to keep the PWRED_DOWN state so that it could be powered up
9195541Slg150142 * later. In the mean while, usbser dev state will be changed to
9205541Slg150142 * SUSPENDED state.
9215541Slg150142 */
9223227Syq193411 mutex_enter(&acmp->acm_mutex);
9235541Slg150142 if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
9245541Slg150142 /* set device status to suspend */
9255541Slg150142 acmp->acm_dev_state = USB_DEV_SUSPENDED;
9265541Slg150142 }
9273227Syq193411 mutex_exit(&acmp->acm_mutex);
9283227Syq193411
9293227Syq193411 usbsacm_disconnect_pipes(acmp);
9303227Syq193411
9313227Syq193411 return (state);
9323227Syq193411 }
9333227Syq193411
9343227Syq193411 /*
9353227Syq193411 * usbsacm_ds_resume:
9363227Syq193411 * GSD routine call ds_resume
9373227Syq193411 * during CPR resume
9383227Syq193411 */
9393227Syq193411 /*ARGSUSED*/
9403227Syq193411 static int
usbsacm_ds_resume(ds_hdl_t hdl)9413227Syq193411 usbsacm_ds_resume(ds_hdl_t hdl)
9423227Syq193411 {
9433227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
9443227Syq193411 int current_state;
9453227Syq193411 int ret;
9463227Syq193411
9473227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
9483227Syq193411 "usbsacm_ds_resume: ");
9493227Syq193411
9503227Syq193411 mutex_enter(&acmp->acm_mutex);
9513227Syq193411 current_state = acmp->acm_dev_state;
9523227Syq193411 mutex_exit(&acmp->acm_mutex);
9533227Syq193411
9543227Syq193411 /* restore the status of device */
9553227Syq193411 if (current_state != USB_DEV_ONLINE) {
9563227Syq193411 ret = usbsacm_restore_device_state(acmp);
9573227Syq193411 } else {
9583227Syq193411 ret = USB_DEV_ONLINE;
9593227Syq193411 }
9603227Syq193411
9613227Syq193411 return (ret);
9623227Syq193411 }
9633227Syq193411
9643227Syq193411 /*
9653227Syq193411 * usbsacm_ds_disconnect:
9663227Syq193411 * GSD routine call ds_disconnect
9673227Syq193411 * to disconnect USB device
9683227Syq193411 */
9693227Syq193411 static int
usbsacm_ds_disconnect(ds_hdl_t hdl)9703227Syq193411 usbsacm_ds_disconnect(ds_hdl_t hdl)
9713227Syq193411 {
9723227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
9735541Slg150142 int state = USB_DEV_DISCONNECTED;
9743227Syq193411
9753227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
9763227Syq193411 "usbsacm_ds_disconnect: ");
9773227Syq193411
9785541Slg150142 /*
9795541Slg150142 * If the device is disconnected while it is under PWRED_DOWN state, we
9805541Slg150142 * need to keep the PWRED_DOWN state so that it could be powered up
9815541Slg150142 * later. In the mean while, usbser dev state will be changed to
9825541Slg150142 * DISCONNECTED state.
9835541Slg150142 */
9843227Syq193411 mutex_enter(&acmp->acm_mutex);
9855541Slg150142 if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
9865541Slg150142 /* set device status to disconnected */
9875541Slg150142 acmp->acm_dev_state = USB_DEV_DISCONNECTED;
9885541Slg150142 }
9893227Syq193411 mutex_exit(&acmp->acm_mutex);
9903227Syq193411
9913227Syq193411 usbsacm_disconnect_pipes(acmp);
9923227Syq193411
9933227Syq193411 return (state);
9943227Syq193411 }
9953227Syq193411
9963227Syq193411
9973227Syq193411 /*
9983227Syq193411 * usbsacm_ds_reconnect:
9993227Syq193411 * GSD routine call ds_reconnect
10003227Syq193411 * to reconnect USB device
10013227Syq193411 */
10023227Syq193411 /*ARGSUSED*/
10033227Syq193411 static int
usbsacm_ds_reconnect(ds_hdl_t hdl)10043227Syq193411 usbsacm_ds_reconnect(ds_hdl_t hdl)
10053227Syq193411 {
10063227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
10073227Syq193411
10083227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
10093227Syq193411 "usbsacm_ds_reconnect: ");
10103227Syq193411
10113227Syq193411 return (usbsacm_restore_device_state(acmp));
10123227Syq193411 }
10133227Syq193411
10143227Syq193411
10153227Syq193411 /*
10163227Syq193411 * usbsacm_ds_set_port_params:
10173227Syq193411 * GSD routine call ds_set_port_params
10183227Syq193411 * to set one or more port parameters
10193227Syq193411 */
10203227Syq193411 /*ARGSUSED*/
10213227Syq193411 static int
usbsacm_ds_set_port_params(ds_hdl_t hdl,uint_t port_num,ds_port_params_t * tp)10223227Syq193411 usbsacm_ds_set_port_params(ds_hdl_t hdl, uint_t port_num, ds_port_params_t *tp)
10233227Syq193411 {
10243227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
10253227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
10263227Syq193411 int i;
10273227Syq193411 uint_t ui;
10283227Syq193411 ds_port_param_entry_t *pe;
10293227Syq193411 usb_cdc_line_coding_t lc;
10303227Syq193411 int ret;
10313227Syq193411
10323227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
10336898Sfb209375 "usbsacm_ds_set_port_params: acmp = 0x%p", (void *)acmp);
10343227Syq193411
10353227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
10363227Syq193411 /*
10373227Syq193411 * If device conform to acm spec, check if it support to set port param.
10383227Syq193411 */
10393227Syq193411 if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
10403227Syq193411 acmp->acm_compatibility == B_TRUE) {
10413227Syq193411
10423227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
10433227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
10443227Syq193411 "usbsacm_ds_set_port_params: "
10453227Syq193411 "don't support Set_Line_Coding.");
10463227Syq193411
10473227Syq193411 return (USB_FAILURE);
10483227Syq193411 }
10493227Syq193411
10503227Syq193411 lc = acm_port->acm_line_coding;
10513227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
10523227Syq193411 pe = tp->tp_entries;
10533227Syq193411 /* Get parameter information from ds_port_params_t */
10543227Syq193411 for (i = 0; i < tp->tp_cnt; i++, pe++) {
10553227Syq193411 switch (pe->param) {
10563227Syq193411 case DS_PARAM_BAUD:
10573227Syq193411 /* Data terminal rate, in bits per second. */
10583227Syq193411 ui = pe->val.ui;
10593227Syq193411
10603227Syq193411 /* if we don't support this speed, return USB_FAILURE */
10613227Syq193411 if ((ui >= NELEM(usbsacm_speedtab)) ||
10623227Syq193411 ((ui > 0) && (usbsacm_speedtab[ui] == 0))) {
10633227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
10643227Syq193411 "usbsacm_ds_set_port_params: "
10653227Syq193411 " error baud rate");
10663227Syq193411
10673227Syq193411 return (USB_FAILURE);
10683227Syq193411 }
10693227Syq193411 lc.dwDTERate = LE_32(usbsacm_speedtab[ui]);
10703227Syq193411
10713227Syq193411 break;
10723227Syq193411 case DS_PARAM_PARITY:
10733227Syq193411 /* Parity Type */
10743227Syq193411 if (pe->val.ui & PARENB) {
10753227Syq193411 if (pe->val.ui & PARODD) {
10763227Syq193411 lc.bParityType = USB_CDC_PARITY_ODD;
10773227Syq193411 } else {
10783227Syq193411 lc.bParityType = USB_CDC_PARITY_EVEN;
10793227Syq193411 }
10803227Syq193411 } else {
10813227Syq193411 lc.bParityType = USB_CDC_PARITY_NO;
10823227Syq193411 }
10833227Syq193411
10843227Syq193411 break;
10853227Syq193411 case DS_PARAM_STOPB:
10863227Syq193411 /* Stop bit */
10873227Syq193411 if (pe->val.ui & CSTOPB) {
10883227Syq193411 lc.bCharFormat = USB_CDC_STOP_BITS_2;
10893227Syq193411 } else {
10903227Syq193411 lc.bCharFormat = USB_CDC_STOP_BITS_1;
10913227Syq193411 }
10923227Syq193411
10933227Syq193411 break;
10943227Syq193411 case DS_PARAM_CHARSZ:
10953227Syq193411 /* Data Bits */
10963227Syq193411 switch (pe->val.ui) {
10973227Syq193411 case CS5:
10983227Syq193411 lc.bDataBits = 5;
10993227Syq193411 break;
11003227Syq193411 case CS6:
11013227Syq193411 lc.bDataBits = 6;
11023227Syq193411 break;
11033227Syq193411 case CS7:
11043227Syq193411 lc.bDataBits = 7;
11053227Syq193411 break;
11063227Syq193411 case CS8:
11073227Syq193411 default:
11083227Syq193411 lc.bDataBits = 8;
11093227Syq193411 break;
11103227Syq193411 }
11113227Syq193411
11123227Syq193411 break;
11133227Syq193411 default:
11143227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
11153227Syq193411 "usbsacm_ds_set_port_params: "
11163227Syq193411 "parameter 0x%x isn't supported",
11173227Syq193411 pe->param);
11183227Syq193411
11193227Syq193411 break;
11203227Syq193411 }
11213227Syq193411 }
11223227Syq193411
11233227Syq193411 if ((ret = usbsacm_set_line_coding(acm_port, &lc)) == USB_SUCCESS) {
11243227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
11253227Syq193411 acm_port->acm_line_coding = lc;
11263227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
11273227Syq193411 }
11283227Syq193411
11293227Syq193411 /*
11303227Syq193411 * If device don't conform to acm spec, return success directly.
11313227Syq193411 */
11323227Syq193411 if (acmp->acm_compatibility != B_TRUE) {
11333227Syq193411 ret = USB_SUCCESS;
11343227Syq193411 }
11353227Syq193411
11363227Syq193411 return (ret);
11373227Syq193411 }
11383227Syq193411
11393227Syq193411
11403227Syq193411 /*
11413227Syq193411 * usbsacm_ds_set_modem_ctl:
11423227Syq193411 * GSD routine call ds_set_modem_ctl
11433227Syq193411 * to set modem control of the given port
11443227Syq193411 */
11453227Syq193411 /*ARGSUSED*/
11463227Syq193411 static int
usbsacm_ds_set_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int val)11473227Syq193411 usbsacm_ds_set_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int val)
11483227Syq193411 {
11493227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
11503227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
11513227Syq193411 uint8_t new_mctl;
11523227Syq193411 int ret;
11533227Syq193411
11543227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
11553227Syq193411 "usbsacm_ds_set_modem_ctl: mask = 0x%x val = 0x%x",
11563227Syq193411 mask, val);
11573227Syq193411
11583227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
11593227Syq193411 /*
11603227Syq193411 * If device conform to acm spec, check if it support to set modem
11613227Syq193411 * controls.
11623227Syq193411 */
11633227Syq193411 if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
11645541Slg150142 acmp->acm_compatibility == B_TRUE) {
11653227Syq193411
11663227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
11673227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
11683227Syq193411 "usbsacm_ds_set_modem_ctl: "
11693227Syq193411 "don't support Set_Control_Line_State.");
11703227Syq193411
11713227Syq193411 return (USB_FAILURE);
11723227Syq193411 }
11733227Syq193411
11743227Syq193411 new_mctl = acm_port->acm_mctlout;
11753227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
11763227Syq193411
11773227Syq193411 usbsacm_mctl2reg(mask, val, &new_mctl);
11783227Syq193411
11793227Syq193411 if ((acmp->acm_compatibility == B_FALSE) || ((ret =
11803227Syq193411 usbsacm_req_write(acm_port, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
11813227Syq193411 new_mctl, NULL)) == USB_SUCCESS)) {
11823227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
11833227Syq193411 acm_port->acm_mctlout = new_mctl;
11843227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
11853227Syq193411 }
11863227Syq193411
11873227Syq193411 /*
11883227Syq193411 * If device don't conform to acm spec, return success directly.
11893227Syq193411 */
11903227Syq193411 if (acmp->acm_compatibility != B_TRUE) {
11913227Syq193411 ret = USB_SUCCESS;
11923227Syq193411 }
11933227Syq193411
11943227Syq193411 return (ret);
11953227Syq193411 }
11963227Syq193411
11973227Syq193411
11983227Syq193411 /*
11993227Syq193411 * usbsacm_ds_get_modem_ctl:
12003227Syq193411 * GSD routine call ds_get_modem_ctl
12013227Syq193411 * to get modem control/status of the given port
12023227Syq193411 */
12033227Syq193411 /*ARGSUSED*/
12043227Syq193411 static int
usbsacm_ds_get_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int * valp)12053227Syq193411 usbsacm_ds_get_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int *valp)
12063227Syq193411 {
12073227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
12083227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
12093227Syq193411
12103227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
12113227Syq193411 *valp = usbsacm_reg2mctl(acm_port->acm_mctlout) & mask;
12123227Syq193411 /*
12133227Syq193411 * If device conform to acm spec, polling function can modify the value
12143227Syq193411 * of acm_mctlin; else set to default value.
12153227Syq193411 */
12163227Syq193411 if (acmp->acm_compatibility) {
12173227Syq193411 *valp |= usbsacm_reg2mctl(acm_port->acm_mctlin) & mask;
12183227Syq193411 *valp |= (mask & (TIOCM_CD | TIOCM_CTS));
12193227Syq193411 } else {
12203227Syq193411 *valp |= (mask & (TIOCM_CD | TIOCM_CTS | TIOCM_DSR | TIOCM_RI));
12213227Syq193411 }
12223227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
12233227Syq193411
12243227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
12253227Syq193411 "usbsacm_ds_get_modem_ctl: val = 0x%x", *valp);
12263227Syq193411
12273227Syq193411 return (USB_SUCCESS);
12283227Syq193411 }
12293227Syq193411
12303227Syq193411
12313227Syq193411 /*
12323227Syq193411 * usbsacm_ds_tx:
12333227Syq193411 * GSD routine call ds_break_ctl
12343227Syq193411 * to set/clear break
12353227Syq193411 */
12363227Syq193411 /*ARGSUSED*/
12373227Syq193411 static int
usbsacm_ds_break_ctl(ds_hdl_t hdl,uint_t port_num,int ctl)12383227Syq193411 usbsacm_ds_break_ctl(ds_hdl_t hdl, uint_t port_num, int ctl)
12393227Syq193411 {
12403227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
12413227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
12423227Syq193411
12433227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
12443227Syq193411 "usbsacm_ds_break_ctl: ");
12453227Syq193411
12463227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
12473227Syq193411 /*
12483227Syq193411 * If device conform to acm spec, check if it support to send break.
12493227Syq193411 */
12503227Syq193411 if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SEND_BREAK) == 0 &&
12515541Slg150142 acmp->acm_compatibility == B_TRUE) {
12523227Syq193411
12533227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
12543227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
12553227Syq193411 "usbsacm_ds_break_ctl: don't support send break.");
12563227Syq193411
12573227Syq193411 return (USB_FAILURE);
12583227Syq193411 }
12593227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
12603227Syq193411
12613227Syq193411 return (usbsacm_req_write(acm_port, USB_CDC_REQ_SEND_BREAK,
12625541Slg150142 ((ctl == DS_ON) ? 0xffff : 0), NULL));
12633227Syq193411 }
12643227Syq193411
12653227Syq193411
12663227Syq193411 /*
12673227Syq193411 * usbsacm_ds_tx:
12683227Syq193411 * GSD routine call ds_tx
12693227Syq193411 * to data transmit
12703227Syq193411 */
12713227Syq193411 /*ARGSUSED*/
12723227Syq193411 static int
usbsacm_ds_tx(ds_hdl_t hdl,uint_t port_num,mblk_t * mp)12733227Syq193411 usbsacm_ds_tx(ds_hdl_t hdl, uint_t port_num, mblk_t *mp)
12743227Syq193411 {
12753227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
12763227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
12773227Syq193411
12783227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
12796898Sfb209375 "usbsacm_ds_tx: mp = 0x%p acmp = 0x%p", (void *)mp, (void *)acmp);
12803227Syq193411
12813227Syq193411 /* sanity checks */
12823227Syq193411 if (mp == NULL) {
12833227Syq193411
12843227Syq193411 return (USB_SUCCESS);
12853227Syq193411 }
12866990Sgd78059 if (MBLKL(mp) < 1) {
12873227Syq193411 freemsg(mp);
12883227Syq193411
12893227Syq193411 return (USB_SUCCESS);
12903227Syq193411 }
12913227Syq193411
12923227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
12933227Syq193411 /* put mblk to tail of mblk chain */
12943227Syq193411 usbsacm_put_tail(&acm_port->acm_tx_mp, mp);
12953227Syq193411 usbsacm_tx_start(acm_port);
12963227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
12973227Syq193411
12983227Syq193411 return (USB_SUCCESS);
12993227Syq193411 }
13003227Syq193411
13013227Syq193411
13023227Syq193411 /*
13033227Syq193411 * usbsacm_ds_rx:
13043227Syq193411 * GSD routine call ds_rx;
13053227Syq193411 * to data receipt
13063227Syq193411 */
13073227Syq193411 /*ARGSUSED*/
13083227Syq193411 static mblk_t *
usbsacm_ds_rx(ds_hdl_t hdl,uint_t port_num)13093227Syq193411 usbsacm_ds_rx(ds_hdl_t hdl, uint_t port_num)
13103227Syq193411 {
13113227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
13123227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
13133227Syq193411 mblk_t *mp;
13143227Syq193411
13153227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
13166898Sfb209375 "usbsacm_ds_rx: acmp = 0x%p", (void *)acmp);
13173227Syq193411
13183227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
13193227Syq193411
13203227Syq193411 mp = acm_port->acm_rx_mp;
13213227Syq193411 acm_port->acm_rx_mp = NULL;
13223227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
13233227Syq193411
13243227Syq193411 return (mp);
13253227Syq193411 }
13263227Syq193411
13273227Syq193411
13283227Syq193411 /*
13293227Syq193411 * usbsacm_ds_stop:
13303227Syq193411 * GSD routine call ds_stop;
13313227Syq193411 * but acm spec don't define this function
13323227Syq193411 */
13333227Syq193411 /*ARGSUSED*/
13343227Syq193411 static void
usbsacm_ds_stop(ds_hdl_t hdl,uint_t port_num,int dir)13353227Syq193411 usbsacm_ds_stop(ds_hdl_t hdl, uint_t port_num, int dir)
13363227Syq193411 {
13373227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
13383227Syq193411
13393227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
13403227Syq193411 "usbsacm_ds_stop: don't support!");
13413227Syq193411 }
13423227Syq193411
13433227Syq193411
13443227Syq193411 /*
13453227Syq193411 * usbsacm_ds_start:
13463227Syq193411 * GSD routine call ds_start;
13473227Syq193411 * but acm spec don't define this function
13483227Syq193411 */
13493227Syq193411 /*ARGSUSED*/
13503227Syq193411 static void
usbsacm_ds_start(ds_hdl_t hdl,uint_t port_num,int dir)13513227Syq193411 usbsacm_ds_start(ds_hdl_t hdl, uint_t port_num, int dir)
13523227Syq193411 {
13533227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
13543227Syq193411
13553227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
13563227Syq193411 "usbsacm_ds_start: don't support!");
13573227Syq193411 }
13583227Syq193411
13593227Syq193411
13603227Syq193411 /*
13613227Syq193411 * usbsacm_ds_fifo_flush:
13623227Syq193411 * GSD routine call ds_fifo_flush
13633227Syq193411 * to flush FIFOs
13643227Syq193411 */
13653227Syq193411 /*ARGSUSED*/
13663227Syq193411 static int
usbsacm_ds_fifo_flush(ds_hdl_t hdl,uint_t port_num,int dir)13673227Syq193411 usbsacm_ds_fifo_flush(ds_hdl_t hdl, uint_t port_num, int dir)
13683227Syq193411 {
13693227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
13703227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
13713227Syq193411 int ret = USB_SUCCESS;
13723227Syq193411
13733227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
13743227Syq193411 "usbsacm_ds_fifo_flush: ");
13753227Syq193411
13763227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
13773227Syq193411 ret = usbsacm_fifo_flush_locked(acmp, port_num, dir);
13783227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
13793227Syq193411
13803227Syq193411 return (ret);
13813227Syq193411 }
13823227Syq193411
13833227Syq193411
13843227Syq193411 /*
13853227Syq193411 * usbsacm_ds_fifo_drain:
13863227Syq193411 * GSD routine call ds_fifo_drain
13873227Syq193411 * to wait until empty output FIFO
13883227Syq193411 */
13893227Syq193411 /*ARGSUSED*/
13903227Syq193411 static int
usbsacm_ds_fifo_drain(ds_hdl_t hdl,uint_t port_num,int timeout)13913227Syq193411 usbsacm_ds_fifo_drain(ds_hdl_t hdl, uint_t port_num, int timeout)
13923227Syq193411 {
13933227Syq193411 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
13943227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
13953227Syq193411 int rval = USB_SUCCESS;
13963227Syq193411
13973227Syq193411 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
13983227Syq193411 "usbsacm_ds_fifo_drain: ");
13993227Syq193411
14003227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
14013227Syq193411 ASSERT(acm_port->acm_port_state == USBSACM_PORT_OPEN);
14023227Syq193411
14033227Syq193411 if (usbsacm_wait_tx_drain(acm_port, timeout) != USB_SUCCESS) {
14043227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
14053227Syq193411 "usbsacm_ds_fifo_drain: fifo drain failed.");
14063227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
14073227Syq193411
14083227Syq193411 return (USB_FAILURE);
14093227Syq193411 }
14103227Syq193411
14113227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
14123227Syq193411
14133227Syq193411 return (rval);
14143227Syq193411 }
14153227Syq193411
14163227Syq193411
14173227Syq193411 /*
14183227Syq193411 * usbsacm_fifo_flush_locked:
14193227Syq193411 * flush FIFOs of the given ports
14203227Syq193411 */
14213227Syq193411 /*ARGSUSED*/
14223227Syq193411 static int
usbsacm_fifo_flush_locked(usbsacm_state_t * acmp,uint_t port_num,int dir)14233227Syq193411 usbsacm_fifo_flush_locked(usbsacm_state_t *acmp, uint_t port_num, int dir)
14243227Syq193411 {
14253227Syq193411 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
14263227Syq193411
14273227Syq193411 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
14283227Syq193411 "usbsacm_fifo_flush_locked: ");
14293227Syq193411
14303227Syq193411 /* flush transmit FIFO if DS_TX is set */
14313227Syq193411 if ((dir & DS_TX) && acm_port->acm_tx_mp) {
14323227Syq193411 freemsg(acm_port->acm_tx_mp);
14333227Syq193411 acm_port->acm_tx_mp = NULL;
14343227Syq193411 }
14353227Syq193411 /* flush received FIFO if DS_RX is set */
14363227Syq193411 if ((dir & DS_RX) && acm_port->acm_rx_mp) {
14373227Syq193411 freemsg(acm_port->acm_rx_mp);
14383227Syq193411 acm_port->acm_rx_mp = NULL;
14393227Syq193411 }
14403227Syq193411
14413227Syq193411 return (USB_SUCCESS);
14423227Syq193411 }
14433227Syq193411
14443227Syq193411
14453227Syq193411 /*
14463227Syq193411 * usbsacm_get_bulk_pipe_number:
14473227Syq193411 * Calculate the number of bulk in or out pipes in current device.
14483227Syq193411 */
14493227Syq193411 static int
usbsacm_get_bulk_pipe_number(usbsacm_state_t * acmp,uint_t dir)14503227Syq193411 usbsacm_get_bulk_pipe_number(usbsacm_state_t *acmp, uint_t dir)
14513227Syq193411 {
14523227Syq193411 int count = 0;
14533227Syq193411 int i, skip;
14543227Syq193411 usb_if_data_t *cur_if;
14553227Syq193411 int ep_num;
14563227Syq193411 int if_num;
14573227Syq193411
14583227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
14593227Syq193411 "usbsacm_get_bulk_pipe_number: ");
14603227Syq193411
14613227Syq193411 cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
14623227Syq193411 if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
14633227Syq193411
14643227Syq193411 /* search each interface which have bulk endpoint */
14653227Syq193411 for (i = 0; i < if_num; i++) {
14663227Syq193411 ep_num = cur_if->if_alt->altif_n_ep;
14673227Syq193411
14683227Syq193411 /*
14693227Syq193411 * search endpoints in current interface,
14703227Syq193411 * which type is input parameter 'dir'
14713227Syq193411 */
14723227Syq193411 for (skip = 0; skip < ep_num; skip++) {
14735541Slg150142 if (usb_lookup_ep_data(acmp->acm_dip,
1474*12024SRaymond.Chen@Sun.COM acmp->acm_dev_data, i, 0, skip,
14755541Slg150142 USB_EP_ATTR_BULK, dir) == NULL) {
14765541Slg150142
14775541Slg150142 /*
14785541Slg150142 * If not found, skip the internal loop
14795541Slg150142 * and search the next interface.
14805541Slg150142 */
14815541Slg150142 break;
14825541Slg150142 }
14835541Slg150142 count++;
14843227Syq193411 }
14853227Syq193411
14863227Syq193411 cur_if++;
14873227Syq193411 }
14883227Syq193411
14893227Syq193411 return (count);
14903227Syq193411 }
14913227Syq193411
14923227Syq193411
14933227Syq193411 /*
14943227Syq193411 * port management
14953227Syq193411 * ---------------
14963227Syq193411 * initialize, release port.
14973227Syq193411 *
14983227Syq193411 *
14993227Syq193411 * usbsacm_init_ports_status:
15003227Syq193411 * Initialize the port status for the current device.
15013227Syq193411 */
15023227Syq193411 static int
usbsacm_init_ports_status(usbsacm_state_t * acmp)15033227Syq193411 usbsacm_init_ports_status(usbsacm_state_t *acmp)
15043227Syq193411 {
15053227Syq193411 usbsacm_port_t *cur_port;
15063227Syq193411 int i, skip;
15073227Syq193411 int if_num;
15083227Syq193411 int intr_if_no = 0;
15093227Syq193411 int ep_num;
15103227Syq193411 usb_if_data_t *cur_if;
15113227Syq193411
15123227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
15136898Sfb209375 "usbsacm_init_ports_status: acmp = 0x%p", (void *)acmp);
15143227Syq193411
15153227Syq193411 /* Initialize the port status to default value */
15163227Syq193411 for (i = 0; i < acmp->acm_port_cnt; i++) {
15173227Syq193411 cur_port = &acmp->acm_ports[i];
15183227Syq193411
15193227Syq193411 cv_init(&cur_port->acm_tx_cv, NULL, CV_DRIVER, NULL);
15203227Syq193411
15213227Syq193411 cur_port->acm_port_state = USBSACM_PORT_CLOSED;
15223227Syq193411
15233227Syq193411 cur_port->acm_line_coding.dwDTERate = LE_32((uint32_t)9600);
15243227Syq193411 cur_port->acm_line_coding.bCharFormat = 0;
15253227Syq193411 cur_port->acm_line_coding.bParityType = USB_CDC_PARITY_NO;
15263227Syq193411 cur_port->acm_line_coding.bDataBits = 8;
15273227Syq193411 cur_port->acm_device = acmp;
15283227Syq193411 mutex_init(&cur_port->acm_port_mutex, NULL, MUTEX_DRIVER,
15293227Syq193411 acmp->acm_dev_data->dev_iblock_cookie);
15303227Syq193411 }
15313227Syq193411
15323227Syq193411 /*
15333227Syq193411 * If device conform to cdc acm spec, parse function descriptors.
15343227Syq193411 */
15353227Syq193411 if (acmp->acm_compatibility == B_TRUE) {
15363227Syq193411
15373227Syq193411 if (usbsacm_get_descriptors(acmp) != USB_SUCCESS) {
15383227Syq193411
15393227Syq193411 return (USB_FAILURE);
15403227Syq193411 }
15413227Syq193411
15423227Syq193411 return (USB_SUCCESS);
15433227Syq193411 }
15443227Syq193411
15453227Syq193411 /*
15463227Syq193411 * If device don't conform to spec, search pairs of bulk in/out
15473227Syq193411 * endpoints and fill port structure.
15483227Syq193411 */
15493227Syq193411 cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
15503227Syq193411 if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
15513227Syq193411 cur_port = acmp->acm_ports;
15523227Syq193411
15533227Syq193411 /* search each interface which have bulk in and out */
15543227Syq193411 for (i = 0; i < if_num; i++) {
15555541Slg150142 ep_num = cur_if->if_alt->altif_n_ep;
15565541Slg150142
15575541Slg150142 for (skip = 0; skip < ep_num; skip++) {
15583227Syq193411
15593227Syq193411 /* search interrupt pipe. */
15603227Syq193411 if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
15613227Syq193411 i, 0, skip, USB_EP_ATTR_INTR, USB_EP_DIR_IN) != NULL)) {
15623227Syq193411
15633227Syq193411 intr_if_no = i;
15643227Syq193411 }
15653227Syq193411
15663227Syq193411 /* search pair of bulk in/out endpoints. */
15673227Syq193411 if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
15683227Syq193411 i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_IN) == NULL) ||
15693227Syq193411 (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
15703227Syq193411 i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_OUT) == NULL)) {
15713227Syq193411
15723227Syq193411 continue;
15733227Syq193411 }
15743227Syq193411
15753227Syq193411 cur_port->acm_data_if_no = i;
15763227Syq193411 cur_port->acm_ctrl_if_no = intr_if_no;
15773227Syq193411 cur_port->acm_data_port_no = skip;
15783227Syq193411 cur_port++;
15793227Syq193411 intr_if_no = 0;
15805541Slg150142 }
15815541Slg150142
15825541Slg150142 cur_if++;
15833227Syq193411 }
15843227Syq193411
15853227Syq193411 return (USB_SUCCESS);
15863227Syq193411 }
15873227Syq193411
15883227Syq193411
15893227Syq193411 /*
15903227Syq193411 * usbsacm_init_alloc_ports:
15913227Syq193411 * Allocate memory and initialize the port state for the current device.
15923227Syq193411 */
15933227Syq193411 static int
usbsacm_init_alloc_ports(usbsacm_state_t * acmp)15943227Syq193411 usbsacm_init_alloc_ports(usbsacm_state_t *acmp)
15953227Syq193411 {
15965541Slg150142 int rval = USB_SUCCESS;
15973227Syq193411 int count_in = 0, count_out = 0;
15983227Syq193411
15993227Syq193411 if (acmp->acm_compatibility) {
16003227Syq193411 acmp->acm_port_cnt = 1;
16013227Syq193411 } else {
16023227Syq193411 /* Calculate the number of the bulk in/out endpoints */
16033227Syq193411 count_in = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_IN);
16043227Syq193411 count_out = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_OUT);
16053227Syq193411
16063227Syq193411 USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
16073227Syq193411 "usbsacm_init_alloc_ports: count_in = %d, count_out = %d",
16083227Syq193411 count_in, count_out);
16093227Syq193411
16103227Syq193411 acmp->acm_port_cnt = min(count_in, count_out);
16113227Syq193411 }
16123227Syq193411
16133227Syq193411 /* return if not found any pair of bulk in/out endpoint. */
16143227Syq193411 if (acmp->acm_port_cnt == 0) {
16153227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
16163227Syq193411 "usbsacm_init_alloc_ports: port count is zero.");
16173227Syq193411
16183227Syq193411 return (USB_FAILURE);
16193227Syq193411 }
16203227Syq193411
16213227Syq193411 /* allocate memory for ports */
16223227Syq193411 acmp->acm_ports = (usbsacm_port_t *)kmem_zalloc(acmp->acm_port_cnt *
16233227Syq193411 sizeof (usbsacm_port_t), KM_SLEEP);
16243227Syq193411 if (acmp->acm_ports == NULL) {
16253227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
16263227Syq193411 "usbsacm_init_alloc_ports: allocate memory failed.");
16273227Syq193411
16283227Syq193411 return (USB_FAILURE);
16293227Syq193411 }
16303227Syq193411
16313227Syq193411 /* fill the status of port structure. */
16323227Syq193411 rval = usbsacm_init_ports_status(acmp);
16333227Syq193411 if (rval != USB_SUCCESS) {
16343227Syq193411 usbsacm_free_ports(acmp);
16353227Syq193411 }
16363227Syq193411
16373227Syq193411 return (rval);
16383227Syq193411 }
16393227Syq193411
16403227Syq193411
16413227Syq193411 /*
16423227Syq193411 * usbsacm_free_ports:
16433227Syq193411 * Release ports and deallocate memory.
16443227Syq193411 */
16453227Syq193411 static void
usbsacm_free_ports(usbsacm_state_t * acmp)16463227Syq193411 usbsacm_free_ports(usbsacm_state_t *acmp)
16473227Syq193411 {
16483227Syq193411 int i;
16493227Syq193411
16503227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
16513227Syq193411 "usbsacm_free_ports: ");
16523227Syq193411
16533227Syq193411 /* Release memory and data structure for each port */
16543227Syq193411 for (i = 0; i < acmp->acm_port_cnt; i++) {
16553227Syq193411 cv_destroy(&acmp->acm_ports[i].acm_tx_cv);
16563227Syq193411 mutex_destroy(&acmp->acm_ports[i].acm_port_mutex);
16573227Syq193411 }
16583227Syq193411 kmem_free((caddr_t)acmp->acm_ports, sizeof (usbsacm_port_t) *
16593227Syq193411 acmp->acm_port_cnt);
16603227Syq193411 acmp->acm_ports = NULL;
16613227Syq193411 }
16623227Syq193411
16633227Syq193411
16643227Syq193411 /*
16653227Syq193411 * usbsacm_get_descriptors:
16663227Syq193411 * analysis functional descriptors of acm device
16673227Syq193411 */
16683227Syq193411 static int
usbsacm_get_descriptors(usbsacm_state_t * acmp)16693227Syq193411 usbsacm_get_descriptors(usbsacm_state_t *acmp)
16703227Syq193411 {
16713227Syq193411 int i;
16723227Syq193411 usb_cfg_data_t *cfg;
16733227Syq193411 usb_alt_if_data_t *altif;
16743227Syq193411 usb_cvs_data_t *cvs;
16753227Syq193411 int mgmt_cap = 0;
16763227Syq193411 int master_if = -1, slave_if = -1;
16773227Syq193411 usbsacm_port_t *acm_port = acmp->acm_ports;
16783227Syq193411
16793227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
16803227Syq193411 "usbsacm_get_descriptors: ");
16813227Syq193411
16823227Syq193411 cfg = acmp->acm_dev_data->dev_curr_cfg;
16833227Syq193411 /* set default control and data interface */
16843227Syq193411 acm_port->acm_ctrl_if_no = acm_port->acm_data_if_no = 0;
16853227Syq193411
16863227Syq193411 /* get current interfaces */
16873227Syq193411 acm_port->acm_ctrl_if_no = acmp->acm_dev_data->dev_curr_if;
16883227Syq193411 if (cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt == 0) {
16893227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
16903227Syq193411 "usbsacm_get_descriptors: elements in if_alt is %d",
16913227Syq193411 cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt);
16923227Syq193411
16933227Syq193411 return (USB_FAILURE);
16943227Syq193411 }
16953227Syq193411
16963227Syq193411 altif = &cfg->cfg_if[acm_port->acm_ctrl_if_no].if_alt[0];
16973227Syq193411
16983227Syq193411 /*
16993227Syq193411 * Based on CDC specification, ACM devices usually include the
17003227Syq193411 * following function descriptors: Header, ACM, Union and Call
17013227Syq193411 * Management function descriptors. This loop search tree data
17023227Syq193411 * structure for each acm class descriptor.
17033227Syq193411 */
17043227Syq193411 for (i = 0; i < altif->altif_n_cvs; i++) {
17053227Syq193411
17063227Syq193411 cvs = &altif->altif_cvs[i];
17073227Syq193411
17083227Syq193411 if ((cvs->cvs_buf == NULL) ||
17093227Syq193411 (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
17103227Syq193411 continue;
17113227Syq193411 }
17123227Syq193411
17133227Syq193411 switch (cvs->cvs_buf[2]) {
17143227Syq193411 case USB_CDC_DESCR_TYPE_CALL_MANAGEMENT:
17153227Syq193411 /* parse call management functional descriptor. */
17163227Syq193411 if (cvs->cvs_buf_len >= 5) {
17173227Syq193411 mgmt_cap = cvs->cvs_buf[3];
17183227Syq193411 acm_port->acm_data_if_no = cvs->cvs_buf[4];
17193227Syq193411 }
17203227Syq193411 break;
17213227Syq193411 case USB_CDC_DESCR_TYPE_ACM:
17223227Syq193411 /* parse ACM functional descriptor. */
17233227Syq193411 if (cvs->cvs_buf_len >= 4) {
17243227Syq193411 acm_port->acm_cap = cvs->cvs_buf[3];
17253227Syq193411 }
17263227Syq193411 break;
17273227Syq193411 case USB_CDC_DESCR_TYPE_UNION:
17283227Syq193411 /* parse Union functional descriptor. */
17293227Syq193411 if (cvs->cvs_buf_len >= 5) {
17303227Syq193411 master_if = cvs->cvs_buf[3];
17313227Syq193411 slave_if = cvs->cvs_buf[4];
17323227Syq193411 }
17333227Syq193411 break;
17343227Syq193411 default:
17353227Syq193411 break;
17363227Syq193411 }
17373227Syq193411 }
17383227Syq193411
17393227Syq193411 /* For usb acm devices, it must satisfy the following options. */
17403227Syq193411 if (cfg->cfg_n_if < 2) {
17413227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17423227Syq193411 "usbsacm_get_descriptors: # of interfaces %d < 2",
17433227Syq193411 cfg->cfg_n_if);
17443227Syq193411
17453227Syq193411 return (USB_FAILURE);
17463227Syq193411 }
17473227Syq193411
17483227Syq193411 if (acm_port->acm_data_if_no == 0 &&
17495541Slg150142 slave_if != acm_port->acm_data_if_no) {
17503227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17513227Syq193411 "usbsacm_get_descriptors: Device hasn't call management "
17523227Syq193411 "descriptor and use Union Descriptor.");
17533227Syq193411
17543227Syq193411 acm_port->acm_data_if_no = slave_if;
17553227Syq193411 }
17563227Syq193411
17573227Syq193411 if ((master_if != acm_port->acm_ctrl_if_no) ||
17583227Syq193411 (slave_if != acm_port->acm_data_if_no)) {
17593227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17603227Syq193411 "usbsacm_get_descriptors: control interface or "
17613227Syq193411 "data interface don't match.");
17623227Syq193411
17633227Syq193411 return (USB_FAILURE);
17643227Syq193411 }
17653227Syq193411
17663227Syq193411 /*
17673227Syq193411 * We usually need both call and data capabilities, but
17683227Syq193411 * some devices, such as Nokia mobile phones, don't provide
17693227Syq193411 * call management descriptor, so we just give a warning
17703227Syq193411 * message.
17713227Syq193411 */
17723227Syq193411 if (((mgmt_cap & USB_CDC_CALL_MGMT_CAP_CALL_MGMT) == 0) ||
17733227Syq193411 ((mgmt_cap & USB_CDC_CALL_MGMT_CAP_DATA_INTERFACE) == 0)) {
17743227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17753227Syq193411 "usbsacm_get_descriptors: "
17763227Syq193411 "insufficient mgmt capabilities %x",
17773227Syq193411 mgmt_cap);
17783227Syq193411 }
17793227Syq193411
17803227Syq193411 if ((acm_port->acm_ctrl_if_no >= cfg->cfg_n_if) ||
17813227Syq193411 (acm_port->acm_data_if_no >= cfg->cfg_n_if)) {
17823227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17833227Syq193411 "usbsacm_get_descriptors: control interface %d or "
17843227Syq193411 "data interface %d out of range.",
17853227Syq193411 acm_port->acm_ctrl_if_no, acm_port->acm_data_if_no);
17863227Syq193411
17873227Syq193411 return (USB_FAILURE);
17883227Syq193411 }
17893227Syq193411
17903227Syq193411 /* control interface must have interrupt endpoint */
17913227Syq193411 if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
17923227Syq193411 acm_port->acm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
17933227Syq193411 USB_EP_DIR_IN) == NULL) {
17943227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17953227Syq193411 "usbsacm_get_descriptors: "
17963227Syq193411 "ctrl interface %d has no interrupt endpoint",
17973227Syq193411 acm_port->acm_data_if_no);
17983227Syq193411
17993227Syq193411 return (USB_FAILURE);
18003227Syq193411 }
18013227Syq193411
18023227Syq193411 /* data interface must have bulk in and out */
18033227Syq193411 if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
18043227Syq193411 acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
18053227Syq193411 USB_EP_DIR_IN) == NULL) {
18063227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
18073227Syq193411 "usbsacm_get_descriptors: "
18083227Syq193411 "data interface %d has no bulk in endpoint",
18093227Syq193411 acm_port->acm_data_if_no);
18103227Syq193411
18113227Syq193411 return (USB_FAILURE);
18123227Syq193411 }
18133227Syq193411 if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
18143227Syq193411 acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
18153227Syq193411 USB_EP_DIR_OUT) == NULL) {
18163227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
18173227Syq193411 "usbsacm_get_descriptors: "
18183227Syq193411 "data interface %d has no bulk out endpoint",
18193227Syq193411 acm_port->acm_data_if_no);
18203227Syq193411
18213227Syq193411 return (USB_FAILURE);
18223227Syq193411 }
18233227Syq193411
18243227Syq193411 return (USB_SUCCESS);
18253227Syq193411 }
18263227Syq193411
18273227Syq193411
18283227Syq193411 /*
18293227Syq193411 * usbsacm_cleanup:
18303227Syq193411 * Release resources of current device during detach.
18313227Syq193411 */
18323227Syq193411 static void
usbsacm_cleanup(usbsacm_state_t * acmp)18333227Syq193411 usbsacm_cleanup(usbsacm_state_t *acmp)
18343227Syq193411 {
18353227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
18363227Syq193411 "usbsacm_cleanup: ");
18373227Syq193411
18383227Syq193411 if (acmp != NULL) {
18393227Syq193411 /* free ports */
18403227Syq193411 if (acmp->acm_ports != NULL) {
18413227Syq193411 usbsacm_free_ports(acmp);
18423227Syq193411 }
18433227Syq193411
18443227Syq193411 /* unregister callback function */
18453227Syq193411 if (acmp->acm_usb_events != NULL) {
18463227Syq193411 usb_unregister_event_cbs(acmp->acm_dip,
18475541Slg150142 acmp->acm_usb_events);
18483227Syq193411 }
18493227Syq193411
18503227Syq193411 /* destroy power management components */
18513227Syq193411 if (acmp->acm_pm != NULL) {
18523227Syq193411 usbsacm_destroy_pm_components(acmp);
18533227Syq193411 }
18543227Syq193411
18553227Syq193411 /* free description of device tree. */
18563227Syq193411 if (acmp->acm_def_ph != NULL) {
18573227Syq193411 mutex_destroy(&acmp->acm_mutex);
18583227Syq193411
18593227Syq193411 usb_free_descr_tree(acmp->acm_dip, acmp->acm_dev_data);
18603227Syq193411 acmp->acm_def_ph = NULL;
18613227Syq193411 }
18623227Syq193411
18633227Syq193411 if (acmp->acm_lh != NULL) {
18643227Syq193411 usb_free_log_hdl(acmp->acm_lh);
18653227Syq193411 acmp->acm_lh = NULL;
18663227Syq193411 }
18673227Syq193411
18683227Syq193411 /* detach client device */
18693227Syq193411 if (acmp->acm_dev_data != NULL) {
18703227Syq193411 usb_client_detach(acmp->acm_dip, acmp->acm_dev_data);
18713227Syq193411 }
18723227Syq193411
18733227Syq193411 kmem_free((caddr_t)acmp, sizeof (usbsacm_state_t));
18743227Syq193411 }
18753227Syq193411 }
18763227Syq193411
18773227Syq193411
18783227Syq193411 /*
18793227Syq193411 * usbsacm_restore_device_state:
18803227Syq193411 * restore device state after CPR resume or reconnect
18813227Syq193411 */
18823227Syq193411 static int
usbsacm_restore_device_state(usbsacm_state_t * acmp)18833227Syq193411 usbsacm_restore_device_state(usbsacm_state_t *acmp)
18843227Syq193411 {
18853227Syq193411 int state;
18863227Syq193411
18873227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
18883227Syq193411 "usbsacm_restore_device_state: ");
18893227Syq193411
18903227Syq193411 mutex_enter(&acmp->acm_mutex);
18913227Syq193411 state = acmp->acm_dev_state;
18923227Syq193411 mutex_exit(&acmp->acm_mutex);
18933227Syq193411
18943227Syq193411 /* Check device status */
18953227Syq193411 if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
18963227Syq193411
18973227Syq193411 return (state);
18983227Syq193411 }
18993227Syq193411
19003227Syq193411 /* Check if we are talking to the same device */
19013227Syq193411 if (usb_check_same_device(acmp->acm_dip, acmp->acm_lh, USB_LOG_L0,
19023227Syq193411 -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
19033227Syq193411 mutex_enter(&acmp->acm_mutex);
19043227Syq193411 state = acmp->acm_dev_state = USB_DEV_DISCONNECTED;
19053227Syq193411 mutex_exit(&acmp->acm_mutex);
19063227Syq193411
19073227Syq193411 return (state);
19083227Syq193411 }
19093227Syq193411
19103227Syq193411 if (state == USB_DEV_DISCONNECTED) {
19113227Syq193411 USB_DPRINTF_L1(PRINT_MASK_ALL, acmp->acm_lh,
19123227Syq193411 "usbsacm_restore_device_state: Device has been reconnected "
19133227Syq193411 "but data may have been lost");
19143227Syq193411 }
19153227Syq193411
19163227Syq193411 /* reconnect pipes */
19173227Syq193411 if (usbsacm_reconnect_pipes(acmp) != USB_SUCCESS) {
19183227Syq193411
19193227Syq193411 return (state);
19203227Syq193411 }
19213227Syq193411
19223227Syq193411 /*
19233227Syq193411 * init device state
19243227Syq193411 */
19253227Syq193411 mutex_enter(&acmp->acm_mutex);
19263227Syq193411 state = acmp->acm_dev_state = USB_DEV_ONLINE;
19273227Syq193411 mutex_exit(&acmp->acm_mutex);
19283227Syq193411
19293227Syq193411 if ((usbsacm_restore_port_state(acmp) != USB_SUCCESS)) {
19303227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
19313227Syq193411 "usbsacm_restore_device_state: failed");
19323227Syq193411 }
19333227Syq193411
19343227Syq193411 return (state);
19353227Syq193411 }
19363227Syq193411
19373227Syq193411
19383227Syq193411 /*
19393227Syq193411 * usbsacm_restore_port_state:
19403227Syq193411 * restore ports state after CPR resume or reconnect
19413227Syq193411 */
19423227Syq193411 static int
usbsacm_restore_port_state(usbsacm_state_t * acmp)19433227Syq193411 usbsacm_restore_port_state(usbsacm_state_t *acmp)
19443227Syq193411 {
19453227Syq193411 int i, ret = USB_SUCCESS;
19463227Syq193411 usbsacm_port_t *cur_port;
19473227Syq193411
19483227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
19493227Syq193411 "usbsacm_restore_port_state: ");
19503227Syq193411
19513227Syq193411 /* restore status of all ports */
19523227Syq193411 for (i = 0; i < acmp->acm_port_cnt; i++) {
19533227Syq193411 cur_port = &acmp->acm_ports[i];
19543227Syq193411 mutex_enter(&cur_port->acm_port_mutex);
19553227Syq193411 if (cur_port->acm_port_state != USBSACM_PORT_OPEN) {
19563227Syq193411 mutex_exit(&cur_port->acm_port_mutex);
19573227Syq193411
19583227Syq193411 continue;
19593227Syq193411 }
19603227Syq193411 mutex_exit(&cur_port->acm_port_mutex);
19613227Syq193411
19623227Syq193411 if ((ret = usbsacm_set_line_coding(cur_port,
19633227Syq193411 &cur_port->acm_line_coding)) != USB_SUCCESS) {
19643227Syq193411 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
19653227Syq193411 "usbsacm_restore_port_state: failed.");
19663227Syq193411 }
19673227Syq193411 }
19683227Syq193411
19693227Syq193411 return (ret);
19703227Syq193411 }
19713227Syq193411
19723227Syq193411
19733227Syq193411 /*
19743227Syq193411 * pipe management
19753227Syq193411 * ---------------
19763227Syq193411 *
19773227Syq193411 *
19783227Syq193411 * usbsacm_open_port_pipes:
19793227Syq193411 * Open pipes of one port and set port structure;
19803227Syq193411 * Each port includes three pipes: bulk in, bulk out and interrupt.
19813227Syq193411 */
19823227Syq193411 static int
usbsacm_open_port_pipes(usbsacm_port_t * acm_port)19833227Syq193411 usbsacm_open_port_pipes(usbsacm_port_t *acm_port)
19843227Syq193411 {
19853227Syq193411 int rval = USB_SUCCESS;
19863227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
19873227Syq193411 usb_ep_data_t *in_data, *out_data, *intr_pipe;
19883227Syq193411 usb_pipe_policy_t policy;
19893227Syq193411
19903227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
19916898Sfb209375 "usbsacm_open_port_pipes: acmp = 0x%p", (void *)acmp);
19923227Syq193411
19933227Syq193411 /* Get bulk and interrupt endpoint data */
19943227Syq193411 intr_pipe = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
19953227Syq193411 acm_port->acm_ctrl_if_no, 0, 0,
19963227Syq193411 USB_EP_ATTR_INTR, USB_EP_DIR_IN);
19973227Syq193411 in_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
19983227Syq193411 acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
19993227Syq193411 USB_EP_ATTR_BULK, USB_EP_DIR_IN);
20003227Syq193411 out_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
20013227Syq193411 acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
20023227Syq193411 USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
20033227Syq193411
20043227Syq193411 /* Bulk in and out must exist meanwhile. */
20053227Syq193411 if ((in_data == NULL) || (out_data == NULL)) {
20063227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20073227Syq193411 "usbsacm_open_port_pipes: look up bulk pipe failed in "
20083227Syq193411 "interface %d port %d",
20093227Syq193411 acm_port->acm_data_if_no, acm_port->acm_data_port_no);
20103227Syq193411
20113227Syq193411 return (USB_FAILURE);
20123227Syq193411 }
20133227Syq193411
20143227Syq193411 /*
20153227Syq193411 * If device conform to acm spec, it must have an interrupt pipe
20163227Syq193411 * for this port.
20173227Syq193411 */
20183227Syq193411 if (acmp->acm_compatibility == B_TRUE && intr_pipe == NULL) {
20193227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20203227Syq193411 "usbsacm_open_port_pipes: look up interrupt pipe failed in "
20213227Syq193411 "interface %d", acm_port->acm_ctrl_if_no);
20223227Syq193411
20233227Syq193411 return (USB_FAILURE);
20243227Syq193411 }
20253227Syq193411
20263227Syq193411 policy.pp_max_async_reqs = 2;
20273227Syq193411
20283227Syq193411 /* Open bulk in endpoint */
20293227Syq193411 if (usb_pipe_open(acmp->acm_dip, &in_data->ep_descr, &policy,
20303227Syq193411 USB_FLAGS_SLEEP, &acm_port->acm_bulkin_ph) != USB_SUCCESS) {
20313227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20323227Syq193411 "usbsacm_open_port_pipes: open bulkin pipe failed!");
20333227Syq193411
20343227Syq193411 return (USB_FAILURE);
20353227Syq193411 }
20363227Syq193411
20373227Syq193411 /* Open bulk out endpoint */
20383227Syq193411 if (usb_pipe_open(acmp->acm_dip, &out_data->ep_descr, &policy,
20393227Syq193411 USB_FLAGS_SLEEP, &acm_port->acm_bulkout_ph) != USB_SUCCESS) {
20403227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20413227Syq193411 "usbsacm_open_port_pipes: open bulkout pipe failed!");
20423227Syq193411
20433227Syq193411 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
20443227Syq193411 USB_FLAGS_SLEEP, NULL, NULL);
20453227Syq193411
20463227Syq193411 return (USB_FAILURE);
20473227Syq193411 }
20483227Syq193411
20493227Syq193411 /* Open interrupt endpoint if found. */
20503227Syq193411 if (intr_pipe != NULL) {
20513227Syq193411
20523227Syq193411 if (usb_pipe_open(acmp->acm_dip, &intr_pipe->ep_descr, &policy,
20533227Syq193411 USB_FLAGS_SLEEP, &acm_port->acm_intr_ph) != USB_SUCCESS) {
20543227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20553227Syq193411 "usbsacm_open_port_pipes: "
20563227Syq193411 "open control pipe failed");
20573227Syq193411
20583227Syq193411 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
20593227Syq193411 USB_FLAGS_SLEEP, NULL, NULL);
20603227Syq193411 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
20613227Syq193411 USB_FLAGS_SLEEP, NULL, NULL);
20623227Syq193411
20633227Syq193411 return (USB_FAILURE);
20643227Syq193411 }
20653227Syq193411 }
20663227Syq193411
20673227Syq193411 /* initialize the port structure. */
20683227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
20693227Syq193411 acm_port->acm_bulkin_size = in_data->ep_descr.wMaxPacketSize;
20703227Syq193411 acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
20713227Syq193411 acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
20723227Syq193411 if (acm_port->acm_intr_ph != NULL) {
20733227Syq193411 acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
20743227Syq193411 acm_port->acm_intr_ep_descr = intr_pipe->ep_descr;
20753227Syq193411 }
20763227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
20773227Syq193411
20783227Syq193411 if (acm_port->acm_intr_ph != NULL) {
20793227Syq193411
20803227Syq193411 usbsacm_pipe_start_polling(acm_port);
20813227Syq193411 }
20823227Syq193411
20833227Syq193411 return (rval);
20843227Syq193411 }
20853227Syq193411
20863227Syq193411
20873227Syq193411 /*
20883227Syq193411 * usbsacm_close_port_pipes:
20893227Syq193411 * Close pipes of one port and reset port structure to closed;
20903227Syq193411 * Each port includes three pipes: bulk in, bulk out and interrupt.
20913227Syq193411 */
20923227Syq193411 static void
usbsacm_close_port_pipes(usbsacm_port_t * acm_port)20933227Syq193411 usbsacm_close_port_pipes(usbsacm_port_t *acm_port)
20943227Syq193411 {
20953227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
20963227Syq193411
20973227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
20983227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
20993227Syq193411 "usbsacm_close_port_pipes: acm_bulkin_state = %d",
21003227Syq193411 acm_port->acm_bulkin_state);
21013227Syq193411
21023227Syq193411 /*
21033227Syq193411 * Check the status of the given port. If port is closing or closed,
21043227Syq193411 * return directly.
21053227Syq193411 */
21063227Syq193411 if ((acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSED) ||
21073227Syq193411 (acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSING)) {
21083227Syq193411 USB_DPRINTF_L2(PRINT_MASK_CLOSE, acmp->acm_lh,
21093227Syq193411 "usbsacm_close_port_pipes: port is closing or has closed");
21103227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
21113227Syq193411
21123227Syq193411 return;
21133227Syq193411 }
21143227Syq193411
21153227Syq193411 acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSING;
21163227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
21173227Syq193411
21183227Syq193411 /* Close pipes */
21193227Syq193411 usb_pipe_reset(acmp->acm_dip, acm_port->acm_bulkin_ph,
21203227Syq193411 USB_FLAGS_SLEEP, 0, 0);
21213227Syq193411 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
21223227Syq193411 USB_FLAGS_SLEEP, 0, 0);
21233227Syq193411 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
21243227Syq193411 USB_FLAGS_SLEEP, 0, 0);
21253227Syq193411 if (acm_port->acm_intr_ph != NULL) {
21263227Syq193411 usb_pipe_stop_intr_polling(acm_port->acm_intr_ph,
21273227Syq193411 USB_FLAGS_SLEEP);
21283227Syq193411 usb_pipe_close(acmp->acm_dip, acm_port->acm_intr_ph,
21293227Syq193411 USB_FLAGS_SLEEP, 0, 0);
21303227Syq193411 }
21313227Syq193411
21323227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
21333227Syq193411 /* Reset the status of pipes to closed */
21343227Syq193411 acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSED;
21353227Syq193411 acm_port->acm_bulkin_ph = NULL;
21363227Syq193411 acm_port->acm_bulkout_state = USBSACM_PIPE_CLOSED;
21373227Syq193411 acm_port->acm_bulkout_ph = NULL;
21383227Syq193411 if (acm_port->acm_intr_ph != NULL) {
21393227Syq193411 acm_port->acm_intr_state = USBSACM_PIPE_CLOSED;
21403227Syq193411 acm_port->acm_intr_ph = NULL;
21413227Syq193411 }
21423227Syq193411
21433227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
21443227Syq193411
21453227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
21463227Syq193411 "usbsacm_close_port_pipes: port has been closed.");
21473227Syq193411 }
21483227Syq193411
21493227Syq193411
21503227Syq193411 /*
21513227Syq193411 * usbsacm_close_pipes:
21523227Syq193411 * close all opened pipes of current devices.
21533227Syq193411 */
21543227Syq193411 static void
usbsacm_close_pipes(usbsacm_state_t * acmp)21553227Syq193411 usbsacm_close_pipes(usbsacm_state_t *acmp)
21563227Syq193411 {
21573227Syq193411 int i;
21583227Syq193411
21593227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
21603227Syq193411 "usbsacm_close_pipes: ");
21613227Syq193411
21623227Syq193411 /* Close all ports */
21633227Syq193411 for (i = 0; i < acmp->acm_port_cnt; i++) {
21643227Syq193411 usbsacm_close_port_pipes(&acmp->acm_ports[i]);
21653227Syq193411 }
21663227Syq193411 }
21673227Syq193411
21683227Syq193411
21693227Syq193411 /*
21703227Syq193411 * usbsacm_disconnect_pipes:
21713227Syq193411 * this function just call usbsacm_close_pipes.
21723227Syq193411 */
21733227Syq193411 static void
usbsacm_disconnect_pipes(usbsacm_state_t * acmp)21743227Syq193411 usbsacm_disconnect_pipes(usbsacm_state_t *acmp)
21753227Syq193411 {
21763227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
21773227Syq193411 "usbsacm_disconnect_pipes: ");
21783227Syq193411
21793227Syq193411 usbsacm_close_pipes(acmp);
21803227Syq193411 }
21813227Syq193411
21823227Syq193411
21833227Syq193411 /*
21843227Syq193411 * usbsacm_reconnect_pipes:
21853227Syq193411 * reconnect pipes in CPR resume or reconnect
21863227Syq193411 */
21873227Syq193411 static int
usbsacm_reconnect_pipes(usbsacm_state_t * acmp)21883227Syq193411 usbsacm_reconnect_pipes(usbsacm_state_t *acmp)
21893227Syq193411 {
21903227Syq193411 usbsacm_port_t *cur_port = acmp->acm_ports;
21913227Syq193411 int i;
21923227Syq193411
21933227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
21943227Syq193411 "usbsacm_reconnect_pipes: ");
21953227Syq193411
21963227Syq193411 /* reopen all ports of current device. */
21973227Syq193411 for (i = 0; i < acmp->acm_port_cnt; i++) {
21983227Syq193411 cur_port = &acmp->acm_ports[i];
21993227Syq193411
22003227Syq193411 mutex_enter(&cur_port->acm_port_mutex);
22013227Syq193411 /*
22023227Syq193411 * If port status is open, reopen it;
22033227Syq193411 * else retain the current status.
22043227Syq193411 */
22053227Syq193411 if (cur_port->acm_port_state == USBSACM_PORT_OPEN) {
22063227Syq193411
22073227Syq193411 mutex_exit(&cur_port->acm_port_mutex);
22083227Syq193411 if (usbsacm_open_port_pipes(cur_port) != USB_SUCCESS) {
22093227Syq193411 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
22103227Syq193411 "usbsacm_reconnect_pipes: "
22113227Syq193411 "open port %d failed.", i);
22123227Syq193411
22133227Syq193411 return (USB_FAILURE);
22143227Syq193411 }
22153227Syq193411 mutex_enter(&cur_port->acm_port_mutex);
22163227Syq193411 }
22173227Syq193411 mutex_exit(&cur_port->acm_port_mutex);
22183227Syq193411 }
22193227Syq193411
22203227Syq193411 return (USB_SUCCESS);
22213227Syq193411 }
22223227Syq193411
22233227Syq193411 /*
22243227Syq193411 * usbsacm_bulkin_cb:
22253227Syq193411 * Bulk In regular and exeception callback;
22263227Syq193411 * USBA framework will call this callback
22273227Syq193411 * after deal with bulkin request.
22283227Syq193411 */
22293227Syq193411 /*ARGSUSED*/
22303227Syq193411 static void
usbsacm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)22313227Syq193411 usbsacm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
22323227Syq193411 {
22333227Syq193411 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->bulk_client_private;
22343227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
22353227Syq193411 mblk_t *data;
22363227Syq193411 int data_len;
22373227Syq193411
22383227Syq193411 data = req->bulk_data;
22393227Syq193411 data_len = (data) ? MBLKL(data) : 0;
22403227Syq193411
22413227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
22423227Syq193411 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
22433227Syq193411 "usbsacm_bulkin_cb: "
22443227Syq193411 "acm_bulkin_state = %d acm_port_state = %d data_len = %d",
22453227Syq193411 acm_port->acm_bulkin_state, acm_port->acm_port_state, data_len);
22463227Syq193411
22473227Syq193411 if ((acm_port->acm_port_state == USBSACM_PORT_OPEN) && (data_len) &&
22483227Syq193411 (req->bulk_completion_reason == USB_CR_OK)) {
22493227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
22503227Syq193411 /* prevent USBA from freeing data along with the request */
22513227Syq193411 req->bulk_data = NULL;
22523227Syq193411
22533227Syq193411 /* save data on the receive list */
22543227Syq193411 usbsacm_put_tail(&acm_port->acm_rx_mp, data);
22553227Syq193411
22563227Syq193411 /* invoke GSD receive callback */
22573227Syq193411 if (acm_port->acm_cb.cb_rx) {
22583227Syq193411 acm_port->acm_cb.cb_rx(acm_port->acm_cb.cb_arg);
22593227Syq193411 }
22603227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
22613227Syq193411 }
22623227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
22633227Syq193411
22643227Syq193411 usb_free_bulk_req(req);
22653227Syq193411
22663227Syq193411 /* receive more */
22673227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
22683227Syq193411 if (((acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) ||
22693227Syq193411 (acm_port->acm_bulkin_state == USBSACM_PIPE_IDLE)) &&
22703227Syq193411 (acm_port->acm_port_state == USBSACM_PORT_OPEN) &&
22713227Syq193411 (acmp->acm_dev_state == USB_DEV_ONLINE)) {
22723227Syq193411 if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
22733227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
22743227Syq193411 "usbsacm_bulkin_cb: restart rx fail "
22753227Syq193411 "acm_port_state = %d", acm_port->acm_port_state);
22763227Syq193411 }
22773227Syq193411 } else if (acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) {
22783227Syq193411 acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
22793227Syq193411 }
22803227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
22813227Syq193411 }
22823227Syq193411
22833227Syq193411
22843227Syq193411 /*
22853227Syq193411 * usbsacm_bulkout_cb:
22863227Syq193411 * Bulk Out regular and exeception callback;
22873227Syq193411 * USBA framework will call this callback function
22883227Syq193411 * after deal with bulkout request.
22893227Syq193411 */
22903227Syq193411 /*ARGSUSED*/
22913227Syq193411 static void
usbsacm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)22923227Syq193411 usbsacm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
22933227Syq193411 {
22943227Syq193411 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->bulk_client_private;
22953227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
22963227Syq193411 int data_len;
22973227Syq193411 mblk_t *data = req->bulk_data;
22983227Syq193411
22993227Syq193411 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
23006898Sfb209375 "usbsacm_bulkout_cb: acmp = 0x%p", (void *)acmp);
23013227Syq193411
23023227Syq193411 data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
23033227Syq193411
23043227Syq193411 /* put untransferred residue back on the transfer list */
23053227Syq193411 if (req->bulk_completion_reason && (data_len > 0)) {
23063227Syq193411 usbsacm_put_head(&acm_port->acm_tx_mp, data);
23073227Syq193411 req->bulk_data = NULL;
23083227Syq193411 }
23093227Syq193411
23103227Syq193411 usb_free_bulk_req(req);
23113227Syq193411
23123227Syq193411 /* invoke GSD transmit callback */
23133227Syq193411 if (acm_port->acm_cb.cb_tx) {
23143227Syq193411 acm_port->acm_cb.cb_tx(acm_port->acm_cb.cb_arg);
23153227Syq193411 }
23163227Syq193411
23173227Syq193411 /* send more */
23183227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
23193227Syq193411 acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
23203227Syq193411 if (acm_port->acm_tx_mp == NULL) {
23213227Syq193411 cv_broadcast(&acm_port->acm_tx_cv);
23223227Syq193411 } else {
23233227Syq193411 usbsacm_tx_start(acm_port);
23243227Syq193411 }
23253227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
23263227Syq193411 }
23273227Syq193411
23283227Syq193411
23293227Syq193411 /*
23303227Syq193411 * usbsacm_rx_start:
23313227Syq193411 * start data receipt
23323227Syq193411 */
23333227Syq193411 static int
usbsacm_rx_start(usbsacm_port_t * acm_port)23343227Syq193411 usbsacm_rx_start(usbsacm_port_t *acm_port)
23353227Syq193411 {
23363227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
23373227Syq193411 usb_bulk_req_t *br;
23383227Syq193411 int rval = USB_FAILURE;
23393227Syq193411 int data_len;
23403227Syq193411
23413227Syq193411 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
23426898Sfb209375 "usbsacm_rx_start: acm_xfer_sz = 0x%lx acm_bulkin_size = 0x%lx",
23433227Syq193411 acmp->acm_xfer_sz, acm_port->acm_bulkin_size);
23443227Syq193411
23453227Syq193411 acm_port->acm_bulkin_state = USBSACM_PIPE_BUSY;
23463227Syq193411 /*
23473227Syq193411 * Qualcomm CDMA card won't response the first request,
23483227Syq193411 * if the following code don't multiply by 2.
23493227Syq193411 */
23503227Syq193411 data_len = min(acmp->acm_xfer_sz, acm_port->acm_bulkin_size * 2);
23513227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
23523227Syq193411
23533227Syq193411 br = usb_alloc_bulk_req(acmp->acm_dip, data_len, USB_FLAGS_SLEEP);
23543227Syq193411 if (br == NULL) {
23553227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
23563227Syq193411 "usbsacm_rx_start: allocate bulk request failed");
23573227Syq193411
23583227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
23593227Syq193411
23603227Syq193411 return (USB_FAILURE);
23613227Syq193411 }
23623227Syq193411 /* initialize bulk in request. */
23633227Syq193411 br->bulk_len = data_len;
23643227Syq193411 br->bulk_timeout = USBSACM_BULKIN_TIMEOUT;
23653227Syq193411 br->bulk_cb = usbsacm_bulkin_cb;
23663227Syq193411 br->bulk_exc_cb = usbsacm_bulkin_cb;
23673227Syq193411 br->bulk_client_private = (usb_opaque_t)acm_port;
23683227Syq193411 br->bulk_attributes = USB_ATTRS_AUTOCLEARING
23693227Syq193411 | USB_ATTRS_SHORT_XFER_OK;
23703227Syq193411
23713227Syq193411 rval = usb_pipe_bulk_xfer(acm_port->acm_bulkin_ph, br, 0);
23723227Syq193411
23733227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
23743227Syq193411 if (rval != USB_SUCCESS) {
23753227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
23763227Syq193411 "usbsacm_rx_start: bulk transfer failed %d", rval);
23773227Syq193411 usb_free_bulk_req(br);
23783227Syq193411 acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
23793227Syq193411 }
23803227Syq193411
23813227Syq193411 return (rval);
23823227Syq193411 }
23833227Syq193411
23843227Syq193411
23853227Syq193411 /*
23863227Syq193411 * usbsacm_tx_start:
23873227Syq193411 * start data transmit
23883227Syq193411 */
23893227Syq193411 static void
usbsacm_tx_start(usbsacm_port_t * acm_port)23903227Syq193411 usbsacm_tx_start(usbsacm_port_t *acm_port)
23913227Syq193411 {
23923227Syq193411 int len; /* bytes we can transmit */
23933227Syq193411 mblk_t *data; /* data to be transmitted */
23943227Syq193411 int data_len; /* bytes in 'data' */
23953227Syq193411 mblk_t *mp; /* current msgblk */
23963227Syq193411 int copylen; /* bytes copy from 'mp' to 'data' */
23973227Syq193411 int rval;
23983227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
23993227Syq193411
24003227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
24013227Syq193411 "usbsacm_tx_start: ");
24023227Syq193411
24033227Syq193411 /* check the transmitted data. */
24043227Syq193411 if (acm_port->acm_tx_mp == NULL) {
24053227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24063227Syq193411 "usbsacm_tx_start: acm_tx_mp is NULL");
24073227Syq193411
24083227Syq193411 return;
24093227Syq193411 }
24103227Syq193411
24113227Syq193411 /* check pipe status */
24123227Syq193411 if (acm_port->acm_bulkout_state != USBSACM_PIPE_IDLE) {
24133227Syq193411
24143227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24153227Syq193411 "usbsacm_tx_start: error state in bulkout endpoint");
24163227Syq193411
24173227Syq193411 return;
24183227Syq193411 }
24193227Syq193411 ASSERT(MBLKL(acm_port->acm_tx_mp) > 0);
24203227Syq193411
24213227Syq193411 /* send as much data as port can receive */
24223227Syq193411 len = min(msgdsize(acm_port->acm_tx_mp), acmp->acm_xfer_sz);
24233227Syq193411
24243227Syq193411 if (len == 0) {
24253227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24263227Syq193411 "usbsacm_tx_start: data len is 0");
24273227Syq193411
24283227Syq193411 return;
24293227Syq193411 }
24303227Syq193411
24313227Syq193411 /* allocate memory for sending data. */
24323227Syq193411 if ((data = allocb(len, BPRI_LO)) == NULL) {
24333227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24343227Syq193411 "usbsacm_tx_start: failure in allocate memory");
24353227Syq193411
24363227Syq193411 return;
24373227Syq193411 }
24383227Syq193411
24393227Syq193411 /*
24403227Syq193411 * copy no more than 'len' bytes from mblk chain to transmit mblk 'data'
24413227Syq193411 */
24423227Syq193411 data_len = 0;
24433227Syq193411 while ((data_len < len) && acm_port->acm_tx_mp) {
24443227Syq193411 /* Get the first mblk from chain. */
24453227Syq193411 mp = acm_port->acm_tx_mp;
24463227Syq193411 copylen = min(MBLKL(mp), len - data_len);
24473227Syq193411 bcopy(mp->b_rptr, data->b_wptr, copylen);
24483227Syq193411 mp->b_rptr += copylen;
24493227Syq193411 data->b_wptr += copylen;
24503227Syq193411 data_len += copylen;
24513227Syq193411
24526990Sgd78059 if (MBLKL(mp) < 1) {
24533227Syq193411 acm_port->acm_tx_mp = unlinkb(mp);
24543227Syq193411 freeb(mp);
24553227Syq193411 } else {
24563227Syq193411 ASSERT(data_len == len);
24573227Syq193411 }
24583227Syq193411 }
24593227Syq193411
24603227Syq193411 if (data_len <= 0) {
24613227Syq193411 freeb(data);
24623227Syq193411
24633227Syq193411 return;
24643227Syq193411 }
24653227Syq193411
24663227Syq193411 acm_port->acm_bulkout_state = USBSACM_PIPE_BUSY;
24673227Syq193411
24683227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
24693227Syq193411 /* send request. */
24703227Syq193411 rval = usbsacm_send_data(acm_port, data);
24713227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
24723227Syq193411
24733227Syq193411 /*
24743227Syq193411 * If send failed, retransmit data when acm_tx_mp is null.
24753227Syq193411 */
24763227Syq193411 if (rval != USB_SUCCESS) {
24773227Syq193411 acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
24783227Syq193411 if (acm_port->acm_tx_mp == NULL) {
24793227Syq193411 usbsacm_put_head(&acm_port->acm_tx_mp, data);
24803227Syq193411 }
24813227Syq193411 }
24823227Syq193411 }
24833227Syq193411
24843227Syq193411
24853227Syq193411 /*
24863227Syq193411 * usbsacm_send_data:
24873227Syq193411 * data transfer
24883227Syq193411 */
24893227Syq193411 static int
usbsacm_send_data(usbsacm_port_t * acm_port,mblk_t * data)24903227Syq193411 usbsacm_send_data(usbsacm_port_t *acm_port, mblk_t *data)
24913227Syq193411 {
24923227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
24933227Syq193411 usb_bulk_req_t *br;
24943227Syq193411 int rval;
24953227Syq193411 int data_len = MBLKL(data);
24963227Syq193411
24973227Syq193411 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
24983227Syq193411 "usbsacm_send_data: data address is 0x%p, length = %d",
24996898Sfb209375 (void *)data, data_len);
25003227Syq193411
25013227Syq193411 br = usb_alloc_bulk_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
25023227Syq193411 if (br == NULL) {
25033227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
25043227Syq193411 "usbsacm_send_data: alloc req failed.");
25053227Syq193411
25063227Syq193411 return (USB_FAILURE);
25073227Syq193411 }
25083227Syq193411
25093227Syq193411 /* initialize the bulk out request */
25103227Syq193411 br->bulk_data = data;
25113227Syq193411 br->bulk_len = data_len;
25123227Syq193411 br->bulk_timeout = USBSACM_BULKOUT_TIMEOUT;
25133227Syq193411 br->bulk_cb = usbsacm_bulkout_cb;
25143227Syq193411 br->bulk_exc_cb = usbsacm_bulkout_cb;
25153227Syq193411 br->bulk_client_private = (usb_opaque_t)acm_port;
25163227Syq193411 br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
25173227Syq193411
25183227Syq193411 rval = usb_pipe_bulk_xfer(acm_port->acm_bulkout_ph, br, 0);
25193227Syq193411
25203227Syq193411 if (rval != USB_SUCCESS) {
25213227Syq193411 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
25223227Syq193411 "usbsacm_send_data: Send Data failed.");
25233227Syq193411
25243227Syq193411 /*
25253227Syq193411 * Don't free it in usb_free_bulk_req because it will
25263227Syq193411 * be linked in usbsacm_put_head
25273227Syq193411 */
25283227Syq193411 br->bulk_data = NULL;
25293227Syq193411
25303227Syq193411 usb_free_bulk_req(br);
25313227Syq193411 }
25323227Syq193411
25333227Syq193411 return (rval);
25343227Syq193411 }
25353227Syq193411
25363227Syq193411 /*
25373227Syq193411 * usbsacm_wait_tx_drain:
25383227Syq193411 * wait until local tx buffer drains.
25393227Syq193411 * 'timeout' is in seconds, zero means wait forever
25403227Syq193411 */
25413227Syq193411 static int
usbsacm_wait_tx_drain(usbsacm_port_t * acm_port,int timeout)25423227Syq193411 usbsacm_wait_tx_drain(usbsacm_port_t *acm_port, int timeout)
25433227Syq193411 {
25443227Syq193411 clock_t until;
25453227Syq193411 int over = 0;
25463227Syq193411
25473227Syq193411 until = ddi_get_lbolt() + drv_usectohz(1000 * 1000 * timeout);
25483227Syq193411
25493227Syq193411 while (acm_port->acm_tx_mp && !over) {
25503227Syq193411 if (timeout > 0) {
25513227Syq193411 over = (cv_timedwait_sig(&acm_port->acm_tx_cv,
25523227Syq193411 &acm_port->acm_port_mutex, until) <= 0);
25533227Syq193411 } else {
25543227Syq193411 over = (cv_wait_sig(&acm_port->acm_tx_cv,
25553227Syq193411 &acm_port->acm_port_mutex) == 0);
25563227Syq193411 }
25573227Syq193411 }
25583227Syq193411
25593227Syq193411 return ((acm_port->acm_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE);
25603227Syq193411 }
25613227Syq193411
25623227Syq193411
25633227Syq193411 /*
25643227Syq193411 * usbsacm_req_write:
25653227Syq193411 * send command over control pipe
25663227Syq193411 */
25673227Syq193411 static int
usbsacm_req_write(usbsacm_port_t * acm_port,uchar_t request,uint16_t value,mblk_t ** data)25683227Syq193411 usbsacm_req_write(usbsacm_port_t *acm_port, uchar_t request, uint16_t value,
25693227Syq193411 mblk_t **data)
25703227Syq193411 {
25713227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
25723227Syq193411 usb_ctrl_setup_t setup;
25733227Syq193411 usb_cb_flags_t cb_flags;
25743227Syq193411 usb_cr_t cr;
25753227Syq193411
25763227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
25773227Syq193411 "usbsacm_req_write: ");
25783227Syq193411
25793227Syq193411 /* initialize the control request. */
25803227Syq193411 setup.bmRequestType = USBSACM_REQ_WRITE_IF;
25813227Syq193411 setup.bRequest = request;
25823227Syq193411 setup.wValue = value;
25833227Syq193411 setup.wIndex = acm_port->acm_ctrl_if_no;
25843227Syq193411 setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
25853227Syq193411 setup.attrs = 0;
25863227Syq193411
25873227Syq193411 return (usb_pipe_ctrl_xfer_wait(acmp->acm_def_ph, &setup, data,
25883227Syq193411 &cr, &cb_flags, 0));
25893227Syq193411 }
25903227Syq193411
25913227Syq193411
25923227Syq193411 /*
25933227Syq193411 * usbsacm_set_line_coding:
25943227Syq193411 * Send USB_CDC_REQ_SET_LINE_CODING request
25953227Syq193411 */
25963227Syq193411 static int
usbsacm_set_line_coding(usbsacm_port_t * acm_port,usb_cdc_line_coding_t * lc)25973227Syq193411 usbsacm_set_line_coding(usbsacm_port_t *acm_port, usb_cdc_line_coding_t *lc)
25983227Syq193411 {
25993227Syq193411 mblk_t *bp;
26003227Syq193411 int ret;
26013227Syq193411
26023227Syq193411 /* allocate mblk and copy supplied structure into it */
26033227Syq193411 if ((bp = allocb(USB_CDC_LINE_CODING_LEN, BPRI_HI)) == NULL) {
26043227Syq193411
26053227Syq193411 return (USB_NO_RESOURCES);
26063227Syq193411 }
26073227Syq193411
26083227Syq193411 #ifndef __lock_lint /* warlock gets confused here */
26097492SZhigang.Lu@Sun.COM /* LINTED E_BAD_PTR_CAST_ALIGN */
26103227Syq193411 *((usb_cdc_line_coding_t *)bp->b_wptr) = *lc;
26113227Syq193411 bp->b_wptr += USB_CDC_LINE_CODING_LEN;
26123227Syq193411 #endif
26133227Syq193411
26143227Syq193411 ret = usbsacm_req_write(acm_port, USB_CDC_REQ_SET_LINE_CODING, 0, &bp);
26153227Syq193411
26163227Syq193411 if (bp != NULL) {
26173227Syq193411 freeb(bp);
26183227Syq193411 }
26193227Syq193411
26203227Syq193411 return (ret);
26213227Syq193411 }
26223227Syq193411
26233227Syq193411
26243227Syq193411
26253227Syq193411 /*
26263227Syq193411 * usbsacm_mctl2reg:
26273227Syq193411 * Set Modem control status
26283227Syq193411 */
26293227Syq193411 static void
usbsacm_mctl2reg(int mask,int val,uint8_t * line_ctl)26303227Syq193411 usbsacm_mctl2reg(int mask, int val, uint8_t *line_ctl)
26313227Syq193411 {
26323227Syq193411 if (mask & TIOCM_RTS) {
26333227Syq193411 if (val & TIOCM_RTS) {
26343227Syq193411 *line_ctl |= USB_CDC_ACM_CONTROL_RTS;
26353227Syq193411 } else {
26363227Syq193411 *line_ctl &= ~USB_CDC_ACM_CONTROL_RTS;
26373227Syq193411 }
26383227Syq193411 }
26393227Syq193411 if (mask & TIOCM_DTR) {
26403227Syq193411 if (val & TIOCM_DTR) {
26413227Syq193411 *line_ctl |= USB_CDC_ACM_CONTROL_DTR;
26423227Syq193411 } else {
26433227Syq193411 *line_ctl &= ~USB_CDC_ACM_CONTROL_DTR;
26443227Syq193411 }
26453227Syq193411 }
26463227Syq193411 }
26473227Syq193411
26483227Syq193411
26493227Syq193411 /*
26503227Syq193411 * usbsacm_reg2mctl:
26513227Syq193411 * Get Modem control status
26523227Syq193411 */
26533227Syq193411 static int
usbsacm_reg2mctl(uint8_t line_ctl)26543227Syq193411 usbsacm_reg2mctl(uint8_t line_ctl)
26553227Syq193411 {
26563227Syq193411 int val = 0;
26573227Syq193411
26583227Syq193411 if (line_ctl & USB_CDC_ACM_CONTROL_RTS) {
26593227Syq193411 val |= TIOCM_RTS;
26603227Syq193411 }
26613227Syq193411 if (line_ctl & USB_CDC_ACM_CONTROL_DTR) {
26623227Syq193411 val |= TIOCM_DTR;
26633227Syq193411 }
26643227Syq193411 if (line_ctl & USB_CDC_ACM_CONTROL_DSR) {
26653227Syq193411 val |= TIOCM_DSR;
26663227Syq193411 }
26673227Syq193411 if (line_ctl & USB_CDC_ACM_CONTROL_RNG) {
26683227Syq193411 val |= TIOCM_RI;
26693227Syq193411 }
26703227Syq193411
26713227Syq193411 return (val);
26723227Syq193411 }
26733227Syq193411
26743227Syq193411
26753227Syq193411 /*
26763227Syq193411 * misc routines
26773227Syq193411 * -------------
26783227Syq193411 *
26793227Syq193411 */
26803227Syq193411
26813227Syq193411 /*
26823227Syq193411 * usbsacm_put_tail:
26833227Syq193411 * link a message block to tail of message
26843227Syq193411 * account for the case when message is null
26853227Syq193411 */
26863227Syq193411 static void
usbsacm_put_tail(mblk_t ** mpp,mblk_t * bp)26873227Syq193411 usbsacm_put_tail(mblk_t **mpp, mblk_t *bp)
26883227Syq193411 {
26893227Syq193411 if (*mpp) {
26903227Syq193411 linkb(*mpp, bp);
26913227Syq193411 } else {
26923227Syq193411 *mpp = bp;
26933227Syq193411 }
26943227Syq193411 }
26953227Syq193411
26963227Syq193411
26973227Syq193411 /*
26983227Syq193411 * usbsacm_put_head:
26993227Syq193411 * put a message block at the head of the message
27003227Syq193411 * account for the case when message is null
27013227Syq193411 */
27023227Syq193411 static void
usbsacm_put_head(mblk_t ** mpp,mblk_t * bp)27033227Syq193411 usbsacm_put_head(mblk_t **mpp, mblk_t *bp)
27043227Syq193411 {
27053227Syq193411 if (*mpp) {
27063227Syq193411 linkb(bp, *mpp);
27073227Syq193411 }
27083227Syq193411 *mpp = bp;
27093227Syq193411 }
27103227Syq193411
27113227Syq193411
27123227Syq193411 /*
27133227Syq193411 * power management
27143227Syq193411 * ----------------
27153227Syq193411 *
27163227Syq193411 * usbsacm_create_pm_components:
27173227Syq193411 * create PM components
27183227Syq193411 */
27193227Syq193411 static int
usbsacm_create_pm_components(usbsacm_state_t * acmp)27203227Syq193411 usbsacm_create_pm_components(usbsacm_state_t *acmp)
27213227Syq193411 {
27223227Syq193411 dev_info_t *dip = acmp->acm_dip;
27233227Syq193411 usbsacm_pm_t *pm;
27243227Syq193411 uint_t pwr_states;
27253227Syq193411 usb_dev_descr_t *dev_descr;
27263227Syq193411
27273227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
27283227Syq193411 "usbsacm_create_pm_components: ");
27293227Syq193411
27303227Syq193411 if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
27313227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
27323227Syq193411 "usbsacm_create_pm_components: failed");
27333227Syq193411
27343227Syq193411 return (USB_SUCCESS);
27353227Syq193411 }
27363227Syq193411
27373227Syq193411 pm = acmp->acm_pm =
27383227Syq193411 (usbsacm_pm_t *)kmem_zalloc(sizeof (usbsacm_pm_t), KM_SLEEP);
27393227Syq193411
27403227Syq193411 pm->pm_pwr_states = (uint8_t)pwr_states;
27413227Syq193411 pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
27423227Syq193411 /*
27433227Syq193411 * Qualcomm CDMA card won't response the following control commands
27443227Syq193411 * after receive USB_REMOTE_WAKEUP_ENABLE. So we just set
27453227Syq193411 * pm_wakeup_enable to 0 for this specific device.
27463227Syq193411 */
27473227Syq193411 dev_descr = acmp->acm_dev_data->dev_descr;
27483227Syq193411 if (dev_descr->idVendor == 0x5c6 && dev_descr->idProduct == 0x3100) {
27493227Syq193411 pm->pm_wakeup_enabled = 0;
27503227Syq193411 } else {
27513227Syq193411 pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
27523227Syq193411 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
27533227Syq193411 }
27543227Syq193411
27553227Syq193411 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
27563227Syq193411
27573227Syq193411 return (USB_SUCCESS);
27583227Syq193411 }
27593227Syq193411
27603227Syq193411
27613227Syq193411 /*
27623227Syq193411 * usbsacm_destroy_pm_components:
27633227Syq193411 * destroy PM components
27643227Syq193411 */
27653227Syq193411 static void
usbsacm_destroy_pm_components(usbsacm_state_t * acmp)27663227Syq193411 usbsacm_destroy_pm_components(usbsacm_state_t *acmp)
27673227Syq193411 {
27683227Syq193411 usbsacm_pm_t *pm = acmp->acm_pm;
27693227Syq193411 dev_info_t *dip = acmp->acm_dip;
27703227Syq193411 int rval;
27713227Syq193411
27723227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
27733227Syq193411 "usbsacm_destroy_pm_components: ");
27743227Syq193411
27753227Syq193411 if (acmp->acm_dev_state != USB_DEV_DISCONNECTED) {
27763227Syq193411 if (pm->pm_wakeup_enabled) {
27773227Syq193411 rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
27783227Syq193411 if (rval != DDI_SUCCESS) {
27793227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
27803227Syq193411 "usbsacm_destroy_pm_components: "
27813227Syq193411 "raising power failed (%d)", rval);
27823227Syq193411 }
27833227Syq193411
27843227Syq193411 rval = usb_handle_remote_wakeup(dip,
27853227Syq193411 USB_REMOTE_WAKEUP_DISABLE);
27863227Syq193411 if (rval != USB_SUCCESS) {
27873227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
27883227Syq193411 "usbsacm_destroy_pm_components: "
27893227Syq193411 "disable remote wakeup failed (%d)", rval);
27903227Syq193411 }
27913227Syq193411 }
27923227Syq193411
27933227Syq193411 (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
27943227Syq193411 }
27953227Syq193411 kmem_free((caddr_t)pm, sizeof (usbsacm_pm_t));
27963227Syq193411 acmp->acm_pm = NULL;
27973227Syq193411 }
27983227Syq193411
27993227Syq193411
28003227Syq193411 /*
28013227Syq193411 * usbsacm_pm_set_busy:
28023227Syq193411 * mark device busy and raise power
28033227Syq193411 */
28043227Syq193411 static void
usbsacm_pm_set_busy(usbsacm_state_t * acmp)28053227Syq193411 usbsacm_pm_set_busy(usbsacm_state_t *acmp)
28063227Syq193411 {
28073227Syq193411 usbsacm_pm_t *pm = acmp->acm_pm;
28083227Syq193411 dev_info_t *dip = acmp->acm_dip;
28093227Syq193411 int rval;
28103227Syq193411
28113227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
28126898Sfb209375 "usbsacm_pm_set_busy: pm = 0x%p", (void *)pm);
28133227Syq193411
28143227Syq193411 if (pm == NULL) {
28153227Syq193411
28163227Syq193411 return;
28173227Syq193411 }
28183227Syq193411
28193227Syq193411 mutex_enter(&acmp->acm_mutex);
28203227Syq193411 /* if already marked busy, just increment the counter */
28213227Syq193411 if (pm->pm_busy_cnt++ > 0) {
28223227Syq193411 mutex_exit(&acmp->acm_mutex);
28233227Syq193411
28243227Syq193411 return;
28253227Syq193411 }
28263227Syq193411
28273227Syq193411 (void) pm_busy_component(dip, 0);
28283227Syq193411
28293227Syq193411 if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
28303227Syq193411 mutex_exit(&acmp->acm_mutex);
28313227Syq193411
28323227Syq193411 return;
28333227Syq193411 }
28343227Syq193411
28353227Syq193411 /* need to raise power */
28363227Syq193411 pm->pm_raise_power = B_TRUE;
28373227Syq193411 mutex_exit(&acmp->acm_mutex);
28383227Syq193411
28393227Syq193411 rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
28403227Syq193411 if (rval != DDI_SUCCESS) {
28413227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
28423227Syq193411 "usbsacm_pm_set_busy: raising power failed");
28433227Syq193411 }
28443227Syq193411
28453227Syq193411 mutex_enter(&acmp->acm_mutex);
28463227Syq193411 pm->pm_raise_power = B_FALSE;
28473227Syq193411 mutex_exit(&acmp->acm_mutex);
28483227Syq193411 }
28493227Syq193411
28503227Syq193411
28513227Syq193411 /*
28523227Syq193411 * usbsacm_pm_set_idle:
28533227Syq193411 * mark device idle
28543227Syq193411 */
28553227Syq193411 static void
usbsacm_pm_set_idle(usbsacm_state_t * acmp)28563227Syq193411 usbsacm_pm_set_idle(usbsacm_state_t *acmp)
28573227Syq193411 {
28583227Syq193411 usbsacm_pm_t *pm = acmp->acm_pm;
28593227Syq193411 dev_info_t *dip = acmp->acm_dip;
28603227Syq193411
28613227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
28623227Syq193411 "usbsacm_pm_set_idle: ");
28633227Syq193411
28643227Syq193411 if (pm == NULL) {
28653227Syq193411
28663227Syq193411 return;
28673227Syq193411 }
28683227Syq193411
28693227Syq193411 /*
28703227Syq193411 * if more ports use the device, do not mark as yet
28713227Syq193411 */
28723227Syq193411 mutex_enter(&acmp->acm_mutex);
28733227Syq193411 if (--pm->pm_busy_cnt > 0) {
28743227Syq193411 mutex_exit(&acmp->acm_mutex);
28753227Syq193411
28763227Syq193411 return;
28773227Syq193411 }
28783227Syq193411
28793227Syq193411 if (pm) {
28803227Syq193411 (void) pm_idle_component(dip, 0);
28813227Syq193411 }
28823227Syq193411 mutex_exit(&acmp->acm_mutex);
28833227Syq193411 }
28843227Syq193411
28853227Syq193411
28863227Syq193411 /*
28873227Syq193411 * usbsacm_pwrlvl0:
28883227Syq193411 * Functions to handle power transition for OS levels 0 -> 3
28893227Syq193411 * The same level as OS state, different from USB state
28903227Syq193411 */
28913227Syq193411 static int
usbsacm_pwrlvl0(usbsacm_state_t * acmp)28923227Syq193411 usbsacm_pwrlvl0(usbsacm_state_t *acmp)
28933227Syq193411 {
28943227Syq193411 int rval;
28953227Syq193411 int i;
28963227Syq193411 usbsacm_port_t *cur_port = acmp->acm_ports;
28973227Syq193411
28983227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
28993227Syq193411 "usbsacm_pwrlvl0: ");
29003227Syq193411
29013227Syq193411 switch (acmp->acm_dev_state) {
29023227Syq193411 case USB_DEV_ONLINE:
29033227Syq193411 /* issue USB D3 command to the device */
29043227Syq193411 rval = usb_set_device_pwrlvl3(acmp->acm_dip);
29053227Syq193411 ASSERT(rval == USB_SUCCESS);
29063227Syq193411
29073227Syq193411 if (cur_port != NULL) {
29085541Slg150142 for (i = 0; i < acmp->acm_port_cnt; i++) {
29095541Slg150142 cur_port = &acmp->acm_ports[i];
29105541Slg150142 if (cur_port->acm_intr_ph != NULL &&
29115541Slg150142 cur_port->acm_port_state !=
29125541Slg150142 USBSACM_PORT_CLOSED) {
29135541Slg150142
29145541Slg150142 mutex_exit(&acmp->acm_mutex);
29155541Slg150142 usb_pipe_stop_intr_polling(
29165541Slg150142 cur_port->acm_intr_ph,
29175541Slg150142 USB_FLAGS_SLEEP);
29185541Slg150142 mutex_enter(&acmp->acm_mutex);
29195541Slg150142
29205541Slg150142 mutex_enter(&cur_port->acm_port_mutex);
29215541Slg150142 cur_port->acm_intr_state =
29225541Slg150142 USBSACM_PIPE_IDLE;
29235541Slg150142 mutex_exit(&cur_port->acm_port_mutex);
29245541Slg150142 }
29253227Syq193411 }
29263227Syq193411 }
29273227Syq193411
29283227Syq193411 acmp->acm_dev_state = USB_DEV_PWRED_DOWN;
29293227Syq193411 acmp->acm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
29303227Syq193411
29313227Syq193411 /* FALLTHRU */
29323227Syq193411 case USB_DEV_DISCONNECTED:
29333227Syq193411 case USB_DEV_SUSPENDED:
29343227Syq193411 /* allow a disconnect/cpr'ed device to go to lower power */
29353227Syq193411
29363227Syq193411 return (USB_SUCCESS);
29373227Syq193411 case USB_DEV_PWRED_DOWN:
29383227Syq193411 default:
29393227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
29403227Syq193411 "usbsacm_pwrlvl0: illegal device state");
29413227Syq193411
29423227Syq193411 return (USB_FAILURE);
29433227Syq193411 }
29443227Syq193411 }
29453227Syq193411
29463227Syq193411
29473227Syq193411 /*
29483227Syq193411 * usbsacm_pwrlvl1:
29493227Syq193411 * Functions to handle power transition for OS levels 1 -> 2
29503227Syq193411 */
29513227Syq193411 static int
usbsacm_pwrlvl1(usbsacm_state_t * acmp)29523227Syq193411 usbsacm_pwrlvl1(usbsacm_state_t *acmp)
29533227Syq193411 {
29543227Syq193411 /* issue USB D2 command to the device */
29553227Syq193411 (void) usb_set_device_pwrlvl2(acmp->acm_dip);
29563227Syq193411
29573227Syq193411 return (USB_FAILURE);
29583227Syq193411 }
29593227Syq193411
29603227Syq193411
29613227Syq193411 /*
29623227Syq193411 * usbsacm_pwrlvl2:
29633227Syq193411 * Functions to handle power transition for OS levels 2 -> 1
29643227Syq193411 */
29653227Syq193411 static int
usbsacm_pwrlvl2(usbsacm_state_t * acmp)29663227Syq193411 usbsacm_pwrlvl2(usbsacm_state_t *acmp)
29673227Syq193411 {
29683227Syq193411 /* issue USB D1 command to the device */
29693227Syq193411 (void) usb_set_device_pwrlvl1(acmp->acm_dip);
29703227Syq193411
29713227Syq193411 return (USB_FAILURE);
29723227Syq193411 }
29733227Syq193411
29743227Syq193411
29753227Syq193411 /*
29763227Syq193411 * usbsacm_pwrlvl3:
29773227Syq193411 * Functions to handle power transition for OS levels 3 -> 0
29783227Syq193411 * The same level as OS state, different from USB state
29793227Syq193411 */
29803227Syq193411 static int
usbsacm_pwrlvl3(usbsacm_state_t * acmp)29813227Syq193411 usbsacm_pwrlvl3(usbsacm_state_t *acmp)
29823227Syq193411 {
29833227Syq193411 int rval;
29843227Syq193411 int i;
29853227Syq193411 usbsacm_port_t *cur_port = acmp->acm_ports;
29863227Syq193411
29873227Syq193411 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
29883227Syq193411 "usbsacm_pwrlvl3: ");
29893227Syq193411
29903227Syq193411 switch (acmp->acm_dev_state) {
29913227Syq193411 case USB_DEV_PWRED_DOWN:
29923227Syq193411 /* Issue USB D0 command to the device here */
29933227Syq193411 rval = usb_set_device_pwrlvl0(acmp->acm_dip);
29943227Syq193411 ASSERT(rval == USB_SUCCESS);
29953227Syq193411
29963227Syq193411 if (cur_port != NULL) {
29975541Slg150142 for (i = 0; i < acmp->acm_port_cnt; i++) {
29985541Slg150142 cur_port = &acmp->acm_ports[i];
29995541Slg150142 if (cur_port->acm_intr_ph != NULL &&
30005541Slg150142 cur_port->acm_port_state !=
30015541Slg150142 USBSACM_PORT_CLOSED) {
30025541Slg150142
30035541Slg150142 mutex_exit(&acmp->acm_mutex);
30045541Slg150142 usbsacm_pipe_start_polling(cur_port);
30055541Slg150142 mutex_enter(&acmp->acm_mutex);
30065541Slg150142 }
30073227Syq193411 }
30083227Syq193411 }
30093227Syq193411
30103227Syq193411 acmp->acm_dev_state = USB_DEV_ONLINE;
30113227Syq193411 acmp->acm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
30123227Syq193411
30133227Syq193411 /* FALLTHRU */
30143227Syq193411 case USB_DEV_ONLINE:
30153227Syq193411 /* we are already in full power */
30163227Syq193411
30173227Syq193411 /* FALLTHRU */
30183227Syq193411 case USB_DEV_DISCONNECTED:
30193227Syq193411 case USB_DEV_SUSPENDED:
30203227Syq193411
30213227Syq193411 return (USB_SUCCESS);
30223227Syq193411 default:
30233227Syq193411 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
30243227Syq193411 "usbsacm_pwrlvl3: illegal device state");
30253227Syq193411
30263227Syq193411 return (USB_FAILURE);
30273227Syq193411 }
30283227Syq193411 }
30293227Syq193411
30303227Syq193411
30313227Syq193411 /*
30323227Syq193411 * usbsacm_pipe_start_polling:
30333227Syq193411 * start polling on the interrupt pipe
30343227Syq193411 */
30353227Syq193411 static void
usbsacm_pipe_start_polling(usbsacm_port_t * acm_port)30363227Syq193411 usbsacm_pipe_start_polling(usbsacm_port_t *acm_port)
30373227Syq193411 {
30383227Syq193411 usb_intr_req_t *intr;
30393227Syq193411 int rval;
30403227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
30413227Syq193411
30423227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
30433227Syq193411 "usbsacm_pipe_start_polling: ");
30443227Syq193411
30453227Syq193411 if (acm_port->acm_intr_ph == NULL) {
30463227Syq193411
30473227Syq193411 return;
30483227Syq193411 }
30493227Syq193411
30503227Syq193411 intr = usb_alloc_intr_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
30513227Syq193411
30523227Syq193411 /*
30533227Syq193411 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
30543227Syq193411 * called with SLEEP flag.
30553227Syq193411 */
30563227Syq193411 if (!intr) {
30573227Syq193411 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
30583227Syq193411 "usbsacm_pipe_start_polling: alloc req failed.");
30593227Syq193411
30603227Syq193411 return;
30613227Syq193411 }
30623227Syq193411
30633227Syq193411 /* initialize the interrupt request. */
30643227Syq193411 intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
30655541Slg150142 USB_ATTRS_AUTOCLEARING;
30663227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
30673227Syq193411 intr->intr_len = acm_port->acm_intr_ep_descr.wMaxPacketSize;
30683227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
30693227Syq193411 intr->intr_client_private = (usb_opaque_t)acm_port;
30703227Syq193411 intr->intr_cb = usbsacm_intr_cb;
30713227Syq193411 intr->intr_exc_cb = usbsacm_intr_ex_cb;
30723227Syq193411
30733227Syq193411 rval = usb_pipe_intr_xfer(acm_port->acm_intr_ph, intr, USB_FLAGS_SLEEP);
30743227Syq193411
30753227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
30763227Syq193411 if (rval == USB_SUCCESS) {
30773227Syq193411 acm_port->acm_intr_state = USBSACM_PIPE_BUSY;
30783227Syq193411 } else {
30793227Syq193411 usb_free_intr_req(intr);
30803227Syq193411 acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
30813227Syq193411 USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
30823227Syq193411 "usbsacm_pipe_start_polling: failed (%d)", rval);
30833227Syq193411 }
30843227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
30853227Syq193411 }
30863227Syq193411
30873227Syq193411
30883227Syq193411 /*
30893227Syq193411 * usbsacm_intr_cb:
30903227Syq193411 * interrupt pipe normal callback
30913227Syq193411 */
30923227Syq193411 /*ARGSUSED*/
30933227Syq193411 static void
usbsacm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)30943227Syq193411 usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
30953227Syq193411 {
30963227Syq193411 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->intr_client_private;
30973227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
30983227Syq193411 mblk_t *data = req->intr_data;
30993227Syq193411 int data_len;
31003227Syq193411
31013227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
31023227Syq193411 "usbsacm_intr_cb: ");
31033227Syq193411
31043227Syq193411 data_len = (data) ? MBLKL(data) : 0;
31053227Syq193411
31063227Syq193411 /* check data length */
31073227Syq193411 if (data_len < 8) {
31083227Syq193411 USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
31093227Syq193411 "usbsacm_intr_cb: %d packet too short", data_len);
31103227Syq193411 usb_free_intr_req(req);
31113227Syq193411
31123227Syq193411 return;
31133227Syq193411 }
31143227Syq193411 req->intr_data = NULL;
31153227Syq193411 usb_free_intr_req(req);
31163227Syq193411
31173227Syq193411 mutex_enter(&acm_port->acm_port_mutex);
31183227Syq193411 /* parse interrupt data. */
31193227Syq193411 usbsacm_parse_intr_data(acm_port, data);
31203227Syq193411 mutex_exit(&acm_port->acm_port_mutex);
31213227Syq193411 }
31223227Syq193411
31233227Syq193411
31243227Syq193411 /*
31253227Syq193411 * usbsacm_intr_ex_cb:
31263227Syq193411 * interrupt pipe exception callback
31273227Syq193411 */
31283227Syq193411 /*ARGSUSED*/
31293227Syq193411 static void
usbsacm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)31303227Syq193411 usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
31313227Syq193411 {
31323227Syq193411 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->intr_client_private;
31333227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
31343227Syq193411 usb_cr_t cr = req->intr_completion_reason;
31353227Syq193411
31363227Syq193411 USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
31373227Syq193411 "usbsacm_intr_ex_cb: ");
31383227Syq193411
31393227Syq193411 usb_free_intr_req(req);
31403227Syq193411
31413227Syq193411 /*
31423227Syq193411 * If completion reason isn't USB_CR_PIPE_CLOSING and
31433227Syq193411 * USB_CR_STOPPED_POLLING, restart polling.
31443227Syq193411 */
31453227Syq193411 if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
31463227Syq193411 mutex_enter(&acmp->acm_mutex);
31473227Syq193411
31483227Syq193411 if (acmp->acm_dev_state != USB_DEV_ONLINE) {
31493227Syq193411
31503227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
31513227Syq193411 "usbsacm_intr_ex_cb: state = %d",
31523227Syq193411 acmp->acm_dev_state);
31533227Syq193411
31543227Syq193411 mutex_exit(&acmp->acm_mutex);
31553227Syq193411
31563227Syq193411 return;
31573227Syq193411 }
31583227Syq193411 mutex_exit(&acmp->acm_mutex);
31593227Syq193411
31603227Syq193411 usbsacm_pipe_start_polling(acm_port);
31613227Syq193411 }
31623227Syq193411 }
31633227Syq193411
31643227Syq193411
31653227Syq193411 /*
31663227Syq193411 * usbsacm_parse_intr_data:
31673227Syq193411 * Parse data received from interrupt callback
31683227Syq193411 */
31693227Syq193411 static void
usbsacm_parse_intr_data(usbsacm_port_t * acm_port,mblk_t * data)31703227Syq193411 usbsacm_parse_intr_data(usbsacm_port_t *acm_port, mblk_t *data)
31713227Syq193411 {
31723227Syq193411 usbsacm_state_t *acmp = acm_port->acm_device;
31733227Syq193411 uint8_t bmRequestType;
31743227Syq193411 uint8_t bNotification;
31753227Syq193411 uint16_t wValue;
31763227Syq193411 uint16_t wLength;
31773227Syq193411 uint16_t wData;
31783227Syq193411
31793227Syq193411 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
31803227Syq193411 "usbsacm_parse_intr_data: ");
31813227Syq193411
31823227Syq193411 bmRequestType = data->b_rptr[0];
31833227Syq193411 bNotification = data->b_rptr[1];
31843227Syq193411 /*
31853227Syq193411 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
31863227Syq193411 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
31873227Syq193411 * mLength is 2. So we directly get the value from the byte.
31883227Syq193411 */
31893227Syq193411 wValue = data->b_rptr[2];
31903227Syq193411 wLength = data->b_rptr[6];
31913227Syq193411
31923227Syq193411 if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
31933227Syq193411 USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
31943227Syq193411 "usbsacm_parse_intr_data: unknown request type - 0x%x",
31953227Syq193411 bmRequestType);
31963227Syq193411
3197*12024SRaymond.Chen@Sun.COM freemsg(data);
3198*12024SRaymond.Chen@Sun.COM
31993227Syq193411 return;
32003227Syq193411 }
32013227Syq193411
32023227Syq193411 /*
32033227Syq193411 * Check the return value of device
32043227Syq193411 */
32053227Syq193411 switch (bNotification) {
32063227Syq193411 case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
32073227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32083227Syq193411 "usbsacm_parse_intr_data: %s network!",
32093227Syq193411 wValue ? "connected to" :"disconnected from");
32103227Syq193411
32113227Syq193411 break;
32123227Syq193411 case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
32133227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32143227Syq193411 "usbsacm_parse_intr_data: A response is a available.");
32153227Syq193411
32163227Syq193411 break;
32173227Syq193411 case USB_CDC_NOTIFICATION_SERIAL_STATE:
32183227Syq193411 /* check the parameter's length. */
32193227Syq193411 if (wLength != 2) {
32203227Syq193411
32213227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32223227Syq193411 "usbsacm_parse_intr_data: error data length.");
32233227Syq193411 } else {
32243227Syq193411 /*
32253227Syq193411 * The Data field is a bitmapped value that contains
32263227Syq193411 * the current state of carrier detect, transmission
32273227Syq193411 * carrier, break, ring signal and device overrun
32283227Syq193411 * error.
32293227Syq193411 */
32303227Syq193411 wData = data->b_rptr[8];
32313227Syq193411 /*
32323227Syq193411 * Check the serial state of the current port.
32333227Syq193411 */
32343227Syq193411 if (wData & USB_CDC_ACM_CONTROL_DCD) {
32353227Syq193411
32363227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32373227Syq193411 "usbsacm_parse_intr_data: "
32383227Syq193411 "receiver carrier is set.");
32393227Syq193411 }
32403227Syq193411 if (wData & USB_CDC_ACM_CONTROL_DSR) {
32413227Syq193411
32423227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32433227Syq193411 "usbsacm_parse_intr_data: "
32443227Syq193411 "transmission carrier is set.");
32453227Syq193411
32463227Syq193411 acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_DSR;
32473227Syq193411 }
32483227Syq193411 if (wData & USB_CDC_ACM_CONTROL_BREAK) {
32493227Syq193411
32503227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32513227Syq193411 "usbsacm_parse_intr_data: "
32523227Syq193411 "break detection mechanism is set.");
32533227Syq193411 }
32543227Syq193411 if (wData & USB_CDC_ACM_CONTROL_RNG) {
32553227Syq193411
32563227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32573227Syq193411 "usbsacm_parse_intr_data: "
32583227Syq193411 "ring signal detection is set.");
32593227Syq193411
32603227Syq193411 acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_RNG;
32613227Syq193411 }
32623227Syq193411 if (wData & USB_CDC_ACM_CONTROL_FRAMING) {
32633227Syq193411
32643227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32653227Syq193411 "usbsacm_parse_intr_data: "
32663227Syq193411 "A framing error has occurred.");
32673227Syq193411 }
32683227Syq193411 if (wData & USB_CDC_ACM_CONTROL_PARITY) {
32693227Syq193411
32703227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32713227Syq193411 "usbsacm_parse_intr_data: "
32723227Syq193411 "A parity error has occurred.");
32733227Syq193411 }
32743227Syq193411 if (wData & USB_CDC_ACM_CONTROL_OVERRUN) {
32753227Syq193411
32763227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32773227Syq193411 "usbsacm_parse_intr_data: "
32783227Syq193411 "Received data has been discarded "
32793227Syq193411 "due to overrun.");
32803227Syq193411 }
32813227Syq193411 }
32823227Syq193411
32833227Syq193411 break;
32843227Syq193411 default:
32853227Syq193411 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32863227Syq193411 "usbsacm_parse_intr_data: unknown notification - 0x%x!",
32873227Syq193411 bNotification);
32883227Syq193411
32893227Syq193411 break;
32903227Syq193411 }
32913227Syq193411
32923227Syq193411 freemsg(data);
32933227Syq193411 }
3294