190f05028Syq /*
290f05028Syq * CDDL HEADER START
390f05028Syq *
490f05028Syq * The contents of this file are subject to the terms of the
590f05028Syq * Common Development and Distribution License (the "License").
690f05028Syq * You may not use this file except in compliance with the License.
790f05028Syq *
890f05028Syq * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
990f05028Syq * or http://www.opensolaris.org/os/licensing.
1090f05028Syq * See the License for the specific language governing permissions
1190f05028Syq * and limitations under the License.
1290f05028Syq *
1390f05028Syq * When distributing Covered Code, include this CDDL HEADER in each
1490f05028Syq * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1590f05028Syq * If applicable, add the following below this CDDL HEADER, with the
1690f05028Syq * fields enclosed by brackets "[]" replaced with your own identifying
1790f05028Syq * information: Portions Copyright [yyyy] [name of copyright owner]
1890f05028Syq *
1990f05028Syq * CDDL HEADER END
2090f05028Syq */
21de81e71eSTim Marsland
2290f05028Syq /*
239e37f2b5SRaymond Chen * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2490f05028Syq * Use is subject to license terms.
2590f05028Syq */
2680727282SMike Zeller /*
2780727282SMike Zeller * Copyright 2019 Joyent, Inc.
2880727282SMike Zeller */
2990f05028Syq
3090f05028Syq /*
3190f05028Syq * USB Serial CDC ACM driver
3290f05028Syq *
3390f05028Syq * 1. General Concepts
3490f05028Syq * -------------------
3590f05028Syq *
3690f05028Syq * 1.1 Overview
3790f05028Syq * ------------
3890f05028Syq * This driver supports devices that comply with the USB Communication
3990f05028Syq * Device Class Abstract Control Model (USB CDC ACM) specification,
4090f05028Syq * which is available at http://www.usb.org. Given the broad nature
4190f05028Syq * of communication equipment, this driver supports the following
4290f05028Syq * types of devices:
434ee52f77Slg * + Telecommunications devices: analog modems, mobile phones;
444ee52f77Slg * + Networking devices: cable modems;
4590f05028Syq * Except the above mentioned acm devices, this driver also supports
4690f05028Syq * some devices which provide modem-like function and have pairs of
4790f05028Syq * bulk in/out pipes.
4890f05028Syq *
4990f05028Syq * There are three classes that make up the definition for communication
5090f05028Syq * devices: the Communication Device Class, the Communication Interface
5190f05028Syq * Class and the Data Interface Class. The Communication Device Class
5290f05028Syq * is a device level definition and is used by the host to properly
5390f05028Syq * identify a communication device that may present several different
5490f05028Syq * types of interfaces. The Communication Interface Class defines a
5590f05028Syq * general-purpose mechanism that can be used to enable all types of
5690f05028Syq * communication services on the Universal Serial Bus (USB). The Data
5790f05028Syq * Interface Class defines a general-purpose mechanism to enable bulk
5890f05028Syq * transfer on the USB when the data does not meet the requirements
5990f05028Syq * for any other class.
6090f05028Syq *
6190f05028Syq * 1.2 Interface Definitions
6290f05028Syq * -------------------------
6390f05028Syq * Communication Class Interface is used for device management and,
6490f05028Syq * optionally, call management. Device management includes the requests
6590f05028Syq * that manage the operational state of a device, the device responses,
6690f05028Syq * and event notifications. In Abstract Control Model, the device can
6790f05028Syq * provide an internal implementation of call management over the Data
6890f05028Syq * Class interface or the Communication Class interface.
6990f05028Syq *
7090f05028Syq * The Data Class defines a data interface as an interface with a class
7190f05028Syq * type of Data Class. Data transmission on a communication device is
7290f05028Syq * not restricted to interfaces using the Data Class. Rather, a data
7390f05028Syq * interface is used to transmit and/or receive data that is not
7490f05028Syq * defined by any other class. The data could be:
754ee52f77Slg * + Some form of raw data from a communication line.
764ee52f77Slg * + Legacy modem data.
774ee52f77Slg * + Data using a proprietary format.
7890f05028Syq *
7990f05028Syq * 1.3 Endpoint Requirements
8090f05028Syq * -------------------------
8190f05028Syq * The Communication Class interface requires one endpoint, the management
8290f05028Syq * element. Optionally, it can have an additional endpoint, the notification
8390f05028Syq * element. The management element uses the default endpoint for all
8490f05028Syq * standard and Communication Class-specific requests. The notification
8590f05028Syq * element normally uses an interrupt endpoint.
8690f05028Syq *
8790f05028Syq * The type of endpoints belonging to a Data Class interface are restricted
8890f05028Syq * to bulk, and are expected to exist in pairs of the same type (one In and
8990f05028Syq * one Out).
9090f05028Syq *
9190f05028Syq * 1.4 ACM Function Characteristics
9290f05028Syq * --------------------------------
9390f05028Syq * With Abstract Control Model, the USB device understands standard
9490f05028Syq * V.25ter (AT) commands. The device contains a Datapump and micro-
9590f05028Syq * controller that handles the AT commands and relay controls. The
9690f05028Syq * device uses both a Data Class interface and a Communication Class.
9790f05028Syq * interface.
9890f05028Syq *
9990f05028Syq * A Communication Class interface of type Abstract Control Model will
10090f05028Syq * consist of a minimum of two pipes; one is used to implement the
10190f05028Syq * management element and the other to implement a notification element.
10290f05028Syq * In addition, the device can use two pipes to implement channels over
10390f05028Syq * which to carry unspecified data, typically over a Data Class interface.
10490f05028Syq *
10590f05028Syq * 1.5 ACM Serial Emulation
10690f05028Syq * ------------------------
10790f05028Syq * The Abstract Control Model can bridge the gap between legacy modem
10890f05028Syq * devices and USB devices. To support certain types of legacy applications,
10990f05028Syq * two problems need to be addressed. The first is supporting specific
11090f05028Syq * legacy control signals and state variables which are addressed
11190f05028Syq * directly by the various carrier modulation standards. To support these
11290f05028Syq * requirement, additional requests and notifications have been created.
11390f05028Syq * Please refer to macro, beginning with USB_CDC_REQ_* and
11490f05028Syq * USB_CDC_NOTIFICATION_*.
11590f05028Syq *
11690f05028Syq * The second significant item which is needed to bridge the gap between
11790f05028Syq * legacy modem designs and the Abstract Control Model is a means to
11890f05028Syq * multiplex call control (AT commands) on the Data Class interface.
11990f05028Syq * Legacy modem designs are limited by only supporting one channel for
12090f05028Syq * both "AT" commands and the actual data. To allow this type of
12190f05028Syq * functionality, the device must have a means to specify this limitation
12290f05028Syq * to the host.
12390f05028Syq *
12490f05028Syq * When describing this type of device, the Communication Class interface
12590f05028Syq * would still specify a Abstract Control Model, but call control would
12690f05028Syq * actually occur over the Data Class interface. To describe this
12790f05028Syq * particular characteristic, the Call Management Functional Descriptor
12890f05028Syq * would have bit D1 of bmCapabilities set.
12990f05028Syq *
13090f05028Syq * 1.6 Other Bulk In/Out Devices
13190f05028Syq * -----------------------------
13290f05028Syq * Some devices don't conform to USB CDC specification, but they provide
13390f05028Syq * modem-like function and have pairs of bulk in/out pipes. This driver
13490f05028Syq * supports this kind of device and exports term nodes by their pipes.
13590f05028Syq *
13690f05028Syq * 2. Implementation
13790f05028Syq * -----------------
13890f05028Syq *
13990f05028Syq * 2.1 Overview
14090f05028Syq * ------------
14190f05028Syq * It is a device-specific driver (DSD) working with USB generic serial
14290f05028Syq * driver (GSD). It implements the USB-to-serial device-specific driver
14390f05028Syq * interface (DSDI) which is offered by GSD. The interface is defined
14490f05028Syq * by ds_ops_t structure.
14590f05028Syq *
14690f05028Syq * 2.2 Port States
14790f05028Syq * ---------------
14890f05028Syq * For USB CDC ACM devices, this driver is attached to its interface,
14990f05028Syq * and exports one port for each interface. For other modem-like devices,
15090f05028Syq * this driver can dynamically find the ports in the current device,
15190f05028Syq * and export one port for each pair bulk in/out pipes. Each port can
15290f05028Syq * be operated independently.
15390f05028Syq *
15490f05028Syq * port_state:
15590f05028Syq *
15690f05028Syq * attach_ports
15790f05028Syq * |
15890f05028Syq * |
15990f05028Syq * |
16090f05028Syq * v
16190f05028Syq * USBSACM_PORT_CLOSED
16290f05028Syq * | ^
16390f05028Syq * | |
16490f05028Syq * V |
16590f05028Syq * open_port close_port
16690f05028Syq * | ^
16790f05028Syq * | |
16890f05028Syq * V |
16990f05028Syq * USBSACM_PORT_OPEN
17090f05028Syq *
17190f05028Syq *
17290f05028Syq * 2.3 Pipe States
17390f05028Syq * ---------------
17490f05028Syq * Each port has its own bulk in/out pipes and some ports could also have
17590f05028Syq * its own interrupt pipes (traced by usbsacm_port structure), which are
17690f05028Syq * opened during attach. The pipe status is as following:
17790f05028Syq *
17890f05028Syq * pipe_state:
17990f05028Syq *
18090f05028Syq * usbsacm_init_alloc_ports usbsacm_free_ports
18190f05028Syq * | ^
18290f05028Syq * v |
1834ee52f77Slg * |---->------ USBSACM_PORT_CLOSED ------>------+
18490f05028Syq * ^ |
18590f05028Syq * | reconnect/resume/open_port
18690f05028Syq * | |
18790f05028Syq * disconnect/suspend/close_port |
18890f05028Syq * | v
18990f05028Syq * +------<------ USBSACM_PIPE_IDLE ------<------|
19090f05028Syq * | |
19190f05028Syq * V ^
19290f05028Syq * | |
19390f05028Syq * +-----------------+ +-----------+
19490f05028Syq * | |
19590f05028Syq * V ^
19690f05028Syq * | |
19790f05028Syq * rx_start/tx_start----->------failed------->---------|
19890f05028Syq * | |
19990f05028Syq * | bulkin_cb/bulkout_cb
20090f05028Syq * V |
20190f05028Syq * | ^
20290f05028Syq * | |
20390f05028Syq * +----->----- USBSACM_PIPE_BUSY ---->------+
20490f05028Syq *
20590f05028Syq *
20690f05028Syq * To get its status in a timely way, acm driver can get the status
20790f05028Syq * of the device by polling the interrupt pipe.
20890f05028Syq *
20990f05028Syq */
21090f05028Syq
21190f05028Syq #include <sys/types.h>
21290f05028Syq #include <sys/param.h>
21390f05028Syq #include <sys/conf.h>
21490f05028Syq #include <sys/stream.h>
21590f05028Syq #include <sys/strsun.h>
21690f05028Syq #include <sys/termio.h>
21790f05028Syq #include <sys/termiox.h>
21890f05028Syq #include <sys/ddi.h>
21990f05028Syq #include <sys/sunddi.h>
22090f05028Syq #include <sys/byteorder.h>
22190f05028Syq #define USBDRV_MAJOR_VER 2
22290f05028Syq #define USBDRV_MINOR_VER 0
22390f05028Syq #include <sys/usb/usba.h>
22480727282SMike Zeller #include <sys/usb/usbdevs.h>
22590f05028Syq #include <sys/usb/usba/usba_types.h>
22690f05028Syq #include <sys/usb/clients/usbser/usbser.h>
22790f05028Syq #include <sys/usb/clients/usbser/usbser_dsdi.h>
22890f05028Syq #include <sys/usb/clients/usbcdc/usb_cdc.h>
22990f05028Syq #include <sys/usb/clients/usbser/usbsacm/usbsacm.h>
23090f05028Syq
23190f05028Syq /* devops entry points */
23290f05028Syq static int usbsacm_attach(dev_info_t *, ddi_attach_cmd_t);
23390f05028Syq static int usbsacm_detach(dev_info_t *, ddi_detach_cmd_t);
2344ee52f77Slg static int usbsacm_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
23590f05028Syq void **);
23690f05028Syq static int usbsacm_open(queue_t *, dev_t *, int, int, cred_t *);
23790f05028Syq
23890f05028Syq /* DSD operations */
23990f05028Syq static int usbsacm_ds_attach(ds_attach_info_t *);
24090f05028Syq static void usbsacm_ds_detach(ds_hdl_t);
24190f05028Syq static int usbsacm_ds_register_cb(ds_hdl_t, uint_t, ds_cb_t *);
24290f05028Syq static void usbsacm_ds_unregister_cb(ds_hdl_t, uint_t);
24390f05028Syq static int usbsacm_ds_open_port(ds_hdl_t, uint_t);
24490f05028Syq static int usbsacm_ds_close_port(ds_hdl_t, uint_t);
24590f05028Syq
24690f05028Syq /* standard UART operations */
24790f05028Syq static int usbsacm_ds_set_port_params(ds_hdl_t, uint_t,
24890f05028Syq ds_port_params_t *);
24990f05028Syq static int usbsacm_ds_set_modem_ctl(ds_hdl_t, uint_t, int, int);
25090f05028Syq static int usbsacm_ds_get_modem_ctl(ds_hdl_t, uint_t, int, int *);
25190f05028Syq static int usbsacm_ds_break_ctl(ds_hdl_t, uint_t, int);
25290f05028Syq
25390f05028Syq /* data xfer */
25490f05028Syq static int usbsacm_ds_tx(ds_hdl_t, uint_t, mblk_t *);
25590f05028Syq static mblk_t *usbsacm_ds_rx(ds_hdl_t, uint_t);
25690f05028Syq static void usbsacm_ds_stop(ds_hdl_t, uint_t, int);
25790f05028Syq static void usbsacm_ds_start(ds_hdl_t, uint_t, int);
25890f05028Syq
25990f05028Syq /* fifo operations */
26090f05028Syq static int usbsacm_ds_fifo_flush(ds_hdl_t, uint_t, int);
26190f05028Syq static int usbsacm_ds_fifo_drain(ds_hdl_t, uint_t, int);
26290f05028Syq static int usbsacm_wait_tx_drain(usbsacm_port_t *, int);
26390f05028Syq static int usbsacm_fifo_flush_locked(usbsacm_state_t *, uint_t, int);
26490f05028Syq
26590f05028Syq /* power management and CPR */
26690f05028Syq static int usbsacm_ds_suspend(ds_hdl_t);
26790f05028Syq static int usbsacm_ds_resume(ds_hdl_t);
26890f05028Syq static int usbsacm_ds_disconnect(ds_hdl_t);
26990f05028Syq static int usbsacm_ds_reconnect(ds_hdl_t);
27090f05028Syq static int usbsacm_ds_usb_power(ds_hdl_t, int, int, int *);
27190f05028Syq static int usbsacm_create_pm_components(usbsacm_state_t *);
27290f05028Syq static void usbsacm_destroy_pm_components(usbsacm_state_t *);
27390f05028Syq static void usbsacm_pm_set_busy(usbsacm_state_t *);
27490f05028Syq static void usbsacm_pm_set_idle(usbsacm_state_t *);
27590f05028Syq static int usbsacm_pwrlvl0(usbsacm_state_t *);
27690f05028Syq static int usbsacm_pwrlvl1(usbsacm_state_t *);
27790f05028Syq static int usbsacm_pwrlvl2(usbsacm_state_t *);
27890f05028Syq static int usbsacm_pwrlvl3(usbsacm_state_t *);
27990f05028Syq
28090f05028Syq /* event handling */
28190f05028Syq /* pipe callbacks */
28290f05028Syq static void usbsacm_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
28390f05028Syq static void usbsacm_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
28490f05028Syq
28590f05028Syq /* interrupt pipe */
28690f05028Syq static void usbsacm_pipe_start_polling(usbsacm_port_t *acmp);
28790f05028Syq static void usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
28890f05028Syq static void usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
28990f05028Syq static void usbsacm_parse_intr_data(usbsacm_port_t *acmp, mblk_t *data);
29090f05028Syq
29190f05028Syq /* Utility functions */
29290f05028Syq /* data transfer routines */
29390f05028Syq static int usbsacm_rx_start(usbsacm_port_t *);
29490f05028Syq static void usbsacm_tx_start(usbsacm_port_t *);
29590f05028Syq static int usbsacm_send_data(usbsacm_port_t *, mblk_t *);
29690f05028Syq
29790f05028Syq /* Initialize or release resources */
29890f05028Syq static int usbsacm_init_alloc_ports(usbsacm_state_t *);
29990f05028Syq static void usbsacm_free_ports(usbsacm_state_t *);
30090f05028Syq static void usbsacm_cleanup(usbsacm_state_t *);
30190f05028Syq
30290f05028Syq /* analysis functional descriptors */
30390f05028Syq static int usbsacm_get_descriptors(usbsacm_state_t *);
30490f05028Syq
30590f05028Syq /* hotplug */
30690f05028Syq static int usbsacm_restore_device_state(usbsacm_state_t *);
30790f05028Syq static int usbsacm_restore_port_state(usbsacm_state_t *);
30890f05028Syq
30990f05028Syq /* pipe operations */
31090f05028Syq static int usbsacm_open_port_pipes(usbsacm_port_t *);
31190f05028Syq static void usbsacm_close_port_pipes(usbsacm_port_t *);
31290f05028Syq static void usbsacm_close_pipes(usbsacm_state_t *);
31390f05028Syq static void usbsacm_disconnect_pipes(usbsacm_state_t *);
31490f05028Syq static int usbsacm_reconnect_pipes(usbsacm_state_t *);
31590f05028Syq
31690f05028Syq /* vendor-specific commands */
31790f05028Syq static int usbsacm_req_write(usbsacm_port_t *, uchar_t, uint16_t,
31890f05028Syq mblk_t **);
31990f05028Syq static int usbsacm_set_line_coding(usbsacm_port_t *,
32090f05028Syq usb_cdc_line_coding_t *);
32190f05028Syq static void usbsacm_mctl2reg(int mask, int val, uint8_t *);
32290f05028Syq static int usbsacm_reg2mctl(uint8_t);
32390f05028Syq
32490f05028Syq /* misc */
32590f05028Syq static void usbsacm_put_tail(mblk_t **, mblk_t *);
32690f05028Syq static void usbsacm_put_head(mblk_t **, mblk_t *);
32790f05028Syq
32890f05028Syq
32990f05028Syq /*
33090f05028Syq * Standard STREAMS driver definitions
33190f05028Syq */
33290f05028Syq struct module_info usbsacm_modinfo = {
33390f05028Syq 0, /* module id */
33490f05028Syq "usbsacm", /* module name */
33590f05028Syq USBSER_MIN_PKTSZ, /* min pkt size */
33690f05028Syq USBSER_MAX_PKTSZ, /* max pkt size */
33790f05028Syq USBSER_HIWAT, /* hi watermark */
33890f05028Syq USBSER_LOWAT /* low watermark */
33990f05028Syq };
34090f05028Syq
34190f05028Syq static struct qinit usbsacm_rinit = {
34290f05028Syq NULL,
34390f05028Syq usbser_rsrv,
34490f05028Syq usbsacm_open,
34590f05028Syq usbser_close,
34690f05028Syq NULL,
34790f05028Syq &usbsacm_modinfo,
34890f05028Syq NULL
34990f05028Syq };
35090f05028Syq
35190f05028Syq static struct qinit usbsacm_winit = {
35290f05028Syq usbser_wput,
35390f05028Syq usbser_wsrv,
35490f05028Syq NULL,
35590f05028Syq NULL,
35690f05028Syq NULL,
35790f05028Syq &usbsacm_modinfo,
35890f05028Syq NULL
35990f05028Syq };
36090f05028Syq
36190f05028Syq
36290f05028Syq struct streamtab usbsacm_str_info = {
36390f05028Syq &usbsacm_rinit, &usbsacm_winit, NULL, NULL
36490f05028Syq };
36590f05028Syq
36690f05028Syq /* cb_ops structure */
36790f05028Syq static struct cb_ops usbsacm_cb_ops = {
36890f05028Syq nodev, /* cb_open */
36990f05028Syq nodev, /* cb_close */
37090f05028Syq nodev, /* cb_strategy */
37190f05028Syq nodev, /* cb_print */
37290f05028Syq nodev, /* cb_dump */
37390f05028Syq nodev, /* cb_read */
37490f05028Syq nodev, /* cb_write */
37590f05028Syq nodev, /* cb_ioctl */
37690f05028Syq nodev, /* cb_devmap */
37790f05028Syq nodev, /* cb_mmap */
37890f05028Syq nodev, /* cb_segmap */
37990f05028Syq nochpoll, /* cb_chpoll */
38090f05028Syq ddi_prop_op, /* cb_prop_op */
38190f05028Syq &usbsacm_str_info, /* cb_stream */
38290f05028Syq (int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG) /* cb_flag */
38390f05028Syq };
38490f05028Syq
38590f05028Syq /* dev_ops structure */
38690f05028Syq struct dev_ops usbsacm_ops = {
38790f05028Syq DEVO_REV, /* devo_rev */
38890f05028Syq 0, /* devo_refcnt */
38990f05028Syq usbsacm_getinfo, /* devo_getinfo */
39090f05028Syq nulldev, /* devo_identify */
39190f05028Syq nulldev, /* devo_probe */
39290f05028Syq usbsacm_attach, /* devo_attach */
39390f05028Syq usbsacm_detach, /* devo_detach */
39490f05028Syq nodev, /* devo_reset */
39590f05028Syq &usbsacm_cb_ops, /* devo_cb_ops */
39690f05028Syq (struct bus_ops *)NULL, /* devo_bus_ops */
39719397407SSherry Moore usbser_power, /* devo_power */
398be529ebcSRaymond Chen ddi_quiesce_not_needed, /* devo_quiesce */
39990f05028Syq };
40090f05028Syq
40190f05028Syq extern struct mod_ops mod_driverops;
40290f05028Syq /* modldrv structure */
40390f05028Syq static struct modldrv modldrv = {
40490f05028Syq &mod_driverops, /* type of module - driver */
40577e51571Sgongtian zhao - Sun Microsystems - Beijing China "USB Serial CDC ACM driver",
40690f05028Syq &usbsacm_ops,
40790f05028Syq };
40890f05028Syq
40990f05028Syq /* modlinkage structure */
41090f05028Syq static struct modlinkage modlinkage = {
41190f05028Syq MODREV_1,
41290f05028Syq &modldrv,
41390f05028Syq NULL
41490f05028Syq };
41590f05028Syq
41690f05028Syq static void *usbsacm_statep; /* soft state */
41790f05028Syq
41890f05028Syq /*
41990f05028Syq * DSD definitions
42090f05028Syq */
421e8ed0869SJohn Beck static ds_ops_t usbsacm_ds_ops = {
42290f05028Syq DS_OPS_VERSION,
42390f05028Syq usbsacm_ds_attach,
42490f05028Syq usbsacm_ds_detach,
42590f05028Syq usbsacm_ds_register_cb,
42690f05028Syq usbsacm_ds_unregister_cb,
42790f05028Syq usbsacm_ds_open_port,
42890f05028Syq usbsacm_ds_close_port,
42990f05028Syq usbsacm_ds_usb_power,
43090f05028Syq usbsacm_ds_suspend,
43190f05028Syq usbsacm_ds_resume,
43290f05028Syq usbsacm_ds_disconnect,
43390f05028Syq usbsacm_ds_reconnect,
43490f05028Syq usbsacm_ds_set_port_params,
43590f05028Syq usbsacm_ds_set_modem_ctl,
43690f05028Syq usbsacm_ds_get_modem_ctl,
43790f05028Syq usbsacm_ds_break_ctl,
43890f05028Syq NULL, /* NULL if h/w doesn't support loopback */
43990f05028Syq usbsacm_ds_tx,
44090f05028Syq usbsacm_ds_rx,
44190f05028Syq usbsacm_ds_stop,
44290f05028Syq usbsacm_ds_start,
44390f05028Syq usbsacm_ds_fifo_flush,
44490f05028Syq usbsacm_ds_fifo_drain
44590f05028Syq };
44690f05028Syq
44790f05028Syq /*
44890f05028Syq * baud code -> baud rate (0 means unsupported rate)
44990f05028Syq */
45090f05028Syq static int usbsacm_speedtab[] = {
451*d9c3e05cSJoshua M. Clulow 0, /* B0 */
452*d9c3e05cSJoshua M. Clulow 50, /* B50 */
453*d9c3e05cSJoshua M. Clulow 75, /* B75 */
454*d9c3e05cSJoshua M. Clulow 110, /* B110 */
455*d9c3e05cSJoshua M. Clulow 134, /* B134 */
456*d9c3e05cSJoshua M. Clulow 150, /* B150 */
457*d9c3e05cSJoshua M. Clulow 200, /* B200 */
458*d9c3e05cSJoshua M. Clulow 300, /* B300 */
459*d9c3e05cSJoshua M. Clulow 600, /* B600 */
460*d9c3e05cSJoshua M. Clulow 1200, /* B1200 */
461*d9c3e05cSJoshua M. Clulow 1800, /* B1800 */
462*d9c3e05cSJoshua M. Clulow 2400, /* B2400 */
463*d9c3e05cSJoshua M. Clulow 4800, /* B4800 */
464*d9c3e05cSJoshua M. Clulow 9600, /* B9600 */
465*d9c3e05cSJoshua M. Clulow 19200, /* B19200 */
466*d9c3e05cSJoshua M. Clulow 38400, /* B38400 */
467*d9c3e05cSJoshua M. Clulow 57600, /* B57600 */
468*d9c3e05cSJoshua M. Clulow 76800, /* B76800 */
469*d9c3e05cSJoshua M. Clulow 115200, /* B115200 */
470*d9c3e05cSJoshua M. Clulow 153600, /* B153600 */
471*d9c3e05cSJoshua M. Clulow 230400, /* B230400 */
472*d9c3e05cSJoshua M. Clulow 307200, /* B307200 */
473*d9c3e05cSJoshua M. Clulow 460800, /* B460800 */
474*d9c3e05cSJoshua M. Clulow 921600, /* B921600 */
475*d9c3e05cSJoshua M. Clulow 1000000, /* B1000000 */
476*d9c3e05cSJoshua M. Clulow 1152000, /* B1152000 */
477*d9c3e05cSJoshua M. Clulow 1500000, /* B1500000 */
478*d9c3e05cSJoshua M. Clulow 2000000, /* B2000000 */
479*d9c3e05cSJoshua M. Clulow 2500000, /* B2500000 */
480*d9c3e05cSJoshua M. Clulow 3000000, /* B3000000 */
481*d9c3e05cSJoshua M. Clulow 3500000, /* B3500000 */
482*d9c3e05cSJoshua M. Clulow 4000000, /* B4000000 */
48390f05028Syq };
48490f05028Syq
48590f05028Syq
48690f05028Syq static uint_t usbsacm_errlevel = USB_LOG_L4;
48790f05028Syq static uint_t usbsacm_errmask = 0xffffffff;
48890f05028Syq static uint_t usbsacm_instance_debug = (uint_t)-1;
48990f05028Syq
49090f05028Syq
49190f05028Syq /*
49290f05028Syq * usbsacm driver's entry points
49390f05028Syq * -----------------------------
49490f05028Syq */
49590f05028Syq /*
49690f05028Syq * Module-wide initialization routine.
49790f05028Syq */
49890f05028Syq int
_init(void)49990f05028Syq _init(void)
50090f05028Syq {
50190f05028Syq int error;
50290f05028Syq
50390f05028Syq if ((error = mod_install(&modlinkage)) == 0) {
50490f05028Syq
50590f05028Syq error = ddi_soft_state_init(&usbsacm_statep,
50690f05028Syq usbser_soft_state_size(), 1);
50790f05028Syq }
50890f05028Syq
50990f05028Syq return (error);
51090f05028Syq }
51190f05028Syq
51290f05028Syq
51390f05028Syq /*
51490f05028Syq * Module-wide tear-down routine.
51590f05028Syq */
51690f05028Syq int
_fini(void)51790f05028Syq _fini(void)
51890f05028Syq {
51990f05028Syq int error;
52090f05028Syq
52190f05028Syq if ((error = mod_remove(&modlinkage)) == 0) {
52290f05028Syq ddi_soft_state_fini(&usbsacm_statep);
52390f05028Syq }
52490f05028Syq
52590f05028Syq return (error);
52690f05028Syq }
52790f05028Syq
52890f05028Syq
52990f05028Syq int
_info(struct modinfo * modinfop)53090f05028Syq _info(struct modinfo *modinfop)
53190f05028Syq {
53290f05028Syq return (mod_info(&modlinkage, modinfop));
53390f05028Syq }
53490f05028Syq
53590f05028Syq
53690f05028Syq /*
53790f05028Syq * Device configuration entry points
53890f05028Syq */
53990f05028Syq static int
usbsacm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)54090f05028Syq usbsacm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
54190f05028Syq {
542e8ed0869SJohn Beck return (usbser_attach(dip, cmd, usbsacm_statep, &usbsacm_ds_ops));
54390f05028Syq }
54490f05028Syq
54590f05028Syq
54690f05028Syq static int
usbsacm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)54790f05028Syq usbsacm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
54890f05028Syq {
54990f05028Syq return (usbser_detach(dip, cmd, usbsacm_statep));
55090f05028Syq }
55190f05028Syq
55290f05028Syq
55390f05028Syq int
usbsacm_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)55490f05028Syq usbsacm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
55580727282SMike Zeller void **result)
55690f05028Syq {
55790f05028Syq return (usbser_getinfo(dip, infocmd, arg, result, usbsacm_statep));
55890f05028Syq }
55990f05028Syq
56090f05028Syq
56190f05028Syq static int
usbsacm_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)56290f05028Syq usbsacm_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
56390f05028Syq {
56490f05028Syq return (usbser_open(rq, dev, flag, sflag, cr, usbsacm_statep));
56590f05028Syq }
56690f05028Syq
56790f05028Syq /*
56890f05028Syq * usbsacm_ds_detach:
56990f05028Syq * attach device instance, called from GSD attach
57090f05028Syq * initialize state and device, including:
57190f05028Syq * state variables, locks, device node
57290f05028Syq * device registration with system
57390f05028Syq * power management
57490f05028Syq */
57590f05028Syq static int
usbsacm_ds_attach(ds_attach_info_t * aip)57690f05028Syq usbsacm_ds_attach(ds_attach_info_t *aip)
57790f05028Syq {
57890f05028Syq usbsacm_state_t *acmp;
57990f05028Syq
58090f05028Syq acmp = (usbsacm_state_t *)kmem_zalloc(sizeof (usbsacm_state_t),
58190f05028Syq KM_SLEEP);
58290f05028Syq acmp->acm_dip = aip->ai_dip;
58390f05028Syq acmp->acm_usb_events = aip->ai_usb_events;
58490f05028Syq acmp->acm_ports = NULL;
58590f05028Syq *aip->ai_hdl = (ds_hdl_t)acmp;
58690f05028Syq
58790f05028Syq /* registers usbsacm with the USBA framework */
58890f05028Syq if (usb_client_attach(acmp->acm_dip, USBDRV_VERSION,
58990f05028Syq 0) != USB_SUCCESS) {
59090f05028Syq
59190f05028Syq goto fail;
59290f05028Syq }
59390f05028Syq
59490f05028Syq /* Get the configuration information of device */
59590f05028Syq if (usb_get_dev_data(acmp->acm_dip, &acmp->acm_dev_data,
59690f05028Syq USB_PARSE_LVL_CFG, 0) != USB_SUCCESS) {
59790f05028Syq
59890f05028Syq goto fail;
59990f05028Syq }
60090f05028Syq acmp->acm_def_ph = acmp->acm_dev_data->dev_default_ph;
60190f05028Syq acmp->acm_dev_state = USB_DEV_ONLINE;
60290f05028Syq mutex_init(&acmp->acm_mutex, NULL, MUTEX_DRIVER,
60390f05028Syq acmp->acm_dev_data->dev_iblock_cookie);
60490f05028Syq
60590f05028Syq acmp->acm_lh = usb_alloc_log_hdl(acmp->acm_dip, "usbsacm",
60690f05028Syq &usbsacm_errlevel, &usbsacm_errmask, &usbsacm_instance_debug, 0);
60790f05028Syq
60890f05028Syq /* Create power management components */
60990f05028Syq if (usbsacm_create_pm_components(acmp) != USB_SUCCESS) {
61090f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
61190f05028Syq "usbsacm_ds_attach: create pm components failed.");
61290f05028Syq
61390f05028Syq goto fail;
61490f05028Syq }
61590f05028Syq
61690f05028Syq /* Register to get callbacks for USB events */
61790f05028Syq if (usb_register_event_cbs(acmp->acm_dip, acmp->acm_usb_events, 0)
61890f05028Syq != USB_SUCCESS) {
61990f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
62090f05028Syq "usbsacm_ds_attach: register event callback failed.");
62190f05028Syq
62290f05028Syq goto fail;
62390f05028Syq }
62490f05028Syq
62590f05028Syq /*
62690f05028Syq * If devices conform to acm spec, driver will attach using class id;
62790f05028Syq * if not, using device id.
62890f05028Syq */
62990f05028Syq if ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
63090f05028Syq "usbif,class2.2") == 0) ||
63190f05028Syq ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
63290f05028Syq "usb,class2.2.0") == 0))) {
63390f05028Syq
63490f05028Syq acmp->acm_compatibility = B_TRUE;
63590f05028Syq } else {
63690f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
63790f05028Syq "usbsacm_ds_attach: A nonstandard device is attaching to "
63890f05028Syq "usbsacm driver. This device doesn't conform to "
63990f05028Syq "usb cdc spec.");
64090f05028Syq
64190f05028Syq acmp->acm_compatibility = B_FALSE;
64290f05028Syq }
64390f05028Syq
64490f05028Syq /* initialize state variables */
64590f05028Syq if (usbsacm_init_alloc_ports(acmp) != USB_SUCCESS) {
64690f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
64790f05028Syq "usbsacm_ds_attach: initialize port structure failed.");
64890f05028Syq
64990f05028Syq goto fail;
65090f05028Syq }
65190f05028Syq *aip->ai_port_cnt = acmp->acm_port_cnt;
65290f05028Syq
65390f05028Syq /* Get max data size of bulk transfer */
65490f05028Syq if (usb_pipe_get_max_bulk_transfer_size(acmp->acm_dip,
65590f05028Syq &acmp->acm_xfer_sz) != USB_SUCCESS) {
65690f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
65790f05028Syq "usbsacm_ds_attach: get max size of transfer failed.");
65890f05028Syq
65990f05028Syq goto fail;
66090f05028Syq }
66190f05028Syq
66290f05028Syq return (USB_SUCCESS);
66390f05028Syq fail:
66490f05028Syq usbsacm_cleanup(acmp);
66590f05028Syq
66690f05028Syq return (USB_FAILURE);
66790f05028Syq }
66890f05028Syq
66990f05028Syq
67090f05028Syq /*
67190f05028Syq * usbsacm_ds_detach:
67290f05028Syq * detach device instance, called from GSD detach
67390f05028Syq */
67490f05028Syq static void
usbsacm_ds_detach(ds_hdl_t hdl)67590f05028Syq usbsacm_ds_detach(ds_hdl_t hdl)
67690f05028Syq {
67790f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
67890f05028Syq
67990f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
68090f05028Syq "usbsacm_ds_detach:");
68190f05028Syq
68290f05028Syq usbsacm_close_pipes(acmp);
68390f05028Syq usbsacm_cleanup(acmp);
68490f05028Syq }
68590f05028Syq
68690f05028Syq
68790f05028Syq /*
68890f05028Syq * usbsacm_ds_register_cb:
68990f05028Syq * GSD routine call ds_register_cb to register interrupt callbacks
69090f05028Syq * for the given port
69190f05028Syq */
69290f05028Syq /*ARGSUSED*/
69390f05028Syq static int
usbsacm_ds_register_cb(ds_hdl_t hdl,uint_t port_num,ds_cb_t * cb)69490f05028Syq usbsacm_ds_register_cb(ds_hdl_t hdl, uint_t port_num, ds_cb_t *cb)
69590f05028Syq {
69690f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
69790f05028Syq usbsacm_port_t *acm_port;
69890f05028Syq
69990f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
70090f05028Syq "usbsacm_ds_register_cb: acmp = 0x%p port_num = %d",
701112116d8Sfb (void *)acmp, port_num);
70290f05028Syq
70390f05028Syq /* Check if port number is greater than actual port number. */
70490f05028Syq if (port_num >= acmp->acm_port_cnt) {
70590f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
70690f05028Syq "usbsacm_ds_register_cb: port number is wrong.");
70790f05028Syq
70890f05028Syq return (USB_FAILURE);
70990f05028Syq }
71090f05028Syq acm_port = &acmp->acm_ports[port_num];
71190f05028Syq acm_port->acm_cb = *cb;
71290f05028Syq
71390f05028Syq return (USB_SUCCESS);
71490f05028Syq }
71590f05028Syq
71690f05028Syq
71790f05028Syq /*
71890f05028Syq * usbsacm_ds_unregister_cb:
71990f05028Syq * GSD routine call ds_unregister_cb to unregister
72090f05028Syq * interrupt callbacks for the given port
72190f05028Syq */
72290f05028Syq /*ARGSUSED*/
72390f05028Syq static void
usbsacm_ds_unregister_cb(ds_hdl_t hdl,uint_t port_num)72490f05028Syq usbsacm_ds_unregister_cb(ds_hdl_t hdl, uint_t port_num)
72590f05028Syq {
72690f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
72790f05028Syq usbsacm_port_t *acm_port;
72890f05028Syq
72990f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
73090f05028Syq "usbsacm_ds_unregister_cb: ");
73190f05028Syq
73290f05028Syq if (port_num < acmp->acm_port_cnt) {
73390f05028Syq /* Release callback function */
73490f05028Syq acm_port = &acmp->acm_ports[port_num];
73590f05028Syq bzero(&acm_port->acm_cb, sizeof (acm_port->acm_cb));
73690f05028Syq }
73790f05028Syq }
73890f05028Syq
73990f05028Syq
74090f05028Syq /*
74190f05028Syq * usbsacm_ds_open_port:
74290f05028Syq * GSD routine call ds_open_port
74390f05028Syq * to open the given port
74490f05028Syq */
74590f05028Syq /*ARGSUSED*/
74690f05028Syq static int
usbsacm_ds_open_port(ds_hdl_t hdl,uint_t port_num)74790f05028Syq usbsacm_ds_open_port(ds_hdl_t hdl, uint_t port_num)
74890f05028Syq {
74990f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
75090f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
75190f05028Syq
75290f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
75390f05028Syq "usbsacm_ds_open_port: port_num = %d", port_num);
75490f05028Syq
75590f05028Syq mutex_enter(&acm_port->acm_port_mutex);
75690f05028Syq /* Check the status of the given port and device */
75790f05028Syq if ((acmp->acm_dev_state == USB_DEV_DISCONNECTED) ||
75890f05028Syq (acm_port->acm_port_state != USBSACM_PORT_CLOSED)) {
75990f05028Syq mutex_exit(&acm_port->acm_port_mutex);
76090f05028Syq
76190f05028Syq return (USB_FAILURE);
76290f05028Syq }
76390f05028Syq mutex_exit(&acm_port->acm_port_mutex);
76490f05028Syq
76590f05028Syq usbsacm_pm_set_busy(acmp);
76690f05028Syq
76790f05028Syq /* open pipes of port */
76890f05028Syq if (usbsacm_open_port_pipes(acm_port) != USB_SUCCESS) {
76990f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
77090f05028Syq "usbsacm_ds_open_port: open pipes failed.");
77190f05028Syq
77290f05028Syq return (USB_FAILURE);
77390f05028Syq }
77490f05028Syq
77590f05028Syq mutex_enter(&acm_port->acm_port_mutex);
77690f05028Syq /* data receipt */
77790f05028Syq if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
77890f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
77990f05028Syq "usbsacm_ds_open_port: start receive data failed.");
78090f05028Syq mutex_exit(&acm_port->acm_port_mutex);
78190f05028Syq
78290f05028Syq return (USB_FAILURE);
78390f05028Syq }
78490f05028Syq acm_port->acm_port_state = USBSACM_PORT_OPEN;
78590f05028Syq
78690f05028Syq mutex_exit(&acm_port->acm_port_mutex);
78790f05028Syq
78890f05028Syq return (USB_SUCCESS);
78990f05028Syq }
79090f05028Syq
79190f05028Syq
79290f05028Syq /*
79390f05028Syq * usbsacm_ds_close_port:
79490f05028Syq * GSD routine call ds_close_port
79590f05028Syq * to close the given port
79690f05028Syq */
79790f05028Syq /*ARGSUSED*/
79890f05028Syq static int
usbsacm_ds_close_port(ds_hdl_t hdl,uint_t port_num)79990f05028Syq usbsacm_ds_close_port(ds_hdl_t hdl, uint_t port_num)
80090f05028Syq {
80190f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
80290f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
80390f05028Syq int rval = USB_SUCCESS;
80490f05028Syq
80590f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
806112116d8Sfb "usbsacm_ds_close_port: acmp = 0x%p", (void *)acmp);
80790f05028Syq
80890f05028Syq mutex_enter(&acm_port->acm_port_mutex);
80990f05028Syq acm_port->acm_port_state = USBSACM_PORT_CLOSED;
81090f05028Syq mutex_exit(&acm_port->acm_port_mutex);
81190f05028Syq
81290f05028Syq usbsacm_close_port_pipes(acm_port);
81390f05028Syq
81490f05028Syq mutex_enter(&acm_port->acm_port_mutex);
81590f05028Syq rval = usbsacm_fifo_flush_locked(acmp, port_num, DS_TX | DS_RX);
81690f05028Syq mutex_exit(&acm_port->acm_port_mutex);
81790f05028Syq
81890f05028Syq usbsacm_pm_set_idle(acmp);
81990f05028Syq
82090f05028Syq return (rval);
82190f05028Syq }
82290f05028Syq
82390f05028Syq
82490f05028Syq /*
82590f05028Syq * usbsacm_ds_usb_power:
82690f05028Syq * GSD routine call ds_usb_power
82790f05028Syq * to set power level of the component
82890f05028Syq */
82990f05028Syq /*ARGSUSED*/
83090f05028Syq static int
usbsacm_ds_usb_power(ds_hdl_t hdl,int comp,int level,int * new_state)83190f05028Syq usbsacm_ds_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state)
83290f05028Syq {
83390f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
83490f05028Syq usbsacm_pm_t *pm = acmp->acm_pm;
83590f05028Syq int rval = USB_SUCCESS;
83690f05028Syq
83790f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
83890f05028Syq "usbsacm_ds_usb_power: ");
83990f05028Syq
84090f05028Syq /* check if pm is NULL */
84190f05028Syq if (pm == NULL) {
84290f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
84390f05028Syq "usbsacm_ds_usb_power: pm is NULL.");
84490f05028Syq
84590f05028Syq return (USB_FAILURE);
84690f05028Syq }
84790f05028Syq
84890f05028Syq mutex_enter(&acmp->acm_mutex);
84990f05028Syq /*
85090f05028Syq * check if we are transitioning to a legal power level
85190f05028Syq */
85290f05028Syq if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
85390f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
85490f05028Syq "usbsacm_ds_usb_power: "
85590f05028Syq "illegal power level %d, pwr_states=%x",
85690f05028Syq level, pm->pm_pwr_states);
85790f05028Syq mutex_exit(&acmp->acm_mutex);
85890f05028Syq
85990f05028Syq return (USB_FAILURE);
86090f05028Syq }
86190f05028Syq
86290f05028Syq /*
86390f05028Syq * if we are about to raise power and asked to lower power, fail
86490f05028Syq */
86590f05028Syq if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
86690f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
86790f05028Syq "usbsacm_ds_usb_power: wrong condition.");
86890f05028Syq mutex_exit(&acmp->acm_mutex);
86990f05028Syq
87090f05028Syq return (USB_FAILURE);
87190f05028Syq }
87290f05028Syq
87390f05028Syq /*
87490f05028Syq * Set the power status of device by request level.
87590f05028Syq */
87690f05028Syq switch (level) {
87790f05028Syq case USB_DEV_OS_PWR_OFF:
87890f05028Syq rval = usbsacm_pwrlvl0(acmp);
87990f05028Syq
88090f05028Syq break;
88190f05028Syq case USB_DEV_OS_PWR_1:
88290f05028Syq rval = usbsacm_pwrlvl1(acmp);
88390f05028Syq
88490f05028Syq break;
88590f05028Syq case USB_DEV_OS_PWR_2:
88690f05028Syq rval = usbsacm_pwrlvl2(acmp);
88790f05028Syq
88890f05028Syq break;
88990f05028Syq case USB_DEV_OS_FULL_PWR:
89090f05028Syq rval = usbsacm_pwrlvl3(acmp);
8914ee52f77Slg /*
8924ee52f77Slg * If usbser dev_state is DISCONNECTED or SUSPENDED, it shows
8934ee52f77Slg * that the usb serial device is disconnected/suspended while it
8944ee52f77Slg * is under power down state, now the device is powered up
8954ee52f77Slg * before it is reconnected/resumed. xxx_pwrlvl3() will set dev
8964ee52f77Slg * state to ONLINE, we need to set the dev state back to
8974ee52f77Slg * DISCONNECTED/SUSPENDED.
8984ee52f77Slg */
8994ee52f77Slg if ((rval == USB_SUCCESS) &&
9004ee52f77Slg ((*new_state == USB_DEV_DISCONNECTED) ||
9014ee52f77Slg (*new_state == USB_DEV_SUSPENDED))) {
9024ee52f77Slg acmp->acm_dev_state = *new_state;
9034ee52f77Slg }
90490f05028Syq
90590f05028Syq break;
90690f05028Syq }
90790f05028Syq
90890f05028Syq *new_state = acmp->acm_dev_state;
90990f05028Syq mutex_exit(&acmp->acm_mutex);
91090f05028Syq
91190f05028Syq return (rval);
91290f05028Syq }
91390f05028Syq
91490f05028Syq
91590f05028Syq /*
91690f05028Syq * usbsacm_ds_suspend:
91790f05028Syq * GSD routine call ds_suspend
91890f05028Syq * during CPR suspend
91990f05028Syq */
92090f05028Syq static int
usbsacm_ds_suspend(ds_hdl_t hdl)92190f05028Syq usbsacm_ds_suspend(ds_hdl_t hdl)
92290f05028Syq {
92390f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
9244ee52f77Slg int state = USB_DEV_SUSPENDED;
92590f05028Syq
92690f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
92790f05028Syq "usbsacm_ds_suspend: ");
9284ee52f77Slg /*
9294ee52f77Slg * If the device is suspended while it is under PWRED_DOWN state, we
9304ee52f77Slg * need to keep the PWRED_DOWN state so that it could be powered up
9314ee52f77Slg * later. In the mean while, usbser dev state will be changed to
9324ee52f77Slg * SUSPENDED state.
9334ee52f77Slg */
93490f05028Syq mutex_enter(&acmp->acm_mutex);
9354ee52f77Slg if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
9364ee52f77Slg /* set device status to suspend */
9374ee52f77Slg acmp->acm_dev_state = USB_DEV_SUSPENDED;
9384ee52f77Slg }
93990f05028Syq mutex_exit(&acmp->acm_mutex);
94090f05028Syq
94190f05028Syq usbsacm_disconnect_pipes(acmp);
94290f05028Syq
94390f05028Syq return (state);
94490f05028Syq }
94590f05028Syq
94690f05028Syq /*
94790f05028Syq * usbsacm_ds_resume:
94890f05028Syq * GSD routine call ds_resume
94990f05028Syq * during CPR resume
95090f05028Syq */
95190f05028Syq /*ARGSUSED*/
95290f05028Syq static int
usbsacm_ds_resume(ds_hdl_t hdl)95390f05028Syq usbsacm_ds_resume(ds_hdl_t hdl)
95490f05028Syq {
95590f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
95690f05028Syq int current_state;
95790f05028Syq int ret;
95890f05028Syq
95990f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
96090f05028Syq "usbsacm_ds_resume: ");
96190f05028Syq
96290f05028Syq mutex_enter(&acmp->acm_mutex);
96390f05028Syq current_state = acmp->acm_dev_state;
96490f05028Syq mutex_exit(&acmp->acm_mutex);
96590f05028Syq
96690f05028Syq /* restore the status of device */
96790f05028Syq if (current_state != USB_DEV_ONLINE) {
96890f05028Syq ret = usbsacm_restore_device_state(acmp);
96990f05028Syq } else {
97090f05028Syq ret = USB_DEV_ONLINE;
97190f05028Syq }
97290f05028Syq
97390f05028Syq return (ret);
97490f05028Syq }
97590f05028Syq
97690f05028Syq /*
97790f05028Syq * usbsacm_ds_disconnect:
97890f05028Syq * GSD routine call ds_disconnect
97990f05028Syq * to disconnect USB device
98090f05028Syq */
98190f05028Syq static int
usbsacm_ds_disconnect(ds_hdl_t hdl)98290f05028Syq usbsacm_ds_disconnect(ds_hdl_t hdl)
98390f05028Syq {
98490f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
9854ee52f77Slg int state = USB_DEV_DISCONNECTED;
98690f05028Syq
98790f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
98890f05028Syq "usbsacm_ds_disconnect: ");
98990f05028Syq
9904ee52f77Slg /*
9914ee52f77Slg * If the device is disconnected while it is under PWRED_DOWN state, we
9924ee52f77Slg * need to keep the PWRED_DOWN state so that it could be powered up
9934ee52f77Slg * later. In the mean while, usbser dev state will be changed to
9944ee52f77Slg * DISCONNECTED state.
9954ee52f77Slg */
99690f05028Syq mutex_enter(&acmp->acm_mutex);
9974ee52f77Slg if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
9984ee52f77Slg /* set device status to disconnected */
9994ee52f77Slg acmp->acm_dev_state = USB_DEV_DISCONNECTED;
10004ee52f77Slg }
100190f05028Syq mutex_exit(&acmp->acm_mutex);
100290f05028Syq
100390f05028Syq usbsacm_disconnect_pipes(acmp);
100490f05028Syq
100590f05028Syq return (state);
100690f05028Syq }
100790f05028Syq
100890f05028Syq
100990f05028Syq /*
101090f05028Syq * usbsacm_ds_reconnect:
101190f05028Syq * GSD routine call ds_reconnect
101290f05028Syq * to reconnect USB device
101390f05028Syq */
101490f05028Syq /*ARGSUSED*/
101590f05028Syq static int
usbsacm_ds_reconnect(ds_hdl_t hdl)101690f05028Syq usbsacm_ds_reconnect(ds_hdl_t hdl)
101790f05028Syq {
101890f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
101990f05028Syq
102090f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
102190f05028Syq "usbsacm_ds_reconnect: ");
102290f05028Syq
102390f05028Syq return (usbsacm_restore_device_state(acmp));
102490f05028Syq }
102590f05028Syq
102690f05028Syq
102790f05028Syq /*
102890f05028Syq * usbsacm_ds_set_port_params:
102990f05028Syq * GSD routine call ds_set_port_params
103090f05028Syq * to set one or more port parameters
103190f05028Syq */
103290f05028Syq /*ARGSUSED*/
103390f05028Syq static int
usbsacm_ds_set_port_params(ds_hdl_t hdl,uint_t port_num,ds_port_params_t * tp)103490f05028Syq usbsacm_ds_set_port_params(ds_hdl_t hdl, uint_t port_num, ds_port_params_t *tp)
103590f05028Syq {
103690f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
103790f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
103890f05028Syq int i;
103990f05028Syq uint_t ui;
104090f05028Syq ds_port_param_entry_t *pe;
104190f05028Syq usb_cdc_line_coding_t lc;
104290f05028Syq int ret;
104390f05028Syq
104490f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1045112116d8Sfb "usbsacm_ds_set_port_params: acmp = 0x%p", (void *)acmp);
104690f05028Syq
104790f05028Syq mutex_enter(&acm_port->acm_port_mutex);
104890f05028Syq /*
104990f05028Syq * If device conform to acm spec, check if it support to set port param.
105090f05028Syq */
105190f05028Syq if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
105290f05028Syq acmp->acm_compatibility == B_TRUE) {
105390f05028Syq
105490f05028Syq mutex_exit(&acm_port->acm_port_mutex);
105590f05028Syq USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
105690f05028Syq "usbsacm_ds_set_port_params: "
105790f05028Syq "don't support Set_Line_Coding.");
105890f05028Syq
105990f05028Syq return (USB_FAILURE);
106090f05028Syq }
106190f05028Syq
106290f05028Syq lc = acm_port->acm_line_coding;
106390f05028Syq mutex_exit(&acm_port->acm_port_mutex);
106490f05028Syq pe = tp->tp_entries;
106590f05028Syq /* Get parameter information from ds_port_params_t */
106690f05028Syq for (i = 0; i < tp->tp_cnt; i++, pe++) {
106790f05028Syq switch (pe->param) {
106890f05028Syq case DS_PARAM_BAUD:
106990f05028Syq /* Data terminal rate, in bits per second. */
107090f05028Syq ui = pe->val.ui;
107190f05028Syq
107290f05028Syq /* if we don't support this speed, return USB_FAILURE */
107390f05028Syq if ((ui >= NELEM(usbsacm_speedtab)) ||
107490f05028Syq ((ui > 0) && (usbsacm_speedtab[ui] == 0))) {
107590f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
107690f05028Syq "usbsacm_ds_set_port_params: "
107790f05028Syq " error baud rate");
107890f05028Syq
107990f05028Syq return (USB_FAILURE);
108090f05028Syq }
108190f05028Syq lc.dwDTERate = LE_32(usbsacm_speedtab[ui]);
108290f05028Syq
108390f05028Syq break;
108490f05028Syq case DS_PARAM_PARITY:
108590f05028Syq /* Parity Type */
108690f05028Syq if (pe->val.ui & PARENB) {
108790f05028Syq if (pe->val.ui & PARODD) {
108890f05028Syq lc.bParityType = USB_CDC_PARITY_ODD;
108990f05028Syq } else {
109090f05028Syq lc.bParityType = USB_CDC_PARITY_EVEN;
109190f05028Syq }
109290f05028Syq } else {
109390f05028Syq lc.bParityType = USB_CDC_PARITY_NO;
109490f05028Syq }
109590f05028Syq
109690f05028Syq break;
109790f05028Syq case DS_PARAM_STOPB:
109890f05028Syq /* Stop bit */
109990f05028Syq if (pe->val.ui & CSTOPB) {
110090f05028Syq lc.bCharFormat = USB_CDC_STOP_BITS_2;
110190f05028Syq } else {
110290f05028Syq lc.bCharFormat = USB_CDC_STOP_BITS_1;
110390f05028Syq }
110490f05028Syq
110590f05028Syq break;
110690f05028Syq case DS_PARAM_CHARSZ:
110790f05028Syq /* Data Bits */
110890f05028Syq switch (pe->val.ui) {
110990f05028Syq case CS5:
111090f05028Syq lc.bDataBits = 5;
111190f05028Syq break;
111290f05028Syq case CS6:
111390f05028Syq lc.bDataBits = 6;
111490f05028Syq break;
111590f05028Syq case CS7:
111690f05028Syq lc.bDataBits = 7;
111790f05028Syq break;
111890f05028Syq case CS8:
111990f05028Syq default:
112090f05028Syq lc.bDataBits = 8;
112190f05028Syq break;
112290f05028Syq }
112390f05028Syq
112490f05028Syq break;
112590f05028Syq default:
112690f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
112790f05028Syq "usbsacm_ds_set_port_params: "
112890f05028Syq "parameter 0x%x isn't supported",
112990f05028Syq pe->param);
113090f05028Syq
113190f05028Syq break;
113290f05028Syq }
113390f05028Syq }
113490f05028Syq
113590f05028Syq if ((ret = usbsacm_set_line_coding(acm_port, &lc)) == USB_SUCCESS) {
113690f05028Syq mutex_enter(&acm_port->acm_port_mutex);
113790f05028Syq acm_port->acm_line_coding = lc;
113890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
113990f05028Syq }
114090f05028Syq
114190f05028Syq /*
114290f05028Syq * If device don't conform to acm spec, return success directly.
114390f05028Syq */
114490f05028Syq if (acmp->acm_compatibility != B_TRUE) {
114590f05028Syq ret = USB_SUCCESS;
114690f05028Syq }
114790f05028Syq
114890f05028Syq return (ret);
114990f05028Syq }
115090f05028Syq
115190f05028Syq
115290f05028Syq /*
115390f05028Syq * usbsacm_ds_set_modem_ctl:
115490f05028Syq * GSD routine call ds_set_modem_ctl
115590f05028Syq * to set modem control of the given port
115690f05028Syq */
115790f05028Syq /*ARGSUSED*/
115890f05028Syq static int
usbsacm_ds_set_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int val)115990f05028Syq usbsacm_ds_set_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int val)
116090f05028Syq {
116190f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
116290f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
116390f05028Syq uint8_t new_mctl;
116490f05028Syq int ret;
116590f05028Syq
116690f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
116790f05028Syq "usbsacm_ds_set_modem_ctl: mask = 0x%x val = 0x%x",
116890f05028Syq mask, val);
116990f05028Syq
117090f05028Syq mutex_enter(&acm_port->acm_port_mutex);
117190f05028Syq /*
117290f05028Syq * If device conform to acm spec, check if it support to set modem
117390f05028Syq * controls.
117490f05028Syq */
117590f05028Syq if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
11764ee52f77Slg acmp->acm_compatibility == B_TRUE) {
117790f05028Syq
117890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
117990f05028Syq USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
118090f05028Syq "usbsacm_ds_set_modem_ctl: "
118190f05028Syq "don't support Set_Control_Line_State.");
118290f05028Syq
118390f05028Syq return (USB_FAILURE);
118490f05028Syq }
118590f05028Syq
118690f05028Syq new_mctl = acm_port->acm_mctlout;
118790f05028Syq mutex_exit(&acm_port->acm_port_mutex);
118890f05028Syq
118990f05028Syq usbsacm_mctl2reg(mask, val, &new_mctl);
119090f05028Syq
119190f05028Syq if ((acmp->acm_compatibility == B_FALSE) || ((ret =
119290f05028Syq usbsacm_req_write(acm_port, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
119390f05028Syq new_mctl, NULL)) == USB_SUCCESS)) {
119490f05028Syq mutex_enter(&acm_port->acm_port_mutex);
119590f05028Syq acm_port->acm_mctlout = new_mctl;
119690f05028Syq mutex_exit(&acm_port->acm_port_mutex);
119790f05028Syq }
119890f05028Syq
119990f05028Syq /*
120090f05028Syq * If device don't conform to acm spec, return success directly.
120190f05028Syq */
120290f05028Syq if (acmp->acm_compatibility != B_TRUE) {
120390f05028Syq ret = USB_SUCCESS;
120490f05028Syq }
120590f05028Syq
120690f05028Syq return (ret);
120790f05028Syq }
120890f05028Syq
120990f05028Syq
121090f05028Syq /*
121190f05028Syq * usbsacm_ds_get_modem_ctl:
121290f05028Syq * GSD routine call ds_get_modem_ctl
121390f05028Syq * to get modem control/status of the given port
121490f05028Syq */
121590f05028Syq /*ARGSUSED*/
121690f05028Syq static int
usbsacm_ds_get_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int * valp)121790f05028Syq usbsacm_ds_get_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int *valp)
121890f05028Syq {
121990f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
122090f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
122190f05028Syq
122290f05028Syq mutex_enter(&acm_port->acm_port_mutex);
122390f05028Syq *valp = usbsacm_reg2mctl(acm_port->acm_mctlout) & mask;
122490f05028Syq /*
122590f05028Syq * If device conform to acm spec, polling function can modify the value
122690f05028Syq * of acm_mctlin; else set to default value.
122790f05028Syq */
122890f05028Syq if (acmp->acm_compatibility) {
122990f05028Syq *valp |= usbsacm_reg2mctl(acm_port->acm_mctlin) & mask;
123090f05028Syq *valp |= (mask & (TIOCM_CD | TIOCM_CTS));
123190f05028Syq } else {
123290f05028Syq *valp |= (mask & (TIOCM_CD | TIOCM_CTS | TIOCM_DSR | TIOCM_RI));
123390f05028Syq }
123490f05028Syq mutex_exit(&acm_port->acm_port_mutex);
123590f05028Syq
123690f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
123790f05028Syq "usbsacm_ds_get_modem_ctl: val = 0x%x", *valp);
123890f05028Syq
123990f05028Syq return (USB_SUCCESS);
124090f05028Syq }
124190f05028Syq
124290f05028Syq
124390f05028Syq /*
124490f05028Syq * usbsacm_ds_tx:
124590f05028Syq * GSD routine call ds_break_ctl
124690f05028Syq * to set/clear break
124790f05028Syq */
124890f05028Syq /*ARGSUSED*/
124990f05028Syq static int
usbsacm_ds_break_ctl(ds_hdl_t hdl,uint_t port_num,int ctl)125090f05028Syq usbsacm_ds_break_ctl(ds_hdl_t hdl, uint_t port_num, int ctl)
125190f05028Syq {
125290f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
125390f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
125490f05028Syq
125590f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
125690f05028Syq "usbsacm_ds_break_ctl: ");
125790f05028Syq
125890f05028Syq mutex_enter(&acm_port->acm_port_mutex);
125990f05028Syq /*
126090f05028Syq * If device conform to acm spec, check if it support to send break.
126190f05028Syq */
126290f05028Syq if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SEND_BREAK) == 0 &&
12634ee52f77Slg acmp->acm_compatibility == B_TRUE) {
126490f05028Syq
126590f05028Syq mutex_exit(&acm_port->acm_port_mutex);
126690f05028Syq USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
126790f05028Syq "usbsacm_ds_break_ctl: don't support send break.");
126890f05028Syq
126990f05028Syq return (USB_FAILURE);
127090f05028Syq }
127190f05028Syq mutex_exit(&acm_port->acm_port_mutex);
127290f05028Syq
127390f05028Syq return (usbsacm_req_write(acm_port, USB_CDC_REQ_SEND_BREAK,
12744ee52f77Slg ((ctl == DS_ON) ? 0xffff : 0), NULL));
127590f05028Syq }
127690f05028Syq
127790f05028Syq
127890f05028Syq /*
127990f05028Syq * usbsacm_ds_tx:
128090f05028Syq * GSD routine call ds_tx
128190f05028Syq * to data transmit
128290f05028Syq */
128390f05028Syq /*ARGSUSED*/
128490f05028Syq static int
usbsacm_ds_tx(ds_hdl_t hdl,uint_t port_num,mblk_t * mp)128590f05028Syq usbsacm_ds_tx(ds_hdl_t hdl, uint_t port_num, mblk_t *mp)
128690f05028Syq {
128790f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
128890f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
128990f05028Syq
129090f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1291112116d8Sfb "usbsacm_ds_tx: mp = 0x%p acmp = 0x%p", (void *)mp, (void *)acmp);
129290f05028Syq
129390f05028Syq /* sanity checks */
129490f05028Syq if (mp == NULL) {
129590f05028Syq
129690f05028Syq return (USB_SUCCESS);
129790f05028Syq }
129822eb7cb5Sgd if (MBLKL(mp) < 1) {
129990f05028Syq freemsg(mp);
130090f05028Syq
130190f05028Syq return (USB_SUCCESS);
130290f05028Syq }
130390f05028Syq
130490f05028Syq mutex_enter(&acm_port->acm_port_mutex);
130590f05028Syq /* put mblk to tail of mblk chain */
130690f05028Syq usbsacm_put_tail(&acm_port->acm_tx_mp, mp);
130790f05028Syq usbsacm_tx_start(acm_port);
130890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
130990f05028Syq
131090f05028Syq return (USB_SUCCESS);
131190f05028Syq }
131290f05028Syq
131390f05028Syq
131490f05028Syq /*
131590f05028Syq * usbsacm_ds_rx:
131690f05028Syq * GSD routine call ds_rx;
131790f05028Syq * to data receipt
131890f05028Syq */
131990f05028Syq /*ARGSUSED*/
132090f05028Syq static mblk_t *
usbsacm_ds_rx(ds_hdl_t hdl,uint_t port_num)132190f05028Syq usbsacm_ds_rx(ds_hdl_t hdl, uint_t port_num)
132290f05028Syq {
132390f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
132490f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
132590f05028Syq mblk_t *mp;
132690f05028Syq
132790f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1328112116d8Sfb "usbsacm_ds_rx: acmp = 0x%p", (void *)acmp);
132990f05028Syq
133090f05028Syq mutex_enter(&acm_port->acm_port_mutex);
133190f05028Syq
133290f05028Syq mp = acm_port->acm_rx_mp;
133390f05028Syq acm_port->acm_rx_mp = NULL;
133490f05028Syq mutex_exit(&acm_port->acm_port_mutex);
133590f05028Syq
133690f05028Syq return (mp);
133790f05028Syq }
133890f05028Syq
133990f05028Syq
134090f05028Syq /*
134190f05028Syq * usbsacm_ds_stop:
134290f05028Syq * GSD routine call ds_stop;
134390f05028Syq * but acm spec don't define this function
134490f05028Syq */
134590f05028Syq /*ARGSUSED*/
134690f05028Syq static void
usbsacm_ds_stop(ds_hdl_t hdl,uint_t port_num,int dir)134790f05028Syq usbsacm_ds_stop(ds_hdl_t hdl, uint_t port_num, int dir)
134890f05028Syq {
134990f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
135090f05028Syq
135190f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
135290f05028Syq "usbsacm_ds_stop: don't support!");
135390f05028Syq }
135490f05028Syq
135590f05028Syq
135690f05028Syq /*
135790f05028Syq * usbsacm_ds_start:
135890f05028Syq * GSD routine call ds_start;
135990f05028Syq * but acm spec don't define this function
136090f05028Syq */
136190f05028Syq /*ARGSUSED*/
136290f05028Syq static void
usbsacm_ds_start(ds_hdl_t hdl,uint_t port_num,int dir)136390f05028Syq usbsacm_ds_start(ds_hdl_t hdl, uint_t port_num, int dir)
136490f05028Syq {
136590f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
136690f05028Syq
136790f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
136890f05028Syq "usbsacm_ds_start: don't support!");
136990f05028Syq }
137090f05028Syq
137190f05028Syq
137290f05028Syq /*
137390f05028Syq * usbsacm_ds_fifo_flush:
137490f05028Syq * GSD routine call ds_fifo_flush
137590f05028Syq * to flush FIFOs
137690f05028Syq */
137790f05028Syq /*ARGSUSED*/
137890f05028Syq static int
usbsacm_ds_fifo_flush(ds_hdl_t hdl,uint_t port_num,int dir)137990f05028Syq usbsacm_ds_fifo_flush(ds_hdl_t hdl, uint_t port_num, int dir)
138090f05028Syq {
138190f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
138290f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
138390f05028Syq int ret = USB_SUCCESS;
138490f05028Syq
138590f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
138690f05028Syq "usbsacm_ds_fifo_flush: ");
138790f05028Syq
138890f05028Syq mutex_enter(&acm_port->acm_port_mutex);
138990f05028Syq ret = usbsacm_fifo_flush_locked(acmp, port_num, dir);
139090f05028Syq mutex_exit(&acm_port->acm_port_mutex);
139190f05028Syq
139290f05028Syq return (ret);
139390f05028Syq }
139490f05028Syq
139590f05028Syq
139690f05028Syq /*
139790f05028Syq * usbsacm_ds_fifo_drain:
139890f05028Syq * GSD routine call ds_fifo_drain
139990f05028Syq * to wait until empty output FIFO
140090f05028Syq */
140190f05028Syq /*ARGSUSED*/
140290f05028Syq static int
usbsacm_ds_fifo_drain(ds_hdl_t hdl,uint_t port_num,int timeout)140390f05028Syq usbsacm_ds_fifo_drain(ds_hdl_t hdl, uint_t port_num, int timeout)
140490f05028Syq {
140590f05028Syq usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
140690f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
140790f05028Syq int rval = USB_SUCCESS;
140890f05028Syq
140990f05028Syq USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
141090f05028Syq "usbsacm_ds_fifo_drain: ");
141190f05028Syq
141290f05028Syq mutex_enter(&acm_port->acm_port_mutex);
141390f05028Syq ASSERT(acm_port->acm_port_state == USBSACM_PORT_OPEN);
141490f05028Syq
141590f05028Syq if (usbsacm_wait_tx_drain(acm_port, timeout) != USB_SUCCESS) {
141690f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
141790f05028Syq "usbsacm_ds_fifo_drain: fifo drain failed.");
141890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
141990f05028Syq
142090f05028Syq return (USB_FAILURE);
142190f05028Syq }
142290f05028Syq
142390f05028Syq mutex_exit(&acm_port->acm_port_mutex);
142490f05028Syq
142590f05028Syq return (rval);
142690f05028Syq }
142790f05028Syq
142890f05028Syq
142990f05028Syq /*
143090f05028Syq * usbsacm_fifo_flush_locked:
143190f05028Syq * flush FIFOs of the given ports
143290f05028Syq */
143390f05028Syq /*ARGSUSED*/
143490f05028Syq static int
usbsacm_fifo_flush_locked(usbsacm_state_t * acmp,uint_t port_num,int dir)143590f05028Syq usbsacm_fifo_flush_locked(usbsacm_state_t *acmp, uint_t port_num, int dir)
143690f05028Syq {
143790f05028Syq usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
143890f05028Syq
143990f05028Syq USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
144090f05028Syq "usbsacm_fifo_flush_locked: ");
144190f05028Syq
144290f05028Syq /* flush transmit FIFO if DS_TX is set */
144390f05028Syq if ((dir & DS_TX) && acm_port->acm_tx_mp) {
144490f05028Syq freemsg(acm_port->acm_tx_mp);
144590f05028Syq acm_port->acm_tx_mp = NULL;
144690f05028Syq }
144790f05028Syq /* flush received FIFO if DS_RX is set */
144890f05028Syq if ((dir & DS_RX) && acm_port->acm_rx_mp) {
144990f05028Syq freemsg(acm_port->acm_rx_mp);
145090f05028Syq acm_port->acm_rx_mp = NULL;
145190f05028Syq }
145290f05028Syq
145390f05028Syq return (USB_SUCCESS);
145490f05028Syq }
145590f05028Syq
145690f05028Syq
145790f05028Syq /*
145890f05028Syq * usbsacm_get_bulk_pipe_number:
145990f05028Syq * Calculate the number of bulk in or out pipes in current device.
146090f05028Syq */
146190f05028Syq static int
usbsacm_get_bulk_pipe_number(usbsacm_state_t * acmp,uint_t dir)146290f05028Syq usbsacm_get_bulk_pipe_number(usbsacm_state_t *acmp, uint_t dir)
146390f05028Syq {
146490f05028Syq int count = 0;
146590f05028Syq int i, skip;
146690f05028Syq usb_if_data_t *cur_if;
146790f05028Syq int ep_num;
146890f05028Syq int if_num;
146990f05028Syq
147090f05028Syq USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
147190f05028Syq "usbsacm_get_bulk_pipe_number: ");
147290f05028Syq
147390f05028Syq cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
147490f05028Syq if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
147590f05028Syq
147690f05028Syq /* search each interface which have bulk endpoint */
147790f05028Syq for (i = 0; i < if_num; i++) {
147890f05028Syq ep_num = cur_if->if_alt->altif_n_ep;
147990f05028Syq
148090f05028Syq /*
148190f05028Syq * search endpoints in current interface,
148290f05028Syq * which type is input parameter 'dir'
148390f05028Syq */
148490f05028Syq for (skip = 0; skip < ep_num; skip++) {
14854ee52f77Slg if (usb_lookup_ep_data(acmp->acm_dip,
14869e37f2b5SRaymond Chen acmp->acm_dev_data, i, 0, skip,
14874ee52f77Slg USB_EP_ATTR_BULK, dir) == NULL) {
14884ee52f77Slg
14894ee52f77Slg /*
14904ee52f77Slg * If not found, skip the internal loop
14914ee52f77Slg * and search the next interface.
14924ee52f77Slg */
14934ee52f77Slg break;
14944ee52f77Slg }
14954ee52f77Slg count++;
149690f05028Syq }
149790f05028Syq
149890f05028Syq cur_if++;
149990f05028Syq }
150090f05028Syq
150190f05028Syq return (count);
150290f05028Syq }
150390f05028Syq
150490f05028Syq
150590f05028Syq /*
150690f05028Syq * port management
150790f05028Syq * ---------------
150890f05028Syq * initialize, release port.
150990f05028Syq *
151090f05028Syq *
151190f05028Syq * usbsacm_init_ports_status:
151290f05028Syq * Initialize the port status for the current device.
151390f05028Syq */
151490f05028Syq static int
usbsacm_init_ports_status(usbsacm_state_t * acmp)151590f05028Syq usbsacm_init_ports_status(usbsacm_state_t *acmp)
151690f05028Syq {
151790f05028Syq usbsacm_port_t *cur_port;
151890f05028Syq int i, skip;
151990f05028Syq int if_num;
152090f05028Syq int intr_if_no = 0;
152190f05028Syq int ep_num;
152290f05028Syq usb_if_data_t *cur_if;
152390f05028Syq
152490f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
1525112116d8Sfb "usbsacm_init_ports_status: acmp = 0x%p", (void *)acmp);
152690f05028Syq
152790f05028Syq /* Initialize the port status to default value */
152890f05028Syq for (i = 0; i < acmp->acm_port_cnt; i++) {
152990f05028Syq cur_port = &acmp->acm_ports[i];
153090f05028Syq
153190f05028Syq cv_init(&cur_port->acm_tx_cv, NULL, CV_DRIVER, NULL);
153290f05028Syq
153390f05028Syq cur_port->acm_port_state = USBSACM_PORT_CLOSED;
153490f05028Syq
153590f05028Syq cur_port->acm_line_coding.dwDTERate = LE_32((uint32_t)9600);
153690f05028Syq cur_port->acm_line_coding.bCharFormat = 0;
153790f05028Syq cur_port->acm_line_coding.bParityType = USB_CDC_PARITY_NO;
153890f05028Syq cur_port->acm_line_coding.bDataBits = 8;
153990f05028Syq cur_port->acm_device = acmp;
154090f05028Syq mutex_init(&cur_port->acm_port_mutex, NULL, MUTEX_DRIVER,
154190f05028Syq acmp->acm_dev_data->dev_iblock_cookie);
154290f05028Syq }
154390f05028Syq
154490f05028Syq /*
154590f05028Syq * If device conform to cdc acm spec, parse function descriptors.
154690f05028Syq */
154790f05028Syq if (acmp->acm_compatibility == B_TRUE) {
154890f05028Syq
154990f05028Syq if (usbsacm_get_descriptors(acmp) != USB_SUCCESS) {
155090f05028Syq
155190f05028Syq return (USB_FAILURE);
155290f05028Syq }
155390f05028Syq
155490f05028Syq return (USB_SUCCESS);
155590f05028Syq }
155690f05028Syq
155790f05028Syq /*
155890f05028Syq * If device don't conform to spec, search pairs of bulk in/out
155990f05028Syq * endpoints and fill port structure.
156090f05028Syq */
156190f05028Syq cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
156290f05028Syq if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
156390f05028Syq cur_port = acmp->acm_ports;
156490f05028Syq
156590f05028Syq /* search each interface which have bulk in and out */
156690f05028Syq for (i = 0; i < if_num; i++) {
15674ee52f77Slg ep_num = cur_if->if_alt->altif_n_ep;
156890f05028Syq
15694ee52f77Slg for (skip = 0; skip < ep_num; skip++) {
157090f05028Syq
157190f05028Syq /* search interrupt pipe. */
157290f05028Syq if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
157390f05028Syq i, 0, skip, USB_EP_ATTR_INTR, USB_EP_DIR_IN) != NULL)) {
157490f05028Syq
157590f05028Syq intr_if_no = i;
157690f05028Syq }
157790f05028Syq
157890f05028Syq /* search pair of bulk in/out endpoints. */
157990f05028Syq if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
158090f05028Syq i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_IN) == NULL) ||
158190f05028Syq (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
158290f05028Syq i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_OUT) == NULL)) {
158390f05028Syq
158490f05028Syq continue;
158590f05028Syq }
158690f05028Syq
158790f05028Syq cur_port->acm_data_if_no = i;
158890f05028Syq cur_port->acm_ctrl_if_no = intr_if_no;
158990f05028Syq cur_port->acm_data_port_no = skip;
159090f05028Syq cur_port++;
159190f05028Syq intr_if_no = 0;
15924ee52f77Slg }
159390f05028Syq
15944ee52f77Slg cur_if++;
159590f05028Syq }
159690f05028Syq
159790f05028Syq return (USB_SUCCESS);
159890f05028Syq }
159990f05028Syq
160090f05028Syq
160190f05028Syq /*
160290f05028Syq * usbsacm_init_alloc_ports:
160390f05028Syq * Allocate memory and initialize the port state for the current device.
160490f05028Syq */
160590f05028Syq static int
usbsacm_init_alloc_ports(usbsacm_state_t * acmp)160690f05028Syq usbsacm_init_alloc_ports(usbsacm_state_t *acmp)
160790f05028Syq {
16084ee52f77Slg int rval = USB_SUCCESS;
160990f05028Syq int count_in = 0, count_out = 0;
161090f05028Syq
161190f05028Syq if (acmp->acm_compatibility) {
161290f05028Syq acmp->acm_port_cnt = 1;
161390f05028Syq } else {
161490f05028Syq /* Calculate the number of the bulk in/out endpoints */
161590f05028Syq count_in = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_IN);
161690f05028Syq count_out = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_OUT);
161790f05028Syq
161890f05028Syq USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
161990f05028Syq "usbsacm_init_alloc_ports: count_in = %d, count_out = %d",
162090f05028Syq count_in, count_out);
162190f05028Syq
162290f05028Syq acmp->acm_port_cnt = min(count_in, count_out);
162390f05028Syq }
162490f05028Syq
162590f05028Syq /* return if not found any pair of bulk in/out endpoint. */
162690f05028Syq if (acmp->acm_port_cnt == 0) {
162790f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
162890f05028Syq "usbsacm_init_alloc_ports: port count is zero.");
162990f05028Syq
163090f05028Syq return (USB_FAILURE);
163190f05028Syq }
163290f05028Syq
163390f05028Syq /* allocate memory for ports */
163490f05028Syq acmp->acm_ports = (usbsacm_port_t *)kmem_zalloc(acmp->acm_port_cnt *
163590f05028Syq sizeof (usbsacm_port_t), KM_SLEEP);
163690f05028Syq if (acmp->acm_ports == NULL) {
163790f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
163890f05028Syq "usbsacm_init_alloc_ports: allocate memory failed.");
163990f05028Syq
164090f05028Syq return (USB_FAILURE);
164190f05028Syq }
164290f05028Syq
164390f05028Syq /* fill the status of port structure. */
164490f05028Syq rval = usbsacm_init_ports_status(acmp);
164590f05028Syq if (rval != USB_SUCCESS) {
164690f05028Syq usbsacm_free_ports(acmp);
164790f05028Syq }
164890f05028Syq
164990f05028Syq return (rval);
165090f05028Syq }
165190f05028Syq
165290f05028Syq
165390f05028Syq /*
165490f05028Syq * usbsacm_free_ports:
165590f05028Syq * Release ports and deallocate memory.
165690f05028Syq */
165790f05028Syq static void
usbsacm_free_ports(usbsacm_state_t * acmp)165890f05028Syq usbsacm_free_ports(usbsacm_state_t *acmp)
165990f05028Syq {
166090f05028Syq int i;
166190f05028Syq
166290f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
166390f05028Syq "usbsacm_free_ports: ");
166490f05028Syq
166590f05028Syq /* Release memory and data structure for each port */
166690f05028Syq for (i = 0; i < acmp->acm_port_cnt; i++) {
166790f05028Syq cv_destroy(&acmp->acm_ports[i].acm_tx_cv);
166890f05028Syq mutex_destroy(&acmp->acm_ports[i].acm_port_mutex);
166990f05028Syq }
167090f05028Syq kmem_free((caddr_t)acmp->acm_ports, sizeof (usbsacm_port_t) *
167190f05028Syq acmp->acm_port_cnt);
167290f05028Syq acmp->acm_ports = NULL;
167390f05028Syq }
167490f05028Syq
167590f05028Syq
167690f05028Syq /*
167790f05028Syq * usbsacm_get_descriptors:
167890f05028Syq * analysis functional descriptors of acm device
167990f05028Syq */
168090f05028Syq static int
usbsacm_get_descriptors(usbsacm_state_t * acmp)168190f05028Syq usbsacm_get_descriptors(usbsacm_state_t *acmp)
168290f05028Syq {
168390f05028Syq int i;
168490f05028Syq usb_cfg_data_t *cfg;
168590f05028Syq usb_alt_if_data_t *altif;
168690f05028Syq usb_cvs_data_t *cvs;
168790f05028Syq int mgmt_cap = 0;
168890f05028Syq int master_if = -1, slave_if = -1;
168990f05028Syq usbsacm_port_t *acm_port = acmp->acm_ports;
169080727282SMike Zeller usb_dev_descr_t *dd;
169190f05028Syq
169290f05028Syq USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
169390f05028Syq "usbsacm_get_descriptors: ");
169490f05028Syq
169580727282SMike Zeller dd = acmp->acm_dev_data->dev_descr;
169690f05028Syq cfg = acmp->acm_dev_data->dev_curr_cfg;
169790f05028Syq /* set default control and data interface */
169890f05028Syq acm_port->acm_ctrl_if_no = acm_port->acm_data_if_no = 0;
169990f05028Syq
170090f05028Syq /* get current interfaces */
170190f05028Syq acm_port->acm_ctrl_if_no = acmp->acm_dev_data->dev_curr_if;
170290f05028Syq if (cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt == 0) {
170390f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
170490f05028Syq "usbsacm_get_descriptors: elements in if_alt is %d",
170590f05028Syq cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt);
170690f05028Syq
170790f05028Syq return (USB_FAILURE);
170890f05028Syq }
170990f05028Syq
171090f05028Syq altif = &cfg->cfg_if[acm_port->acm_ctrl_if_no].if_alt[0];
171190f05028Syq
171290f05028Syq /*
171390f05028Syq * Based on CDC specification, ACM devices usually include the
171490f05028Syq * following function descriptors: Header, ACM, Union and Call
171590f05028Syq * Management function descriptors. This loop search tree data
171690f05028Syq * structure for each acm class descriptor.
171790f05028Syq */
171890f05028Syq for (i = 0; i < altif->altif_n_cvs; i++) {
171990f05028Syq
172090f05028Syq cvs = &altif->altif_cvs[i];
172190f05028Syq
172290f05028Syq if ((cvs->cvs_buf == NULL) ||
172390f05028Syq (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
172490f05028Syq continue;
172590f05028Syq }
172690f05028Syq
172790f05028Syq switch (cvs->cvs_buf[2]) {
172890f05028Syq case USB_CDC_DESCR_TYPE_CALL_MANAGEMENT:
172990f05028Syq /* parse call management functional descriptor. */
173090f05028Syq if (cvs->cvs_buf_len >= 5) {
173190f05028Syq mgmt_cap = cvs->cvs_buf[3];
173290f05028Syq acm_port->acm_data_if_no = cvs->cvs_buf[4];
173390f05028Syq }
173490f05028Syq break;
173590f05028Syq case USB_CDC_DESCR_TYPE_ACM:
173690f05028Syq /* parse ACM functional descriptor. */
173790f05028Syq if (cvs->cvs_buf_len >= 4) {
173890f05028Syq acm_port->acm_cap = cvs->cvs_buf[3];
173990f05028Syq }
174080727282SMike Zeller
174180727282SMike Zeller /*
174280727282SMike Zeller * The Sigma Designs, Inc. USB device does not report
174380727282SMike Zeller * itself as implementing the full ACM spec. However,
174480727282SMike Zeller * it does function as a usb serial modem, so we opt to
174580727282SMike Zeller * scribble in the reported functionality if we
174680727282SMike Zeller * determine the USB device matches this vendor
174780727282SMike Zeller * and product ID.
174880727282SMike Zeller */
174980727282SMike Zeller if (dd->idVendor == USB_VENDOR_SIGMADESIGNS &&
175080727282SMike Zeller dd->idProduct == USB_PRODUCT_SIGMADESIGNS_ZW090) {
175180727282SMike Zeller acm_port->acm_cap |=
175280727282SMike Zeller USB_CDC_ACM_CAP_SERIAL_LINE;
175380727282SMike Zeller }
175490f05028Syq break;
175590f05028Syq case USB_CDC_DESCR_TYPE_UNION:
175690f05028Syq /* parse Union functional descriptor. */
175790f05028Syq if (cvs->cvs_buf_len >= 5) {
175890f05028Syq master_if = cvs->cvs_buf[3];
175990f05028Syq slave_if = cvs->cvs_buf[4];
176090f05028Syq }
176190f05028Syq break;
176290f05028Syq default:
176390f05028Syq break;
176490f05028Syq }
176590f05028Syq }
176690f05028Syq
176790f05028Syq /* For usb acm devices, it must satisfy the following options. */
176890f05028Syq if (cfg->cfg_n_if < 2) {
176990f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
177090f05028Syq "usbsacm_get_descriptors: # of interfaces %d < 2",
177190f05028Syq cfg->cfg_n_if);
177290f05028Syq
177390f05028Syq return (USB_FAILURE);
177490f05028Syq }
177590f05028Syq
177690f05028Syq if (acm_port->acm_data_if_no == 0 &&
17774ee52f77Slg slave_if != acm_port->acm_data_if_no) {
177890f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
177990f05028Syq "usbsacm_get_descriptors: Device hasn't call management "
178090f05028Syq "descriptor and use Union Descriptor.");
178190f05028Syq
178290f05028Syq acm_port->acm_data_if_no = slave_if;
178390f05028Syq }
178490f05028Syq
178590f05028Syq if ((master_if != acm_port->acm_ctrl_if_no) ||
178690f05028Syq (slave_if != acm_port->acm_data_if_no)) {
178790f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
178890f05028Syq "usbsacm_get_descriptors: control interface or "
178990f05028Syq "data interface don't match.");
179090f05028Syq
179190f05028Syq return (USB_FAILURE);
179290f05028Syq }
179390f05028Syq
179490f05028Syq /*
179590f05028Syq * We usually need both call and data capabilities, but
179690f05028Syq * some devices, such as Nokia mobile phones, don't provide
179790f05028Syq * call management descriptor, so we just give a warning
179890f05028Syq * message.
179990f05028Syq */
180090f05028Syq if (((mgmt_cap & USB_CDC_CALL_MGMT_CAP_CALL_MGMT) == 0) ||
180190f05028Syq ((mgmt_cap & USB_CDC_CALL_MGMT_CAP_DATA_INTERFACE) == 0)) {
180290f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
180390f05028Syq "usbsacm_get_descriptors: "
180490f05028Syq "insufficient mgmt capabilities %x",
180590f05028Syq mgmt_cap);
180690f05028Syq }
180790f05028Syq
180890f05028Syq if ((acm_port->acm_ctrl_if_no >= cfg->cfg_n_if) ||
180990f05028Syq (acm_port->acm_data_if_no >= cfg->cfg_n_if)) {
181090f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
181190f05028Syq "usbsacm_get_descriptors: control interface %d or "
181290f05028Syq "data interface %d out of range.",
181390f05028Syq acm_port->acm_ctrl_if_no, acm_port->acm_data_if_no);
181490f05028Syq
181590f05028Syq return (USB_FAILURE);
181690f05028Syq }
181790f05028Syq
181890f05028Syq /* control interface must have interrupt endpoint */
181990f05028Syq if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
182090f05028Syq acm_port->acm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
182190f05028Syq USB_EP_DIR_IN) == NULL) {
182290f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
182390f05028Syq "usbsacm_get_descriptors: "
182490f05028Syq "ctrl interface %d has no interrupt endpoint",
182590f05028Syq acm_port->acm_data_if_no);
182690f05028Syq
182790f05028Syq return (USB_FAILURE);
182890f05028Syq }
182990f05028Syq
183090f05028Syq /* data interface must have bulk in and out */
183190f05028Syq if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
183290f05028Syq acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
183390f05028Syq USB_EP_DIR_IN) == NULL) {
183490f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
183590f05028Syq "usbsacm_get_descriptors: "
183690f05028Syq "data interface %d has no bulk in endpoint",
183790f05028Syq acm_port->acm_data_if_no);
183890f05028Syq
183990f05028Syq return (USB_FAILURE);
184090f05028Syq }
184190f05028Syq if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
184290f05028Syq acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
184390f05028Syq USB_EP_DIR_OUT) == NULL) {
184490f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
184590f05028Syq "usbsacm_get_descriptors: "
184690f05028Syq "data interface %d has no bulk out endpoint",
184790f05028Syq acm_port->acm_data_if_no);
184890f05028Syq
184990f05028Syq return (USB_FAILURE);
185090f05028Syq }
185190f05028Syq
185290f05028Syq return (USB_SUCCESS);
185390f05028Syq }
185490f05028Syq
185590f05028Syq
185690f05028Syq /*
185790f05028Syq * usbsacm_cleanup:
185890f05028Syq * Release resources of current device during detach.
185990f05028Syq */
186090f05028Syq static void
usbsacm_cleanup(usbsacm_state_t * acmp)186190f05028Syq usbsacm_cleanup(usbsacm_state_t *acmp)
186290f05028Syq {
186390f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
186490f05028Syq "usbsacm_cleanup: ");
186590f05028Syq
186690f05028Syq if (acmp != NULL) {
186790f05028Syq /* free ports */
186890f05028Syq if (acmp->acm_ports != NULL) {
186990f05028Syq usbsacm_free_ports(acmp);
187090f05028Syq }
187190f05028Syq
187290f05028Syq /* unregister callback function */
187390f05028Syq if (acmp->acm_usb_events != NULL) {
187490f05028Syq usb_unregister_event_cbs(acmp->acm_dip,
18754ee52f77Slg acmp->acm_usb_events);
187690f05028Syq }
187790f05028Syq
187890f05028Syq /* destroy power management components */
187990f05028Syq if (acmp->acm_pm != NULL) {
188090f05028Syq usbsacm_destroy_pm_components(acmp);
188190f05028Syq }
188290f05028Syq
188390f05028Syq /* free description of device tree. */
188490f05028Syq if (acmp->acm_def_ph != NULL) {
188590f05028Syq mutex_destroy(&acmp->acm_mutex);
188690f05028Syq
188790f05028Syq usb_free_descr_tree(acmp->acm_dip, acmp->acm_dev_data);
188890f05028Syq acmp->acm_def_ph = NULL;
188990f05028Syq }
189090f05028Syq
189190f05028Syq if (acmp->acm_lh != NULL) {
189290f05028Syq usb_free_log_hdl(acmp->acm_lh);
189390f05028Syq acmp->acm_lh = NULL;
189490f05028Syq }
189590f05028Syq
189690f05028Syq /* detach client device */
189790f05028Syq if (acmp->acm_dev_data != NULL) {
189890f05028Syq usb_client_detach(acmp->acm_dip, acmp->acm_dev_data);
189990f05028Syq }
190090f05028Syq
190190f05028Syq kmem_free((caddr_t)acmp, sizeof (usbsacm_state_t));
190290f05028Syq }
190390f05028Syq }
190490f05028Syq
190590f05028Syq
190690f05028Syq /*
190790f05028Syq * usbsacm_restore_device_state:
190890f05028Syq * restore device state after CPR resume or reconnect
190990f05028Syq */
191090f05028Syq static int
usbsacm_restore_device_state(usbsacm_state_t * acmp)191190f05028Syq usbsacm_restore_device_state(usbsacm_state_t *acmp)
191290f05028Syq {
191390f05028Syq int state;
191490f05028Syq
191590f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
191690f05028Syq "usbsacm_restore_device_state: ");
191790f05028Syq
191890f05028Syq mutex_enter(&acmp->acm_mutex);
191990f05028Syq state = acmp->acm_dev_state;
192090f05028Syq mutex_exit(&acmp->acm_mutex);
192190f05028Syq
192290f05028Syq /* Check device status */
192390f05028Syq if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
192490f05028Syq
192590f05028Syq return (state);
192690f05028Syq }
192790f05028Syq
192890f05028Syq /* Check if we are talking to the same device */
192990f05028Syq if (usb_check_same_device(acmp->acm_dip, acmp->acm_lh, USB_LOG_L0,
193090f05028Syq -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
193190f05028Syq mutex_enter(&acmp->acm_mutex);
193290f05028Syq state = acmp->acm_dev_state = USB_DEV_DISCONNECTED;
193390f05028Syq mutex_exit(&acmp->acm_mutex);
193490f05028Syq
193590f05028Syq return (state);
193690f05028Syq }
193790f05028Syq
193890f05028Syq if (state == USB_DEV_DISCONNECTED) {
193990f05028Syq USB_DPRINTF_L1(PRINT_MASK_ALL, acmp->acm_lh,
194090f05028Syq "usbsacm_restore_device_state: Device has been reconnected "
194190f05028Syq "but data may have been lost");
194290f05028Syq }
194390f05028Syq
194490f05028Syq /* reconnect pipes */
194590f05028Syq if (usbsacm_reconnect_pipes(acmp) != USB_SUCCESS) {
194690f05028Syq
194790f05028Syq return (state);
194890f05028Syq }
194990f05028Syq
195090f05028Syq /*
195190f05028Syq * init device state
195290f05028Syq */
195390f05028Syq mutex_enter(&acmp->acm_mutex);
195490f05028Syq state = acmp->acm_dev_state = USB_DEV_ONLINE;
195590f05028Syq mutex_exit(&acmp->acm_mutex);
195690f05028Syq
195790f05028Syq if ((usbsacm_restore_port_state(acmp) != USB_SUCCESS)) {
195890f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
195990f05028Syq "usbsacm_restore_device_state: failed");
196090f05028Syq }
196190f05028Syq
196290f05028Syq return (state);
196390f05028Syq }
196490f05028Syq
196590f05028Syq
196690f05028Syq /*
196790f05028Syq * usbsacm_restore_port_state:
196890f05028Syq * restore ports state after CPR resume or reconnect
196990f05028Syq */
197090f05028Syq static int
usbsacm_restore_port_state(usbsacm_state_t * acmp)197190f05028Syq usbsacm_restore_port_state(usbsacm_state_t *acmp)
197290f05028Syq {
197390f05028Syq int i, ret = USB_SUCCESS;
197490f05028Syq usbsacm_port_t *cur_port;
197590f05028Syq
197690f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
197790f05028Syq "usbsacm_restore_port_state: ");
197890f05028Syq
197990f05028Syq /* restore status of all ports */
198090f05028Syq for (i = 0; i < acmp->acm_port_cnt; i++) {
198190f05028Syq cur_port = &acmp->acm_ports[i];
198290f05028Syq mutex_enter(&cur_port->acm_port_mutex);
198390f05028Syq if (cur_port->acm_port_state != USBSACM_PORT_OPEN) {
198490f05028Syq mutex_exit(&cur_port->acm_port_mutex);
198590f05028Syq
198690f05028Syq continue;
198790f05028Syq }
198890f05028Syq mutex_exit(&cur_port->acm_port_mutex);
198990f05028Syq
199090f05028Syq if ((ret = usbsacm_set_line_coding(cur_port,
199190f05028Syq &cur_port->acm_line_coding)) != USB_SUCCESS) {
199290f05028Syq USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
199390f05028Syq "usbsacm_restore_port_state: failed.");
199490f05028Syq }
199590f05028Syq }
199690f05028Syq
199790f05028Syq return (ret);
199890f05028Syq }
199990f05028Syq
200090f05028Syq
200190f05028Syq /*
200290f05028Syq * pipe management
200390f05028Syq * ---------------
200490f05028Syq *
200590f05028Syq *
200690f05028Syq * usbsacm_open_port_pipes:
200790f05028Syq * Open pipes of one port and set port structure;
200890f05028Syq * Each port includes three pipes: bulk in, bulk out and interrupt.
200990f05028Syq */
201090f05028Syq static int
usbsacm_open_port_pipes(usbsacm_port_t * acm_port)201190f05028Syq usbsacm_open_port_pipes(usbsacm_port_t *acm_port)
201290f05028Syq {
201390f05028Syq int rval = USB_SUCCESS;
201490f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
201590f05028Syq usb_ep_data_t *in_data, *out_data, *intr_pipe;
201690f05028Syq usb_pipe_policy_t policy;
201790f05028Syq
201890f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
2019112116d8Sfb "usbsacm_open_port_pipes: acmp = 0x%p", (void *)acmp);
202090f05028Syq
202190f05028Syq /* Get bulk and interrupt endpoint data */
202290f05028Syq intr_pipe = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
202390f05028Syq acm_port->acm_ctrl_if_no, 0, 0,
202490f05028Syq USB_EP_ATTR_INTR, USB_EP_DIR_IN);
202590f05028Syq in_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
202690f05028Syq acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
202790f05028Syq USB_EP_ATTR_BULK, USB_EP_DIR_IN);
202890f05028Syq out_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
202990f05028Syq acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
203090f05028Syq USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
203190f05028Syq
203290f05028Syq /* Bulk in and out must exist meanwhile. */
203390f05028Syq if ((in_data == NULL) || (out_data == NULL)) {
203490f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
203590f05028Syq "usbsacm_open_port_pipes: look up bulk pipe failed in "
203690f05028Syq "interface %d port %d",
203790f05028Syq acm_port->acm_data_if_no, acm_port->acm_data_port_no);
203890f05028Syq
203990f05028Syq return (USB_FAILURE);
204090f05028Syq }
204190f05028Syq
204290f05028Syq /*
204390f05028Syq * If device conform to acm spec, it must have an interrupt pipe
204490f05028Syq * for this port.
204590f05028Syq */
204690f05028Syq if (acmp->acm_compatibility == B_TRUE && intr_pipe == NULL) {
204790f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
204890f05028Syq "usbsacm_open_port_pipes: look up interrupt pipe failed in "
204990f05028Syq "interface %d", acm_port->acm_ctrl_if_no);
205090f05028Syq
205190f05028Syq return (USB_FAILURE);
205290f05028Syq }
205390f05028Syq
205490f05028Syq policy.pp_max_async_reqs = 2;
205590f05028Syq
205690f05028Syq /* Open bulk in endpoint */
205790f05028Syq if (usb_pipe_open(acmp->acm_dip, &in_data->ep_descr, &policy,
205890f05028Syq USB_FLAGS_SLEEP, &acm_port->acm_bulkin_ph) != USB_SUCCESS) {
205990f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
206090f05028Syq "usbsacm_open_port_pipes: open bulkin pipe failed!");
206190f05028Syq
206290f05028Syq return (USB_FAILURE);
206390f05028Syq }
206490f05028Syq
206590f05028Syq /* Open bulk out endpoint */
206690f05028Syq if (usb_pipe_open(acmp->acm_dip, &out_data->ep_descr, &policy,
206790f05028Syq USB_FLAGS_SLEEP, &acm_port->acm_bulkout_ph) != USB_SUCCESS) {
206890f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
206990f05028Syq "usbsacm_open_port_pipes: open bulkout pipe failed!");
207090f05028Syq
207190f05028Syq usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
207290f05028Syq USB_FLAGS_SLEEP, NULL, NULL);
207390f05028Syq
207490f05028Syq return (USB_FAILURE);
207590f05028Syq }
207690f05028Syq
207790f05028Syq /* Open interrupt endpoint if found. */
207890f05028Syq if (intr_pipe != NULL) {
207990f05028Syq
208090f05028Syq if (usb_pipe_open(acmp->acm_dip, &intr_pipe->ep_descr, &policy,
208190f05028Syq USB_FLAGS_SLEEP, &acm_port->acm_intr_ph) != USB_SUCCESS) {
208290f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
208390f05028Syq "usbsacm_open_port_pipes: "
208490f05028Syq "open control pipe failed");
208590f05028Syq
208690f05028Syq usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
208790f05028Syq USB_FLAGS_SLEEP, NULL, NULL);
208890f05028Syq usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
208990f05028Syq USB_FLAGS_SLEEP, NULL, NULL);
209090f05028Syq
209190f05028Syq return (USB_FAILURE);
209290f05028Syq }
209390f05028Syq }
209490f05028Syq
209590f05028Syq /* initialize the port structure. */
209690f05028Syq mutex_enter(&acm_port->acm_port_mutex);
209790f05028Syq acm_port->acm_bulkin_size = in_data->ep_descr.wMaxPacketSize;
209890f05028Syq acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
209990f05028Syq acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
210090f05028Syq if (acm_port->acm_intr_ph != NULL) {
210190f05028Syq acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
210290f05028Syq acm_port->acm_intr_ep_descr = intr_pipe->ep_descr;
210390f05028Syq }
210490f05028Syq mutex_exit(&acm_port->acm_port_mutex);
210590f05028Syq
210690f05028Syq if (acm_port->acm_intr_ph != NULL) {
210790f05028Syq
210890f05028Syq usbsacm_pipe_start_polling(acm_port);
210990f05028Syq }
211090f05028Syq
211190f05028Syq return (rval);
211290f05028Syq }
211390f05028Syq
211490f05028Syq
211590f05028Syq /*
211690f05028Syq * usbsacm_close_port_pipes:
211790f05028Syq * Close pipes of one port and reset port structure to closed;
211890f05028Syq * Each port includes three pipes: bulk in, bulk out and interrupt.
211990f05028Syq */
212090f05028Syq static void
usbsacm_close_port_pipes(usbsacm_port_t * acm_port)212190f05028Syq usbsacm_close_port_pipes(usbsacm_port_t *acm_port)
212290f05028Syq {
212390f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
212490f05028Syq
212590f05028Syq mutex_enter(&acm_port->acm_port_mutex);
212690f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
212790f05028Syq "usbsacm_close_port_pipes: acm_bulkin_state = %d",
212890f05028Syq acm_port->acm_bulkin_state);
212990f05028Syq
213090f05028Syq /*
213190f05028Syq * Check the status of the given port. If port is closing or closed,
213290f05028Syq * return directly.
213390f05028Syq */
213490f05028Syq if ((acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSED) ||
213590f05028Syq (acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSING)) {
213690f05028Syq USB_DPRINTF_L2(PRINT_MASK_CLOSE, acmp->acm_lh,
213790f05028Syq "usbsacm_close_port_pipes: port is closing or has closed");
213890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
213990f05028Syq
214090f05028Syq return;
214190f05028Syq }
214290f05028Syq
214390f05028Syq acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSING;
214490f05028Syq mutex_exit(&acm_port->acm_port_mutex);
214590f05028Syq
214690f05028Syq /* Close pipes */
214790f05028Syq usb_pipe_reset(acmp->acm_dip, acm_port->acm_bulkin_ph,
214890f05028Syq USB_FLAGS_SLEEP, 0, 0);
214990f05028Syq usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
215090f05028Syq USB_FLAGS_SLEEP, 0, 0);
215190f05028Syq usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
215290f05028Syq USB_FLAGS_SLEEP, 0, 0);
215390f05028Syq if (acm_port->acm_intr_ph != NULL) {
215490f05028Syq usb_pipe_stop_intr_polling(acm_port->acm_intr_ph,
215590f05028Syq USB_FLAGS_SLEEP);
215690f05028Syq usb_pipe_close(acmp->acm_dip, acm_port->acm_intr_ph,
215790f05028Syq USB_FLAGS_SLEEP, 0, 0);
215890f05028Syq }
215990f05028Syq
216090f05028Syq mutex_enter(&acm_port->acm_port_mutex);
216190f05028Syq /* Reset the status of pipes to closed */
216290f05028Syq acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSED;
216390f05028Syq acm_port->acm_bulkin_ph = NULL;
216490f05028Syq acm_port->acm_bulkout_state = USBSACM_PIPE_CLOSED;
216590f05028Syq acm_port->acm_bulkout_ph = NULL;
216690f05028Syq if (acm_port->acm_intr_ph != NULL) {
216790f05028Syq acm_port->acm_intr_state = USBSACM_PIPE_CLOSED;
216890f05028Syq acm_port->acm_intr_ph = NULL;
216990f05028Syq }
217090f05028Syq
217190f05028Syq mutex_exit(&acm_port->acm_port_mutex);
217290f05028Syq
217390f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
217490f05028Syq "usbsacm_close_port_pipes: port has been closed.");
217590f05028Syq }
217690f05028Syq
217790f05028Syq
217890f05028Syq /*
217990f05028Syq * usbsacm_close_pipes:
218090f05028Syq * close all opened pipes of current devices.
218190f05028Syq */
218290f05028Syq static void
usbsacm_close_pipes(usbsacm_state_t * acmp)218390f05028Syq usbsacm_close_pipes(usbsacm_state_t *acmp)
218490f05028Syq {
218590f05028Syq int i;
218690f05028Syq
218790f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
218890f05028Syq "usbsacm_close_pipes: ");
218990f05028Syq
219090f05028Syq /* Close all ports */
219190f05028Syq for (i = 0; i < acmp->acm_port_cnt; i++) {
219290f05028Syq usbsacm_close_port_pipes(&acmp->acm_ports[i]);
219390f05028Syq }
219490f05028Syq }
219590f05028Syq
219690f05028Syq
219790f05028Syq /*
219890f05028Syq * usbsacm_disconnect_pipes:
219990f05028Syq * this function just call usbsacm_close_pipes.
220090f05028Syq */
220190f05028Syq static void
usbsacm_disconnect_pipes(usbsacm_state_t * acmp)220290f05028Syq usbsacm_disconnect_pipes(usbsacm_state_t *acmp)
220390f05028Syq {
220490f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
220590f05028Syq "usbsacm_disconnect_pipes: ");
220690f05028Syq
220790f05028Syq usbsacm_close_pipes(acmp);
220890f05028Syq }
220990f05028Syq
221090f05028Syq
221190f05028Syq /*
221290f05028Syq * usbsacm_reconnect_pipes:
221390f05028Syq * reconnect pipes in CPR resume or reconnect
221490f05028Syq */
221590f05028Syq static int
usbsacm_reconnect_pipes(usbsacm_state_t * acmp)221690f05028Syq usbsacm_reconnect_pipes(usbsacm_state_t *acmp)
221790f05028Syq {
221890f05028Syq usbsacm_port_t *cur_port = acmp->acm_ports;
221990f05028Syq int i;
222090f05028Syq
222190f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
222290f05028Syq "usbsacm_reconnect_pipes: ");
222390f05028Syq
222490f05028Syq /* reopen all ports of current device. */
222590f05028Syq for (i = 0; i < acmp->acm_port_cnt; i++) {
222690f05028Syq cur_port = &acmp->acm_ports[i];
222790f05028Syq
222890f05028Syq mutex_enter(&cur_port->acm_port_mutex);
222990f05028Syq /*
223090f05028Syq * If port status is open, reopen it;
223190f05028Syq * else retain the current status.
223290f05028Syq */
223390f05028Syq if (cur_port->acm_port_state == USBSACM_PORT_OPEN) {
223490f05028Syq
223590f05028Syq mutex_exit(&cur_port->acm_port_mutex);
223690f05028Syq if (usbsacm_open_port_pipes(cur_port) != USB_SUCCESS) {
223790f05028Syq USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
223890f05028Syq "usbsacm_reconnect_pipes: "
223990f05028Syq "open port %d failed.", i);
224090f05028Syq
224190f05028Syq return (USB_FAILURE);
224290f05028Syq }
224390f05028Syq mutex_enter(&cur_port->acm_port_mutex);
224490f05028Syq }
224590f05028Syq mutex_exit(&cur_port->acm_port_mutex);
224690f05028Syq }
224790f05028Syq
224890f05028Syq return (USB_SUCCESS);
224990f05028Syq }
225090f05028Syq
225190f05028Syq /*
225290f05028Syq * usbsacm_bulkin_cb:
225390f05028Syq * Bulk In regular and exeception callback;
225490f05028Syq * USBA framework will call this callback
225590f05028Syq * after deal with bulkin request.
225690f05028Syq */
225790f05028Syq /*ARGSUSED*/
225890f05028Syq static void
usbsacm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)225990f05028Syq usbsacm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
226090f05028Syq {
226190f05028Syq usbsacm_port_t *acm_port = (usbsacm_port_t *)req->bulk_client_private;
226290f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
226390f05028Syq mblk_t *data;
226490f05028Syq int data_len;
226590f05028Syq
226690f05028Syq data = req->bulk_data;
226790f05028Syq data_len = (data) ? MBLKL(data) : 0;
226890f05028Syq
226990f05028Syq mutex_enter(&acm_port->acm_port_mutex);
227090f05028Syq USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
227190f05028Syq "usbsacm_bulkin_cb: "
227290f05028Syq "acm_bulkin_state = %d acm_port_state = %d data_len = %d",
227390f05028Syq acm_port->acm_bulkin_state, acm_port->acm_port_state, data_len);
227490f05028Syq
227590f05028Syq if ((acm_port->acm_port_state == USBSACM_PORT_OPEN) && (data_len) &&
227690f05028Syq (req->bulk_completion_reason == USB_CR_OK)) {
227790f05028Syq mutex_exit(&acm_port->acm_port_mutex);
227890f05028Syq /* prevent USBA from freeing data along with the request */
227990f05028Syq req->bulk_data = NULL;
228090f05028Syq
228190f05028Syq /* save data on the receive list */
228290f05028Syq usbsacm_put_tail(&acm_port->acm_rx_mp, data);
228390f05028Syq
228490f05028Syq /* invoke GSD receive callback */
228590f05028Syq if (acm_port->acm_cb.cb_rx) {
228690f05028Syq acm_port->acm_cb.cb_rx(acm_port->acm_cb.cb_arg);
228790f05028Syq }
228890f05028Syq mutex_enter(&acm_port->acm_port_mutex);
228990f05028Syq }
229090f05028Syq mutex_exit(&acm_port->acm_port_mutex);
229190f05028Syq
229290f05028Syq usb_free_bulk_req(req);
229390f05028Syq
229490f05028Syq /* receive more */
229590f05028Syq mutex_enter(&acm_port->acm_port_mutex);
229690f05028Syq if (((acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) ||
229790f05028Syq (acm_port->acm_bulkin_state == USBSACM_PIPE_IDLE)) &&
229890f05028Syq (acm_port->acm_port_state == USBSACM_PORT_OPEN) &&
229990f05028Syq (acmp->acm_dev_state == USB_DEV_ONLINE)) {
230090f05028Syq if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
230190f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
230290f05028Syq "usbsacm_bulkin_cb: restart rx fail "
230390f05028Syq "acm_port_state = %d", acm_port->acm_port_state);
230490f05028Syq }
230590f05028Syq } else if (acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) {
230690f05028Syq acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
230790f05028Syq }
230890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
230990f05028Syq }
231090f05028Syq
231190f05028Syq
231290f05028Syq /*
231390f05028Syq * usbsacm_bulkout_cb:
231490f05028Syq * Bulk Out regular and exeception callback;
231590f05028Syq * USBA framework will call this callback function
231690f05028Syq * after deal with bulkout request.
231790f05028Syq */
231890f05028Syq /*ARGSUSED*/
231990f05028Syq static void
usbsacm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)232090f05028Syq usbsacm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
232190f05028Syq {
232290f05028Syq usbsacm_port_t *acm_port = (usbsacm_port_t *)req->bulk_client_private;
232390f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
232490f05028Syq int data_len;
232590f05028Syq mblk_t *data = req->bulk_data;
232690f05028Syq
232790f05028Syq USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
2328112116d8Sfb "usbsacm_bulkout_cb: acmp = 0x%p", (void *)acmp);
232990f05028Syq
233090f05028Syq data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
233190f05028Syq
233290f05028Syq /* put untransferred residue back on the transfer list */
233390f05028Syq if (req->bulk_completion_reason && (data_len > 0)) {
233490f05028Syq usbsacm_put_head(&acm_port->acm_tx_mp, data);
233590f05028Syq req->bulk_data = NULL;
233690f05028Syq }
233790f05028Syq
233890f05028Syq usb_free_bulk_req(req);
233990f05028Syq
234090f05028Syq /* invoke GSD transmit callback */
234190f05028Syq if (acm_port->acm_cb.cb_tx) {
234290f05028Syq acm_port->acm_cb.cb_tx(acm_port->acm_cb.cb_arg);
234390f05028Syq }
234490f05028Syq
234590f05028Syq /* send more */
234690f05028Syq mutex_enter(&acm_port->acm_port_mutex);
234790f05028Syq acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
234890f05028Syq if (acm_port->acm_tx_mp == NULL) {
234990f05028Syq cv_broadcast(&acm_port->acm_tx_cv);
235090f05028Syq } else {
235190f05028Syq usbsacm_tx_start(acm_port);
235290f05028Syq }
235390f05028Syq mutex_exit(&acm_port->acm_port_mutex);
235490f05028Syq }
235590f05028Syq
235690f05028Syq
235790f05028Syq /*
235890f05028Syq * usbsacm_rx_start:
235990f05028Syq * start data receipt
236090f05028Syq */
236190f05028Syq static int
usbsacm_rx_start(usbsacm_port_t * acm_port)236290f05028Syq usbsacm_rx_start(usbsacm_port_t *acm_port)
236390f05028Syq {
236490f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
236590f05028Syq usb_bulk_req_t *br;
236690f05028Syq int rval = USB_FAILURE;
236790f05028Syq int data_len;
236890f05028Syq
236990f05028Syq USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
2370112116d8Sfb "usbsacm_rx_start: acm_xfer_sz = 0x%lx acm_bulkin_size = 0x%lx",
237190f05028Syq acmp->acm_xfer_sz, acm_port->acm_bulkin_size);
237290f05028Syq
237390f05028Syq acm_port->acm_bulkin_state = USBSACM_PIPE_BUSY;
237490f05028Syq /*
237590f05028Syq * Qualcomm CDMA card won't response the first request,
237690f05028Syq * if the following code don't multiply by 2.
237790f05028Syq */
237890f05028Syq data_len = min(acmp->acm_xfer_sz, acm_port->acm_bulkin_size * 2);
237990f05028Syq mutex_exit(&acm_port->acm_port_mutex);
238090f05028Syq
238190f05028Syq br = usb_alloc_bulk_req(acmp->acm_dip, data_len, USB_FLAGS_SLEEP);
238290f05028Syq if (br == NULL) {
238390f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
238490f05028Syq "usbsacm_rx_start: allocate bulk request failed");
238590f05028Syq
238690f05028Syq mutex_enter(&acm_port->acm_port_mutex);
238790f05028Syq
238890f05028Syq return (USB_FAILURE);
238990f05028Syq }
239090f05028Syq /* initialize bulk in request. */
239190f05028Syq br->bulk_len = data_len;
239290f05028Syq br->bulk_timeout = USBSACM_BULKIN_TIMEOUT;
239390f05028Syq br->bulk_cb = usbsacm_bulkin_cb;
239490f05028Syq br->bulk_exc_cb = usbsacm_bulkin_cb;
239590f05028Syq br->bulk_client_private = (usb_opaque_t)acm_port;
239690f05028Syq br->bulk_attributes = USB_ATTRS_AUTOCLEARING
239790f05028Syq | USB_ATTRS_SHORT_XFER_OK;
239890f05028Syq
239990f05028Syq rval = usb_pipe_bulk_xfer(acm_port->acm_bulkin_ph, br, 0);
240090f05028Syq
240190f05028Syq mutex_enter(&acm_port->acm_port_mutex);
240290f05028Syq if (rval != USB_SUCCESS) {
240390f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
240490f05028Syq "usbsacm_rx_start: bulk transfer failed %d", rval);
240590f05028Syq usb_free_bulk_req(br);
240690f05028Syq acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
240790f05028Syq }
240890f05028Syq
240990f05028Syq return (rval);
241090f05028Syq }
241190f05028Syq
241290f05028Syq
241390f05028Syq /*
241490f05028Syq * usbsacm_tx_start:
241590f05028Syq * start data transmit
241690f05028Syq */
241790f05028Syq static void
usbsacm_tx_start(usbsacm_port_t * acm_port)241890f05028Syq usbsacm_tx_start(usbsacm_port_t *acm_port)
241990f05028Syq {
242090f05028Syq int len; /* bytes we can transmit */
242190f05028Syq mblk_t *data; /* data to be transmitted */
242290f05028Syq int data_len; /* bytes in 'data' */
242390f05028Syq mblk_t *mp; /* current msgblk */
242490f05028Syq int copylen; /* bytes copy from 'mp' to 'data' */
242590f05028Syq int rval;
242690f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
242790f05028Syq
242890f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
242990f05028Syq "usbsacm_tx_start: ");
243090f05028Syq
243190f05028Syq /* check the transmitted data. */
243290f05028Syq if (acm_port->acm_tx_mp == NULL) {
243390f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
243490f05028Syq "usbsacm_tx_start: acm_tx_mp is NULL");
243590f05028Syq
243690f05028Syq return;
243790f05028Syq }
243890f05028Syq
243990f05028Syq /* check pipe status */
244090f05028Syq if (acm_port->acm_bulkout_state != USBSACM_PIPE_IDLE) {
244190f05028Syq
244290f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
244390f05028Syq "usbsacm_tx_start: error state in bulkout endpoint");
244490f05028Syq
244590f05028Syq return;
244690f05028Syq }
244790f05028Syq ASSERT(MBLKL(acm_port->acm_tx_mp) > 0);
244890f05028Syq
244990f05028Syq /* send as much data as port can receive */
245090f05028Syq len = min(msgdsize(acm_port->acm_tx_mp), acmp->acm_xfer_sz);
245190f05028Syq
245290f05028Syq if (len == 0) {
245390f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
245490f05028Syq "usbsacm_tx_start: data len is 0");
245590f05028Syq
245690f05028Syq return;
245790f05028Syq }
245890f05028Syq
245990f05028Syq /* allocate memory for sending data. */
246090f05028Syq if ((data = allocb(len, BPRI_LO)) == NULL) {
246190f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
246290f05028Syq "usbsacm_tx_start: failure in allocate memory");
246390f05028Syq
246490f05028Syq return;
246590f05028Syq }
246690f05028Syq
246790f05028Syq /*
246890f05028Syq * copy no more than 'len' bytes from mblk chain to transmit mblk 'data'
246990f05028Syq */
247090f05028Syq data_len = 0;
247190f05028Syq while ((data_len < len) && acm_port->acm_tx_mp) {
247290f05028Syq /* Get the first mblk from chain. */
247390f05028Syq mp = acm_port->acm_tx_mp;
247490f05028Syq copylen = min(MBLKL(mp), len - data_len);
247590f05028Syq bcopy(mp->b_rptr, data->b_wptr, copylen);
247690f05028Syq mp->b_rptr += copylen;
247790f05028Syq data->b_wptr += copylen;
247890f05028Syq data_len += copylen;
247990f05028Syq
248022eb7cb5Sgd if (MBLKL(mp) < 1) {
248190f05028Syq acm_port->acm_tx_mp = unlinkb(mp);
248290f05028Syq freeb(mp);
248390f05028Syq } else {
248490f05028Syq ASSERT(data_len == len);
248590f05028Syq }
248690f05028Syq }
248790f05028Syq
248890f05028Syq if (data_len <= 0) {
248990f05028Syq freeb(data);
249090f05028Syq
249190f05028Syq return;
249290f05028Syq }
249390f05028Syq
249490f05028Syq acm_port->acm_bulkout_state = USBSACM_PIPE_BUSY;
249590f05028Syq
249690f05028Syq mutex_exit(&acm_port->acm_port_mutex);
249790f05028Syq /* send request. */
249890f05028Syq rval = usbsacm_send_data(acm_port, data);
249990f05028Syq mutex_enter(&acm_port->acm_port_mutex);
250090f05028Syq
250190f05028Syq /*
250290f05028Syq * If send failed, retransmit data when acm_tx_mp is null.
250390f05028Syq */
250490f05028Syq if (rval != USB_SUCCESS) {
250590f05028Syq acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
250690f05028Syq if (acm_port->acm_tx_mp == NULL) {
250790f05028Syq usbsacm_put_head(&acm_port->acm_tx_mp, data);
250890f05028Syq }
250990f05028Syq }
251090f05028Syq }
251190f05028Syq
251290f05028Syq
251390f05028Syq /*
251490f05028Syq * usbsacm_send_data:
251590f05028Syq * data transfer
251690f05028Syq */
251790f05028Syq static int
usbsacm_send_data(usbsacm_port_t * acm_port,mblk_t * data)251890f05028Syq usbsacm_send_data(usbsacm_port_t *acm_port, mblk_t *data)
251990f05028Syq {
252090f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
252190f05028Syq usb_bulk_req_t *br;
252290f05028Syq int rval;
252390f05028Syq int data_len = MBLKL(data);
252490f05028Syq
252590f05028Syq USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
252690f05028Syq "usbsacm_send_data: data address is 0x%p, length = %d",
2527112116d8Sfb (void *)data, data_len);
252890f05028Syq
252990f05028Syq br = usb_alloc_bulk_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
253090f05028Syq if (br == NULL) {
253190f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
253290f05028Syq "usbsacm_send_data: alloc req failed.");
253390f05028Syq
253490f05028Syq return (USB_FAILURE);
253590f05028Syq }
253690f05028Syq
253790f05028Syq /* initialize the bulk out request */
253890f05028Syq br->bulk_data = data;
253990f05028Syq br->bulk_len = data_len;
254090f05028Syq br->bulk_timeout = USBSACM_BULKOUT_TIMEOUT;
254190f05028Syq br->bulk_cb = usbsacm_bulkout_cb;
254290f05028Syq br->bulk_exc_cb = usbsacm_bulkout_cb;
254390f05028Syq br->bulk_client_private = (usb_opaque_t)acm_port;
254490f05028Syq br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
254590f05028Syq
254690f05028Syq rval = usb_pipe_bulk_xfer(acm_port->acm_bulkout_ph, br, 0);
254790f05028Syq
254890f05028Syq if (rval != USB_SUCCESS) {
254990f05028Syq USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
255090f05028Syq "usbsacm_send_data: Send Data failed.");
255190f05028Syq
255290f05028Syq /*
255390f05028Syq * Don't free it in usb_free_bulk_req because it will
255490f05028Syq * be linked in usbsacm_put_head
255590f05028Syq */
255690f05028Syq br->bulk_data = NULL;
255790f05028Syq
255890f05028Syq usb_free_bulk_req(br);
255990f05028Syq }
256090f05028Syq
256190f05028Syq return (rval);
256290f05028Syq }
256390f05028Syq
256490f05028Syq /*
256590f05028Syq * usbsacm_wait_tx_drain:
256690f05028Syq * wait until local tx buffer drains.
256790f05028Syq * 'timeout' is in seconds, zero means wait forever
256890f05028Syq */
256990f05028Syq static int
usbsacm_wait_tx_drain(usbsacm_port_t * acm_port,int timeout)257090f05028Syq usbsacm_wait_tx_drain(usbsacm_port_t *acm_port, int timeout)
257190f05028Syq {
257290f05028Syq clock_t until;
257390f05028Syq int over = 0;
257490f05028Syq
257590f05028Syq until = ddi_get_lbolt() + drv_usectohz(1000 * 1000 * timeout);
257690f05028Syq
257790f05028Syq while (acm_port->acm_tx_mp && !over) {
257890f05028Syq if (timeout > 0) {
257990f05028Syq over = (cv_timedwait_sig(&acm_port->acm_tx_cv,
258090f05028Syq &acm_port->acm_port_mutex, until) <= 0);
258190f05028Syq } else {
258290f05028Syq over = (cv_wait_sig(&acm_port->acm_tx_cv,
258390f05028Syq &acm_port->acm_port_mutex) == 0);
258490f05028Syq }
258590f05028Syq }
258690f05028Syq
258790f05028Syq return ((acm_port->acm_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE);
258890f05028Syq }
258990f05028Syq
259090f05028Syq
259190f05028Syq /*
259290f05028Syq * usbsacm_req_write:
259390f05028Syq * send command over control pipe
259490f05028Syq */
259590f05028Syq static int
usbsacm_req_write(usbsacm_port_t * acm_port,uchar_t request,uint16_t value,mblk_t ** data)259690f05028Syq usbsacm_req_write(usbsacm_port_t *acm_port, uchar_t request, uint16_t value,
259790f05028Syq mblk_t **data)
259890f05028Syq {
259990f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
260090f05028Syq usb_ctrl_setup_t setup;
260190f05028Syq usb_cb_flags_t cb_flags;
260290f05028Syq usb_cr_t cr;
260390f05028Syq
260490f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
260590f05028Syq "usbsacm_req_write: ");
260690f05028Syq
260790f05028Syq /* initialize the control request. */
260890f05028Syq setup.bmRequestType = USBSACM_REQ_WRITE_IF;
260990f05028Syq setup.bRequest = request;
261090f05028Syq setup.wValue = value;
261190f05028Syq setup.wIndex = acm_port->acm_ctrl_if_no;
261290f05028Syq setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
261390f05028Syq setup.attrs = 0;
261490f05028Syq
261590f05028Syq return (usb_pipe_ctrl_xfer_wait(acmp->acm_def_ph, &setup, data,
261690f05028Syq &cr, &cb_flags, 0));
261790f05028Syq }
261890f05028Syq
261990f05028Syq
262090f05028Syq /*
262190f05028Syq * usbsacm_set_line_coding:
262290f05028Syq * Send USB_CDC_REQ_SET_LINE_CODING request
262390f05028Syq */
262490f05028Syq static int
usbsacm_set_line_coding(usbsacm_port_t * acm_port,usb_cdc_line_coding_t * lc)262590f05028Syq usbsacm_set_line_coding(usbsacm_port_t *acm_port, usb_cdc_line_coding_t *lc)
262690f05028Syq {
262790f05028Syq mblk_t *bp;
262890f05028Syq int ret;
262990f05028Syq
263090f05028Syq /* allocate mblk and copy supplied structure into it */
263190f05028Syq if ((bp = allocb(USB_CDC_LINE_CODING_LEN, BPRI_HI)) == NULL) {
263290f05028Syq
263390f05028Syq return (USB_NO_RESOURCES);
263490f05028Syq }
263590f05028Syq
263690f05028Syq #ifndef __lock_lint /* warlock gets confused here */
2637d29f5a71Szhigang lu - Sun Microsystems - Beijing China /* LINTED E_BAD_PTR_CAST_ALIGN */
263890f05028Syq *((usb_cdc_line_coding_t *)bp->b_wptr) = *lc;
263990f05028Syq bp->b_wptr += USB_CDC_LINE_CODING_LEN;
264090f05028Syq #endif
264190f05028Syq
264290f05028Syq ret = usbsacm_req_write(acm_port, USB_CDC_REQ_SET_LINE_CODING, 0, &bp);
264390f05028Syq
264490f05028Syq if (bp != NULL) {
264590f05028Syq freeb(bp);
264690f05028Syq }
264790f05028Syq
264890f05028Syq return (ret);
264990f05028Syq }
265090f05028Syq
265190f05028Syq
265290f05028Syq
265390f05028Syq /*
265490f05028Syq * usbsacm_mctl2reg:
265590f05028Syq * Set Modem control status
265690f05028Syq */
265790f05028Syq static void
usbsacm_mctl2reg(int mask,int val,uint8_t * line_ctl)265890f05028Syq usbsacm_mctl2reg(int mask, int val, uint8_t *line_ctl)
265990f05028Syq {
266090f05028Syq if (mask & TIOCM_RTS) {
266190f05028Syq if (val & TIOCM_RTS) {
266290f05028Syq *line_ctl |= USB_CDC_ACM_CONTROL_RTS;
266390f05028Syq } else {
266490f05028Syq *line_ctl &= ~USB_CDC_ACM_CONTROL_RTS;
266590f05028Syq }
266690f05028Syq }
266790f05028Syq if (mask & TIOCM_DTR) {
266890f05028Syq if (val & TIOCM_DTR) {
266990f05028Syq *line_ctl |= USB_CDC_ACM_CONTROL_DTR;
267090f05028Syq } else {
267190f05028Syq *line_ctl &= ~USB_CDC_ACM_CONTROL_DTR;
267290f05028Syq }
267390f05028Syq }
267490f05028Syq }
267590f05028Syq
267690f05028Syq
267790f05028Syq /*
267890f05028Syq * usbsacm_reg2mctl:
267990f05028Syq * Get Modem control status
268090f05028Syq */
268190f05028Syq static int
usbsacm_reg2mctl(uint8_t line_ctl)268290f05028Syq usbsacm_reg2mctl(uint8_t line_ctl)
268390f05028Syq {
268490f05028Syq int val = 0;
268590f05028Syq
268690f05028Syq if (line_ctl & USB_CDC_ACM_CONTROL_RTS) {
268790f05028Syq val |= TIOCM_RTS;
268890f05028Syq }
268990f05028Syq if (line_ctl & USB_CDC_ACM_CONTROL_DTR) {
269090f05028Syq val |= TIOCM_DTR;
269190f05028Syq }
269290f05028Syq if (line_ctl & USB_CDC_ACM_CONTROL_DSR) {
269390f05028Syq val |= TIOCM_DSR;
269490f05028Syq }
269590f05028Syq if (line_ctl & USB_CDC_ACM_CONTROL_RNG) {
269690f05028Syq val |= TIOCM_RI;
269790f05028Syq }
269890f05028Syq
269990f05028Syq return (val);
270090f05028Syq }
270190f05028Syq
270290f05028Syq
270390f05028Syq /*
270490f05028Syq * misc routines
270590f05028Syq * -------------
270690f05028Syq *
270790f05028Syq */
270890f05028Syq
270990f05028Syq /*
271090f05028Syq * usbsacm_put_tail:
271190f05028Syq * link a message block to tail of message
271290f05028Syq * account for the case when message is null
271390f05028Syq */
271490f05028Syq static void
usbsacm_put_tail(mblk_t ** mpp,mblk_t * bp)271590f05028Syq usbsacm_put_tail(mblk_t **mpp, mblk_t *bp)
271690f05028Syq {
271790f05028Syq if (*mpp) {
271890f05028Syq linkb(*mpp, bp);
271990f05028Syq } else {
272090f05028Syq *mpp = bp;
272190f05028Syq }
272290f05028Syq }
272390f05028Syq
272490f05028Syq
272590f05028Syq /*
272690f05028Syq * usbsacm_put_head:
272790f05028Syq * put a message block at the head of the message
272890f05028Syq * account for the case when message is null
272990f05028Syq */
273090f05028Syq static void
usbsacm_put_head(mblk_t ** mpp,mblk_t * bp)273190f05028Syq usbsacm_put_head(mblk_t **mpp, mblk_t *bp)
273290f05028Syq {
273390f05028Syq if (*mpp) {
273490f05028Syq linkb(bp, *mpp);
273590f05028Syq }
273690f05028Syq *mpp = bp;
273790f05028Syq }
273890f05028Syq
273990f05028Syq
274090f05028Syq /*
274190f05028Syq * power management
274290f05028Syq * ----------------
274390f05028Syq *
274490f05028Syq * usbsacm_create_pm_components:
274590f05028Syq * create PM components
274690f05028Syq */
274790f05028Syq static int
usbsacm_create_pm_components(usbsacm_state_t * acmp)274890f05028Syq usbsacm_create_pm_components(usbsacm_state_t *acmp)
274990f05028Syq {
275090f05028Syq dev_info_t *dip = acmp->acm_dip;
275190f05028Syq usbsacm_pm_t *pm;
275290f05028Syq uint_t pwr_states;
275390f05028Syq usb_dev_descr_t *dev_descr;
275490f05028Syq
275590f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
275690f05028Syq "usbsacm_create_pm_components: ");
275790f05028Syq
275890f05028Syq if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
275990f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
276090f05028Syq "usbsacm_create_pm_components: failed");
276190f05028Syq
276290f05028Syq return (USB_SUCCESS);
276390f05028Syq }
276490f05028Syq
276590f05028Syq pm = acmp->acm_pm =
276690f05028Syq (usbsacm_pm_t *)kmem_zalloc(sizeof (usbsacm_pm_t), KM_SLEEP);
276790f05028Syq
276890f05028Syq pm->pm_pwr_states = (uint8_t)pwr_states;
276990f05028Syq pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
277090f05028Syq /*
277190f05028Syq * Qualcomm CDMA card won't response the following control commands
277290f05028Syq * after receive USB_REMOTE_WAKEUP_ENABLE. So we just set
277390f05028Syq * pm_wakeup_enable to 0 for this specific device.
277490f05028Syq */
277590f05028Syq dev_descr = acmp->acm_dev_data->dev_descr;
277690f05028Syq if (dev_descr->idVendor == 0x5c6 && dev_descr->idProduct == 0x3100) {
277790f05028Syq pm->pm_wakeup_enabled = 0;
277890f05028Syq } else {
277990f05028Syq pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
278090f05028Syq USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
278190f05028Syq }
278290f05028Syq
278390f05028Syq (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
278490f05028Syq
278590f05028Syq return (USB_SUCCESS);
278690f05028Syq }
278790f05028Syq
278890f05028Syq
278990f05028Syq /*
279090f05028Syq * usbsacm_destroy_pm_components:
279190f05028Syq * destroy PM components
279290f05028Syq */
279390f05028Syq static void
usbsacm_destroy_pm_components(usbsacm_state_t * acmp)279490f05028Syq usbsacm_destroy_pm_components(usbsacm_state_t *acmp)
279590f05028Syq {
279690f05028Syq usbsacm_pm_t *pm = acmp->acm_pm;
279790f05028Syq dev_info_t *dip = acmp->acm_dip;
279890f05028Syq int rval;
279990f05028Syq
280090f05028Syq USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
280190f05028Syq "usbsacm_destroy_pm_components: ");
280290f05028Syq
280390f05028Syq if (acmp->acm_dev_state != USB_DEV_DISCONNECTED) {
280490f05028Syq if (pm->pm_wakeup_enabled) {
280590f05028Syq rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
280690f05028Syq if (rval != DDI_SUCCESS) {
280790f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
280890f05028Syq "usbsacm_destroy_pm_components: "
280990f05028Syq "raising power failed (%d)", rval);
281090f05028Syq }
281190f05028Syq
281290f05028Syq rval = usb_handle_remote_wakeup(dip,
281390f05028Syq USB_REMOTE_WAKEUP_DISABLE);
281490f05028Syq if (rval != USB_SUCCESS) {
281590f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
281690f05028Syq "usbsacm_destroy_pm_components: "
281790f05028Syq "disable remote wakeup failed (%d)", rval);
281890f05028Syq }
281990f05028Syq }
282090f05028Syq
282190f05028Syq (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
282290f05028Syq }
282390f05028Syq kmem_free((caddr_t)pm, sizeof (usbsacm_pm_t));
282490f05028Syq acmp->acm_pm = NULL;
282590f05028Syq }
282690f05028Syq
282790f05028Syq
282890f05028Syq /*
282990f05028Syq * usbsacm_pm_set_busy:
283090f05028Syq * mark device busy and raise power
283190f05028Syq */
283290f05028Syq static void
usbsacm_pm_set_busy(usbsacm_state_t * acmp)283390f05028Syq usbsacm_pm_set_busy(usbsacm_state_t *acmp)
283490f05028Syq {
283590f05028Syq usbsacm_pm_t *pm = acmp->acm_pm;
283690f05028Syq dev_info_t *dip = acmp->acm_dip;
283790f05028Syq int rval;
283890f05028Syq
283990f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
2840112116d8Sfb "usbsacm_pm_set_busy: pm = 0x%p", (void *)pm);
284190f05028Syq
284290f05028Syq if (pm == NULL) {
284390f05028Syq
284490f05028Syq return;
284590f05028Syq }
284690f05028Syq
284790f05028Syq mutex_enter(&acmp->acm_mutex);
284890f05028Syq /* if already marked busy, just increment the counter */
284990f05028Syq if (pm->pm_busy_cnt++ > 0) {
285090f05028Syq mutex_exit(&acmp->acm_mutex);
285190f05028Syq
285290f05028Syq return;
285390f05028Syq }
285490f05028Syq
285590f05028Syq (void) pm_busy_component(dip, 0);
285690f05028Syq
285790f05028Syq if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
285890f05028Syq mutex_exit(&acmp->acm_mutex);
285990f05028Syq
286090f05028Syq return;
286190f05028Syq }
286290f05028Syq
286390f05028Syq /* need to raise power */
286490f05028Syq pm->pm_raise_power = B_TRUE;
286590f05028Syq mutex_exit(&acmp->acm_mutex);
286690f05028Syq
286790f05028Syq rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
286890f05028Syq if (rval != DDI_SUCCESS) {
286990f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
287090f05028Syq "usbsacm_pm_set_busy: raising power failed");
287190f05028Syq }
287290f05028Syq
287390f05028Syq mutex_enter(&acmp->acm_mutex);
287490f05028Syq pm->pm_raise_power = B_FALSE;
287590f05028Syq mutex_exit(&acmp->acm_mutex);
287690f05028Syq }
287790f05028Syq
287890f05028Syq
287990f05028Syq /*
288090f05028Syq * usbsacm_pm_set_idle:
288190f05028Syq * mark device idle
288290f05028Syq */
288390f05028Syq static void
usbsacm_pm_set_idle(usbsacm_state_t * acmp)288490f05028Syq usbsacm_pm_set_idle(usbsacm_state_t *acmp)
288590f05028Syq {
288690f05028Syq usbsacm_pm_t *pm = acmp->acm_pm;
288790f05028Syq dev_info_t *dip = acmp->acm_dip;
288890f05028Syq
288990f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
289090f05028Syq "usbsacm_pm_set_idle: ");
289190f05028Syq
289290f05028Syq if (pm == NULL) {
289390f05028Syq
289490f05028Syq return;
289590f05028Syq }
289690f05028Syq
289790f05028Syq /*
289890f05028Syq * if more ports use the device, do not mark as yet
289990f05028Syq */
290090f05028Syq mutex_enter(&acmp->acm_mutex);
290190f05028Syq if (--pm->pm_busy_cnt > 0) {
290290f05028Syq mutex_exit(&acmp->acm_mutex);
290390f05028Syq
290490f05028Syq return;
290590f05028Syq }
290690f05028Syq
290790f05028Syq if (pm) {
290890f05028Syq (void) pm_idle_component(dip, 0);
290990f05028Syq }
291090f05028Syq mutex_exit(&acmp->acm_mutex);
291190f05028Syq }
291290f05028Syq
291390f05028Syq
291490f05028Syq /*
291590f05028Syq * usbsacm_pwrlvl0:
291690f05028Syq * Functions to handle power transition for OS levels 0 -> 3
291790f05028Syq * The same level as OS state, different from USB state
291890f05028Syq */
291990f05028Syq static int
usbsacm_pwrlvl0(usbsacm_state_t * acmp)292090f05028Syq usbsacm_pwrlvl0(usbsacm_state_t *acmp)
292190f05028Syq {
292290f05028Syq int rval;
292390f05028Syq int i;
292490f05028Syq usbsacm_port_t *cur_port = acmp->acm_ports;
292590f05028Syq
292690f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
292790f05028Syq "usbsacm_pwrlvl0: ");
292890f05028Syq
292990f05028Syq switch (acmp->acm_dev_state) {
293090f05028Syq case USB_DEV_ONLINE:
293190f05028Syq /* issue USB D3 command to the device */
293290f05028Syq rval = usb_set_device_pwrlvl3(acmp->acm_dip);
293390f05028Syq ASSERT(rval == USB_SUCCESS);
293490f05028Syq
293590f05028Syq if (cur_port != NULL) {
29364ee52f77Slg for (i = 0; i < acmp->acm_port_cnt; i++) {
29374ee52f77Slg cur_port = &acmp->acm_ports[i];
29384ee52f77Slg if (cur_port->acm_intr_ph != NULL &&
29394ee52f77Slg cur_port->acm_port_state !=
29404ee52f77Slg USBSACM_PORT_CLOSED) {
29414ee52f77Slg
29424ee52f77Slg mutex_exit(&acmp->acm_mutex);
29434ee52f77Slg usb_pipe_stop_intr_polling(
29444ee52f77Slg cur_port->acm_intr_ph,
29454ee52f77Slg USB_FLAGS_SLEEP);
29464ee52f77Slg mutex_enter(&acmp->acm_mutex);
29474ee52f77Slg
29484ee52f77Slg mutex_enter(&cur_port->acm_port_mutex);
29494ee52f77Slg cur_port->acm_intr_state =
29504ee52f77Slg USBSACM_PIPE_IDLE;
29514ee52f77Slg mutex_exit(&cur_port->acm_port_mutex);
29524ee52f77Slg }
295390f05028Syq }
295490f05028Syq }
295590f05028Syq
295690f05028Syq acmp->acm_dev_state = USB_DEV_PWRED_DOWN;
295790f05028Syq acmp->acm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
295890f05028Syq
295990f05028Syq /* FALLTHRU */
296090f05028Syq case USB_DEV_DISCONNECTED:
296190f05028Syq case USB_DEV_SUSPENDED:
296290f05028Syq /* allow a disconnect/cpr'ed device to go to lower power */
296390f05028Syq
296490f05028Syq return (USB_SUCCESS);
296590f05028Syq case USB_DEV_PWRED_DOWN:
296690f05028Syq default:
296790f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
296890f05028Syq "usbsacm_pwrlvl0: illegal device state");
296990f05028Syq
297090f05028Syq return (USB_FAILURE);
297190f05028Syq }
297290f05028Syq }
297390f05028Syq
297490f05028Syq
297590f05028Syq /*
297690f05028Syq * usbsacm_pwrlvl1:
297790f05028Syq * Functions to handle power transition for OS levels 1 -> 2
297890f05028Syq */
297990f05028Syq static int
usbsacm_pwrlvl1(usbsacm_state_t * acmp)298090f05028Syq usbsacm_pwrlvl1(usbsacm_state_t *acmp)
298190f05028Syq {
298290f05028Syq /* issue USB D2 command to the device */
298390f05028Syq (void) usb_set_device_pwrlvl2(acmp->acm_dip);
298490f05028Syq
298590f05028Syq return (USB_FAILURE);
298690f05028Syq }
298790f05028Syq
298890f05028Syq
298990f05028Syq /*
299090f05028Syq * usbsacm_pwrlvl2:
299190f05028Syq * Functions to handle power transition for OS levels 2 -> 1
299290f05028Syq */
299390f05028Syq static int
usbsacm_pwrlvl2(usbsacm_state_t * acmp)299490f05028Syq usbsacm_pwrlvl2(usbsacm_state_t *acmp)
299590f05028Syq {
299690f05028Syq /* issue USB D1 command to the device */
299790f05028Syq (void) usb_set_device_pwrlvl1(acmp->acm_dip);
299890f05028Syq
299990f05028Syq return (USB_FAILURE);
300090f05028Syq }
300190f05028Syq
300290f05028Syq
300390f05028Syq /*
300490f05028Syq * usbsacm_pwrlvl3:
300590f05028Syq * Functions to handle power transition for OS levels 3 -> 0
300690f05028Syq * The same level as OS state, different from USB state
300790f05028Syq */
300890f05028Syq static int
usbsacm_pwrlvl3(usbsacm_state_t * acmp)300990f05028Syq usbsacm_pwrlvl3(usbsacm_state_t *acmp)
301090f05028Syq {
301190f05028Syq int rval;
301290f05028Syq int i;
301390f05028Syq usbsacm_port_t *cur_port = acmp->acm_ports;
301490f05028Syq
301590f05028Syq USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
301690f05028Syq "usbsacm_pwrlvl3: ");
301790f05028Syq
301890f05028Syq switch (acmp->acm_dev_state) {
301990f05028Syq case USB_DEV_PWRED_DOWN:
302090f05028Syq /* Issue USB D0 command to the device here */
302190f05028Syq rval = usb_set_device_pwrlvl0(acmp->acm_dip);
302290f05028Syq ASSERT(rval == USB_SUCCESS);
302390f05028Syq
302490f05028Syq if (cur_port != NULL) {
30254ee52f77Slg for (i = 0; i < acmp->acm_port_cnt; i++) {
30264ee52f77Slg cur_port = &acmp->acm_ports[i];
30274ee52f77Slg if (cur_port->acm_intr_ph != NULL &&
30284ee52f77Slg cur_port->acm_port_state !=
30294ee52f77Slg USBSACM_PORT_CLOSED) {
30304ee52f77Slg
30314ee52f77Slg mutex_exit(&acmp->acm_mutex);
30324ee52f77Slg usbsacm_pipe_start_polling(cur_port);
30334ee52f77Slg mutex_enter(&acmp->acm_mutex);
30344ee52f77Slg }
303590f05028Syq }
303690f05028Syq }
303790f05028Syq
303890f05028Syq acmp->acm_dev_state = USB_DEV_ONLINE;
303990f05028Syq acmp->acm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
304090f05028Syq
304190f05028Syq /* FALLTHRU */
304290f05028Syq case USB_DEV_ONLINE:
304390f05028Syq /* we are already in full power */
304490f05028Syq
304590f05028Syq /* FALLTHRU */
304690f05028Syq case USB_DEV_DISCONNECTED:
304790f05028Syq case USB_DEV_SUSPENDED:
304890f05028Syq
304990f05028Syq return (USB_SUCCESS);
305090f05028Syq default:
305190f05028Syq USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
305290f05028Syq "usbsacm_pwrlvl3: illegal device state");
305390f05028Syq
305490f05028Syq return (USB_FAILURE);
305590f05028Syq }
305690f05028Syq }
305790f05028Syq
305890f05028Syq
305990f05028Syq /*
306090f05028Syq * usbsacm_pipe_start_polling:
306190f05028Syq * start polling on the interrupt pipe
306290f05028Syq */
306390f05028Syq static void
usbsacm_pipe_start_polling(usbsacm_port_t * acm_port)306490f05028Syq usbsacm_pipe_start_polling(usbsacm_port_t *acm_port)
306590f05028Syq {
306690f05028Syq usb_intr_req_t *intr;
306790f05028Syq int rval;
306890f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
306990f05028Syq
307090f05028Syq USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
307190f05028Syq "usbsacm_pipe_start_polling: ");
307290f05028Syq
307390f05028Syq if (acm_port->acm_intr_ph == NULL) {
307490f05028Syq
307590f05028Syq return;
307690f05028Syq }
307790f05028Syq
307890f05028Syq intr = usb_alloc_intr_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
307990f05028Syq
308090f05028Syq /*
308190f05028Syq * If it is in interrupt context, usb_alloc_intr_req will return NULL if
308290f05028Syq * called with SLEEP flag.
308390f05028Syq */
308490f05028Syq if (!intr) {
308590f05028Syq USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
308690f05028Syq "usbsacm_pipe_start_polling: alloc req failed.");
308790f05028Syq
308890f05028Syq return;
308990f05028Syq }
309090f05028Syq
309190f05028Syq /* initialize the interrupt request. */
309290f05028Syq intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
30934ee52f77Slg USB_ATTRS_AUTOCLEARING;
309490f05028Syq mutex_enter(&acm_port->acm_port_mutex);
309590f05028Syq intr->intr_len = acm_port->acm_intr_ep_descr.wMaxPacketSize;
309690f05028Syq mutex_exit(&acm_port->acm_port_mutex);
309790f05028Syq intr->intr_client_private = (usb_opaque_t)acm_port;
309890f05028Syq intr->intr_cb = usbsacm_intr_cb;
309990f05028Syq intr->intr_exc_cb = usbsacm_intr_ex_cb;
310090f05028Syq
310190f05028Syq rval = usb_pipe_intr_xfer(acm_port->acm_intr_ph, intr, USB_FLAGS_SLEEP);
310290f05028Syq
310390f05028Syq mutex_enter(&acm_port->acm_port_mutex);
310490f05028Syq if (rval == USB_SUCCESS) {
310590f05028Syq acm_port->acm_intr_state = USBSACM_PIPE_BUSY;
310690f05028Syq } else {
310790f05028Syq usb_free_intr_req(intr);
310890f05028Syq acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
310990f05028Syq USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
311090f05028Syq "usbsacm_pipe_start_polling: failed (%d)", rval);
311190f05028Syq }
311290f05028Syq mutex_exit(&acm_port->acm_port_mutex);
311390f05028Syq }
311490f05028Syq
311590f05028Syq
311690f05028Syq /*
311790f05028Syq * usbsacm_intr_cb:
311890f05028Syq * interrupt pipe normal callback
311990f05028Syq */
312090f05028Syq /*ARGSUSED*/
312190f05028Syq static void
usbsacm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)312290f05028Syq usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
312390f05028Syq {
312490f05028Syq usbsacm_port_t *acm_port = (usbsacm_port_t *)req->intr_client_private;
312590f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
312690f05028Syq mblk_t *data = req->intr_data;
312790f05028Syq int data_len;
312890f05028Syq
312990f05028Syq USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
313090f05028Syq "usbsacm_intr_cb: ");
313190f05028Syq
313290f05028Syq data_len = (data) ? MBLKL(data) : 0;
313390f05028Syq
313490f05028Syq /* check data length */
313590f05028Syq if (data_len < 8) {
313690f05028Syq USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
313790f05028Syq "usbsacm_intr_cb: %d packet too short", data_len);
313890f05028Syq usb_free_intr_req(req);
313990f05028Syq
314090f05028Syq return;
314190f05028Syq }
314290f05028Syq req->intr_data = NULL;
314390f05028Syq usb_free_intr_req(req);
314490f05028Syq
314590f05028Syq mutex_enter(&acm_port->acm_port_mutex);
314690f05028Syq /* parse interrupt data. */
314790f05028Syq usbsacm_parse_intr_data(acm_port, data);
314890f05028Syq mutex_exit(&acm_port->acm_port_mutex);
314990f05028Syq }
315090f05028Syq
315190f05028Syq
315290f05028Syq /*
315390f05028Syq * usbsacm_intr_ex_cb:
315490f05028Syq * interrupt pipe exception callback
315590f05028Syq */
315690f05028Syq /*ARGSUSED*/
315790f05028Syq static void
usbsacm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)315890f05028Syq usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
315990f05028Syq {
316090f05028Syq usbsacm_port_t *acm_port = (usbsacm_port_t *)req->intr_client_private;
316190f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
316290f05028Syq usb_cr_t cr = req->intr_completion_reason;
316390f05028Syq
316490f05028Syq USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
316590f05028Syq "usbsacm_intr_ex_cb: ");
316690f05028Syq
316790f05028Syq usb_free_intr_req(req);
316890f05028Syq
316990f05028Syq /*
317090f05028Syq * If completion reason isn't USB_CR_PIPE_CLOSING and
317190f05028Syq * USB_CR_STOPPED_POLLING, restart polling.
317290f05028Syq */
317390f05028Syq if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
317490f05028Syq mutex_enter(&acmp->acm_mutex);
317590f05028Syq
317690f05028Syq if (acmp->acm_dev_state != USB_DEV_ONLINE) {
317790f05028Syq
317890f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
317990f05028Syq "usbsacm_intr_ex_cb: state = %d",
318090f05028Syq acmp->acm_dev_state);
318190f05028Syq
318290f05028Syq mutex_exit(&acmp->acm_mutex);
318390f05028Syq
318490f05028Syq return;
318590f05028Syq }
318690f05028Syq mutex_exit(&acmp->acm_mutex);
318790f05028Syq
318890f05028Syq usbsacm_pipe_start_polling(acm_port);
318990f05028Syq }
319090f05028Syq }
319190f05028Syq
319290f05028Syq
319390f05028Syq /*
319490f05028Syq * usbsacm_parse_intr_data:
319590f05028Syq * Parse data received from interrupt callback
319690f05028Syq */
319790f05028Syq static void
usbsacm_parse_intr_data(usbsacm_port_t * acm_port,mblk_t * data)319890f05028Syq usbsacm_parse_intr_data(usbsacm_port_t *acm_port, mblk_t *data)
319990f05028Syq {
320090f05028Syq usbsacm_state_t *acmp = acm_port->acm_device;
320190f05028Syq uint8_t bmRequestType;
320290f05028Syq uint8_t bNotification;
320390f05028Syq uint16_t wValue;
320490f05028Syq uint16_t wLength;
320590f05028Syq uint16_t wData;
320690f05028Syq
320790f05028Syq USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
320890f05028Syq "usbsacm_parse_intr_data: ");
320990f05028Syq
321090f05028Syq bmRequestType = data->b_rptr[0];
321190f05028Syq bNotification = data->b_rptr[1];
321290f05028Syq /*
321390f05028Syq * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
321490f05028Syq * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
321590f05028Syq * mLength is 2. So we directly get the value from the byte.
321690f05028Syq */
321790f05028Syq wValue = data->b_rptr[2];
321890f05028Syq wLength = data->b_rptr[6];
321990f05028Syq
322090f05028Syq if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
322190f05028Syq USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
322290f05028Syq "usbsacm_parse_intr_data: unknown request type - 0x%x",
322390f05028Syq bmRequestType);
322490f05028Syq
32259e37f2b5SRaymond Chen freemsg(data);
32269e37f2b5SRaymond Chen
322790f05028Syq return;
322890f05028Syq }
322990f05028Syq
323090f05028Syq /*
323190f05028Syq * Check the return value of device
323290f05028Syq */
323390f05028Syq switch (bNotification) {
323490f05028Syq case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
323590f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
323690f05028Syq "usbsacm_parse_intr_data: %s network!",
323790f05028Syq wValue ? "connected to" :"disconnected from");
323890f05028Syq
323990f05028Syq break;
324090f05028Syq case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
324190f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
324290f05028Syq "usbsacm_parse_intr_data: A response is a available.");
324390f05028Syq
324490f05028Syq break;
324590f05028Syq case USB_CDC_NOTIFICATION_SERIAL_STATE:
324690f05028Syq /* check the parameter's length. */
324790f05028Syq if (wLength != 2) {
324890f05028Syq
324990f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
325090f05028Syq "usbsacm_parse_intr_data: error data length.");
325190f05028Syq } else {
325290f05028Syq /*
325390f05028Syq * The Data field is a bitmapped value that contains
325490f05028Syq * the current state of carrier detect, transmission
325590f05028Syq * carrier, break, ring signal and device overrun
325690f05028Syq * error.
325790f05028Syq */
325890f05028Syq wData = data->b_rptr[8];
325990f05028Syq /*
326090f05028Syq * Check the serial state of the current port.
326190f05028Syq */
326290f05028Syq if (wData & USB_CDC_ACM_CONTROL_DCD) {
326390f05028Syq
326490f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
326590f05028Syq "usbsacm_parse_intr_data: "
326690f05028Syq "receiver carrier is set.");
326790f05028Syq }
326890f05028Syq if (wData & USB_CDC_ACM_CONTROL_DSR) {
326990f05028Syq
327090f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
327190f05028Syq "usbsacm_parse_intr_data: "
327290f05028Syq "transmission carrier is set.");
327390f05028Syq
327490f05028Syq acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_DSR;
327590f05028Syq }
327690f05028Syq if (wData & USB_CDC_ACM_CONTROL_BREAK) {
327790f05028Syq
327890f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
327990f05028Syq "usbsacm_parse_intr_data: "
328090f05028Syq "break detection mechanism is set.");
328190f05028Syq }
328290f05028Syq if (wData & USB_CDC_ACM_CONTROL_RNG) {
328390f05028Syq
328490f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
328590f05028Syq "usbsacm_parse_intr_data: "
328690f05028Syq "ring signal detection is set.");
328790f05028Syq
328890f05028Syq acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_RNG;
328990f05028Syq }
329090f05028Syq if (wData & USB_CDC_ACM_CONTROL_FRAMING) {
329190f05028Syq
329290f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
329390f05028Syq "usbsacm_parse_intr_data: "
329490f05028Syq "A framing error has occurred.");
329590f05028Syq }
329690f05028Syq if (wData & USB_CDC_ACM_CONTROL_PARITY) {
329790f05028Syq
329890f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
329990f05028Syq "usbsacm_parse_intr_data: "
330090f05028Syq "A parity error has occurred.");
330190f05028Syq }
330290f05028Syq if (wData & USB_CDC_ACM_CONTROL_OVERRUN) {
330390f05028Syq
330490f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
330590f05028Syq "usbsacm_parse_intr_data: "
330690f05028Syq "Received data has been discarded "
330790f05028Syq "due to overrun.");
330890f05028Syq }
330990f05028Syq }
331090f05028Syq
331190f05028Syq break;
331290f05028Syq default:
331390f05028Syq USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
331490f05028Syq "usbsacm_parse_intr_data: unknown notification - 0x%x!",
331590f05028Syq bNotification);
331690f05028Syq
331790f05028Syq break;
331890f05028Syq }
331990f05028Syq
332090f05028Syq freemsg(data);
332190f05028Syq }
3322