This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

USB printer package


Here's a USB printer package, similar to the USB serial package. It can enumerate as a Unidirectional printer (one Bulk OUT endpoint) or a Bidirectional printer (one Bulk OUT and one Bulk IN endpoint).

This isn't as generally useful as the serial package, but can server as the basis for an actual printer or some other device that wants to appear like a printer to the host. As such, I didn't include any host drivers. Those would probably have to be specific to the actual device created. But there is an example program that accepts data from a host sends it out a serial port. On insertion to a host, the user can manually select any printer driver, and see the raw data output when a document is printed.

I tested with an AT91SAM7S-EK eval board.

Frank
diff -urN --exclude=CVS --exclude=.svn ecos/packages/ecos.db usbprinter/packages/ecos.db
--- ecos/packages/ecos.db	2008-05-06 15:04:53.000000000 -0400
+++ usbprinter/packages/ecos.db	2008-08-11 09:49:15.000000000 -0400
@@ -1393,6 +1393,13 @@
 	description     "Generic USB slave-side support"
 }
 
+package CYGPKG_IO_USB_SLAVE_PRINTER {
+        alias		{ "USB slave-side printer driver" usbs_printer }
+        directory       io/usb/printer/slave
+        script          usbs_printer.cdl
+        description     "Support for USB printer devices."
+}
+
 package CYGPKG_IO_USB_SLAVE_ETH {
         alias		{ "USB slave-side ethernet class drivers" usbs_eth }
         directory       io/usb/eth/slave
@@ -4525,6 +4532,8 @@
                    CYGPKG_DEVS_FLASH_AT91
                    CYGPKG_DEVS_SPI_ARM_AT91
                    CYGPKG_DEVICES_WATCHDOG_ARM_AT91WDTC
+                   CYGPKG_IO_USB
+                   CYGPKG_IO_USB_SLAVE
                    CYGPKG_DEVS_USB_AT91
         }
         description "
diff -urN --exclude=CVS --exclude=.svn ecos/packages/io/usb/printer/slave/current/cdl/usbs_printer.cdl usbprinter/packages/io/usb/printer/slave/current/cdl/usbs_printer.cdl
--- ecos/packages/io/usb/printer/slave/current/cdl/usbs_printer.cdl	1969-12-31 19:00:00.000000000 -0500
+++ usbprinter/packages/io/usb/printer/slave/current/cdl/usbs_printer.cdl	2008-08-15 13:56:21.000000000 -0400
@@ -0,0 +1,236 @@
+# ====================================================================
+#
+#      usbs_printer.cdl
+#
+#      USB slave-side printer package.
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      Frank M. Pagliughi (fmp), SoRo Systems, Inc.
+# Contributors:
+# Date:           2008-06-04
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_IO_USB_SLAVE_PRINTER {
+    display     "USB slave printer support"
+    include_dir "cyg/io/usb"
+    parent      CYGPKG_IO_USB_SLAVE
+    requires    { CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS >= 1 }
+    requires    { CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS >= 0 }
+    compile     usbs_printer.c
+    implements  CYGINT_IO_USB_SLAVE_CLIENTS
+    doc         ref/io-usb-slave-printer.html
+    
+    description "
+        The USB slave printer package supports the development of USB 
+        printers and other peripherals that mimic a printer connection
+        to the host machine."
+
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_TYPE {
+        display         "Printer type"
+        flavor          data
+        default_value   { "Bidirectional" }
+        legal_values    { "Unidirectional" "Bidirectional" }
+        description     "
+            The USB printer module can enumerate as either a unidirectional
+            or bidirectional printer. A unidirectional printer emulates
+            a classic printer that only accepts data from the host (and 
+            prints it). It only requires a single Bulk OUT endpoint.
+            A bidirectional printer can send data back to the host and 
+            requires both a Bulk OUT and a Bulk IN endpoint."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_EP0 {
+        display       "Name of EP0 structure"
+        flavor        data               
+        default_value { "usbs_at91_ep0" }
+        description   "
+            The name of the variable that contains the endpoint 0 structure.
+            This should be set to the EP0 structure for the desired USB device driver
+            such as usbs_at91_ep0, usbs_sa11x0_ep0, etc"
+    }
+    cdl_option CYGNUM_IO_USB_SLAVE_PRINTER_EP0_MAX_PACKET_SIZE {
+        display       "The size of EP0"
+        flavor        data
+        default_value 8
+        legal_values  { 8 16 64 } 
+        description   "
+            The size of the EP0 hardware buffer on the specific USB chip used."
+    } 
+    cdl_option CYGNUM_IO_USB_SLAVE_PRINTER_RX_EP_NUM {
+        display         "Rx (USB OUT) endpoint number"
+        flavor          data
+        default_value   1
+        description     "
+            The endpoint that should be used for the device-side receiver, which
+            is the USB OUT direction."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_RX_EP {
+        display         "The Rx (USB OUT) endpoint structure"
+         flavor         data               
+         default_value  { "usbs_at91_ep1" }
+         description    "
+             The endpoint structure that corresponds to the selected Rx endpoint
+             number. This is dependent on the USBS device driver selected, and 
+             could be usbs_at91_ep1, usbs_sa11x0_ep1, etc"
+    }
+    cdl_option CYGNUM_IO_USB_SLAVE_PRINTER_TX_EP_NUM {
+        display       "Tx (USB IN) endpoint number"
+        flavor        data
+        default_value 2
+        active_if     { CYGDAT_IO_USB_SLAVE_PRINTER_TYPE == "Bidirectional" }
+        description   "
+            The endpoint that should be used for the device-side transmitter, which
+            is the USB IN direction."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_TX_EP {
+        display         "The Tx (USB IN) endpoint structure"
+         flavor         data
+         default_value  { "usbs_at91_ep2" }
+         description    "
+             The endpoint structure that corresponds to the selected Tx endpoint
+             number. This is dependent on the USBS device driver selected, and 
+             could be usbs_at91_ep1, usbs_sa11x0_ep1, etc"
+    }
+    cdl_option CYGNUM_IO_USB_SLAVE_PRINTER_VENDOR_ID {
+        display       "USB Vendor ID"
+        flavor        data
+        default_value 0xFFFF
+        legal_values  1 to 0xFFFF
+        description   "
+            Each USB vendor has an Vendor ID allocated to it by the USB-IF organization.
+            Any arbitrary value can be selected for testing provided that it doesn't 
+            conflict with devices on the development host, but a device should NEVER be
+            released publicly without a valid Vendor ID"
+    }
+    cdl_option CYGNUM_IO_USB_SLAVE_PRINTER_PRODUCT_ID {
+        display       "USB Product ID"
+        flavor        data
+        default_value 1
+        legal_values  1 to 0xFFFF
+        description   "
+            You are free to select an arbitrary 16-bit Product ID for a device. The combination
+            of Vendor ID and Product ID uniquely identified a USB device."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_MFG_STR {
+        display       "The Device Vendor's Name (Manufacturer String)"
+        flavor        data
+        default_value { "\"eCos\"" }
+        description "
+                The standard USB enumeration allows for a manufacturer's name which is
+                normally reported to the user when the device is first plugged into
+                the host."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_PRODUCT_STR {
+        display       "The Device Product Name"
+        flavor        data
+        default_value { "\"USB Printer Example\"" }
+        description "
+                The standard USB enumeration allows for a product name which is
+                normally reported to the user when the device is first plugged into
+                the host."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_PRINTER_COMMAND_SET {
+        display       "The Printer Command Set"
+        flavor        data
+        default_value { "\"TEXT\"" }
+        description "
+                The list of command languages that the printer supports, like
+                PCL, etc. See IEEE-1284 documentation for more information."
+    }
+
+    cdl_option  CYGBLD_IO_USB_SLAVE_PRINTER_DEBUG {
+        display       "Enable debug output from the driver"
+        default_value 0
+        flavor        bool
+        description   "
+            The driver may produce debug output which can be
+            useful to work out why it is not working as expected."
+    }
+    cdl_component CYGPKG_IO_USB_SLAVE_PRINTER_OPTIONS {
+    display     "Build options"
+    flavor      none
+
+    description "
+        Package-specific build options including control over compiler
+        flags used only in building this package."
+
+    cdl_option CYGPKG_IO_USB_SLAVE_PRINTER_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building this package. These flags are used in addition
+                to the set of global flags."
+    }
+        cdl_option CYGPKG_IO_USB_RUP_CFLAGS_REMOVE {
+            display "Suppressed compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building this package. These flags are removed from
+                the set of global flags if present."
+        }
+        cdl_component CYGBLD_IO_USB_SLAVE_PRINTER_EXAMPLES {
+            display "Build example programs"
+            no_define
+            requires    { CYGPKG_IO_FILEIO }
+            requires    { CYGPKG_IO_SERIAL_DEVICES }
+            description "
+                Enabling this option will cause the example programs
+                to be built using the normal test case infrastructure.
+                They are however not test cases and should not be used
+                with any automatic test farm running all the
+                tests. Hence this option is disabled by default."
+            
+             cdl_option CYGPKG_IO_USB_SLAVE_PRINTER_TESTS {
+                 display "USBS printer example/test programs"
+                 no_define
+                 flavor data
+                 calculated { "tests/usbprinter_ex.c" }
+                 description "
+                     usbprinter_ex is an example of how to use the USB slave 
+                     printer package. It accepts data from the host and sends
+                     the data out a real serial port on the device. It can be 
+                     used to show the raw data sent out by the various printer
+                     drivers on the host. It is hard coded to use /dev/ser0."
+             }
+        }
+    }
+}
diff -urN --exclude=CVS --exclude=.svn ecos/packages/io/usb/printer/slave/current/include/usbs_printer.h usbprinter/packages/io/usb/printer/slave/current/include/usbs_printer.h
--- ecos/packages/io/usb/printer/slave/current/include/usbs_printer.h	1969-12-31 19:00:00.000000000 -0500
+++ usbprinter/packages/io/usb/printer/slave/current/include/usbs_printer.h	2008-08-15 15:54:17.000000000 -0400
@@ -0,0 +1,199 @@
+#ifndef CYGONCE_USBS_PRINTER_H
+#define CYGONCE_USBS_PRINTER_H
+//==========================================================================
+//
+//      include/usbs_printer.h
+//
+//      Description of the USB slave-side printer support
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    Frank M. Pagliughi (fmp), SoRo Systems, Inc.
+// Contributors: 
+// Date:         2008-08-08
+// Purpose:
+// Description:  USB slave-side printer support
+//
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+//
+// The purpose of the USB slave-side printer code is to provide a starting
+// point for USB printers and other devices that present themselves as
+// printers to the host computer.
+// 
+// On this side (the eCos USB slave side), the application sees the host
+// through a normal USB slave connection with either one or two Bulk 
+// endpoints. A unidirectional printer has a single Bulk OUT endpoint
+// whereas a bidirectional printer has two endpoints - one in the IN
+// direction and one in the OUT direction. This module provides the
+// necessary USB descriptors to enumerate the device for a single printer,
+// but then the application is free to communicate with the host using
+// any desired API:
+//  - The standard eCos USB slave API
+//  - The low-level File I/O layer (if USB devtab entries configured)
+//  - The C stdio functions (again, if USB devtab entries configured)
+//  - The USB printer API defined here.
+// 
+// The USB printer API is a thin layer over the standard eCos USB functions
+// to provide common synchronous and asynchronous transfers over the assigned
+// Bulk endpoint(s).
+//
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/io/usb/usbs.h>
+#include <cyg/hal/drv_api.h>
+
+#define USBS_DEVICE_CLASS_PRINTER       7
+#define USBS_PRINTER_SUBCLASS_PRINTER   1
+
+#define USBS_PRINTER_TYPE_UNIDIRECTIONAL    1
+#define USBS_PRINTER_TYPE_BIDIRECTIONAL     2
+#define USBS_PRINTER_TYPE_IEEE_1284_4       3
+
+// ----------------------------------------------------------------------------
+
+// --------------------------------------------------------------------------
+//                      Printer Device-Class Handlers
+// --------------------------------------------------------------------------
+
+#define USBS_PRINTER_DEVREQ_GET_DEVICE_ID       0
+#define USBS_PRINTER_DEVREQ_GET_PORT_STATUS     1
+#define USBS_PRINTER_DEVREQ_SOFT_RESET          2
+
+// ----------------------------------------------------------------------------
+// Data structure to manage the pair of USB endpoints that comprise a single
+// printer port connection. Each "port" requires one Bulk IN endpoint and one
+// Bulk OUT endpoint.
+// 
+
+typedef struct usbs_printer_callback_data {
+    cyg_bool            completed;
+    int                 result;
+    cyg_drv_mutex_t     lock;
+    cyg_drv_cond_t      signal;
+} usbs_printer_callback_data;
+
+typedef struct usbs_printer {
+    // The receiver communication endpoint. This is normally set by the
+    // configuration, but can be changed by the application, if desired.
+    usbs_rx_endpoint* rx_ep;
+
+    // The signal that a receive operation is complete, and it's result.
+    usbs_printer_callback_data rx_callback_data;
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+    // Only bidirectional printers have a Tx endpoint
+    usbs_tx_endpoint* tx_ep;
+
+    // The signal that a transmit operation is complete, and it's result.
+    usbs_printer_callback_data tx_callback_data;
+#endif
+
+} usbs_printer;
+
+// The package constains one USB printer device.
+extern usbs_printer usbs_prn0;
+
+// It's assumed that there's a single USB slave chip in the system, with a
+// single control endpoint 0. The actual variable is contained in the device
+// driver, but the USB printer code keeps a pointer to it for driver 
+// independence. The application might find it useful for overriding low-level
+// code or callbacks.
+extern usbs_control_endpoint* usbs_printer_ep0;
+
+// ----------------------------------------------------------------------------
+// A C interface to the printer USB code.
+// The application can use this interface, the standard (low-level) USB slave
+// API, the standard Unix-like I/O API, or C stdio API.
+    
+// Initialize support for a particular USB printer "port"
+// This associates a usbs_printer structure with specific endpoints and 
+// initializes the structure for communications.
+void usbs_printer_init(usbs_printer*, usbs_rx_endpoint*, usbs_tx_endpoint*);
+
+// Block the calling thread until the host configures the USB device.
+void usbs_printer_wait_until_configured(void);
+
+// Determines if the USB substsrem is configured
+cyg_bool usbs_printer_is_configured(void);
+
+// Start an asynchronous receive of a buffer.
+void usbs_printer_start_rx(usbs_printer*, void* buf, int n);
+
+// Block the calling thread until the receive completes.
+// Returns the result code for the transfer
+int usbs_printer_wait_for_rx(usbs_printer*);
+
+// Blocking, synchronous receive of a single buffer.
+int usbs_printer_rx(usbs_printer*, void* buf, int n);
+
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+// Start an asynchronous transmit of a single buffer.
+void usbs_printer_start_tx(usbs_printer*, const void* buf, int n);
+
+// Block the calling thread until the transmit completes.
+// Returns the result code for the transfer
+int usbs_printer_wait_for_tx(usbs_printer*);
+
+// Blocking, synchronous transmit of a single buffer.
+int usbs_printer_tx(usbs_printer*, const void* buf, int n);
+#endif
+
+
+// The default USB-printer state change handler paces the functions
+// usbs_printer_wait_until_configured() and usbs_printer_is_configured().
+// The application can override the state chain handler, but chain to 
+// this function to keep the full USB-printer system working.
+void usbs_printer_state_change_handler(usbs_control_endpoint*, void*, 
+                                       usbs_state_change, int);
+
+// Starts the USB subsystem
+void usbs_printer_start(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // CYGONCE_USBS_PRINTER_H
+
diff -urN --exclude=CVS --exclude=.svn ecos/packages/io/usb/printer/slave/current/src/usbs_printer.c usbprinter/packages/io/usb/printer/slave/current/src/usbs_printer.c
--- ecos/packages/io/usb/printer/slave/current/src/usbs_printer.c	1969-12-31 19:00:00.000000000 -0500
+++ usbprinter/packages/io/usb/printer/slave/current/src/usbs_printer.c	2008-08-15 16:21:23.000000000 -0400
@@ -0,0 +1,585 @@
+//==========================================================================
+//
+//      usbs_printer.c
+//
+//      Support for slave-side USB printer devices.
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    Frank M. Pagliughi (fmp), SoRo Systems, Inc.
+// Date:         2008-06-02
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/kernel/kapi.h>
+
+#include <pkgconf/io_usb_slave_printer.h>
+#include <cyg/io/usb/usbs_printer.h>
+#include <string.h>
+
+#if defined(CYGBLD_IO_USB_SLAVE_PRINTER_DEBUG)
+#define DBG diag_printf
+#else
+#define DBG (1) ? (void)0 : diag_printf
+#endif
+
+#define EP0_MAX_PACKET_SIZE     CYGNUM_IO_USB_SLAVE_PRINTER_EP0_MAX_PACKET_SIZE
+
+extern usbs_control_endpoint    CYGDAT_IO_USB_SLAVE_PRINTER_EP0;
+extern usbs_rx_endpoint         CYGDAT_IO_USB_SLAVE_PRINTER_RX_EP;
+
+#define EP0                     (&CYGDAT_IO_USB_SLAVE_PRINTER_EP0)
+
+#define RX_EP_NUM               CYGNUM_IO_USB_SLAVE_PRINTER_RX_EP_NUM
+#define RX_EP                   (&CYGDAT_IO_USB_SLAVE_PRINTER_RX_EP)
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+extern usbs_tx_endpoint         CYGDAT_IO_USB_SLAVE_PRINTER_TX_EP;
+#define TX_EP_NUM               CYGNUM_IO_USB_SLAVE_PRINTER_TX_EP_NUM
+#define TX_EP                   (&CYGDAT_IO_USB_SLAVE_PRINTER_TX_EP)
+#else
+#define TX_EP                   ((void*)0)
+#endif
+
+#define VENDOR_ID               CYGNUM_IO_USB_SLAVE_PRINTER_VENDOR_ID
+#define PRODUCT_ID              CYGNUM_IO_USB_SLAVE_PRINTER_PRODUCT_ID
+
+#define USB_MAX_STR_LEN         256
+#define PRN_RSP_BUF_SIZE        258
+
+
+#define LO_BYTE_16(word16)      ((cyg_uint8) ((word16) & 0xFF))
+#define HI_BYTE_16(word16)      ((cyg_uint8) (((word16) >> 8) & 0xFF))
+
+#define BYTE0_32(word32)        ((cyg_uint8) ((word32) & 0xFF))
+#define BYTE1_32(word32)        ((cyg_uint8) (((word32) >>  8) & 0xFF))
+#define BYTE2_32(word32)        ((cyg_uint8) (((word32) >> 16) & 0xFF))
+#define BYTE3_32(word32)        ((cyg_uint8) (((word32) >> 24) & 0xFF))
+
+
+#define MFG_STR_INDEX           '\x01'
+#define PRODUCT_STR_INDEX       '\x02'
+
+#define USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH(interfaces, endpoints) \
+            (USB_CONFIGURATION_DESCRIPTOR_LENGTH +            \
+            ((interfaces) * USB_INTERFACE_DESCRIPTOR_LENGTH) +  \
+            ((endpoints)  * USB_ENDPOINT_DESCRIPTOR_LENGTH))
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+    #define USBS_PRINTER_TYPE                USBS_PRINTER_TYPE_BIDIRECTIONAL
+    #define USBS_PRINTER_NUM_ENDP            2
+#else
+    #define USBS_PRINTER_TYPE                USBS_PRINTER_TYPE_UNIDIRECTIONAL
+    #define USBS_PRINTER_NUM_ENDP            1
+#endif
+
+// ----- Configuration Descriptor -----
+
+static const usb_configuration_descriptor usb_configuration = {
+    length:             sizeof(usb_configuration_descriptor),
+    type:               USB_CONFIGURATION_DESCRIPTOR_TYPE,
+    total_length_lo:
+        USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_LO(1, USBS_PRINTER_NUM_ENDP),
+    total_length_hi:
+        USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_HI(1, USBS_PRINTER_NUM_ENDP),
+    number_interfaces:  1,
+    configuration_id:   1,
+    configuration_str:  0,
+    attributes:         USB_CONFIGURATION_DESCRIPTOR_ATTR_REQUIRED |
+                                USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED,
+    max_power:          50
+};
+
+// ----- Interface Descriptor -----
+
+static const usb_interface_descriptor usb_interface = {
+    length:             sizeof(usb_interface_descriptor),
+    type:               USB_INTERFACE_DESCRIPTOR_TYPE,
+    interface_id:       0,
+    alternate_setting:  0,
+    number_endpoints:   USBS_PRINTER_NUM_ENDP,
+    interface_class:    USBS_DEVICE_CLASS_PRINTER,
+    interface_subclass: USBS_PRINTER_SUBCLASS_PRINTER,
+    interface_protocol: USBS_PRINTER_TYPE,
+    interface_str:      0x00
+};
+
+// ----- Endpoint Descriptors -----
+
+static const usb_endpoint_descriptor usb_endpoints[] =
+{
+    // Bulk OUT Endpoint Descriptor
+    {
+        sizeof(usb_endpoint_descriptor),
+        USB_ENDPOINT_DESCRIPTOR_TYPE,
+        USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT | RX_EP_NUM,
+        USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+        0x40,
+        0,
+        0
+    },
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+    // Bulk IN Endpoint Descriptor
+    {
+        sizeof(usb_endpoint_descriptor),
+        USB_ENDPOINT_DESCRIPTOR_TYPE,
+        USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN | TX_EP_NUM,
+        USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+        0x40,
+        0,
+        0
+    }
+#endif
+};
+
+// ----- String Descriptors -----
+
+static char mfg_str_descr[USB_MAX_STR_LEN],
+            product_str_descr[USB_MAX_STR_LEN];
+
+
+static const char* usb_strings[] = {
+    "\x04\x03\x09\x04",
+    mfg_str_descr,
+    product_str_descr
+};
+
+// ----- Enumeration Data w/ Device Descriptor -----
+
+static usbs_enumeration_data usb_enum_data = {
+    {
+        length:                 sizeof(usb_device_descriptor),
+        type:                   USB_DEVICE_DESCRIPTOR_TYPE,
+        usb_spec_lo:            0x00, 
+        usb_spec_hi:            0x02,
+        device_class:           USBS_DEVICE_CLASS_PRINTER,
+        device_subclass:        0,
+        device_protocol:        0,
+        max_packet_size:        EP0_MAX_PACKET_SIZE,
+        vendor_lo:              LO_BYTE_16(VENDOR_ID),
+        vendor_hi:              HI_BYTE_16(VENDOR_ID),
+        product_lo:             LO_BYTE_16(PRODUCT_ID),
+        product_hi:             HI_BYTE_16(PRODUCT_ID),
+        device_lo:              0x00,
+        device_hi:              0x00,
+        manufacturer_str:       MFG_STR_INDEX,
+        product_str:            PRODUCT_STR_INDEX,
+        serial_number_str:      0,
+        number_configurations:  1
+    },
+
+    total_number_interfaces:    1,
+    total_number_endpoints:     USBS_PRINTER_NUM_ENDP,
+    total_number_strings:       3,
+    configurations:             &usb_configuration,
+    interfaces:                 &usb_interface,
+    endpoints:                  usb_endpoints,
+    strings:                    (const unsigned char **) usb_strings
+};
+
+
+// ----- 1284 Printer String -----
+// We build this string from USB identifiers supplied by the configuration.
+
+static const char *PRN_IDENTITY_STR =
+    "MFG:" CYGDAT_IO_USB_SLAVE_PRINTER_MFG_STR 
+    ";MDL:" CYGDAT_IO_USB_SLAVE_PRINTER_PRODUCT_STR
+    ";CMD:" CYGDAT_IO_USB_SLAVE_PRINTER_COMMAND_SET
+    ";CLASS:PRINTER;";
+
+// --------------------------------------------------------------------------
+// USBS Printer Data
+// --------------------------------------------------------------------------
+
+// The USB control endpoint, as defined by the user.
+usbs_control_endpoint* usbs_printer_ep0 = EP0;
+
+// Variables to update the USB state: mutex, cond var, and the state.
+cyg_mutex_t usbs_printer_lock;   
+cyg_cond_t  usbs_printer_state_cond;
+int         usbs_printer_state;
+
+// The default printer object. FOr most systems this will be the only one,
+// though the use is free to define more.
+usbs_printer usbs_prn0;
+
+static void (*usbs_printer_app_state_change_fn)(struct usbs_control_endpoint*, 
+                                                void*, usbs_state_change, int) = 0;
+
+static void usbs_printer_rx_complete(void *p, int result);
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+static void usbs_printer_tx_complete(void *p, int result);
+#endif
+
+// --------------------------------------------------------------------------
+// Create a USB String Descriptor from a C string.
+
+void
+usbs_printer_create_str_descriptor(char descr[], const char *str)
+{
+    int i, n = strlen(str);
+
+    if (n > (USB_MAX_STR_LEN/2 - 2))
+        n = USB_MAX_STR_LEN/2 - 2;
+
+    descr[0] = (cyg_uint8) (2*n + 2);
+    descr[1] = USB_DEVREQ_DESCRIPTOR_TYPE_STRING;
+
+    for (i=0; i<n; i++) {
+        descr[i*2+2] = str[i];
+        descr[i*2+3] = '\x00';
+    }
+}
+
+// --------------------------------------------------------------------------
+
+void
+usbs_printer_end_all_transders(usbs_printer *prn, int retcode)
+{
+    cyg_drv_mutex_lock(&prn->rx_callback_data.lock);
+
+    cyg_drv_mutex_lock(&prn->rx_callback_data.lock);
+}
+
+// --------------------------------------------------------------------------
+
+usbs_control_return
+usbs_printer_class_handler(usbs_control_endpoint *ep0, void *data)
+{
+    static unsigned char prn_rsp_buf[PRN_RSP_BUF_SIZE];
+    usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+    DBG("Printer Class Handler: ");
+
+    unsigned    n;
+    usb_devreq  *req = (usb_devreq *) ep0->control_buffer;
+
+    switch (req->request) {
+        case USBS_PRINTER_DEVREQ_GET_DEVICE_ID :
+                DBG("Get Device ID\n");
+                n = strlen(PRN_IDENTITY_STR)+1;
+                prn_rsp_buf[0] = HI_BYTE_16(n);
+                prn_rsp_buf[1] = LO_BYTE_16(n);
+                memcpy(prn_rsp_buf+2, PRN_IDENTITY_STR, n);
+                prn_rsp_buf[n+2] = '\0';
+
+                ep0->buffer = prn_rsp_buf;
+                ep0->buffer_size = (int) (n+3);
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+        case USBS_PRINTER_DEVREQ_GET_PORT_STATUS :
+                DBG("Get Port Status\n");
+                prn_rsp_buf[0] = '\x18';
+                ep0->buffer = prn_rsp_buf;
+                ep0->buffer_size = 1;
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+        case USBS_PRINTER_DEVREQ_SOFT_RESET :
+                DBG("Soft Reset\n");
+                // End any pending transfers
+                usbs_printer_rx_complete(&usbs_prn0, -EPIPE);
+                #ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+                    usbs_printer_tx_complete(&usbs_prn0, -EPIPE);
+                #endif
+                ep0->buffer = prn_rsp_buf;  // Arbitrary non-null
+                ep0->buffer_size = 0;
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+        default :
+                DBG("Unhandled Printer Request: 0x%02X\n",
+                    (unsigned) req->request);
+    }   
+
+    return result;
+}
+
+// --------------------------------------------------------------------------
+// Callback for a USB state change
+
+void
+usbs_printer_state_change_handler(usbs_control_endpoint* ep, void* data,
+                                  usbs_state_change change, int prev_state)
+{
+    #if defined(CYGBLD_IO_USB_SLAVE_PRINTER_DEBUG)
+        const char *STATE_CHG_STR[] = { "Detached", "Attached", "Powered", "Reset",
+                                        "Addressed", "Configured", "Deconfigured",
+                                        "Suspended", "Resumed" };
+
+        if (change > 0) {
+            DBG("### %s ###\n", STATE_CHG_STR[(int) change-1]);
+        }
+    #endif
+
+    // Called from DSR, cond broadcast should be ok without mutex lock
+    usbs_printer_state = usbs_printer_ep0->state;
+    cyg_cond_broadcast(&usbs_printer_state_cond);
+
+    if (usbs_printer_app_state_change_fn)
+        (*usbs_printer_app_state_change_fn)(ep, data, change, prev_state);
+}
+
+// --------------------------------------------------------------------------
+// Block the calling thread until the USB is configured.
+ 
+void
+usbs_printer_wait_until_configured(void)
+{
+    cyg_mutex_lock(&usbs_printer_lock);
+    while (usbs_printer_state != USBS_STATE_CONFIGURED)
+      cyg_cond_wait(&usbs_printer_state_cond);
+    cyg_mutex_unlock(&usbs_printer_lock);
+}
+
+// --------------------------------------------------------------------------
+// Determine if the device is currently configured.
+
+cyg_bool
+usbs_printer_is_configured(void)
+{
+    return usbs_printer_state == USBS_STATE_CONFIGURED;
+}
+
+// --------------------------------------------------------------------------
+//                                  Receive
+// --------------------------------------------------------------------------
+// Callback for when a receive is complete
+
+static void 
+usbs_printer_rx_complete(void *p, int result)
+{
+    usbs_printer* prn = (usbs_printer*) p;
+    prn->rx_callback_data.result = result;
+    prn->rx_callback_data.completed = true;
+    cyg_drv_cond_signal(&prn->rx_callback_data.signal);
+}
+
+// --------------------------------------------------------------------------
+// Start an asynchronous receive of a buffer.
+
+void
+usbs_printer_start_rx(usbs_printer* prn, void* buf, int n)
+{
+    cyg_drv_mutex_lock(&prn->rx_callback_data.lock);
+    prn->rx_callback_data.completed = false;
+    cyg_drv_mutex_unlock(&prn->rx_callback_data.lock);
+
+    usbs_start_rx_buffer(prn->rx_ep, (unsigned char*) buf, n,
+                         usbs_printer_rx_complete, prn);
+}
+
+// --------------------------------------------------------------------------
+// Block the caller until the receive is complete
+
+int
+usbs_printer_wait_for_rx(usbs_printer* prn)
+{
+    int result;
+
+    cyg_drv_mutex_lock(&prn->rx_callback_data.lock);
+    while (!prn->rx_callback_data.completed)
+        cyg_drv_cond_wait(&prn->rx_callback_data.signal);
+    result = prn->rx_callback_data.result;
+    cyg_drv_mutex_unlock(&prn->rx_callback_data.lock);
+
+    return result;
+}
+
+// --------------------------------------------------------------------------
+// Perform a synchronous receive and wait for it to complete.
+
+int
+usbs_printer_rx(usbs_printer* prn, void* buf, int n)
+{
+    int result;
+
+    cyg_drv_mutex_lock(&prn->rx_callback_data.lock);
+    prn->rx_callback_data.completed = false;
+    usbs_start_rx_buffer(prn->rx_ep, (unsigned char*) buf, n,
+                         usbs_printer_rx_complete, prn);
+    while (!prn->rx_callback_data.completed)
+        cyg_drv_cond_wait(&prn->rx_callback_data.signal);
+    result = prn->rx_callback_data.result;
+    cyg_drv_mutex_unlock(&prn->rx_callback_data.lock);
+
+    return result;
+}
+
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+
+// --------------------------------------------------------------------------
+//                   Transmit (Bidirectional Printers Only)
+// --------------------------------------------------------------------------
+
+// Callback for when a transmit is complete
+
+static void 
+usbs_printer_tx_complete(void *p, int result)
+{
+    usbs_printer* prn = (usbs_printer*) p;
+    prn->tx_callback_data.result = result;
+    prn->tx_callback_data.completed = true;
+    cyg_drv_cond_signal(&prn->tx_callback_data.signal);
+}
+
+// --------------------------------------------------------------------------
+// Start an asynchronous transmit of a buffer.
+ 
+void
+usbs_printer_start_tx(usbs_printer* prn, const void* buf, int n)
+{
+    cyg_drv_mutex_lock(&prn->tx_callback_data.lock);
+    prn->tx_callback_data.completed = false;
+    cyg_drv_mutex_unlock(&prn->tx_callback_data.lock);
+
+    usbs_start_tx_buffer(prn->tx_ep, (unsigned char*) buf, n,
+                         usbs_printer_tx_complete, prn);
+}
+
+// --------------------------------------------------------------------------
+// Block the caller until the transmit is complete
+
+int
+usbs_printer_wait_for_tx(usbs_printer* prn)
+{
+    int result;
+
+    cyg_drv_mutex_lock(&prn->tx_callback_data.lock);
+    while (!prn->tx_callback_data.completed)
+        cyg_drv_cond_wait(&prn->rx_callback_data.signal);
+    result = prn->tx_callback_data.result;
+    cyg_drv_mutex_unlock(&prn->tx_callback_data.lock);
+
+    return result;
+}
+
+// --------------------------------------------------------------------------
+// Perform a synchronous transmit and wait for it to complete.
+
+int
+usbs_printer_tx(usbs_printer* prn, const void* buf, int n)
+{
+    int result;
+
+    cyg_drv_mutex_lock(&prn->tx_callback_data.lock);
+    prn->tx_callback_data.completed = false;
+
+    usbs_start_tx_buffer(prn->tx_ep, (unsigned char*) buf, n,
+                         usbs_printer_tx_complete, prn);
+
+    while (!prn->tx_callback_data.completed)
+        cyg_drv_cond_wait(&prn->rx_callback_data.signal);
+    result = prn->tx_callback_data.result;
+    cyg_drv_mutex_unlock(&prn->tx_callback_data.lock);
+
+    return result;
+}
+
+#endif      // CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+
+// --------------------------------------------------------------------------
+//                              Startup
+// --------------------------------------------------------------------------
+// Initialize a printer port structure.
+
+void
+usbs_printer_init(usbs_printer* prn, usbs_rx_endpoint* rx_ep, usbs_tx_endpoint* tx_ep)
+{
+    prn->rx_ep = rx_ep;
+    prn->rx_callback_data.result = 0;
+    prn->rx_callback_data.completed = false;
+    cyg_mutex_init(&prn->rx_callback_data.lock);
+    cyg_cond_init(&prn->rx_callback_data.signal, &prn->rx_callback_data.lock);
+
+#ifdef CYGDAT_IO_USB_SLAVE_PRINTER_TYPE_Bidirectional
+    prn->tx_ep = tx_ep;
+    prn->tx_callback_data.result = 0;
+    prn->tx_callback_data.completed = false;
+    cyg_mutex_init(&prn->tx_callback_data.lock);
+    cyg_cond_init(&prn->tx_callback_data.signal, &prn->tx_callback_data.lock);
+#endif
+}
+
+// --------------------------------------------------------------------------
+// Start the USB subsystem
+
+void
+usbs_printer_start(void)
+{
+    usbs_printer_init(&usbs_prn0, RX_EP, TX_EP);
+
+    cyg_mutex_init(&usbs_printer_lock);
+    cyg_cond_init(&usbs_printer_state_cond, &usbs_printer_lock);
+
+    // Make the mfg & product names into USB string descriptors
+
+    usbs_printer_create_str_descriptor(mfg_str_descr, 
+                                       CYGDAT_IO_USB_SLAVE_PRINTER_MFG_STR);
+    usbs_printer_create_str_descriptor(product_str_descr, 
+                                       CYGDAT_IO_USB_SLAVE_PRINTER_PRODUCT_STR);
+
+    // ----- Set up enumeration & USB callbacks -----
+
+    usbs_printer_state = usbs_printer_ep0->state;
+
+    usbs_printer_ep0->enumeration_data = &usb_enum_data;
+
+    if (usbs_printer_ep0->state_change_fn)
+        usbs_printer_app_state_change_fn = usbs_printer_ep0->state_change_fn;
+
+    usbs_printer_ep0->state_change_fn = usbs_printer_state_change_handler;
+
+    // The user can install a printer class handler, but if not already done
+    // use our default.
+    if (!usbs_printer_ep0->class_control_fn)
+        usbs_printer_ep0->class_control_fn = usbs_printer_class_handler;
+
+    // ----- Start USB subsystem -----
+
+    usbs_start(usbs_printer_ep0);
+}
+
diff -urN --exclude=CVS --exclude=.svn ecos/packages/io/usb/printer/slave/current/tests/usbprinter_ex.c usbprinter/packages/io/usb/printer/slave/current/tests/usbprinter_ex.c
--- ecos/packages/io/usb/printer/slave/current/tests/usbprinter_ex.c	1969-12-31 19:00:00.000000000 -0500
+++ usbprinter/packages/io/usb/printer/slave/current/tests/usbprinter_ex.c	2008-08-15 16:59:28.000000000 -0400
@@ -0,0 +1,145 @@
+//==========================================================================
+//
+//      usbprinter_ex.c
+//
+//      USB printer example application.
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    Frank M. Pagliughi (fmp), SoRo Systems, Inc.
+// Contributors: 
+// Date:         2008-08-08
+// Description:  USB-printer example application.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/kernel/kapi.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/infra/diag.h>
+#include <pkgconf/kernel.h>
+
+// Replace this with any other USB driver.
+#include <cyg/io/usb/usbs_at91.h>
+#include <cyg/io/usb/usbs_printer.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// This is an example application for the USB-printer layer in eCos. To the 
+// host, the device will appear as a printer. It accepts data from the host and
+// just sends it out to a local device, such as a serial port.
+// 
+// This example uses the low-level, asynchronous, USB-printer API for a fairly
+// efficient algorithm. The receiver employs ping-pong buffering, so that a
+// second packet can be recieved from the host while the first is being written out.
+
+// Comment this line out to remove debug output.
+#define DEBUG_OUTPUT
+
+#if defined(DEBUG_OUTPUT)
+#define DBG diag_printf
+#else
+#define DBG (1) ? (void)0 : diag_printf
+#endif
+
+#define BUF_SIZE 4096
+static unsigned char buf[2][BUF_SIZE];
+
+// --------------------------------------------------------------------------
+// Main Routine
+// --------------------------------------------------------------------------
+
+int main(void)
+{
+    int n, ibuf, next_buf, htx;
+
+    DBG("usbprinter_ex started\n");
+
+    // ----- Start USB subsystem -----
+
+    usbs_printer_start();
+
+    // Open the output device
+
+    htx = open("/dev/ser0", O_WRONLY);
+
+    if (htx < 0) {
+        DBG("Error opening the output device\n");
+        return 1;
+    }
+
+
+    // ----- Loop forver, echoing packets -----
+
+    while (1) {
+
+        // ----- Wait for the host to configure -----
+
+        DBG("Waiting for host to configure USB\n");
+        usbs_printer_wait_until_configured();
+        cyg_thread_delay((cyg_tick_count_t) 10);
+
+        // ----- While configured read data & send back to the host -----
+
+        DBG("USB configured\n");
+
+        ibuf = 0;
+        usbs_printer_start_rx(&usbs_prn0, buf[ibuf], BUF_SIZE);
+
+        while (usbs_printer_is_configured()) {
+
+            // Wait for a packet to arrive
+            n = usbs_printer_wait_for_rx(&usbs_prn0);
+            next_buf = ibuf ^ 1;
+
+            // Start the next packet receive while we send the one
+            // we just got.
+            usbs_printer_start_rx(&usbs_prn0, buf[next_buf], BUF_SIZE);
+
+            if (n < 0) {
+                DBG("*** I/O Error: %d ***\n", n);
+            }
+            else
+                write(htx, buf[ibuf], n);
+
+            ibuf = next_buf;
+        }
+    }
+
+    return 0;
+}
+

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]