From owner-freebsd-usb@FreeBSD.ORG Mon Jan 1 04:40:22 2007 Return-Path: X-Original-To: freebsd-usb@hub.freebsd.org Delivered-To: freebsd-usb@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id BAB9316A40F for ; Mon, 1 Jan 2007 04:40:22 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [69.147.83.40]) by mx1.freebsd.org (Postfix) with ESMTP id 9763C13C44C for ; Mon, 1 Jan 2007 04:40:22 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.13.4/8.13.4) with ESMTP id l014eMCb015851 for ; Mon, 1 Jan 2007 04:40:22 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.13.4/8.13.4/Submit) id l014eMTv015850; Mon, 1 Jan 2007 04:40:22 GMT (envelope-from gnats) Resent-Date: Mon, 1 Jan 2007 04:40:22 GMT Resent-Message-Id: <200701010440.l014eMTv015850@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-usb@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Keith Jones Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 5651516A407 for ; Mon, 1 Jan 2007 04:39:08 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [69.147.83.33]) by mx1.freebsd.org (Postfix) with ESMTP id 43F9413C43E for ; Mon, 1 Jan 2007 04:39:08 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.13.1/8.13.1) with ESMTP id l014d831003231 for ; Mon, 1 Jan 2007 04:39:08 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.13.1/8.13.1/Submit) id l014d8E8003230; Mon, 1 Jan 2007 04:39:08 GMT (envelope-from nobody) Message-Id: <200701010439.l014d8E8003230@www.freebsd.org> Date: Mon, 1 Jan 2007 04:39:08 GMT From: Keith Jones To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.0 Cc: Subject: usb/107388: [PATCH] Add utoppy device from NetBSD X-BeenThere: freebsd-usb@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: FreeBSD support for USB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 01 Jan 2007 04:40:22 -0000 >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 +#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 +#include +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#include +#include +#endif +#if __FreeBSD_version >= 500014 +#include +#else +#include +#endif +#include +#include +#include + +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#elif defined(__FreeBSD__) +#include "usbdevs.h" +#endif +#include +#include + +#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 + +#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 --- /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: