usbser.c revision 9354:9559ac454e7e
1254721Semaste/* 2254721Semaste * CDDL HEADER START 3254721Semaste * 4254721Semaste * The contents of this file are subject to the terms of the 5254721Semaste * Common Development and Distribution License (the "License"). 6254721Semaste * You may not use this file except in compliance with the License. 7254721Semaste * 8254721Semaste * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9254721Semaste * or http://www.opensolaris.org/os/licensing. 10254721Semaste * See the License for the specific language governing permissions 11254721Semaste * and limitations under the License. 12254721Semaste * 13254721Semaste * When distributing Covered Code, include this CDDL HEADER in each 14254721Semaste * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15254721Semaste * If applicable, add the following below this CDDL HEADER, with the 16254721Semaste * fields enclosed by brackets "[]" replaced with your own identifying 17254721Semaste * information: Portions Copyright [yyyy] [name of copyright owner] 18254721Semaste * 19254721Semaste * CDDL HEADER END 20254721Semaste */ 21254721Semaste/* 22254721Semaste * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23254721Semaste * Use is subject to license terms. 24254721Semaste */ 25254721Semaste 26254721Semaste 27254721Semaste/* 28254721Semaste * 29254721Semaste * USB generic serial driver (GSD) 30254721Semaste * 31254721Semaste */ 32254721Semaste#include <sys/types.h> 33254721Semaste#include <sys/param.h> 34254721Semaste#include <sys/stream.h> 35254721Semaste#include <sys/stropts.h> 36254721Semaste#include <sys/errno.h> 37254721Semaste#include <sys/cred.h> 38254721Semaste#include <sys/conf.h> 39254721Semaste#include <sys/stat.h> 40254721Semaste#include <sys/modctl.h> 41254721Semaste#include <sys/ddi.h> 42254721Semaste#include <sys/sunddi.h> 43254721Semaste#include <sys/sunndi.h> 44254721Semaste#include <sys/termio.h> 45254721Semaste#include <sys/termiox.h> 46254721Semaste#include <sys/stropts.h> 47254721Semaste#include <sys/stream.h> 48254721Semaste#include <sys/strsubr.h> 49254721Semaste#include <sys/strsun.h> 50254721Semaste#include <sys/strtty.h> 51254721Semaste#include <sys/policy.h> 52254721Semaste#include <sys/consdev.h> 53254721Semaste 54254721Semaste#include <sys/usb/usba.h> 55254721Semaste#include <sys/usb/clients/usbser/usbser_var.h> 56254721Semaste#include <sys/usb/clients/usbser/usbser_dsdi.h> 57254721Semaste#include <sys/usb/clients/usbser/usbser_rseq.h> 58254721Semaste#include <sys/usb/usba/genconsole.h> 59254721Semaste 60254721Semaste/* autoconfiguration subroutines */ 61254721Semastestatic int usbser_rseq_do_cb(rseq_t *, int, uintptr_t); 62254721Semastestatic int usbser_free_soft_state(usbser_state_t *); 63254721Semastestatic int usbser_init_soft_state(usbser_state_t *); 64254721Semastestatic int usbser_fini_soft_state(usbser_state_t *); 65254721Semastestatic int usbser_attach_dev(usbser_state_t *); 66254721Semastestatic void usbser_detach_dev(usbser_state_t *); 67254721Semastestatic int usbser_attach_ports(usbser_state_t *); 68254721Semastestatic int usbser_create_port_minor_nodes(usbser_state_t *, int); 69254721Semastestatic void usbser_detach_ports(usbser_state_t *); 70254721Semastestatic int usbser_create_taskq(usbser_state_t *); 71254721Semastestatic void usbser_destroy_taskq(usbser_state_t *); 72254721Semastestatic void usbser_set_dev_state_init(usbser_state_t *); 73254721Semaste 74254721Semaste/* hotplugging and power management */ 75254721Semastestatic int usbser_disconnect_cb(dev_info_t *); 76254721Semastestatic int usbser_reconnect_cb(dev_info_t *); 77254721Semastestatic void usbser_disconnect_ports(usbser_state_t *); 78254721Semastestatic int usbser_cpr_suspend(dev_info_t *); 79254721Semastestatic int usbser_suspend_ports(usbser_state_t *); 80254721Semastestatic void usbser_cpr_resume(dev_info_t *); 81254721Semastestatic int usbser_restore_device_state(usbser_state_t *); 82254721Semastestatic void usbser_restore_ports_state(usbser_state_t *); 83254721Semaste 84254721Semaste/* STREAMS subroutines */ 85254721Semastestatic int usbser_open_setup(queue_t *, usbser_port_t *, int, int, 86254721Semaste cred_t *); 87254721Semastestatic int usbser_open_init(usbser_port_t *, int); 88254721Semastestatic void usbser_check_port_props(usbser_port_t *); 89254721Semastestatic void usbser_open_fini(usbser_port_t *); 90254721Semastestatic int usbser_open_line_setup(usbser_port_t *, int, int); 91254721Semastestatic int usbser_open_carrier_check(usbser_port_t *, int, int); 92254721Semastestatic void usbser_open_queues_init(usbser_port_t *, queue_t *); 93254721Semastestatic void usbser_open_queues_fini(usbser_port_t *); 94254721Semastestatic void usbser_close_drain(usbser_port_t *); 95254721Semastestatic void usbser_close_cancel_break(usbser_port_t *); 96254721Semastestatic void usbser_close_hangup(usbser_port_t *); 97254721Semastestatic void usbser_close_cleanup(usbser_port_t *); 98254721Semaste 99254721Semaste/* threads */ 100254721Semastestatic void usbser_thr_dispatch(usbser_thread_t *); 101263363Semastestatic void usbser_thr_cancel(usbser_thread_t *); 102254721Semastestatic void usbser_thr_wake(usbser_thread_t *); 103254721Semastestatic void usbser_wq_thread(void *); 104254721Semastestatic void usbser_rq_thread(void *); 105254721Semaste 106254721Semaste/* DSD callbacks */ 107254721Semastestatic void usbser_tx_cb(caddr_t); 108254721Semastestatic void usbser_rx_cb(caddr_t); 109254721Semastestatic void usbser_rx_massage_data(usbser_port_t *, mblk_t *); 110254721Semastestatic void usbser_rx_massage_mbreak(usbser_port_t *, mblk_t *); 111254721Semastestatic void usbser_rx_cb_put(usbser_port_t *, queue_t *, queue_t *, 112254721Semaste mblk_t *); 113254721Semastestatic void usbser_status_cb(caddr_t); 114254721Semastestatic void usbser_status_proc_cb(usbser_port_t *); 115254721Semaste 116254721Semaste/* serial support */ 117254721Semastestatic void usbser_wmsg(usbser_port_t *); 118254721Semastestatic int usbser_data(usbser_port_t *, mblk_t *); 119254721Semastestatic int usbser_ioctl(usbser_port_t *, mblk_t *); 120254721Semastestatic void usbser_iocdata(usbser_port_t *, mblk_t *); 121254721Semastestatic void usbser_stop(usbser_port_t *, mblk_t *); 122254721Semastestatic void usbser_start(usbser_port_t *, mblk_t *); 123254721Semastestatic void usbser_stopi(usbser_port_t *, mblk_t *); 124254721Semastestatic void usbser_starti(usbser_port_t *, mblk_t *); 125254721Semastestatic void usbser_flush(usbser_port_t *, mblk_t *); 126254721Semastestatic void usbser_break(usbser_port_t *, mblk_t *); 127254721Semastestatic void usbser_delay(usbser_port_t *, mblk_t *); 128254721Semastestatic void usbser_restart(void *); 129254721Semastestatic int usbser_port_program(usbser_port_t *); 130254721Semastestatic void usbser_inbound_flow_ctl(usbser_port_t *); 131254721Semaste 132254721Semaste/* misc */ 133254721Semastestatic int usbser_dev_is_online(usbser_state_t *); 134254721Semastestatic void usbser_serialize_port_act(usbser_port_t *, int); 135254721Semastestatic void usbser_release_port_act(usbser_port_t *, int); 136254721Semastestatic char *usbser_msgtype2str(int); 137254721Semastestatic char *usbser_ioctl2str(int); 138254721Semaste 139254721Semaste 140254721Semaste/* USBA events */ 141254721Semasteusb_event_t usbser_usb_events = { 142254721Semaste usbser_disconnect_cb, /* disconnect */ 143254721Semaste usbser_reconnect_cb, /* reconnect */ 144254721Semaste NULL, /* pre-suspend */ 145254721Semaste NULL, /* pre-resume */ 146254721Semaste}; 147254721Semaste 148254721Semaste/* debug support */ 149254721Semasteuint_t usbser_errlevel = USB_LOG_L4; 150254721Semasteuint_t usbser_errmask = DPRINT_MASK_ALL; 151254721Semasteuint_t usbser_instance_debug = (uint_t)-1; 152254721Semaste 153254721Semaste/* usb serial console */ 154254721Semastestatic struct usbser_state *usbser_list; 155254721Semastestatic kmutex_t usbser_lock; 156254721Semastestatic int usbser_console_abort; 157254721Semastestatic usb_console_info_t console_input, console_output; 158263363Semastestatic uchar_t *console_input_buf; 159263363Semastestatic uchar_t *console_input_start, *console_input_end; 160263363Semaste 161263363Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", usbser_console_abort)) 162263363Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", console_input)) 163263363Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", console_output)) 164254721Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_start)) 165254721Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_end)) 166254721Semaste 167254721Semastestatic void usbser_putchar(cons_polledio_arg_t, uchar_t); 168254721Semastestatic int usbser_getchar(cons_polledio_arg_t); 169254721Semastestatic boolean_t usbser_ischar(cons_polledio_arg_t); 170254721Semastestatic void usbser_polledio_enter(cons_polledio_arg_t); 171254721Semastestatic void usbser_polledio_exit(cons_polledio_arg_t); 172254721Semastestatic int usbser_polledio_init(usbser_port_t *); 173254721Semastestatic void usbser_polledio_fini(usbser_port_t *); 174254721Semaste 175254721Semastestatic struct cons_polledio usbser_polledio = { 176254721Semaste CONSPOLLEDIO_V1, 177254721Semaste NULL, /* to be set later */ 178254721Semaste usbser_putchar, 179254721Semaste usbser_getchar, 180254721Semaste usbser_ischar, 181254721Semaste usbser_polledio_enter, 182254721Semaste usbser_polledio_exit 183254721Semaste}; 184254721Semaste 185254721Semaste/* various statistics. TODO: replace with kstats */ 186254721Semastestatic int usbser_st_tx_data_loss = 0; 187254721Semastestatic int usbser_st_rx_data_loss = 0; 188254721Semastestatic int usbser_st_put_stopi = 0; 189254721Semastestatic int usbser_st_mstop = 0; 190254721Semastestatic int usbser_st_mstart = 0; 191254721Semastestatic int usbser_st_mstopi = 0; 192254721Semastestatic int usbser_st_mstarti = 0; 193254721Semastestatic int usbser_st_rsrv = 0; 194254721Semaste_NOTE(SCHEME_PROTECTS_DATA("monotonic stats", usbser_st_{ 195254721Semaste tx_data_loss rx_data_loss put_stopi mstop mstart mstopi mstarti rsrv})) 196254721Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", usb_bulk_req_t)) 197254721Semaste_NOTE(SCHEME_PROTECTS_DATA("unshared", usb_intr_req_t)) 198254721Semaste 199254721Semaste/* taskq parameter */ 200254721Semasteextern pri_t minclsyspri; 201254721Semaste 202254721Semaste/* 203254721Semaste * tell warlock not to worry about STREAMS structures 204263363Semaste */ 205263363Semaste_NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk datab msgb queue copyreq)) 206263363Semaste 207254721Semaste/* 208254721Semaste * modload support 209254721Semaste */ 210254721Semasteextern struct mod_ops mod_miscops; 211254721Semaste 212254721Semastestatic struct modlmisc modlmisc = { 213254721Semaste &mod_miscops, /* Type of module */ 214254721Semaste "USB generic serial module" 215254721Semaste}; 216254721Semaste 217254721Semastestatic struct modlinkage modlinkage = { 218254721Semaste MODREV_1, (void *)&modlmisc, NULL 219254721Semaste}; 220254721Semaste 221254721Semaste 222254721Semaste#define RSEQ(f1, f2) RSEQE(f1, usbser_rseq_do_cb, f2, NULL) 223254721Semaste 224254721Semaste 225254721Semaste/* 226254721Semaste * loadable module entry points 227254721Semaste * ---------------------------- 228254721Semaste */ 229254721Semaste 230254721Semasteint 231254721Semaste_init(void) 232254721Semaste{ 233254721Semaste int err; 234254721Semaste 235254721Semaste mutex_init(&usbser_lock, NULL, MUTEX_DRIVER, (void *)NULL); 236254721Semaste if (err = mod_install(&modlinkage)) 237254721Semaste mutex_destroy(&usbser_lock); 238254721Semaste 239254721Semaste return (err); 240254721Semaste} 241254721Semaste 242254721Semaste 243254721Semasteint 244254721Semaste_fini(void) 245254721Semaste{ 246254721Semaste int err; 247254721Semaste 248254721Semaste if (err = mod_remove(&modlinkage)) 249254721Semaste 250254721Semaste return (err); 251254721Semaste 252254721Semaste mutex_destroy(&usbser_lock); 253254721Semaste 254254721Semaste return (0); 255254721Semaste} 256254721Semaste 257254721Semaste 258254721Semasteint 259254721Semaste_info(struct modinfo *modinfop) 260254721Semaste{ 261263363Semaste return (mod_info(&modlinkage, modinfop)); 262254721Semaste} 263254721Semaste 264254721Semaste 265254721Semaste/* 266254721Semaste * soft state size 267254721Semaste */ 268254721Semasteint 269254721Semasteusbser_soft_state_size() 270254721Semaste{ 271254721Semaste return (sizeof (usbser_state_t)); 272254721Semaste} 273254721Semaste 274254721Semaste 275254721Semaste/* 276254721Semaste * autoconfiguration entry points 277254721Semaste * ------------------------------ 278254721Semaste */ 279254721Semaste 280254721Semaste/*ARGSUSED*/ 281254721Semasteint 282254721Semasteusbser_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 283254721Semaste void **result, void *statep) 284254721Semaste{ 285254721Semaste int instance; 286254721Semaste int ret = DDI_FAILURE; 287254721Semaste usbser_state_t *usbserp; 288254721Semaste 289254721Semaste instance = USBSER_MINOR2INST(getminor((dev_t)arg)); 290254721Semaste 291254721Semaste switch (infocmd) { 292254721Semaste case DDI_INFO_DEVT2DEVINFO: 293254721Semaste *result = NULL; 294254721Semaste usbserp = ddi_get_soft_state(statep, instance); 295254721Semaste if (usbserp != NULL) { 296254721Semaste *result = usbserp->us_dip; 297254721Semaste if (*result != NULL) { 298254721Semaste ret = DDI_SUCCESS; 299254721Semaste } 300254721Semaste } 301254721Semaste 302254721Semaste break; 303254721Semaste case DDI_INFO_DEVT2INSTANCE: 304254721Semaste *result = (void *)(uintptr_t)instance; 305254721Semaste ret = DDI_SUCCESS; 306254721Semaste 307254721Semaste break; 308254721Semaste default: 309254721Semaste break; 310254721Semaste } 311254721Semaste 312254721Semaste return (ret); 313254721Semaste} 314254721Semaste 315254721Semaste/* 316254721Semaste * device attach 317254721Semaste */ 318254721Semastestatic rseq_t rseq_att[] = { 319254721Semaste RSEQ(NULL, usbser_free_soft_state), 320254721Semaste RSEQ(usbser_init_soft_state, usbser_fini_soft_state), 321254721Semaste RSEQ(usbser_attach_dev, usbser_detach_dev), 322254721Semaste RSEQ(usbser_attach_ports, usbser_detach_ports), 323254721Semaste RSEQ(usbser_create_taskq, usbser_destroy_taskq), 324254721Semaste RSEQ(NULL, usbser_set_dev_state_init) 325254721Semaste}; 326254721Semaste 327254721Semastestatic void 328254721Semasteusbser_insert(struct usbser_state *usp) 329254721Semaste{ 330254721Semaste struct usbser_state *tmp; 331254721Semaste 332254721Semaste mutex_enter(&usbser_lock); 333254721Semaste tmp = usbser_list; 334254721Semaste if (tmp == NULL) 335254721Semaste usbser_list = usp; 336254721Semaste else { 337254721Semaste while (tmp->us_next) 338254721Semaste tmp = tmp->us_next; 339254721Semaste tmp->us_next = usp; 340254721Semaste } 341254721Semaste mutex_exit(&usbser_lock); 342254721Semaste} 343254721Semaste 344254721Semastestatic void 345254721Semasteusbser_remove(struct usbser_state *usp) 346254721Semaste{ 347254721Semaste struct usbser_state *tmp, *prev = NULL; 348254721Semaste 349254721Semaste mutex_enter(&usbser_lock); 350254721Semaste tmp = usbser_list; 351254721Semaste while (tmp != usp) { 352254721Semaste prev = tmp; 353254721Semaste tmp = tmp->us_next; 354254721Semaste } 355254721Semaste ASSERT(tmp == usp); /* must exist, else attach/detach wrong */ 356254721Semaste if (prev) 357254721Semaste prev->us_next = usp->us_next; 358254721Semaste else 359254721Semaste usbser_list = usp->us_next; 360254721Semaste usp->us_next = NULL; 361254721Semaste mutex_exit(&usbser_lock); 362254721Semaste} 363254721Semaste 364254721Semaste/* 365254721Semaste * Return the first serial device, with dip held. This is called 366254721Semaste * from the console subsystem to place console on usb serial device. 367254721Semaste */ 368254721Semastedev_info_t * 369254721Semasteusbser_first_device(void) 370254721Semaste{ 371254721Semaste dev_info_t *dip = NULL; 372254721Semaste 373254721Semaste mutex_enter(&usbser_lock); 374254721Semaste if (usbser_list) { 375254721Semaste dip = usbser_list->us_dip; 376254721Semaste ndi_hold_devi(dip); 377254721Semaste } 378254721Semaste mutex_exit(&usbser_lock); 379254721Semaste 380254721Semaste return (dip); 381254721Semaste} 382254721Semaste 383254721Semasteint 384254721Semasteusbser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, 385254721Semaste void *statep, ds_ops_t *ds_ops) 386254721Semaste{ 387254721Semaste int instance; 388254721Semaste usbser_state_t *usp; 389254721Semaste 390254721Semaste instance = ddi_get_instance(dip); 391254721Semaste 392254721Semaste switch (cmd) { 393254721Semaste case DDI_ATTACH: 394254721Semaste 395254721Semaste break; 396254721Semaste case DDI_RESUME: 397254721Semaste usbser_cpr_resume(dip); 398254721Semaste 399254721Semaste return (DDI_SUCCESS); 400254721Semaste default: 401254721Semaste 402254721Semaste return (DDI_FAILURE); 403254721Semaste } 404254721Semaste 405254721Semaste /* allocate and get soft state */ 406254721Semaste if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) { 407254721Semaste 408254721Semaste return (DDI_FAILURE); 409254721Semaste } 410254721Semaste if ((usp = ddi_get_soft_state(statep, instance)) == NULL) { 411254721Semaste ddi_soft_state_free(statep, instance); 412254721Semaste 413254721Semaste return (DDI_FAILURE); 414254721Semaste } 415254721Semaste 416254721Semaste usp->us_statep = statep; 417254721Semaste usp->us_dip = dip; 418254721Semaste usp->us_instance = instance; 419254721Semaste usp->us_ds_ops = ds_ops; 420254721Semaste 421254721Semaste if (rseq_do(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0) == RSEQ_OK) { 422254721Semaste ddi_report_dev(dip); 423254721Semaste usbser_insert(usp); 424254721Semaste 425254721Semaste return (DDI_SUCCESS); 426254721Semaste } else { 427254721Semaste 428254721Semaste return (DDI_FAILURE); 429254721Semaste } 430254721Semaste} 431254721Semaste 432254721Semaste/* 433254721Semaste * device detach 434254721Semaste */ 435254721Semasteint 436254721Semasteusbser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep) 437254721Semaste{ 438254721Semaste int instance = ddi_get_instance(dip); 439254721Semaste usbser_state_t *usp; 440254721Semaste int rval; 441254721Semaste 442254721Semaste usp = ddi_get_soft_state(statep, instance); 443254721Semaste 444254721Semaste switch (cmd) { 445254721Semaste case DDI_DETACH: 446254721Semaste USB_DPRINTF_L4(DPRINT_DETACH, usp->us_lh, "usbser_detach"); 447254721Semaste usbser_remove(usp); 448254721Semaste (void) rseq_undo(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0); 449254721Semaste USB_DPRINTF_L4(DPRINT_DETACH, NULL, 450254721Semaste "usbser_detach.%d: end", instance); 451254721Semaste 452254721Semaste return (DDI_SUCCESS); 453254721Semaste case DDI_SUSPEND: 454254721Semaste rval = usbser_cpr_suspend(dip); 455254721Semaste 456254721Semaste return ((rval == USB_SUCCESS)? DDI_SUCCESS : DDI_FAILURE); 457254721Semaste default: 458254721Semaste 459254721Semaste return (DDI_FAILURE); 460254721Semaste } 461254721Semaste} 462254721Semaste 463254721Semaste/* 464254721Semaste * STREAMS entry points 465254721Semaste * -------------------- 466254721Semaste * 467254721Semaste * 468254721Semaste * port open 469254721Semaste */ 470254721Semaste/*ARGSUSED*/ 471254721Semasteint 472254721Semasteusbser_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr, 473254721Semaste void *statep) 474254721Semaste{ 475254721Semaste usbser_state_t *usp; 476254721Semaste usbser_port_t *pp; 477254721Semaste int minor = getminor(*dev); 478254721Semaste int instance; 479254721Semaste uint_t port_num; 480254721Semaste int rval; 481254721Semaste 482254721Semaste instance = USBSER_MINOR2INST(minor); 483254721Semaste if (instance < 0) { 484254721Semaste 485254721Semaste return (ENXIO); 486254721Semaste } 487254721Semaste 488254721Semaste usp = ddi_get_soft_state(statep, instance); 489254721Semaste if (usp == NULL) { 490254721Semaste 491254721Semaste return (ENXIO); 492254721Semaste } 493254721Semaste 494254721Semaste /* don't allow to open disconnected device */ 495254721Semaste mutex_enter(&usp->us_mutex); 496254721Semaste if (usp->us_dev_state == USB_DEV_DISCONNECTED) { 497254721Semaste mutex_exit(&usp->us_mutex); 498254721Semaste 499254721Semaste return (ENXIO); 500254721Semaste } 501254721Semaste mutex_exit(&usp->us_mutex); 502254721Semaste 503254721Semaste /* get port soft state */ 504254721Semaste port_num = USBSER_MINOR2PORT(minor); 505254721Semaste if (port_num >= usp->us_port_cnt) { 506254721Semaste 507254721Semaste return (ENXIO); 508254721Semaste } 509254721Semaste pp = &usp->us_ports[port_num]; 510254721Semaste 511254721Semaste /* set up everything for open */ 512254721Semaste rval = usbser_open_setup(rq, pp, minor, flag, cr); 513254721Semaste 514254721Semaste USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh, "usbser_open: rval=%d", rval); 515254721Semaste 516254721Semaste return (rval); 517254721Semaste} 518254721Semaste 519254721Semaste 520254721Semaste/* 521254721Semaste * port close 522254721Semaste * 523254721Semaste * some things driver should do when the last app closes the line: 524254721Semaste * 525254721Semaste * drain data; 526254721Semaste * cancel break/delay; 527254721Semaste * hangup line (if necessary); 528254721Semaste * DSD close; 529254721Semaste * cleanup soft state; 530254721Semaste */ 531254721Semaste/*ARGSUSED*/ 532254721Semasteint 533254721Semasteusbser_close(queue_t *rq, int flag, cred_t *cr) 534254721Semaste{ 535254721Semaste usbser_port_t *pp = (usbser_port_t *)rq->q_ptr; 536254721Semaste int online; 537254721Semaste 538254721Semaste if (pp == NULL) { 539254721Semaste 540254721Semaste return (ENXIO); 541254721Semaste } 542254721Semaste 543254721Semaste online = usbser_dev_is_online(pp->port_usp); 544254721Semaste 545254721Semaste /* 546254721Semaste * in the closing state new activities will not be initiated 547254721Semaste */ 548254721Semaste mutex_enter(&pp->port_mutex); 549254721Semaste pp->port_state = USBSER_PORT_CLOSING; 550254721Semaste 551254721Semaste if (online) { 552254721Semaste /* drain the data */ 553254721Semaste usbser_close_drain(pp); 554254721Semaste } 555254721Semaste 556254721Semaste /* stop break/delay */ 557254721Semaste usbser_close_cancel_break(pp); 558254721Semaste 559254721Semaste if (online) { 560254721Semaste /* hangup line */ 561254721Semaste usbser_close_hangup(pp); 562254721Semaste } 563254721Semaste 564254721Semaste /* 565254721Semaste * close DSD, cleanup state and transition to 'closed' state 566254721Semaste */ 567254721Semaste usbser_close_cleanup(pp); 568254721Semaste mutex_exit(&pp->port_mutex); 569254721Semaste 570254721Semaste USB_DPRINTF_L4(DPRINT_CLOSE, pp->port_lh, "usbser_close: end"); 571254721Semaste 572254721Semaste return (0); 573254721Semaste} 574254721Semaste 575254721Semaste 576254721Semaste/* 577254721Semaste * read side service routine: send as much as possible messages upstream 578254721Semaste * and if there is still place on the queue, enable receive (if not already) 579254721Semaste */ 580254721Semasteint 581254721Semasteusbser_rsrv(queue_t *q) 582254721Semaste{ 583254721Semaste usbser_port_t *pp = (usbser_port_t *)q->q_ptr; 584254721Semaste mblk_t *mp; 585254721Semaste 586254721Semaste usbser_st_rsrv++; 587254721Semaste USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rsrv"); 588254721Semaste 589254721Semaste while (canputnext(q) && (mp = getq(q))) { 590254721Semaste putnext(q, mp); 591254721Semaste } 592254721Semaste 593254721Semaste if (canputnext(q)) { 594254721Semaste mutex_enter(&pp->port_mutex); 595254721Semaste ASSERT(pp->port_state != USBSER_PORT_CLOSED); 596254721Semaste 597254721Semaste if (USBSER_PORT_ACCESS_OK(pp)) { 598254721Semaste usbser_thr_wake(&pp->port_rq_thread); 599254721Semaste } 600254721Semaste mutex_exit(&pp->port_mutex); 601254721Semaste } 602254721Semaste 603254721Semaste return (0); 604254721Semaste} 605254721Semaste 606254721Semaste 607254721Semaste/* 608254721Semaste * wput: put message on the queue and wake wq thread 609254721Semaste */ 610254721Semasteint 611254721Semasteusbser_wput(queue_t *q, mblk_t *mp) 612254721Semaste{ 613254721Semaste usbser_port_t *pp = (usbser_port_t *)q->q_ptr; 614254721Semaste 615254721Semaste USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wput"); 616254721Semaste 617254721Semaste mutex_enter(&pp->port_mutex); 618254721Semaste ASSERT(pp->port_state != USBSER_PORT_CLOSED); 619254721Semaste 620254721Semaste /* ignore new messages if port is already closing */ 621254721Semaste if (pp->port_state == USBSER_PORT_CLOSING) { 622254721Semaste freemsg(mp); 623254721Semaste } else if (putq(q, mp)) { 624254721Semaste /* 625254721Semaste * this counter represents amount of tx data on the wq. 626254721Semaste * each time the data is passed to DSD for transmission, 627254721Semaste * the counter is decremented accordingly 628254721Semaste */ 629254721Semaste pp->port_wq_data_cnt += msgdsize(mp); 630254721Semaste } else { 631254721Semaste usbser_st_tx_data_loss++; 632254721Semaste } 633254721Semaste mutex_exit(&pp->port_mutex); 634254721Semaste 635254721Semaste return (0); 636254721Semaste} 637254721Semaste 638254721Semaste 639254721Semaste/* 640254721Semaste * we need wsrv() routine to take advantage of STREAMS flow control: 641254721Semaste * without it the framework will consider we are always able to process msgs 642254721Semaste */ 643254721Semasteint 644254721Semasteusbser_wsrv(queue_t *q) 645254721Semaste{ 646254721Semaste usbser_port_t *pp = (usbser_port_t *)q->q_ptr; 647254721Semaste 648254721Semaste USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wsrv"); 649254721Semaste 650254721Semaste mutex_enter(&pp->port_mutex); 651254721Semaste ASSERT(pp->port_state != USBSER_PORT_CLOSED); 652254721Semaste 653254721Semaste if (USBSER_PORT_ACCESS_OK(pp)) { 654254721Semaste usbser_thr_wake(&pp->port_wq_thread); 655254721Semaste } 656254721Semaste mutex_exit(&pp->port_mutex); 657254721Semaste 658254721Semaste return (0); 659254721Semaste} 660254721Semaste 661254721Semaste 662254721Semaste/* 663254721Semaste * power entry point 664254721Semaste */ 665254721Semasteint 666254721Semasteusbser_power(dev_info_t *dip, int comp, int level) 667254721Semaste{ 668254721Semaste void *statep; 669254721Semaste usbser_state_t *usp; 670254721Semaste int new_state; 671254721Semaste int rval; 672254721Semaste 673254721Semaste statep = ddi_get_driver_private(dip); 674254721Semaste usp = ddi_get_soft_state(statep, ddi_get_instance(dip)); 675254721Semaste 676254721Semaste USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, 677254721Semaste "usbser_power: dip=0x%p, comp=%d, level=%d", 678254721Semaste (void *)dip, comp, level); 679254721Semaste 680254721Semaste mutex_enter(&usp->us_mutex); 681254721Semaste new_state = usp->us_dev_state; 682254721Semaste mutex_exit(&usp->us_mutex); 683254721Semaste 684254721Semaste /* let DSD do the job */ 685254721Semaste rval = USBSER_DS_USB_POWER(usp, comp, level, &new_state); 686254721Semaste 687254721Semaste /* stay in sync with DSD */ 688254721Semaste mutex_enter(&usp->us_mutex); 689 usp->us_dev_state = new_state; 690 mutex_exit(&usp->us_mutex); 691 692 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 693} 694 695 696/* 697 * 698 * configuration entry point subroutines 699 * ------------------------------------- 700 * 701 * rseq callback 702 */ 703static int 704usbser_rseq_do_cb(rseq_t *rseq, int num, uintptr_t arg) 705{ 706 usbser_state_t *usp = (usbser_state_t *)arg; 707 int rval = rseq[num].r_do.s_rval; 708 char *name = rseq[num].r_do.s_name; 709 710 if (rval != DDI_SUCCESS) { 711 USB_DPRINTF_L2(DPRINT_ATTACH, usp->us_lh, 712 "do %s failed (%d)", name, rval); 713 714 return (RSEQ_UNDO); 715 } else { 716 717 return (RSEQ_OK); 718 } 719} 720 721 722/* 723 * free soft state 724 */ 725static int 726usbser_free_soft_state(usbser_state_t *usp) 727{ 728 ddi_soft_state_free(usp->us_statep, usp->us_instance); 729 730 return (USB_SUCCESS); 731} 732 733/* 734 * init instance soft state 735 */ 736static int 737usbser_init_soft_state(usbser_state_t *usp) 738{ 739 usp->us_lh = usb_alloc_log_hdl(usp->us_dip, "usbs[*].", 740 &usbser_errlevel, &usbser_errmask, &usbser_instance_debug, 741 0); 742 mutex_init(&usp->us_mutex, NULL, MUTEX_DRIVER, (void *)NULL); 743 744 /* save state pointer for use in event callbacks */ 745 ddi_set_driver_private(usp->us_dip, usp->us_statep); 746 747 usp->us_dev_state = USBSER_DEV_INIT; 748 749 return (DDI_SUCCESS); 750} 751 752/* 753 * fini instance soft state 754 */ 755static int 756usbser_fini_soft_state(usbser_state_t *usp) 757{ 758 usb_free_log_hdl(usp->us_lh); 759 mutex_destroy(&usp->us_mutex); 760 ddi_set_driver_private(usp->us_dip, NULL); 761 762 return (DDI_SUCCESS); 763} 764 765/* 766 * attach entire device 767 */ 768static int 769usbser_attach_dev(usbser_state_t *usp) 770{ 771 ds_attach_info_t ai; 772 int rval; 773 774 usp->us_dev_state = USB_DEV_ONLINE; 775 776 ai.ai_dip = usp->us_dip; 777 ai.ai_usb_events = &usbser_usb_events; 778 ai.ai_hdl = &usp->us_ds_hdl; 779 ai.ai_port_cnt = &usp->us_port_cnt; 780 781 rval = USBSER_DS_ATTACH(usp, &ai); 782 783 if ((rval != USB_SUCCESS) || (usp->us_ds_hdl == NULL) || 784 (usp->us_port_cnt == 0)) { 785 USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh, "usbser_attach_dev: " 786 "failed %d %p %d", rval, usp->us_ds_hdl, usp->us_port_cnt); 787 788 return (DDI_FAILURE); 789 } 790 791 USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh, 792 "usbser_attach_dev: port_cnt = %d", usp->us_port_cnt); 793 794 return (DDI_SUCCESS); 795} 796 797 798/* 799 * detach entire device 800 */ 801static void 802usbser_detach_dev(usbser_state_t *usp) 803{ 804 USBSER_DS_DETACH(usp); 805} 806 807 808/* 809 * attach each individual port 810 */ 811static int 812usbser_attach_ports(usbser_state_t *usp) 813{ 814 int i; 815 usbser_port_t *pp; 816 ds_cb_t ds_cb; 817 818 /* 819 * allocate port array 820 */ 821 usp->us_ports = kmem_zalloc(usp->us_port_cnt * 822 sizeof (usbser_port_t), KM_SLEEP); 823 824 /* callback handlers */ 825 ds_cb.cb_tx = usbser_tx_cb; 826 ds_cb.cb_rx = usbser_rx_cb; 827 ds_cb.cb_status = usbser_status_cb; 828 829 /* 830 * initialize each port 831 */ 832 for (i = 0; i < usp->us_port_cnt; i++) { 833 pp = &usp->us_ports[i]; 834 835 /* 836 * initialize data 837 */ 838 pp->port_num = i; 839 pp->port_usp = usp; 840 pp->port_ds_ops = usp->us_ds_ops; 841 pp->port_ds_hdl = usp->us_ds_hdl; 842 843 /* allocate log handle */ 844 (void) sprintf(pp->port_lh_name, "usbs[%d].", i); 845 pp->port_lh = usb_alloc_log_hdl(usp->us_dip, 846 pp->port_lh_name, &usbser_errlevel, &usbser_errmask, 847 &usbser_instance_debug, 0); 848 849 mutex_init(&pp->port_mutex, NULL, MUTEX_DRIVER, (void *)NULL); 850 cv_init(&pp->port_state_cv, NULL, CV_DEFAULT, NULL); 851 cv_init(&pp->port_act_cv, NULL, CV_DEFAULT, NULL); 852 cv_init(&pp->port_car_cv, NULL, CV_DEFAULT, NULL); 853 854 /* 855 * init threads 856 */ 857 pp->port_wq_thread.thr_port = pp; 858 pp->port_wq_thread.thr_func = usbser_wq_thread; 859 pp->port_wq_thread.thr_arg = (void *)&pp->port_wq_thread; 860 cv_init(&pp->port_wq_thread.thr_cv, NULL, CV_DEFAULT, NULL); 861 862 pp->port_rq_thread.thr_port = pp; 863 pp->port_rq_thread.thr_func = usbser_rq_thread; 864 pp->port_rq_thread.thr_arg = (void *)&pp->port_rq_thread; 865 cv_init(&pp->port_rq_thread.thr_cv, NULL, CV_DEFAULT, NULL); 866 867 /* 868 * register callbacks 869 */ 870 ds_cb.cb_arg = (caddr_t)pp; 871 USBSER_DS_REGISTER_CB(usp, i, &ds_cb); 872 873 pp->port_state = USBSER_PORT_CLOSED; 874 875 if (usbser_create_port_minor_nodes(usp, i) != USB_SUCCESS) { 876 usbser_detach_ports(usp); 877 878 return (DDI_FAILURE); 879 } 880 } 881 882 return (DDI_SUCCESS); 883} 884 885 886/* 887 * create a pair of minor nodes for the port 888 */ 889static int 890usbser_create_port_minor_nodes(usbser_state_t *usp, int port_num) 891{ 892 int instance = usp->us_instance; 893 minor_t minor; 894 char name[16]; 895 896 /* 897 * tty node 898 */ 899 (void) sprintf(name, "%d", port_num); 900 minor = USBSER_MAKEMINOR(instance, port_num, 0); 901 902 if (ddi_create_minor_node(usp->us_dip, name, 903 S_IFCHR, minor, DDI_NT_SERIAL, NULL) != DDI_SUCCESS) { 904 905 return (USB_FAILURE); 906 } 907 908 /* 909 * dial-out node 910 */ 911 (void) sprintf(name, "%d,cu", port_num); 912 minor = USBSER_MAKEMINOR(instance, port_num, OUTLINE); 913 914 if (ddi_create_minor_node(usp->us_dip, name, 915 S_IFCHR, minor, DDI_NT_SERIAL_DO, NULL) != DDI_SUCCESS) { 916 917 return (USB_FAILURE); 918 } 919 920 return (USB_SUCCESS); 921} 922 923 924/* 925 * detach each port individually 926 */ 927static void 928usbser_detach_ports(usbser_state_t *usp) 929{ 930 int i; 931 int sz; 932 usbser_port_t *pp; 933 934 /* 935 * remove all minor nodes 936 */ 937 ddi_remove_minor_node(usp->us_dip, NULL); 938 939 for (i = 0; i < usp->us_port_cnt; i++) { 940 pp = &usp->us_ports[i]; 941 942 if (pp->port_state != USBSER_PORT_CLOSED) { 943 ASSERT(pp->port_state == USBSER_PORT_NOT_INIT); 944 945 continue; 946 } 947 948 USBSER_DS_UNREGISTER_CB(usp, i); 949 950 mutex_destroy(&pp->port_mutex); 951 cv_destroy(&pp->port_state_cv); 952 cv_destroy(&pp->port_act_cv); 953 cv_destroy(&pp->port_car_cv); 954 955 cv_destroy(&pp->port_wq_thread.thr_cv); 956 cv_destroy(&pp->port_rq_thread.thr_cv); 957 958 usb_free_log_hdl(pp->port_lh); 959 } 960 961 /* 962 * free memory 963 */ 964 sz = usp->us_port_cnt * sizeof (usbser_port_t); 965 kmem_free(usp->us_ports, sz); 966 usp->us_ports = NULL; 967} 968 969 970/* 971 * create a taskq with two threads per port (read and write sides) 972 */ 973static int 974usbser_create_taskq(usbser_state_t *usp) 975{ 976 int nthr = usp->us_port_cnt * 2; 977 978 usp->us_taskq = ddi_taskq_create(usp->us_dip, "usbser_taskq", 979 nthr, TASKQ_DEFAULTPRI, 0); 980 981 return ((usp->us_taskq == NULL) ? DDI_FAILURE : DDI_SUCCESS); 982} 983 984 985static void 986usbser_destroy_taskq(usbser_state_t *usp) 987{ 988 ddi_taskq_destroy(usp->us_taskq); 989} 990 991 992static void 993usbser_set_dev_state_init(usbser_state_t *usp) 994{ 995 mutex_enter(&usp->us_mutex); 996 usp->us_dev_state = USBSER_DEV_INIT; 997 mutex_exit(&usp->us_mutex); 998} 999 1000/* 1001 * hotplugging and power management 1002 * --------------------------------- 1003 * 1004 * disconnect event callback 1005 */ 1006/*ARGSUSED*/ 1007static int 1008usbser_disconnect_cb(dev_info_t *dip) 1009{ 1010 void *statep; 1011 usbser_state_t *usp; 1012 1013 statep = ddi_get_driver_private(dip); 1014 usp = ddi_get_soft_state(statep, ddi_get_instance(dip)); 1015 1016 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, 1017 "usbser_disconnect_cb: dip=%p", (void *)dip); 1018 1019 mutex_enter(&usp->us_mutex); 1020 switch (usp->us_dev_state) { 1021 case USB_DEV_ONLINE: 1022 case USB_DEV_PWRED_DOWN: 1023 /* prevent further activity */ 1024 usp->us_dev_state = USB_DEV_DISCONNECTED; 1025 mutex_exit(&usp->us_mutex); 1026 1027 /* see if any of the ports are open and do necessary handling */ 1028 usbser_disconnect_ports(usp); 1029 1030 /* call DSD to do any necessary work */ 1031 if (USBSER_DS_DISCONNECT(usp) != USB_DEV_DISCONNECTED) { 1032 USB_DPRINTF_L2(DPRINT_EVENTS, usp->us_lh, 1033 "usbser_disconnect_cb: ds_disconnect failed"); 1034 } 1035 1036 break; 1037 case USB_DEV_SUSPENDED: 1038 /* we remain suspended */ 1039 default: 1040 mutex_exit(&usp->us_mutex); 1041 1042 break; 1043 } 1044 1045 return (USB_SUCCESS); 1046} 1047 1048 1049/* 1050 * reconnect event callback 1051 */ 1052/*ARGSUSED*/ 1053static int 1054usbser_reconnect_cb(dev_info_t *dip) 1055{ 1056 void *statep; 1057 usbser_state_t *usp; 1058 1059 statep = ddi_get_driver_private(dip); 1060 usp = ddi_get_soft_state(statep, ddi_get_instance(dip)); 1061 1062 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, 1063 "usbser_reconnect_cb: dip=%p", (void *)dip); 1064 1065 (void) usbser_restore_device_state(usp); 1066 1067 return (USB_SUCCESS); 1068} 1069 1070 1071/* 1072 * if any of the ports is open during disconnect, 1073 * send M_HANGUP message upstream and log a warning 1074 */ 1075static void 1076usbser_disconnect_ports(usbser_state_t *usp) 1077{ 1078 usbser_port_t *pp; 1079 queue_t *rq; 1080 int complain = 0; 1081 int hangup = 0; 1082 timeout_id_t delay_id = 0; 1083 int i; 1084 1085 if (usp->us_ports == NULL) { 1086 return; 1087 } 1088 1089 for (i = 0; i < usp->us_port_cnt; i++) { 1090 pp = &usp->us_ports[i]; 1091 1092 mutex_enter(&pp->port_mutex); 1093 if (pp->port_state == USBSER_PORT_OPEN || 1094 USBSER_IS_OPENING(pp) || 1095 pp->port_state == USBSER_PORT_CLOSING) { 1096 complain = 1; 1097 } 1098 1099 if (pp->port_state == USBSER_PORT_OPEN) { 1100 rq = pp->port_ttycommon.t_readq; 1101 1102 /* 1103 * hangup the stream; will send actual 1104 * M_HANGUP message after releasing mutex 1105 */ 1106 pp->port_flags |= USBSER_FL_HUNGUP; 1107 hangup = 1; 1108 1109 /* 1110 * cancel all activities 1111 */ 1112 usbser_release_port_act(pp, USBSER_ACT_ALL); 1113 1114 delay_id = pp->port_delay_id; 1115 pp->port_delay_id = 0; 1116 1117 /* mark disconnected */ 1118 pp->port_state = USBSER_PORT_DISCONNECTED; 1119 cv_broadcast(&pp->port_state_cv); 1120 } 1121 mutex_exit(&pp->port_mutex); 1122 1123 if (hangup) { 1124 (void) putnextctl(rq, M_HANGUP); 1125 hangup = 0; 1126 } 1127 1128 /* 1129 * we couldn't untimeout while holding the mutex - do it now 1130 */ 1131 if (delay_id) { 1132 (void) untimeout(delay_id); 1133 delay_id = 0; 1134 } 1135 } 1136 1137 /* 1138 * complain about disconnecting device while open 1139 */ 1140 if (complain) { 1141 USB_DPRINTF_L0(DPRINT_EVENTS, usp->us_lh, "device was " 1142 "disconnected while open. Data may have been lost"); 1143 } 1144} 1145 1146 1147/* 1148 * do CPR suspend 1149 * 1150 * We use a trivial CPR strategy - fail if any of the device's ports are open. 1151 * The problem with more sophisticated strategies is that each open port uses 1152 * two threads that sit in the loop until the port is closed, while CPR has to 1153 * stop all kernel threads to succeed. Stopping port threads is a rather 1154 * intrusive and delicate procedure; I leave it as an RFE for now. 1155 * 1156 */ 1157static int 1158usbser_cpr_suspend(dev_info_t *dip) 1159{ 1160 void *statep; 1161 usbser_state_t *usp; 1162 int new_state; 1163 int rval; 1164 1165 statep = ddi_get_driver_private(dip); 1166 usp = ddi_get_soft_state(statep, ddi_get_instance(dip)); 1167 1168 USB_DPRINTF_L4(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_suspend"); 1169 1170 /* suspend each port first */ 1171 if (usbser_suspend_ports(usp) != USB_SUCCESS) { 1172 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, 1173 "usbser_cpr_suspend: GSD failure"); 1174 1175 return (USB_FAILURE); 1176 } 1177 1178 new_state = USBSER_DS_SUSPEND(usp); /* let DSD do its part */ 1179 1180 mutex_enter(&usp->us_mutex); 1181 if (new_state == USB_DEV_SUSPENDED) { 1182 rval = USB_SUCCESS; 1183 } else { 1184 ASSERT(new_state == USB_DEV_ONLINE); 1185 rval = USB_FAILURE; 1186 } 1187 usp->us_dev_state = new_state; 1188 mutex_exit(&usp->us_mutex); 1189 1190 return (rval); 1191} 1192 1193 1194static int 1195usbser_suspend_ports(usbser_state_t *usp) 1196{ 1197 usbser_port_t *pp; 1198 int i; 1199 1200 for (i = 0; i < usp->us_port_cnt; i++) { 1201 pp = &usp->us_ports[i]; 1202 1203 mutex_enter(&pp->port_mutex); 1204 if (pp->port_state != USBSER_PORT_CLOSED) { 1205 mutex_exit(&pp->port_mutex); 1206 1207 return (USB_FAILURE); 1208 } 1209 mutex_exit(&pp->port_mutex); 1210 } 1211 1212 return (USB_SUCCESS); 1213} 1214 1215 1216/* 1217 * do CPR resume 1218 * 1219 * DSD will return USB_DEV_ONLINE in case of success 1220 */ 1221static void 1222usbser_cpr_resume(dev_info_t *dip) 1223{ 1224 void *statep; 1225 usbser_state_t *usp; 1226 1227 statep = ddi_get_driver_private(dip); 1228 usp = ddi_get_soft_state(statep, ddi_get_instance(dip)); 1229 1230 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_resume"); 1231 1232 (void) usbser_restore_device_state(usp); 1233} 1234 1235 1236/* 1237 * restore device state after CPR resume or reconnect 1238 */ 1239static int 1240usbser_restore_device_state(usbser_state_t *usp) 1241{ 1242 int new_state, current_state; 1243 1244 /* needed as power up state of dev is "unknown" to system */ 1245 (void) pm_busy_component(usp->us_dip, 0); 1246 (void) pm_raise_power(usp->us_dip, 0, USB_DEV_OS_FULL_PWR); 1247 1248 mutex_enter(&usp->us_mutex); 1249 current_state = usp->us_dev_state; 1250 mutex_exit(&usp->us_mutex); 1251 1252 ASSERT((current_state == USB_DEV_DISCONNECTED) || 1253 (current_state == USB_DEV_SUSPENDED)); 1254 1255 /* 1256 * call DSD to perform device-specific work 1257 */ 1258 if (current_state == USB_DEV_DISCONNECTED) { 1259 new_state = USBSER_DS_RECONNECT(usp); 1260 } else { 1261 new_state = USBSER_DS_RESUME(usp); 1262 } 1263 1264 mutex_enter(&usp->us_mutex); 1265 usp->us_dev_state = new_state; 1266 mutex_exit(&usp->us_mutex); 1267 1268 if (new_state == USB_DEV_ONLINE) { 1269 /* 1270 * restore ports state 1271 */ 1272 usbser_restore_ports_state(usp); 1273 } 1274 1275 (void) pm_idle_component(usp->us_dip, 0); 1276 1277 return (USB_SUCCESS); 1278} 1279 1280 1281/* 1282 * restore ports state after device reconnect/resume 1283 */ 1284static void 1285usbser_restore_ports_state(usbser_state_t *usp) 1286{ 1287 usbser_port_t *pp; 1288 queue_t *rq; 1289 int i; 1290 1291 for (i = 0; i < usp->us_port_cnt; i++) { 1292 pp = &usp->us_ports[i]; 1293 1294 mutex_enter(&pp->port_mutex); 1295 /* 1296 * only care about ports that are open 1297 */ 1298 if ((pp->port_state != USBSER_PORT_SUSPENDED) && 1299 (pp->port_state != USBSER_PORT_DISCONNECTED)) { 1300 mutex_exit(&pp->port_mutex); 1301 1302 continue; 1303 } 1304 1305 pp->port_state = USBSER_PORT_OPEN; 1306 1307 /* 1308 * if the stream was hung up during disconnect, restore it 1309 */ 1310 if (pp->port_flags & USBSER_FL_HUNGUP) { 1311 pp->port_flags &= ~USBSER_FL_HUNGUP; 1312 rq = pp->port_ttycommon.t_readq; 1313 1314 mutex_exit(&pp->port_mutex); 1315 (void) putnextctl(rq, M_UNHANGUP); 1316 mutex_enter(&pp->port_mutex); 1317 } 1318 1319 /* 1320 * restore serial parameters 1321 */ 1322 (void) usbser_port_program(pp); 1323 1324 /* 1325 * wake anything that might be sleeping 1326 */ 1327 cv_broadcast(&pp->port_state_cv); 1328 cv_broadcast(&pp->port_act_cv); 1329 usbser_thr_wake(&pp->port_wq_thread); 1330 usbser_thr_wake(&pp->port_rq_thread); 1331 mutex_exit(&pp->port_mutex); 1332 } 1333} 1334 1335 1336/* 1337 * STREAMS subroutines 1338 * ------------------- 1339 * 1340 * 1341 * port open state machine 1342 * 1343 * here's a list of things that the driver has to do while open; 1344 * because device can be opened any number of times, 1345 * initial open has additional responsibilities: 1346 * 1347 * if (initial_open) { 1348 * initialize soft state; \ 1349 * DSD open; - see usbser_open_init() 1350 * dispatch threads; / 1351 * } 1352 * raise DTR; 1353 * wait for carrier (if necessary); 1354 * 1355 * we should also take into consideration that two threads can try to open 1356 * the same physical port simultaneously (/dev/term/N and /dev/cua/N). 1357 * 1358 * return values: 1359 * 0 - success; 1360 * >0 - fail with this error code; 1361 */ 1362static int 1363usbser_open_setup(queue_t *rq, usbser_port_t *pp, int minor, int flag, 1364 cred_t *cr) 1365{ 1366 int rval = USBSER_CONTINUE; 1367 1368 mutex_enter(&pp->port_mutex); 1369 /* 1370 * refer to port state diagram in the header file 1371 */ 1372loop: 1373 switch (pp->port_state) { 1374 case USBSER_PORT_CLOSED: 1375 /* 1376 * initial open 1377 */ 1378 rval = usbser_open_init(pp, minor); 1379 1380 break; 1381 case USBSER_PORT_OPENING_TTY: 1382 /* 1383 * dial-out thread can overtake the port 1384 * if tty open thread is sleeping waiting for carrier 1385 */ 1386 if ((minor & OUTLINE) && (pp->port_flags & USBSER_FL_WOPEN)) { 1387 pp->port_state = USBSER_PORT_OPENING_OUT; 1388 1389 USB_DPRINTF_L3(DPRINT_OPEN, pp->port_lh, 1390 "usbser_open_state: overtake"); 1391 } 1392 1393 /* FALLTHRU */ 1394 case USBSER_PORT_OPENING_OUT: 1395 /* 1396 * if no other open in progress, setup the line 1397 */ 1398 if (USBSER_NO_OTHER_OPEN(pp, minor)) { 1399 rval = usbser_open_line_setup(pp, minor, flag); 1400 1401 break; 1402 } 1403 1404 /* FALLTHRU */ 1405 case USBSER_PORT_CLOSING: 1406 /* 1407 * wait until close active phase ends 1408 */ 1409 if (cv_wait_sig(&pp->port_state_cv, &pp->port_mutex) == 0) { 1410 rval = EINTR; 1411 } 1412 1413 break; 1414 case USBSER_PORT_OPEN: 1415 if ((pp->port_ttycommon.t_flags & TS_XCLUDE) && 1416 secpolicy_excl_open(cr) != 0) { 1417 /* 1418 * exclusive use 1419 */ 1420 rval = EBUSY; 1421 } else if (USBSER_OPEN_IN_OTHER_MODE(pp, minor)) { 1422 /* 1423 * tty and dial-out modes are mutually exclusive 1424 */ 1425 rval = EBUSY; 1426 } else { 1427 /* 1428 * port is being re-open in the same mode 1429 */ 1430 rval = usbser_open_line_setup(pp, minor, flag); 1431 } 1432 1433 break; 1434 default: 1435 rval = ENXIO; 1436 1437 break; 1438 } 1439 1440 if (rval == USBSER_CONTINUE) { 1441 1442 goto loop; 1443 } 1444 1445 /* 1446 * initial open requires additional handling 1447 */ 1448 if (USBSER_IS_OPENING(pp)) { 1449 if (rval == USBSER_COMPLETE) { 1450 if (pp->port_state == USBSER_PORT_OPENING_OUT) { 1451 pp->port_flags |= USBSER_FL_OUT; 1452 } 1453 pp->port_state = USBSER_PORT_OPEN; 1454 cv_broadcast(&pp->port_state_cv); 1455 1456 usbser_open_queues_init(pp, rq); 1457 } else { 1458 usbser_open_fini(pp); 1459 } 1460 } 1461 mutex_exit(&pp->port_mutex); 1462 1463 return (rval); 1464} 1465 1466 1467/* 1468 * initialize the port when opened for the first time 1469 */ 1470static int 1471usbser_open_init(usbser_port_t *pp, int minor) 1472{ 1473 usbser_state_t *usp = pp->port_usp; 1474 tty_common_t *tp = &pp->port_ttycommon; 1475 int rval = ENXIO; 1476 1477 ASSERT(pp->port_state == USBSER_PORT_CLOSED); 1478 1479 /* 1480 * init state 1481 */ 1482 pp->port_act = 0; 1483 pp->port_flags &= USBSER_FL_PRESERVE; 1484 pp->port_flowc = '\0'; 1485 pp->port_wq_data_cnt = 0; 1486 1487 if (minor & OUTLINE) { 1488 pp->port_state = USBSER_PORT_OPENING_OUT; 1489 } else { 1490 pp->port_state = USBSER_PORT_OPENING_TTY; 1491 } 1492 1493 /* 1494 * init termios settings 1495 */ 1496 tp->t_iflag = 0; 1497 tp->t_iocpending = NULL; 1498 tp->t_size.ws_row = tp->t_size.ws_col = 0; 1499 tp->t_size.ws_xpixel = tp->t_size.ws_ypixel = 0; 1500 tp->t_startc = CSTART; 1501 tp->t_stopc = CSTOP; 1502 1503 usbser_check_port_props(pp); 1504 1505 /* 1506 * dispatch wq and rq threads: 1507 * although queues are not enabled at this point, 1508 * we will need wq to run status processing callback 1509 */ 1510 usbser_thr_dispatch(&pp->port_wq_thread); 1511 usbser_thr_dispatch(&pp->port_rq_thread); 1512 1513 /* 1514 * open DSD port 1515 */ 1516 mutex_exit(&pp->port_mutex); 1517 rval = USBSER_DS_OPEN_PORT(usp, pp->port_num); 1518 mutex_enter(&pp->port_mutex); 1519 1520 if (rval != USB_SUCCESS) { 1521 1522 return (ENXIO); 1523 } 1524 pp->port_flags |= USBSER_FL_DSD_OPEN; 1525 1526 /* 1527 * program port with default parameters 1528 */ 1529 if ((rval = usbser_port_program(pp)) != 0) { 1530 1531 return (ENXIO); 1532 } 1533 1534 return (USBSER_CONTINUE); 1535} 1536 1537 1538/* 1539 * create a pair of minor nodes for the port 1540 */ 1541static void 1542usbser_check_port_props(usbser_port_t *pp) 1543{ 1544 dev_info_t *dip = pp->port_usp->us_dip; 1545 tty_common_t *tp = &pp->port_ttycommon; 1546 struct termios *termiosp; 1547 uint_t len; 1548 char name[20]; 1549 1550 /* 1551 * take default modes from "ttymodes" property if it exists 1552 */ 1553 if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 0, 1554 "ttymodes", (uchar_t **)&termiosp, &len) == DDI_PROP_SUCCESS) { 1555 1556 if (len == sizeof (struct termios)) { 1557 tp->t_cflag = termiosp->c_cflag; 1558 1559 if (termiosp->c_iflag & (IXON | IXANY)) { 1560 tp->t_iflag = 1561 termiosp->c_iflag & (IXON | IXANY); 1562 tp->t_startc = termiosp->c_cc[VSTART]; 1563 tp->t_stopc = termiosp->c_cc[VSTOP]; 1564 } 1565 } 1566 ddi_prop_free(termiosp); 1567 } 1568 1569 /* 1570 * look for "ignore-cd" or "port-N-ignore-cd" property 1571 */ 1572 (void) sprintf(name, "port-%d-ignore-cd", pp->port_num); 1573 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1574 "ignore-cd", 0) || 1575 ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, 0)) { 1576 pp->port_flags |= USBSER_FL_IGNORE_CD; 1577 } else { 1578 pp->port_flags &= ~USBSER_FL_IGNORE_CD; 1579 } 1580} 1581 1582 1583/* 1584 * undo what was done in usbser_open_init() 1585 */ 1586static void 1587usbser_open_fini(usbser_port_t *pp) 1588{ 1589 uint_t port_num = pp->port_num; 1590 usbser_state_t *usp = pp->port_usp; 1591 1592 /* 1593 * close DSD if it is open 1594 */ 1595 if (pp->port_flags & USBSER_FL_DSD_OPEN) { 1596 mutex_exit(&pp->port_mutex); 1597 if (USBSER_DS_CLOSE_PORT(usp, port_num) != USB_SUCCESS) { 1598 USB_DPRINTF_L2(DPRINT_CLOSE, pp->port_lh, 1599 "usbser_open_fini: CLOSE_PORT fail"); 1600 } 1601 mutex_enter(&pp->port_mutex); 1602 } 1603 1604 /* 1605 * cancel threads 1606 */ 1607 usbser_thr_cancel(&pp->port_wq_thread); 1608 usbser_thr_cancel(&pp->port_rq_thread); 1609 1610 /* 1611 * unpdate soft state 1612 */ 1613 pp->port_state = USBSER_PORT_CLOSED; 1614 cv_broadcast(&pp->port_state_cv); 1615 cv_broadcast(&pp->port_car_cv); 1616} 1617 1618 1619/* 1620 * setup serial line 1621 */ 1622static int 1623usbser_open_line_setup(usbser_port_t *pp, int minor, int flag) 1624{ 1625 int rval; 1626 1627 mutex_exit(&pp->port_mutex); 1628 /* 1629 * prevent opening a disconnected device 1630 */ 1631 if (!usbser_dev_is_online(pp->port_usp)) { 1632 mutex_enter(&pp->port_mutex); 1633 1634 return (ENXIO); 1635 } 1636 1637 /* raise DTR on every open */ 1638 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, TIOCM_DTR); 1639 1640 mutex_enter(&pp->port_mutex); 1641 /* 1642 * check carrier 1643 */ 1644 rval = usbser_open_carrier_check(pp, minor, flag); 1645 1646 return (rval); 1647} 1648 1649 1650/* 1651 * check carrier and wait if needed 1652 */ 1653static int 1654usbser_open_carrier_check(usbser_port_t *pp, int minor, int flag) 1655{ 1656 tty_common_t *tp = &pp->port_ttycommon; 1657 int val = 0; 1658 int rval; 1659 1660 if (pp->port_flags & USBSER_FL_IGNORE_CD) { 1661 tp->t_flags |= TS_SOFTCAR; 1662 } 1663 1664 /* 1665 * check carrier 1666 */ 1667 if (tp->t_flags & TS_SOFTCAR) { 1668 pp->port_flags |= USBSER_FL_CARR_ON; 1669 } else if (USBSER_DS_GET_MODEM_CTL(pp, TIOCM_CD, &val) != USB_SUCCESS) { 1670 1671 return (ENXIO); 1672 } else if (val & TIOCM_CD) { 1673 pp->port_flags |= USBSER_FL_CARR_ON; 1674 } else { 1675 pp->port_flags &= ~USBSER_FL_CARR_ON; 1676 } 1677 1678 /* 1679 * don't block if 1) not allowed to, 2) this is a local device, 1680 * 3) opening in dial-out mode, or 4) carrier is already on 1681 */ 1682 if ((flag & (FNDELAY | FNONBLOCK)) || (tp->t_cflag & CLOCAL) || 1683 (minor & OUTLINE) || (pp->port_flags & USBSER_FL_CARR_ON)) { 1684 1685 return (USBSER_COMPLETE); 1686 } 1687 1688 /* 1689 * block until carrier up (only in tty mode) 1690 */ 1691 USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh, 1692 "usbser_open_carrier_check: waiting for carrier..."); 1693 1694 pp->port_flags |= USBSER_FL_WOPEN; 1695 1696 rval = cv_wait_sig(&pp->port_car_cv, &pp->port_mutex); 1697 1698 pp->port_flags &= ~USBSER_FL_WOPEN; 1699 1700 if (rval == 0) { 1701 /* 1702 * interrupted with a signal 1703 */ 1704 return (EINTR); 1705 } else { 1706 /* 1707 * try again 1708 */ 1709 return (USBSER_CONTINUE); 1710 } 1711} 1712 1713 1714/* 1715 * during open, setup queues and message processing 1716 */ 1717static void 1718usbser_open_queues_init(usbser_port_t *pp, queue_t *rq) 1719{ 1720 pp->port_ttycommon.t_readq = rq; 1721 pp->port_ttycommon.t_writeq = WR(rq); 1722 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)pp; 1723 1724 qprocson(rq); 1725} 1726 1727 1728/* 1729 * clean up queues and message processing 1730 */ 1731static void 1732usbser_open_queues_fini(usbser_port_t *pp) 1733{ 1734 queue_t *rq = pp->port_ttycommon.t_readq; 1735 1736 mutex_exit(&pp->port_mutex); 1737 /* 1738 * clean up queues 1739 */ 1740 qprocsoff(rq); 1741 1742 /* 1743 * free unused messages 1744 */ 1745 flushq(rq, FLUSHALL); 1746 flushq(WR(rq), FLUSHALL); 1747 1748 rq->q_ptr = WR(rq)->q_ptr = NULL; 1749 ttycommon_close(&pp->port_ttycommon); 1750 mutex_enter(&pp->port_mutex); 1751} 1752 1753 1754/* 1755 * during close, wait until pending data is gone or the signal is sent 1756 */ 1757static void 1758usbser_close_drain(usbser_port_t *pp) 1759{ 1760 int need_drain; 1761 clock_t until; 1762 int rval; 1763 1764 /* 1765 * port_wq_data_cnt indicates amount of data on the write queue, 1766 * which becomes zero when all data is submitted to DSD. But usbser 1767 * stays busy until it gets tx callback from DSD, signalling that 1768 * data has been sent over USB. To be continued in the next comment... 1769 */ 1770 until = ddi_get_lbolt() + 1771 drv_usectohz(USBSER_WQ_DRAIN_TIMEOUT * 1000000); 1772 1773 while ((pp->port_wq_data_cnt > 0) && USBSER_PORT_IS_BUSY(pp)) { 1774 if ((rval = cv_timedwait_sig(&pp->port_act_cv, &pp->port_mutex, 1775 until)) <= 0) { 1776 1777 break; 1778 } 1779 } 1780 1781 /* don't drain if timed out or received a signal */ 1782 need_drain = (pp->port_wq_data_cnt == 0) || !USBSER_PORT_IS_BUSY(pp) || 1783 (rval != 0); 1784 1785 mutex_exit(&pp->port_mutex); 1786 /* 1787 * Once the data reaches USB serial box, it may still be stored in its 1788 * internal output buffer (FIFO). We call DSD drain to ensure that all 1789 * the data is transmitted transmitted over the serial line. 1790 */ 1791 if (need_drain) { 1792 rval = USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT); 1793 if (rval != USB_SUCCESS) { 1794 (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX); 1795 } 1796 } else { 1797 (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX); 1798 } 1799 mutex_enter(&pp->port_mutex); 1800} 1801 1802 1803/* 1804 * during close, cancel break/delay 1805 */ 1806static void 1807usbser_close_cancel_break(usbser_port_t *pp) 1808{ 1809 timeout_id_t delay_id; 1810 1811 if (pp->port_act & USBSER_ACT_BREAK) { 1812 delay_id = pp->port_delay_id; 1813 pp->port_delay_id = 0; 1814 1815 mutex_exit(&pp->port_mutex); 1816 (void) untimeout(delay_id); 1817 (void) USBSER_DS_BREAK_CTL(pp, DS_OFF); 1818 mutex_enter(&pp->port_mutex); 1819 1820 pp->port_act &= ~USBSER_ACT_BREAK; 1821 } 1822} 1823 1824 1825/* 1826 * during close, drop RTS/DTR if necessary 1827 */ 1828static void 1829usbser_close_hangup(usbser_port_t *pp) 1830{ 1831 /* 1832 * drop DTR and RTS if HUPCL is set 1833 */ 1834 if (pp->port_ttycommon.t_cflag & HUPCL) { 1835 mutex_exit(&pp->port_mutex); 1836 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS | TIOCM_DTR, 0); 1837 mutex_enter(&pp->port_mutex); 1838 } 1839} 1840 1841 1842/* 1843 * state cleanup during close 1844 */ 1845static void 1846usbser_close_cleanup(usbser_port_t *pp) 1847{ 1848 usbser_open_queues_fini(pp); 1849 1850 usbser_open_fini(pp); 1851} 1852 1853 1854/* 1855 * 1856 * thread management 1857 * ----------------- 1858 * 1859 * 1860 * dispatch a thread 1861 */ 1862static void 1863usbser_thr_dispatch(usbser_thread_t *thr) 1864{ 1865 usbser_port_t *pp = thr->thr_port; 1866 usbser_state_t *usp = pp->port_usp; 1867 int rval; 1868 1869 ASSERT(mutex_owned(&pp->port_mutex)); 1870 ASSERT((thr->thr_flags & USBSER_THR_RUNNING) == 0); 1871 1872 thr->thr_flags = USBSER_THR_RUNNING; 1873 1874 rval = ddi_taskq_dispatch(usp->us_taskq, thr->thr_func, thr->thr_arg, 1875 DDI_SLEEP); 1876 ASSERT(rval == DDI_SUCCESS); 1877} 1878 1879 1880/* 1881 * cancel a thread 1882 */ 1883static void 1884usbser_thr_cancel(usbser_thread_t *thr) 1885{ 1886 usbser_port_t *pp = thr->thr_port; 1887 1888 ASSERT(mutex_owned(&pp->port_mutex)); 1889 1890 thr->thr_flags &= ~USBSER_THR_RUNNING; 1891 cv_signal(&thr->thr_cv); 1892 1893 /* wait until the thread actually exits */ 1894 do { 1895 cv_wait(&thr->thr_cv, &pp->port_mutex); 1896 1897 } while ((thr->thr_flags & USBSER_THR_EXITED) == 0); 1898} 1899 1900 1901/* 1902 * wake thread 1903 */ 1904static void 1905usbser_thr_wake(usbser_thread_t *thr) 1906{ 1907 usbser_port_t *pp = thr->thr_port; 1908 1909 ASSERT(mutex_owned(&pp->port_mutex)); 1910 1911 thr->thr_flags |= USBSER_THR_WAKE; 1912 cv_signal(&thr->thr_cv); 1913} 1914 1915 1916/* 1917 * thread handling write queue requests 1918 */ 1919static void 1920usbser_wq_thread(void *arg) 1921{ 1922 usbser_thread_t *thr = (usbser_thread_t *)arg; 1923 usbser_port_t *pp = thr->thr_port; 1924 1925 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: enter"); 1926 1927 mutex_enter(&pp->port_mutex); 1928 while (thr->thr_flags & USBSER_THR_RUNNING) { 1929 /* 1930 * when woken, see what we should do 1931 */ 1932 if (thr->thr_flags & USBSER_THR_WAKE) { 1933 thr->thr_flags &= ~USBSER_THR_WAKE; 1934 1935 /* 1936 * status callback pending? 1937 */ 1938 if (pp->port_flags & USBSER_FL_STATUS_CB) { 1939 usbser_status_proc_cb(pp); 1940 } 1941 1942 usbser_wmsg(pp); 1943 } else { 1944 /* 1945 * sleep until woken up to do some work, e.g: 1946 * - new message arrives; 1947 * - data transmit completes; 1948 * - status callback pending; 1949 * - wq thread is cancelled; 1950 */ 1951 cv_wait(&thr->thr_cv, &pp->port_mutex); 1952 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, 1953 "usbser_wq_thread: wakeup"); 1954 } 1955 } 1956 thr->thr_flags |= USBSER_THR_EXITED; 1957 cv_signal(&thr->thr_cv); 1958 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: exit"); 1959 mutex_exit(&pp->port_mutex); 1960} 1961 1962 1963/* 1964 * thread handling read queue requests 1965 */ 1966static void 1967usbser_rq_thread(void *arg) 1968{ 1969 usbser_thread_t *thr = (usbser_thread_t *)arg; 1970 usbser_port_t *pp = thr->thr_port; 1971 1972 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_rq_thread: enter"); 1973 1974 mutex_enter(&pp->port_mutex); 1975 while (thr->thr_flags & USBSER_THR_RUNNING) { 1976 /* 1977 * read service routine will wake us when 1978 * more space is available on the read queue 1979 */ 1980 if (thr->thr_flags & USBSER_THR_WAKE) { 1981 thr->thr_flags &= ~USBSER_THR_WAKE; 1982 1983 /* 1984 * don't process messages until queue is enabled 1985 */ 1986 if (!pp->port_ttycommon.t_readq) { 1987 1988 continue; 1989 } 1990 1991 /* 1992 * check whether we need to resume receive 1993 */ 1994 if (pp->port_flags & USBSER_FL_RX_STOPPED) { 1995 pp->port_flowc = pp->port_ttycommon.t_startc; 1996 usbser_inbound_flow_ctl(pp); 1997 } 1998 1999 /* 2000 * grab more data if available 2001 */ 2002 mutex_exit(&pp->port_mutex); 2003 usbser_rx_cb((caddr_t)pp); 2004 mutex_enter(&pp->port_mutex); 2005 } else { 2006 cv_wait(&thr->thr_cv, &pp->port_mutex); 2007 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, 2008 "usbser_rq_thread: wakeup"); 2009 } 2010 } 2011 thr->thr_flags |= USBSER_THR_EXITED; 2012 cv_signal(&thr->thr_cv); 2013 USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rq_thread: exit"); 2014 mutex_exit(&pp->port_mutex); 2015} 2016 2017 2018/* 2019 * DSD callbacks 2020 * ------------- 2021 * 2022 * Note: to avoid deadlocks with DSD, these callbacks 2023 * should not call DSD functions that can block. 2024 * 2025 * 2026 * transmit callback 2027 * 2028 * invoked by DSD when the last byte of data is transmitted over USB 2029 */ 2030static void 2031usbser_tx_cb(caddr_t arg) 2032{ 2033 usbser_port_t *pp = (usbser_port_t *)arg; 2034 int online; 2035 2036 online = usbser_dev_is_online(pp->port_usp); 2037 2038 mutex_enter(&pp->port_mutex); 2039 USB_DPRINTF_L4(DPRINT_TX_CB, pp->port_lh, 2040 "usbser_tx_cb: act=%x curthread=%p", pp->port_act, 2041 (void *)curthread); 2042 2043 usbser_release_port_act(pp, USBSER_ACT_TX); 2044 2045 if (online && USBSER_PORT_ACCESS_OK(pp) && !USBSER_PORT_IS_BUSY(pp)) { 2046 /* 2047 * wake wq thread for further data/ioctl processing 2048 */ 2049 usbser_thr_wake(&pp->port_wq_thread); 2050 } 2051 mutex_exit(&pp->port_mutex); 2052} 2053 2054 2055/* 2056 * receive callback 2057 * 2058 * invoked by DSD when there is more data for us to pick 2059 */ 2060static void 2061usbser_rx_cb(caddr_t arg) 2062{ 2063 usbser_port_t *pp = (usbser_port_t *)arg; 2064 queue_t *rq, *wq; 2065 mblk_t *mp; /* current mblk */ 2066 mblk_t *data, *data_tail; /* M_DATA mblk list and its tail */ 2067 mblk_t *emp; /* error (M_BREAK) mblk */ 2068 2069 USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, "usbser_rx_cb"); 2070 2071 if (!usbser_dev_is_online(pp->port_usp)) { 2072 2073 return; 2074 } 2075 2076 /* get data from DSD */ 2077 if ((mp = USBSER_DS_RX(pp)) == NULL) { 2078 2079 return; 2080 } 2081 2082 mutex_enter(&pp->port_mutex); 2083 if ((!USBSER_PORT_ACCESS_OK(pp)) || 2084 ((pp->port_ttycommon.t_cflag & CREAD) == 0)) { 2085 freemsg(mp); 2086 mutex_exit(&pp->port_mutex); 2087 USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh, 2088 "usbser_rx_cb: access not ok or receiver disabled"); 2089 2090 return; 2091 } 2092 2093 usbser_serialize_port_act(pp, USBSER_ACT_RX); 2094 2095 rq = pp->port_ttycommon.t_readq; 2096 wq = pp->port_ttycommon.t_writeq; 2097 mutex_exit(&pp->port_mutex); 2098 2099 /* 2100 * DSD data is a b_cont-linked list of M_DATA and M_BREAK blocks. 2101 * M_DATA is correctly received data. 2102 * M_BREAK is a character with either framing or parity error. 2103 * 2104 * this loop runs through the list of mblks. when it meets an M_BREAK, 2105 * it sends all leading M_DATA's in one shot, then sends M_BREAK. 2106 * in the trivial case when list contains only M_DATA's, the loop 2107 * does nothing but set data variable. 2108 */ 2109 data = data_tail = NULL; 2110 while (mp) { 2111 /* 2112 * skip data until we meet M_BREAK or end of list 2113 */ 2114 if (DB_TYPE(mp) == M_DATA) { 2115 if (data == NULL) { 2116 data = mp; 2117 } 2118 data_tail = mp; 2119 mp = mp->b_cont; 2120 2121 continue; 2122 } 2123 2124 /* detach data list from mp */ 2125 if (data_tail) { 2126 data_tail->b_cont = NULL; 2127 } 2128 /* detach emp from the list */ 2129 emp = mp; 2130 mp = mp->b_cont; 2131 emp->b_cont = NULL; 2132 2133 /* DSD shouldn't send anything but M_DATA or M_BREAK */ 2134 if ((DB_TYPE(emp) != M_BREAK) || (MBLKL(emp) != 2)) { 2135 freemsg(emp); 2136 USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh, 2137 "usbser_rx_cb: bad message"); 2138 2139 continue; 2140 } 2141 2142 /* 2143 * first tweak and send M_DATA's 2144 */ 2145 if (data) { 2146 usbser_rx_massage_data(pp, data); 2147 usbser_rx_cb_put(pp, rq, wq, data); 2148 data = data_tail = NULL; 2149 } 2150 2151 /* 2152 * now tweak and send M_BREAK 2153 */ 2154 mutex_enter(&pp->port_mutex); 2155 usbser_rx_massage_mbreak(pp, emp); 2156 mutex_exit(&pp->port_mutex); 2157 usbser_rx_cb_put(pp, rq, wq, emp); 2158 } 2159 2160 /* send the rest of the data, if any */ 2161 if (data) { 2162 usbser_rx_massage_data(pp, data); 2163 usbser_rx_cb_put(pp, rq, wq, data); 2164 } 2165 2166 mutex_enter(&pp->port_mutex); 2167 usbser_release_port_act(pp, USBSER_ACT_RX); 2168 mutex_exit(&pp->port_mutex); 2169} 2170 2171/* 2172 * the joys of termio -- this is to accomodate Unix98 assertion: 2173 * 2174 * If PARENB is supported and is set, when PARMRK is set, and CSIZE is 2175 * set to CS8, and IGNPAR is clear, and ISTRIP is clear, a valid 2176 * character of '\377' is read as '\377', '\377'. 2177 * 2178 * Posix Ref: Assertion 7.1.2.2-16(C) 2179 * 2180 * this requires the driver to scan every incoming valid character 2181 */ 2182static void 2183usbser_rx_massage_data(usbser_port_t *pp, mblk_t *mp) 2184{ 2185 tty_common_t *tp = &pp->port_ttycommon; 2186 uchar_t *p; 2187 mblk_t *newmp; 2188 int tailsz; 2189 2190 /* avoid scanning if possible */ 2191 mutex_enter(&pp->port_mutex); 2192 if (!((tp->t_cflag & PARENB) && (tp->t_iflag & PARMRK) && 2193 ((tp->t_cflag & CSIZE) == CS8) && 2194 ((tp->t_iflag & (IGNPAR|ISTRIP)) == 0))) { 2195 mutex_exit(&pp->port_mutex); 2196 2197 return; 2198 } 2199 mutex_exit(&pp->port_mutex); 2200 2201 while (mp) { 2202 for (p = mp->b_rptr; p < mp->b_wptr; ) { 2203 if (*p++ != 0377) { 2204 2205 continue; 2206 } 2207 USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, 2208 "usbser_rx_massage_data: mp=%p off=%ld(%ld)", 2209 (void *)mp, _PTRDIFF(p, mp->b_rptr) - 1, 2210 (long)MBLKL(mp)); 2211 2212 /* 2213 * insert another 0377 after this one. all data after 2214 * the original 0377 have to be copied to the new mblk 2215 */ 2216 tailsz = _PTRDIFF(mp->b_wptr, p); 2217 if ((newmp = allocb(tailsz + 1, BPRI_HI)) == NULL) { 2218 USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh, 2219 "usbser_rx_massage_data: allocb failed"); 2220 2221 continue; 2222 } 2223 2224 /* fill in the new mblk */ 2225 *newmp->b_wptr++ = 0377; 2226 if (tailsz > 0) { 2227 bcopy(p, newmp->b_wptr, tailsz); 2228 newmp->b_wptr += tailsz; 2229 } 2230 /* shrink the original mblk */ 2231 mp->b_wptr = p; 2232 2233 newmp->b_cont = mp->b_cont; 2234 mp->b_cont = newmp; 2235 p = newmp->b_rptr + 1; 2236 mp = newmp; 2237 } 2238 mp = mp->b_cont; 2239 } 2240} 2241 2242/* 2243 * more joys of termio 2244 */ 2245static void 2246usbser_rx_massage_mbreak(usbser_port_t *pp, mblk_t *mp) 2247{ 2248 tty_common_t *tp = &pp->port_ttycommon; 2249 uchar_t err, c; 2250 2251 err = *mp->b_rptr; 2252 c = *(mp->b_rptr + 1); 2253 2254 if ((err & (DS_FRAMING_ERR | DS_BREAK_ERR)) && (c == 0)) { 2255 /* break */ 2256 mp->b_rptr += 2; 2257 } else if (!(tp->t_iflag & INPCK) && (err & (DS_PARITY_ERR))) { 2258 /* Posix Ref: Assertion 7.1.2.2-20(C) */ 2259 mp->b_rptr++; 2260 DB_TYPE(mp) = M_DATA; 2261 } else { 2262 /* for ldterm to handle */ 2263 mp->b_rptr++; 2264 } 2265 2266 USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, 2267 "usbser_rx_massage_mbreak: type=%x len=%ld [0]=0%o", 2268 DB_TYPE(mp), (long)MBLKL(mp), (MBLKL(mp) > 0) ? *mp->b_rptr : 45); 2269} 2270 2271 2272/* 2273 * in rx callback, try to send an mblk upstream 2274 */ 2275static void 2276usbser_rx_cb_put(usbser_port_t *pp, queue_t *rq, queue_t *wq, mblk_t *mp) 2277{ 2278 if (canputnext(rq)) { 2279 putnext(rq, mp); 2280 } else if (canput(rq) && putq(rq, mp)) { 2281 /* 2282 * full queue indicates the need for inbound flow control 2283 */ 2284 (void) putctl(wq, M_STOPI); 2285 usbser_st_put_stopi++; 2286 2287 USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh, 2288 "usbser_rx_cb: cannot putnext, flow ctl"); 2289 } else { 2290 freemsg(mp); 2291 usbser_st_rx_data_loss++; 2292 (void) putctl(wq, M_STOPI); 2293 usbser_st_put_stopi++; 2294 2295 USB_DPRINTF_L1(DPRINT_RX_CB, pp->port_lh, 2296 "input overrun"); 2297 } 2298} 2299 2300 2301/* 2302 * modem status change callback 2303 * 2304 * each time external status lines are changed, DSD calls this routine 2305 */ 2306static void 2307usbser_status_cb(caddr_t arg) 2308{ 2309 usbser_port_t *pp = (usbser_port_t *)arg; 2310 2311 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_cb"); 2312 2313 if (!usbser_dev_is_online(pp->port_usp)) { 2314 2315 return; 2316 } 2317 2318 /* 2319 * actual processing will be done in usbser_status_proc_cb() 2320 * running in wq thread 2321 */ 2322 mutex_enter(&pp->port_mutex); 2323 if (USBSER_PORT_ACCESS_OK(pp) || USBSER_IS_OPENING(pp)) { 2324 pp->port_flags |= USBSER_FL_STATUS_CB; 2325 usbser_thr_wake(&pp->port_wq_thread); 2326 } 2327 mutex_exit(&pp->port_mutex); 2328} 2329 2330 2331/* 2332 * modem status change 2333 */ 2334static void 2335usbser_status_proc_cb(usbser_port_t *pp) 2336{ 2337 tty_common_t *tp = &pp->port_ttycommon; 2338 queue_t *rq, *wq; 2339 int status; 2340 int drop_dtr = 0; 2341 int rq_msg = 0, wq_msg = 0; 2342 2343 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_proc_cb"); 2344 2345 pp->port_flags &= ~USBSER_FL_STATUS_CB; 2346 2347 mutex_exit(&pp->port_mutex); 2348 if (!usbser_dev_is_online(pp->port_usp)) { 2349 mutex_enter(&pp->port_mutex); 2350 2351 return; 2352 } 2353 2354 /* get modem status */ 2355 if (USBSER_DS_GET_MODEM_CTL(pp, -1, &status) != USB_SUCCESS) { 2356 mutex_enter(&pp->port_mutex); 2357 2358 return; 2359 } 2360 2361 mutex_enter(&pp->port_mutex); 2362 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2363 2364 rq = pp->port_ttycommon.t_readq; 2365 wq = pp->port_ttycommon.t_writeq; 2366 2367 /* 2368 * outbound flow control 2369 */ 2370 if (tp->t_cflag & CRTSCTS) { 2371 if (!(status & TIOCM_CTS)) { 2372 /* 2373 * CTS dropped, stop xmit 2374 */ 2375 if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) { 2376 wq_msg = M_STOP; 2377 } 2378 } else if (pp->port_flags & USBSER_FL_TX_STOPPED) { 2379 /* 2380 * CTS raised, resume xmit 2381 */ 2382 wq_msg = M_START; 2383 } 2384 } 2385 2386 /* 2387 * check carrier 2388 */ 2389 if ((status & TIOCM_CD) || (tp->t_flags & TS_SOFTCAR)) { 2390 /* 2391 * carrier present 2392 */ 2393 if ((pp->port_flags & USBSER_FL_CARR_ON) == 0) { 2394 pp->port_flags |= USBSER_FL_CARR_ON; 2395 2396 rq_msg = M_UNHANGUP; 2397 /* 2398 * wake open 2399 */ 2400 if (pp->port_flags & USBSER_FL_WOPEN) { 2401 cv_broadcast(&pp->port_car_cv); 2402 } 2403 2404 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, 2405 "usbser_status_cb: carr on"); 2406 } 2407 } else if (pp->port_flags & USBSER_FL_CARR_ON) { 2408 pp->port_flags &= ~USBSER_FL_CARR_ON; 2409 /* 2410 * carrier went away: if not local line, drop DTR 2411 */ 2412 if (!(tp->t_cflag & CLOCAL)) { 2413 drop_dtr = 1; 2414 rq_msg = M_HANGUP; 2415 } 2416 if ((pp->port_flags & USBSER_FL_TX_STOPPED) && (wq_msg == 0)) { 2417 wq_msg = M_START; 2418 } 2419 2420 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, 2421 "usbser_status_cb: carr off"); 2422 } 2423 mutex_exit(&pp->port_mutex); 2424 2425 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, 2426 "usbser_status_cb: rq_msg=%d wq_msg=%d", rq_msg, wq_msg); 2427 2428 /* 2429 * commit postponed actions now 2430 * do so only if port is fully open (queues are enabled) 2431 */ 2432 if (rq) { 2433 if (rq_msg) { 2434 (void) putnextctl(rq, rq_msg); 2435 } 2436 if (drop_dtr) { 2437 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, 0); 2438 } 2439 if (wq_msg) { 2440 (void) putctl(wq, wq_msg); 2441 } 2442 } 2443 2444 mutex_enter(&pp->port_mutex); 2445 usbser_release_port_act(pp, USBSER_ACT_CTL); 2446} 2447 2448 2449/* 2450 * serial support 2451 * -------------- 2452 * 2453 * 2454 * this routine is run by wq thread every time it's woken, 2455 * i.e. when the queue contains messages to process 2456 */ 2457static void 2458usbser_wmsg(usbser_port_t *pp) 2459{ 2460 queue_t *q = pp->port_ttycommon.t_writeq; 2461 mblk_t *mp; 2462 int msgtype; 2463 2464 ASSERT(mutex_owned(&pp->port_mutex)); 2465 2466 if (q == NULL) { 2467 USB_DPRINTF_L3(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=NULL"); 2468 2469 return; 2470 } 2471 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=%p act=%x 0x%x", 2472 (void *)q, pp->port_act, q->q_first ? DB_TYPE(q->q_first) : 0xff); 2473 2474 while ((mp = getq(q)) != NULL) { 2475 msgtype = DB_TYPE(mp); 2476 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: " 2477 "type=%s (0x%x)", usbser_msgtype2str(msgtype), msgtype); 2478 2479 switch (msgtype) { 2480 /* 2481 * high-priority messages 2482 */ 2483 case M_STOP: 2484 usbser_stop(pp, mp); 2485 2486 break; 2487 case M_START: 2488 usbser_start(pp, mp); 2489 2490 break; 2491 case M_STOPI: 2492 usbser_stopi(pp, mp); 2493 2494 break; 2495 case M_STARTI: 2496 usbser_starti(pp, mp); 2497 2498 break; 2499 case M_IOCDATA: 2500 usbser_iocdata(pp, mp); 2501 2502 break; 2503 case M_FLUSH: 2504 usbser_flush(pp, mp); 2505 2506 break; 2507 /* 2508 * normal-priority messages 2509 */ 2510 case M_BREAK: 2511 usbser_break(pp, mp); 2512 2513 break; 2514 case M_DELAY: 2515 usbser_delay(pp, mp); 2516 2517 break; 2518 case M_DATA: 2519 if (usbser_data(pp, mp) != USB_SUCCESS) { 2520 (void) putbq(q, mp); 2521 2522 return; 2523 } 2524 2525 break; 2526 case M_IOCTL: 2527 if (usbser_ioctl(pp, mp) != USB_SUCCESS) { 2528 (void) putbq(q, mp); 2529 2530 return; 2531 } 2532 2533 break; 2534 default: 2535 freemsg(mp); 2536 2537 break; 2538 } 2539 } 2540} 2541 2542 2543/* 2544 * process M_DATA message 2545 */ 2546static int 2547usbser_data(usbser_port_t *pp, mblk_t *mp) 2548{ 2549 /* put off until current transfer ends or delay is over */ 2550 if ((pp->port_act & USBSER_ACT_TX) || 2551 (pp->port_act & USBSER_ACT_DELAY)) { 2552 2553 return (USB_FAILURE); 2554 } 2555 if (MBLKL(mp) <= 0) { 2556 freemsg(mp); 2557 2558 return (USB_SUCCESS); 2559 } 2560 2561 pp->port_act |= USBSER_ACT_TX; 2562 pp->port_wq_data_cnt -= msgdsize(mp); 2563 2564 mutex_exit(&pp->port_mutex); 2565 /* DSD is required to accept data block in any case */ 2566 (void) USBSER_DS_TX(pp, mp); 2567 mutex_enter(&pp->port_mutex); 2568 2569 return (USB_SUCCESS); 2570} 2571 2572 2573/* 2574 * process an M_IOCTL message 2575 */ 2576static int 2577usbser_ioctl(usbser_port_t *pp, mblk_t *mp) 2578{ 2579 tty_common_t *tp = &pp->port_ttycommon; 2580 queue_t *q = tp->t_writeq; 2581 struct iocblk *iocp; 2582 int cmd; 2583 mblk_t *datamp; 2584 int error = 0, rval; 2585 int val; 2586 2587 ASSERT(mutex_owned(&pp->port_mutex)); 2588 ASSERT(DB_TYPE(mp) == M_IOCTL); 2589 2590 iocp = (struct iocblk *)mp->b_rptr; 2591 cmd = iocp->ioc_cmd; 2592 2593 USB_DPRINTF_L4(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: " 2594 "mp=%p %s (0x%x)", (void *)mp, usbser_ioctl2str(cmd), cmd); 2595 2596 if (tp->t_iocpending != NULL) { 2597 /* 2598 * We were holding an ioctl response pending the 2599 * availability of an mblk to hold data to be passed up; 2600 * another ioctl came through, which means that ioctl 2601 * must have timed out or been aborted. 2602 */ 2603 freemsg(tp->t_iocpending); 2604 tp->t_iocpending = NULL; 2605 } 2606 2607 switch (cmd) { 2608 case TIOCMGET: 2609 case TIOCMBIC: 2610 case TIOCMBIS: 2611 case TIOCMSET: 2612 case CONSOPENPOLLEDIO: 2613 case CONSCLOSEPOLLEDIO: 2614 case CONSSETABORTENABLE: 2615 case CONSGETABORTENABLE: 2616 /* 2617 * For the above ioctls do not call ttycommon_ioctl() because 2618 * this function frees up the message block (mp->b_cont) that 2619 * contains the address of the user variable where we need to 2620 * pass back the bit array. 2621 */ 2622 error = -1; 2623 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2624 mutex_exit(&pp->port_mutex); 2625 break; 2626 2627 case TCSBRK: 2628 /* serialize breaks */ 2629 if (pp->port_act & USBSER_ACT_BREAK) 2630 return (USB_FAILURE); 2631 /*FALLTHRU*/ 2632 default: 2633 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2634 mutex_exit(&pp->port_mutex); 2635 (void) ttycommon_ioctl(tp, q, mp, &error); 2636 break; 2637 } 2638 2639 if (error == 0) { 2640 /* 2641 * ttycommon_ioctl() did most of the work 2642 * we just use the data it set up 2643 */ 2644 switch (cmd) { 2645 case TCSETSF: 2646 case TCSETSW: 2647 case TCSETA: 2648 case TCSETAW: 2649 case TCSETAF: 2650 (void) USBSER_DS_FIFO_DRAIN(pp, DS_TX); 2651 /*FALLTHRU*/ 2652 2653 case TCSETS: 2654 mutex_enter(&pp->port_mutex); 2655 error = usbser_port_program(pp); 2656 mutex_exit(&pp->port_mutex); 2657 break; 2658 } 2659 goto end; 2660 2661 } else if (error > 0) { 2662 USB_DPRINTF_L3(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: " 2663 "ttycommon_ioctl returned %d", error); 2664 goto end; 2665 } 2666 2667 /* 2668 * error < 0: ttycommon_ioctl() didn't do anything, we process it here 2669 */ 2670 error = 0; 2671 switch (cmd) { 2672 case TCSBRK: 2673 if ((error = miocpullup(mp, sizeof (int))) != 0) 2674 break; 2675 2676 /* drain output */ 2677 (void) USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT); 2678 2679 /* 2680 * if required, set break 2681 */ 2682 if (*(int *)mp->b_cont->b_rptr == 0) { 2683 if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS) { 2684 error = EIO; 2685 break; 2686 } 2687 2688 mutex_enter(&pp->port_mutex); 2689 pp->port_act |= USBSER_ACT_BREAK; 2690 pp->port_delay_id = timeout(usbser_restart, pp, 2691 drv_usectohz(250000)); 2692 mutex_exit(&pp->port_mutex); 2693 } 2694 mioc2ack(mp, NULL, 0, 0); 2695 break; 2696 2697 case TIOCSBRK: /* set break */ 2698 if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS) 2699 error = EIO; 2700 else 2701 mioc2ack(mp, NULL, 0, 0); 2702 break; 2703 2704 case TIOCCBRK: /* clear break */ 2705 if (USBSER_DS_BREAK_CTL(pp, DS_OFF) != USB_SUCCESS) 2706 error = EIO; 2707 else 2708 mioc2ack(mp, NULL, 0, 0); 2709 break; 2710 2711 case TIOCMSET: /* set all modem bits */ 2712 case TIOCMBIS: /* bis modem bits */ 2713 case TIOCMBIC: /* bic modem bits */ 2714 if (iocp->ioc_count == TRANSPARENT) { 2715 mcopyin(mp, NULL, sizeof (int), NULL); 2716 break; 2717 } 2718 if ((error = miocpullup(mp, sizeof (int))) != 0) 2719 break; 2720 2721 val = *(int *)mp->b_cont->b_rptr; 2722 if (cmd == TIOCMSET) { 2723 rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val); 2724 } else if (cmd == TIOCMBIS) { 2725 rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1); 2726 } else if (cmd == TIOCMBIC) { 2727 rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0); 2728 } 2729 if (rval == USB_SUCCESS) 2730 mioc2ack(mp, NULL, 0, 0); 2731 else 2732 error = EIO; 2733 break; 2734 2735 case TIOCSILOOP: 2736 if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) { 2737 if (USBSER_DS_LOOPBACK(pp, DS_ON) == USB_SUCCESS) 2738 mioc2ack(mp, NULL, 0, 0); 2739 else 2740 error = EIO; 2741 } else { 2742 error = EINVAL; 2743 } 2744 break; 2745 2746 case TIOCCILOOP: 2747 if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) { 2748 if (USBSER_DS_LOOPBACK(pp, DS_OFF) == USB_SUCCESS) 2749 mioc2ack(mp, NULL, 0, 0); 2750 else 2751 error = EIO; 2752 } else { 2753 error = EINVAL; 2754 } 2755 break; 2756 2757 case TIOCMGET: /* get all modem bits */ 2758 if ((datamp = allocb(sizeof (int), BPRI_MED)) == NULL) { 2759 error = EAGAIN; 2760 break; 2761 } 2762 rval = USBSER_DS_GET_MODEM_CTL(pp, -1, (int *)datamp->b_rptr); 2763 if (rval != USB_SUCCESS) { 2764 error = EIO; 2765 break; 2766 } 2767 if (iocp->ioc_count == TRANSPARENT) 2768 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 2769 else 2770 mioc2ack(mp, datamp, sizeof (int), 0); 2771 break; 2772 2773 case CONSOPENPOLLEDIO: 2774 error = usbser_polledio_init(pp); 2775 if (error != 0) 2776 break; 2777 2778 error = miocpullup(mp, sizeof (struct cons_polledio *)); 2779 if (error != 0) 2780 break; 2781 2782 *(struct cons_polledio **)mp->b_cont->b_rptr = &usbser_polledio; 2783 mioc2ack(mp, NULL, 0, 0); 2784 break; 2785 2786 case CONSCLOSEPOLLEDIO: 2787 usbser_polledio_fini(pp); 2788 mioc2ack(mp, NULL, 0, 0); 2789 break; 2790 2791 case CONSSETABORTENABLE: 2792 error = secpolicy_console(iocp->ioc_cr); 2793 if (error != 0) 2794 break; 2795 2796 if (iocp->ioc_count != TRANSPARENT) { 2797 error = EINVAL; 2798 break; 2799 } 2800 2801 /* 2802 * To do: implement console abort support 2803 * This involves adding a console flag to usbser 2804 * state structure. If flag is set, parse input stream 2805 * for abort sequence (see asy for example). 2806 * 2807 * For now, run mdb -K to get kmdb prompt. 2808 */ 2809 if (*(intptr_t *)mp->b_cont->b_rptr) 2810 usbser_console_abort = 1; 2811 else 2812 usbser_console_abort = 0; 2813 mioc2ack(mp, NULL, 0, 0); 2814 break; 2815 2816 case CONSGETABORTENABLE: 2817 /*CONSTANTCONDITION*/ 2818 ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 2819 /* 2820 * Store the return value right in the payload 2821 * we were passed. Crude. 2822 */ 2823 mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 2824 *(boolean_t *)mp->b_cont->b_rptr = (usbser_console_abort != 0); 2825 mioc2ack(mp, NULL, 0, 0); 2826 break; 2827 2828 default: 2829 error = EINVAL; 2830 break; 2831 } 2832end: 2833 if (error != 0) 2834 miocnak(q, mp, 0, error); 2835 else 2836 qreply(q, mp); 2837 2838 mutex_enter(&pp->port_mutex); 2839 usbser_release_port_act(pp, USBSER_ACT_CTL); 2840 2841 return (USB_SUCCESS); 2842} 2843 2844 2845/* 2846 * process M_IOCDATA message 2847 */ 2848static void 2849usbser_iocdata(usbser_port_t *pp, mblk_t *mp) 2850{ 2851 tty_common_t *tp = &pp->port_ttycommon; 2852 queue_t *q = tp->t_writeq; 2853 struct copyresp *csp; 2854 int cmd; 2855 int val; 2856 int rval; 2857 2858 ASSERT(mutex_owned(&pp->port_mutex)); 2859 2860 csp = (struct copyresp *)mp->b_rptr; 2861 cmd = csp->cp_cmd; 2862 2863 if (csp->cp_rval != 0) { 2864 freemsg(mp); 2865 return; 2866 } 2867 2868 switch (cmd) { 2869 case TIOCMSET: /* set all modem bits */ 2870 case TIOCMBIS: /* bis modem bits */ 2871 case TIOCMBIC: /* bic modem bits */ 2872 if ((mp->b_cont == NULL) || 2873 (MBLKL(mp->b_cont) < sizeof (int))) { 2874 miocnak(q, mp, 0, EINVAL); 2875 break; 2876 } 2877 val = *(int *)mp->b_cont->b_rptr; 2878 2879 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2880 mutex_exit(&pp->port_mutex); 2881 2882 if (cmd == TIOCMSET) { 2883 rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val); 2884 } else if (cmd == TIOCMBIS) { 2885 rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1); 2886 } else if (cmd == TIOCMBIC) { 2887 rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0); 2888 } 2889 2890 if (mp->b_cont) { 2891 freemsg(mp->b_cont); 2892 mp->b_cont = NULL; 2893 } 2894 2895 if (rval == USB_SUCCESS) 2896 miocack(q, mp, 0, 0); 2897 else 2898 miocnak(q, mp, 0, EIO); 2899 2900 mutex_enter(&pp->port_mutex); 2901 usbser_release_port_act(pp, USBSER_ACT_CTL); 2902 break; 2903 2904 case TIOCMGET: /* get all modem bits */ 2905 mutex_exit(&pp->port_mutex); 2906 miocack(q, mp, 0, 0); 2907 mutex_enter(&pp->port_mutex); 2908 break; 2909 2910 default: 2911 mutex_exit(&pp->port_mutex); 2912 miocnak(q, mp, 0, EINVAL); 2913 mutex_enter(&pp->port_mutex); 2914 break; 2915 } 2916} 2917 2918 2919/* 2920 * handle M_START[I]/M_STOP[I] messages 2921 */ 2922static void 2923usbser_stop(usbser_port_t *pp, mblk_t *mp) 2924{ 2925 usbser_st_mstop++; 2926 if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) { 2927 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2928 pp->port_flags |= USBSER_FL_TX_STOPPED; 2929 2930 mutex_exit(&pp->port_mutex); 2931 USBSER_DS_STOP(pp, DS_TX); 2932 mutex_enter(&pp->port_mutex); 2933 2934 usbser_release_port_act(pp, USBSER_ACT_TX); 2935 usbser_release_port_act(pp, USBSER_ACT_CTL); 2936 } 2937 freemsg(mp); 2938} 2939 2940 2941static void 2942usbser_start(usbser_port_t *pp, mblk_t *mp) 2943{ 2944 usbser_st_mstart++; 2945 if (pp->port_flags & USBSER_FL_TX_STOPPED) { 2946 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2947 pp->port_flags &= ~USBSER_FL_TX_STOPPED; 2948 2949 mutex_exit(&pp->port_mutex); 2950 USBSER_DS_START(pp, DS_TX); 2951 mutex_enter(&pp->port_mutex); 2952 usbser_release_port_act(pp, USBSER_ACT_CTL); 2953 } 2954 freemsg(mp); 2955} 2956 2957 2958static void 2959usbser_stopi(usbser_port_t *pp, mblk_t *mp) 2960{ 2961 usbser_st_mstopi++; 2962 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2963 pp->port_flowc = pp->port_ttycommon.t_stopc; 2964 usbser_inbound_flow_ctl(pp); 2965 usbser_release_port_act(pp, USBSER_ACT_CTL); 2966 freemsg(mp); 2967} 2968 2969static void 2970usbser_starti(usbser_port_t *pp, mblk_t *mp) 2971{ 2972 usbser_st_mstarti++; 2973 usbser_serialize_port_act(pp, USBSER_ACT_CTL); 2974 pp->port_flowc = pp->port_ttycommon.t_startc; 2975 usbser_inbound_flow_ctl(pp); 2976 usbser_release_port_act(pp, USBSER_ACT_CTL); 2977 freemsg(mp); 2978} 2979 2980/* 2981 * process M_FLUSH message 2982 */ 2983static void 2984usbser_flush(usbser_port_t *pp, mblk_t *mp) 2985{ 2986 queue_t *q = pp->port_ttycommon.t_writeq; 2987 2988 if (*mp->b_rptr & FLUSHW) { 2989 mutex_exit(&pp->port_mutex); 2990 (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX); /* flush FIFO buffers */ 2991 flushq(q, FLUSHDATA); /* flush write queue */ 2992 mutex_enter(&pp->port_mutex); 2993 2994 usbser_release_port_act(pp, USBSER_ACT_TX); 2995 2996 *mp->b_rptr &= ~FLUSHW; 2997 } 2998 if (*mp->b_rptr & FLUSHR) { 2999 /* 3000 * flush FIFO buffers 3001 */ 3002 mutex_exit(&pp->port_mutex); 3003 (void) USBSER_DS_FIFO_FLUSH(pp, DS_RX); 3004 flushq(RD(q), FLUSHDATA); 3005 qreply(q, mp); 3006 mutex_enter(&pp->port_mutex); 3007 } else { 3008 freemsg(mp); 3009 } 3010} 3011 3012/* 3013 * process M_BREAK message 3014 */ 3015static void 3016usbser_break(usbser_port_t *pp, mblk_t *mp) 3017{ 3018 int rval; 3019 3020 /* 3021 * set the break and arrange for usbser_restart() to be called in 1/4 s 3022 */ 3023 mutex_exit(&pp->port_mutex); 3024 rval = USBSER_DS_BREAK_CTL(pp, DS_ON); 3025 mutex_enter(&pp->port_mutex); 3026 3027 if (rval == USB_SUCCESS) { 3028 pp->port_act |= USBSER_ACT_BREAK; 3029 pp->port_delay_id = timeout(usbser_restart, pp, 3030 drv_usectohz(250000)); 3031 } 3032 freemsg(mp); 3033} 3034 3035 3036/* 3037 * process M_DELAY message 3038 */ 3039static void 3040usbser_delay(usbser_port_t *pp, mblk_t *mp) 3041{ 3042 /* 3043 * arrange for usbser_restart() to be called when the delay expires 3044 */ 3045 pp->port_act |= USBSER_ACT_DELAY; 3046 pp->port_delay_id = timeout(usbser_restart, pp, 3047 (clock_t)(*(uchar_t *)mp->b_rptr + 6)); 3048 freemsg(mp); 3049} 3050 3051 3052/* 3053 * restart output on a line after a delay or break timer expired 3054 */ 3055static void 3056usbser_restart(void *arg) 3057{ 3058 usbser_port_t *pp = (usbser_port_t *)arg; 3059 3060 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_restart"); 3061 3062 mutex_enter(&pp->port_mutex); 3063 /* if cancelled, return immediately */ 3064 if (pp->port_delay_id == 0) { 3065 mutex_exit(&pp->port_mutex); 3066 3067 return; 3068 } 3069 pp->port_delay_id = 0; 3070 3071 /* clear break if necessary */ 3072 if (pp->port_act & USBSER_ACT_BREAK) { 3073 mutex_exit(&pp->port_mutex); 3074 (void) USBSER_DS_BREAK_CTL(pp, DS_OFF); 3075 mutex_enter(&pp->port_mutex); 3076 } 3077 3078 usbser_release_port_act(pp, USBSER_ACT_BREAK | USBSER_ACT_DELAY); 3079 3080 /* wake wq thread to resume message processing */ 3081 usbser_thr_wake(&pp->port_wq_thread); 3082 mutex_exit(&pp->port_mutex); 3083} 3084 3085 3086/* 3087 * program port hardware with the chosen parameters 3088 * most of the operation is based on the values of 'c_iflag' and 'c_cflag' 3089 */ 3090static int 3091usbser_port_program(usbser_port_t *pp) 3092{ 3093 tty_common_t *tp = &pp->port_ttycommon; 3094 int baudrate; 3095 int c_flag; 3096 ds_port_param_entry_t pe[6]; 3097 ds_port_params_t params; 3098 int flow_ctl, ctl_val; 3099 int err = 0; 3100 3101 baudrate = tp->t_cflag & CBAUD; 3102 if (tp->t_cflag & CBAUDEXT) { 3103 baudrate += 16; 3104 } 3105 3106 /* 3107 * set input speed same as output, as split speed not supported 3108 */ 3109 if (tp->t_cflag & (CIBAUD|CIBAUDEXT)) { 3110 tp->t_cflag &= ~(CIBAUD); 3111 if (baudrate > CBAUD) { 3112 tp->t_cflag |= CIBAUDEXT; 3113 tp->t_cflag |= 3114 (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD); 3115 } else { 3116 tp->t_cflag &= ~CIBAUDEXT; 3117 tp->t_cflag |= ((baudrate << IBSHIFT) & CIBAUD); 3118 } 3119 } 3120 3121 c_flag = tp->t_cflag; 3122 3123 /* 3124 * flow control 3125 */ 3126 flow_ctl = tp->t_iflag & (IXON | IXANY | IXOFF); 3127 if (c_flag & CRTSCTS) { 3128 flow_ctl |= CTSXON; 3129 } 3130 if (c_flag & CRTSXOFF) { 3131 flow_ctl |= RTSXOFF; 3132 } 3133 3134 /* 3135 * fill in port parameters we need to set: 3136 * 3137 * baud rate 3138 */ 3139 pe[0].param = DS_PARAM_BAUD; 3140 pe[0].val.ui = baudrate; 3141 3142 /* stop bits */ 3143 pe[1].param = DS_PARAM_STOPB; 3144 pe[1].val.ui = c_flag & CSTOPB; 3145 3146 /* parity */ 3147 pe[2].param = DS_PARAM_PARITY; 3148 pe[2].val.ui = c_flag & (PARENB | PARODD); 3149 3150 /* char size */ 3151 pe[3].param = DS_PARAM_CHARSZ; 3152 pe[3].val.ui = c_flag & CSIZE; 3153 3154 /* start & stop chars */ 3155 pe[4].param = DS_PARAM_XON_XOFF; 3156 pe[4].val.uc[0] = tp->t_startc; 3157 pe[4].val.uc[1] = tp->t_stopc; 3158 3159 /* flow control */ 3160 pe[5].param = DS_PARAM_FLOW_CTL; 3161 pe[5].val.ui = flow_ctl; 3162 3163 params.tp_entries = &pe[0]; 3164 params.tp_cnt = 6; 3165 3166 /* control signals */ 3167 ctl_val = TIOCM_DTR | TIOCM_RTS; 3168 if (baudrate == 0) { 3169 ctl_val &= ~TIOCM_DTR; /* zero baudrate means drop DTR */ 3170 } 3171 if (pp->port_flags & USBSER_FL_RX_STOPPED) { 3172 ctl_val &= ~TIOCM_RTS; 3173 } 3174 3175 /* submit */ 3176 mutex_exit(&pp->port_mutex); 3177 err = USBSER_DS_SET_PORT_PARAMS(pp, ¶ms); 3178 if (err != USB_SUCCESS) { 3179 mutex_enter(&pp->port_mutex); 3180 3181 return (EINVAL); 3182 } 3183 3184 err = USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR | TIOCM_RTS, ctl_val); 3185 mutex_enter(&pp->port_mutex); 3186 3187 return ((err == USB_SUCCESS) ? 0 : EIO); 3188} 3189 3190 3191/* 3192 * check if any inbound flow control action needed 3193 */ 3194static void 3195usbser_inbound_flow_ctl(usbser_port_t *pp) 3196{ 3197 tcflag_t need_hw; 3198 int rts; 3199 char c = pp->port_flowc; 3200 mblk_t *mp = NULL; 3201 3202 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, 3203 "usbser_inbound_flow_ctl: c=%x cflag=%x port_flags=%x", 3204 c, pp->port_ttycommon.t_cflag, pp->port_flags); 3205 3206 if (c == '\0') { 3207 3208 return; 3209 } 3210 pp->port_flowc = '\0'; 3211 3212 /* 3213 * if inbound hardware flow control enabled, we need to frob RTS 3214 */ 3215 need_hw = (pp->port_ttycommon.t_cflag & CRTSXOFF); 3216 if (c == pp->port_ttycommon.t_startc) { 3217 rts = TIOCM_RTS; 3218 pp->port_flags &= ~USBSER_FL_RX_STOPPED; 3219 } else { 3220 rts = 0; 3221 pp->port_flags |= USBSER_FL_RX_STOPPED; 3222 } 3223 3224 /* 3225 * if character flow control active, transmit a start or stop char, 3226 */ 3227 if (pp->port_ttycommon.t_iflag & IXOFF) { 3228 if ((mp = allocb(1, BPRI_LO)) == NULL) { 3229 USB_DPRINTF_L2(DPRINT_WQ, pp->port_lh, 3230 "usbser_inbound_flow_ctl: allocb failed"); 3231 } else { 3232 *mp->b_wptr++ = c; 3233 pp->port_flags |= USBSER_ACT_TX; 3234 } 3235 } 3236 3237 mutex_exit(&pp->port_mutex); 3238 if (need_hw) { 3239 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS, rts); 3240 } 3241 if (mp) { 3242 (void) USBSER_DS_TX(pp, mp); 3243 } 3244 mutex_enter(&pp->port_mutex); 3245} 3246 3247 3248/* 3249 * misc 3250 * ---- 3251 * 3252 * 3253 * returns != 0 if device is online, 0 otherwise 3254 */ 3255static int 3256usbser_dev_is_online(usbser_state_t *usp) 3257{ 3258 int rval; 3259 3260 mutex_enter(&usp->us_mutex); 3261 rval = (usp->us_dev_state == USB_DEV_ONLINE); 3262 mutex_exit(&usp->us_mutex); 3263 3264 return (rval); 3265} 3266 3267/* 3268 * serialize port activities defined by 'act' mask 3269 */ 3270static void 3271usbser_serialize_port_act(usbser_port_t *pp, int act) 3272{ 3273 while (pp->port_act & act) 3274 cv_wait(&pp->port_act_cv, &pp->port_mutex); 3275 pp->port_act |= act; 3276} 3277 3278 3279/* 3280 * indicate that port activity is finished 3281 */ 3282static void 3283usbser_release_port_act(usbser_port_t *pp, int act) 3284{ 3285 pp->port_act &= ~act; 3286 cv_broadcast(&pp->port_act_cv); 3287} 3288 3289 3290/* 3291 * message type to string and back conversion. 3292 * 3293 * pardon breaks on the same line, but as long as cstyle doesn't 3294 * complain, I'd like to keep this form for trivial cases like this. 3295 * associative arrays in the kernel, anyone? 3296 */ 3297static char * 3298usbser_msgtype2str(int type) 3299{ 3300 char *str; 3301 3302 switch (type) { 3303 case M_STOP: str = "M_STOP"; break; 3304 case M_START: str = "M_START"; break; 3305 case M_STOPI: str = "M_STOPI"; break; 3306 case M_STARTI: str = "M_STARTI"; break; 3307 case M_DATA: str = "M_DATA"; break; 3308 case M_DELAY: str = "M_DELAY"; break; 3309 case M_BREAK: str = "M_BREAK"; break; 3310 case M_IOCTL: str = "M_IOCTL"; break; 3311 case M_IOCDATA: str = "M_IOCDATA"; break; 3312 case M_FLUSH: str = "M_FLUSH"; break; 3313 case M_CTL: str = "M_CTL"; break; 3314 case M_READ: str = "M_READ"; break; 3315 default: str = "unknown"; break; 3316 } 3317 3318 return (str); 3319} 3320 3321 3322static char * 3323usbser_ioctl2str(int ioctl) 3324{ 3325 char *str; 3326 3327 switch (ioctl) { 3328 case TCGETA: str = "TCGETA"; break; 3329 case TCSETA: str = "TCSETA"; break; 3330 case TCSETAF: str = "TCSETAF"; break; 3331 case TCSETAW: str = "TCSETAW"; break; 3332 case TCSBRK: str = "TCSBRK"; break; 3333 case TCXONC: str = "TCXONC"; break; 3334 case TCFLSH: str = "TCFLSH"; break; 3335 case TCGETS: str = "TCGETS"; break; 3336 case TCSETS: str = "TCSETS"; break; 3337 case TCSETSF: str = "TCSETSF"; break; 3338 case TCSETSW: str = "TCSETSW"; break; 3339 case TIOCSBRK: str = "TIOCSBRK"; break; 3340 case TIOCCBRK: str = "TIOCCBRK"; break; 3341 case TIOCMSET: str = "TIOCMSET"; break; 3342 case TIOCMBIS: str = "TIOCMBIS"; break; 3343 case TIOCMBIC: str = "TIOCMBIC"; break; 3344 case TIOCMGET: str = "TIOCMGET"; break; 3345 case TIOCSILOOP: str = "TIOCSILOOP"; break; 3346 case TIOCCILOOP: str = "TIOCCILOOP"; break; 3347 case TCGETX: str = "TCGETX"; break; 3348 case TCSETX: str = "TCGETX"; break; 3349 case TCSETXW: str = "TCGETX"; break; 3350 case TCSETXF: str = "TCGETX"; break; 3351 default: str = "unknown"; break; 3352 } 3353 3354 return (str); 3355} 3356 3357/* 3358 * Polled IO support 3359 */ 3360 3361/* called once by consconfig() when polledio is opened */ 3362static int 3363usbser_polledio_init(usbser_port_t *pp) 3364{ 3365 int err; 3366 usb_pipe_handle_t hdl; 3367 ds_ops_t *ds_ops = pp->port_ds_ops; 3368 3369 /* only one serial line console supported */ 3370 if (console_input != NULL) 3371 return (USB_FAILURE); 3372 3373 /* check if underlying driver supports polled io */ 3374 if (ds_ops->ds_version < DS_OPS_VERSION_V1 || 3375 ds_ops->ds_out_pipe == NULL || ds_ops->ds_in_pipe == NULL) 3376 return (USB_FAILURE); 3377 3378 /* init polled input pipe */ 3379 hdl = ds_ops->ds_in_pipe(pp->port_ds_hdl, pp->port_num); 3380 err = usb_console_input_init(pp->port_usp->us_dip, hdl, 3381 &console_input_buf, &console_input); 3382 if (err) 3383 return (USB_FAILURE); 3384 3385 /* init polled output pipe */ 3386 hdl = ds_ops->ds_out_pipe(pp->port_ds_hdl, pp->port_num); 3387 err = usb_console_output_init(pp->port_usp->us_dip, hdl, 3388 &console_output); 3389 if (err) { 3390 (void) usb_console_input_fini(console_input); 3391 console_input = NULL; 3392 return (USB_FAILURE); 3393 } 3394 3395 return (USB_SUCCESS); 3396} 3397 3398/* called once by consconfig() when polledio is closed */ 3399/*ARGSUSED*/ 3400static void usbser_polledio_fini(usbser_port_t *pp) 3401{ 3402 /* Since we can't move the console, there is nothing to do. */ 3403} 3404 3405/*ARGSUSED*/ 3406static void 3407usbser_polledio_enter(cons_polledio_arg_t arg) 3408{ 3409 (void) usb_console_input_enter(console_input); 3410 (void) usb_console_output_enter(console_output); 3411} 3412 3413/*ARGSUSED*/ 3414static void 3415usbser_polledio_exit(cons_polledio_arg_t arg) 3416{ 3417 (void) usb_console_output_exit(console_output); 3418 (void) usb_console_input_exit(console_input); 3419} 3420 3421/*ARGSUSED*/ 3422static void 3423usbser_putchar(cons_polledio_arg_t arg, uchar_t c) 3424{ 3425 static uchar_t cr[2] = {'\r', '\n'}; 3426 uint_t nout; 3427 3428 if (c == '\n') 3429 (void) usb_console_write(console_output, cr, 2, &nout); 3430 else 3431 (void) usb_console_write(console_output, &c, 1, &nout); 3432} 3433 3434/*ARGSUSED*/ 3435static int 3436usbser_getchar(cons_polledio_arg_t arg) 3437{ 3438 while (!usbser_ischar(arg)) 3439 ; 3440 3441 return (*console_input_start++); 3442} 3443 3444/*ARGSUSED*/ 3445static boolean_t 3446usbser_ischar(cons_polledio_arg_t arg) 3447{ 3448 uint_t num_bytes; 3449 3450 if (console_input_start < console_input_end) 3451 return (B_TRUE); 3452 3453 if (usb_console_read(console_input, &num_bytes) != USB_SUCCESS) 3454 return (B_FALSE); 3455 3456 console_input_start = console_input_buf; 3457 console_input_end = console_input_buf + num_bytes; 3458 3459 return (num_bytes != 0); 3460} 3461