From owner-p4-projects@FreeBSD.ORG Mon May 29 15:39:05 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 5772616A8F2; Mon, 29 May 2006 15:39:05 +0000 (UTC) X-Original-To: perforce@FreeBSD.org Delivered-To: perforce@FreeBSD.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id A33B216A6FA for ; Mon, 29 May 2006 15:39:03 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 3CC4543D48 for ; Mon, 29 May 2006 15:39:03 +0000 (GMT) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id k4TFbnCF059903 for ; Mon, 29 May 2006 15:37:49 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id k4TFbnkq059900 for perforce@freebsd.org; Mon, 29 May 2006 15:37:49 GMT (envelope-from hselasky@FreeBSD.org) Date: Mon, 29 May 2006 15:37:49 GMT Message-Id: <200605291537.k4TFbnkq059900@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 98074 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 29 May 2006 15:39:08 -0000 http://perforce.freebsd.org/chv.cgi?CH=98074 Change 98074 by hselasky@hselasky_mini_itx on 2006/05/29 15:37:49 Created a generic buffer abstraction system for USB device drivers. See "usbd_alloc_mbufs()" in "usbd_subr.c". Reworked the "ulpt" driver. The "ulpt" driver should now be MP-safe. Please test! Affected files ... .. //depot/projects/usb/src/sys/dev/usb/ulpt.c#5 edit .. //depot/projects/usb/src/sys/dev/usb/usb_subr.c#5 edit .. //depot/projects/usb/src/sys/dev/usb/usb_subr.h#5 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/ulpt.c#5 (text+ko) ==== @@ -1,0 +1,1133 @@ +/* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */ + +/*- + * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF + * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +__FBSDID("$FreeBSD: src/sys/dev/usb/ulpt.c,v 1.68 2005/11/12 17:39:31 iedowse Exp $"); + +#ifdef USB_DEBUG +#define DPRINTF(n,fmt,...) \ + do { if (ulpt_debug > (n)) { \ + printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0) + +static int ulpt_debug = 0; +SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt"); +SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RW, + &ulpt_debug, 0, "ulpt debug level"); +#else +#define DPRINTF(...) +#endif + +#define DEV2SC(dev) (dev)->si_drv1 + +#define ULPT_BSIZE (1<<17) /* bytes */ +#define ULPT_IFQ_MAXLEN 2 /* units */ +#define ULPT_WATCHDOG_INTERVAL 5 /* times per second */ +#define ULPT_N_TRANSFER 4 /* units */ + +#define UR_GET_DEVICE_ID 0x00 +#define UR_GET_PORT_STATUS 0x01 +#define UR_SOFT_RESET 0x02 + +#define LPS_NERR 0x08 /* printer no error */ +#define LPS_SELECT 0x10 /* printer selected */ +#define LPS_NOPAPER 0x20 /* printer out of paper */ +#define LPS_INVERT (LPS_SELECT|LPS_NERR) +#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) + +struct ulpt_softc { + device_t sc_dev; + struct usbd_device * sc_udev; + struct cdev * sc_cdev_1; + struct cdev * sc_cdev_2; + struct usbd_xfer * sc_xfer[ULPT_N_TRANSFER]; + struct __callout sc_watchdog; + struct mtx sc_mtx; + struct usbd_ifqueue sc_rdq_free; + struct usbd_ifqueue sc_rdq_used; + struct usbd_ifqueue sc_wrq_free; + struct usbd_ifqueue sc_wrq_used; + + void * sc_mem_ptr_1; /* should be freed at detach */ + void * sc_mem_ptr_2; /* should be freed at detach */ + + u_int32_t sc_flags; +#define ULPT_FLAG_DEV_OPEN 0x00000001 /* device is open */ +#define ULPT_FLAG_NO_READ 0x00000002 /* device has no read endpoint */ +#define ULPT_FLAG_RST_SLP 0x00000004 /* device is sleeping */ +#define ULPT_FLAG_RST_WUP 0x00000008 /* device is waiting for wakeup */ +#define ULPT_FLAG_WR_UIO 0x00000010 /* device is doing I/O */ +#define ULPT_FLAG_RD_UIO 0x00000020 /* device is doing I/O */ +#define ULPT_FLAG_WR_SLP 0x00000040 /* device is sleeping */ +#define ULPT_FLAG_RD_SLP 0x00000080 /* device is sleeping */ +#define ULPT_FLAG_WR_WUP 0x00000100 /* device is waiting for wakeup */ +#define ULPT_FLAG_RD_WUP 0x00000200 /* device is waiting for wakeup */ +#define ULPT_FLAG_CLOSING 0x00000400 /* device is closing */ +#define ULPT_FLAG_GONE 0x00000800 /* device is gone */ +#define ULPT_FLAG_WAIT_USB 0x00001000 /* device is waiting for USB callbacks */ +#define ULPT_FLAG_WAIT_CO 0x00002000 /* device is waiting for callouts */ +#define ULPT_FLAG_DUMP_READ 0x00004000 /* device is not opened for read */ +#define ULPT_FLAG_WR_FLUSH 0x00008000 /* device is flushing write data */ +#define ULPT_FLAG_NO_FLUSH 0x00010000 /* device should not flush write data */ +#define ULPT_FLAG_PIPE_ERR 0x00020000 /* device has signalled an error */ + + u_int8_t sc_iface_no; + u_int8_t sc_last_status; + + u_int8_t sc_wakeup_detach; /* dummy */ + u_int8_t sc_wakeup_reset; /* dummy */ + u_int8_t sc_wakeup_read; /* dummy */ + u_int8_t sc_wakeup_write; /* dummy */ + u_int8_t sc_wakeup_flush; /* dummy */ + u_int8_t sc_wakeup_sync_1; /* dummy */ +}; + +extern cdevsw_t ulpt_cdevsw; + +static void +ulpt_watchdog(void *__sc) +{ + struct ulpt_softc *sc = __sc; + + mtx_assert(&(sc->sc_mtx), MA_OWNED); + + DPRINTF(2, "start sc=%p\n", sc); + + /* start reading of status, if not already started */ + + usbd_transfer_start(sc->sc_xfer[2]); + + if ((sc->sc_flags & (ULPT_FLAG_NO_READ|ULPT_FLAG_DUMP_READ)) && + (sc->sc_flags & (ULPT_FLAG_DEV_OPEN)) && + (!(sc->sc_flags & (ULPT_FLAG_CLOSING)))) { + + /* start reading of data, if not already started */ + + usbd_transfer_start(sc->sc_xfer[1]); + } + + __callout_reset(&(sc->sc_watchdog), + hz / ULPT_WATCHDOG_INTERVAL, + &ulpt_watchdog, sc); + + mtx_unlock(&(sc->sc_mtx)); + + return; +} + +static void +ulpt_write_callback(struct usbd_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usbd_mbuf *m; + + USBD_CHECK_STATUS(xfer); + + tr_transferred: + tr_setup: + USBD_IF_DEQUEUE(&sc->sc_wrq_used, m); + + if (m) { + + if (m->cur_data_len > ULPT_BSIZE) { + /* extra length check */ + m->cur_data_len = ULPT_BSIZE; + } + + bcopy(m->cur_data_ptr, xfer->buffer, m->cur_data_len); + xfer->length = m->cur_data_len; + + USBD_IF_ENQUEUE(&sc->sc_wrq_free, m); + + usbd_start_hardware(xfer); + + if (sc->sc_flags & ULPT_FLAG_WR_WUP) { + sc->sc_flags &= ~ULPT_FLAG_WR_WUP; + wakeup(&(sc->sc_wakeup_write)); + } + + } else { + if (sc->sc_flags & ULPT_FLAG_WR_FLUSH) { + sc->sc_flags &= ~ULPT_FLAG_WR_FLUSH; + wakeup(&(sc->sc_wakeup_flush)); + } + } + return; + + tr_error: + DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); + + sc->sc_flags |= ULPT_FLAG_PIPE_ERR; + + if (sc->sc_flags & ULPT_FLAG_WR_WUP) { + sc->sc_flags &= ~ULPT_FLAG_WR_WUP; + wakeup(&(sc->sc_wakeup_write)); + } + + if (sc->sc_flags & ULPT_FLAG_WR_FLUSH) { + sc->sc_flags &= ~ULPT_FLAG_WR_FLUSH; + wakeup(&(sc->sc_wakeup_flush)); + } + + return; +} + +static void +ulpt_read_callback(struct usbd_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + struct usbd_mbuf *m; + + USBD_CHECK_STATUS(xfer); + + tr_transferred: + if (sc->sc_flags & (ULPT_FLAG_NO_READ|ULPT_FLAG_DUMP_READ)) { + return; + } + + USBD_IF_DEQUEUE(&sc->sc_rdq_free, m); + + if (m) { + USBD_MBUF_RESET(m); + + if (xfer->actlen > ULPT_BSIZE) { + /* extra length check */ + xfer->actlen = ULPT_BSIZE; + } + + bcopy(xfer->buffer, m->cur_data_ptr, xfer->actlen); + m->cur_data_len = xfer->actlen; + + USBD_IF_ENQUEUE(&sc->sc_rdq_used, m); + + if (sc->sc_flags & ULPT_FLAG_RD_WUP) { + sc->sc_flags &= ~ULPT_FLAG_RD_WUP; + wakeup(&(sc->sc_wakeup_read)); + } + } + + tr_setup: + USBD_IF_POLL(&sc->sc_rdq_free, m); + + if (m) { + usbd_start_hardware(xfer); + } + + return; + + tr_error: + DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); + + sc->sc_flags |= ULPT_FLAG_PIPE_ERR; + + if (sc->sc_flags & ULPT_FLAG_RD_WUP) { + sc->sc_flags &= ~ULPT_FLAG_RD_WUP; + wakeup(&(sc->sc_wakeup_read)); + } + return; +} + +static void +ulpt_status_callback(struct usbd_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + usb_device_request_t *req = xfer->buffer; + u_int8_t cur_status = req->bData[0]; + u_int8_t new_status; + + USBD_CHECK_STATUS(xfer); + + tr_transferred: + cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK; + new_status = cur_status & ~sc->sc_last_status; + sc->sc_last_status = cur_status; + + if (new_status & LPS_SELECT) + log(LOG_NOTICE, "%s: offline\n", + device_get_nameunit(sc->sc_dev)); + else if (new_status & LPS_NOPAPER) + log(LOG_NOTICE, "%s: out of paper\n", + device_get_nameunit(sc->sc_dev)); + else if (new_status & LPS_NERR) + log(LOG_NOTICE, "%s: output error\n", + device_get_nameunit(sc->sc_dev)); + return; + + tr_setup: + req->bmRequestType = UT_READ_CLASS_INTERFACE; + req->bRequest = UR_GET_PORT_STATUS; + USETW(req->wValue, 0); + USETW(req->wIndex, sc->sc_iface_no); + USETW(req->wLength, 1); + + usbd_start_hardware(xfer); + + return; + + tr_error: + DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); + return; +} + +static void +ulpt_reset_callback(struct usbd_xfer *xfer) +{ + struct ulpt_softc *sc = xfer->priv_sc; + usb_device_request_t *req = xfer->buffer; + + USBD_CHECK_STATUS(xfer); + + tr_error: + if (req->bmRequestType == UT_WRITE_CLASS_OTHER) { + /* + * There was a mistake in the USB printer 1.0 spec that + * gave the request type as UT_WRITE_CLASS_OTHER; it + * should have been UT_WRITE_CLASS_INTERFACE. Many + * printers use the old one, so try both: + */ + req->bmRequestType = UT_WRITE_CLASS_INTERFACE; /* 1.1 */ + req->bRequest = UR_SOFT_RESET; + USETW(req->wValue, 0); + USETW(req->wIndex, sc->sc_iface_no); + USETW(req->wLength, 0); + + usbd_start_hardware(xfer); + + return; + } + + tr_transferred: + if (sc->sc_flags & ULPT_FLAG_RST_WUP) { + sc->sc_flags &= ~ULPT_FLAG_RST_WUP; + + wakeup(&(sc->sc_wakeup_reset)); + } + return; + + tr_setup: + req->bmRequestType = UT_WRITE_CLASS_OTHER; /* 1.0 */ + req->bRequest = UR_SOFT_RESET; + USETW(req->wValue, 0); + USETW(req->wIndex, sc->sc_iface_no); + USETW(req->wLength, 0); + + usbd_start_hardware(xfer); + + return; +} + +static const struct usbd_config ulpt_config[ULPT_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = -1, /* any */ + .direction = UE_DIR_OUT, + .bufsize = ULPT_BSIZE, + .flags = 0, + .callback = &ulpt_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = -1, /* any */ + .direction = UE_DIR_IN, + .bufsize = ULPT_BSIZE, + .flags = USBD_SHORT_XFER_OK, + .callback = &ulpt_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = -1, + .bufsize = sizeof(usb_device_request_t) + 1, + .callback = &ulpt_status_callback, + .timeout = 1000, /* 1 second */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = -1, + .bufsize = sizeof(usb_device_request_t), + .callback = &ulpt_reset_callback, + .timeout = 1000, /* 1 second */ + }, +}; + +/* prototypes */ + +static device_probe_t ulpt_probe; +static device_attach_t ulpt_attach; +static device_detach_t ulpt_detach; +static d_close_t ulpt_close; + +static int +ulpt_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + usb_interface_descriptor_t *id; + + DPRINTF(10, "\n"); + + if (uaa->iface == NULL) { + return UMATCH_NONE; + } + + id = usbd_get_interface_descriptor(uaa->iface); + + if ((id != NULL) && + (id->bInterfaceClass == UICLASS_PRINTER) && + (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && + ((id->bInterfaceProtocol == UIPROTO_PRINTER_UNI) || + (id->bInterfaceProtocol == UIPROTO_PRINTER_BI) || + (id->bInterfaceProtocol == UIPROTO_PRINTER_1284))) { + return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO; + } + return UMATCH_NONE; +} + +static void +ulpt_detach_complete(struct usbd_memory_info *info) +{ + struct ulpt_softc *sc = info->priv_sc; + + mtx_lock(&(sc->sc_mtx)); + + if (sc->sc_flags & ULPT_FLAG_WAIT_USB) { + sc->sc_flags &= ~ULPT_FLAG_WAIT_USB; + wakeup(&(sc->sc_wakeup_detach)); + } + + mtx_unlock(&(sc->sc_mtx)); + + return; +} + +static int +ulpt_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct ulpt_softc *sc = device_get_softc(dev); + struct usbd_interface *iface_ptr = uaa->iface; + usb_interface_descriptor_t *id; + int32_t iface_index = uaa->iface_index; + int32_t iface_alt_index = 0; + int32_t unit = device_get_unit(dev); + usbd_status err; + + DPRINTF(10, "sc=%p\n", sc); + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + + sc->sc_rdq_free.ifq_maxlen = ULPT_IFQ_MAXLEN; + sc->sc_rdq_used.ifq_maxlen = ULPT_IFQ_MAXLEN; + sc->sc_wrq_free.ifq_maxlen = ULPT_IFQ_MAXLEN; + sc->sc_wrq_used.ifq_maxlen = ULPT_IFQ_MAXLEN; + + usbd_set_desc(dev, sc->sc_udev); + + mtx_init(&(sc->sc_mtx), "ulpt lock", NULL, MTX_DEF|MTX_RECURSE); + + __callout_init_mtx(&(sc->sc_watchdog), + &(sc->sc_mtx), CALLOUT_RETURNUNLOCKED); +#if 0 + /* TODO: "__callout_init_mtx()" does not support this: */ + + sc->sc_flags |= ULPT_FLAG_WAIT_CO; +#endif + + sc->sc_mem_ptr_1 = + usbd_alloc_mbufs(M_DEVBUF, &(sc->sc_rdq_free), ULPT_BSIZE, ULPT_IFQ_MAXLEN); + + if (sc->sc_mem_ptr_1 == NULL) { + goto detach; + } + + sc->sc_mem_ptr_2 = + usbd_alloc_mbufs(M_DEVBUF, &(sc->sc_wrq_free), ULPT_BSIZE, ULPT_IFQ_MAXLEN); + + if (sc->sc_mem_ptr_2 == NULL) { + goto detach; + } + + /* search through all the descriptors looking for bidir mode */ + + while(iface_alt_index < 32) { + + err = usbd_fill_iface_data + (sc->sc_udev, iface_index, iface_alt_index); + + if (err) { + DPRINTF(0, "end of alternate settings, " + "error=%s\n", usbd_errstr(err)); + goto detach; + } + + id = usbd_get_interface_descriptor(iface_ptr); + + if ((id->bInterfaceClass == UICLASS_PRINTER) && + (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && + (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) { + goto found; + } + + iface_alt_index++; + } + goto detach; + + found: + + DPRINTF(0, "setting alternate " + "config number: %d\n", iface_alt_index); + + if (iface_alt_index) { + + err = usbreq_set_interface + (sc->sc_udev, iface_index, iface_alt_index); + + if (err) { + DPRINTF(0, "could not set alternate " + "config, error=%s\n", usbd_errstr(err)); + goto detach; + } + } + + sc->sc_iface_no = id->bInterfaceNumber; + + err = usbd_transfer_setup(sc->sc_udev, iface_index, sc->sc_xfer, + ulpt_config, ULPT_N_TRANSFER, sc, &(sc->sc_mtx), + &ulpt_detach_complete); + if (err) { + DPRINTF(0, "error=%s\n", usbd_errstr(err)) ; + goto detach; + } + + sc->sc_flags |= ULPT_FLAG_WAIT_USB; + + if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_BROKEN_BIDIR) { + /* this device doesn't handle reading properly. */ + sc->sc_flags |= ULPT_FLAG_NO_READ; + } + + device_printf(sc->sc_dev, "using %s-directional mode\n", + (sc->sc_flags & ULPT_FLAG_NO_READ) ? "uni" : "bi"); + + +#if 0 +/* + * This code is disabled because for some mysterious reason it causes + * printing not to work. But only sometimes, and mostly with + * UHCI and less often with OHCI. *sigh* + */ + { + usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); + usb_device_request_t req; + int len, alen; + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UR_GET_DEVICE_ID; + USETW(req.wValue, cd->bConfigurationValue); + USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); + USETW(req.wLength, sizeof devinfo - 1); + err = usbd_do_request_flags(dev, &req, devinfo, USBD_SHORT_XFER_OK, + &alen, USBD_DEFAULT_TIMEOUT); + if (err) { + device_printf(sc->sc_dev, "cannot get device id\n"); + } else if (alen <= 2) { + device_printf(sc->sc_dev, "empty device id, no " + "printer connected?\n"); + } else { + /* devinfo now contains an IEEE-1284 device ID */ + len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff); + if (len > sizeof devinfo - 3) + len = sizeof devinfo - 3; + devinfo[len] = 0; + printf("%s: device id <", device_get_nameunit(sc->sc_dev)); + ieee1284_print_id(devinfo+2); + printf(">\n"); + } + } +#endif + + sc->sc_cdev_1 = make_dev + (&ulpt_cdevsw, (2*unit)|0, UID_ROOT, GID_OPERATOR, + 0644, "ulpt%d", unit); + + sc->sc_cdev_2 = make_dev + (&ulpt_cdevsw, (2*unit)|1, UID_ROOT, GID_OPERATOR, + 0644, "unlpt%d", unit); + + if (sc->sc_cdev_1) { + DEV2SC(sc->sc_cdev_1) = sc; + } + + if (sc->sc_cdev_2) { + DEV2SC(sc->sc_cdev_2) = sc; + } + + /* start watchdog (returns unlocked) */ + + mtx_lock(&(sc->sc_mtx)); + + ulpt_watchdog(sc); + + return 0; + + detach: + ulpt_detach(dev); + return ENOMEM; +} + +static int +ulpt_detach(device_t dev) +{ + struct ulpt_softc *sc = device_get_softc(dev); + int error; + + DPRINTF(0, "sc=%p\n", sc); + + mtx_lock(&(sc->sc_mtx)); + sc->sc_flags |= ULPT_FLAG_GONE; + mtx_unlock(&(sc->sc_mtx)); + + if (sc->sc_cdev_1) { + + ulpt_close(sc->sc_cdev_1, 0, 0, 0); + + DEV2SC(sc->sc_cdev_1) = NULL; + + destroy_dev(sc->sc_cdev_1); + } + + if (sc->sc_cdev_2) { + + ulpt_close(sc->sc_cdev_2, 0, 0, 0); + + DEV2SC(sc->sc_cdev_2) = NULL; + + destroy_dev(sc->sc_cdev_2); + } + + mtx_lock(&(sc->sc_mtx)); + + __callout_stop(&(sc->sc_watchdog)); + + mtx_unlock(&(sc->sc_mtx)); + + usbd_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER); + + if (sc->sc_mem_ptr_1) { + free(sc->sc_mem_ptr_1, M_DEVBUF); + } + + if (sc->sc_mem_ptr_2) { + free(sc->sc_mem_ptr_2, M_DEVBUF); + } + + /* wait for callbacks to be aborted */ + + mtx_lock(&(sc->sc_mtx)); + while (sc->sc_flags & (ULPT_FLAG_WAIT_USB|ULPT_FLAG_WAIT_CO)) { + + error = msleep(&(sc->sc_wakeup_detach), &(sc->sc_mtx), + PRIBIO, "ulpt_sync_2", 0); + } + mtx_unlock(&(sc->sc_mtx)); + + mtx_destroy(&(sc->sc_mtx)); + + return 0; +} + +static int +ulpt_uiomove(struct ulpt_softc *sc, u_int32_t context_bit, void *cp, int n, + struct uio *uio) +{ + int error; + + sc->sc_flags |= context_bit; + + mtx_unlock(&(sc->sc_mtx)); + + /* "uiomove()" can sleep so one + * needs to make a wrapper, exiting + * the mutex and checking things + */ + error = uiomove(cp, n, uio); + + mtx_lock(&(sc->sc_mtx)); + + sc->sc_flags &= ~context_bit; + + if (sc->sc_flags & ULPT_FLAG_CLOSING) { + wakeup(&(sc->sc_wakeup_sync_1)); + error = EINTR; + } + + if (sc->sc_flags & ULPT_FLAG_PIPE_ERR) { + error = EINTR; + } + + if (error) { + sc->sc_flags |= ULPT_FLAG_NO_FLUSH; + } + + return error; +} + +static int +ulpt_msleep(struct ulpt_softc *sc, u_int32_t context_bit, void *ident) +{ + int error; + + sc->sc_flags |= context_bit; + + error = msleep(ident, &(sc->sc_mtx), PRIBIO|PCATCH, "ulpt_sleep", 0); + + sc->sc_flags &= ~context_bit; + + if (sc->sc_flags & ULPT_FLAG_CLOSING) { + wakeup(&(sc->sc_wakeup_sync_1)); + error = EINTR; + } + + if (sc->sc_flags & ULPT_FLAG_PIPE_ERR) { + error = EINTR; + } + + if (error) { + sc->sc_flags |= ULPT_FLAG_NO_FLUSH; + } + return error; +} + +static int +ulpt_reset(struct ulpt_softc *sc) +{ + DPRINTF(1, "\n"); + + /* start reset, if not already started */ + + usbd_transfer_start(sc->sc_xfer[3]); + + return ulpt_msleep(sc, ULPT_FLAG_RST_SLP|ULPT_FLAG_RST_WUP, + &(sc->sc_wakeup_reset)); +} + +static int +ulpt_open(struct cdev *dev, int flag, int mode, struct thread *td) +{ + u_int8_t no_prime = (minor(dev) & 1); + struct ulpt_softc *sc = DEV2SC(dev); + struct usbd_mbuf *m; + int error = 0; + + DPRINTF(1, "\n"); + + if (sc == NULL) { + return EIO; + } + + mtx_lock(&(sc->sc_mtx)); + + if (sc->sc_flags & + (ULPT_FLAG_DEV_OPEN|ULPT_FLAG_GONE| + ULPT_FLAG_RST_SLP|ULPT_FLAG_RST_WUP)) { + error = EBUSY; + goto done; + } + + if (no_prime == 0) { + error = ulpt_reset(sc); + if (error) { + goto done; + } + } + + /* reset read queue */ + + while(1) { + USBD_IF_DEQUEUE(&(sc->sc_rdq_used), m); + + if (m) { + USBD_IF_ENQUEUE(&(sc->sc_rdq_free), m); + } else { + break; + } + } + + /* reset write queue */ + + while(1) { + USBD_IF_DEQUEUE(&(sc->sc_wrq_used), m); + + if (m) { + USBD_IF_ENQUEUE(&(sc->sc_wrq_free), m); + } else { + break; + } + } + + if (flag & FREAD) { + sc->sc_flags &= ~ULPT_FLAG_DUMP_READ; + } else { + sc->sc_flags |= ULPT_FLAG_DUMP_READ; + } + + sc->sc_flags |= ULPT_FLAG_DEV_OPEN; + + done: + mtx_unlock(&(sc->sc_mtx)); + + DPRINTF(0, "done, error=%d\n", error); + return error; +} + +static int +ulpt_close(struct cdev *dev, int flag, int mode, struct thread *td) +{ + struct ulpt_softc *sc = DEV2SC(dev); + int error; + + DPRINTF(1, "\n"); + + if (sc == NULL) { + return EIO; + } + + mtx_lock(&(sc->sc_mtx)); + + if (sc->sc_flags & (ULPT_FLAG_WR_FLUSH|ULPT_FLAG_CLOSING)) { + goto done; + } + + if (sc->sc_flags & ULPT_FLAG_DEV_OPEN) { + + /* + * wait for data to + * be written to pipe: + */ + + if (!(sc->sc_flags & (ULPT_FLAG_GONE|ULPT_FLAG_NO_FLUSH| + ULPT_FLAG_PIPE_ERR))) { + + sc->sc_flags |= ULPT_FLAG_WR_FLUSH; + + /* start write transfer, if not already started */ + + usbd_transfer_start(sc->sc_xfer[0]); + + while (sc->sc_flags & ULPT_FLAG_WR_FLUSH) { + + error = msleep(&(sc->sc_wakeup_flush), &(sc->sc_mtx), + PRIBIO|PCATCH, "ulpt_sync_0", 0); + if (error) { + break; + } + } + } + + sc->sc_flags |= ULPT_FLAG_CLOSING; + + if (sc->sc_xfer[0]) { + usbd_transfer_stop(sc->sc_xfer[0]); + } + + if (sc->sc_xfer[1]) { + usbd_transfer_stop(sc->sc_xfer[1]); + } + + while (sc->sc_flags & + (ULPT_FLAG_RD_SLP|ULPT_FLAG_RD_WUP|ULPT_FLAG_RD_UIO| + ULPT_FLAG_WR_SLP|ULPT_FLAG_WR_WUP|ULPT_FLAG_WR_UIO| + ULPT_FLAG_RST_SLP|ULPT_FLAG_RST_WUP)) { + + if (sc->sc_flags & ULPT_FLAG_RD_WUP) { + sc->sc_flags &= ~ULPT_FLAG_RD_WUP; + wakeup(&(sc->sc_wakeup_read)); + } + + if (sc->sc_flags & ULPT_FLAG_WR_WUP) { + sc->sc_flags &= ~ULPT_FLAG_WR_WUP; + wakeup(&(sc->sc_wakeup_write)); + } + + if (sc->sc_flags & ULPT_FLAG_RST_WUP) { + sc->sc_flags &= ~ULPT_FLAG_RST_WUP; + wakeup(&(sc->sc_wakeup_reset)); + } + + error = msleep(&(sc->sc_wakeup_sync_1), &(sc->sc_mtx), + PRIBIO, "ulpt_sync_1", 0); + } + + sc->sc_flags &= ~(ULPT_FLAG_DEV_OPEN| + ULPT_FLAG_CLOSING| + ULPT_FLAG_WR_FLUSH| + ULPT_FLAG_NO_FLUSH| + ULPT_FLAG_PIPE_ERR); + } + + done: + mtx_unlock(&(sc->sc_mtx)); + + DPRINTF(0, "closed\n"); + + return 0; +} + +static int +ulpt_write(struct cdev *dev, struct uio *uio, int flags) +{ + struct ulpt_softc *sc = DEV2SC(dev); + struct usbd_mbuf *m; + int error = 0; + int io_len; + + DPRINTF(1, "\n"); + + if (sc == NULL) { + return EIO; + } + + mtx_lock(&(sc->sc_mtx)); + + if(sc->sc_flags & (ULPT_FLAG_CLOSING|ULPT_FLAG_GONE| + ULPT_FLAG_WR_SLP|ULPT_FLAG_WR_UIO| + ULPT_FLAG_PIPE_ERR)) { + error = EIO; + goto done; + } + + while (uio->uio_resid) { + + USBD_IF_DEQUEUE(&sc->sc_wrq_free, m); + + if (m == NULL) { + error = ulpt_msleep(sc, (ULPT_FLAG_WR_SLP|ULPT_FLAG_WR_WUP), + &(sc->sc_wakeup_write)); + if (error) { + break; + } else { + continue; + } + } + + USBD_MBUF_RESET(m); + + io_len = min(m->cur_data_len, uio->uio_resid); + + m->cur_data_len = io_len; + + DPRINTF(1, "transfer %d bytes to %p\n", + io_len, m->cur_data_ptr); + + error = ulpt_uiomove(sc, ULPT_FLAG_WR_UIO, >>> TRUNCATED FOR MAIL (1000 lines) <<<