Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 1 Jan 2007 04:39:08 GMT
From:      Keith Jones<mithy@blueyonder.co.uk>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   usb/107388: [PATCH] Add utoppy device from NetBSD
Message-ID:  <200701010439.l014d8E8003230@www.freebsd.org>
Resent-Message-ID: <200701010440.l014eMTv015850@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         107388
>Category:       usb
>Synopsis:       [PATCH] Add utoppy device from NetBSD
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-usb
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jan 01 04:40:16 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Keith Jones
>Release:        6.2-PRERELEASE
>Organization:
>Environment:
FreeBSD beastie.local 6.2-PRERELEASE FreeBSD 6.2-PRERELEASE #4: Sun Dec 31 18:50:38 GMT 2006    root@beastie.local:/usr/obj/usr/src/sys/BEASTIE  i386
>Description:
The following diffs amount to a port to FreeBSD of Steve Woodford's utoppy(4) driver, originally written for NetBSD 4.0, for communication over USB with Topfield TF5000PVR and TF5800PVR digital video recorders.
>How-To-Repeat:
This device is not currently supported in FreeBSD-STABLE or -CURRENT.
>Fix:
Apply the following patch to the source tree (diff -Nru format). The patch was applied to -STABLE so there may be some minor differences with -CURRENT.

Patch attached with submission follows:

diff -Nru /usr/local/cvsup-stable/src/share/man/man4/Makefile /usr/src/share/man/man4/Makefile
--- /usr/local/cvsup-stable/src/share/man/man4/Makefile	Sat Dec 30 17:55:14 2006
+++ /usr/src/share/man/man4/Makefile	Sun Dec 31 20:01:31 2006
@@ -370,6 +370,7 @@
 	usb.4 \
 	uscanner.4 \
 	utopia.4 \
+	utoppy.4 \
 	uvisor.4 \
 	uvscom.4 \
 	vga.4 \
diff -Nru /usr/local/cvsup-stable/src/share/man/man4/utoppy.4 /usr/src/share/man/man4/utoppy.4
--- /usr/local/cvsup-stable/src/share/man/man4/utoppy.4	Thu Jan  1 01:00:00 1970
+++ /usr/src/share/man/man4/utoppy.4	Sun Dec 31 19:58:01 2006
@@ -0,0 +1,313 @@
+.\" $NetBSD: utoppy.4,v 1.3 2006/04/04 20:34:46 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Steve C. Woodford.
+.\"
+.\" 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.
+.\"
+.Dd April 3, 2006
+.Dt UTOPPY 4
+.Os
+.Sh NAME
+.Nm utoppy
+.Nd USB driver for the Topfield TF5000PVR range of digital video recorders
+.Sh SYNOPSIS
+.Cd "utoppy* at uhub? port ?"
+.Pp
+.In dev/usb/utoppy.h
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the Topfield TF5000PVR range of DVB recorders
+(nicknamed
+.Ql Toppy )
+which are popular in Europe and Australia.
+These recorders have a
+.Tn USB
+device interface which can be used to transfer
+recordings to and from the unit's hard disk.
+The
+.Tn USB
+interface can also be used to upload binary images for execution
+on the Toppy's MIPS cpu.
+.Pp
+The Toppy's
+.Tn USB
+protocol has not been officially documented by Topfield,
+but the basic features have been reverse engineered by others in order
+to write replacements for the official
+.Ql Altair
+download/upload program from Topfield.
+.Pp
+Existing replacements for Altair suffer from the fact that they are
+ultimately built on top of
+.Xr ugen 4 .
+This has a number of detrimental side-effects:
+.Bl -enum
+.It
+Performance suffers since all Toppy command packets have to cross the
+user-kernel interface.
+.It
+The userland programs are full of clutter to deal with interpreting the
+command/status packets, not to mention byte-swapping and host endian
+issues.
+.It
+Signals can interrupt a data transfer at a critical point, leaving the
+Toppy in an undefined state.
+For example, interrupting a download with
+.Ql Turbo
+mode enabled will leave the Toppy completely unresponsive to the remote
+control, and prevent timer-based recordings from starting.
+.El
+.Pp
+The
+.Nm
+driver provides a clean and stable interface to the Toppy protocol, and
+ensures that an interrupt caused by a signal does not leave the Toppy in
+an undefined state.
+.Sh UTOPPY INTERFACE
+Use the following header file to get access to the
+.Tn utoppy
+specific structures and defines.
+.Bd -literal
+#include \*[Lt]dev/usb/utoppy.h\*[Gt]
+.Ed
+.Pp
+The
+.Nm
+driver can be accessed through the
+.Pa /dev/utoppyN
+character device.
+The primary means of controlling the driver is by issuing a series of
+.Xr ioctl 2
+system calls followed by
+.Xr read 2
+or
+.Xr write 2
+system calls as appropriate.
+.Pp
+The following
+.Xr ioctl 2
+commands are supported by the
+.Nm
+driver:
+.Bl -tag -width xxxxxx
+.It Dv UTOPPYIOTURBO Fa "int *mode"
+This command can be used to enable or disable
+.Ql Turbo
+mode for subsequent
+.Dv UTOPPYIOREADFILE
+or
+.Dv UTOPPYIOWRITEFILE
+commands (see below).
+If
+.Fa num
+is non-zero, Turbo mode will be enabled.
+Otherwise Turbo mode will be disabled.
+In non-Turbo mode, the Toppy's
+.Tn USB
+interface is capable of sustaining around 5.6 Mbit/s during a file transfer.
+With Turbo mode enabled, it can sustain just over 16 Mbit/s.
+Of course, these figures are valid only if the Toppy is connected via a
+.Tn USB
+2.0 interface.
+Performance using an older
+.Tn USB
+1 interface will be significantly lower.
+.It Dv UTOPPYIOCANCEL Fa void
+This command can be used to abort an in-progress
+.Dv UTOPPYIOREADDIR ,
+.Dv UTOPPYIOREADFILE ,
+or
+.Dv UTOPPYIOWRITEFILE
+command.
+.It Dv UTOPPYIOREBOOT Fa void
+This command will cause the Toppy to reboot cleanly.
+.It Dv UTOPPYIOSTATS Fa "struct utoppy_stats *stats"
+This command retrieves statistics for the Toppy's hard disk.
+.Bd -literal
+struct utoppy_stats {
+	uint64_t us_hdd_size;	/* Size of the disk, in bytes */
+	uint64_t us_hdd_free;	/* Free space, in bytes */
+};
+.Ed
+.It UTOPPYIORENAME Fa "struct utoppy_rename *rename"
+This command is used to rename a file or directory on the Toppy's
+hard disk.
+The full pathname to each file must be provided.
+.Bd -literal
+struct utoppy_rename {
+	char *ur_old_path;	/* Path to existing file */
+	char *ur_new_path;	/* Path to new file */
+};
+.Ed
+.It UTOPPYIOMKDIR Fa "char *path"
+This command creates the directory specified by
+.Fa path .
+.It UTOPPYIODELETE Fa "char *path"
+This command deletes the file or directory specified by
+.Fa path .
+.It UTOPPYIOREADDIR Fa "char *path"
+This command initiates a read of the contents of the directory specified by
+.Fa path .
+After issuing this command, the directory contents must be read using
+consecutive
+.Xr read 2
+system calls.
+Each
+.Xr read 2
+will transfer one or more directory entries into the user-supplied buffer.
+The buffer must be large enough to receive at least one directory entry.
+When
+.Xr read 2
+returns zero, all directory entries have been read.
+.Pp
+A directory entry is described using the following data structure:
+.Bd -literal
+struct utoppy_dirent {
+	char ud_path[UTOPPY_MAX_FILENAME_LEN + 1];
+	enum {
+		UTOPPY_DIRENT_UNKNOWN,
+		UTOPPY_DIRENT_DIRECTORY,
+		UTOPPY_DIRENT_FILE
+	} ud_type;
+	off_t ud_size;
+	time_t ud_mtime;
+	uint32_t ud_attributes;
+};
+.Ed
+.Pp
+The
+.Va ud_path
+field contains the name of the directory entry.
+.Pp
+The
+.Va ud_type
+field specifies whether the entry corresponds to a file or a sub-directory.
+.Pp
+The
+.Va ud_size
+field is valid for files only, and specifies the file's size in bytes.
+.Pp
+The
+.Va ud_mtime
+field describes the file or directory's modification time, specified as
+seconds from the Unix epoch.
+The timestamp is relative to the current timezone, so
+.Xr localtime 3
+can be used to convert it into human readable form.
+Note that the Toppy sets directory timestamps to a predefined value so
+they are not particularly useful.
+.Pp
+The
+.Va ud_attributes
+field is not used at this time.
+.It UTOPPYIOREADFILE Fa "struct utoppy_readfile *"
+This command is used to initiate reading a file from the Toppy's hard disk.
+The full pathname, together with the file offset at which to start reading,
+is specified using the following data structure:
+.Bd -literal
+struct utoppy_readfile {
+	char *ur_path;
+	off_t ur_offset;
+};
+.Ed
+.Pp
+After issuing this command, the file must be read using consecutive
+.Xr read 2
+system calls.
+When
+.Xr read 2
+returns zero, the entire file has been read.
+.It UTOPPYIOWRITEFILE Fa "struct utoppy_writefile *"
+This command is used to initiate writing to a file on the Toppy's hard disk.
+The file to be written is described using the following data structure:
+.Bd -literal
+struct utoppy_writefile {
+	char *uw_path;
+	off_t uw_offset;
+	off_t uw_size;
+	time_t uw_mtime;
+};
+.Ed
+.Pp
+The
+.Va uw_path
+field specifies the full pathname of the file to be written.
+.Pp
+The
+.Va uw_offset
+field specifies the file offset at which to start writing, assuming the file
+already exists.
+Otherwise,
+.Va uw_offset
+must be zero.
+.Pp
+The protocol requires that the Toppy must be informed of a file's size in
+advance of the file being written.
+This is accomplished using the
+.Va uw_size
+field.
+It may be possible to work around this limitation in a future version of
+the driver.
+.Pp
+The
+.Va uw_mtime
+field specifies the file's timestamp expressed as seconds from the Unix epoch
+in the local timezone.
+.El
+.Pp
+Due to limitations with the protocol, a
+.Nm
+device can be opened by only one application at a time.
+Also, only a single
+.Dv UTOPPYIOREADDIR ,
+.Dv UTOPPYIOREADFILE ,
+or
+.Dv UTOPPYIOWRITEFILE
+command can be in progress at any given time.
+.Sh FILES
+.Bl -tag -width /dev/utoppy0 -compact
+.It Pa /dev/utoppy0
+device node
+.El
+.Sh SEE ALSO
+.Xr utoppya 1 ,
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+driver
+appeared in
+.Nx 4.0 .
+.Sh AUTHORS
+.An Steve C. Woodford Aq scw@netbsd.org
diff -Nru /usr/local/cvsup-stable/src/sys/conf/files /usr/src/sys/conf/files
--- /usr/local/cvsup-stable/src/sys/conf/files	Sat Dec 30 17:55:15 2006
+++ /usr/src/sys/conf/files	Sun Dec 31 16:39:00 2006
@@ -1002,6 +1002,7 @@
 dev/usb/usbdi.c			optional usb
 dev/usb/usbdi_util.c		optional usb
 dev/usb/uscanner.c		optional uscanner
+dev/usb/utoppy.c		optional utoppy
 dev/usb/uvisor.c		optional uvisor ucom
 dev/usb/uvscom.c		optional uvscom ucom
 dev/utopia/idtphy.c		optional utopia
diff -Nru /usr/local/cvsup-stable/src/sys/dev/usb/usbdevs /usr/src/sys/dev/usb/usbdevs
--- /usr/local/cvsup-stable/src/sys/dev/usb/usbdevs	Sat Dec 30 17:55:15 2006
+++ /usr/src/sys/dev/usb/usbdevs	Sun Dec 31 17:47:07 2006
@@ -500,6 +500,7 @@
 vendor SERVERWORKS	0x1166	ServerWorks
 vendor ACERCM		0x1189	Acer Communications & Multimedia
 vendor SIERRA		0x1199	Sierra Wireless
+vendor TOPFIELD		0x11db	Topfield Co., Ltd
 vendor PROLIFIC2	0x11f6	Prolific
 vendor TWINMOS		0x126f	TwinMOS
 vendor TSUNAMI		0x1241	Tsunami
@@ -1627,6 +1628,9 @@
 
 /* Thrustmaster products */
 product THRUST FUSION_PAD	0xa0a3	Fusion Digital Gamepad
+
+/* Topfield Co., Ltd products */
+product TOPFIELD TF5000PVR	0x1000	TF5000PVR Digital Video Recorder
 
 /* Toshiba Corporation products */
 product TOSHIBA POCKETPC_E740	0x0706	PocketPC e740
diff -Nru /usr/local/cvsup-stable/src/sys/dev/usb/utoppy.c /usr/src/sys/dev/usb/utoppy.c
--- /usr/local/cvsup-stable/src/sys/dev/usb/utoppy.c	Thu Jan  1 01:00:00 1970
+++ /usr/src/sys/dev/usb/utoppy.c	Sun Dec 31 18:47:44 2006
@@ -0,0 +1,2029 @@
+/*	$NetBSD: utoppy.c,v 1.8 2006/11/16 01:33:27 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Steve C. Woodford.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+__KERNEL_RCSID(0, "$NetBSD: utoppy.c,v 1.8 2006/11/16 01:33:27 christos Exp $");
+#elif defined(__FreeBSD__)
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/fcntl.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#endif
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/uio.h>
+#include <sys/conf.h>
+#include <sys/vnode.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <dev/usb/usbdevs.h>
+#elif defined(__FreeBSD__)
+#include "usbdevs.h"
+#endif
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/utoppy.h>
+
+#if defined(__FreeBSD__) && defined(USB_DEBUG)
+#define UTOPPY_DEBUG
+#else
+#undef UTOPPY_DEBUG
+#endif
+#ifdef UTOPPY_DEBUG
+#define	UTOPPY_DBG_OPEN		0x0001
+#define	UTOPPY_DBG_CLOSE	0x0002
+#define	UTOPPY_DBG_READ		0x0004
+#define	UTOPPY_DBG_WRITE	0x0008
+#define	UTOPPY_DBG_IOCTL	0x0010
+#define	UTOPPY_DBG_SEND_PACKET	0x0020
+#define	UTOPPY_DBG_RECV_PACKET	0x0040
+#define	UTOPPY_DBG_ADDPATH	0x0080
+#define	UTOPPY_DBG_READDIR	0x0100
+#define	UTOPPY_DBG_DUMP		0x0200
+#define	DPRINTF(l, m)				\
+		do {				\
+			if (utoppy_debug & l)	\
+				printf m;	\
+		} while (/*CONSTCOND*/0)
+static int utoppy_debug = 0;
+static void utoppy_dump_packet(const void *, size_t);
+#define	DDUMP_PACKET(p, l)					\
+		do {						\
+			if (utoppy_debug & UTOPPY_DBG_DUMP)	\
+				utoppy_dump_packet((p), (l));	\
+		} while (/*CONSTCOND*/0)
+#else
+#define	DPRINTF(l, m)		/* nothing */
+#define	DDUMP_PACKET(p, l)	/* nothing */
+#endif
+
+
+#define	UTOPPY_CONFIG_NO	1
+#define	UTOPPY_NUMENDPOINTS	2
+
+#define	UTOPPY_BSIZE		0xffff
+#define	UTOPPY_FRAG_SIZE	0x1000
+#define	UTOPPY_HEADER_SIZE	8
+#define	UTOPPY_SHORT_TIMEOUT	(500)		/* 0.5 seconds */
+#define	UTOPPY_LONG_TIMEOUT	(10 * 1000)	/* 10 seconds */
+
+/* Protocol Commands and Responses */
+#define	UTOPPY_RESP_ERROR		0x0001
+#define	UTOPPY_CMD_ACK			0x0002
+#define	 UTOPPY_RESP_SUCCESS		UTOPPY_CMD_ACK
+#define	UTOPPY_CMD_CANCEL		0x0003
+#define	UTOPPY_CMD_READY		0x0100
+#define	UTOPPY_CMD_RESET		0x0101
+#define	UTOPPY_CMD_TURBO		0x0102
+#define	UTOPPY_CMD_STATS		0x1000
+#define  UTOPPY_RESP_STATS_DATA		0x1001
+#define	UTOPPY_CMD_READDIR		0x1002
+#define	 UTOPPY_RESP_READDIR_DATA	0x1003
+#define	 UTOPPY_RESP_READDIR_END	0x1004
+#define	UTOPPY_CMD_DELETE		0x1005
+#define	UTOPPY_CMD_RENAME		0x1006
+#define	UTOPPY_CMD_MKDIR		0x1007
+#define	UTOPPY_CMD_FILE			0x1008
+#define  UTOPPY_FILE_WRITE		0
+#define  UTOPPY_FILE_READ		1
+#define	 UTOPPY_RESP_FILE_HEADER	0x1009
+#define	 UTOPPY_RESP_FILE_DATA		0x100a
+#define	 UTOPPY_RESP_FILE_END		0x100b
+
+enum utoppy_state {
+	UTOPPY_STATE_CLOSED,
+	UTOPPY_STATE_OPENING,
+	UTOPPY_STATE_IDLE,
+	UTOPPY_STATE_READDIR,
+	UTOPPY_STATE_READFILE,
+	UTOPPY_STATE_WRITEFILE
+};
+
+struct utoppy_softc {
+	USBBASEDEVICE sc_dev;
+	usbd_device_handle sc_udev;	/* device */
+	usbd_interface_handle sc_iface;	/* interface */
+#if defined(__FreeBSD__)
+	struct cdev *dev;
+#endif
+	int sc_dying;
+	int sc_refcnt;
+
+	enum utoppy_state sc_state;
+	u_int sc_turbo_mode;
+
+	int sc_out;
+	usbd_pipe_handle sc_out_pipe;	/* bulk out pipe */
+	usbd_xfer_handle sc_out_xfer;
+	void *sc_out_buf;
+	void *sc_out_data;
+	uint64_t sc_wr_offset;
+	uint64_t sc_wr_size;
+
+	int sc_in;
+	usbd_pipe_handle sc_in_pipe;	/* bulk in pipe */
+	usbd_xfer_handle sc_in_xfer;
+	void *sc_in_buf;
+	void *sc_in_data;
+	size_t sc_in_len;
+	u_int sc_in_offset;
+};
+
+struct utoppy_header {
+	uint16_t h_len;
+	uint16_t h_crc;
+	uint16_t h_cmd2;
+	uint16_t h_cmd;
+	uint8_t h_data[0];
+};
+#define	UTOPPY_OUT_INIT(sc)					\
+	do {							\
+		struct utoppy_header *_h = sc->sc_out_data;	\
+		_h->h_len = 0;					\
+	} while (/*CONSTCOND*/0)
+
+#define	UTOPPY_MJD_1970 40587u	/* MJD value for Jan 1 00:00:00 1970 */
+
+#define	UTOPPY_FTYPE_DIR	1
+#define	UTOPPY_FTYPE_FILE	2
+
+#define	UTOPPY_IN_DATA(sc)	\
+ ((void*)&(((uint8_t*)(sc)->sc_in_data)[(sc)->sc_in_offset+UTOPPY_HEADER_SIZE]))
+
+#if defined(__NetBSD__)
+dev_type_open(utoppyopen);
+dev_type_close(utoppyclose);
+dev_type_read(utoppyread);
+dev_type_write(utoppywrite);
+dev_type_ioctl(utoppyioctl);
+
+const struct cdevsw utoppy_cdevsw = {
+	utoppyopen, utoppyclose, utoppyread, utoppywrite, utoppyioctl,
+	nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
+};
+#elif defined(__OpenBSD__)
+cdev_decl(utoppy);
+#elif defined(__FreeBSD__)
+d_open_t utoppyopen;
+d_close_t utoppyclose;
+d_write_t utoppywrite;
+d_read_t utoppyread;
+d_ioctl_t utoppyioctl;
+
+Static struct cdevsw utoppy_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_flags =	D_NEEDGIANT,
+	.d_open =	utoppyopen,
+	.d_close =	utoppyclose,
+	.d_write =	utoppywrite,
+	.d_read =	utoppyread,
+	.d_ioctl =	utoppyioctl,
+	.d_name =	"utoppy",
+#if __FreeBSD_version < 500014
+	.d_bmaj =	-1
+#endif
+};
+#endif
+
+#define	UTOPPYUNIT(n)	(minor(n))
+
+USB_DECLARE_DRIVER(utoppy);
+
+USB_MATCH(utoppy)
+{
+	USB_MATCH_START(utoppy, uaa);
+
+	if (uaa->iface == NULL)
+		return (UMATCH_NONE);
+
+	if (uaa->vendor == USB_VENDOR_TOPFIELD &&
+	    uaa->product == USB_PRODUCT_TOPFIELD_TF5000PVR)
+		return (UMATCH_VENDOR_PRODUCT);
+
+	return (UMATCH_NONE);
+}
+
+USB_ATTACH(utoppy)
+{
+	USB_ATTACH_START(utoppy, sc, uaa);
+	usbd_device_handle dev = uaa->device;
+	usb_endpoint_descriptor_t *ed;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	char *devinfo;
+#elif defined(__FreeBSD__)
+	char devinfo[1024];
+#endif
+	u_int8_t epcount;
+	int i;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	devinfo = usbd_devinfo_alloc(dev, 0);
+#elif defined(__FreeBSD__)
+	usbd_devinfo(dev, 0, devinfo);
+#endif
+	USB_ATTACH_SETUP;
+	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	usbd_devinfo_free(devinfo);
+#endif
+
+	sc->sc_dying = 0;
+	sc->sc_refcnt = 0;
+	sc->sc_udev = dev;
+
+	epcount = 0;
+	(void) usbd_endpoint_count(uaa->iface, &epcount);
+	if (epcount != UTOPPY_NUMENDPOINTS) {
+		printf("%s: Expected %d endpoints, got %d\n",
+		    USBDEVNAME(sc->sc_dev), UTOPPY_NUMENDPOINTS, epcount);
+		USB_ATTACH_ERROR_RETURN;
+	}
+
+	sc->sc_in = -1;
+	sc->sc_out = -1;
+
+	for (i = 0; i < epcount; i++) {
+		ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+		if (ed == NULL) {
+			printf("%s: couldn't get ep %d\n",
+			    USBDEVNAME(sc->sc_dev), i);
+			USB_ATTACH_ERROR_RETURN;
+		}
+
+		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+			sc->sc_in = ed->bEndpointAddress;
+		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+			sc->sc_out = ed->bEndpointAddress;
+		}
+	}
+
+	if (sc->sc_out == -1 || sc->sc_in == -1) {
+		printf("%s: could not find bulk in/out endpoints\n",
+		    USBDEVNAME(sc->sc_dev));
+		sc->sc_dying = 1;
+		USB_ATTACH_ERROR_RETURN;
+	}
+
+	sc->sc_iface = uaa->iface;
+	sc->sc_udev = dev;
+
+	sc->sc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+	if (sc->sc_out_xfer == NULL) {
+		printf("%s: could not allocate bulk out xfer\n",
+		    USBDEVNAME(sc->sc_dev));
+		goto fail0;
+	}
+
+	sc->sc_out_buf = usbd_alloc_buffer(sc->sc_out_xfer, UTOPPY_FRAG_SIZE);
+	if (sc->sc_out_buf == NULL) {
+		printf("%s: could not allocate bulk out buffer\n",
+		    USBDEVNAME(sc->sc_dev));
+		goto fail1;
+	}
+
+	sc->sc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+	if (sc->sc_in_xfer == NULL) {
+		printf("%s: could not allocate bulk in xfer\n",
+		    USBDEVNAME(sc->sc_dev));
+		goto fail1;
+	}
+
+	sc->sc_in_buf = usbd_alloc_buffer(sc->sc_in_xfer, UTOPPY_FRAG_SIZE);
+	if (sc->sc_in_buf == NULL) {
+		printf("%s: could not allocate bulk in buffer\n",
+		    USBDEVNAME(sc->sc_dev));
+		goto fail2;
+	}
+
+#if defined(__FreeBSD__)
+	sc->dev = make_dev(&utoppy_cdevsw, device_get_unit(self),
+		UID_ROOT, GID_OPERATOR, 0644, "utoppy%d", device_get_unit(self));
+#endif
+
+	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+			   USBDEV(sc->sc_dev));
+
+	USB_ATTACH_SUCCESS_RETURN;
+
+ fail2:	usbd_free_xfer(sc->sc_in_xfer);
+	sc->sc_in_xfer = NULL;
+
+ fail1:	usbd_free_xfer(sc->sc_out_xfer);
+	sc->sc_out_xfer = NULL;
+
+ fail0:	sc->sc_dying = 1;
+	USB_ATTACH_ERROR_RETURN;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+utoppy_activate(device_ptr_t self, enum devact act)
+{
+	struct utoppy_softc *sc = (struct utoppy_softc *)self;
+
+	switch (act) {
+	case DVACT_ACTIVATE:
+		return (EOPNOTSUPP);
+
+	case DVACT_DEACTIVATE:
+		sc->sc_dying = 1;
+		break;
+	}
+	return (0);
+}
+#endif
+
+USB_DETACH(utoppy)
+{
+	USB_DETACH_START(utoppy, sc);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	int maj, mn;
+#endif
+	int s;
+
+	sc->sc_dying = 1;
+	if (sc->sc_out_pipe != NULL)
+		usbd_abort_pipe(sc->sc_out_pipe);
+	if (sc->sc_in_pipe != NULL)
+		usbd_abort_pipe(sc->sc_in_pipe);
+
+	if (sc->sc_in_xfer != NULL)
+		usbd_free_xfer(sc->sc_in_xfer);
+	if (sc->sc_out_xfer != NULL)
+		usbd_free_xfer(sc->sc_out_xfer);
+
+	s = splusb();
+	if (--sc->sc_refcnt >= 0)
+		usb_detach_wait(USBDEV(sc->sc_dev));
+	splx(s);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	/* locate the major number */
+	maj = cdevsw_lookup_major(&utoppy_cdevsw);
+
+	/* Nuke the vnodes for any open instances (calls close). */
+	mn = self->dv_unit;
+	vdevgone(maj, mn, mn, VCHR);
+#elif defined(__FreeBSD__)
+	destroy_dev(sc->dev);
+#endif
+
+	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+			   USBDEV(sc->sc_dev));
+
+	return (0);
+}
+
+static const uint16_t utoppy_crc16_lookup[] = {
+	0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+	0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+	0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+	0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+	0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+	0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+	0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+	0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+	0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+	0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+	0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+	0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+	0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+	0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+	0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+	0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+	0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+	0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+	0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+	0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+	0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+	0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+	0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+	0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+	0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+	0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+	0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+	0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+	0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+	0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+	0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+	0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+};
+
+#define	UTOPPY_CRC16(ccrc,b)	\
+	(utoppy_crc16_lookup[((ccrc) ^ (b)) & 0xffu] ^ ((ccrc) >> 8))
+
+static const int utoppy_usbdstatus_lookup[] = {
+	0,		/* USBD_NORMAL_COMPLETION */
+	EINPROGRESS,	/* USBD_IN_PROGRESS */
+	EALREADY,	/* USBD_PENDING_REQUESTS */
+	EAGAIN,		/* USBD_NOT_STARTED */
+	EINVAL,		/* USBD_INVAL */
+	ENOMEM,		/* USBD_NOMEM */
+	ECONNRESET,	/* USBD_CANCELLED */
+	EFAULT,		/* USBD_BAD_ADDRESS */
+	EBUSY,		/* USBD_IN_USE */
+	EADDRNOTAVAIL,	/* USBD_NO_ADDR */
+	ENETDOWN,	/* USBD_SET_ADDR_FAILED */
+	EIO,		/* USBD_NO_POWER */
+	EMLINK,		/* USBD_TOO_DEEP */
+	EIO,		/* USBD_IOERROR */
+	ENXIO,		/* USBD_NOT_CONFIGURED */
+	ETIMEDOUT,	/* USBD_TIMEOUT */
+	EBADMSG,	/* USBD_SHORT_XFER */
+	EHOSTDOWN,	/* USBD_STALLED */
+	EINTR		/* USBD_INTERRUPTED */
+};
+
+static __inline int
+utoppy_usbd_status2errno(usbd_status err)
+{
+
+	if (err >= USBD_ERROR_MAX)
+		return (EFAULT);
+	return (utoppy_usbdstatus_lookup[err]);
+}
+
+#ifdef UTOPPY_DEBUG
+static const char *
+utoppy_state_string(enum utoppy_state state)
+{
+	const char *str;
+
+	switch (state) {
+	case UTOPPY_STATE_CLOSED:
+		str = "CLOSED";
+		break;
+	case UTOPPY_STATE_OPENING:
+		str = "OPENING";
+		break;
+	case UTOPPY_STATE_IDLE:
+		str = "IDLE";
+		break;
+	case UTOPPY_STATE_READDIR:
+		str = "READ DIRECTORY";
+		break;
+	case UTOPPY_STATE_READFILE:
+		str = "READ FILE";
+		break;
+	case UTOPPY_STATE_WRITEFILE:
+		str = "WRITE FILE";
+		break;
+	default:
+		str = "INVALID!";
+		break;
+	}
+
+	return (str);
+}
+
+static void
+utoppy_dump_packet(const void *b, size_t len)
+{
+	const uint8_t *buf = b, *l;
+	uint8_t c;
+	size_t i, j;
+
+	if (len == 0)
+		return;
+
+	len = min(len, 256);
+
+	printf("00: ");
+
+	for (i = 0, l = buf; i < len; i++) {
+		printf("%02x ", *buf++);
+
+		if ((i % 16) == 15) {
+			for (j = 0; j < 16; j++) {
+				c = *l++;
+				if (c < ' ' || c > 0x7e)
+					c = '.';
+				printf("%c", c);
+			}
+
+			printf("\n");
+			l = buf;
+
+			if ((i + 1) < len)
+				printf("%02x: ", (u_int)i + 1);
+		}
+	}
+
+	while ((i++ % 16) != 0)
+		printf("   ");
+
+	if (l < buf) {
+		while (l < buf) {
+			c = *l++;
+			if (c < ' ' || c > 0x7e)
+				c = '.';
+			printf("%c", c);
+		}
+
+		printf("\n");
+	}
+}
+#endif
+
+/*
+ * Very much like usbd_bulk_transfer(), except don't catch signals
+ */
+static void
+utoppy_bulk_transfer_cb(usbd_xfer_handle xfer,
+    usbd_private_handle priv,
+    usbd_status status)
+{
+
+	wakeup(xfer);
+}
+
+static usbd_status
+utoppy_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+    u_int16_t flags, u_int32_t timeout, void *buf, u_int32_t *size,
+    const char *lbl)
+{
+	usbd_status err;
+	int s, error;
+
+	usbd_setup_xfer(xfer, pipe, 0, buf, *size, flags, timeout,
+	    utoppy_bulk_transfer_cb);
+	s = splusb();
+	err = usbd_transfer(xfer);
+	if (err != USBD_IN_PROGRESS) {
+		splx(s);
+		return (err);
+	}
+	error = tsleep((caddr_t)xfer, PZERO, lbl, 0);
+	splx(s);
+	if (error) {
+		usbd_abort_pipe(pipe);
+		return (USBD_INTERRUPTED);
+	}
+	usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
+	return (err);
+}
+
+static int
+utoppy_send_packet(struct utoppy_softc *sc, uint16_t cmd, uint32_t timeout)
+{
+	struct utoppy_header *h;
+	usbd_status err;
+	uint32_t len;
+	uint16_t dlen, crc;
+	uint8_t *data, *e, t1, t2;
+
+	h = sc->sc_out_data;
+
+	DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: cmd 0x%04x, "
+	    "len %d\n", USBDEVNAME(sc->sc_dev), (u_int)cmd, h->h_len));
+
+	dlen = h->h_len;
+	len = dlen + UTOPPY_HEADER_SIZE;
+
+	if (len & 1)
+		len++;
+	if ((len % 64) == 0)
+		len += 2;
+
+	if (len >= UTOPPY_BSIZE) {
+		DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: "
+		    "packet too big (%d)\n", USBDEVNAME(sc->sc_dev), (int)len));
+		return (EINVAL);
+	}
+
+	h->h_len = htole16(dlen + UTOPPY_HEADER_SIZE);
+	h->h_cmd2 = 0;
+	h->h_cmd = htole16(cmd);
+
+	/* The command word is part of the CRC */
+	crc = UTOPPY_CRC16(0,   0);
+	crc = UTOPPY_CRC16(crc, 0);
+	crc = UTOPPY_CRC16(crc, cmd >> 8);
+	crc = UTOPPY_CRC16(crc, cmd);
+
+	/*
+	 * If there is data following the header, calculate the CRC and
+	 * byte-swap as we go.
+	 */
+	if (dlen) {
+		data = h->h_data;
+		e = data + (dlen & ~1);
+
+		do {
+			t1 = data[0];
+			t2 = data[1];
+			crc = UTOPPY_CRC16(crc, t1);
+			crc = UTOPPY_CRC16(crc, t2);
+			*data++ = t2;
+			*data++ = t1;
+		} while (data < e);
+
+		if (dlen & 1) {
+			t1 = data[0];
+			crc = UTOPPY_CRC16(crc, t1);
+			data[1] = t1;
+		}
+	}
+
+	h->h_crc = htole16(crc);
+	data = sc->sc_out_data;
+
+	DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: total len "
+	    "%d...\n", USBDEVNAME(sc->sc_dev), (int)len));
+	DDUMP_PACKET(data, len);
+
+	do {
+		uint32_t thislen;
+
+		thislen = min(len, UTOPPY_FRAG_SIZE);
+
+		memcpy(sc->sc_out_buf, data, thislen);
+
+		err = utoppy_bulk_transfer(sc->sc_out_xfer, sc->sc_out_pipe,
+		    USBD_NO_COPY, timeout, sc->sc_out_buf, &thislen,
+		    "utoppytx");
+
+		if (thislen != min(len, UTOPPY_FRAG_SIZE)) {
+			DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: "
+			    "utoppy_send_packet: sent %ld, err %d\n",
+			    USBDEVNAME(sc->sc_dev), (u_long)thislen, err));
+		}
+
+		if (err == 0) {
+			len -= thislen;
+			data += thislen;
+		}
+	} while (err == 0 && len);
+
+	DPRINTF(UTOPPY_DBG_SEND_PACKET, ("%s: utoppy_send_packet: "
+	    "usbd_bulk_transfer() returned %d.\n", USBDEVNAME(sc->sc_dev),err));
+
+	return (err ? utoppy_usbd_status2errno(err) : 0);
+}
+
+static int
+utoppy_recv_packet(struct utoppy_softc *sc, uint16_t *respp, uint32_t timeout)
+{
+	struct utoppy_header *h;
+	usbd_status err;
+	uint32_t len, thislen, requested, bytesleft;
+	uint16_t crc;
+	uint8_t *data, *e, t1, t2;
+
+	data = sc->sc_in_data;
+	len = 0;
+	bytesleft = UTOPPY_BSIZE;
+
+	DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: ...\n",
+	    USBDEVNAME(sc->sc_dev)));
+
+	do {
+		requested = thislen = min(bytesleft, UTOPPY_FRAG_SIZE);
+
+		err = utoppy_bulk_transfer(sc->sc_in_xfer, sc->sc_in_pipe,
+		    USBD_NO_COPY | USBD_SHORT_XFER_OK, timeout, sc->sc_in_buf,
+		    &thislen, "utoppyrx");
+
+		DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: "
+		    "usbd_bulk_transfer() returned %d, thislen %d, data %p\n",
+		    USBDEVNAME(sc->sc_dev), err, (u_int)thislen, data));
+
+		if (err == 0) {
+			memcpy(data, sc->sc_in_buf, thislen);
+			DDUMP_PACKET(data, thislen);
+			len += thislen;
+			bytesleft -= thislen;
+			data += thislen;
+		}
+	} while (err == 0 && bytesleft && thislen == requested);
+
+	if (err)
+		return (utoppy_usbd_status2errno(err));
+
+	h = sc->sc_in_data;
+
+	DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: received %d "
+	    "bytes in total to %p\n", USBDEVNAME(sc->sc_dev), (u_int)len, h));
+	DDUMP_PACKET(h, len);
+
+	if (len < UTOPPY_HEADER_SIZE || len < (uint32_t)le16toh(h->h_len)) {
+		DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: bad "
+		    " length (len %d, h_len %d)\n", USBDEVNAME(sc->sc_dev),
+		    (int)len, le16toh(h->h_len)));
+		return (EIO);
+	}
+
+	len = h->h_len = le16toh(h->h_len);
+	h->h_crc = le16toh(h->h_crc);
+	*respp = h->h_cmd = le16toh(h->h_cmd);
+	h->h_cmd2 = le16toh(h->h_cmd2);
+
+	/*
+	 * To maximise data throughput when transferring files, acknowledge
+	 * data blocks as soon as we receive them. If we detect an error
+	 * later on, we can always cancel.
+	 */
+	if (*respp == UTOPPY_RESP_FILE_DATA) {
+		DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: "
+		    "ACKing file data\n", USBDEVNAME(sc->sc_dev)));
+
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_send_packet(sc, UTOPPY_CMD_ACK,
+		    UTOPPY_SHORT_TIMEOUT);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: "
+			    "utoppy_recv_packet: failed to ACK file data: %d\n",
+			    USBDEVNAME(sc->sc_dev), err));
+			return (err);
+		}
+	}
+
+	/* The command word is part of the CRC */
+	crc = UTOPPY_CRC16(0,   h->h_cmd2 >> 8);
+	crc = UTOPPY_CRC16(crc, h->h_cmd2);
+	crc = UTOPPY_CRC16(crc, h->h_cmd >> 8);
+	crc = UTOPPY_CRC16(crc, h->h_cmd);
+
+	/*
+	 * Extract any payload, byte-swapping and calculating the CRC16
+	 * as we go.
+	 */
+	if (len > UTOPPY_HEADER_SIZE) {
+		data = h->h_data;
+		e = data + ((len & ~1) - UTOPPY_HEADER_SIZE);
+
+		while (data < e) {
+			t1 = data[0];
+			t2 = data[1];
+			crc = UTOPPY_CRC16(crc, t2);
+			crc = UTOPPY_CRC16(crc, t1);
+			*data++ = t2;
+			*data++ = t1;
+		}
+
+		if (len & 1) {
+			t1 = data[1];
+			crc = UTOPPY_CRC16(crc, t1);
+			*data = t1;
+		}
+	}
+
+	sc->sc_in_len = (size_t) len - UTOPPY_HEADER_SIZE;
+	sc->sc_in_offset = 0;
+
+	DPRINTF(UTOPPY_DBG_RECV_PACKET, ("%s: utoppy_recv_packet: len %d, "
+	    "crc 0x%04x, hdrcrc 0x%04x\n", USBDEVNAME(sc->sc_dev),
+	    (int)len, crc, h->h_crc));
+	DDUMP_PACKET(h, len);
+
+	return ((crc == h->h_crc) ? 0 : EBADMSG);
+}
+
+static __inline void *
+utoppy_current_ptr(void *b)
+{
+	struct utoppy_header *h = b;
+
+	return (&h->h_data[h->h_len]);
+}
+
+static __inline void
+utoppy_advance_ptr(void *b, size_t len)
+{
+	struct utoppy_header *h = b;
+
+	h->h_len += len;
+}
+
+static __inline void
+utoppy_add_8(struct utoppy_softc *sc, uint8_t v)
+{
+	struct utoppy_header *h = sc->sc_out_data;
+	uint8_t *p;
+
+	p = utoppy_current_ptr(h);
+	*p = v;
+	utoppy_advance_ptr(h, sizeof(v));
+}
+
+static __inline void
+utoppy_add_16(struct utoppy_softc *sc, uint16_t v)
+{
+	struct utoppy_header *h = sc->sc_out_data;
+	uint8_t *p;
+
+	p = utoppy_current_ptr(h);
+	*p++ = (uint8_t)(v >> 8);
+	*p = (uint8_t)v;
+	utoppy_advance_ptr(h, sizeof(v));
+}
+
+static __inline void
+utoppy_add_32(struct utoppy_softc *sc, uint32_t v)
+{
+	struct utoppy_header *h = sc->sc_out_data;
+	uint8_t *p;
+
+	p = utoppy_current_ptr(h);
+	*p++ = (uint8_t)(v >> 24);
+	*p++ = (uint8_t)(v >> 16);
+	*p++ = (uint8_t)(v >> 8);
+	*p = (uint8_t)v;
+	utoppy_advance_ptr(h, sizeof(v));
+}
+
+static __inline void
+utoppy_add_64(struct utoppy_softc *sc, uint64_t v)
+{
+	struct utoppy_header *h = sc->sc_out_data;
+	uint8_t *p;
+
+	p = utoppy_current_ptr(h);
+	*p++ = (uint8_t)(v >> 56);
+	*p++ = (uint8_t)(v >> 48);
+	*p++ = (uint8_t)(v >> 40);
+	*p++ = (uint8_t)(v >> 32);
+	*p++ = (uint8_t)(v >> 24);
+	*p++ = (uint8_t)(v >> 16);
+	*p++ = (uint8_t)(v >> 8);
+	*p = (uint8_t)v;
+	utoppy_advance_ptr(h, sizeof(v));
+}
+
+static __inline void
+utoppy_add_string(struct utoppy_softc *sc, const char *str, size_t len)
+{
+	struct utoppy_header *h = sc->sc_out_data;
+	char *p;
+
+	p = utoppy_current_ptr(h);
+	memset(p, 0, len);
+	strncpy(p, str, len);
+	utoppy_advance_ptr(h, len);
+}
+
+static int
+utoppy_add_path(struct utoppy_softc *sc, const char *path, int putlen)
+{
+	struct utoppy_header *h = sc->sc_out_data;
+	uint8_t *p, *str, *s;
+	size_t len;
+	int err;
+
+	p = utoppy_current_ptr(h);
+
+	str = putlen ? (p + sizeof(uint16_t)) : p;
+
+	err = copyinstr(path, str, UTOPPY_MAX_FILENAME_LEN, &len);
+
+	DPRINTF(UTOPPY_DBG_ADDPATH, ("utoppy_add_path: err %d, len %d\n",
+	    err, (int)len));
+
+	if (err)
+		return (err);
+
+	if (len < 2)
+		return (EINVAL);
+
+	/*
+	 * copyinstr(9) has already copied the terminating NUL character,
+	 * but we append another one in case we have to pad the length
+	 * later on.
+	 */
+	str[len] = '\0';
+
+	/*
+	 * The Toppy uses backslash as the directory separator, so convert
+	 * all forward slashes.
+	 */
+	for (s = &str[len - 2]; s >= str; s--)
+		if (*s == '/')
+			*s = '\\';
+
+	if ((len + h->h_len) & 1)
+		len++;
+
+	if (putlen)
+		utoppy_add_16(sc, len);
+
+	utoppy_advance_ptr(h, len);
+
+	DPRINTF(UTOPPY_DBG_ADDPATH, ("utoppy_add_path: final len %d\n",
+	    (u_int)len));
+
+	return (0);
+}
+
+static __inline int
+utoppy_get_8(struct utoppy_softc *sc, uint8_t *vp)
+{
+	uint8_t *p;
+
+	if (sc->sc_in_len < sizeof(*vp))
+		return (1);
+
+	p = UTOPPY_IN_DATA(sc);
+	*vp = *p;
+	sc->sc_in_offset += sizeof(*vp);
+	sc->sc_in_len -= sizeof(*vp);
+	return (0);
+}
+
+static __inline int
+utoppy_get_16(struct utoppy_softc *sc, uint16_t *vp)
+{
+	uint16_t v;
+	uint8_t *p;
+
+	if (sc->sc_in_len < sizeof(v))
+		return (1);
+
+	p = UTOPPY_IN_DATA(sc);
+	v = *p++;
+	v = (v << 8) | *p;
+	*vp = v;
+	sc->sc_in_offset += sizeof(v);
+	sc->sc_in_len -= sizeof(v);
+	return (0);
+}
+
+static __inline int
+utoppy_get_32(struct utoppy_softc *sc, uint32_t *vp)
+{
+	uint32_t v;
+	uint8_t *p;
+
+	if (sc->sc_in_len < sizeof(v))
+		return (1);
+
+	p = UTOPPY_IN_DATA(sc);
+	v = *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p;
+	*vp = v;
+	sc->sc_in_offset += sizeof(v);
+	sc->sc_in_len -= sizeof(v);
+	return (0);
+}
+
+static __inline int
+utoppy_get_64(struct utoppy_softc *sc, uint64_t *vp)
+{
+	uint64_t v;
+	uint8_t *p;
+
+	if (sc->sc_in_len < sizeof(v))
+		return (1);
+
+	p = UTOPPY_IN_DATA(sc);
+	v = *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p++;
+	v = (v << 8) | *p;
+	*vp = v;
+	sc->sc_in_offset += sizeof(v);
+	sc->sc_in_len -= sizeof(v);
+	return (0);
+}
+
+static __inline int
+utoppy_get_string(struct utoppy_softc *sc, char *str, size_t len)
+{
+	char *p;
+
+	if (sc->sc_in_len < len)
+		return (1);
+
+	memset(str, 0, len);
+	p = UTOPPY_IN_DATA(sc);
+	strncpy(str, p, len);
+	sc->sc_in_offset += len;
+	sc->sc_in_len -= len;
+	return (0);
+}
+
+static int
+utoppy_command(struct utoppy_softc *sc, uint16_t cmd, int timeout,
+    uint16_t *presp)
+{
+	int err;
+
+	err = utoppy_send_packet(sc, cmd, timeout);
+	if (err)
+		return (err);
+
+	err = utoppy_recv_packet(sc, presp, timeout);
+	if (err == EBADMSG) {
+		UTOPPY_OUT_INIT(sc);
+		utoppy_send_packet(sc, UTOPPY_RESP_ERROR, timeout);
+	}
+
+	return (err);
+}
+
+static int
+utoppy_timestamp_decode(struct utoppy_softc *sc, time_t *tp)
+{
+	uint16_t mjd;
+	uint8_t hour, minute, sec;
+	uint32_t rv;
+
+	if (utoppy_get_16(sc, &mjd) || utoppy_get_8(sc, &hour) ||
+	    utoppy_get_8(sc, &minute) || utoppy_get_8(sc, &sec))
+		return (1);
+
+	if (mjd == 0xffffu && hour == 0xffu && minute == 0xffu && sec == 0xffu){
+		*tp = 0;
+		return (0);
+	}
+
+	rv = (mjd < UTOPPY_MJD_1970) ? UTOPPY_MJD_1970 : (uint32_t) mjd;
+
+	/* Calculate seconds since 1970 */
+	rv = (rv - UTOPPY_MJD_1970) * 60 * 60 * 24;
+
+	/* Add in the hours, minutes, and seconds */
+	rv += (uint32_t)hour * 60 * 60;
+	rv += (uint32_t)minute * 60;
+	rv += sec;
+	*tp = (time_t)rv;
+
+	return (0);
+}
+
+static void
+utoppy_timestamp_encode(struct utoppy_softc *sc, time_t t)
+{
+	u_int mjd, hour, minute;
+
+	mjd = t / (60 * 60 * 24);
+	t -= mjd * 60 * 60 * 24;
+
+	hour = t / (60 * 60);
+	t -= hour * 60 * 60;
+
+	minute = t / 60;
+	t -= minute * 60;
+
+	utoppy_add_16(sc, mjd + UTOPPY_MJD_1970);
+	utoppy_add_8(sc, hour);
+	utoppy_add_8(sc, minute);
+	utoppy_add_8(sc, t);
+}
+
+static int
+utoppy_turbo_mode(struct utoppy_softc *sc, int state)
+{
+	uint16_t r;
+	int err;
+
+	UTOPPY_OUT_INIT(sc);
+	utoppy_add_32(sc, state);
+
+	err = utoppy_command(sc, UTOPPY_CMD_TURBO, UTOPPY_SHORT_TIMEOUT, &r);
+	if (err)
+		return (err);
+
+	return ((r == UTOPPY_RESP_SUCCESS) ? 0 : EIO);
+}
+
+static int
+utoppy_check_ready(struct utoppy_softc *sc)
+{
+	uint16_t r;
+	int err;
+
+	UTOPPY_OUT_INIT(sc);
+
+	err = utoppy_command(sc, UTOPPY_CMD_READY, UTOPPY_LONG_TIMEOUT, &r);
+	if (err)
+		return (err);
+
+	return ((r == UTOPPY_RESP_SUCCESS) ? 0 : EIO);
+}
+
+static int
+utoppy_cancel(struct utoppy_softc *sc)
+{
+	uint16_t r;
+	int err, i;
+
+	/*
+	 * Issue the cancel command serveral times. the Toppy doesn't
+	 * always respond to the first.
+	 */
+	for (i = 0; i < 3; i++) {
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_command(sc, UTOPPY_CMD_CANCEL,
+		    UTOPPY_SHORT_TIMEOUT, &r);
+		if (err == 0 && r == UTOPPY_RESP_SUCCESS)
+			break;
+		err = ETIMEDOUT;
+	}
+
+	if (err)
+		return (err);
+
+	/*
+	 * Make sure turbo mode is off, otherwise the Toppy will not
+	 * respond to remote control input.
+	 */
+	(void) utoppy_turbo_mode(sc, 0);
+
+	sc->sc_state = UTOPPY_STATE_IDLE;
+	return (0);
+}
+
+static int
+utoppy_stats(struct utoppy_softc *sc, struct utoppy_stats *us)
+{
+	uint32_t hsize, hfree;
+	uint16_t r;
+	int err;
+
+	UTOPPY_OUT_INIT(sc);
+	err = utoppy_command(sc, UTOPPY_CMD_STATS, UTOPPY_LONG_TIMEOUT, &r);
+	if (err)
+		return (err);
+
+	if (r != UTOPPY_RESP_STATS_DATA)
+		return (EIO);
+
+	if (utoppy_get_32(sc, &hsize) || utoppy_get_32(sc, &hfree))
+		return (EIO);
+
+	us->us_hdd_size = hsize;
+	us->us_hdd_size *= 1024;
+	us->us_hdd_free = hfree;
+	us->us_hdd_free *= 1024;
+
+	return (0);
+}
+
+static int
+utoppy_readdir_next(struct utoppy_softc *sc)
+{
+	uint16_t resp;
+	int err;
+
+	DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: running...\n",
+	    USBDEVNAME(sc->sc_dev)));
+
+	/*
+	 * Fetch the next READDIR response
+	 */
+	err = utoppy_recv_packet(sc, &resp, UTOPPY_LONG_TIMEOUT);
+	if (err) {
+		DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: "
+		    "utoppy_recv_packet() returned %d\n",
+		    USBDEVNAME(sc->sc_dev), err));
+		if (err == EBADMSG) {
+			UTOPPY_OUT_INIT(sc);
+			utoppy_send_packet(sc, UTOPPY_RESP_ERROR,
+			    UTOPPY_LONG_TIMEOUT);
+		}
+		utoppy_cancel(sc);
+		return (err);
+	}
+
+	DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: "
+	    "utoppy_recv_packet() returned %d, len %ld\n",
+	    USBDEVNAME(sc->sc_dev), err, (u_long)sc->sc_in_len));
+
+	switch (resp) {
+	case UTOPPY_RESP_READDIR_DATA:
+		DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: "
+		    "UTOPPY_RESP_READDIR_DATA\n", USBDEVNAME(sc->sc_dev)));
+
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_send_packet(sc, UTOPPY_CMD_ACK,
+		    UTOPPY_LONG_TIMEOUT);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: "
+			    "utoppy_send_packet(ACK) returned %d\n",
+			    USBDEVNAME(sc->sc_dev), err));
+			utoppy_cancel(sc);
+			return (err);
+		}
+		sc->sc_state = UTOPPY_STATE_READDIR;
+		sc->sc_in_offset = 0;
+		break;
+
+	case UTOPPY_RESP_READDIR_END:
+		DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: "
+		    "UTOPPY_RESP_READDIR_END\n", USBDEVNAME(sc->sc_dev)));
+
+		UTOPPY_OUT_INIT(sc);
+		utoppy_send_packet(sc, UTOPPY_CMD_ACK, UTOPPY_SHORT_TIMEOUT);
+		sc->sc_state = UTOPPY_STATE_IDLE;
+		sc->sc_in_len = 0;
+		break;
+
+	default:
+		DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_next: "
+		    "bad response: 0x%x\n", USBDEVNAME(sc->sc_dev), resp));
+		sc->sc_state = UTOPPY_STATE_IDLE;
+		sc->sc_in_len = 0;
+		return (EIO);
+	}
+
+	return (0);
+}
+
+static size_t
+utoppy_readdir_decode(struct utoppy_softc *sc, struct utoppy_dirent *ud)
+{
+	uint8_t ftype;
+
+	DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_decode: bytes left"
+	    " %d\n", USBDEVNAME(sc->sc_dev), (int)sc->sc_in_len));
+
+	if (utoppy_timestamp_decode(sc, &ud->ud_mtime) ||
+	    utoppy_get_8(sc, &ftype) || utoppy_get_64(sc, &ud->ud_size) ||
+	    utoppy_get_string(sc, ud->ud_path, UTOPPY_MAX_FILENAME_LEN + 1) ||
+	    utoppy_get_32(sc, &ud->ud_attributes)) {
+		DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_decode: no "
+		    "more to decode\n", USBDEVNAME(sc->sc_dev)));
+		return (0);
+	}
+
+	switch (ftype) {
+	case UTOPPY_FTYPE_DIR:
+		ud->ud_type = UTOPPY_DIRENT_DIRECTORY;
+		break;
+	case UTOPPY_FTYPE_FILE:
+		ud->ud_type = UTOPPY_DIRENT_FILE;
+		break;
+	default:
+		ud->ud_type = UTOPPY_DIRENT_UNKNOWN;
+		break;
+	}
+
+	DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppy_readdir_decode: %s '%s', "
+	    "size %lld, time 0x%08lx, attr 0x%08x\n", USBDEVNAME(sc->sc_dev),
+	    (ftype == UTOPPY_FTYPE_DIR) ? "DIR" :
+	    ((ftype == UTOPPY_FTYPE_FILE) ? "FILE" : "UNKNOWN"), ud->ud_path,
+	    ud->ud_size, (u_long)ud->ud_mtime, ud->ud_attributes));
+
+	return (1);
+}
+
+static int
+utoppy_readfile_next(struct utoppy_softc *sc)
+{
+	uint64_t off;
+	uint16_t resp;
+	int err;
+
+	err = utoppy_recv_packet(sc, &resp, UTOPPY_LONG_TIMEOUT);
+	if (err) {
+		DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: "
+		    "utoppy_recv_packet() returned %d\n",
+		    USBDEVNAME(sc->sc_dev), err));
+		utoppy_cancel(sc);
+		return (err);
+	}
+
+	switch (resp) {
+	case UTOPPY_RESP_FILE_HEADER:
+		/* ACK it */
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_send_packet(sc, UTOPPY_CMD_ACK,
+		    UTOPPY_LONG_TIMEOUT);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: "
+			    "utoppy_send_packet(UTOPPY_CMD_ACK) returned %d\n",
+			    USBDEVNAME(sc->sc_dev), err));
+			utoppy_cancel(sc);
+			return (err);
+		}
+
+		sc->sc_in_len = 0;
+		DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: "
+		    "FILE_HEADER done\n", USBDEVNAME(sc->sc_dev)));
+		break;
+
+	case UTOPPY_RESP_FILE_DATA:
+		/* Already ACK'd */
+		if (utoppy_get_64(sc, &off)) {
+			DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: "
+			    "UTOPPY_RESP_FILE_DATA did not provide offset\n",
+			    USBDEVNAME(sc->sc_dev)));
+			utoppy_cancel(sc);
+			return (EBADMSG);
+		}
+
+		DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: "
+		    "UTOPPY_RESP_FILE_DATA: offset %lld, bytes left %ld\n",
+		    USBDEVNAME(sc->sc_dev), off, (u_long)sc->sc_in_len));
+		break;
+
+	case UTOPPY_RESP_FILE_END:
+		DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: "
+		    "UTOPPY_RESP_FILE_END: sending ACK\n",
+		    USBDEVNAME(sc->sc_dev)));
+		UTOPPY_OUT_INIT(sc);
+		utoppy_send_packet(sc, UTOPPY_CMD_ACK, UTOPPY_SHORT_TIMEOUT);
+		/*FALLTHROUGH*/
+
+	case UTOPPY_RESP_SUCCESS:
+		sc->sc_state = UTOPPY_STATE_IDLE;
+		(void) utoppy_turbo_mode(sc, 0);
+		DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: all "
+		    "done\n", USBDEVNAME(sc->sc_dev)));
+		break;
+
+	case UTOPPY_RESP_ERROR:
+	default:
+		DPRINTF(UTOPPY_DBG_READ, ("%s: utoppy_readfile_next: bad "
+		    "response code 0x%0x\n", USBDEVNAME(sc->sc_dev), resp));
+		utoppy_cancel(sc);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+utoppyopen(dev_t dev, int flag, int mode,
+    struct lwp *l)
+#elif defined(__FreeBSD__)
+int
+utoppyopen(struct cdev *dev, int flag, int mode, usb_proc_ptr l)
+#endif
+{
+	struct utoppy_softc *sc;
+	int error = 0;
+
+	USB_GET_SC_OPEN(utoppy, UTOPPYUNIT(dev), sc);
+
+	if (sc == NULL || sc->sc_iface == NULL || sc->sc_dying)
+		return (ENXIO);
+
+	if (sc->sc_state != UTOPPY_STATE_CLOSED) {
+		DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: already open\n",
+		    USBDEVNAME(sc->sc_dev)));
+		return (EBUSY);
+	}
+
+	DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: opening...\n",
+	    USBDEVNAME(sc->sc_dev)));
+
+	sc->sc_refcnt++;
+	sc->sc_state = UTOPPY_STATE_OPENING;
+	sc->sc_turbo_mode = 0;
+	sc->sc_out_pipe = NULL;
+	sc->sc_in_pipe = NULL;
+
+	if (usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe)) {
+		DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: usbd_open_pipe(OUT) "
+		    "failed\n", USBDEVNAME(sc->sc_dev)));
+		error = EIO;
+		goto done;
+	}
+
+	if (usbd_open_pipe(sc->sc_iface, sc->sc_in, 0, &sc->sc_in_pipe)) {
+		DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: usbd_open_pipe(IN) "
+		    "failed\n", USBDEVNAME(sc->sc_dev)));
+		error = EIO;
+		usbd_close_pipe(sc->sc_out_pipe);
+		sc->sc_out_pipe = NULL;
+		goto done;
+	}
+
+	sc->sc_out_data = malloc(UTOPPY_BSIZE + 1, M_DEVBUF, M_WAITOK);
+	if (sc->sc_out_data == NULL) {
+		error = ENOMEM;
+		goto error;
+	}
+
+	sc->sc_in_data = malloc(UTOPPY_BSIZE + 1, M_DEVBUF, M_WAITOK);
+	if (sc->sc_in_data == NULL) {
+		free(sc->sc_out_data, M_DEVBUF);
+		sc->sc_out_data = NULL;
+		error = ENOMEM;
+		goto error;
+	}
+
+	if ((error = utoppy_cancel(sc)) != 0)
+		goto error;
+
+	if ((error = utoppy_check_ready(sc)) != 0) {
+		DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: utoppy_check_ready()"
+		    " returned %d\n", USBDEVNAME(sc->sc_dev), error));
+ error:
+		usbd_abort_pipe(sc->sc_out_pipe);
+		usbd_close_pipe(sc->sc_out_pipe);
+		sc->sc_out_pipe = NULL;
+		usbd_abort_pipe(sc->sc_in_pipe);
+		usbd_close_pipe(sc->sc_in_pipe);
+		sc->sc_in_pipe = NULL;
+	}
+
+ done:
+	sc->sc_state = error ? UTOPPY_STATE_CLOSED : UTOPPY_STATE_IDLE;
+
+	DPRINTF(UTOPPY_DBG_OPEN, ("%s: utoppyopen: done. error %d, new state "
+	    "'%s'\n", USBDEVNAME(sc->sc_dev), error,
+	    utoppy_state_string(sc->sc_state)));
+
+	if (--sc->sc_refcnt < 0)
+		usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+	return (error);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+utoppyclose(dev_t dev, int flag, int mode,
+    struct lwp *l)
+#elif defined(__FreeBSD__)
+int
+utoppyclose(struct cdev *dev, int flag, int mode, usb_proc_ptr l)
+#endif
+{
+	struct utoppy_softc *sc;
+	usbd_status err;
+
+	USB_GET_SC(utoppy, UTOPPYUNIT(dev), sc);
+
+	DPRINTF(UTOPPY_DBG_CLOSE, ("%s: utoppyclose: closing...\n",
+	    USBDEVNAME(sc->sc_dev)));
+
+	if (sc->sc_state < UTOPPY_STATE_IDLE) {
+		/* We are being forced to close before the open completed. */
+		DPRINTF(UTOPPY_DBG_CLOSE, ("%s: utoppyclose: not properly open:"
+		    " %s\n", USBDEVNAME(sc->sc_dev),
+		    utoppy_state_string(sc->sc_state)));
+		return (0);
+	}
+
+	if (sc->sc_out_data)
+		(void) utoppy_cancel(sc);
+
+	if (sc->sc_out_pipe != NULL) {
+		if ((err = usbd_abort_pipe(sc->sc_out_pipe)) != 0)
+			printf("usbd_abort_pipe(OUT) returned %d\n", err);
+		if ((err = usbd_close_pipe(sc->sc_out_pipe)) != 0)
+			printf("usbd_close_pipe(OUT) returned %d\n", err);
+		sc->sc_out_pipe = NULL;
+	}
+
+	if (sc->sc_in_pipe != NULL) {
+		if ((err = usbd_abort_pipe(sc->sc_in_pipe)) != 0)
+			printf("usbd_abort_pipe(IN) returned %d\n", err);
+		if ((err = usbd_close_pipe(sc->sc_in_pipe)) != 0)
+			printf("usbd_close_pipe(IN) returned %d\n", err);
+		sc->sc_in_pipe = NULL;
+	}
+
+	if (sc->sc_out_data) {
+		free(sc->sc_out_data, M_DEVBUF);
+		sc->sc_out_data = NULL;
+	}
+
+	if (sc->sc_in_data) {
+		free(sc->sc_in_data, M_DEVBUF);
+		sc->sc_in_data = NULL;
+	}
+
+	sc->sc_state = UTOPPY_STATE_CLOSED;
+
+	DPRINTF(UTOPPY_DBG_CLOSE, ("%s: utoppyclose: done.\n",
+	    USBDEVNAME(sc->sc_dev)));
+
+	return (0);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+utoppyread(dev_t dev, struct uio *uio, int flags)
+#elif defined(__FreeBSD__)
+int
+utoppyread(struct cdev *dev, struct uio *uio, int flags)
+#endif
+{
+	struct utoppy_softc *sc;
+	struct utoppy_dirent ud;
+	size_t len;
+	int err;
+
+	USB_GET_SC(utoppy, UTOPPYUNIT(dev), sc);
+
+	if (sc->sc_dying)
+		return (EIO);
+
+	sc->sc_refcnt++;
+
+	DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: reading: state '%s'\n",
+	    USBDEVNAME(sc->sc_dev), utoppy_state_string(sc->sc_state)));
+
+	switch (sc->sc_state) {
+	case UTOPPY_STATE_READDIR:
+		err = 0;
+		while (err == 0 && uio->uio_resid >= sizeof(ud) &&
+		    sc->sc_state != UTOPPY_STATE_IDLE) {
+			if (utoppy_readdir_decode(sc, &ud) == 0)
+				err = utoppy_readdir_next(sc);
+			else
+			if ((err = uiomove(&ud, sizeof(ud), uio)) != 0)
+				utoppy_cancel(sc); 
+		}
+		break;
+
+	case UTOPPY_STATE_READFILE:
+		err = 0;
+		while (err == 0 && uio->uio_resid > 0 &&
+		    sc->sc_state != UTOPPY_STATE_IDLE) {
+			DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: READFILE: "
+			    "resid %ld, bytes_left %ld\n",
+			    USBDEVNAME(sc->sc_dev), (u_long)uio->uio_resid,
+			    (u_long)sc->sc_in_len));
+
+			if (sc->sc_in_len == 0 &&
+			    (err = utoppy_readfile_next(sc)) != 0) {
+				DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: "
+				    "READFILE: utoppy_readfile_next returned "
+				    "%d\n", USBDEVNAME(sc->sc_dev), err));
+				break;
+			}
+
+			len = min(uio->uio_resid, sc->sc_in_len);
+			if (len) {
+				err = uiomove(UTOPPY_IN_DATA(sc), len, uio);
+				if (err == 0) {
+					sc->sc_in_offset += len;
+					sc->sc_in_len -= len;
+				}
+			}
+		}
+		break;
+
+	case UTOPPY_STATE_IDLE:
+		err = 0;
+		break;
+
+	case UTOPPY_STATE_WRITEFILE:
+		err = EBUSY;
+		break;
+
+	default:
+		err = EIO;
+		break;
+	}
+
+	DPRINTF(UTOPPY_DBG_READ, ("%s: utoppyread: done. err %d, state '%s'\n",
+	    USBDEVNAME(sc->sc_dev), err, utoppy_state_string(sc->sc_state)));
+
+	if (--sc->sc_refcnt < 0)
+		usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+	return (err);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+utoppywrite(dev_t dev, struct uio *uio, int flags)
+#elif defined(__FreeBSD__)
+int
+utoppywrite(struct cdev *dev, struct uio *uio, int flags)
+#endif
+{
+	struct utoppy_softc *sc;
+	uint16_t resp;
+	size_t len;
+	int err;
+
+	USB_GET_SC(utoppy, UTOPPYUNIT(dev), sc);
+
+	if (sc->sc_dying)
+		return (EIO);
+
+	switch(sc->sc_state) {
+	case UTOPPY_STATE_WRITEFILE:
+		break;
+
+	case UTOPPY_STATE_IDLE:
+		return (0);
+
+	default:
+		return (EIO);
+	}
+
+	sc->sc_refcnt++;
+	err = 0;
+
+	DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: PRE-WRITEFILE: resid %ld, "
+	    "wr_size %lld, wr_offset %lld\n", USBDEVNAME(sc->sc_dev),
+	    (u_long)uio->uio_resid, sc->sc_wr_size, sc->sc_wr_offset));
+
+	while (sc->sc_state == UTOPPY_STATE_WRITEFILE &&
+	    (len = min(uio->uio_resid, sc->sc_wr_size)) != 0) {
+
+		len = min(len, UTOPPY_BSIZE - (UTOPPY_HEADER_SIZE +
+		    sizeof(uint64_t) + 3));
+
+		DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: uiomove(%ld)\n",
+		    USBDEVNAME(sc->sc_dev), (u_long)len));
+
+		UTOPPY_OUT_INIT(sc);
+		utoppy_add_64(sc, sc->sc_wr_offset);
+
+		err = uiomove(utoppy_current_ptr(sc->sc_out_data), len, uio);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: uiomove() "
+			    "returned %d\n", USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+
+		utoppy_advance_ptr(sc->sc_out_data, len);
+
+		err = utoppy_command(sc, UTOPPY_RESP_FILE_DATA,
+		    UTOPPY_LONG_TIMEOUT, &resp);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: "
+			    "utoppy_command(UTOPPY_RESP_FILE_DATA) "
+			    "returned %d\n", USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+		if (resp != UTOPPY_RESP_SUCCESS) {
+			DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: "
+			    "utoppy_command(UTOPPY_RESP_FILE_DATA) returned "
+			    "bad response 0x%x\n", USBDEVNAME(sc->sc_dev),
+			    resp));
+			utoppy_cancel(sc);
+			err = EIO;
+			break;
+		}
+
+		sc->sc_wr_offset += len;
+		sc->sc_wr_size -= len;
+	}
+
+	DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: POST-WRITEFILE: resid %ld,"
+	    " wr_size %lld, wr_offset %lld, err %d\n", USBDEVNAME(sc->sc_dev),
+	    (u_long)uio->uio_resid, sc->sc_wr_size, sc->sc_wr_offset, err));
+
+	if (err == 0 && sc->sc_wr_size == 0) {
+		DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: sending "
+		    "FILE_END...\n", USBDEVNAME(sc->sc_dev)));
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_command(sc, UTOPPY_RESP_FILE_END,
+		    UTOPPY_LONG_TIMEOUT, &resp);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: "
+			    "utoppy_command(UTOPPY_RESP_FILE_END) returned "
+			    "%d\n", USBDEVNAME(sc->sc_dev), err));
+
+			utoppy_cancel(sc);
+		}
+
+		sc->sc_state = UTOPPY_STATE_IDLE;
+		DPRINTF(UTOPPY_DBG_WRITE, ("%s: utoppywrite: state %s\n",
+		    USBDEVNAME(sc->sc_dev), utoppy_state_string(sc->sc_state)));
+	}
+
+	if (--sc->sc_refcnt < 0)
+		usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+	return (err);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+utoppyioctl(dev_t dev, u_long cmd, caddr_t data, int flag,
+    struct lwp *l)
+#elif defined(__FreeBSD__)
+int
+utoppyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
+    usb_proc_ptr l)
+#endif
+{
+	struct utoppy_softc *sc;
+	struct utoppy_rename *ur;
+	struct utoppy_readfile *urf;
+	struct utoppy_writefile *uw;
+	char uwf[UTOPPY_MAX_FILENAME_LEN + 1], *uwfp;
+	uint16_t resp;
+	int err;
+
+	USB_GET_SC(utoppy, UTOPPYUNIT(dev), sc);
+
+	if (sc->sc_dying)
+		return (EIO);
+
+	DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: cmd 0x%08lx, state '%s'\n",
+	    USBDEVNAME(sc->sc_dev), cmd, utoppy_state_string(sc->sc_state)));
+
+	if (sc->sc_state != UTOPPY_STATE_IDLE && cmd != UTOPPYIOCANCEL) {
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: still busy.\n",
+		    USBDEVNAME(sc->sc_dev)));
+		return (EBUSY);
+	}
+
+	sc->sc_refcnt++;
+
+	switch (cmd) {
+	case UTOPPYIOTURBO:
+		err = 0;
+		sc->sc_turbo_mode = *((int *)data) ? 1 : 0;
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOTURBO: "
+		    "%s\n", USBDEVNAME(sc->sc_dev), sc->sc_turbo_mode ? "On" :
+		    "Off"));
+		break;
+
+	case UTOPPYIOCANCEL:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOCANCEL\n",
+		    USBDEVNAME(sc->sc_dev)));
+		err = utoppy_cancel(sc);
+		break;
+
+	case UTOPPYIOREBOOT:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOREBOOT\n",
+		    USBDEVNAME(sc->sc_dev)));
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_command(sc, UTOPPY_CMD_RESET, UTOPPY_LONG_TIMEOUT,
+		    &resp);
+		if (err)
+			break;
+
+		if (resp != UTOPPY_RESP_SUCCESS)
+			err = EIO;
+		break;
+
+	case UTOPPYIOSTATS:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOSTATS\n",
+		    USBDEVNAME(sc->sc_dev)));
+		err = utoppy_stats(sc, (struct utoppy_stats *)data);
+		break;
+
+	case UTOPPYIORENAME:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIORENAME\n",
+		    USBDEVNAME(sc->sc_dev)));
+		ur = (struct utoppy_rename *)data;
+		UTOPPY_OUT_INIT(sc);
+
+		if ((err = utoppy_add_path(sc, ur->ur_old_path, 1)) != 0)
+			break;
+		if ((err = utoppy_add_path(sc, ur->ur_new_path, 1)) != 0)
+			break;
+
+		err = utoppy_command(sc, UTOPPY_CMD_RENAME, UTOPPY_LONG_TIMEOUT,
+		    &resp);
+		if (err)
+			break;
+
+		if (resp != UTOPPY_RESP_SUCCESS)
+			err = EIO;
+		break;
+
+	case UTOPPYIOMKDIR:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOMKDIR\n",
+		    USBDEVNAME(sc->sc_dev)));
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_add_path(sc, *((const char **)data), 1);
+		if (err)
+			break;
+
+		err = utoppy_command(sc, UTOPPY_CMD_MKDIR, UTOPPY_LONG_TIMEOUT,
+		    &resp);
+		if (err)
+			break;
+
+		if (resp != UTOPPY_RESP_SUCCESS)
+			err = EIO;
+		break;
+
+	case UTOPPYIODELETE:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIODELETE\n",
+		    USBDEVNAME(sc->sc_dev)));
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_add_path(sc, *((const char **)data), 0);
+		if (err)
+			break;
+
+		err = utoppy_command(sc, UTOPPY_CMD_DELETE, UTOPPY_LONG_TIMEOUT,
+		    &resp);
+		if (err)
+			break;
+
+		if (resp != UTOPPY_RESP_SUCCESS)
+			err = EIO;
+		break;
+
+	case UTOPPYIOREADDIR:
+		DPRINTF(UTOPPY_DBG_IOCTL, ("%s: utoppyioctl: UTOPPYIOREADDIR\n",
+		    USBDEVNAME(sc->sc_dev)));
+		UTOPPY_OUT_INIT(sc);
+		err = utoppy_add_path(sc, *((const char **)data), 0);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppyioctl: "
+			    "utoppy_add_path() returned %d\n",
+			    USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+
+		err = utoppy_send_packet(sc, UTOPPY_CMD_READDIR,
+		    UTOPPY_LONG_TIMEOUT);
+		if (err != 0) {
+			DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppyioctl: "
+			    "UTOPPY_CMD_READDIR returned %d\n",
+			    USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+
+		err = utoppy_readdir_next(sc);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_READDIR, ("%s: utoppyioctl: "
+			    "utoppy_readdir_next() returned %d\n",
+			    USBDEVNAME(sc->sc_dev), err));
+		}
+		break;
+
+	case UTOPPYIOREADFILE:
+		urf = (struct utoppy_readfile *)data;
+
+		DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: UTOPPYIOREADFILE "
+		    "%s, offset %lld\n", USBDEVNAME(sc->sc_dev), urf->ur_path,
+		    urf->ur_offset));
+
+		if ((err = utoppy_turbo_mode(sc, sc->sc_turbo_mode)) != 0)
+			break;
+
+		UTOPPY_OUT_INIT(sc);
+		utoppy_add_8(sc, UTOPPY_FILE_READ);
+
+		if ((err = utoppy_add_path(sc, urf->ur_path, 1)) != 0)
+			break;
+
+		utoppy_add_64(sc, urf->ur_offset);
+
+		sc->sc_state = UTOPPY_STATE_READFILE;
+		sc->sc_in_offset = 0;
+
+		err = utoppy_send_packet(sc, UTOPPY_CMD_FILE,
+		    UTOPPY_LONG_TIMEOUT);
+		if (err == 0)
+			err = utoppy_readfile_next(sc);
+		break;
+
+	case UTOPPYIOWRITEFILE:
+		uw = (struct utoppy_writefile *)data;
+
+		DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: UTOPPYIOWRITEFILE "
+		    "%s, size %lld, offset %lld\n", USBDEVNAME(sc->sc_dev),
+		    uw->uw_path, uw->uw_size, uw->uw_offset));
+
+		if ((err = utoppy_turbo_mode(sc, sc->sc_turbo_mode)) != 0)
+			break;
+
+		UTOPPY_OUT_INIT(sc);
+		utoppy_add_8(sc, UTOPPY_FILE_WRITE);
+		uwfp = utoppy_current_ptr(sc->sc_out_data);
+
+		if ((err = utoppy_add_path(sc, uw->uw_path, 1)) != 0) {
+			DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: add_path() "
+			    "returned %d\n", USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+
+		strncpy(uwf, &uwfp[2], sizeof(uwf));
+		utoppy_add_64(sc, uw->uw_offset);
+
+		err = utoppy_command(sc, UTOPPY_CMD_FILE, UTOPPY_LONG_TIMEOUT,
+		    &resp);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: "
+			    "utoppy_command(UTOPPY_CMD_FILE) returned "
+			    "%d\n", USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+		if (resp != UTOPPY_RESP_SUCCESS) {
+			DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: "
+			    "utoppy_command(UTOPPY_CMD_FILE) returned "
+			    "bad response 0x%x\n", USBDEVNAME(sc->sc_dev),
+			    resp));
+			err = EIO;
+			break;
+		}
+
+		UTOPPY_OUT_INIT(sc);
+		utoppy_timestamp_encode(sc, uw->uw_mtime);
+		utoppy_add_8(sc, UTOPPY_FTYPE_FILE);
+		utoppy_add_64(sc, uw->uw_size);
+		utoppy_add_string(sc, uwf, sizeof(uwf));
+		utoppy_add_32(sc, 0);
+
+		err = utoppy_command(sc, UTOPPY_RESP_FILE_HEADER,
+		    UTOPPY_LONG_TIMEOUT, &resp);
+		if (err) {
+			DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: "
+			    "utoppy_command(UTOPPY_RESP_FILE_HEADER) "
+			    "returned %d\n", USBDEVNAME(sc->sc_dev), err));
+			break;
+		}
+		if (resp != UTOPPY_RESP_SUCCESS) {
+			DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: "
+			    "utoppy_command(UTOPPY_RESP_FILE_HEADER) "
+			    "returned bad response 0x%x\n",
+			    USBDEVNAME(sc->sc_dev), resp));
+			err = EIO;
+			break;
+		}
+
+		sc->sc_wr_offset = uw->uw_offset;
+		sc->sc_wr_size = uw->uw_size;
+		sc->sc_state = UTOPPY_STATE_WRITEFILE;
+
+		DPRINTF(UTOPPY_DBG_WRITE,("%s: utoppyioctl: Changing state to "
+		    "%s. wr_offset %lld, wr_size %lld\n",
+		    USBDEVNAME(sc->sc_dev), utoppy_state_string(sc->sc_state),
+		    sc->sc_wr_offset, sc->sc_wr_size));
+		break;
+
+	default:
+		DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: Invalid cmd\n",
+		    USBDEVNAME(sc->sc_dev)));
+		err = ENODEV;
+		break;
+	}
+
+	DPRINTF(UTOPPY_DBG_IOCTL,("%s: utoppyioctl: done. err %d, state '%s'\n",
+	    USBDEVNAME(sc->sc_dev), err, utoppy_state_string(sc->sc_state)));
+
+	if (err)
+		utoppy_cancel(sc);
+
+	if (--sc->sc_refcnt < 0)
+		usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+	return (err);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(utoppy, uhub, utoppy_driver, utoppy_devclass, usbd_driver_load, 0);
+#endif
diff -Nru /usr/local/cvsup-stable/src/sys/dev/usb/utoppy.h /usr/src/sys/dev/usb/utoppy.h
--- /usr/local/cvsup-stable/src/sys/dev/usb/utoppy.h	Thu Jan  1 01:00:00 1970
+++ /usr/src/sys/dev/usb/utoppy.h	Mon Apr  3 08:15:00 2006
@@ -0,0 +1,106 @@
+/*	$NetBSD: utoppy.h,v 1.1 2006/04/03 08:15:48 scw Exp $	*/
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Steve C. Woodford.
+ *
+ * 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.
+ */
+
+#ifndef _DEV_USB_UTOPPY_H_
+#define _DEV_USB_UTOPPY_H_
+
+#include <sys/ioccom.h>
+
+#define	UTOPPY_MAX_FILENAME_LEN	95
+#define	UTOPPY_MAX_PATHNAME_LEN	((UTOPPY_MAX_FILENAME_LEN + 1) * 6)
+
+/* Set/clear turbo mode */
+#define	UTOPPYIOTURBO		_IOW('t', 1, int)
+
+/* Cancel previous op */
+#define	UTOPPYIOCANCEL		_IO('t', 2)
+
+/* Reboot the toppy */
+#define	UTOPPYIOREBOOT		_IO('t', 3)
+
+/* Get status of Toppy's hard disk drive */
+#define	UTOPPYIOSTATS		_IOR('t', 4, struct utoppy_stats)
+struct utoppy_stats {
+	uint64_t us_hdd_size;
+	uint64_t us_hdd_free;
+};
+
+/* Rename a file/directory */
+#define	UTOPPYIORENAME		_IOW('t', 5, struct utoppy_rename)
+struct utoppy_rename {
+	char *ur_old_path;
+	char *ur_new_path;
+};
+
+/* Create a directory */
+#define	UTOPPYIOMKDIR		_IOW('t', 6, char *)
+
+/* Delete a file/directory */
+#define	UTOPPYIODELETE		_IOW('t', 7, char *)
+
+/* Initiate reading of the contents of a directory */
+#define	UTOPPYIOREADDIR		_IOW('t', 8, char *)
+struct utoppy_dirent {
+	char ud_path[UTOPPY_MAX_FILENAME_LEN + 1];
+	enum {
+		UTOPPY_DIRENT_UNKNOWN,
+		UTOPPY_DIRENT_DIRECTORY,
+		UTOPPY_DIRENT_FILE
+	} ud_type;
+	off_t ud_size;
+	time_t ud_mtime;
+	uint32_t ud_attributes;
+};
+
+/* Initiate reading from a specific file */
+#define	UTOPPYIOREADFILE	_IOW('t', 9, struct utoppy_readfile)
+struct utoppy_readfile {
+	char *ur_path;
+	off_t ur_offset;
+};
+
+/* Initiate writing to a new file */
+#define	UTOPPYIOWRITEFILE	_IOW('t', 10, struct utoppy_writefile)
+struct utoppy_writefile {
+	char *uw_path;
+	off_t uw_offset;
+	off_t uw_size;
+	time_t uw_mtime;
+};
+
+#endif /* _DEV_USB_UTOPPY_H_ */
diff -Nru /usr/local/cvsup-stable/src/sys/modules/Makefile /usr/src/sys/modules/Makefile
--- /usr/local/cvsup-stable/src/sys/modules/Makefile	Sat Dec 30 17:55:15 2006
+++ /usr/src/sys/modules/Makefile	Sun Dec 31 16:40:18 2006
@@ -274,6 +274,7 @@
 	usb \
 	uscanner \
 	utopia \
+	utoppy \
 	uvisor \
 	uvscom \
 	${_vesa} \
diff -Nru /usr/local/cvsup-stable/src/sys/modules/utoppy/Makefile /usr/src/sys/modules/utoppy/Makefile
--- /usr/local/cvsup-stable/src/sys/modules/utoppy/Makefile	Thu Jan  1 01:00:00 1970
+++ /usr/src/sys/modules/utoppy/Makefile	Sun Dec 31 16:39:59 2006
@@ -0,0 +1,8 @@
+# $FreeBSD: $
+
+.PATH: ${.CURDIR}/../../dev/usb
+
+KMOD=	 utoppy
+SRCS=	bus_if.h device_if.h opt_usb.h utoppy.c utoppy.h usbdevs.h
+
+.include <bsd.kmod.mk>
--- /usr/local/cvsup-stable/src/sys/conf/NOTES	Sat Dec 30 17:55:15 2006
+++ /usr/src/sys/conf/NOTES	Mon Jan  1 04:15:33 2007
@@ -2352,6 +2352,8 @@
 device		urio
 # USB scanners
 device		uscanner
+# USB support for the Topfield TF5000PVR digital video recorder
+device		utoppy
 #
 # USB serial support
 device		ucom

>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200701010439.l014d8E8003230>