1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /*
27 * Copyright 2019 Joyent, Inc.
28 */
29
30 /*
31 * USB Serial CDC ACM driver
32 *
33 * 1. General Concepts
34 * -------------------
35 *
36 * 1.1 Overview
37 * ------------
38 * This driver supports devices that comply with the USB Communication
39 * Device Class Abstract Control Model (USB CDC ACM) specification,
40 * which is available at http://www.usb.org. Given the broad nature
41 * of communication equipment, this driver supports the following
42 * types of devices:
43 * + Telecommunications devices: analog modems, mobile phones;
44 * + Networking devices: cable modems;
45 * Except the above mentioned acm devices, this driver also supports
46 * some devices which provide modem-like function and have pairs of
47 * bulk in/out pipes.
48 *
49 * There are three classes that make up the definition for communication
50 * devices: the Communication Device Class, the Communication Interface
51 * Class and the Data Interface Class. The Communication Device Class
52 * is a device level definition and is used by the host to properly
53 * identify a communication device that may present several different
54 * types of interfaces. The Communication Interface Class defines a
55 * general-purpose mechanism that can be used to enable all types of
56 * communication services on the Universal Serial Bus (USB). The Data
57 * Interface Class defines a general-purpose mechanism to enable bulk
58 * transfer on the USB when the data does not meet the requirements
59 * for any other class.
60 *
61 * 1.2 Interface Definitions
62 * -------------------------
63 * Communication Class Interface is used for device management and,
64 * optionally, call management. Device management includes the requests
65 * that manage the operational state of a device, the device responses,
66 * and event notifications. In Abstract Control Model, the device can
67 * provide an internal implementation of call management over the Data
68 * Class interface or the Communication Class interface.
69 *
70 * The Data Class defines a data interface as an interface with a class
71 * type of Data Class. Data transmission on a communication device is
72 * not restricted to interfaces using the Data Class. Rather, a data
73 * interface is used to transmit and/or receive data that is not
74 * defined by any other class. The data could be:
75 * + Some form of raw data from a communication line.
76 * + Legacy modem data.
77 * + Data using a proprietary format.
78 *
79 * 1.3 Endpoint Requirements
80 * -------------------------
81 * The Communication Class interface requires one endpoint, the management
82 * element. Optionally, it can have an additional endpoint, the notification
83 * element. The management element uses the default endpoint for all
84 * standard and Communication Class-specific requests. The notification
85 * element normally uses an interrupt endpoint.
86 *
87 * The type of endpoints belonging to a Data Class interface are restricted
88 * to bulk, and are expected to exist in pairs of the same type (one In and
89 * one Out).
90 *
91 * 1.4 ACM Function Characteristics
92 * --------------------------------
93 * With Abstract Control Model, the USB device understands standard
94 * V.25ter (AT) commands. The device contains a Datapump and micro-
95 * controller that handles the AT commands and relay controls. The
96 * device uses both a Data Class interface and a Communication Class.
97 * interface.
98 *
99 * A Communication Class interface of type Abstract Control Model will
100 * consist of a minimum of two pipes; one is used to implement the
101 * management element and the other to implement a notification element.
102 * In addition, the device can use two pipes to implement channels over
103 * which to carry unspecified data, typically over a Data Class interface.
104 *
105 * 1.5 ACM Serial Emulation
106 * ------------------------
107 * The Abstract Control Model can bridge the gap between legacy modem
108 * devices and USB devices. To support certain types of legacy applications,
109 * two problems need to be addressed. The first is supporting specific
110 * legacy control signals and state variables which are addressed
111 * directly by the various carrier modulation standards. To support these
112 * requirement, additional requests and notifications have been created.
113 * Please refer to macro, beginning with USB_CDC_REQ_* and
114 * USB_CDC_NOTIFICATION_*.
115 *
116 * The second significant item which is needed to bridge the gap between
117 * legacy modem designs and the Abstract Control Model is a means to
118 * multiplex call control (AT commands) on the Data Class interface.
119 * Legacy modem designs are limited by only supporting one channel for
120 * both "AT" commands and the actual data. To allow this type of
121 * functionality, the device must have a means to specify this limitation
122 * to the host.
123 *
124 * When describing this type of device, the Communication Class interface
125 * would still specify a Abstract Control Model, but call control would
126 * actually occur over the Data Class interface. To describe this
127 * particular characteristic, the Call Management Functional Descriptor
128 * would have bit D1 of bmCapabilities set.
129 *
130 * 1.6 Other Bulk In/Out Devices
131 * -----------------------------
132 * Some devices don't conform to USB CDC specification, but they provide
133 * modem-like function and have pairs of bulk in/out pipes. This driver
134 * supports this kind of device and exports term nodes by their pipes.
135 *
136 * 2. Implementation
137 * -----------------
138 *
139 * 2.1 Overview
140 * ------------
141 * It is a device-specific driver (DSD) working with USB generic serial
142 * driver (GSD). It implements the USB-to-serial device-specific driver
143 * interface (DSDI) which is offered by GSD. The interface is defined
144 * by ds_ops_t structure.
145 *
146 * 2.2 Port States
147 * ---------------
148 * For USB CDC ACM devices, this driver is attached to its interface,
149 * and exports one port for each interface. For other modem-like devices,
150 * this driver can dynamically find the ports in the current device,
151 * and export one port for each pair bulk in/out pipes. Each port can
152 * be operated independently.
153 *
154 * port_state:
155 *
156 * attach_ports
157 * |
158 * |
159 * |
160 * v
161 * USBSACM_PORT_CLOSED
162 * | ^
163 * | |
164 * V |
165 * open_port close_port
166 * | ^
167 * | |
168 * V |
169 * USBSACM_PORT_OPEN
170 *
171 *
172 * 2.3 Pipe States
173 * ---------------
174 * Each port has its own bulk in/out pipes and some ports could also have
175 * its own interrupt pipes (traced by usbsacm_port structure), which are
176 * opened during attach. The pipe status is as following:
177 *
178 * pipe_state:
179 *
180 * usbsacm_init_alloc_ports usbsacm_free_ports
181 * | ^
182 * v |
183 * |---->------ USBSACM_PORT_CLOSED ------>------+
184 * ^ |
185 * | reconnect/resume/open_port
186 * | |
187 * disconnect/suspend/close_port |
188 * | v
189 * +------<------ USBSACM_PIPE_IDLE ------<------|
190 * | |
191 * V ^
192 * | |
193 * +-----------------+ +-----------+
194 * | |
195 * V ^
196 * | |
197 * rx_start/tx_start----->------failed------->---------|
198 * | |
199 * | bulkin_cb/bulkout_cb
200 * V |
201 * | ^
202 * | |
203 * +----->----- USBSACM_PIPE_BUSY ---->------+
204 *
205 *
206 * To get its status in a timely way, acm driver can get the status
207 * of the device by polling the interrupt pipe.
208 *
209 */
210
211 #include <sys/types.h>
212 #include <sys/param.h>
213 #include <sys/conf.h>
214 #include <sys/stream.h>
215 #include <sys/strsun.h>
216 #include <sys/termio.h>
217 #include <sys/termiox.h>
218 #include <sys/ddi.h>
219 #include <sys/sunddi.h>
220 #include <sys/byteorder.h>
221 #define USBDRV_MAJOR_VER 2
222 #define USBDRV_MINOR_VER 0
223 #include <sys/usb/usba.h>
224 #include <sys/usb/usbdevs.h>
225 #include <sys/usb/usba/usba_types.h>
226 #include <sys/usb/clients/usbser/usbser.h>
227 #include <sys/usb/clients/usbser/usbser_dsdi.h>
228 #include <sys/usb/clients/usbcdc/usb_cdc.h>
229 #include <sys/usb/clients/usbser/usbsacm/usbsacm.h>
230
231 /* devops entry points */
232 static int usbsacm_attach(dev_info_t *, ddi_attach_cmd_t);
233 static int usbsacm_detach(dev_info_t *, ddi_detach_cmd_t);
234 static int usbsacm_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
235 void **);
236 static int usbsacm_open(queue_t *, dev_t *, int, int, cred_t *);
237
238 /* DSD operations */
239 static int usbsacm_ds_attach(ds_attach_info_t *);
240 static void usbsacm_ds_detach(ds_hdl_t);
241 static int usbsacm_ds_register_cb(ds_hdl_t, uint_t, ds_cb_t *);
242 static void usbsacm_ds_unregister_cb(ds_hdl_t, uint_t);
243 static int usbsacm_ds_open_port(ds_hdl_t, uint_t);
244 static int usbsacm_ds_close_port(ds_hdl_t, uint_t);
245
246 /* standard UART operations */
247 static int usbsacm_ds_set_port_params(ds_hdl_t, uint_t,
248 ds_port_params_t *);
249 static int usbsacm_ds_set_modem_ctl(ds_hdl_t, uint_t, int, int);
250 static int usbsacm_ds_get_modem_ctl(ds_hdl_t, uint_t, int, int *);
251 static int usbsacm_ds_break_ctl(ds_hdl_t, uint_t, int);
252
253 /* data xfer */
254 static int usbsacm_ds_tx(ds_hdl_t, uint_t, mblk_t *);
255 static mblk_t *usbsacm_ds_rx(ds_hdl_t, uint_t);
256 static void usbsacm_ds_stop(ds_hdl_t, uint_t, int);
257 static void usbsacm_ds_start(ds_hdl_t, uint_t, int);
258
259 /* fifo operations */
260 static int usbsacm_ds_fifo_flush(ds_hdl_t, uint_t, int);
261 static int usbsacm_ds_fifo_drain(ds_hdl_t, uint_t, int);
262 static int usbsacm_wait_tx_drain(usbsacm_port_t *, int);
263 static int usbsacm_fifo_flush_locked(usbsacm_state_t *, uint_t, int);
264
265 /* power management and CPR */
266 static int usbsacm_ds_suspend(ds_hdl_t);
267 static int usbsacm_ds_resume(ds_hdl_t);
268 static int usbsacm_ds_disconnect(ds_hdl_t);
269 static int usbsacm_ds_reconnect(ds_hdl_t);
270 static int usbsacm_ds_usb_power(ds_hdl_t, int, int, int *);
271 static int usbsacm_create_pm_components(usbsacm_state_t *);
272 static void usbsacm_destroy_pm_components(usbsacm_state_t *);
273 static void usbsacm_pm_set_busy(usbsacm_state_t *);
274 static void usbsacm_pm_set_idle(usbsacm_state_t *);
275 static int usbsacm_pwrlvl0(usbsacm_state_t *);
276 static int usbsacm_pwrlvl1(usbsacm_state_t *);
277 static int usbsacm_pwrlvl2(usbsacm_state_t *);
278 static int usbsacm_pwrlvl3(usbsacm_state_t *);
279
280 /* event handling */
281 /* pipe callbacks */
282 static void usbsacm_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
283 static void usbsacm_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
284
285 /* interrupt pipe */
286 static void usbsacm_pipe_start_polling(usbsacm_port_t *acmp);
287 static void usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
288 static void usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
289 static void usbsacm_parse_intr_data(usbsacm_port_t *acmp, mblk_t *data);
290
291 /* Utility functions */
292 /* data transfer routines */
293 static int usbsacm_rx_start(usbsacm_port_t *);
294 static void usbsacm_tx_start(usbsacm_port_t *);
295 static int usbsacm_send_data(usbsacm_port_t *, mblk_t *);
296
297 /* Initialize or release resources */
298 static int usbsacm_init_alloc_ports(usbsacm_state_t *);
299 static void usbsacm_free_ports(usbsacm_state_t *);
300 static void usbsacm_cleanup(usbsacm_state_t *);
301
302 /* analysis functional descriptors */
303 static int usbsacm_get_descriptors(usbsacm_state_t *);
304
305 /* hotplug */
306 static int usbsacm_restore_device_state(usbsacm_state_t *);
307 static int usbsacm_restore_port_state(usbsacm_state_t *);
308
309 /* pipe operations */
310 static int usbsacm_open_port_pipes(usbsacm_port_t *);
311 static void usbsacm_close_port_pipes(usbsacm_port_t *);
312 static void usbsacm_close_pipes(usbsacm_state_t *);
313 static void usbsacm_disconnect_pipes(usbsacm_state_t *);
314 static int usbsacm_reconnect_pipes(usbsacm_state_t *);
315
316 /* vendor-specific commands */
317 static int usbsacm_req_write(usbsacm_port_t *, uchar_t, uint16_t,
318 mblk_t **);
319 static int usbsacm_set_line_coding(usbsacm_port_t *,
320 usb_cdc_line_coding_t *);
321 static void usbsacm_mctl2reg(int mask, int val, uint8_t *);
322 static int usbsacm_reg2mctl(uint8_t);
323
324 /* misc */
325 static void usbsacm_put_tail(mblk_t **, mblk_t *);
326 static void usbsacm_put_head(mblk_t **, mblk_t *);
327
328
329 /*
330 * Standard STREAMS driver definitions
331 */
332 struct module_info usbsacm_modinfo = {
333 0, /* module id */
334 "usbsacm", /* module name */
335 USBSER_MIN_PKTSZ, /* min pkt size */
336 USBSER_MAX_PKTSZ, /* max pkt size */
337 USBSER_HIWAT, /* hi watermark */
338 USBSER_LOWAT /* low watermark */
339 };
340
341 static struct qinit usbsacm_rinit = {
342 NULL,
343 usbser_rsrv,
344 usbsacm_open,
345 usbser_close,
346 NULL,
347 &usbsacm_modinfo,
348 NULL
349 };
350
351 static struct qinit usbsacm_winit = {
352 usbser_wput,
353 usbser_wsrv,
354 NULL,
355 NULL,
356 NULL,
357 &usbsacm_modinfo,
358 NULL
359 };
360
361
362 struct streamtab usbsacm_str_info = {
363 &usbsacm_rinit, &usbsacm_winit, NULL, NULL
364 };
365
366 /* cb_ops structure */
367 static struct cb_ops usbsacm_cb_ops = {
368 nodev, /* cb_open */
369 nodev, /* cb_close */
370 nodev, /* cb_strategy */
371 nodev, /* cb_print */
372 nodev, /* cb_dump */
373 nodev, /* cb_read */
374 nodev, /* cb_write */
375 nodev, /* cb_ioctl */
376 nodev, /* cb_devmap */
377 nodev, /* cb_mmap */
378 nodev, /* cb_segmap */
379 nochpoll, /* cb_chpoll */
380 ddi_prop_op, /* cb_prop_op */
381 &usbsacm_str_info, /* cb_stream */
382 (int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG) /* cb_flag */
383 };
384
385 /* dev_ops structure */
386 struct dev_ops usbsacm_ops = {
387 DEVO_REV, /* devo_rev */
388 0, /* devo_refcnt */
389 usbsacm_getinfo, /* devo_getinfo */
390 nulldev, /* devo_identify */
391 nulldev, /* devo_probe */
392 usbsacm_attach, /* devo_attach */
393 usbsacm_detach, /* devo_detach */
394 nodev, /* devo_reset */
395 &usbsacm_cb_ops, /* devo_cb_ops */
396 (struct bus_ops *)NULL, /* devo_bus_ops */
397 usbser_power, /* devo_power */
398 ddi_quiesce_not_needed, /* devo_quiesce */
399 };
400
401 extern struct mod_ops mod_driverops;
402 /* modldrv structure */
403 static struct modldrv modldrv = {
404 &mod_driverops, /* type of module - driver */
405 "USB Serial CDC ACM driver",
406 &usbsacm_ops,
407 };
408
409 /* modlinkage structure */
410 static struct modlinkage modlinkage = {
411 MODREV_1,
412 &modldrv,
413 NULL
414 };
415
416 static void *usbsacm_statep; /* soft state */
417
418 /*
419 * DSD definitions
420 */
421 static ds_ops_t usbsacm_ds_ops = {
422 DS_OPS_VERSION,
423 usbsacm_ds_attach,
424 usbsacm_ds_detach,
425 usbsacm_ds_register_cb,
426 usbsacm_ds_unregister_cb,
427 usbsacm_ds_open_port,
428 usbsacm_ds_close_port,
429 usbsacm_ds_usb_power,
430 usbsacm_ds_suspend,
431 usbsacm_ds_resume,
432 usbsacm_ds_disconnect,
433 usbsacm_ds_reconnect,
434 usbsacm_ds_set_port_params,
435 usbsacm_ds_set_modem_ctl,
436 usbsacm_ds_get_modem_ctl,
437 usbsacm_ds_break_ctl,
438 NULL, /* NULL if h/w doesn't support loopback */
439 usbsacm_ds_tx,
440 usbsacm_ds_rx,
441 usbsacm_ds_stop,
442 usbsacm_ds_start,
443 usbsacm_ds_fifo_flush,
444 usbsacm_ds_fifo_drain
445 };
446
447 /*
448 * baud code -> baud rate (0 means unsupported rate)
449 */
450 static int usbsacm_speedtab[] = {
451 0, /* B0 */
452 50, /* B50 */
453 75, /* B75 */
454 110, /* B110 */
455 134, /* B134 */
456 150, /* B150 */
457 200, /* B200 */
458 300, /* B300 */
459 600, /* B600 */
460 1200, /* B1200 */
461 1800, /* B1800 */
462 2400, /* B2400 */
463 4800, /* B4800 */
464 9600, /* B9600 */
465 19200, /* B19200 */
466 38400, /* B38400 */
467 57600, /* B57600 */
468 76800, /* B76800 */
469 115200, /* B115200 */
470 153600, /* B153600 */
471 230400, /* B230400 */
472 307200, /* B307200 */
473 460800, /* B460800 */
474 921600, /* B921600 */
475 1000000, /* B1000000 */
476 1152000, /* B1152000 */
477 1500000, /* B1500000 */
478 2000000, /* B2000000 */
479 2500000, /* B2500000 */
480 3000000, /* B3000000 */
481 3500000, /* B3500000 */
482 4000000, /* B4000000 */
483 };
484
485
486 static uint_t usbsacm_errlevel = USB_LOG_L4;
487 static uint_t usbsacm_errmask = 0xffffffff;
488 static uint_t usbsacm_instance_debug = (uint_t)-1;
489
490
491 /*
492 * usbsacm driver's entry points
493 * -----------------------------
494 */
495 /*
496 * Module-wide initialization routine.
497 */
498 int
_init(void)499 _init(void)
500 {
501 int error;
502
503 if ((error = mod_install(&modlinkage)) == 0) {
504
505 error = ddi_soft_state_init(&usbsacm_statep,
506 usbser_soft_state_size(), 1);
507 }
508
509 return (error);
510 }
511
512
513 /*
514 * Module-wide tear-down routine.
515 */
516 int
_fini(void)517 _fini(void)
518 {
519 int error;
520
521 if ((error = mod_remove(&modlinkage)) == 0) {
522 ddi_soft_state_fini(&usbsacm_statep);
523 }
524
525 return (error);
526 }
527
528
529 int
_info(struct modinfo * modinfop)530 _info(struct modinfo *modinfop)
531 {
532 return (mod_info(&modlinkage, modinfop));
533 }
534
535
536 /*
537 * Device configuration entry points
538 */
539 static int
usbsacm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)540 usbsacm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
541 {
542 return (usbser_attach(dip, cmd, usbsacm_statep, &usbsacm_ds_ops));
543 }
544
545
546 static int
usbsacm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)547 usbsacm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
548 {
549 return (usbser_detach(dip, cmd, usbsacm_statep));
550 }
551
552
553 int
usbsacm_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)554 usbsacm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
555 void **result)
556 {
557 return (usbser_getinfo(dip, infocmd, arg, result, usbsacm_statep));
558 }
559
560
561 static int
usbsacm_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)562 usbsacm_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
563 {
564 return (usbser_open(rq, dev, flag, sflag, cr, usbsacm_statep));
565 }
566
567 /*
568 * usbsacm_ds_detach:
569 * attach device instance, called from GSD attach
570 * initialize state and device, including:
571 * state variables, locks, device node
572 * device registration with system
573 * power management
574 */
575 static int
usbsacm_ds_attach(ds_attach_info_t * aip)576 usbsacm_ds_attach(ds_attach_info_t *aip)
577 {
578 usbsacm_state_t *acmp;
579
580 acmp = (usbsacm_state_t *)kmem_zalloc(sizeof (usbsacm_state_t),
581 KM_SLEEP);
582 acmp->acm_dip = aip->ai_dip;
583 acmp->acm_usb_events = aip->ai_usb_events;
584 acmp->acm_ports = NULL;
585 *aip->ai_hdl = (ds_hdl_t)acmp;
586
587 /* registers usbsacm with the USBA framework */
588 if (usb_client_attach(acmp->acm_dip, USBDRV_VERSION,
589 0) != USB_SUCCESS) {
590
591 goto fail;
592 }
593
594 /* Get the configuration information of device */
595 if (usb_get_dev_data(acmp->acm_dip, &acmp->acm_dev_data,
596 USB_PARSE_LVL_CFG, 0) != USB_SUCCESS) {
597
598 goto fail;
599 }
600 acmp->acm_def_ph = acmp->acm_dev_data->dev_default_ph;
601 acmp->acm_dev_state = USB_DEV_ONLINE;
602 mutex_init(&acmp->acm_mutex, NULL, MUTEX_DRIVER,
603 acmp->acm_dev_data->dev_iblock_cookie);
604
605 acmp->acm_lh = usb_alloc_log_hdl(acmp->acm_dip, "usbsacm",
606 &usbsacm_errlevel, &usbsacm_errmask, &usbsacm_instance_debug, 0);
607
608 /* Create power management components */
609 if (usbsacm_create_pm_components(acmp) != USB_SUCCESS) {
610 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
611 "usbsacm_ds_attach: create pm components failed.");
612
613 goto fail;
614 }
615
616 /* Register to get callbacks for USB events */
617 if (usb_register_event_cbs(acmp->acm_dip, acmp->acm_usb_events, 0)
618 != USB_SUCCESS) {
619 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
620 "usbsacm_ds_attach: register event callback failed.");
621
622 goto fail;
623 }
624
625 /*
626 * If devices conform to acm spec, driver will attach using class id;
627 * if not, using device id.
628 */
629 if ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
630 "usbif,class2.2") == 0) ||
631 ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
632 "usb,class2.2.0") == 0))) {
633
634 acmp->acm_compatibility = B_TRUE;
635 } else {
636 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
637 "usbsacm_ds_attach: A nonstandard device is attaching to "
638 "usbsacm driver. This device doesn't conform to "
639 "usb cdc spec.");
640
641 acmp->acm_compatibility = B_FALSE;
642 }
643
644 /* initialize state variables */
645 if (usbsacm_init_alloc_ports(acmp) != USB_SUCCESS) {
646 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
647 "usbsacm_ds_attach: initialize port structure failed.");
648
649 goto fail;
650 }
651 *aip->ai_port_cnt = acmp->acm_port_cnt;
652
653 /* Get max data size of bulk transfer */
654 if (usb_pipe_get_max_bulk_transfer_size(acmp->acm_dip,
655 &acmp->acm_xfer_sz) != USB_SUCCESS) {
656 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
657 "usbsacm_ds_attach: get max size of transfer failed.");
658
659 goto fail;
660 }
661
662 return (USB_SUCCESS);
663 fail:
664 usbsacm_cleanup(acmp);
665
666 return (USB_FAILURE);
667 }
668
669
670 /*
671 * usbsacm_ds_detach:
672 * detach device instance, called from GSD detach
673 */
674 static void
usbsacm_ds_detach(ds_hdl_t hdl)675 usbsacm_ds_detach(ds_hdl_t hdl)
676 {
677 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
678
679 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
680 "usbsacm_ds_detach:");
681
682 usbsacm_close_pipes(acmp);
683 usbsacm_cleanup(acmp);
684 }
685
686
687 /*
688 * usbsacm_ds_register_cb:
689 * GSD routine call ds_register_cb to register interrupt callbacks
690 * for the given port
691 */
692 /*ARGSUSED*/
693 static int
usbsacm_ds_register_cb(ds_hdl_t hdl,uint_t port_num,ds_cb_t * cb)694 usbsacm_ds_register_cb(ds_hdl_t hdl, uint_t port_num, ds_cb_t *cb)
695 {
696 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
697 usbsacm_port_t *acm_port;
698
699 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
700 "usbsacm_ds_register_cb: acmp = 0x%p port_num = %d",
701 (void *)acmp, port_num);
702
703 /* Check if port number is greater than actual port number. */
704 if (port_num >= acmp->acm_port_cnt) {
705 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
706 "usbsacm_ds_register_cb: port number is wrong.");
707
708 return (USB_FAILURE);
709 }
710 acm_port = &acmp->acm_ports[port_num];
711 acm_port->acm_cb = *cb;
712
713 return (USB_SUCCESS);
714 }
715
716
717 /*
718 * usbsacm_ds_unregister_cb:
719 * GSD routine call ds_unregister_cb to unregister
720 * interrupt callbacks for the given port
721 */
722 /*ARGSUSED*/
723 static void
usbsacm_ds_unregister_cb(ds_hdl_t hdl,uint_t port_num)724 usbsacm_ds_unregister_cb(ds_hdl_t hdl, uint_t port_num)
725 {
726 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
727 usbsacm_port_t *acm_port;
728
729 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
730 "usbsacm_ds_unregister_cb: ");
731
732 if (port_num < acmp->acm_port_cnt) {
733 /* Release callback function */
734 acm_port = &acmp->acm_ports[port_num];
735 bzero(&acm_port->acm_cb, sizeof (acm_port->acm_cb));
736 }
737 }
738
739
740 /*
741 * usbsacm_ds_open_port:
742 * GSD routine call ds_open_port
743 * to open the given port
744 */
745 /*ARGSUSED*/
746 static int
usbsacm_ds_open_port(ds_hdl_t hdl,uint_t port_num)747 usbsacm_ds_open_port(ds_hdl_t hdl, uint_t port_num)
748 {
749 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
750 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
751
752 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
753 "usbsacm_ds_open_port: port_num = %d", port_num);
754
755 mutex_enter(&acm_port->acm_port_mutex);
756 /* Check the status of the given port and device */
757 if ((acmp->acm_dev_state == USB_DEV_DISCONNECTED) ||
758 (acm_port->acm_port_state != USBSACM_PORT_CLOSED)) {
759 mutex_exit(&acm_port->acm_port_mutex);
760
761 return (USB_FAILURE);
762 }
763 mutex_exit(&acm_port->acm_port_mutex);
764
765 usbsacm_pm_set_busy(acmp);
766
767 /* open pipes of port */
768 if (usbsacm_open_port_pipes(acm_port) != USB_SUCCESS) {
769 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
770 "usbsacm_ds_open_port: open pipes failed.");
771
772 return (USB_FAILURE);
773 }
774
775 mutex_enter(&acm_port->acm_port_mutex);
776 /* data receipt */
777 if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
778 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
779 "usbsacm_ds_open_port: start receive data failed.");
780 mutex_exit(&acm_port->acm_port_mutex);
781
782 return (USB_FAILURE);
783 }
784 acm_port->acm_port_state = USBSACM_PORT_OPEN;
785
786 mutex_exit(&acm_port->acm_port_mutex);
787
788 return (USB_SUCCESS);
789 }
790
791
792 /*
793 * usbsacm_ds_close_port:
794 * GSD routine call ds_close_port
795 * to close the given port
796 */
797 /*ARGSUSED*/
798 static int
usbsacm_ds_close_port(ds_hdl_t hdl,uint_t port_num)799 usbsacm_ds_close_port(ds_hdl_t hdl, uint_t port_num)
800 {
801 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
802 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
803 int rval = USB_SUCCESS;
804
805 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
806 "usbsacm_ds_close_port: acmp = 0x%p", (void *)acmp);
807
808 mutex_enter(&acm_port->acm_port_mutex);
809 acm_port->acm_port_state = USBSACM_PORT_CLOSED;
810 mutex_exit(&acm_port->acm_port_mutex);
811
812 usbsacm_close_port_pipes(acm_port);
813
814 mutex_enter(&acm_port->acm_port_mutex);
815 rval = usbsacm_fifo_flush_locked(acmp, port_num, DS_TX | DS_RX);
816 mutex_exit(&acm_port->acm_port_mutex);
817
818 usbsacm_pm_set_idle(acmp);
819
820 return (rval);
821 }
822
823
824 /*
825 * usbsacm_ds_usb_power:
826 * GSD routine call ds_usb_power
827 * to set power level of the component
828 */
829 /*ARGSUSED*/
830 static int
usbsacm_ds_usb_power(ds_hdl_t hdl,int comp,int level,int * new_state)831 usbsacm_ds_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state)
832 {
833 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
834 usbsacm_pm_t *pm = acmp->acm_pm;
835 int rval = USB_SUCCESS;
836
837 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
838 "usbsacm_ds_usb_power: ");
839
840 /* check if pm is NULL */
841 if (pm == NULL) {
842 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
843 "usbsacm_ds_usb_power: pm is NULL.");
844
845 return (USB_FAILURE);
846 }
847
848 mutex_enter(&acmp->acm_mutex);
849 /*
850 * check if we are transitioning to a legal power level
851 */
852 if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
853 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
854 "usbsacm_ds_usb_power: "
855 "illegal power level %d, pwr_states=%x",
856 level, pm->pm_pwr_states);
857 mutex_exit(&acmp->acm_mutex);
858
859 return (USB_FAILURE);
860 }
861
862 /*
863 * if we are about to raise power and asked to lower power, fail
864 */
865 if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
866 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
867 "usbsacm_ds_usb_power: wrong condition.");
868 mutex_exit(&acmp->acm_mutex);
869
870 return (USB_FAILURE);
871 }
872
873 /*
874 * Set the power status of device by request level.
875 */
876 switch (level) {
877 case USB_DEV_OS_PWR_OFF:
878 rval = usbsacm_pwrlvl0(acmp);
879
880 break;
881 case USB_DEV_OS_PWR_1:
882 rval = usbsacm_pwrlvl1(acmp);
883
884 break;
885 case USB_DEV_OS_PWR_2:
886 rval = usbsacm_pwrlvl2(acmp);
887
888 break;
889 case USB_DEV_OS_FULL_PWR:
890 rval = usbsacm_pwrlvl3(acmp);
891 /*
892 * If usbser dev_state is DISCONNECTED or SUSPENDED, it shows
893 * that the usb serial device is disconnected/suspended while it
894 * is under power down state, now the device is powered up
895 * before it is reconnected/resumed. xxx_pwrlvl3() will set dev
896 * state to ONLINE, we need to set the dev state back to
897 * DISCONNECTED/SUSPENDED.
898 */
899 if ((rval == USB_SUCCESS) &&
900 ((*new_state == USB_DEV_DISCONNECTED) ||
901 (*new_state == USB_DEV_SUSPENDED))) {
902 acmp->acm_dev_state = *new_state;
903 }
904
905 break;
906 }
907
908 *new_state = acmp->acm_dev_state;
909 mutex_exit(&acmp->acm_mutex);
910
911 return (rval);
912 }
913
914
915 /*
916 * usbsacm_ds_suspend:
917 * GSD routine call ds_suspend
918 * during CPR suspend
919 */
920 static int
usbsacm_ds_suspend(ds_hdl_t hdl)921 usbsacm_ds_suspend(ds_hdl_t hdl)
922 {
923 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
924 int state = USB_DEV_SUSPENDED;
925
926 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
927 "usbsacm_ds_suspend: ");
928 /*
929 * If the device is suspended while it is under PWRED_DOWN state, we
930 * need to keep the PWRED_DOWN state so that it could be powered up
931 * later. In the mean while, usbser dev state will be changed to
932 * SUSPENDED state.
933 */
934 mutex_enter(&acmp->acm_mutex);
935 if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
936 /* set device status to suspend */
937 acmp->acm_dev_state = USB_DEV_SUSPENDED;
938 }
939 mutex_exit(&acmp->acm_mutex);
940
941 usbsacm_disconnect_pipes(acmp);
942
943 return (state);
944 }
945
946 /*
947 * usbsacm_ds_resume:
948 * GSD routine call ds_resume
949 * during CPR resume
950 */
951 /*ARGSUSED*/
952 static int
usbsacm_ds_resume(ds_hdl_t hdl)953 usbsacm_ds_resume(ds_hdl_t hdl)
954 {
955 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
956 int current_state;
957 int ret;
958
959 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
960 "usbsacm_ds_resume: ");
961
962 mutex_enter(&acmp->acm_mutex);
963 current_state = acmp->acm_dev_state;
964 mutex_exit(&acmp->acm_mutex);
965
966 /* restore the status of device */
967 if (current_state != USB_DEV_ONLINE) {
968 ret = usbsacm_restore_device_state(acmp);
969 } else {
970 ret = USB_DEV_ONLINE;
971 }
972
973 return (ret);
974 }
975
976 /*
977 * usbsacm_ds_disconnect:
978 * GSD routine call ds_disconnect
979 * to disconnect USB device
980 */
981 static int
usbsacm_ds_disconnect(ds_hdl_t hdl)982 usbsacm_ds_disconnect(ds_hdl_t hdl)
983 {
984 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
985 int state = USB_DEV_DISCONNECTED;
986
987 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
988 "usbsacm_ds_disconnect: ");
989
990 /*
991 * If the device is disconnected while it is under PWRED_DOWN state, we
992 * need to keep the PWRED_DOWN state so that it could be powered up
993 * later. In the mean while, usbser dev state will be changed to
994 * DISCONNECTED state.
995 */
996 mutex_enter(&acmp->acm_mutex);
997 if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
998 /* set device status to disconnected */
999 acmp->acm_dev_state = USB_DEV_DISCONNECTED;
1000 }
1001 mutex_exit(&acmp->acm_mutex);
1002
1003 usbsacm_disconnect_pipes(acmp);
1004
1005 return (state);
1006 }
1007
1008
1009 /*
1010 * usbsacm_ds_reconnect:
1011 * GSD routine call ds_reconnect
1012 * to reconnect USB device
1013 */
1014 /*ARGSUSED*/
1015 static int
usbsacm_ds_reconnect(ds_hdl_t hdl)1016 usbsacm_ds_reconnect(ds_hdl_t hdl)
1017 {
1018 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1019
1020 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
1021 "usbsacm_ds_reconnect: ");
1022
1023 return (usbsacm_restore_device_state(acmp));
1024 }
1025
1026
1027 /*
1028 * usbsacm_ds_set_port_params:
1029 * GSD routine call ds_set_port_params
1030 * to set one or more port parameters
1031 */
1032 /*ARGSUSED*/
1033 static int
usbsacm_ds_set_port_params(ds_hdl_t hdl,uint_t port_num,ds_port_params_t * tp)1034 usbsacm_ds_set_port_params(ds_hdl_t hdl, uint_t port_num, ds_port_params_t *tp)
1035 {
1036 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1037 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1038 int i;
1039 uint_t ui;
1040 ds_port_param_entry_t *pe;
1041 usb_cdc_line_coding_t lc;
1042 int ret;
1043
1044 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1045 "usbsacm_ds_set_port_params: acmp = 0x%p", (void *)acmp);
1046
1047 mutex_enter(&acm_port->acm_port_mutex);
1048 /*
1049 * If device conform to acm spec, check if it support to set port param.
1050 */
1051 if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
1052 acmp->acm_compatibility == B_TRUE) {
1053
1054 mutex_exit(&acm_port->acm_port_mutex);
1055 USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
1056 "usbsacm_ds_set_port_params: "
1057 "don't support Set_Line_Coding.");
1058
1059 return (USB_FAILURE);
1060 }
1061
1062 lc = acm_port->acm_line_coding;
1063 mutex_exit(&acm_port->acm_port_mutex);
1064 pe = tp->tp_entries;
1065 /* Get parameter information from ds_port_params_t */
1066 for (i = 0; i < tp->tp_cnt; i++, pe++) {
1067 switch (pe->param) {
1068 case DS_PARAM_BAUD:
1069 /* Data terminal rate, in bits per second. */
1070 ui = pe->val.ui;
1071
1072 /* if we don't support this speed, return USB_FAILURE */
1073 if ((ui >= NELEM(usbsacm_speedtab)) ||
1074 ((ui > 0) && (usbsacm_speedtab[ui] == 0))) {
1075 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
1076 "usbsacm_ds_set_port_params: "
1077 " error baud rate");
1078
1079 return (USB_FAILURE);
1080 }
1081 lc.dwDTERate = LE_32(usbsacm_speedtab[ui]);
1082
1083 break;
1084 case DS_PARAM_PARITY:
1085 /* Parity Type */
1086 if (pe->val.ui & PARENB) {
1087 if (pe->val.ui & PARODD) {
1088 lc.bParityType = USB_CDC_PARITY_ODD;
1089 } else {
1090 lc.bParityType = USB_CDC_PARITY_EVEN;
1091 }
1092 } else {
1093 lc.bParityType = USB_CDC_PARITY_NO;
1094 }
1095
1096 break;
1097 case DS_PARAM_STOPB:
1098 /* Stop bit */
1099 if (pe->val.ui & CSTOPB) {
1100 lc.bCharFormat = USB_CDC_STOP_BITS_2;
1101 } else {
1102 lc.bCharFormat = USB_CDC_STOP_BITS_1;
1103 }
1104
1105 break;
1106 case DS_PARAM_CHARSZ:
1107 /* Data Bits */
1108 switch (pe->val.ui) {
1109 case CS5:
1110 lc.bDataBits = 5;
1111 break;
1112 case CS6:
1113 lc.bDataBits = 6;
1114 break;
1115 case CS7:
1116 lc.bDataBits = 7;
1117 break;
1118 case CS8:
1119 default:
1120 lc.bDataBits = 8;
1121 break;
1122 }
1123
1124 break;
1125 default:
1126 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
1127 "usbsacm_ds_set_port_params: "
1128 "parameter 0x%x isn't supported",
1129 pe->param);
1130
1131 break;
1132 }
1133 }
1134
1135 if ((ret = usbsacm_set_line_coding(acm_port, &lc)) == USB_SUCCESS) {
1136 mutex_enter(&acm_port->acm_port_mutex);
1137 acm_port->acm_line_coding = lc;
1138 mutex_exit(&acm_port->acm_port_mutex);
1139 }
1140
1141 /*
1142 * If device don't conform to acm spec, return success directly.
1143 */
1144 if (acmp->acm_compatibility != B_TRUE) {
1145 ret = USB_SUCCESS;
1146 }
1147
1148 return (ret);
1149 }
1150
1151
1152 /*
1153 * usbsacm_ds_set_modem_ctl:
1154 * GSD routine call ds_set_modem_ctl
1155 * to set modem control of the given port
1156 */
1157 /*ARGSUSED*/
1158 static int
usbsacm_ds_set_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int val)1159 usbsacm_ds_set_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int val)
1160 {
1161 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1162 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1163 uint8_t new_mctl;
1164 int ret;
1165
1166 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1167 "usbsacm_ds_set_modem_ctl: mask = 0x%x val = 0x%x",
1168 mask, val);
1169
1170 mutex_enter(&acm_port->acm_port_mutex);
1171 /*
1172 * If device conform to acm spec, check if it support to set modem
1173 * controls.
1174 */
1175 if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
1176 acmp->acm_compatibility == B_TRUE) {
1177
1178 mutex_exit(&acm_port->acm_port_mutex);
1179 USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
1180 "usbsacm_ds_set_modem_ctl: "
1181 "don't support Set_Control_Line_State.");
1182
1183 return (USB_FAILURE);
1184 }
1185
1186 new_mctl = acm_port->acm_mctlout;
1187 mutex_exit(&acm_port->acm_port_mutex);
1188
1189 usbsacm_mctl2reg(mask, val, &new_mctl);
1190
1191 if ((acmp->acm_compatibility == B_FALSE) || ((ret =
1192 usbsacm_req_write(acm_port, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
1193 new_mctl, NULL)) == USB_SUCCESS)) {
1194 mutex_enter(&acm_port->acm_port_mutex);
1195 acm_port->acm_mctlout = new_mctl;
1196 mutex_exit(&acm_port->acm_port_mutex);
1197 }
1198
1199 /*
1200 * If device don't conform to acm spec, return success directly.
1201 */
1202 if (acmp->acm_compatibility != B_TRUE) {
1203 ret = USB_SUCCESS;
1204 }
1205
1206 return (ret);
1207 }
1208
1209
1210 /*
1211 * usbsacm_ds_get_modem_ctl:
1212 * GSD routine call ds_get_modem_ctl
1213 * to get modem control/status of the given port
1214 */
1215 /*ARGSUSED*/
1216 static int
usbsacm_ds_get_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int * valp)1217 usbsacm_ds_get_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int *valp)
1218 {
1219 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1220 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1221
1222 mutex_enter(&acm_port->acm_port_mutex);
1223 *valp = usbsacm_reg2mctl(acm_port->acm_mctlout) & mask;
1224 /*
1225 * If device conform to acm spec, polling function can modify the value
1226 * of acm_mctlin; else set to default value.
1227 */
1228 if (acmp->acm_compatibility) {
1229 *valp |= usbsacm_reg2mctl(acm_port->acm_mctlin) & mask;
1230 *valp |= (mask & (TIOCM_CD | TIOCM_CTS));
1231 } else {
1232 *valp |= (mask & (TIOCM_CD | TIOCM_CTS | TIOCM_DSR | TIOCM_RI));
1233 }
1234 mutex_exit(&acm_port->acm_port_mutex);
1235
1236 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1237 "usbsacm_ds_get_modem_ctl: val = 0x%x", *valp);
1238
1239 return (USB_SUCCESS);
1240 }
1241
1242
1243 /*
1244 * usbsacm_ds_tx:
1245 * GSD routine call ds_break_ctl
1246 * to set/clear break
1247 */
1248 /*ARGSUSED*/
1249 static int
usbsacm_ds_break_ctl(ds_hdl_t hdl,uint_t port_num,int ctl)1250 usbsacm_ds_break_ctl(ds_hdl_t hdl, uint_t port_num, int ctl)
1251 {
1252 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1253 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1254
1255 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1256 "usbsacm_ds_break_ctl: ");
1257
1258 mutex_enter(&acm_port->acm_port_mutex);
1259 /*
1260 * If device conform to acm spec, check if it support to send break.
1261 */
1262 if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SEND_BREAK) == 0 &&
1263 acmp->acm_compatibility == B_TRUE) {
1264
1265 mutex_exit(&acm_port->acm_port_mutex);
1266 USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
1267 "usbsacm_ds_break_ctl: don't support send break.");
1268
1269 return (USB_FAILURE);
1270 }
1271 mutex_exit(&acm_port->acm_port_mutex);
1272
1273 return (usbsacm_req_write(acm_port, USB_CDC_REQ_SEND_BREAK,
1274 ((ctl == DS_ON) ? 0xffff : 0), NULL));
1275 }
1276
1277
1278 /*
1279 * usbsacm_ds_tx:
1280 * GSD routine call ds_tx
1281 * to data transmit
1282 */
1283 /*ARGSUSED*/
1284 static int
usbsacm_ds_tx(ds_hdl_t hdl,uint_t port_num,mblk_t * mp)1285 usbsacm_ds_tx(ds_hdl_t hdl, uint_t port_num, mblk_t *mp)
1286 {
1287 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1288 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1289
1290 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1291 "usbsacm_ds_tx: mp = 0x%p acmp = 0x%p", (void *)mp, (void *)acmp);
1292
1293 /* sanity checks */
1294 if (mp == NULL) {
1295
1296 return (USB_SUCCESS);
1297 }
1298 if (MBLKL(mp) < 1) {
1299 freemsg(mp);
1300
1301 return (USB_SUCCESS);
1302 }
1303
1304 mutex_enter(&acm_port->acm_port_mutex);
1305 /* put mblk to tail of mblk chain */
1306 usbsacm_put_tail(&acm_port->acm_tx_mp, mp);
1307 usbsacm_tx_start(acm_port);
1308 mutex_exit(&acm_port->acm_port_mutex);
1309
1310 return (USB_SUCCESS);
1311 }
1312
1313
1314 /*
1315 * usbsacm_ds_rx:
1316 * GSD routine call ds_rx;
1317 * to data receipt
1318 */
1319 /*ARGSUSED*/
1320 static mblk_t *
usbsacm_ds_rx(ds_hdl_t hdl,uint_t port_num)1321 usbsacm_ds_rx(ds_hdl_t hdl, uint_t port_num)
1322 {
1323 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1324 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1325 mblk_t *mp;
1326
1327 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1328 "usbsacm_ds_rx: acmp = 0x%p", (void *)acmp);
1329
1330 mutex_enter(&acm_port->acm_port_mutex);
1331
1332 mp = acm_port->acm_rx_mp;
1333 acm_port->acm_rx_mp = NULL;
1334 mutex_exit(&acm_port->acm_port_mutex);
1335
1336 return (mp);
1337 }
1338
1339
1340 /*
1341 * usbsacm_ds_stop:
1342 * GSD routine call ds_stop;
1343 * but acm spec don't define this function
1344 */
1345 /*ARGSUSED*/
1346 static void
usbsacm_ds_stop(ds_hdl_t hdl,uint_t port_num,int dir)1347 usbsacm_ds_stop(ds_hdl_t hdl, uint_t port_num, int dir)
1348 {
1349 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1350
1351 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
1352 "usbsacm_ds_stop: don't support!");
1353 }
1354
1355
1356 /*
1357 * usbsacm_ds_start:
1358 * GSD routine call ds_start;
1359 * but acm spec don't define this function
1360 */
1361 /*ARGSUSED*/
1362 static void
usbsacm_ds_start(ds_hdl_t hdl,uint_t port_num,int dir)1363 usbsacm_ds_start(ds_hdl_t hdl, uint_t port_num, int dir)
1364 {
1365 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1366
1367 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
1368 "usbsacm_ds_start: don't support!");
1369 }
1370
1371
1372 /*
1373 * usbsacm_ds_fifo_flush:
1374 * GSD routine call ds_fifo_flush
1375 * to flush FIFOs
1376 */
1377 /*ARGSUSED*/
1378 static int
usbsacm_ds_fifo_flush(ds_hdl_t hdl,uint_t port_num,int dir)1379 usbsacm_ds_fifo_flush(ds_hdl_t hdl, uint_t port_num, int dir)
1380 {
1381 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1382 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1383 int ret = USB_SUCCESS;
1384
1385 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1386 "usbsacm_ds_fifo_flush: ");
1387
1388 mutex_enter(&acm_port->acm_port_mutex);
1389 ret = usbsacm_fifo_flush_locked(acmp, port_num, dir);
1390 mutex_exit(&acm_port->acm_port_mutex);
1391
1392 return (ret);
1393 }
1394
1395
1396 /*
1397 * usbsacm_ds_fifo_drain:
1398 * GSD routine call ds_fifo_drain
1399 * to wait until empty output FIFO
1400 */
1401 /*ARGSUSED*/
1402 static int
usbsacm_ds_fifo_drain(ds_hdl_t hdl,uint_t port_num,int timeout)1403 usbsacm_ds_fifo_drain(ds_hdl_t hdl, uint_t port_num, int timeout)
1404 {
1405 usbsacm_state_t *acmp = (usbsacm_state_t *)hdl;
1406 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1407 int rval = USB_SUCCESS;
1408
1409 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
1410 "usbsacm_ds_fifo_drain: ");
1411
1412 mutex_enter(&acm_port->acm_port_mutex);
1413 ASSERT(acm_port->acm_port_state == USBSACM_PORT_OPEN);
1414
1415 if (usbsacm_wait_tx_drain(acm_port, timeout) != USB_SUCCESS) {
1416 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
1417 "usbsacm_ds_fifo_drain: fifo drain failed.");
1418 mutex_exit(&acm_port->acm_port_mutex);
1419
1420 return (USB_FAILURE);
1421 }
1422
1423 mutex_exit(&acm_port->acm_port_mutex);
1424
1425 return (rval);
1426 }
1427
1428
1429 /*
1430 * usbsacm_fifo_flush_locked:
1431 * flush FIFOs of the given ports
1432 */
1433 /*ARGSUSED*/
1434 static int
usbsacm_fifo_flush_locked(usbsacm_state_t * acmp,uint_t port_num,int dir)1435 usbsacm_fifo_flush_locked(usbsacm_state_t *acmp, uint_t port_num, int dir)
1436 {
1437 usbsacm_port_t *acm_port = &acmp->acm_ports[port_num];
1438
1439 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
1440 "usbsacm_fifo_flush_locked: ");
1441
1442 /* flush transmit FIFO if DS_TX is set */
1443 if ((dir & DS_TX) && acm_port->acm_tx_mp) {
1444 freemsg(acm_port->acm_tx_mp);
1445 acm_port->acm_tx_mp = NULL;
1446 }
1447 /* flush received FIFO if DS_RX is set */
1448 if ((dir & DS_RX) && acm_port->acm_rx_mp) {
1449 freemsg(acm_port->acm_rx_mp);
1450 acm_port->acm_rx_mp = NULL;
1451 }
1452
1453 return (USB_SUCCESS);
1454 }
1455
1456
1457 /*
1458 * usbsacm_get_bulk_pipe_number:
1459 * Calculate the number of bulk in or out pipes in current device.
1460 */
1461 static int
usbsacm_get_bulk_pipe_number(usbsacm_state_t * acmp,uint_t dir)1462 usbsacm_get_bulk_pipe_number(usbsacm_state_t *acmp, uint_t dir)
1463 {
1464 int count = 0;
1465 int i, skip;
1466 usb_if_data_t *cur_if;
1467 int ep_num;
1468 int if_num;
1469
1470 USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
1471 "usbsacm_get_bulk_pipe_number: ");
1472
1473 cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
1474 if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
1475
1476 /* search each interface which have bulk endpoint */
1477 for (i = 0; i < if_num; i++) {
1478 ep_num = cur_if->if_alt->altif_n_ep;
1479
1480 /*
1481 * search endpoints in current interface,
1482 * which type is input parameter 'dir'
1483 */
1484 for (skip = 0; skip < ep_num; skip++) {
1485 if (usb_lookup_ep_data(acmp->acm_dip,
1486 acmp->acm_dev_data, i, 0, skip,
1487 USB_EP_ATTR_BULK, dir) == NULL) {
1488
1489 /*
1490 * If not found, skip the internal loop
1491 * and search the next interface.
1492 */
1493 break;
1494 }
1495 count++;
1496 }
1497
1498 cur_if++;
1499 }
1500
1501 return (count);
1502 }
1503
1504
1505 /*
1506 * port management
1507 * ---------------
1508 * initialize, release port.
1509 *
1510 *
1511 * usbsacm_init_ports_status:
1512 * Initialize the port status for the current device.
1513 */
1514 static int
usbsacm_init_ports_status(usbsacm_state_t * acmp)1515 usbsacm_init_ports_status(usbsacm_state_t *acmp)
1516 {
1517 usbsacm_port_t *cur_port;
1518 int i, skip;
1519 int if_num;
1520 int intr_if_no = 0;
1521 int ep_num;
1522 usb_if_data_t *cur_if;
1523
1524 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
1525 "usbsacm_init_ports_status: acmp = 0x%p", (void *)acmp);
1526
1527 /* Initialize the port status to default value */
1528 for (i = 0; i < acmp->acm_port_cnt; i++) {
1529 cur_port = &acmp->acm_ports[i];
1530
1531 cv_init(&cur_port->acm_tx_cv, NULL, CV_DRIVER, NULL);
1532
1533 cur_port->acm_port_state = USBSACM_PORT_CLOSED;
1534
1535 cur_port->acm_line_coding.dwDTERate = LE_32((uint32_t)9600);
1536 cur_port->acm_line_coding.bCharFormat = 0;
1537 cur_port->acm_line_coding.bParityType = USB_CDC_PARITY_NO;
1538 cur_port->acm_line_coding.bDataBits = 8;
1539 cur_port->acm_device = acmp;
1540 mutex_init(&cur_port->acm_port_mutex, NULL, MUTEX_DRIVER,
1541 acmp->acm_dev_data->dev_iblock_cookie);
1542 }
1543
1544 /*
1545 * If device conform to cdc acm spec, parse function descriptors.
1546 */
1547 if (acmp->acm_compatibility == B_TRUE) {
1548
1549 if (usbsacm_get_descriptors(acmp) != USB_SUCCESS) {
1550
1551 return (USB_FAILURE);
1552 }
1553
1554 return (USB_SUCCESS);
1555 }
1556
1557 /*
1558 * If device don't conform to spec, search pairs of bulk in/out
1559 * endpoints and fill port structure.
1560 */
1561 cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
1562 if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
1563 cur_port = acmp->acm_ports;
1564
1565 /* search each interface which have bulk in and out */
1566 for (i = 0; i < if_num; i++) {
1567 ep_num = cur_if->if_alt->altif_n_ep;
1568
1569 for (skip = 0; skip < ep_num; skip++) {
1570
1571 /* search interrupt pipe. */
1572 if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
1573 i, 0, skip, USB_EP_ATTR_INTR, USB_EP_DIR_IN) != NULL)) {
1574
1575 intr_if_no = i;
1576 }
1577
1578 /* search pair of bulk in/out endpoints. */
1579 if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
1580 i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_IN) == NULL) ||
1581 (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
1582 i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_OUT) == NULL)) {
1583
1584 continue;
1585 }
1586
1587 cur_port->acm_data_if_no = i;
1588 cur_port->acm_ctrl_if_no = intr_if_no;
1589 cur_port->acm_data_port_no = skip;
1590 cur_port++;
1591 intr_if_no = 0;
1592 }
1593
1594 cur_if++;
1595 }
1596
1597 return (USB_SUCCESS);
1598 }
1599
1600
1601 /*
1602 * usbsacm_init_alloc_ports:
1603 * Allocate memory and initialize the port state for the current device.
1604 */
1605 static int
usbsacm_init_alloc_ports(usbsacm_state_t * acmp)1606 usbsacm_init_alloc_ports(usbsacm_state_t *acmp)
1607 {
1608 int rval = USB_SUCCESS;
1609 int count_in = 0, count_out = 0;
1610
1611 if (acmp->acm_compatibility) {
1612 acmp->acm_port_cnt = 1;
1613 } else {
1614 /* Calculate the number of the bulk in/out endpoints */
1615 count_in = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_IN);
1616 count_out = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_OUT);
1617
1618 USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
1619 "usbsacm_init_alloc_ports: count_in = %d, count_out = %d",
1620 count_in, count_out);
1621
1622 acmp->acm_port_cnt = min(count_in, count_out);
1623 }
1624
1625 /* return if not found any pair of bulk in/out endpoint. */
1626 if (acmp->acm_port_cnt == 0) {
1627 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
1628 "usbsacm_init_alloc_ports: port count is zero.");
1629
1630 return (USB_FAILURE);
1631 }
1632
1633 /* allocate memory for ports */
1634 acmp->acm_ports = (usbsacm_port_t *)kmem_zalloc(acmp->acm_port_cnt *
1635 sizeof (usbsacm_port_t), KM_SLEEP);
1636 if (acmp->acm_ports == NULL) {
1637 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
1638 "usbsacm_init_alloc_ports: allocate memory failed.");
1639
1640 return (USB_FAILURE);
1641 }
1642
1643 /* fill the status of port structure. */
1644 rval = usbsacm_init_ports_status(acmp);
1645 if (rval != USB_SUCCESS) {
1646 usbsacm_free_ports(acmp);
1647 }
1648
1649 return (rval);
1650 }
1651
1652
1653 /*
1654 * usbsacm_free_ports:
1655 * Release ports and deallocate memory.
1656 */
1657 static void
usbsacm_free_ports(usbsacm_state_t * acmp)1658 usbsacm_free_ports(usbsacm_state_t *acmp)
1659 {
1660 int i;
1661
1662 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
1663 "usbsacm_free_ports: ");
1664
1665 /* Release memory and data structure for each port */
1666 for (i = 0; i < acmp->acm_port_cnt; i++) {
1667 cv_destroy(&acmp->acm_ports[i].acm_tx_cv);
1668 mutex_destroy(&acmp->acm_ports[i].acm_port_mutex);
1669 }
1670 kmem_free((caddr_t)acmp->acm_ports, sizeof (usbsacm_port_t) *
1671 acmp->acm_port_cnt);
1672 acmp->acm_ports = NULL;
1673 }
1674
1675
1676 /*
1677 * usbsacm_get_descriptors:
1678 * analysis functional descriptors of acm device
1679 */
1680 static int
usbsacm_get_descriptors(usbsacm_state_t * acmp)1681 usbsacm_get_descriptors(usbsacm_state_t *acmp)
1682 {
1683 int i;
1684 usb_cfg_data_t *cfg;
1685 usb_alt_if_data_t *altif;
1686 usb_cvs_data_t *cvs;
1687 int mgmt_cap = 0;
1688 int master_if = -1, slave_if = -1;
1689 usbsacm_port_t *acm_port = acmp->acm_ports;
1690 usb_dev_descr_t *dd;
1691
1692 USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
1693 "usbsacm_get_descriptors: ");
1694
1695 dd = acmp->acm_dev_data->dev_descr;
1696 cfg = acmp->acm_dev_data->dev_curr_cfg;
1697 /* set default control and data interface */
1698 acm_port->acm_ctrl_if_no = acm_port->acm_data_if_no = 0;
1699
1700 /* get current interfaces */
1701 acm_port->acm_ctrl_if_no = acmp->acm_dev_data->dev_curr_if;
1702 if (cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt == 0) {
1703 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1704 "usbsacm_get_descriptors: elements in if_alt is %d",
1705 cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt);
1706
1707 return (USB_FAILURE);
1708 }
1709
1710 altif = &cfg->cfg_if[acm_port->acm_ctrl_if_no].if_alt[0];
1711
1712 /*
1713 * Based on CDC specification, ACM devices usually include the
1714 * following function descriptors: Header, ACM, Union and Call
1715 * Management function descriptors. This loop search tree data
1716 * structure for each acm class descriptor.
1717 */
1718 for (i = 0; i < altif->altif_n_cvs; i++) {
1719
1720 cvs = &altif->altif_cvs[i];
1721
1722 if ((cvs->cvs_buf == NULL) ||
1723 (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
1724 continue;
1725 }
1726
1727 switch (cvs->cvs_buf[2]) {
1728 case USB_CDC_DESCR_TYPE_CALL_MANAGEMENT:
1729 /* parse call management functional descriptor. */
1730 if (cvs->cvs_buf_len >= 5) {
1731 mgmt_cap = cvs->cvs_buf[3];
1732 acm_port->acm_data_if_no = cvs->cvs_buf[4];
1733 }
1734 break;
1735 case USB_CDC_DESCR_TYPE_ACM:
1736 /* parse ACM functional descriptor. */
1737 if (cvs->cvs_buf_len >= 4) {
1738 acm_port->acm_cap = cvs->cvs_buf[3];
1739 }
1740
1741 /*
1742 * The Sigma Designs, Inc. USB device does not report
1743 * itself as implementing the full ACM spec. However,
1744 * it does function as a usb serial modem, so we opt to
1745 * scribble in the reported functionality if we
1746 * determine the USB device matches this vendor
1747 * and product ID.
1748 */
1749 if (dd->idVendor == USB_VENDOR_SIGMADESIGNS &&
1750 dd->idProduct == USB_PRODUCT_SIGMADESIGNS_ZW090) {
1751 acm_port->acm_cap |=
1752 USB_CDC_ACM_CAP_SERIAL_LINE;
1753 }
1754 break;
1755 case USB_CDC_DESCR_TYPE_UNION:
1756 /* parse Union functional descriptor. */
1757 if (cvs->cvs_buf_len >= 5) {
1758 master_if = cvs->cvs_buf[3];
1759 slave_if = cvs->cvs_buf[4];
1760 }
1761 break;
1762 default:
1763 break;
1764 }
1765 }
1766
1767 /* For usb acm devices, it must satisfy the following options. */
1768 if (cfg->cfg_n_if < 2) {
1769 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1770 "usbsacm_get_descriptors: # of interfaces %d < 2",
1771 cfg->cfg_n_if);
1772
1773 return (USB_FAILURE);
1774 }
1775
1776 if (acm_port->acm_data_if_no == 0 &&
1777 slave_if != acm_port->acm_data_if_no) {
1778 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1779 "usbsacm_get_descriptors: Device hasn't call management "
1780 "descriptor and use Union Descriptor.");
1781
1782 acm_port->acm_data_if_no = slave_if;
1783 }
1784
1785 if ((master_if != acm_port->acm_ctrl_if_no) ||
1786 (slave_if != acm_port->acm_data_if_no)) {
1787 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1788 "usbsacm_get_descriptors: control interface or "
1789 "data interface don't match.");
1790
1791 return (USB_FAILURE);
1792 }
1793
1794 /*
1795 * We usually need both call and data capabilities, but
1796 * some devices, such as Nokia mobile phones, don't provide
1797 * call management descriptor, so we just give a warning
1798 * message.
1799 */
1800 if (((mgmt_cap & USB_CDC_CALL_MGMT_CAP_CALL_MGMT) == 0) ||
1801 ((mgmt_cap & USB_CDC_CALL_MGMT_CAP_DATA_INTERFACE) == 0)) {
1802 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1803 "usbsacm_get_descriptors: "
1804 "insufficient mgmt capabilities %x",
1805 mgmt_cap);
1806 }
1807
1808 if ((acm_port->acm_ctrl_if_no >= cfg->cfg_n_if) ||
1809 (acm_port->acm_data_if_no >= cfg->cfg_n_if)) {
1810 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1811 "usbsacm_get_descriptors: control interface %d or "
1812 "data interface %d out of range.",
1813 acm_port->acm_ctrl_if_no, acm_port->acm_data_if_no);
1814
1815 return (USB_FAILURE);
1816 }
1817
1818 /* control interface must have interrupt endpoint */
1819 if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
1820 acm_port->acm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
1821 USB_EP_DIR_IN) == NULL) {
1822 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1823 "usbsacm_get_descriptors: "
1824 "ctrl interface %d has no interrupt endpoint",
1825 acm_port->acm_data_if_no);
1826
1827 return (USB_FAILURE);
1828 }
1829
1830 /* data interface must have bulk in and out */
1831 if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
1832 acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
1833 USB_EP_DIR_IN) == NULL) {
1834 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1835 "usbsacm_get_descriptors: "
1836 "data interface %d has no bulk in endpoint",
1837 acm_port->acm_data_if_no);
1838
1839 return (USB_FAILURE);
1840 }
1841 if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
1842 acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
1843 USB_EP_DIR_OUT) == NULL) {
1844 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1845 "usbsacm_get_descriptors: "
1846 "data interface %d has no bulk out endpoint",
1847 acm_port->acm_data_if_no);
1848
1849 return (USB_FAILURE);
1850 }
1851
1852 return (USB_SUCCESS);
1853 }
1854
1855
1856 /*
1857 * usbsacm_cleanup:
1858 * Release resources of current device during detach.
1859 */
1860 static void
usbsacm_cleanup(usbsacm_state_t * acmp)1861 usbsacm_cleanup(usbsacm_state_t *acmp)
1862 {
1863 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
1864 "usbsacm_cleanup: ");
1865
1866 if (acmp != NULL) {
1867 /* free ports */
1868 if (acmp->acm_ports != NULL) {
1869 usbsacm_free_ports(acmp);
1870 }
1871
1872 /* unregister callback function */
1873 if (acmp->acm_usb_events != NULL) {
1874 usb_unregister_event_cbs(acmp->acm_dip,
1875 acmp->acm_usb_events);
1876 }
1877
1878 /* destroy power management components */
1879 if (acmp->acm_pm != NULL) {
1880 usbsacm_destroy_pm_components(acmp);
1881 }
1882
1883 /* free description of device tree. */
1884 if (acmp->acm_def_ph != NULL) {
1885 mutex_destroy(&acmp->acm_mutex);
1886
1887 usb_free_descr_tree(acmp->acm_dip, acmp->acm_dev_data);
1888 acmp->acm_def_ph = NULL;
1889 }
1890
1891 if (acmp->acm_lh != NULL) {
1892 usb_free_log_hdl(acmp->acm_lh);
1893 acmp->acm_lh = NULL;
1894 }
1895
1896 /* detach client device */
1897 if (acmp->acm_dev_data != NULL) {
1898 usb_client_detach(acmp->acm_dip, acmp->acm_dev_data);
1899 }
1900
1901 kmem_free((caddr_t)acmp, sizeof (usbsacm_state_t));
1902 }
1903 }
1904
1905
1906 /*
1907 * usbsacm_restore_device_state:
1908 * restore device state after CPR resume or reconnect
1909 */
1910 static int
usbsacm_restore_device_state(usbsacm_state_t * acmp)1911 usbsacm_restore_device_state(usbsacm_state_t *acmp)
1912 {
1913 int state;
1914
1915 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1916 "usbsacm_restore_device_state: ");
1917
1918 mutex_enter(&acmp->acm_mutex);
1919 state = acmp->acm_dev_state;
1920 mutex_exit(&acmp->acm_mutex);
1921
1922 /* Check device status */
1923 if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
1924
1925 return (state);
1926 }
1927
1928 /* Check if we are talking to the same device */
1929 if (usb_check_same_device(acmp->acm_dip, acmp->acm_lh, USB_LOG_L0,
1930 -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
1931 mutex_enter(&acmp->acm_mutex);
1932 state = acmp->acm_dev_state = USB_DEV_DISCONNECTED;
1933 mutex_exit(&acmp->acm_mutex);
1934
1935 return (state);
1936 }
1937
1938 if (state == USB_DEV_DISCONNECTED) {
1939 USB_DPRINTF_L1(PRINT_MASK_ALL, acmp->acm_lh,
1940 "usbsacm_restore_device_state: Device has been reconnected "
1941 "but data may have been lost");
1942 }
1943
1944 /* reconnect pipes */
1945 if (usbsacm_reconnect_pipes(acmp) != USB_SUCCESS) {
1946
1947 return (state);
1948 }
1949
1950 /*
1951 * init device state
1952 */
1953 mutex_enter(&acmp->acm_mutex);
1954 state = acmp->acm_dev_state = USB_DEV_ONLINE;
1955 mutex_exit(&acmp->acm_mutex);
1956
1957 if ((usbsacm_restore_port_state(acmp) != USB_SUCCESS)) {
1958 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1959 "usbsacm_restore_device_state: failed");
1960 }
1961
1962 return (state);
1963 }
1964
1965
1966 /*
1967 * usbsacm_restore_port_state:
1968 * restore ports state after CPR resume or reconnect
1969 */
1970 static int
usbsacm_restore_port_state(usbsacm_state_t * acmp)1971 usbsacm_restore_port_state(usbsacm_state_t *acmp)
1972 {
1973 int i, ret = USB_SUCCESS;
1974 usbsacm_port_t *cur_port;
1975
1976 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
1977 "usbsacm_restore_port_state: ");
1978
1979 /* restore status of all ports */
1980 for (i = 0; i < acmp->acm_port_cnt; i++) {
1981 cur_port = &acmp->acm_ports[i];
1982 mutex_enter(&cur_port->acm_port_mutex);
1983 if (cur_port->acm_port_state != USBSACM_PORT_OPEN) {
1984 mutex_exit(&cur_port->acm_port_mutex);
1985
1986 continue;
1987 }
1988 mutex_exit(&cur_port->acm_port_mutex);
1989
1990 if ((ret = usbsacm_set_line_coding(cur_port,
1991 &cur_port->acm_line_coding)) != USB_SUCCESS) {
1992 USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
1993 "usbsacm_restore_port_state: failed.");
1994 }
1995 }
1996
1997 return (ret);
1998 }
1999
2000
2001 /*
2002 * pipe management
2003 * ---------------
2004 *
2005 *
2006 * usbsacm_open_port_pipes:
2007 * Open pipes of one port and set port structure;
2008 * Each port includes three pipes: bulk in, bulk out and interrupt.
2009 */
2010 static int
usbsacm_open_port_pipes(usbsacm_port_t * acm_port)2011 usbsacm_open_port_pipes(usbsacm_port_t *acm_port)
2012 {
2013 int rval = USB_SUCCESS;
2014 usbsacm_state_t *acmp = acm_port->acm_device;
2015 usb_ep_data_t *in_data, *out_data, *intr_pipe;
2016 usb_pipe_policy_t policy;
2017
2018 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
2019 "usbsacm_open_port_pipes: acmp = 0x%p", (void *)acmp);
2020
2021 /* Get bulk and interrupt endpoint data */
2022 intr_pipe = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
2023 acm_port->acm_ctrl_if_no, 0, 0,
2024 USB_EP_ATTR_INTR, USB_EP_DIR_IN);
2025 in_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
2026 acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
2027 USB_EP_ATTR_BULK, USB_EP_DIR_IN);
2028 out_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
2029 acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
2030 USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
2031
2032 /* Bulk in and out must exist meanwhile. */
2033 if ((in_data == NULL) || (out_data == NULL)) {
2034 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
2035 "usbsacm_open_port_pipes: look up bulk pipe failed in "
2036 "interface %d port %d",
2037 acm_port->acm_data_if_no, acm_port->acm_data_port_no);
2038
2039 return (USB_FAILURE);
2040 }
2041
2042 /*
2043 * If device conform to acm spec, it must have an interrupt pipe
2044 * for this port.
2045 */
2046 if (acmp->acm_compatibility == B_TRUE && intr_pipe == NULL) {
2047 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
2048 "usbsacm_open_port_pipes: look up interrupt pipe failed in "
2049 "interface %d", acm_port->acm_ctrl_if_no);
2050
2051 return (USB_FAILURE);
2052 }
2053
2054 policy.pp_max_async_reqs = 2;
2055
2056 /* Open bulk in endpoint */
2057 if (usb_pipe_open(acmp->acm_dip, &in_data->ep_descr, &policy,
2058 USB_FLAGS_SLEEP, &acm_port->acm_bulkin_ph) != USB_SUCCESS) {
2059 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
2060 "usbsacm_open_port_pipes: open bulkin pipe failed!");
2061
2062 return (USB_FAILURE);
2063 }
2064
2065 /* Open bulk out endpoint */
2066 if (usb_pipe_open(acmp->acm_dip, &out_data->ep_descr, &policy,
2067 USB_FLAGS_SLEEP, &acm_port->acm_bulkout_ph) != USB_SUCCESS) {
2068 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
2069 "usbsacm_open_port_pipes: open bulkout pipe failed!");
2070
2071 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
2072 USB_FLAGS_SLEEP, NULL, NULL);
2073
2074 return (USB_FAILURE);
2075 }
2076
2077 /* Open interrupt endpoint if found. */
2078 if (intr_pipe != NULL) {
2079
2080 if (usb_pipe_open(acmp->acm_dip, &intr_pipe->ep_descr, &policy,
2081 USB_FLAGS_SLEEP, &acm_port->acm_intr_ph) != USB_SUCCESS) {
2082 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
2083 "usbsacm_open_port_pipes: "
2084 "open control pipe failed");
2085
2086 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
2087 USB_FLAGS_SLEEP, NULL, NULL);
2088 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
2089 USB_FLAGS_SLEEP, NULL, NULL);
2090
2091 return (USB_FAILURE);
2092 }
2093 }
2094
2095 /* initialize the port structure. */
2096 mutex_enter(&acm_port->acm_port_mutex);
2097 acm_port->acm_bulkin_size = in_data->ep_descr.wMaxPacketSize;
2098 acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
2099 acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
2100 if (acm_port->acm_intr_ph != NULL) {
2101 acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
2102 acm_port->acm_intr_ep_descr = intr_pipe->ep_descr;
2103 }
2104 mutex_exit(&acm_port->acm_port_mutex);
2105
2106 if (acm_port->acm_intr_ph != NULL) {
2107
2108 usbsacm_pipe_start_polling(acm_port);
2109 }
2110
2111 return (rval);
2112 }
2113
2114
2115 /*
2116 * usbsacm_close_port_pipes:
2117 * Close pipes of one port and reset port structure to closed;
2118 * Each port includes three pipes: bulk in, bulk out and interrupt.
2119 */
2120 static void
usbsacm_close_port_pipes(usbsacm_port_t * acm_port)2121 usbsacm_close_port_pipes(usbsacm_port_t *acm_port)
2122 {
2123 usbsacm_state_t *acmp = acm_port->acm_device;
2124
2125 mutex_enter(&acm_port->acm_port_mutex);
2126 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
2127 "usbsacm_close_port_pipes: acm_bulkin_state = %d",
2128 acm_port->acm_bulkin_state);
2129
2130 /*
2131 * Check the status of the given port. If port is closing or closed,
2132 * return directly.
2133 */
2134 if ((acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSED) ||
2135 (acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSING)) {
2136 USB_DPRINTF_L2(PRINT_MASK_CLOSE, acmp->acm_lh,
2137 "usbsacm_close_port_pipes: port is closing or has closed");
2138 mutex_exit(&acm_port->acm_port_mutex);
2139
2140 return;
2141 }
2142
2143 acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSING;
2144 mutex_exit(&acm_port->acm_port_mutex);
2145
2146 /* Close pipes */
2147 usb_pipe_reset(acmp->acm_dip, acm_port->acm_bulkin_ph,
2148 USB_FLAGS_SLEEP, 0, 0);
2149 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
2150 USB_FLAGS_SLEEP, 0, 0);
2151 usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
2152 USB_FLAGS_SLEEP, 0, 0);
2153 if (acm_port->acm_intr_ph != NULL) {
2154 usb_pipe_stop_intr_polling(acm_port->acm_intr_ph,
2155 USB_FLAGS_SLEEP);
2156 usb_pipe_close(acmp->acm_dip, acm_port->acm_intr_ph,
2157 USB_FLAGS_SLEEP, 0, 0);
2158 }
2159
2160 mutex_enter(&acm_port->acm_port_mutex);
2161 /* Reset the status of pipes to closed */
2162 acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSED;
2163 acm_port->acm_bulkin_ph = NULL;
2164 acm_port->acm_bulkout_state = USBSACM_PIPE_CLOSED;
2165 acm_port->acm_bulkout_ph = NULL;
2166 if (acm_port->acm_intr_ph != NULL) {
2167 acm_port->acm_intr_state = USBSACM_PIPE_CLOSED;
2168 acm_port->acm_intr_ph = NULL;
2169 }
2170
2171 mutex_exit(&acm_port->acm_port_mutex);
2172
2173 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
2174 "usbsacm_close_port_pipes: port has been closed.");
2175 }
2176
2177
2178 /*
2179 * usbsacm_close_pipes:
2180 * close all opened pipes of current devices.
2181 */
2182 static void
usbsacm_close_pipes(usbsacm_state_t * acmp)2183 usbsacm_close_pipes(usbsacm_state_t *acmp)
2184 {
2185 int i;
2186
2187 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
2188 "usbsacm_close_pipes: ");
2189
2190 /* Close all ports */
2191 for (i = 0; i < acmp->acm_port_cnt; i++) {
2192 usbsacm_close_port_pipes(&acmp->acm_ports[i]);
2193 }
2194 }
2195
2196
2197 /*
2198 * usbsacm_disconnect_pipes:
2199 * this function just call usbsacm_close_pipes.
2200 */
2201 static void
usbsacm_disconnect_pipes(usbsacm_state_t * acmp)2202 usbsacm_disconnect_pipes(usbsacm_state_t *acmp)
2203 {
2204 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
2205 "usbsacm_disconnect_pipes: ");
2206
2207 usbsacm_close_pipes(acmp);
2208 }
2209
2210
2211 /*
2212 * usbsacm_reconnect_pipes:
2213 * reconnect pipes in CPR resume or reconnect
2214 */
2215 static int
usbsacm_reconnect_pipes(usbsacm_state_t * acmp)2216 usbsacm_reconnect_pipes(usbsacm_state_t *acmp)
2217 {
2218 usbsacm_port_t *cur_port = acmp->acm_ports;
2219 int i;
2220
2221 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
2222 "usbsacm_reconnect_pipes: ");
2223
2224 /* reopen all ports of current device. */
2225 for (i = 0; i < acmp->acm_port_cnt; i++) {
2226 cur_port = &acmp->acm_ports[i];
2227
2228 mutex_enter(&cur_port->acm_port_mutex);
2229 /*
2230 * If port status is open, reopen it;
2231 * else retain the current status.
2232 */
2233 if (cur_port->acm_port_state == USBSACM_PORT_OPEN) {
2234
2235 mutex_exit(&cur_port->acm_port_mutex);
2236 if (usbsacm_open_port_pipes(cur_port) != USB_SUCCESS) {
2237 USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
2238 "usbsacm_reconnect_pipes: "
2239 "open port %d failed.", i);
2240
2241 return (USB_FAILURE);
2242 }
2243 mutex_enter(&cur_port->acm_port_mutex);
2244 }
2245 mutex_exit(&cur_port->acm_port_mutex);
2246 }
2247
2248 return (USB_SUCCESS);
2249 }
2250
2251 /*
2252 * usbsacm_bulkin_cb:
2253 * Bulk In regular and exeception callback;
2254 * USBA framework will call this callback
2255 * after deal with bulkin request.
2256 */
2257 /*ARGSUSED*/
2258 static void
usbsacm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)2259 usbsacm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
2260 {
2261 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->bulk_client_private;
2262 usbsacm_state_t *acmp = acm_port->acm_device;
2263 mblk_t *data;
2264 int data_len;
2265
2266 data = req->bulk_data;
2267 data_len = (data) ? MBLKL(data) : 0;
2268
2269 mutex_enter(&acm_port->acm_port_mutex);
2270 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
2271 "usbsacm_bulkin_cb: "
2272 "acm_bulkin_state = %d acm_port_state = %d data_len = %d",
2273 acm_port->acm_bulkin_state, acm_port->acm_port_state, data_len);
2274
2275 if ((acm_port->acm_port_state == USBSACM_PORT_OPEN) && (data_len) &&
2276 (req->bulk_completion_reason == USB_CR_OK)) {
2277 mutex_exit(&acm_port->acm_port_mutex);
2278 /* prevent USBA from freeing data along with the request */
2279 req->bulk_data = NULL;
2280
2281 /* save data on the receive list */
2282 usbsacm_put_tail(&acm_port->acm_rx_mp, data);
2283
2284 /* invoke GSD receive callback */
2285 if (acm_port->acm_cb.cb_rx) {
2286 acm_port->acm_cb.cb_rx(acm_port->acm_cb.cb_arg);
2287 }
2288 mutex_enter(&acm_port->acm_port_mutex);
2289 }
2290 mutex_exit(&acm_port->acm_port_mutex);
2291
2292 usb_free_bulk_req(req);
2293
2294 /* receive more */
2295 mutex_enter(&acm_port->acm_port_mutex);
2296 if (((acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) ||
2297 (acm_port->acm_bulkin_state == USBSACM_PIPE_IDLE)) &&
2298 (acm_port->acm_port_state == USBSACM_PORT_OPEN) &&
2299 (acmp->acm_dev_state == USB_DEV_ONLINE)) {
2300 if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
2301 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2302 "usbsacm_bulkin_cb: restart rx fail "
2303 "acm_port_state = %d", acm_port->acm_port_state);
2304 }
2305 } else if (acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) {
2306 acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
2307 }
2308 mutex_exit(&acm_port->acm_port_mutex);
2309 }
2310
2311
2312 /*
2313 * usbsacm_bulkout_cb:
2314 * Bulk Out regular and exeception callback;
2315 * USBA framework will call this callback function
2316 * after deal with bulkout request.
2317 */
2318 /*ARGSUSED*/
2319 static void
usbsacm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)2320 usbsacm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
2321 {
2322 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->bulk_client_private;
2323 usbsacm_state_t *acmp = acm_port->acm_device;
2324 int data_len;
2325 mblk_t *data = req->bulk_data;
2326
2327 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
2328 "usbsacm_bulkout_cb: acmp = 0x%p", (void *)acmp);
2329
2330 data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
2331
2332 /* put untransferred residue back on the transfer list */
2333 if (req->bulk_completion_reason && (data_len > 0)) {
2334 usbsacm_put_head(&acm_port->acm_tx_mp, data);
2335 req->bulk_data = NULL;
2336 }
2337
2338 usb_free_bulk_req(req);
2339
2340 /* invoke GSD transmit callback */
2341 if (acm_port->acm_cb.cb_tx) {
2342 acm_port->acm_cb.cb_tx(acm_port->acm_cb.cb_arg);
2343 }
2344
2345 /* send more */
2346 mutex_enter(&acm_port->acm_port_mutex);
2347 acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
2348 if (acm_port->acm_tx_mp == NULL) {
2349 cv_broadcast(&acm_port->acm_tx_cv);
2350 } else {
2351 usbsacm_tx_start(acm_port);
2352 }
2353 mutex_exit(&acm_port->acm_port_mutex);
2354 }
2355
2356
2357 /*
2358 * usbsacm_rx_start:
2359 * start data receipt
2360 */
2361 static int
usbsacm_rx_start(usbsacm_port_t * acm_port)2362 usbsacm_rx_start(usbsacm_port_t *acm_port)
2363 {
2364 usbsacm_state_t *acmp = acm_port->acm_device;
2365 usb_bulk_req_t *br;
2366 int rval = USB_FAILURE;
2367 int data_len;
2368
2369 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
2370 "usbsacm_rx_start: acm_xfer_sz = 0x%lx acm_bulkin_size = 0x%lx",
2371 acmp->acm_xfer_sz, acm_port->acm_bulkin_size);
2372
2373 acm_port->acm_bulkin_state = USBSACM_PIPE_BUSY;
2374 /*
2375 * Qualcomm CDMA card won't response the first request,
2376 * if the following code don't multiply by 2.
2377 */
2378 data_len = min(acmp->acm_xfer_sz, acm_port->acm_bulkin_size * 2);
2379 mutex_exit(&acm_port->acm_port_mutex);
2380
2381 br = usb_alloc_bulk_req(acmp->acm_dip, data_len, USB_FLAGS_SLEEP);
2382 if (br == NULL) {
2383 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2384 "usbsacm_rx_start: allocate bulk request failed");
2385
2386 mutex_enter(&acm_port->acm_port_mutex);
2387
2388 return (USB_FAILURE);
2389 }
2390 /* initialize bulk in request. */
2391 br->bulk_len = data_len;
2392 br->bulk_timeout = USBSACM_BULKIN_TIMEOUT;
2393 br->bulk_cb = usbsacm_bulkin_cb;
2394 br->bulk_exc_cb = usbsacm_bulkin_cb;
2395 br->bulk_client_private = (usb_opaque_t)acm_port;
2396 br->bulk_attributes = USB_ATTRS_AUTOCLEARING
2397 | USB_ATTRS_SHORT_XFER_OK;
2398
2399 rval = usb_pipe_bulk_xfer(acm_port->acm_bulkin_ph, br, 0);
2400
2401 mutex_enter(&acm_port->acm_port_mutex);
2402 if (rval != USB_SUCCESS) {
2403 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2404 "usbsacm_rx_start: bulk transfer failed %d", rval);
2405 usb_free_bulk_req(br);
2406 acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
2407 }
2408
2409 return (rval);
2410 }
2411
2412
2413 /*
2414 * usbsacm_tx_start:
2415 * start data transmit
2416 */
2417 static void
usbsacm_tx_start(usbsacm_port_t * acm_port)2418 usbsacm_tx_start(usbsacm_port_t *acm_port)
2419 {
2420 int len; /* bytes we can transmit */
2421 mblk_t *data; /* data to be transmitted */
2422 int data_len; /* bytes in 'data' */
2423 mblk_t *mp; /* current msgblk */
2424 int copylen; /* bytes copy from 'mp' to 'data' */
2425 int rval;
2426 usbsacm_state_t *acmp = acm_port->acm_device;
2427
2428 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
2429 "usbsacm_tx_start: ");
2430
2431 /* check the transmitted data. */
2432 if (acm_port->acm_tx_mp == NULL) {
2433 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2434 "usbsacm_tx_start: acm_tx_mp is NULL");
2435
2436 return;
2437 }
2438
2439 /* check pipe status */
2440 if (acm_port->acm_bulkout_state != USBSACM_PIPE_IDLE) {
2441
2442 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2443 "usbsacm_tx_start: error state in bulkout endpoint");
2444
2445 return;
2446 }
2447 ASSERT(MBLKL(acm_port->acm_tx_mp) > 0);
2448
2449 /* send as much data as port can receive */
2450 len = min(msgdsize(acm_port->acm_tx_mp), acmp->acm_xfer_sz);
2451
2452 if (len == 0) {
2453 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2454 "usbsacm_tx_start: data len is 0");
2455
2456 return;
2457 }
2458
2459 /* allocate memory for sending data. */
2460 if ((data = allocb(len, BPRI_LO)) == NULL) {
2461 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2462 "usbsacm_tx_start: failure in allocate memory");
2463
2464 return;
2465 }
2466
2467 /*
2468 * copy no more than 'len' bytes from mblk chain to transmit mblk 'data'
2469 */
2470 data_len = 0;
2471 while ((data_len < len) && acm_port->acm_tx_mp) {
2472 /* Get the first mblk from chain. */
2473 mp = acm_port->acm_tx_mp;
2474 copylen = min(MBLKL(mp), len - data_len);
2475 bcopy(mp->b_rptr, data->b_wptr, copylen);
2476 mp->b_rptr += copylen;
2477 data->b_wptr += copylen;
2478 data_len += copylen;
2479
2480 if (MBLKL(mp) < 1) {
2481 acm_port->acm_tx_mp = unlinkb(mp);
2482 freeb(mp);
2483 } else {
2484 ASSERT(data_len == len);
2485 }
2486 }
2487
2488 if (data_len <= 0) {
2489 freeb(data);
2490
2491 return;
2492 }
2493
2494 acm_port->acm_bulkout_state = USBSACM_PIPE_BUSY;
2495
2496 mutex_exit(&acm_port->acm_port_mutex);
2497 /* send request. */
2498 rval = usbsacm_send_data(acm_port, data);
2499 mutex_enter(&acm_port->acm_port_mutex);
2500
2501 /*
2502 * If send failed, retransmit data when acm_tx_mp is null.
2503 */
2504 if (rval != USB_SUCCESS) {
2505 acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
2506 if (acm_port->acm_tx_mp == NULL) {
2507 usbsacm_put_head(&acm_port->acm_tx_mp, data);
2508 }
2509 }
2510 }
2511
2512
2513 /*
2514 * usbsacm_send_data:
2515 * data transfer
2516 */
2517 static int
usbsacm_send_data(usbsacm_port_t * acm_port,mblk_t * data)2518 usbsacm_send_data(usbsacm_port_t *acm_port, mblk_t *data)
2519 {
2520 usbsacm_state_t *acmp = acm_port->acm_device;
2521 usb_bulk_req_t *br;
2522 int rval;
2523 int data_len = MBLKL(data);
2524
2525 USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
2526 "usbsacm_send_data: data address is 0x%p, length = %d",
2527 (void *)data, data_len);
2528
2529 br = usb_alloc_bulk_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
2530 if (br == NULL) {
2531 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
2532 "usbsacm_send_data: alloc req failed.");
2533
2534 return (USB_FAILURE);
2535 }
2536
2537 /* initialize the bulk out request */
2538 br->bulk_data = data;
2539 br->bulk_len = data_len;
2540 br->bulk_timeout = USBSACM_BULKOUT_TIMEOUT;
2541 br->bulk_cb = usbsacm_bulkout_cb;
2542 br->bulk_exc_cb = usbsacm_bulkout_cb;
2543 br->bulk_client_private = (usb_opaque_t)acm_port;
2544 br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
2545
2546 rval = usb_pipe_bulk_xfer(acm_port->acm_bulkout_ph, br, 0);
2547
2548 if (rval != USB_SUCCESS) {
2549 USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
2550 "usbsacm_send_data: Send Data failed.");
2551
2552 /*
2553 * Don't free it in usb_free_bulk_req because it will
2554 * be linked in usbsacm_put_head
2555 */
2556 br->bulk_data = NULL;
2557
2558 usb_free_bulk_req(br);
2559 }
2560
2561 return (rval);
2562 }
2563
2564 /*
2565 * usbsacm_wait_tx_drain:
2566 * wait until local tx buffer drains.
2567 * 'timeout' is in seconds, zero means wait forever
2568 */
2569 static int
usbsacm_wait_tx_drain(usbsacm_port_t * acm_port,int timeout)2570 usbsacm_wait_tx_drain(usbsacm_port_t *acm_port, int timeout)
2571 {
2572 clock_t until;
2573 int over = 0;
2574
2575 until = ddi_get_lbolt() + drv_usectohz(1000 * 1000 * timeout);
2576
2577 while (acm_port->acm_tx_mp && !over) {
2578 if (timeout > 0) {
2579 over = (cv_timedwait_sig(&acm_port->acm_tx_cv,
2580 &acm_port->acm_port_mutex, until) <= 0);
2581 } else {
2582 over = (cv_wait_sig(&acm_port->acm_tx_cv,
2583 &acm_port->acm_port_mutex) == 0);
2584 }
2585 }
2586
2587 return ((acm_port->acm_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE);
2588 }
2589
2590
2591 /*
2592 * usbsacm_req_write:
2593 * send command over control pipe
2594 */
2595 static int
usbsacm_req_write(usbsacm_port_t * acm_port,uchar_t request,uint16_t value,mblk_t ** data)2596 usbsacm_req_write(usbsacm_port_t *acm_port, uchar_t request, uint16_t value,
2597 mblk_t **data)
2598 {
2599 usbsacm_state_t *acmp = acm_port->acm_device;
2600 usb_ctrl_setup_t setup;
2601 usb_cb_flags_t cb_flags;
2602 usb_cr_t cr;
2603
2604 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
2605 "usbsacm_req_write: ");
2606
2607 /* initialize the control request. */
2608 setup.bmRequestType = USBSACM_REQ_WRITE_IF;
2609 setup.bRequest = request;
2610 setup.wValue = value;
2611 setup.wIndex = acm_port->acm_ctrl_if_no;
2612 setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
2613 setup.attrs = 0;
2614
2615 return (usb_pipe_ctrl_xfer_wait(acmp->acm_def_ph, &setup, data,
2616 &cr, &cb_flags, 0));
2617 }
2618
2619
2620 /*
2621 * usbsacm_set_line_coding:
2622 * Send USB_CDC_REQ_SET_LINE_CODING request
2623 */
2624 static int
usbsacm_set_line_coding(usbsacm_port_t * acm_port,usb_cdc_line_coding_t * lc)2625 usbsacm_set_line_coding(usbsacm_port_t *acm_port, usb_cdc_line_coding_t *lc)
2626 {
2627 mblk_t *bp;
2628 int ret;
2629
2630 /* allocate mblk and copy supplied structure into it */
2631 if ((bp = allocb(USB_CDC_LINE_CODING_LEN, BPRI_HI)) == NULL) {
2632
2633 return (USB_NO_RESOURCES);
2634 }
2635
2636 #ifndef __lock_lint /* warlock gets confused here */
2637 /* LINTED E_BAD_PTR_CAST_ALIGN */
2638 *((usb_cdc_line_coding_t *)bp->b_wptr) = *lc;
2639 bp->b_wptr += USB_CDC_LINE_CODING_LEN;
2640 #endif
2641
2642 ret = usbsacm_req_write(acm_port, USB_CDC_REQ_SET_LINE_CODING, 0, &bp);
2643
2644 if (bp != NULL) {
2645 freeb(bp);
2646 }
2647
2648 return (ret);
2649 }
2650
2651
2652
2653 /*
2654 * usbsacm_mctl2reg:
2655 * Set Modem control status
2656 */
2657 static void
usbsacm_mctl2reg(int mask,int val,uint8_t * line_ctl)2658 usbsacm_mctl2reg(int mask, int val, uint8_t *line_ctl)
2659 {
2660 if (mask & TIOCM_RTS) {
2661 if (val & TIOCM_RTS) {
2662 *line_ctl |= USB_CDC_ACM_CONTROL_RTS;
2663 } else {
2664 *line_ctl &= ~USB_CDC_ACM_CONTROL_RTS;
2665 }
2666 }
2667 if (mask & TIOCM_DTR) {
2668 if (val & TIOCM_DTR) {
2669 *line_ctl |= USB_CDC_ACM_CONTROL_DTR;
2670 } else {
2671 *line_ctl &= ~USB_CDC_ACM_CONTROL_DTR;
2672 }
2673 }
2674 }
2675
2676
2677 /*
2678 * usbsacm_reg2mctl:
2679 * Get Modem control status
2680 */
2681 static int
usbsacm_reg2mctl(uint8_t line_ctl)2682 usbsacm_reg2mctl(uint8_t line_ctl)
2683 {
2684 int val = 0;
2685
2686 if (line_ctl & USB_CDC_ACM_CONTROL_RTS) {
2687 val |= TIOCM_RTS;
2688 }
2689 if (line_ctl & USB_CDC_ACM_CONTROL_DTR) {
2690 val |= TIOCM_DTR;
2691 }
2692 if (line_ctl & USB_CDC_ACM_CONTROL_DSR) {
2693 val |= TIOCM_DSR;
2694 }
2695 if (line_ctl & USB_CDC_ACM_CONTROL_RNG) {
2696 val |= TIOCM_RI;
2697 }
2698
2699 return (val);
2700 }
2701
2702
2703 /*
2704 * misc routines
2705 * -------------
2706 *
2707 */
2708
2709 /*
2710 * usbsacm_put_tail:
2711 * link a message block to tail of message
2712 * account for the case when message is null
2713 */
2714 static void
usbsacm_put_tail(mblk_t ** mpp,mblk_t * bp)2715 usbsacm_put_tail(mblk_t **mpp, mblk_t *bp)
2716 {
2717 if (*mpp) {
2718 linkb(*mpp, bp);
2719 } else {
2720 *mpp = bp;
2721 }
2722 }
2723
2724
2725 /*
2726 * usbsacm_put_head:
2727 * put a message block at the head of the message
2728 * account for the case when message is null
2729 */
2730 static void
usbsacm_put_head(mblk_t ** mpp,mblk_t * bp)2731 usbsacm_put_head(mblk_t **mpp, mblk_t *bp)
2732 {
2733 if (*mpp) {
2734 linkb(bp, *mpp);
2735 }
2736 *mpp = bp;
2737 }
2738
2739
2740 /*
2741 * power management
2742 * ----------------
2743 *
2744 * usbsacm_create_pm_components:
2745 * create PM components
2746 */
2747 static int
usbsacm_create_pm_components(usbsacm_state_t * acmp)2748 usbsacm_create_pm_components(usbsacm_state_t *acmp)
2749 {
2750 dev_info_t *dip = acmp->acm_dip;
2751 usbsacm_pm_t *pm;
2752 uint_t pwr_states;
2753 usb_dev_descr_t *dev_descr;
2754
2755 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
2756 "usbsacm_create_pm_components: ");
2757
2758 if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
2759 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
2760 "usbsacm_create_pm_components: failed");
2761
2762 return (USB_SUCCESS);
2763 }
2764
2765 pm = acmp->acm_pm =
2766 (usbsacm_pm_t *)kmem_zalloc(sizeof (usbsacm_pm_t), KM_SLEEP);
2767
2768 pm->pm_pwr_states = (uint8_t)pwr_states;
2769 pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
2770 /*
2771 * Qualcomm CDMA card won't response the following control commands
2772 * after receive USB_REMOTE_WAKEUP_ENABLE. So we just set
2773 * pm_wakeup_enable to 0 for this specific device.
2774 */
2775 dev_descr = acmp->acm_dev_data->dev_descr;
2776 if (dev_descr->idVendor == 0x5c6 && dev_descr->idProduct == 0x3100) {
2777 pm->pm_wakeup_enabled = 0;
2778 } else {
2779 pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
2780 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
2781 }
2782
2783 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2784
2785 return (USB_SUCCESS);
2786 }
2787
2788
2789 /*
2790 * usbsacm_destroy_pm_components:
2791 * destroy PM components
2792 */
2793 static void
usbsacm_destroy_pm_components(usbsacm_state_t * acmp)2794 usbsacm_destroy_pm_components(usbsacm_state_t *acmp)
2795 {
2796 usbsacm_pm_t *pm = acmp->acm_pm;
2797 dev_info_t *dip = acmp->acm_dip;
2798 int rval;
2799
2800 USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
2801 "usbsacm_destroy_pm_components: ");
2802
2803 if (acmp->acm_dev_state != USB_DEV_DISCONNECTED) {
2804 if (pm->pm_wakeup_enabled) {
2805 rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2806 if (rval != DDI_SUCCESS) {
2807 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
2808 "usbsacm_destroy_pm_components: "
2809 "raising power failed (%d)", rval);
2810 }
2811
2812 rval = usb_handle_remote_wakeup(dip,
2813 USB_REMOTE_WAKEUP_DISABLE);
2814 if (rval != USB_SUCCESS) {
2815 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
2816 "usbsacm_destroy_pm_components: "
2817 "disable remote wakeup failed (%d)", rval);
2818 }
2819 }
2820
2821 (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
2822 }
2823 kmem_free((caddr_t)pm, sizeof (usbsacm_pm_t));
2824 acmp->acm_pm = NULL;
2825 }
2826
2827
2828 /*
2829 * usbsacm_pm_set_busy:
2830 * mark device busy and raise power
2831 */
2832 static void
usbsacm_pm_set_busy(usbsacm_state_t * acmp)2833 usbsacm_pm_set_busy(usbsacm_state_t *acmp)
2834 {
2835 usbsacm_pm_t *pm = acmp->acm_pm;
2836 dev_info_t *dip = acmp->acm_dip;
2837 int rval;
2838
2839 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
2840 "usbsacm_pm_set_busy: pm = 0x%p", (void *)pm);
2841
2842 if (pm == NULL) {
2843
2844 return;
2845 }
2846
2847 mutex_enter(&acmp->acm_mutex);
2848 /* if already marked busy, just increment the counter */
2849 if (pm->pm_busy_cnt++ > 0) {
2850 mutex_exit(&acmp->acm_mutex);
2851
2852 return;
2853 }
2854
2855 (void) pm_busy_component(dip, 0);
2856
2857 if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
2858 mutex_exit(&acmp->acm_mutex);
2859
2860 return;
2861 }
2862
2863 /* need to raise power */
2864 pm->pm_raise_power = B_TRUE;
2865 mutex_exit(&acmp->acm_mutex);
2866
2867 rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2868 if (rval != DDI_SUCCESS) {
2869 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
2870 "usbsacm_pm_set_busy: raising power failed");
2871 }
2872
2873 mutex_enter(&acmp->acm_mutex);
2874 pm->pm_raise_power = B_FALSE;
2875 mutex_exit(&acmp->acm_mutex);
2876 }
2877
2878
2879 /*
2880 * usbsacm_pm_set_idle:
2881 * mark device idle
2882 */
2883 static void
usbsacm_pm_set_idle(usbsacm_state_t * acmp)2884 usbsacm_pm_set_idle(usbsacm_state_t *acmp)
2885 {
2886 usbsacm_pm_t *pm = acmp->acm_pm;
2887 dev_info_t *dip = acmp->acm_dip;
2888
2889 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
2890 "usbsacm_pm_set_idle: ");
2891
2892 if (pm == NULL) {
2893
2894 return;
2895 }
2896
2897 /*
2898 * if more ports use the device, do not mark as yet
2899 */
2900 mutex_enter(&acmp->acm_mutex);
2901 if (--pm->pm_busy_cnt > 0) {
2902 mutex_exit(&acmp->acm_mutex);
2903
2904 return;
2905 }
2906
2907 if (pm) {
2908 (void) pm_idle_component(dip, 0);
2909 }
2910 mutex_exit(&acmp->acm_mutex);
2911 }
2912
2913
2914 /*
2915 * usbsacm_pwrlvl0:
2916 * Functions to handle power transition for OS levels 0 -> 3
2917 * The same level as OS state, different from USB state
2918 */
2919 static int
usbsacm_pwrlvl0(usbsacm_state_t * acmp)2920 usbsacm_pwrlvl0(usbsacm_state_t *acmp)
2921 {
2922 int rval;
2923 int i;
2924 usbsacm_port_t *cur_port = acmp->acm_ports;
2925
2926 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
2927 "usbsacm_pwrlvl0: ");
2928
2929 switch (acmp->acm_dev_state) {
2930 case USB_DEV_ONLINE:
2931 /* issue USB D3 command to the device */
2932 rval = usb_set_device_pwrlvl3(acmp->acm_dip);
2933 ASSERT(rval == USB_SUCCESS);
2934
2935 if (cur_port != NULL) {
2936 for (i = 0; i < acmp->acm_port_cnt; i++) {
2937 cur_port = &acmp->acm_ports[i];
2938 if (cur_port->acm_intr_ph != NULL &&
2939 cur_port->acm_port_state !=
2940 USBSACM_PORT_CLOSED) {
2941
2942 mutex_exit(&acmp->acm_mutex);
2943 usb_pipe_stop_intr_polling(
2944 cur_port->acm_intr_ph,
2945 USB_FLAGS_SLEEP);
2946 mutex_enter(&acmp->acm_mutex);
2947
2948 mutex_enter(&cur_port->acm_port_mutex);
2949 cur_port->acm_intr_state =
2950 USBSACM_PIPE_IDLE;
2951 mutex_exit(&cur_port->acm_port_mutex);
2952 }
2953 }
2954 }
2955
2956 acmp->acm_dev_state = USB_DEV_PWRED_DOWN;
2957 acmp->acm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
2958
2959 /* FALLTHRU */
2960 case USB_DEV_DISCONNECTED:
2961 case USB_DEV_SUSPENDED:
2962 /* allow a disconnect/cpr'ed device to go to lower power */
2963
2964 return (USB_SUCCESS);
2965 case USB_DEV_PWRED_DOWN:
2966 default:
2967 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
2968 "usbsacm_pwrlvl0: illegal device state");
2969
2970 return (USB_FAILURE);
2971 }
2972 }
2973
2974
2975 /*
2976 * usbsacm_pwrlvl1:
2977 * Functions to handle power transition for OS levels 1 -> 2
2978 */
2979 static int
usbsacm_pwrlvl1(usbsacm_state_t * acmp)2980 usbsacm_pwrlvl1(usbsacm_state_t *acmp)
2981 {
2982 /* issue USB D2 command to the device */
2983 (void) usb_set_device_pwrlvl2(acmp->acm_dip);
2984
2985 return (USB_FAILURE);
2986 }
2987
2988
2989 /*
2990 * usbsacm_pwrlvl2:
2991 * Functions to handle power transition for OS levels 2 -> 1
2992 */
2993 static int
usbsacm_pwrlvl2(usbsacm_state_t * acmp)2994 usbsacm_pwrlvl2(usbsacm_state_t *acmp)
2995 {
2996 /* issue USB D1 command to the device */
2997 (void) usb_set_device_pwrlvl1(acmp->acm_dip);
2998
2999 return (USB_FAILURE);
3000 }
3001
3002
3003 /*
3004 * usbsacm_pwrlvl3:
3005 * Functions to handle power transition for OS levels 3 -> 0
3006 * The same level as OS state, different from USB state
3007 */
3008 static int
usbsacm_pwrlvl3(usbsacm_state_t * acmp)3009 usbsacm_pwrlvl3(usbsacm_state_t *acmp)
3010 {
3011 int rval;
3012 int i;
3013 usbsacm_port_t *cur_port = acmp->acm_ports;
3014
3015 USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
3016 "usbsacm_pwrlvl3: ");
3017
3018 switch (acmp->acm_dev_state) {
3019 case USB_DEV_PWRED_DOWN:
3020 /* Issue USB D0 command to the device here */
3021 rval = usb_set_device_pwrlvl0(acmp->acm_dip);
3022 ASSERT(rval == USB_SUCCESS);
3023
3024 if (cur_port != NULL) {
3025 for (i = 0; i < acmp->acm_port_cnt; i++) {
3026 cur_port = &acmp->acm_ports[i];
3027 if (cur_port->acm_intr_ph != NULL &&
3028 cur_port->acm_port_state !=
3029 USBSACM_PORT_CLOSED) {
3030
3031 mutex_exit(&acmp->acm_mutex);
3032 usbsacm_pipe_start_polling(cur_port);
3033 mutex_enter(&acmp->acm_mutex);
3034 }
3035 }
3036 }
3037
3038 acmp->acm_dev_state = USB_DEV_ONLINE;
3039 acmp->acm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
3040
3041 /* FALLTHRU */
3042 case USB_DEV_ONLINE:
3043 /* we are already in full power */
3044
3045 /* FALLTHRU */
3046 case USB_DEV_DISCONNECTED:
3047 case USB_DEV_SUSPENDED:
3048
3049 return (USB_SUCCESS);
3050 default:
3051 USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
3052 "usbsacm_pwrlvl3: illegal device state");
3053
3054 return (USB_FAILURE);
3055 }
3056 }
3057
3058
3059 /*
3060 * usbsacm_pipe_start_polling:
3061 * start polling on the interrupt pipe
3062 */
3063 static void
usbsacm_pipe_start_polling(usbsacm_port_t * acm_port)3064 usbsacm_pipe_start_polling(usbsacm_port_t *acm_port)
3065 {
3066 usb_intr_req_t *intr;
3067 int rval;
3068 usbsacm_state_t *acmp = acm_port->acm_device;
3069
3070 USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
3071 "usbsacm_pipe_start_polling: ");
3072
3073 if (acm_port->acm_intr_ph == NULL) {
3074
3075 return;
3076 }
3077
3078 intr = usb_alloc_intr_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
3079
3080 /*
3081 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
3082 * called with SLEEP flag.
3083 */
3084 if (!intr) {
3085 USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
3086 "usbsacm_pipe_start_polling: alloc req failed.");
3087
3088 return;
3089 }
3090
3091 /* initialize the interrupt request. */
3092 intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
3093 USB_ATTRS_AUTOCLEARING;
3094 mutex_enter(&acm_port->acm_port_mutex);
3095 intr->intr_len = acm_port->acm_intr_ep_descr.wMaxPacketSize;
3096 mutex_exit(&acm_port->acm_port_mutex);
3097 intr->intr_client_private = (usb_opaque_t)acm_port;
3098 intr->intr_cb = usbsacm_intr_cb;
3099 intr->intr_exc_cb = usbsacm_intr_ex_cb;
3100
3101 rval = usb_pipe_intr_xfer(acm_port->acm_intr_ph, intr, USB_FLAGS_SLEEP);
3102
3103 mutex_enter(&acm_port->acm_port_mutex);
3104 if (rval == USB_SUCCESS) {
3105 acm_port->acm_intr_state = USBSACM_PIPE_BUSY;
3106 } else {
3107 usb_free_intr_req(intr);
3108 acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
3109 USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
3110 "usbsacm_pipe_start_polling: failed (%d)", rval);
3111 }
3112 mutex_exit(&acm_port->acm_port_mutex);
3113 }
3114
3115
3116 /*
3117 * usbsacm_intr_cb:
3118 * interrupt pipe normal callback
3119 */
3120 /*ARGSUSED*/
3121 static void
usbsacm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)3122 usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
3123 {
3124 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->intr_client_private;
3125 usbsacm_state_t *acmp = acm_port->acm_device;
3126 mblk_t *data = req->intr_data;
3127 int data_len;
3128
3129 USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
3130 "usbsacm_intr_cb: ");
3131
3132 data_len = (data) ? MBLKL(data) : 0;
3133
3134 /* check data length */
3135 if (data_len < 8) {
3136 USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
3137 "usbsacm_intr_cb: %d packet too short", data_len);
3138 usb_free_intr_req(req);
3139
3140 return;
3141 }
3142 req->intr_data = NULL;
3143 usb_free_intr_req(req);
3144
3145 mutex_enter(&acm_port->acm_port_mutex);
3146 /* parse interrupt data. */
3147 usbsacm_parse_intr_data(acm_port, data);
3148 mutex_exit(&acm_port->acm_port_mutex);
3149 }
3150
3151
3152 /*
3153 * usbsacm_intr_ex_cb:
3154 * interrupt pipe exception callback
3155 */
3156 /*ARGSUSED*/
3157 static void
usbsacm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)3158 usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
3159 {
3160 usbsacm_port_t *acm_port = (usbsacm_port_t *)req->intr_client_private;
3161 usbsacm_state_t *acmp = acm_port->acm_device;
3162 usb_cr_t cr = req->intr_completion_reason;
3163
3164 USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
3165 "usbsacm_intr_ex_cb: ");
3166
3167 usb_free_intr_req(req);
3168
3169 /*
3170 * If completion reason isn't USB_CR_PIPE_CLOSING and
3171 * USB_CR_STOPPED_POLLING, restart polling.
3172 */
3173 if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
3174 mutex_enter(&acmp->acm_mutex);
3175
3176 if (acmp->acm_dev_state != USB_DEV_ONLINE) {
3177
3178 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3179 "usbsacm_intr_ex_cb: state = %d",
3180 acmp->acm_dev_state);
3181
3182 mutex_exit(&acmp->acm_mutex);
3183
3184 return;
3185 }
3186 mutex_exit(&acmp->acm_mutex);
3187
3188 usbsacm_pipe_start_polling(acm_port);
3189 }
3190 }
3191
3192
3193 /*
3194 * usbsacm_parse_intr_data:
3195 * Parse data received from interrupt callback
3196 */
3197 static void
usbsacm_parse_intr_data(usbsacm_port_t * acm_port,mblk_t * data)3198 usbsacm_parse_intr_data(usbsacm_port_t *acm_port, mblk_t *data)
3199 {
3200 usbsacm_state_t *acmp = acm_port->acm_device;
3201 uint8_t bmRequestType;
3202 uint8_t bNotification;
3203 uint16_t wValue;
3204 uint16_t wLength;
3205 uint16_t wData;
3206
3207 USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
3208 "usbsacm_parse_intr_data: ");
3209
3210 bmRequestType = data->b_rptr[0];
3211 bNotification = data->b_rptr[1];
3212 /*
3213 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
3214 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
3215 * mLength is 2. So we directly get the value from the byte.
3216 */
3217 wValue = data->b_rptr[2];
3218 wLength = data->b_rptr[6];
3219
3220 if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
3221 USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
3222 "usbsacm_parse_intr_data: unknown request type - 0x%x",
3223 bmRequestType);
3224
3225 freemsg(data);
3226
3227 return;
3228 }
3229
3230 /*
3231 * Check the return value of device
3232 */
3233 switch (bNotification) {
3234 case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
3235 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3236 "usbsacm_parse_intr_data: %s network!",
3237 wValue ? "connected to" :"disconnected from");
3238
3239 break;
3240 case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
3241 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3242 "usbsacm_parse_intr_data: A response is a available.");
3243
3244 break;
3245 case USB_CDC_NOTIFICATION_SERIAL_STATE:
3246 /* check the parameter's length. */
3247 if (wLength != 2) {
3248
3249 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3250 "usbsacm_parse_intr_data: error data length.");
3251 } else {
3252 /*
3253 * The Data field is a bitmapped value that contains
3254 * the current state of carrier detect, transmission
3255 * carrier, break, ring signal and device overrun
3256 * error.
3257 */
3258 wData = data->b_rptr[8];
3259 /*
3260 * Check the serial state of the current port.
3261 */
3262 if (wData & USB_CDC_ACM_CONTROL_DCD) {
3263
3264 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3265 "usbsacm_parse_intr_data: "
3266 "receiver carrier is set.");
3267 }
3268 if (wData & USB_CDC_ACM_CONTROL_DSR) {
3269
3270 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3271 "usbsacm_parse_intr_data: "
3272 "transmission carrier is set.");
3273
3274 acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_DSR;
3275 }
3276 if (wData & USB_CDC_ACM_CONTROL_BREAK) {
3277
3278 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3279 "usbsacm_parse_intr_data: "
3280 "break detection mechanism is set.");
3281 }
3282 if (wData & USB_CDC_ACM_CONTROL_RNG) {
3283
3284 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3285 "usbsacm_parse_intr_data: "
3286 "ring signal detection is set.");
3287
3288 acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_RNG;
3289 }
3290 if (wData & USB_CDC_ACM_CONTROL_FRAMING) {
3291
3292 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3293 "usbsacm_parse_intr_data: "
3294 "A framing error has occurred.");
3295 }
3296 if (wData & USB_CDC_ACM_CONTROL_PARITY) {
3297
3298 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3299 "usbsacm_parse_intr_data: "
3300 "A parity error has occurred.");
3301 }
3302 if (wData & USB_CDC_ACM_CONTROL_OVERRUN) {
3303
3304 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3305 "usbsacm_parse_intr_data: "
3306 "Received data has been discarded "
3307 "due to overrun.");
3308 }
3309 }
3310
3311 break;
3312 default:
3313 USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
3314 "usbsacm_parse_intr_data: unknown notification - 0x%x!",
3315 bNotification);
3316
3317 break;
3318 }
3319
3320 freemsg(data);
3321 }
3322