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