/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Printer Class Driver for USB * * This driver supports devices that adhere to the USB Printer Class * specification 1.0. * * NOTE: This driver is not DDI compliant in that it uses undocumented * functions for logging (USB_DPRINTF_L*, usb_alloc_log_hdl, usb_free_log_hdl), * and serialization (usb_serialize_access, usb_release_access, * usb_init_serialization, usb_fini_serialization) * * Undocumented functions may go away in a future Solaris OS release. * * Please see the DDK for sample code of these functions, and for the usbskel * skeleton template driver which contains scaled-down versions of these * functions written in a DDI-compliant way. */ #if defined(lint) && !defined(DEBUG) #define DEBUG #endif #ifdef __lock_lint #define _MULTI_DATAMODEL #endif #define USBDRV_MAJOR_VER 2 #define USBDRV_MINOR_VER 0 #include #include #include #include #include #include #include #include #include /* Debugging support */ uint_t usbprn_errmask = (uint_t)PRINT_MASK_ALL; uint_t usbprn_errlevel = USB_LOG_L4; uint_t usbprn_instance_debug = (uint_t)-1; /* local variables */ static uint_t usbprn_ifcap = PRN_HOTPLUG | PRN_1284_DEVID | PRN_1284_STATUS | PRN_TIMEOUTS; /* * Function Prototypes */ static int usbprn_attach(dev_info_t *, ddi_attach_cmd_t); static int usbprn_detach(dev_info_t *, ddi_detach_cmd_t); static int usbprn_info(dev_info_t *, ddi_info_cmd_t, void *, void **); static void usbprn_cleanup(dev_info_t *, usbprn_state_t *); static int usbprn_get_descriptors(usbprn_state_t *); static int usbprn_get_device_id(usbprn_state_t *); static int usbprn_get_port_status(usbprn_state_t *); static int usbprn_open(dev_t *, int, int, cred_t *); static int usbprn_close(dev_t, int, int, cred_t *); static int usbprn_open_usb_pipes(usbprn_state_t *); static void usbprn_close_usb_pipes(usbprn_state_t *); static int usbprn_write(dev_t, struct uio *, cred_t *); static int usbprn_read(dev_t, struct uio *, cred_t *); static int usbprn_poll(dev_t, short, int, short *, struct pollhead **); static int usbprn_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); static void usbprn_minphys(struct buf *); static int usbprn_strategy(struct buf *); static int usbprn_setparms(usbprn_state_t *, intptr_t arg, int); static int usbprn_getparms(usbprn_state_t *, intptr_t, int); static void usbprn_geterr(usbprn_state_t *, intptr_t, int); static int usbprn_testio(usbprn_state_t *, int); static int usbprn_ioctl_get_status(usbprn_state_t *); static int usbprn_prnio_get_status(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_get_1284_status(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_get_ifcap(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_set_ifcap(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_get_ifinfo(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_get_1284_devid(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_get_timeouts(usbprn_state_t *, intptr_t, int); static int usbprn_prnio_set_timeouts(usbprn_state_t *, intptr_t, int); static void usbprn_send_async_bulk_data(usbprn_state_t *); static void usbprn_bulk_xfer_cb(usb_pipe_handle_t, usb_bulk_req_t *); static void usbprn_bulk_xfer_exc_cb(usb_pipe_handle_t, usb_bulk_req_t *); static void usbprn_biodone(usbprn_state_t *, int, int); static char usbprn_error_state(uchar_t); static void usbprn_print_long(usbprn_state_t *, char *, int); /* event handling */ static void usbprn_restore_device_state(dev_info_t *, usbprn_state_t *); static int usbprn_disconnect_event_cb(dev_info_t *); static int usbprn_reconnect_event_cb(dev_info_t *); static int usbprn_cpr_suspend(dev_info_t *); static void usbprn_cpr_resume(dev_info_t *); static usb_event_t usbprn_events = { usbprn_disconnect_event_cb, usbprn_reconnect_event_cb, NULL, NULL }; /* PM handling */ static void usbprn_create_pm_components(dev_info_t *, usbprn_state_t *); static int usbprn_power(dev_info_t *, int comp, int level); static int usbprn_pwrlvl0(usbprn_state_t *); static int usbprn_pwrlvl1(usbprn_state_t *); static int usbprn_pwrlvl2(usbprn_state_t *); static int usbprn_pwrlvl3(usbprn_state_t *); static void usbprn_pm_busy_component(usbprn_state_t *); static void usbprn_pm_idle_component(usbprn_state_t *); /* module loading stuff */ struct cb_ops usbprn_cb_ops = { usbprn_open, /* open */ usbprn_close, /* close */ nulldev, /* strategy */ nulldev, /* print */ nulldev, /* dump */ usbprn_read, /* read */ usbprn_write, /* write */ usbprn_ioctl, /* ioctl */ nulldev, /* devmap */ nulldev, /* mmap */ nulldev, /* segmap */ usbprn_poll, /* poll */ ddi_prop_op, /* cb_prop_op */ NULL, /* streamtab */ D_64BIT | D_MP }; static struct dev_ops usbprn_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ usbprn_info, /* info */ nulldev, /* identify */ nulldev, /* probe */ usbprn_attach, /* attach */ usbprn_detach, /* detach */ nodev, /* reset */ &usbprn_cb_ops, /* driver operations */ NULL, /* bus operations */ usbprn_power, /* power */ ddi_quiesce_not_supported, /* devo_quiesce */ }; static struct modldrv usbprnmodldrv = { &mod_driverops, "USB printer client driver", &usbprn_ops }; static struct modlinkage modlinkage = { MODREV_1, &usbprnmodldrv, NULL, }; /* local variables */ /* soft state structures */ #define USBPRN_INITIAL_SOFT_SPACE 1 static void *usbprn_statep; static int usbprn_max_xfer_size = USBPRN_MAX_XFER_SIZE; /* prnio support */ static const char usbprn_prnio_ifinfo[] = PRN_USB; int _init(void) { int rval; if ((rval = ddi_soft_state_init(&usbprn_statep, sizeof (usbprn_state_t), USBPRN_INITIAL_SOFT_SPACE)) != 0) { return (rval); } if ((rval = mod_install(&modlinkage)) != 0) { ddi_soft_state_fini(&usbprn_statep); } return (rval); } int _fini(void) { int rval; if ((rval = mod_remove(&modlinkage)) != 0) { return (rval); } ddi_soft_state_fini(&usbprn_statep); return (rval); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * usbprn_info: * Get minor number, soft state structure, etc. */ /*ARGSUSED*/ static int usbprn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { usbprn_state_t *usbprnp; int error = DDI_FAILURE; minor_t minor = getminor((dev_t)arg); int instance = USBPRN_MINOR_TO_INSTANCE(minor); switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if ((usbprnp = ddi_get_soft_state(usbprn_statep, instance)) != NULL) { *result = usbprnp->usbprn_dip; if (*result != NULL) { error = DDI_SUCCESS; } } else { *result = NULL; } break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)(uintptr_t)instance; error = DDI_SUCCESS; break; default: break; } return (error); } /* * usbprn_attach: * Attach driver * Get the descriptor information * Get the device id * Reset the device * Get the port status */ static int usbprn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance = ddi_get_instance(dip); usbprn_state_t *usbprnp = NULL; size_t sz; usb_ugen_info_t usb_ugen_info; switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: usbprn_cpr_resume(dip); return (DDI_SUCCESS); default: return (DDI_FAILURE); } if (ddi_soft_state_zalloc(usbprn_statep, instance) == DDI_SUCCESS) { usbprnp = ddi_get_soft_state(usbprn_statep, instance); } if (usbprnp == NULL) { return (DDI_FAILURE); } usbprnp->usbprn_instance = instance; usbprnp->usbprn_dip = dip; usbprnp->usbprn_log_handle = usb_alloc_log_hdl(dip, "prn", &usbprn_errlevel, &usbprn_errmask, &usbprn_instance_debug, 0); USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_attach: cmd=%x", cmd); if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usb_client_attach failed"); goto fail; } if (usb_get_dev_data(dip, &usbprnp->usbprn_dev_data, USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usb_get_dev_data failed"); goto fail; } /* Initialize locks and conditional variables */ mutex_init(&usbprnp->usbprn_mutex, NULL, MUTEX_DRIVER, usbprnp->usbprn_dev_data->dev_iblock_cookie); usbprnp->usbprn_write_acc = usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD); usbprnp->usbprn_ser_acc = usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD); usbprnp->usbprn_dev_acc = usb_init_serialization(dip, 0); usbprnp->usbprn_flags |= USBPRN_LOCKS_INIT_DONE; /* Obtain all the relevant descriptors */ if (usbprn_get_descriptors(usbprnp) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usb get descriptors failed"); goto fail; } usbprnp->usbprn_def_ph = usbprnp->usbprn_dev_data->dev_default_ph; /* Obtain the device id */ (void) usbprn_get_device_id(usbprnp); /* Get the port status */ if (usbprn_get_port_status(usbprnp) != USB_SUCCESS) { /* some printers fail on the first */ if (usbprn_get_port_status(usbprnp) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usb get port status failed"); goto fail; } } USB_DPRINTF_L3(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_attach: printer status=0x%x", usbprnp->usbprn_last_status); if ((usbprnp->usbprn_last_status & USB_PRINTER_PORT_NO_ERROR) == 0) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_attach: error occurred with the printer"); } /* * Create minor node based on information from the * descriptors */ if ((ddi_create_minor_node(dip, "printer", S_IFCHR, instance << USBPRN_MINOR_INSTANCE_SHIFT, DDI_NT_PRINTER, 0)) != DDI_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_attach: cannot create minor node"); goto fail; } usbprnp->usbprn_setparms.write_timeout = USBPRN_XFER_TIMEOUT; usbprnp->usbprn_setparms.mode = ECPP_CENTRONICS; usbprnp->usbprn_dev_state = USB_DEV_ONLINE; if (usb_pipe_get_max_bulk_transfer_size(usbprnp->usbprn_dip, &sz)) { goto fail; } usbprnp->usbprn_max_bulk_xfer_size = sz; USB_DPRINTF_L4(PRINT_MASK_OPEN, usbprnp->usbprn_log_handle, "usbprn_attach: xfer_size=0x%lx", sz); /* enable PM */ usbprn_create_pm_components(dip, usbprnp); /* Register for events */ if (usb_register_event_cbs(dip, &usbprn_events, 0) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_attach: usb_register_event_cbs failed"); goto fail; } usb_free_dev_data(dip, usbprnp->usbprn_dev_data); usbprnp->usbprn_dev_data = NULL; if (usb_owns_device(dip)) { /* get a ugen handle */ bzero(&usb_ugen_info, sizeof (usb_ugen_info)); usb_ugen_info.usb_ugen_flags = 0; usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask = (dev_t)USBPRN_MINOR_UGEN_BITS_MASK; usb_ugen_info.usb_ugen_minor_node_instance_mask = (dev_t)~USBPRN_MINOR_UGEN_BITS_MASK; usbprnp->usbprn_ugen_hdl = usb_ugen_get_hdl(dip, &usb_ugen_info); if (usb_ugen_attach(usbprnp->usbprn_ugen_hdl, cmd) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usb_ugen_attach failed"); usb_ugen_release_hdl(usbprnp->usbprn_ugen_hdl); usbprnp->usbprn_ugen_hdl = NULL; } } /* Report device */ ddi_report_dev(dip); USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_attach: done"); return (DDI_SUCCESS); fail: if (usbprnp) { usbprn_cleanup(dip, usbprnp); } return (DDI_FAILURE); } /* * usbprn_detach: * detach or suspend driver instance */ static int usbprn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance = ddi_get_instance(dip); usbprn_state_t *usbprnp; int rval = DDI_FAILURE; usbprnp = ddi_get_soft_state(usbprn_statep, instance); USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_detach: cmd=%x", cmd); switch (cmd) { case DDI_DETACH: ASSERT((usbprnp->usbprn_flags & USBPRN_OPEN) == 0); usbprn_cleanup(dip, usbprnp); return (DDI_SUCCESS); case DDI_SUSPEND: rval = usbprn_cpr_suspend(dip); return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); default: return (rval); } } /* * usbprn_cleanup: * clean up the driver state */ static void usbprn_cleanup(dev_info_t *dip, usbprn_state_t *usbprnp) { usbprn_power_t *usbprnpm = usbprnp->usbprn_pm; int rval = 0; USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_cleanup: Start"); ASSERT(usbprnp != NULL); if (usbprnp->usbprn_flags & USBPRN_LOCKS_INIT_DONE) { /* * Disable the event callbacks first, after this point, event * callbacks will never get called. Note we shouldn't hold * mutex while unregistering events because there may be a * competing event callback thread. Event callbacks are done * with ndi mutex held and this can cause a potential deadlock. */ usb_unregister_event_cbs(dip, &usbprn_events); mutex_enter(&usbprnp->usbprn_mutex); if ((usbprnpm) && (usbprnp->usbprn_dev_state != USB_DEV_DISCONNECTED)) { mutex_exit(&usbprnp->usbprn_mutex); usbprn_pm_busy_component(usbprnp); mutex_enter(&usbprnp->usbprn_mutex); if (usbprnpm->usbprn_wakeup_enabled) { mutex_exit(&usbprnp->usbprn_mutex); (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); if ((rval = usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_cleanup: " "disable remote wakeup " "failed, rval=%d", rval); } } else { mutex_exit(&usbprnp->usbprn_mutex); } (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF); usbprn_pm_idle_component(usbprnp); mutex_enter(&usbprnp->usbprn_mutex); } ddi_remove_minor_node(dip, NULL); mutex_exit(&usbprnp->usbprn_mutex); if (usbprnp->usbprn_device_id) { kmem_free(usbprnp->usbprn_device_id, usbprnp->usbprn_device_id_len + 1); } mutex_destroy(&usbprnp->usbprn_mutex); usb_fini_serialization(usbprnp->usbprn_dev_acc); usb_fini_serialization(usbprnp->usbprn_ser_acc); usb_fini_serialization(usbprnp->usbprn_write_acc); } if (usbprnpm) { kmem_free(usbprnpm, sizeof (usbprn_power_t)); } USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_cleanup: End"); if (usbprnp->usbprn_ugen_hdl) { (void) usb_ugen_detach(usbprnp->usbprn_ugen_hdl, DDI_DETACH); usb_ugen_release_hdl(usbprnp->usbprn_ugen_hdl); } /* unregister with USBA */ usb_client_detach(dip, usbprnp->usbprn_dev_data); usb_free_log_hdl(usbprnp->usbprn_log_handle); ddi_prop_remove_all(dip); ddi_soft_state_free(usbprn_statep, usbprnp->usbprn_instance); } /* * usbprn_cpr_suspend: * prepare to be suspended */ static int usbprn_cpr_suspend(dev_info_t *dip) { usbprn_state_t *usbprnp; int instance = ddi_get_instance(dip); int rval = USB_FAILURE; usbprnp = ddi_get_soft_state(usbprn_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CPR, usbprnp->usbprn_log_handle, "usbprn_cpr_suspend"); (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); mutex_enter(&usbprnp->usbprn_mutex); if ((usbprnp->usbprn_flags & USBPRN_OPEN) != 0) { mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L2(PRINT_MASK_CPR, usbprnp->usbprn_log_handle, "usbprn_cpr_suspend: " "Device is open. Can't suspend"); } else { usbprnp->usbprn_dev_state = USB_DEV_SUSPENDED; mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L4(PRINT_MASK_CPR, usbprnp->usbprn_log_handle, "usbprn_cpr_suspend: SUCCESS"); rval = USB_SUCCESS; } usb_release_access(usbprnp->usbprn_ser_acc); if ((rval == USB_SUCCESS) && usbprnp->usbprn_ugen_hdl) { rval = usb_ugen_detach(usbprnp->usbprn_ugen_hdl, DDI_SUSPEND); } return (rval); } static void usbprn_cpr_resume(dev_info_t *dip) { int instance = ddi_get_instance(dip); usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CPR, usbprnp->usbprn_log_handle, "usbprn_cpr_resume"); /* Needed as power up state of dev is "unknown" to system */ usbprn_pm_busy_component(usbprnp); (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); usbprn_restore_device_state(dip, usbprnp); usbprn_pm_idle_component(usbprnp); if (usbprnp->usbprn_ugen_hdl) { (void) usb_ugen_attach(usbprnp->usbprn_ugen_hdl, DDI_RESUME); } } /* * usbprn_get_descriptors: * Obtain all the descriptors for the device */ static int usbprn_get_descriptors(usbprn_state_t *usbprnp) { int interface; usb_client_dev_data_t *dev_data = usbprnp->usbprn_dev_data; usb_alt_if_data_t *altif_data; usb_cfg_data_t *cfg_data; usb_ep_data_t *ep_data; dev_info_t *dip = usbprnp->usbprn_dip; int alt, rval; ASSERT(!mutex_owned(&usbprnp->usbprn_mutex)); /* * Section 4.2.1 of the spec says the printer could have * multiple configurations. This driver is just for one * configuration interface and one interface. */ interface = dev_data->dev_curr_if; cfg_data = dev_data->dev_curr_cfg; /* find alternate that supports BI/UNI protocol */ for (alt = 0; alt < cfg_data->cfg_if[interface].if_n_alt; alt++) { altif_data = &cfg_data->cfg_if[interface].if_alt[alt]; if ((altif_data->altif_descr.bInterfaceProtocol == USB_PROTO_PRINTER_UNI) || (altif_data->altif_descr.bInterfaceProtocol == USB_PROTO_PRINTER_BI)) { break; } else { USB_DPRINTF_L3(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "alternate %d not supported", alt); } } if (alt == cfg_data->cfg_if[interface].if_n_alt) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_descriptors: no alternate"); return (USB_FAILURE); } if ((rval = usb_set_alt_if(dip, interface, alt, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_descriptors: set alternate failed (%d)", rval); return (rval); } usbprnp->usbprn_config_descr = cfg_data->cfg_descr; usbprnp->usbprn_if_descr = altif_data->altif_descr; /* * find the endpoint descriptors. There will be a bulk-out endpoint * and an optional bulk-in endpoint. */ if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, alt, 0, USB_EP_ATTR_BULK, USB_EP_DIR_OUT)) != NULL) { usbprnp->usbprn_bulk_out.ps_ept_descr = ep_data->ep_descr; } if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, alt, 0, USB_EP_ATTR_BULK, USB_EP_DIR_IN)) != NULL) { usbprnp->usbprn_bulk_in.ps_ept_descr = ep_data->ep_descr; } return (USB_SUCCESS); } /* * usbprn_get_device_id: * Get the device id as described in 4.2.1 of the specification * Lexmark printer returns 2 bytes when asked for 8 bytes * We are ignoring data over and underrun. * This is a synchronous function */ static int usbprn_get_device_id(usbprn_state_t *usbprnp) { int len, n; mblk_t *data = NULL; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; int rval = USB_FAILURE; usb_ctrl_setup_t setup = { USB_DEV_REQ_DEV_TO_HOST | /* bmRequestType */ USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF, USB_PRINTER_GET_DEVICE_ID, /* bRequest */ 0, /* wValue: fill in later */ 0, /* wIndex: fill in later */ 0, /* wLength: fill in later */ 0 /* attributes */ }; void *ptr; USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_device_id: Begin"); ASSERT(!mutex_owned(&usbprnp->usbprn_mutex)); setup.wIndex = (usbprnp->usbprn_if_descr.bInterfaceNumber << 0x8) | (usbprnp->usbprn_if_descr.bAlternateSetting); setup.wLength = USBPRN_MAX_DEVICE_ID_LENGTH; setup.wValue = usbprnp->usbprn_config_descr.iConfiguration; /* * This is always a sync request as this will never * be called in interrupt context. * First get the first two bytes that gives the length * of the device id string; then get the whole string */ if (usb_pipe_ctrl_xfer_wait(usbprnp->usbprn_def_ph, &setup, &data, &completion_reason, &cb_flags, 0) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_device_id: First sync command failed, cr=%d ", completion_reason); /* * some devices return more than requested. as long as * we get the first two bytes, we can continue */ if (((completion_reason != USB_CR_DATA_OVERRUN) && (completion_reason != USB_CR_DATA_UNDERRUN)) || (data == NULL)) { goto done; } } ASSERT(data); n = MBLKL(data); if (n < 2) { goto done; } len = (((*data->b_rptr) << 0x8) | (*(data->b_rptr+1))); /* * Std 1284-1994, chapter 7.6: * Length values of x'0000', x'0001' and x'0002' are reserved */ if (len < 3) { goto done; } USB_DPRINTF_L3(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_device_id: device id length=%d", len); /* did we get enough data */ if (len > n) { freemsg(data); data = NULL; setup.wLength = (uint16_t)len; if ((rval = usb_pipe_ctrl_xfer_wait(usbprnp->usbprn_def_ph, &setup, &data, &completion_reason, &cb_flags, 0)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_device_id: 2nd command failed " "cr=%d cb_flags=0x%x", completion_reason, cb_flags); goto done; } ASSERT(len == MBLKL(data)); } USB_DPRINTF_L3(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_device_id: returned data length=%ld", (long)(MBLKL(data))); ptr = kmem_zalloc(len + 1, KM_SLEEP); mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_device_id_len = len; usbprnp->usbprn_device_id = ptr; bcopy(data->b_rptr, usbprnp->usbprn_device_id, usbprnp->usbprn_device_id_len); usbprnp->usbprn_device_id[usbprnp->usbprn_device_id_len] = '\0'; /* Length is in the first two bytes, dump string in logbuf */ usbprn_print_long(usbprnp, usbprnp->usbprn_device_id + 2, usbprnp->usbprn_device_id_len - 2); mutex_exit(&usbprnp->usbprn_mutex); rval = USB_SUCCESS; done: freemsg(data); USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_get_device_id: rval=%d", rval); return (rval); } /* * usbprn_get_port_status: * Get the port status. * This is a synchronous function */ static int usbprn_get_port_status(usbprn_state_t *usbprnp) { mblk_t *data = NULL; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; usb_ctrl_setup_t setup = { USB_DEV_REQ_DEV_TO_HOST | /* bmRequestType */ USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF, USB_PRINTER_GET_PORT_STATUS, /* bRequest */ 0, /* wValue */ 0, /* wIndex: fill in later */ 1, /* wLength */ 0 /* attributes */ }; ASSERT(!mutex_owned(&usbprnp->usbprn_mutex)); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_get_port_status: Begin"); setup.wIndex = usbprnp->usbprn_if_descr.bInterfaceNumber; if (usb_pipe_ctrl_xfer_wait(usbprnp->usbprn_def_ph, &setup, &data, &completion_reason, &cb_flags, 0) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_get_port_status: Sync command failed " "cr=%d cb_flags=0x%x", completion_reason, cb_flags); freemsg(data); return (USB_FAILURE); } else { mutex_enter(&usbprnp->usbprn_mutex); ASSERT(data); ASSERT(MBLKL(data) == 1); usbprnp->usbprn_last_status = *data->b_rptr; USB_DPRINTF_L3(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_get_port_status(sync): status=0x%x", usbprnp->usbprn_last_status); mutex_exit(&usbprnp->usbprn_mutex); freemsg(data); return (USB_SUCCESS); } } /* * usbprn_open: * Open the pipes */ /*ARGSUSED*/ static int usbprn_open(dev_t *devp, int flag, int sflag, cred_t *credp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(*devp))); int rval = 0; if (usbprnp == NULL) { return (ENXIO); } USB_DPRINTF_L4(PRINT_MASK_OPEN, usbprnp->usbprn_log_handle, "usbprn_open:"); (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); /* Fail open on a disconnected device */ mutex_enter(&usbprnp->usbprn_mutex); if (usbprnp->usbprn_dev_state == USB_DEV_DISCONNECTED) { mutex_exit(&usbprnp->usbprn_mutex); usb_release_access(usbprnp->usbprn_ser_acc); return (ENODEV); } /* cannot happen? but just in case */ if (usbprnp->usbprn_dev_state == USB_DEV_SUSPENDED) { mutex_exit(&usbprnp->usbprn_mutex); usb_release_access(usbprnp->usbprn_ser_acc); return (EIO); } if (getminor(*devp) & USBPRN_MINOR_UGEN_BITS_MASK) { mutex_exit(&usbprnp->usbprn_mutex); rval = usb_ugen_open(usbprnp->usbprn_ugen_hdl, devp, flag, sflag, credp); usb_release_access(usbprnp->usbprn_ser_acc); return (rval); } /* Exit if this instance is already open */ if (usbprnp->usbprn_flags & USBPRN_OPEN) { mutex_exit(&usbprnp->usbprn_mutex); usb_release_access(usbprnp->usbprn_ser_acc); return (EBUSY); } mutex_exit(&usbprnp->usbprn_mutex); /* raise power */ usbprn_pm_busy_component(usbprnp); (void) pm_raise_power(usbprnp->usbprn_dip, 0, USB_DEV_OS_FULL_PWR); /* initialize some softstate data */ mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_prn_timeouts.tmo_forward = usbprnp->usbprn_setparms.write_timeout; usbprnp->usbprn_prn_timeouts.tmo_reverse = 0; mutex_exit(&usbprnp->usbprn_mutex); if (usbprn_open_usb_pipes(usbprnp) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_open: pipe open failed"); usb_release_access(usbprnp->usbprn_ser_acc); usbprn_pm_idle_component(usbprnp); return (EIO); } mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_flags |= USBPRN_OPEN; /* set last status to online */ usbprnp->usbprn_last_status &= ~USB_PRINTER_PORT_NO_SELECT; mutex_exit(&usbprnp->usbprn_mutex); usb_release_access(usbprnp->usbprn_ser_acc); USB_DPRINTF_L4(PRINT_MASK_OPEN, usbprnp->usbprn_log_handle, "usbprn_open: End"); return (rval); } /* * usbprn_close: * Close the pipes */ /*ARGSUSED*/ static int usbprn_close(dev_t dev, int flag, int otyp, cred_t *credp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(dev))); int rval = 0; if (usbprnp == NULL) { return (ENXIO); } USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbprnp->usbprn_log_handle, "usbprn_close:"); if (getminor(dev) & USBPRN_MINOR_UGEN_BITS_MASK) { rval = usb_ugen_close(usbprnp->usbprn_ugen_hdl, dev, flag, otyp, credp); return (rval); } /* avoid races with connect/disconnect */ (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); (void) usb_serialize_access(usbprnp->usbprn_dev_acc, USB_WAIT, 0); /* Close all usb pipes */ usbprn_close_usb_pipes(usbprnp); /* prevent any accesses by setting flags to closed */ mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_flags &= ~USBPRN_OPEN; mutex_exit(&usbprnp->usbprn_mutex); usb_release_access(usbprnp->usbprn_dev_acc); usb_release_access(usbprnp->usbprn_ser_acc); usbprn_pm_idle_component(usbprnp); USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbprnp->usbprn_log_handle, "usbprn_close: End"); return (rval); } /* * usbprn_read: * Read entry point (TBD) */ /* ARGSUSED */ static int usbprn_read(dev_t dev, struct uio *uiop, cred_t *credp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(dev))); if (usbprnp == NULL) { return (ENXIO); } if (getminor(dev) & USBPRN_MINOR_UGEN_BITS_MASK) { int rval; /* raise power */ usbprn_pm_busy_component(usbprnp); (void) pm_raise_power(usbprnp->usbprn_dip, 0, USB_DEV_OS_FULL_PWR); if (usb_serialize_access(usbprnp->usbprn_write_acc, USB_WAIT_SIG, 0) == 0) { usbprn_pm_idle_component(usbprnp); return (EINTR); } rval = usb_ugen_read(usbprnp->usbprn_ugen_hdl, dev, uiop, credp); usb_release_access(usbprnp->usbprn_write_acc); usbprn_pm_idle_component(usbprnp); return (rval); } /* Do a bulk-in from the printer */ return (EIO); } /* * usbprn_write: * Write to the printer */ /* ARGSUSED2 */ static int usbprn_write(dev_t dev, struct uio *uiop, cred_t *credp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(dev))); usbprn_ps_t *bulk_in = &usbprnp->usbprn_bulk_in; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; int rval; if (usbprnp == NULL) { return (ENXIO); } USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_write: Begin usbprnp=0x%p ", (void *)usbprnp); if (getminor(dev) & USBPRN_MINOR_UGEN_BITS_MASK) { /* raise power */ usbprn_pm_busy_component(usbprnp); (void) pm_raise_power(usbprnp->usbprn_dip, 0, USB_DEV_OS_FULL_PWR); if (usb_serialize_access(usbprnp->usbprn_write_acc, USB_WAIT_SIG, 0) == 0) { usbprn_pm_idle_component(usbprnp); return (EINTR); } rval = usb_ugen_write(usbprnp->usbprn_ugen_hdl, dev, uiop, credp); usb_release_access(usbprnp->usbprn_write_acc); usbprn_pm_idle_component(usbprnp); return (rval); } /* * serialize writes * we cannot use usbprn_ser_acc sync object at this point because * that would block out the ioctls for the full duration of the write. */ if (usb_serialize_access(usbprnp->usbprn_write_acc, USB_WAIT_SIG, 0) == 0) { return (EINTR); } /* * Check the status of the pipe. If it's not idle, * then wait. */ mutex_enter(&usbprnp->usbprn_mutex); /* if device is disconnected or pipes closed, fail immediately */ if (!(USBPRN_DEVICE_ACCESS_OK(usbprnp))) { mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_write: device can't be accessed"); usb_release_access(usbprnp->usbprn_write_acc); return (EIO); } /* all pipes must be idle */ ASSERT(bulk_out->ps_flags == USBPRN_PS_IDLE); ASSERT(bulk_in->ps_flags == USBPRN_PS_IDLE); mutex_exit(&usbprnp->usbprn_mutex); /* * Call physio to do the transfer. physio will * call the strategy routine, and then call * biowait() to block until the transfer completes. */ rval = physio(usbprn_strategy, (struct buf *)0, dev, B_WRITE, usbprn_minphys, uiop); usb_release_access(usbprnp->usbprn_write_acc); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_write: End"); return (rval); } /* * usbprn_poll */ static int usbprn_poll(dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(dev))); if (usbprnp == NULL) { return (ENXIO); } if (getminor(dev) & USBPRN_MINOR_UGEN_BITS_MASK) { return (usb_ugen_poll(usbprnp->usbprn_ugen_hdl, dev, events, anyyet, reventsp, phpp)); } return (ENXIO); } /* * usbprn_strategy: * service a request to the device. */ static int usbprn_strategy(struct buf *bp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(bp->b_edev))); usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; bp_mapin(bp); /* * serialize to avoid races * access is released in usbprn_biodone() */ (void) usb_serialize_access(usbprnp->usbprn_dev_acc, USB_WAIT, 0); mutex_enter(&usbprnp->usbprn_mutex); if (!(USBPRN_DEVICE_ACCESS_OK(usbprnp))) { usbprn_biodone(usbprnp, EIO, 0); mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_strategy: device can't be accessed"); return (0); } bulk_out->ps_flags = USBPRN_PS_NEED_TO_XFER; ASSERT(usbprnp->usbprn_bp == NULL); usbprnp->usbprn_bp = bp; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_strategy: usbprnp=0x%p bp=0x%p count=%lu", (void *)usbprnp, (void *)bp, bp->b_bcount); ASSERT(usbprnp->usbprn_bulk_mp == NULL); usbprnp->usbprn_bulk_mp = allocb(bp->b_bcount, BPRI_HI); if (usbprnp->usbprn_bulk_mp == NULL) { bulk_out->ps_flags = USBPRN_PS_IDLE; usbprn_biodone(usbprnp, EIO, 0); mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_strategy: allocb failed"); return (0); } bcopy((caddr_t)bp->b_un.b_addr, usbprnp->usbprn_bulk_mp->b_datap->db_base, bp->b_bcount); usbprnp->usbprn_bulk_mp->b_wptr += bp->b_bcount; mutex_exit(&usbprnp->usbprn_mutex); usbprn_send_async_bulk_data(usbprnp); return (0); } /* * usbprn_ioctl: * handle the ioctl */ /*ARGSUSED4*/ static int usbprn_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp) { int err = 0; usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(dev))); struct ecpp_device_id usbprn_devid; int len; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: Begin "); (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); mutex_enter(&usbprnp->usbprn_mutex); /* * only for PRNIOC_GET_STATUS cmd: * if device is disconnected or pipes closed, fail immediately */ if ((cmd == PRNIOC_GET_STATUS) && !(USBPRN_DEVICE_ACCESS_OK(usbprnp))) { mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_write: device can't be accessed"); usb_release_access(usbprnp->usbprn_ser_acc); return (EIO); } mutex_exit(&usbprnp->usbprn_mutex); switch (cmd) { case ECPPIOC_GETDEVID: /* * With genericized ioctls this interface should change. * We ignore the mode in USB printer driver because * it need not be in nibble mode in usb driver unlike * ecpp to retrieve the device id string. Also we do * not expect the application to call this twice since * it doesn't change since attach time and we take care * of calling it twice: once for getting the length and * once for getting the actual device id string. So we * set both the lengths to actual device id string length. * Ref: PSARC/2000/018 */ USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: ECPPIOC_GETDEVID(0x%x)", cmd); bzero(&usbprn_devid, sizeof (usbprn_devid)); ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); #ifdef _MULTI_DATAMODEL switch (ddi_model_convert_from(flag & FMODELS)) { case DDI_MODEL_ILP32: { struct ecpp_device_id32 usbprn_devid32; if (ddi_copyin((caddr_t)arg, &usbprn_devid32, sizeof (struct ecpp_device_id32), flag)) { err = EFAULT; break; } if (usbprnp->usbprn_device_id == NULL) { err = EIO; break; } ASSERT(usbprnp->usbprn_device_id_len > 2); usbprn_devid32.rlen = usbprnp->usbprn_device_id_len - 2; len = min(usbprn_devid32.len, usbprn_devid32.rlen); if (ddi_copyout(usbprnp->usbprn_device_id + 2, (caddr_t)(uintptr_t)usbprn_devid32.addr, len, flag)) { err = EFAULT; break; } if (ddi_copyout(&usbprn_devid32, (caddr_t)arg, sizeof (struct ecpp_device_id32), flag)) { err = EFAULT; break; } break; } case DDI_MODEL_NONE: if (ddi_copyin((caddr_t)arg, &usbprn_devid, sizeof (struct ecpp_device_id), flag)) { err = EFAULT; break; } if (usbprnp->usbprn_device_id == NULL) { err = EIO; break; } ASSERT(usbprnp->usbprn_device_id_len > 2); usbprn_devid.rlen = usbprnp->usbprn_device_id_len - 2; len = min(usbprn_devid.len, usbprn_devid.rlen); if (ddi_copyout(usbprnp->usbprn_device_id + 2, usbprn_devid.addr, len, flag)) { err = EFAULT; break; } if (ddi_copyout(&usbprn_devid, (caddr_t)arg, sizeof (struct ecpp_device_id), flag)) { err = EFAULT; break; } break; } break; #else if (ddi_copyin((caddr_t)arg, &usbprn_devid, sizeof (struct ecpp_device_id), flag)) { err = EFAULT; break; } if (usbprnp->usbprn_device_id == NULL) { err = EIO; break; } ASSERT(usbprnp->usbprn_device_id_len > 2); usbprn_devid.rlen = usbprnp->usbprn_device_id_len - 2; len = min(usbprn_devid.len, usbprn_devid.rlen); if (ddi_copyout(usbprnp->usbprn_device_id + 2, usbprn_devid.addr, len, flag)) { err = EFAULT; break; } if (ddi_copyout(&usbprn_devid, (caddr_t)arg, sizeof (struct ecpp_device_id), flag)) { err = EFAULT; break; } break; #endif case ECPPIOC_SETPARMS: err = usbprn_setparms(usbprnp, arg, flag); break; case ECPPIOC_GETPARMS: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: ECPPIOC_GETPARMS(0x%x)", cmd); /* Get the parameters */ err = usbprn_getparms(usbprnp, arg, flag); break; case BPPIOC_GETERR: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: ECPPIOC_GETERR(0x%x)", cmd); /* Get the error state */ usbprn_geterr(usbprnp, arg, flag); break; case BPPIOC_TESTIO: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: BPPIOC_TESTIO(0x%x)", cmd); /* Get the port status */ err = usbprn_testio(usbprnp, flag); break; case PRNIOC_GET_IFCAP: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_GET_IFCAP(0x%x)", cmd); /* get interface capabilities */ err = usbprn_prnio_get_ifcap(usbprnp, arg, flag); break; case PRNIOC_SET_IFCAP: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_SET_IFCAP(0x%x)", cmd); /* get interface capabilities */ err = usbprn_prnio_set_ifcap(usbprnp, arg, flag); break; case PRNIOC_GET_IFINFO: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_GET_IFINFO(0x%x)", cmd); /* get interface information */ err = usbprn_prnio_get_ifinfo(usbprnp, arg, flag); break; case PRNIOC_GET_STATUS: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_GET_STATUS(0x%x)", cmd); /* get prnio status */ err = usbprn_prnio_get_status(usbprnp, arg, flag); break; case PRNIOC_GET_1284_DEVID: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_GET_1284_DEVID(0x%x)", cmd); /* get device ID */ err = usbprn_prnio_get_1284_devid(usbprnp, arg, flag); break; case PRNIOC_GET_1284_STATUS: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_GET_1284_STATUS(0x%x)", cmd); /* get prnio status */ err = usbprn_prnio_get_1284_status(usbprnp, arg, flag); break; case PRNIOC_GET_TIMEOUTS: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_GET_TIMEOUTS(0x%x)", cmd); /* Get the parameters */ err = usbprn_prnio_get_timeouts(usbprnp, arg, flag); break; case PRNIOC_SET_TIMEOUTS: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_SET_TIMEOUTS(0x%x)", cmd); /* Get the parameters */ err = usbprn_prnio_set_timeouts(usbprnp, arg, flag); break; case PRNIOC_RESET: USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl : PRNIOC_RESET(0x%x)", cmd); /* nothing */ err = 0; break; default: USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: unknown(0x%x)", cmd); err = EINVAL; } usb_release_access(usbprnp->usbprn_ser_acc); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl: End "); return (err); } /* * breakup by physio */ static void usbprn_minphys(struct buf *bp) { usbprn_state_t *usbprnp = ddi_get_soft_state(usbprn_statep, USBPRN_MINOR_TO_INSTANCE(getminor(bp->b_edev))); mutex_enter(&usbprnp->usbprn_mutex); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_minphys: bcount=%lu", bp->b_bcount); if (bp->b_bcount > usbprnp->usbprn_max_bulk_xfer_size) { bp->b_bcount = min(usbprn_max_xfer_size, usbprnp->usbprn_max_bulk_xfer_size); } else { bp->b_bcount = min(usbprn_max_xfer_size, bp->b_bcount); } mutex_exit(&usbprnp->usbprn_mutex); } /* * usbprn_open_usb_pipes: * Open all pipes on the device */ static int usbprn_open_usb_pipes(usbprn_state_t *usbprnp) { usb_pipe_policy_t *policy; usbprn_ps_t *bulk_in = &usbprnp->usbprn_bulk_in; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_open_usb_pipes:"); /* * Intitialize the pipe policy for the bulk out pipe */ mutex_enter(&usbprnp->usbprn_mutex); policy = &(bulk_out->ps_policy); policy->pp_max_async_reqs = 1; mutex_exit(&usbprnp->usbprn_mutex); /* Open bulk_out pipe */ if (usb_pipe_open(usbprnp->usbprn_dip, &bulk_out->ps_ept_descr, policy, USB_FLAGS_SLEEP, &bulk_out->ps_handle) != USB_SUCCESS) { return (USB_FAILURE); } #ifdef LATER mutex_enter(&usbprnp->usbprn_mutex); /* Open the bulk in pipe if one exists */ if (bulk_in->ps_ept_descr->bLength) { /* * Initialize the pipe policy for the Bulk In pipe */ policy = &bulk_in->ps_policy; bulk_in->ps_flags = USBPRN_PS_IDLE; policy->pp_max_async_reqs = 1; mutex_exit(&usbprnp->usbprn_mutex); /* Open bulk_in pipe */ if (usb_pipe_open(usbprnp->usbprn_dip, bulk_in->ps_ept_descr, policy, USB_FLAGS_SLEEP, &bulk_in->ps_handle) != USB_SUCCESS) { return (USB_FAILURE); } } else { mutex_exit(&usbprnp->usbprn_mutex); } #else mutex_enter(&usbprnp->usbprn_mutex); bulk_in->ps_flags = USBPRN_PS_IDLE; mutex_exit(&usbprnp->usbprn_mutex); #endif USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_open_usb_pipes: success"); return (USB_SUCCESS); } /* * usbprn_close_usb_pipes: * Close the default/bulk in/out pipes synchronously */ static void usbprn_close_usb_pipes(usbprn_state_t *usbprnp) { usbprn_ps_t *bulk_in = &usbprnp->usbprn_bulk_in; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_close_usb_pipes:"); #ifdef DEBUG mutex_enter(&usbprnp->usbprn_mutex); ASSERT(bulk_out->ps_flags == USBPRN_PS_IDLE); ASSERT(bulk_in->ps_flags == USBPRN_PS_IDLE); mutex_exit(&usbprnp->usbprn_mutex); #endif /* * close the pipe, if another thread is already closing the * pipe, we get USB_INVALID_PIPE */ if (bulk_out->ps_handle) { USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_close_usb_pipes: Closing bulk out pipe"); usb_pipe_close(usbprnp->usbprn_dip, bulk_out->ps_handle, USB_FLAGS_SLEEP, NULL, NULL); bulk_out->ps_handle = NULL; } if (bulk_in->ps_handle) { USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_close_usb_pipes: Closing bulk in pipe"); usb_pipe_close(usbprnp->usbprn_dip, bulk_in->ps_handle, USB_FLAGS_SLEEP, NULL, NULL); bulk_in->ps_handle = NULL; } } /* * usbprn_getparms: * Get the parameters for the device */ static int usbprn_getparms(usbprn_state_t *usbprnp, intptr_t arg, int flag) { ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyout(&usbprnp->usbprn_setparms, (caddr_t)arg, sizeof (struct ecpp_transfer_parms), flag)) { return (EFAULT); } return (0); } /* * usbprn_setparms: * Set the parameters for the device */ static int usbprn_setparms(usbprn_state_t *usbprnp, intptr_t arg, int flag) { struct ecpp_transfer_parms xfer; ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyin((caddr_t)arg, &xfer, sizeof (struct ecpp_transfer_parms), flag)) { return (EFAULT); } if ((xfer.write_timeout < USBPRN_XFER_TIMEOUT_MIN) || (xfer.write_timeout > USBPRN_XFER_TIMEOUT_MAX)) { return (EINVAL); } if (!((xfer.mode == ECPP_CENTRONICS) || (xfer.mode == ECPP_COMPAT_MODE) || (xfer.mode == ECPP_NIBBLE_MODE) || (xfer.mode == ECPP_ECP_MODE) || (xfer.mode == ECPP_DIAG_MODE))) { return (EINVAL); } if (xfer.mode != ECPP_CENTRONICS) { return (EPROTONOSUPPORT); } mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_setparms = xfer; usbprnp->usbprn_prn_timeouts.tmo_forward = xfer.write_timeout; mutex_exit(&usbprnp->usbprn_mutex); return (0); } /* * usbprn_geterr: * Return the any device error state */ static void usbprn_geterr(usbprn_state_t *usbprnp, intptr_t arg, int flag) { struct bpp_error_status bpp_status; bzero(&bpp_status, sizeof (bpp_status)); mutex_enter(&usbprnp->usbprn_mutex); bpp_status.bus_error = 0; bpp_status.timeout_occurred = 0; bpp_status.pin_status = usbprn_error_state(usbprnp->usbprn_last_status); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_geterr: status=0x%x", usbprnp->usbprn_last_status); mutex_exit(&usbprnp->usbprn_mutex); (void) ddi_copyout(&bpp_status, (caddr_t)arg, sizeof (struct bpp_error_status), flag); } /* * usbprn_error_state: * Map the driver error state to that of the application */ static char usbprn_error_state(uchar_t status) { uchar_t app_err_status = 0; if (!(status & USB_PRINTER_PORT_NO_ERROR)) { app_err_status |= USB_PRINTER_ERR_ERR; } if (status & USB_PRINTER_PORT_EMPTY) { app_err_status |= USB_PRINTER_PE_ERR; } if (!(status & USB_PRINTER_PORT_NO_SELECT)) { app_err_status |= USB_PRINTER_SLCT_ERR; } return (app_err_status); } static int usbprn_ioctl_get_status(usbprn_state_t *usbprnp) { /* Check the transfer mode */ mutex_enter(&usbprnp->usbprn_mutex); /* if device is disconnected or pipes closed, fail immediately */ if (!(USBPRN_DEVICE_ACCESS_OK(usbprnp))) { mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_ioctl_get_status: device can't be accessed"); return (EIO); } mutex_exit(&usbprnp->usbprn_mutex); if (usbprn_get_port_status(usbprnp) != USB_SUCCESS) { return (EIO); } return (0); } /* * usbprn_testio: * Execute the ECPP_TESTIO ioctl */ /* ARGSUSED1 */ static int usbprn_testio(usbprn_state_t *usbprnp, int flag) { int err; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_testio: begin"); if ((err = usbprn_ioctl_get_status(usbprnp)) != 0) { return (err); } /* There is an error. Return it to the user */ mutex_enter(&usbprnp->usbprn_mutex); if (usbprn_error_state(usbprnp->usbprn_last_status) != 0) { mutex_exit(&usbprnp->usbprn_mutex); return (EIO); } else { mutex_exit(&usbprnp->usbprn_mutex); return (0); } } /* * usbprn_prnio_get_status: * Execute the PRNIOC_GET_STATUS ioctl */ static int usbprn_prnio_get_status(usbprn_state_t *usbprnp, intptr_t arg, int flag) { uint_t prnio_status = 0; int err; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_prnio_get_status: begin"); /* capture printer status */ err = usbprn_ioctl_get_status(usbprnp); mutex_enter(&usbprnp->usbprn_mutex); if (usbprnp->usbprn_dev_state == USB_DEV_ONLINE) { prnio_status |= PRN_ONLINE; } if ((err == 0) && (usbprnp->usbprn_last_status & USB_PRINTER_PORT_NO_ERROR)) { prnio_status |= PRN_READY; } mutex_exit(&usbprnp->usbprn_mutex); if (ddi_copyout(&prnio_status, (caddr_t)arg, sizeof (prnio_status), flag)) { return (EFAULT); } return (0); } /* * usbprn_prnio_get_1284_status: * Execute the PRNIOC_GET_1284_STATUS ioctl */ static int usbprn_prnio_get_1284_status(usbprn_state_t *usbprnp, intptr_t arg, int flag) { uchar_t status; int err; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_prnio_get_1284_status: begin"); if ((err = usbprn_ioctl_get_status(usbprnp)) != 0) { return (err); } /* status was captured successfully */ mutex_enter(&usbprnp->usbprn_mutex); status = usbprnp->usbprn_last_status & (USB_PRINTER_PORT_NO_ERROR | USB_PRINTER_PORT_NO_SELECT | USB_PRINTER_PORT_EMPTY); mutex_exit(&usbprnp->usbprn_mutex); if (ddi_copyout(&status, (caddr_t)arg, sizeof (status), flag)) { return (EFAULT); } return (0); } /* * usbprn_prnio_get_ifcap: * Execute the PRNIOC_GET_IFCAP ioctl */ /* ARGSUSED */ static int usbprn_prnio_get_ifcap(usbprn_state_t *usbprnp, intptr_t arg, int flag) { ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyout(&usbprn_ifcap, (caddr_t)arg, sizeof (usbprn_ifcap), flag)) { return (EFAULT); } return (0); } /* * usbprn_prnio_get_ifcap: * Execute the PRNIOC_SET_IFCAP ioctl */ /* ARGSUSED */ static int usbprn_prnio_set_ifcap(usbprn_state_t *usbprnp, intptr_t arg, int flag) { uint_t new_ifcap; ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyin((caddr_t)arg, &new_ifcap, sizeof (new_ifcap), flag)) { return (EFAULT); } /* no settable capabilities */ if (usbprn_ifcap != new_ifcap) { return (EINVAL); } return (0); } /* * usbprn_prnio_get_ifinfo: * Execute the PRNIOC_GET_IFINFO ioctl */ /* ARGSUSED */ static int usbprn_prnio_get_ifinfo(usbprn_state_t *usbprnp, intptr_t arg, int flag) { struct prn_interface_info prn_info; int rlen, len; rlen = strlen(usbprn_prnio_ifinfo); #ifdef _MULTI_DATAMODEL ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); switch (ddi_model_convert_from(flag & FMODELS)) { case DDI_MODEL_ILP32: { struct prn_interface_info32 prn_info32; if (ddi_copyin((caddr_t)arg, &prn_info32, sizeof (struct prn_interface_info32), flag)) { return (EFAULT); } prn_info32.if_rlen = rlen; len = min(rlen, prn_info32.if_len); if (ddi_copyout(&usbprn_prnio_ifinfo[0], (caddr_t)(uintptr_t)prn_info32.if_data, len, flag)) { return (EFAULT); } if (ddi_copyout(&prn_info32, (caddr_t)arg, sizeof (struct prn_interface_info32), flag)) { return (EFAULT); } break; } case DDI_MODEL_NONE: #endif /* _MULTI_DATAMODEL */ ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyin((caddr_t)arg, &prn_info, sizeof (struct prn_interface_info), flag)) { return (EFAULT); } prn_info.if_rlen = rlen; len = min(rlen, prn_info.if_len); if (ddi_copyout(&usbprn_prnio_ifinfo[0], prn_info.if_data, len, flag)) { return (EFAULT); } if (ddi_copyout(&prn_info, (caddr_t)arg, sizeof (struct prn_interface_info), flag)) { return (EFAULT); } #ifdef _MULTI_DATAMODEL break; } #endif /* _MULTI_DATAMODEL */ return (0); } /* * usbprn_prnio_getdevid: * Execute the PRNIOC_GET_1284_DEVID ioctl */ static int usbprn_prnio_get_1284_devid(usbprn_state_t *usbprnp, intptr_t arg, int flag) { struct prn_1284_device_id prn_devid; int len; ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); #ifdef _MULTI_DATAMODEL switch (ddi_model_convert_from(flag & FMODELS)) { case DDI_MODEL_ILP32: { struct prn_1284_device_id32 prn_devid32; if (ddi_copyin((caddr_t)arg, &prn_devid32, sizeof (struct prn_1284_device_id32), flag)) { return (EFAULT); } prn_devid32.id_rlen = usbprnp->usbprn_device_id_len - 2; len = min(prn_devid32.id_rlen, prn_devid32.id_len); if (ddi_copyout(usbprnp->usbprn_device_id + 2, (caddr_t)(uintptr_t)prn_devid32.id_data, len, flag)) { return (EFAULT); } if (ddi_copyout(&prn_devid32, (caddr_t)arg, sizeof (struct prn_1284_device_id32), flag)) { return (EFAULT); } break; } case DDI_MODEL_NONE: #endif /* _MULTI_DATAMODEL */ if (ddi_copyin((caddr_t)arg, &prn_devid, sizeof (struct prn_1284_device_id), flag)) { return (EFAULT); } prn_devid.id_rlen = usbprnp->usbprn_device_id_len - 2; len = min(prn_devid.id_rlen, prn_devid.id_len); if (ddi_copyout(usbprnp->usbprn_device_id + 2, prn_devid.id_data, len, flag)) { return (EFAULT); } if (ddi_copyout(&prn_devid, (caddr_t)arg, sizeof (struct prn_1284_device_id), flag)) { return (EFAULT); } #ifdef _MULTI_DATAMODEL break; } #endif /* _MULTI_DATAMODEL */ return (0); } /* * usbprn_prnio_get_timeouts: * Return timeout */ static int usbprn_prnio_get_timeouts(usbprn_state_t *usbprnp, intptr_t arg, int flag) { ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyout(&usbprnp->usbprn_prn_timeouts, (caddr_t)arg, sizeof (struct prn_timeouts), flag)) { return (EFAULT); } return (0); } /* * usbprn_prnio_set_timeouts: * Set write timeout and prn timeout */ static int usbprn_prnio_set_timeouts(usbprn_state_t *usbprnp, intptr_t arg, int flag) { struct prn_timeouts prn_timeouts; ASSERT(!(mutex_owned(&usbprnp->usbprn_mutex))); if (ddi_copyin((caddr_t)arg, &prn_timeouts, sizeof (struct prn_timeouts), flag)) { return (EFAULT); } if ((prn_timeouts.tmo_forward < USBPRN_XFER_TIMEOUT_MIN) || (prn_timeouts.tmo_forward > USBPRN_XFER_TIMEOUT_MAX)) { return (EINVAL); } mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_prn_timeouts = prn_timeouts; usbprnp->usbprn_setparms.write_timeout = prn_timeouts.tmo_forward; mutex_exit(&usbprnp->usbprn_mutex); return (0); } /* * usbprn_biodone: * If there is a bp, complete it */ static void usbprn_biodone(usbprn_state_t *usbprnp, int err, int bytes_remaining) { struct buf *bp = usbprnp->usbprn_bp; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; usbprn_ps_t *bulk_in = &usbprnp->usbprn_bulk_in; ASSERT(mutex_owned(&usbprnp->usbprn_mutex)); /* all pipes must be idle now */ ASSERT(bulk_out->ps_flags == USBPRN_PS_IDLE); ASSERT(bulk_in->ps_flags == USBPRN_PS_IDLE); if (bp) { bp->b_resid = bytes_remaining; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_biodone: " "bp=0x%p bcount=0x%lx resid=0x%lx remaining=0x%x err=%d", (void *)bp, bp->b_bcount, bp->b_resid, bytes_remaining, err); if (err) { bioerror(bp, err); } usbprnp->usbprn_bp = NULL; biodone(bp); } /* release access */ usb_release_access(usbprnp->usbprn_dev_acc); } /* * usbprn_send_async_bulk_data: * Send bulk data down to the device through the bulk out pipe */ static void usbprn_send_async_bulk_data(usbprn_state_t *usbprnp) { int rval; int timeout; mblk_t *mp; size_t max_xfer_count, xfer_count; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; usb_bulk_req_t *req; mutex_enter(&usbprnp->usbprn_mutex); ASSERT(bulk_out->ps_flags == USBPRN_PS_NEED_TO_XFER); timeout = usbprnp->usbprn_setparms.write_timeout; max_xfer_count = usbprnp->usbprn_bp->b_bcount; mp = usbprnp->usbprn_bulk_mp; ASSERT(mp != NULL); xfer_count = MBLKL(mp); mutex_exit(&usbprnp->usbprn_mutex); req = usb_alloc_bulk_req(usbprnp->usbprn_dip, 0, USB_FLAGS_SLEEP); req->bulk_len = (uint_t)xfer_count; req->bulk_data = mp; req->bulk_timeout = timeout; req->bulk_cb = usbprn_bulk_xfer_cb; req->bulk_exc_cb = usbprn_bulk_xfer_exc_cb; req->bulk_client_private = (usb_opaque_t)usbprnp; req->bulk_attributes = USB_ATTRS_AUTOCLEARING; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_send_async_bulk_data: req = 0x%p " "max_bulk_xfer_size=%lu mp=0x%p xfer_cnt=%lu timeout=%x", (void *)req, max_xfer_count, (void *)mp, xfer_count, timeout); ASSERT(xfer_count <= max_xfer_count); if ((rval = usb_pipe_bulk_xfer(bulk_out->ps_handle, req, 0)) != USB_SUCCESS) { USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_send_async_bulk_data: Bulk mp=0x%p " "rval=%d", (void *)mp, rval); mutex_enter(&usbprnp->usbprn_mutex); bulk_out->ps_flags = USBPRN_PS_IDLE; usbprnp->usbprn_bulk_mp = NULL; usbprn_biodone(usbprnp, EIO, 0); mutex_exit(&usbprnp->usbprn_mutex); usb_free_bulk_req(req); } else { mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_bulk_mp = NULL; mutex_exit(&usbprnp->usbprn_mutex); } } /* * usbprn_bulk_xfer_cb * Callback for a normal transfer for both bulk pipes. */ /*ARGSUSED*/ static void usbprn_bulk_xfer_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) { usbprn_state_t *usbprnp = (usbprn_state_t *)req->bulk_client_private; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; ASSERT(usbprnp != NULL); ASSERT(!mutex_owned(&usbprnp->usbprn_mutex)); mutex_enter(&usbprnp->usbprn_mutex); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_bulk_xfer_cb: mp=0x%p ", (void *)usbprnp->usbprn_bulk_mp); ASSERT(bulk_out->ps_flags == USBPRN_PS_NEED_TO_XFER); ASSERT(usbprnp->usbprn_bp != NULL); ASSERT((req->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0); /* * if device is disconnected or driver close called, return * The pipe could be closed, or a timeout could have * come in and the pipe is being reset. If the * state isn't transferring, then return */ if (!(USBPRN_DEVICE_ACCESS_OK(usbprnp)) || (bulk_out->ps_flags != USBPRN_PS_NEED_TO_XFER)) { USB_DPRINTF_L3(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_bulk_xfer_cb: no access or pipe closed"); bulk_out->ps_flags = USBPRN_PS_IDLE; usbprn_biodone(usbprnp, EIO, 0); } else { /* * data has been xferred, complete the bp. */ USB_DPRINTF_L3(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_bulk_xfer_cb: transaction over"); bulk_out->ps_flags = USBPRN_PS_IDLE; usbprn_biodone(usbprnp, 0, 0); } mutex_exit(&usbprnp->usbprn_mutex); usb_free_bulk_req(req); } /* * usbprn_bulk_xfer_exc_cb: * Exception callback for the bulk pipes */ static void usbprn_bulk_xfer_exc_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) { usbprn_state_t *usbprnp = (usbprn_state_t *)req->bulk_client_private; usbprn_ps_t *bulk_out = &usbprnp->usbprn_bulk_out; int bytes_remaining = 0; mblk_t *data = req->bulk_data; usb_cr_t completion_reason = req->bulk_completion_reason; usb_cb_flags_t cb_flags = req->bulk_cb_flags; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_bulk_xfer_exc_cb: " "pipe=0x%p req=0x%p cr=%d cb_flags=0x%x data=0x%p", (void *)pipe, (void *)req, completion_reason, cb_flags, (void *)data); ASSERT((req->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0); ASSERT(data != NULL); mutex_enter(&usbprnp->usbprn_mutex); ASSERT(bulk_out->ps_flags == USBPRN_PS_NEED_TO_XFER); bulk_out->ps_flags = USBPRN_PS_IDLE; bulk_out->ps_cr = completion_reason; if (data) { bytes_remaining = MBLKL(data); } /* * If the pipe is closed or device not responding or not in * need of transfer, just give up on this bp. */ if (!(USBPRN_DEVICE_ACCESS_OK(usbprnp)) || (req->bulk_completion_reason == USB_CR_DEV_NOT_RESP)) { USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_bulk_xfer_exc_cb: " "device not accesible or wrong state"); usbprn_biodone(usbprnp, EIO, 0); } else { if (completion_reason == USB_CR_TIMEOUT) { USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_bulk_xfer_exc_cb: timeout error, " "xferred %lu bytes", ((usbprnp->usbprn_bp->b_bcount) - bytes_remaining)); usbprn_biodone(usbprnp, 0, bytes_remaining); } else { usbprn_biodone(usbprnp, EIO, 0); } } mutex_exit(&usbprnp->usbprn_mutex); usb_free_bulk_req(req); } /* * usbprn_reconnect_event_cb: * Called upon when the device is hotplugged back; event handling */ /*ARGSUSED*/ static int usbprn_reconnect_event_cb(dev_info_t *dip) { usbprn_state_t *usbprnp = (usbprn_state_t *)ddi_get_soft_state(usbprn_statep, ddi_get_instance(dip)); ASSERT(usbprnp != NULL); USB_DPRINTF_L3(PRINT_MASK_EVENTS, usbprnp->usbprn_log_handle, "usbprn_reconnect_event_cb:"); (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); mutex_enter(&usbprnp->usbprn_mutex); ASSERT(usbprnp->usbprn_dev_state == USB_DEV_DISCONNECTED); mutex_exit(&usbprnp->usbprn_mutex); usbprn_restore_device_state(dip, usbprnp); if (usbprnp->usbprn_ugen_hdl) { (void) usb_ugen_reconnect_ev_cb(usbprnp->usbprn_ugen_hdl); } usb_release_access(usbprnp->usbprn_ser_acc); return (USB_SUCCESS); } /* * usbprn_disconnect_event_cb: * callback for disconnect events */ /*ARGSUSED*/ static int usbprn_disconnect_event_cb(dev_info_t *dip) { usbprn_state_t *usbprnp = (usbprn_state_t *)ddi_get_soft_state( usbprn_statep, ddi_get_instance(dip)); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_disconnect_event_cb: Begin"); (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_dev_state = USB_DEV_DISCONNECTED; if (usbprnp->usbprn_flags & USBPRN_OPEN) { USB_DPRINTF_L0(PRINT_MASK_EVENTS, usbprnp->usbprn_log_handle, "device was disconnected while open. " "Data may have been lost"); } /* For now, we set the offline bit in usbprn_last_status */ usbprnp->usbprn_last_status |= USB_PRINTER_PORT_NO_SELECT; mutex_exit(&usbprnp->usbprn_mutex); if (usbprnp->usbprn_ugen_hdl) { (void) usb_ugen_disconnect_ev_cb(usbprnp->usbprn_ugen_hdl); } usb_release_access(usbprnp->usbprn_ser_acc); USB_DPRINTF_L4(PRINT_MASK_EVENTS, usbprnp->usbprn_log_handle, "usbprn_disconnect_event_cb: End"); return (USB_SUCCESS); } /* * usbprn_restore_device_state: * set original configuration of the device * Restores data xfer */ static void usbprn_restore_device_state(dev_info_t *dip, usbprn_state_t *usbprnp) { int alt, rval, iface; USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_restore_device_state:"); mutex_enter(&usbprnp->usbprn_mutex); ASSERT((usbprnp->usbprn_dev_state == USB_DEV_DISCONNECTED) || (usbprnp->usbprn_dev_state == USB_DEV_SUSPENDED)); mutex_exit(&usbprnp->usbprn_mutex); /* Check if we are talking to the same device */ if (usb_check_same_device(dip, usbprnp->usbprn_log_handle, USB_LOG_L0, PRINT_MASK_ALL, USB_CHK_ALL, NULL) != USB_SUCCESS) { /* change the device state from suspended to disconnected */ mutex_enter(&usbprnp->usbprn_mutex); usbprnp->usbprn_dev_state = USB_DEV_DISCONNECTED; mutex_exit(&usbprnp->usbprn_mutex); return; } USB_DPRINTF_L0(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "Printer has been reconnected but data may have been lost"); mutex_enter(&usbprnp->usbprn_mutex); /* set last status to online */ usbprnp->usbprn_last_status &= ~USB_PRINTER_PORT_NO_SELECT; mutex_exit(&usbprnp->usbprn_mutex); /* Get the port status */ if (usbprn_get_port_status(usbprnp) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_restore_device_state: port status failed"); return; } mutex_enter(&usbprnp->usbprn_mutex); if ((usbprnp->usbprn_last_status & USB_PRINTER_PORT_NO_ERROR) == 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_restore_device_state: An error with the printer"); } if (usbprnp->usbprn_flags & USBPRN_OPEN) { mutex_exit(&usbprnp->usbprn_mutex); usbprn_close_usb_pipes(usbprnp); mutex_enter(&usbprnp->usbprn_mutex); } /* restore alternate */ alt = usbprnp->usbprn_if_descr.bAlternateSetting, mutex_exit(&usbprnp->usbprn_mutex); iface = usb_owns_device(dip) ? 0 : usb_get_if_number(dip); if ((rval = usb_set_alt_if(dip, iface, alt, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "usbprn_restore_device_state: set alternate failed (%d)", rval); return; } mutex_enter(&usbprnp->usbprn_mutex); if (usbprnp->usbprn_flags & USBPRN_OPEN) { mutex_exit(&usbprnp->usbprn_mutex); (void) usbprn_open_usb_pipes(usbprnp); mutex_enter(&usbprnp->usbprn_mutex); } if (usbprnp->usbprn_pm && usbprnp->usbprn_pm->usbprn_wakeup_enabled) { mutex_exit(&usbprnp->usbprn_mutex); (void) usb_handle_remote_wakeup(usbprnp->usbprn_dip, USB_REMOTE_WAKEUP_ENABLE); mutex_enter(&usbprnp->usbprn_mutex); } usbprnp->usbprn_dev_state = USB_DEV_ONLINE; mutex_exit(&usbprnp->usbprn_mutex); USB_DPRINTF_L4(PRINT_MASK_ALL, usbprnp->usbprn_log_handle, "usbprn_restore_device_state: End"); } /* * Create power managements components */ static void usbprn_create_pm_components(dev_info_t *dip, usbprn_state_t *usbprnp) { usbprn_power_t *usbprnpm; uint_t pwr_states; USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_create_pm_components: Begin"); /* Allocate the state structure */ usbprnpm = kmem_zalloc(sizeof (usbprn_power_t), KM_SLEEP); usbprnp->usbprn_pm = usbprnpm; usbprnpm->usbprn_pm_capabilities = 0; usbprnpm->usbprn_current_power = USB_DEV_OS_FULL_PWR; if (usb_create_pm_components(dip, &pwr_states) == USB_SUCCESS) { USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_create_pm_components: " "created PM components"); if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { usbprnpm->usbprn_wakeup_enabled = 1; } usbprnpm->usbprn_pwr_states = (uint8_t)pwr_states; (void) pm_raise_power(usbprnp->usbprn_dip, 0, USB_DEV_OS_FULL_PWR); } else { USB_DPRINTF_L2(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_create_pm_components: Failed"); } USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_create_pm_components: END"); } /* * usbprn_pwrlvl0: * Functions to handle power transition for OS levels 0 -> 3 */ static int usbprn_pwrlvl0(usbprn_state_t *usbprnp) { int rval; USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_pwrlvl0:"); switch (usbprnp->usbprn_dev_state) { case USB_DEV_ONLINE: /* Deny the powerdown request if the device is busy */ if (usbprnp->usbprn_pm->usbprn_pm_busy != 0) { return (USB_FAILURE); } /* Issue USB D3 command to the device here */ rval = usb_set_device_pwrlvl3(usbprnp->usbprn_dip); ASSERT(rval == USB_SUCCESS); usbprnp->usbprn_dev_state = USB_DEV_PWRED_DOWN; usbprnp->usbprn_pm->usbprn_current_power = USB_DEV_OS_PWR_OFF; /* FALLTHRU */ case USB_DEV_DISCONNECTED: case USB_DEV_SUSPENDED: /* allow a disconnect/cpr'ed device to go to lower power */ return (USB_SUCCESS); case USB_DEV_PWRED_DOWN: default: USB_DPRINTF_L2(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_pwrlvl0: illegal dev state"); return (USB_FAILURE); } } /* * usbprn_pwrlvl1: * Functions to handle power transition to OS levels -> 2 */ static int usbprn_pwrlvl1(usbprn_state_t *usbprnp) { int rval; USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_pwrlvl1:"); /* Issue USB D2 command to the device here */ rval = usb_set_device_pwrlvl2(usbprnp->usbprn_dip); ASSERT(rval == USB_SUCCESS); return (USB_FAILURE); } /* * usbprn_pwrlvl2: * Functions to handle power transition to OS levels -> 1 */ static int usbprn_pwrlvl2(usbprn_state_t *usbprnp) { int rval; USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_pwrlvl2:"); /* Issue USB D1 command to the device here */ rval = usb_set_device_pwrlvl1(usbprnp->usbprn_dip); ASSERT(rval == USB_SUCCESS); return (USB_FAILURE); } /* * usbprn_pwrlvl3: * Functions to handle power transition to OS level -> 0 */ static int usbprn_pwrlvl3(usbprn_state_t *usbprnp) { USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_pwrlvl3:"); switch (usbprnp->usbprn_dev_state) { case USB_DEV_PWRED_DOWN: /* Issue USB D0 command to the device here */ (void) usb_set_device_pwrlvl0(usbprnp->usbprn_dip); usbprnp->usbprn_dev_state = USB_DEV_ONLINE; usbprnp->usbprn_pm->usbprn_current_power = USB_DEV_OS_FULL_PWR; /* FALLTHRU */ case USB_DEV_ONLINE: /* we are already in full power */ /* FALLTHRU */ case USB_DEV_DISCONNECTED: case USB_DEV_SUSPENDED: /* * PM framework tries to put us in full power * during system shutdown. If we are disconnected/cpr'ed * return success anyways */ return (USB_SUCCESS); default: USB_DPRINTF_L4(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_pwrlvl3:"); return (USB_FAILURE); } } /* * usbprn_power : * Power entry point */ /* ARGSUSED */ static int usbprn_power(dev_info_t *dip, int comp, int level) { usbprn_state_t *usbprnp; usbprn_power_t *pm; int rval = USB_FAILURE; usbprnp = (usbprn_state_t *)ddi_get_soft_state(usbprn_statep, ddi_get_instance(dip)); USB_DPRINTF_L3(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_power: Begin: level=%d", level); (void) usb_serialize_access(usbprnp->usbprn_ser_acc, USB_WAIT, 0); mutex_enter(&usbprnp->usbprn_mutex); pm = usbprnp->usbprn_pm; ASSERT(pm != NULL); /* Check if we are transitioning to a legal power level */ if (USB_DEV_PWRSTATE_OK(pm->usbprn_pwr_states, level)) { USB_DPRINTF_L2(PRINT_MASK_PM, usbprnp->usbprn_log_handle, "usbprn_power: illegal power level=%d " "pwr_states=0x%x", level, pm->usbprn_pwr_states); goto done; } switch (level) { case USB_DEV_OS_PWR_OFF : rval = usbprn_pwrlvl0(usbprnp); break; case USB_DEV_OS_PWR_1 : rval = usbprn_pwrlvl1(usbprnp); break; case USB_DEV_OS_PWR_2 : rval = usbprn_pwrlvl2(usbprnp); break; case USB_DEV_OS_FULL_PWR : rval = usbprn_pwrlvl3(usbprnp); break; } done: mutex_exit(&usbprnp->usbprn_mutex); usb_release_access(usbprnp->usbprn_ser_acc); return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); } /* * usbprn_print_long: * Breakup a string which is > USBPRN_PRINT_MAXLINE and print it */ static void usbprn_print_long(usbprn_state_t *usbprnp, char *str, int len) { char *tmp = str; char pbuf[USBPRN_PRINT_MAXLINE]; for (;;) { if (len <= USBPRN_PRINT_MAXLINE) { USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "%s", tmp); break; } else { bcopy(tmp, pbuf, USBPRN_PRINT_MAXLINE); USB_DPRINTF_L4(PRINT_MASK_ATTA, usbprnp->usbprn_log_handle, "%s", pbuf); tmp += USBPRN_PRINT_MAXLINE; len -= USBPRN_PRINT_MAXLINE; } } } static void usbprn_pm_busy_component(usbprn_state_t *usbprn_statep) { ASSERT(!mutex_owned(&usbprn_statep->usbprn_mutex)); if (usbprn_statep->usbprn_pm != NULL) { mutex_enter(&usbprn_statep->usbprn_mutex); usbprn_statep->usbprn_pm->usbprn_pm_busy++; USB_DPRINTF_L4(PRINT_MASK_PM, usbprn_statep->usbprn_log_handle, "usbprn_pm_busy_component: %d", usbprn_statep->usbprn_pm->usbprn_pm_busy); mutex_exit(&usbprn_statep->usbprn_mutex); if (pm_busy_component(usbprn_statep->usbprn_dip, 0) != DDI_SUCCESS) { mutex_enter(&usbprn_statep->usbprn_mutex); usbprn_statep->usbprn_pm->usbprn_pm_busy--; USB_DPRINTF_L2(PRINT_MASK_PM, usbprn_statep->usbprn_log_handle, "usbprn_pm_busy_component: %d", usbprn_statep->usbprn_pm->usbprn_pm_busy); mutex_exit(&usbprn_statep->usbprn_mutex); } } } static void usbprn_pm_idle_component(usbprn_state_t *usbprn_statep) { ASSERT(!mutex_owned(&usbprn_statep->usbprn_mutex)); if (usbprn_statep->usbprn_pm != NULL) { if (pm_idle_component(usbprn_statep->usbprn_dip, 0) == DDI_SUCCESS) { mutex_enter(&usbprn_statep->usbprn_mutex); ASSERT(usbprn_statep->usbprn_pm->usbprn_pm_busy > 0); usbprn_statep->usbprn_pm->usbprn_pm_busy--; USB_DPRINTF_L4(PRINT_MASK_PM, usbprn_statep->usbprn_log_handle, "usbprn_pm_idle_component: %d", usbprn_statep->usbprn_pm->usbprn_pm_busy); mutex_exit(&usbprn_statep->usbprn_mutex); } } }