PERFORCE change 98074 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Mon May 29 08:39:08 PDT 2006
http://perforce.freebsd.org/chv.cgi?CH=98074
Change 98074 by hselasky at 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 at 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 <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/syslog.h>
+
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_subr.h>
+#include <dev/usb/usb_quirks.h>
+
+__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) <<<
More information about the p4-projects
mailing list