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 serial package


This attachment contains a slave package for USB-serial devices for eCos. It's the first of a couple device-side class modules that I hope to submit in the next few months.

This package is for USB devices that mimic a serial port connection to the host. The enclosed module provides the USB descriptors and callbacks necessary to handle the enumeration with the host and also provides a simple API for the device. Note, however the resulting device mimics a serial connection on the host side, but the device sees the connection as a normal USB connection with two bulk endpoints - an IN and an OUT. The eCos application can be programmed using the provided "usbs_serial_xxx" API, the standard eCos USB API, Unix-like File I/O, or C stdio.

The module can be used as a simple way for a USB device to communicate with a Windows or Linux host without the need for custom host device drivers, and without the limitations (and annoyance) of creating a HID device. It presents a pair of Bulk endpoints for host communications, and the actual data is free-form, just like any serial connection. This can also be used as the basis for a real USB modem or a USB-serial converter.

The device can work with the standard USB serial device drivers on a Windows or Linux host, so there's no need for a custom host driver. Included are a micro Linux driver which just associates a USB VID/PID to the usbserial driver, and a Windows INF file to do the same for that host. The Linux driver requires a 2.6 kernel, while the Windows INF file should work with any version since Windows 2000.

The slave device can be configured as an ACM communications device or a 'generic' device. A windows host requires an ACM device. A Linux host should also work with ACM devices, but the latest kernels are picky about requiring class-specific descriptors. I couldn't figure out how to add class descriptors to the configuration descriptors using the existing eCos USB API.

But, the generic configuration works well with Linux, and is probably preferable anyway. For Linux hosts, 'make' the driver, then:
# modprobe usbserial
# insmod ./ecos_usbserial.ko
then plug in the device. It should be recognized and assigned a tty device, like '/dev/ttyUSB0'


For windows, copy the INF file and the 'usbser.sys' driver from your version of Windows into a directory, plug in the device, and when prompted, navigate to the INF file.

In all cases, the eCos library module must be configured with a real Vendor ID and Product ID, and the Linux driver and INF file must be updated with those matching values.

I tested this with the AT91SAM7S-EK board as the device target, but it should work with any USB-enabled eCos board.

Sorry, I don't know how to write eCos documentation, so I added some example applications instead. Let me know if there's an easy way I can document this better.

Frank Pagliughi
President,
SoRo Systems, Inc.
diff -urN --exclude=.svn ecos/packages/ecos.db usbserial/packages/ecos.db
--- ecos/packages/ecos.db	2008-01-06 09:19:30.000000000 -0500
+++ usbserial/packages/ecos.db	2008-06-03 01:41:26.000000000 -0400
@@ -1409,6 +1409,14 @@
         description     "Support for USB peripherals that provide an ethernet-class function"
 }
 
+package CYGPKG_IO_USB_SLAVE_SERIAL {
+        alias		{ "USB slave-side serial drivers" usbs_serial }
+        directory       io/usb/serial/slave
+        script          usbs_serial.cdl
+        description     "Support for USB peripherals that present themselves as serial 
+                        devices to the host."
+}
+
 package CYGPKG_DEVS_USB_SA11X0 {
         alias           { "Device-driver for the SA11X0 on-chip USB support" usb_sa11x0 }
         hardware
@@ -4534,6 +4542,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=.svn ecos/packages/io/usb/serial/slave/current/cdl/usbs_serial.cdl usbserial/packages/io/usb/serial/slave/current/cdl/usbs_serial.cdl
--- ecos/packages/io/usb/serial/slave/current/cdl/usbs_serial.cdl	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/cdl/usbs_serial.cdl	2008-06-05 12:18:17.000000000 -0400
@@ -0,0 +1,229 @@
+# ====================================================================
+#
+#      usbs_serial.cdl
+#
+#      USB slave-side serial 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_SERIAL {
+    display     "USB slave serial 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 >= 1 }
+    compile     usbs_serial.c
+    implements  CYGINT_IO_USB_SLAVE_CLIENTS
+    doc         ref/io-usb-slave-eth.html
+    
+    description "
+        The USB slave serial package supports the development
+        of USB peripherals which mimic a serial connection to 
+        the host machine. Such a device creates a relatively
+        simple upgrade to USB from a legacy serial connection,
+        especially from the perspective of the host software and
+        device drivers."
+
+    cdl_option CYGDAT_IO_USB_SLAVE_CLASS_TYPE {
+        display         "Serial USB Class"
+        flavor          data
+        default_value   { "ACM" }
+        legal_values    { "ACM" "generic" }
+        description     "
+            The USB serial module can enumerate as either a generic 
+            (vendor-specific) usb device or a communications class ACM
+            device. The generic device requires a pair of Bulk endpoints,
+            while the ACM device also requires an Interupt IN endpoint.
+            For a Windows host, the ACM configuration is required to use 
+            the syandard 'usbser.sys' device driver, but for a Linux 
+            host, the generic configurarion works, and saves the additional
+            endpoint."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_SERIAL_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_SERIAL_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_SERIAL_TX_EP_NUM {
+        display       "Tx (USB IN) endpoint number"
+        flavor        data
+        default_value 1
+        description   "
+            The endpoint that should be used for the device-side transmitter, which
+            is the USB IN direction."
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_SERIAL_TX_EP {
+        display         "The Tx (USB IN) endpoint structure"
+         flavor         data               
+         default_value  { "usbs_at91_ep1" }
+         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_SERIAL_RX_EP_NUM {
+        display         "Rx (USB OUT) endpoint number"
+        flavor          data
+        default_value   2
+        description     "
+            The endpoint that should be used for the device-side receiver, which
+            is the USB OUT direction."
+    } 
+    cdl_option CYGDAT_IO_USB_SLAVE_SERIAL_RX_EP {
+        display         "The Rx (USB OUT) endpoint structure"
+         flavor         data               
+         default_value  { "usbs_at91_ep2" }
+         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_SERIAL_INTR_EP_NUM {
+        display         "Interrupt IN endpoint number"
+        flavor          data
+        default_value   3
+        active_if       { CYGDAT_IO_USB_SLAVE_CLASS_TYPE == "ACM" }
+        description     "
+            The endpoint that should be used for the ACM Interrupt IN"
+    }
+    cdl_option CYGDAT_IO_USB_SLAVE_SERIAL_INTR_EP {
+        display         "Interrupt IN endpoint structure"
+         flavor         data               
+         default_value  { "usbs_at91_ep3" }
+         active_if      { CYGDAT_IO_USB_SLAVE_CLASS_TYPE == "ACM" }
+         description    "
+             The endpoint structure that corresponds to the selected ACM
+             Interrupt IN endpoint."
+    }
+    cdl_option CYGNUM_IO_USB_SLAVE_SERIAL_VENDOR_ID {
+        display       "USB Forum 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_SERIAL_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_SERIAL_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_SERIAL_PRODUCT_STR {
+        display       "The Device Product Name"
+        flavor        data
+        default_value { "\"eCos USB Serial Device\"" }
+        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  CYGBLD_IO_USB_SLAVE_SERIAL_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_SERIAL_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_SERIAL_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_option  CYGPKG_IO_USB_SLAVE_SERIAL_TESTS {
+        display       "Tests"
+        flavor        data
+        default_value { "tests/usb/serial" }
+    }
+}
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/examples/Makefile usbserial/packages/io/usb/serial/slave/current/examples/Makefile
--- ecos/packages/io/usb/serial/slave/current/examples/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/examples/Makefile	2008-06-05 11:09:57.000000000 -0400
@@ -0,0 +1,49 @@
+# Mostly written by Jonathan Larmour, Red Hat, Inc.
+# Reference to ecos.mak added by John Dallaway, eCosCentric Limited, 2003-01-20
+# This file is in the public domain and may be used for any purpose
+
+# Usage:   make INSTALL_DIR=/path/to/ecos/install
+
+INSTALL_DIR=$$(INSTALL_DIR) # override on make command line
+
+include $(INSTALL_DIR)/include/pkgconf/ecos.mak
+
+XCC           = $(ECOS_COMMAND_PREFIX)gcc
+XCXX          = $(XCC)
+XLD           = $(XCC)
+
+CFLAGS        = -I$(INSTALL_DIR)/include
+CXXFLAGS      = $(CFLAGS)
+LDFLAGS       = -nostartfiles -L$(INSTALL_DIR)/lib -Ttarget.ld
+
+# RULES
+
+.PHONY: all clean
+
+all: usbserial_echo usb2serial
+
+clean:
+	-rm -f usbserial_echo usb2serial
+	-rm -f usbserial_echo.o usb2serial.o
+
+%.o: %.c
+	$(XCC) -c -o $*.o $(CFLAGS) $(ECOS_GLOBAL_CFLAGS) $<
+
+%.o: %.cpp
+	$(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $<
+
+%.o: %.cxx
+	$(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $<
+
+%.o: %.C
+	$(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $<
+
+%.o: %.cc
+	$(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $<
+
+usbserial_echo: usbserial_echo.o
+	$(XLD) $(LDFLAGS) $(ECOS_GLOBAL_LDFLAGS) -o $@ $@.o
+
+usb2serial: usb2serial.o
+	$(XLD) $(LDFLAGS) $(ECOS_GLOBAL_LDFLAGS) -o $@ $@.o
+
Binary files ecos/packages/io/usb/serial/slave/current/examples/usb2serial.bin and usbserial/packages/io/usb/serial/slave/current/examples/usb2serial.bin differ
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/examples/usb2serial.c usbserial/packages/io/usb/serial/slave/current/examples/usb2serial.c
--- ecos/packages/io/usb/serial/slave/current/examples/usb2serial.c	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/examples/usb2serial.c	2008-06-05 20:05:19.000000000 -0400
@@ -0,0 +1,358 @@
+//==========================================================================
+//
+//      usb2serial.c
+//
+//      Example application for the USB serial layer in eCos.
+//
+//==========================================================================
+//####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-02
+// Description:  USB serial example application.
+//
+//####DESCRIPTIONEND####
+//===========================================================================
+
+#include <cyg/kernel/kapi.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/infra/diag.h>
+#include <pkgconf/kernel.h>
+#include <cyg/io/serialio.h>
+
+// Replace this with any other USB driver desired.
+#include <cyg/io/usb/usbs_at91.h>
+#include <cyg/io/usb/usbs_serial.h>
+#include <pkgconf/io_usb_slave_serial.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+// This application creates a USB-serial converter. To the host it will appear
+// as a single serial USB port, such as /dev/ttyUSB0 for a Linux host.
+// Any characters recieved from the USB host will be sent out the serial port
+// and visa-versa. It creates a separate, dedicated thread for each direction.
+// 
+// It uses the eCos USB-serial layer to enumerate with the USB host and monitor
+// the connection, but then uses standard C I/O functions to perform the 
+// communications.
+// 
+// The USB serial module can be configured as a generic adapter or an an ACM
+// communications class device. For the latter, the application handles the
+// USB communactions class requests which allows it to recieve requests from
+// the host to set serial parameters, like the baud rate. This actually turns
+// this example into a more realistic USB-serial adapter that can be configured
+// dynamically by the host.
+// 
+// The eCos library must be configured with the packages for USB slave, USB 
+// serial, and File I/O. It also requires the proper serial port driver for the
+// target platform.
+// 
+// This example was tested with the AT91SAM7S-EK board, but should work with any
+// board that has a USB slave and serial port, and the necessary drivers.
+
+
+// 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
+
+// Set these to the USB devtab entries for the Tx and Rx Bulk endpoints 
+// selected in the configuration of the USB serial subsystem.
+#define USB_TX_DEV  "/dev/usbs1"
+#define USB_RX_DEV  "/dev/usbs2"
+
+// Set this for any available serial port on the target.
+#define SER_DEV     "/dev/ser0"
+
+// Buffer for incoming USB bulk data. The local USB driver can probably split
+// packets, but just in case, making this the page size of the host might be
+// helpful.
+#define BUF_SIZE 4096
+static char usb2ser_buf[BUF_SIZE];
+
+// The threads
+cyg_thread thread[2];
+
+// Space for two 4K stacks
+#define THREAD_STACK_SIZE 4096
+char stack[2][THREAD_STACK_SIZE];
+
+// The handles for the threads
+cyg_handle_t    usb2ser_thread, 
+                ser2usb_thread;
+
+// --------------------------------------------------------------------------
+// For an ACM serial device we can handle the USB class messages to deal with
+// requests from the host like setting the serial parameters (baud rate, 
+// etc).
+
+#ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+
+static cyg_uint8    acm_buf[32];
+static cyg_uint32   baud = 34800;
+
+// --------------------------------------------------------------------------
+// Handler for the completion of a SetLineCoding request from the host.
+// The 'acm_buf' should contain the 7-byte request OUT packet from the
+// host. This contains a request to change the serial parameters.
+// In this example function, we will accept a few different baud rates.
+// To keep the example relatively simple, though, we keep the other serial
+// parameters to 1 stop bit, no parity, 8 data bits.
+
+static usbs_control_return
+acm_set_line_coding(usbs_control_endpoint* ep0, int n)
+{
+    int err;
+    cyg_uint32 req_baud;
+    cyg_io_handle_t handle;
+
+    // Get the requested baud rate from the received ctrl OUT packet
+    req_baud = (acm_buf[3] << 24) | (acm_buf[2] << 16) | 
+                (acm_buf[1] << 8) | acm_buf[0];
+
+    DBG("Set Baud: %u\n", (unsigned) baud);
+
+    // Look up the serial handle and attempt to set the baud rate.
+    if (cyg_io_lookup(SER_DEV, &handle) == 0) {
+        cyg_serial_info_t ser_info;
+        cyg_uint32 len = sizeof(ser_info);
+
+        switch (baud) {
+            case   9600 : ser_info.baud = CYGNUM_SERIAL_BAUD_9600;      break;
+            case  38400 : ser_info.baud = CYGNUM_SERIAL_BAUD_38400;     break;
+            case 115200 : ser_info.baud = CYGNUM_SERIAL_BAUD_115200;    break;
+            default:
+                DBG("Unsupported baud rate\n");
+                return USBS_CONTROL_RETURN_HANDLED;
+        }
+        ser_info.stop = CYGNUM_SERIAL_STOP_1;
+        ser_info.parity = CYGNUM_SERIAL_PARITY_NONE;
+        ser_info.word_length = CYGNUM_SERIAL_WORD_LENGTH_8;
+        ser_info.flags = 0;
+
+        err = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, 
+                                &ser_info, &len);
+        if (err == 0)
+            baud = req_baud;
+        else {
+            DBG("Error setting serial params\n");
+        }
+    }
+    else {
+        DBG("Error looking up serial device: %s\n", SER_DEV);
+    }
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+// --------------------------------------------------------------------------
+// Handler for the ACM class messages.
+// 
+static usbs_control_return 
+acm_class_handler(usbs_control_endpoint* ep0, void* data)
+{
+    usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+    usb_devreq  *req = (usb_devreq *) ep0->control_buffer;
+
+    static cyg_uint8 rsp_buf[32];
+
+    DBG("ACM Class Handler\n");
+
+    switch (req->request) {
+
+        case USBS_SERIAL_SET_LINE_CODING :
+                DBG("Set Line Coding\n");
+                memset(acm_buf, 0, 32);
+                ep0->buffer = acm_buf;
+                ep0->buffer_size = 7;
+                ep0->complete_fn = acm_set_line_coding;
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+        case USBS_SERIAL_GET_LINE_CODING :
+                DBG("Get Line Coding\n");
+                rsp_buf[0] = baud & 0xFF;
+                rsp_buf[1] = (baud >>  8) & 0xFF;
+                rsp_buf[2] = (baud >> 16) & 0xFF;
+                rsp_buf[3] = (baud >> 24) & 0xFF;
+                rsp_buf[4] = 0; // One stop bit
+                rsp_buf[5] = 0; // No parity
+                rsp_buf[6] = 8; // 8 data bits
+                ep0->buffer = rsp_buf;
+                ep0->buffer_size = 7;
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+        default :
+                DBG("*** Unhandled ACM Request: 0x%02X ***\n",
+                    (unsigned) req->request);
+    }
+
+    return result;
+}
+
+#endif      // CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+
+// --------------------------------------------------------------------------
+// Thread recieves packets from the USB and sends them out the serial port
+// It uses a buffered stdio input, an un-buffered low-level file output.
+// This isn't terribly efficient, but rather an example of both methods.
+
+void usb2ser_func(cyg_addrword_t data)
+{
+    int  c;
+    FILE *rxf = fopen(USB_RX_DEV, "r");
+    int   txh = open(SER_DEV, O_WRONLY, 0);
+
+    DBG("Usb2Ser: Thread starting\n");
+
+    if (!rxf) {
+        DBG("Error opening USB rx port\n");
+        return;
+    }
+
+    if (txh < 0) {
+        DBG("Error opening serial tx port\n");
+        return;
+    }
+
+    // Give the USB receiver an adequate buffer.
+    setvbuf(rxf, usb2ser_buf, _IOFBF, BUF_SIZE);
+
+    while (1) {
+
+        // ----- Wait for the host to configure -----
+
+        DBG("Usb2Ser: Waiting for USB configuration\n");
+        usbs_serial_wait_until_configured();
+        cyg_thread_delay((cyg_tick_count_t) 10);
+
+        // ----- While configured read data & send out serial port -----
+
+        DBG("Usb2Ser: USB configured\n");
+        while (usbs_serial_is_configured()) {
+            if ((c = getc(rxf)) < 0) {
+                DBG("*** USB Read Error: %d ***\n", c);
+            }
+            else {
+                char ch = (char) c;
+                write(txh, &ch, 1);
+            }
+        }
+    }
+}
+
+// --------------------------------------------------------------------------
+// Thread recieves packets from the serial port and sends them out the USB
+// It uses a buffered stdio input, an un-buffered low-level file output.
+// This isn't terribly efficient, but rather an example of both methods.
+
+void ser2usb_func(cyg_addrword_t data)
+{
+    int  c;
+    FILE *rxf = fopen(SER_DEV, "r");
+    int  txh = open(USB_TX_DEV, O_WRONLY, 0);
+
+    DBG("Ser2Usb: Thread starting\n");
+
+    if (!rxf) {
+        DBG("Error opening serial rx port\n");
+        return;
+    }
+
+    if (txh < 0) {
+        DBG("Error opening USB tx port\n");
+        return;
+    }
+
+    while (1) {
+
+        // ----- Wait for the host to configure -----
+
+        DBG("Ser2Usb: Waiting for USB configuration\n");
+        usbs_serial_wait_until_configured();
+        cyg_thread_delay((cyg_tick_count_t) 10);
+
+        // ----- While configured read data & send out serial port -----
+
+        DBG("Ser2Usb: USB configured\n");
+        while (usbs_serial_is_configured()) {
+            if ((c = getc(rxf)) < 0) {
+                DBG("*** Console Read Error: %d ***\n", c);
+            }
+            else {
+                char ch = (char) c;
+                write(txh, &ch, 1);
+            }
+        }
+    }
+}
+
+// --------------------------------------------------------------------------
+//  Application Startup
+// --------------------------------------------------------------------------
+
+void cyg_user_start(void)
+{
+    DBG("Entering cyg_user_start() function\n");
+
+#ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+    // Override the class handler to use ours.
+    usbs_serial_ep0->class_control_fn = acm_class_handler;
+#endif
+
+    cyg_thread_create(4, usb2ser_func, (cyg_addrword_t) 0,
+                      "Usb2Serial", (void *) stack[0], THREAD_STACK_SIZE,
+                      &usb2ser_thread, &thread[0]);
+
+    cyg_thread_create(4, ser2usb_func, (cyg_addrword_t) 1,
+                      "Serial2Usb", (void *) stack[1], THREAD_STACK_SIZE,
+                      &ser2usb_thread, &thread[1]);
+
+    // Start USB subsystem
+    usbs_serial_start();
+
+    // Start the threads running.
+    cyg_thread_resume(usb2ser_thread);
+    cyg_thread_resume(ser2usb_thread);
+}
+
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/examples/usbserial_echo.c usbserial/packages/io/usb/serial/slave/current/examples/usbserial_echo.c
--- ecos/packages/io/usb/serial/slave/current/examples/usbserial_echo.c	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/examples/usbserial_echo.c	2008-06-05 12:29:38.000000000 -0400
@@ -0,0 +1,94 @@
+// usb_serial_echo.cpp
+// 
+// Device-side program to make a USB slave appear like a serial port
+// that echoes characters sent to it back to the host.
+//
+// AUTHOR:
+//		Frank Pagliughi
+//
+//		SoRo Systems, Inc.
+//		www.sorosys.com
+//
+// DATE:
+//		May 2008
+//
+
+#include <cyg/kernel/kapi.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/infra/diag.h>
+#include <pkgconf/kernel.h>
+#include <cyg/io/usb/usbs_at91.h>
+#include <cyg/io/usb/usbs_serial.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEBUG_OUTPUT
+
+#if defined(DEBUG_OUTPUT)
+#define DBG diag_printf
+#else
+#define DBG (1) ? (void)0 : diag_printf
+#endif
+
+typedef unsigned char byte;
+
+// --------------------------------------------------------------------------
+//								Data & Callback(s)
+// --------------------------------------------------------------------------
+
+#define BUF_SIZE	4096
+
+static byte	rx_buf[2][BUF_SIZE],
+			tx_buf[BUF_SIZE];
+
+// --------------------------------------------------------------------------
+//								Main Routine
+// --------------------------------------------------------------------------
+
+int main(void)
+{
+	int			n;
+	unsigned	ibuf,
+				next_buf;
+
+	// ----- Start USB subsystem -----
+
+	usbs_serial_start();
+
+ 	// ----- Get data from host and send it back -----
+
+	while (1) {
+		ibuf = 0;
+
+		// ----- Wait for the host to configure -----
+
+		usbs_serial_wait_until_configured();
+		cyg_thread_delay((cyg_tick_count_t) 10);
+
+		// ----- While configured read data & print to screen -----
+
+		usbs_serial_start_rx(&usbs_ser0, rx_buf[ibuf], BUF_SIZE);
+
+		while (usbs_serial_is_configured()) {
+
+			n = usbs_serial_wait_for_rx(&usbs_ser0);
+			next_buf = ibuf ^ 1;
+
+			usbs_serial_start_rx(&usbs_ser0, rx_buf[next_buf], BUF_SIZE);
+
+			if (n < 0) {
+				DBG("*** I/O Error: %d ***\n", n);
+			}
+			else {
+				memcpy(tx_buf, rx_buf[ibuf], n);
+				usbs_serial_tx(&usbs_ser0, tx_buf, n);
+			}
+
+			ibuf = next_buf;
+		}
+	}
+
+	return 0;
+}
+
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/host/linux/ecos_usbserial.c usbserial/packages/io/usb/serial/slave/current/host/linux/ecos_usbserial.c
--- ecos/packages/io/usb/serial/slave/current/host/linux/ecos_usbserial.c	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/host/linux/ecos_usbserial.c	2008-06-03 01:39:37.000000000 -0400
@@ -0,0 +1,66 @@
+// ecos_usbserial.c
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define VENDOR_ID	0xFFFF
+#define PRODUCT_ID	1
+
+static struct usb_device_id id_table[] = {
+	{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver ecos_usbserial_driver = {
+	.name	 	= "ecos_usbserial",
+	.probe		= usb_serial_probe,
+	.disconnect	= usb_serial_disconnect,
+	.id_table	= id_table
+};
+
+static struct usb_serial_driver ecos_usbserial_device = {
+	.driver = {
+		.owner			= THIS_MODULE,
+		.name			= "ecos_usbserial",
+	},
+	.id_table			= id_table, 
+	.num_interrupt_in	= NUM_DONT_CARE,
+	.num_bulk_in		= NUM_DONT_CARE,
+	.num_bulk_out		= NUM_DONT_CARE,
+	.num_ports			= 1
+};
+
+static int __init ecos_usbserial_init(void)
+{
+	int retval;
+
+	retval = usb_serial_register(&ecos_usbserial_device);
+	if (retval)
+		return retval;
+
+	retval = usb_register(&ecos_usbserial_driver);
+	if (retval) {
+		usb_serial_deregister(&ecos_usbserial_device);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void __exit ecos_usbserial_exit(void)
+{
+	usb_deregister(&ecos_usbserial_driver);
+	usb_serial_deregister(&ecos_usbserial_device);
+}
+
+module_init(ecos_usbserial_init);
+module_exit(ecos_usbserial_exit);
+
+MODULE_LICENSE("GPL");
+
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/host/linux/Makefile usbserial/packages/io/usb/serial/slave/current/host/linux/Makefile
--- ecos/packages/io/usb/serial/slave/current/host/linux/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/host/linux/Makefile	2008-06-03 01:39:37.000000000 -0400
@@ -0,0 +1,11 @@
+# Makefile for ecos_usbserial Linux kernel module
+
+ifneq ($(KERNELRELEASE),)
+	obj-m := ecos_usbserial.o
+else
+	KDIR ?= /lib/modules/$(shell uname -r)/build
+	PWD := $(shell pwd)
+default:
+	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+endif
+
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/host/windows/eCosUsbSerial.inf usbserial/packages/io/usb/serial/slave/current/host/windows/eCosUsbSerial.inf
--- ecos/packages/io/usb/serial/slave/current/host/windows/eCosUsbSerial.inf	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/host/windows/eCosUsbSerial.inf	2008-06-05 11:09:57.000000000 -0400
@@ -0,0 +1,49 @@
+; eCos USB Serial INF file for Windows
+; This can be used for any USB device that emulates a serial port connection.
+; Simply set the VID & PID numbers in this file to the proper values for the
+; device, copy this file and the 'usbser.sys' file from your version of Windows
+; into the same directory, and load these when prompted by the Windows device
+; manager.
+
+[Version]
+Signature="$Windows NT$"
+Class=Ports
+ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider=eCosUsbSerial
+DriverVer=06/02/2008,0.0.0.1
+
+[DestinationDirs]
+DefaultDestDir=12	; %windir$\System32\drivers
+
+[Manufacturer]
+eCosUsbSerial=eCosDevices
+
+[eCosDevices]
+"eCosUsbSerial"=InstalleCosUsbSerial,USB\VID_FFFF&PID_0001
+
+[InstalleCosUsbSerial]
+CopyFiles=CopyeCosUsbFiles
+AddReg=eCosUsbReg
+
+[CopyeCosUsbFiles]
+usbser.sys			; The standard Windows USB serial driver
+
+[eCosUsbReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,usbser.sys
+HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
+
+[InstalleCosUsbSerial.Services]
+AddService = usbser,2,eCosUsbSerialService
+
+[eCosUsbSerialService]
+DisplayName = ECOS_USB_SERIAL_NAME
+ServiceType = 1				; driver
+StartType = 3				; on-demand or manual
+ErrorControl = 1			; report errors
+ServiceBinary = %12%\usbser.sys		; Driver path: %windir%\System32\drivers
+LoadOrderGroup = Base
+
+[Strings]
+ECOS_USB_SERIAL_NAME = "eCos USB Serial Driver"
+
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/include/usbs_serial.h usbserial/packages/io/usb/serial/slave/current/include/usbs_serial.h
--- ecos/packages/io/usb/serial/slave/current/include/usbs_serial.h	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/include/usbs_serial.h	2008-06-05 17:57:43.000000000 -0400
@@ -0,0 +1,184 @@
+#ifndef CYGONCE_USBS_SERIAL_H
+#define CYGONCE_USBS_SERIAL_H
+//==========================================================================
+//
+//      include/usbs_serial.h
+//
+//      Description of the USB slave-side serial device 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-06-02
+// Purpose:
+// Description:  USB slave-side serial support
+//
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+//
+// The primary purpose of the USB slave-side serial code is to provide a 
+// simple USB connection to the host, especially for embedded systems that
+// are upgrading from RS-232 serial connections. The host would see the 
+// device as if through a serial port, and thus the host software would
+// remain unchanged. It would also eliminate the need for a new device
+// driver on the host.
+// 
+// On this side (the eCos USB slave side), the application sees the host
+// through a normal USB slave connection with two Bulk 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 serial
+// port, 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 serial API defined here.
+// 
+// The USB serial API is a thin layer over the standard eCos USB functions
+// to provide common synchronous and asynchronous transfers over the assigned
+// Bulk endpoints.
+//
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/io/usb/usbs.h>
+
+// ----------------------------------------------------------------------------
+// The ACM class requests
+// 
+
+#define USBS_SERIAL_SEND_ENCAPSULATED_COMMAND   0x00
+#define USBS_SERIAL_GET_ENCAPSULATED_RESPONSE   0x01
+#define USBS_SERIAL_SET_COMM_FEATURE            0x02
+#define USBS_SERIAL_GET_COMM_FEATURE            0x03
+#define USBS_SERIAL_CLEAR_COMM_FEATURE          0x04
+
+#define USBS_SERIAL_SET_LINE_CODING             0x20
+#define USBS_SERIAL_GET_LINE_CODING             0x21
+#define USBS_SERIAL_SET_CONTROL_LINE_STATE      0x22
+#define USBS_SERIAL_SEND_BREAK                  0x23
+
+// ----------------------------------------------------------------------------
+// Data structure to manage the pair of USB endpoints that comprise a single
+// serial port connection. Each "port" requires one Bulk IN endpoint and one
+// Bulk OUT endpoint.
+
+typedef struct usbs_serial {
+    // The communication endpoints. For the first (default) channel, these
+    // are normally set by the configuration, but can be changed by the
+    // application, if desired.
+    usbs_tx_endpoint*   tx_ep;
+    usbs_rx_endpoint*   rx_ep;
+
+    // The signal that a transmit operation is complete, and it's result.
+    cyg_sem_t   tx_ready;
+    int         tx_result;
+
+    // The signal that a receive operation is complete, and it's result.
+    cyg_sem_t   rx_ready;
+    int         rx_result;
+
+} usbs_serial;
+
+// The package constains one USB serial device.
+extern usbs_serial usbs_ser0;
+
+// 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 serial 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_serial_ep0;
+
+// ----------------------------------------------------------------------------
+// A C interface to the serial 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 serial "port"
+// This associates a usbs_serial structure with specific endpoints and 
+// initializes the structure for communications.
+void usbs_serial_init(usbs_serial*, usbs_tx_endpoint*, usbs_rx_endpoint*);
+
+// Block the calling thread until the host configures the USB device.
+void usbs_serial_wait_until_configured(void);
+
+// Determines if the USB substsrem is configured
+cyg_bool usbs_serial_is_configured(void);
+
+// Start an asynchronous transmit of a single buffer.
+void usbs_serial_start_tx(usbs_serial*, const void* buf, int n);
+
+// Block the calling thread until the transmit completes.
+// Returns the result code for the transfer
+int usbs_serial_wait_for_tx(usbs_serial*);
+
+// Blocking, synchronous transmit of a single buffer.
+int usbs_serial_tx(usbs_serial*, const void* buf, int n);
+
+// Start an asynchronous receive of a buffer.
+void usbs_serial_start_rx(usbs_serial*, void* buf, int n);
+
+// Block the calling thread until the receive completes.
+// Returns the result code for the transfer
+int usbs_serial_wait_for_rx(usbs_serial*);
+
+// Blocking, synchronous receive of a single buffer.
+int usbs_serial_rx(usbs_serial*, void* buf, int n);
+
+// The default USB-serial state change handler paces the functions
+// usbs_serial_wait_until_configured() and usbs_serial_is_configured().
+// The application can override the state chain handler, but chain to 
+// this function to keep the full USP-serial system working.
+void usbs_serial_state_change_handler(usbs_control_endpoint*, void*, 
+                                      usbs_state_change, int);
+
+// Starts the USB subsystem
+void usbs_serial_start(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // CYGONCE_USBS_SERIAL_H
+
diff -urN --exclude=.svn ecos/packages/io/usb/serial/slave/current/src/usbs_serial.c usbserial/packages/io/usb/serial/slave/current/src/usbs_serial.c
--- ecos/packages/io/usb/serial/slave/current/src/usbs_serial.c	1969-12-31 19:00:00.000000000 -0500
+++ usbserial/packages/io/usb/serial/slave/current/src/usbs_serial.c	2008-06-05 20:38:06.000000000 -0400
@@ -0,0 +1,544 @@
+//==========================================================================
+//
+//      usbs_serial.c
+//
+//      Support for slave-side USB serial 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_serial.h>
+#include <cyg/io/usb/usbs_serial.h>
+#include <string.h>
+
+#if defined(CYGBLD_IO_USB_SLAVE_SERIAL_DEBUG)
+#define DBG diag_printf
+#else
+#define DBG (1) ? (void)0 : diag_printf
+#endif
+
+#define EP0_MAX_PACKET_SIZE     CYGNUM_IO_USB_SLAVE_SERIAL_EP0_MAX_PACKET_SIZE
+
+extern usbs_control_endpoint    CYGDAT_IO_USB_SLAVE_SERIAL_EP0;
+extern usbs_tx_endpoint         CYGDAT_IO_USB_SLAVE_SERIAL_TX_EP;
+extern usbs_rx_endpoint         CYGDAT_IO_USB_SLAVE_SERIAL_RX_EP;
+
+#define TX_EP_NUM               CYGNUM_IO_USB_SLAVE_SERIAL_TX_EP_NUM
+#define RX_EP_NUM               CYGNUM_IO_USB_SLAVE_SERIAL_RX_EP_NUM
+#define INTR_EP_NUM             CYGNUM_IO_USB_SLAVE_SERIAL_INTR_EP_NUM
+#define EP0                     (&CYGDAT_IO_USB_SLAVE_SERIAL_EP0)
+#define TX_EP                   (&CYGDAT_IO_USB_SLAVE_SERIAL_TX_EP)
+#define RX_EP                   (&CYGDAT_IO_USB_SLAVE_SERIAL_RX_EP)
+#define INTR_EP                 (&CYGDAT_IO_USB_SLAVE_SERIAL_INTR_EP)
+
+
+#define VENDOR_ID               CYGNUM_IO_USB_SLAVE_SERIAL_VENDOR_ID
+#define PRODUCT_ID              CYGNUM_IO_USB_SLAVE_SERIAL_PRODUCT_ID
+
+#define USB_MAX_STR_LEN         256
+
+#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_CLASS_TYPE_ACM
+    #define USBS_SERIAL_DEVICE_CLASS        2
+    #define USBS_SERIAL_NUM_IFACE           2
+    #define USBS_SERIAL_NUM_ENDP            3
+    #define USBS_SERIAL_DATA_IFACE_CLASS    0x0A    // Data
+#else
+    #define USBS_SERIAL_DEVICE_CLASS        0
+    #define USBS_SERIAL_NUM_IFACE           1
+    #define USBS_SERIAL_NUM_ENDP            2
+    #define USBS_SERIAL_DATA_IFACE_CLASS    0xFF    // Vendor
+#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(USBS_SERIAL_NUM_IFACE, USBS_SERIAL_NUM_ENDP),
+    total_length_hi:    
+        USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_HI(USBS_SERIAL_NUM_IFACE, USBS_SERIAL_NUM_ENDP),
+    number_interfaces:  USBS_SERIAL_NUM_IFACE,
+    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[] = {
+
+#ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+    {
+        length:             sizeof(usb_interface_descriptor),
+        type:               USB_INTERFACE_DESCRIPTOR_TYPE,
+        interface_id:       0,
+        alternate_setting:  0,
+        number_endpoints:   1,
+        interface_class:    0x02,   // Comm class
+        interface_subclass: 0x02,
+        interface_protocol: 0x01,
+        interface_str:      0x00
+    },
+    {
+        length:             sizeof(usb_interface_descriptor),
+        type:               USB_INTERFACE_DESCRIPTOR_TYPE,
+        interface_id:       1,
+        alternate_setting:  0,
+        number_endpoints:   2,
+        interface_class:    USBS_SERIAL_DATA_IFACE_CLASS,
+        interface_subclass: 0x00,
+        interface_protocol: 0x00,
+        interface_str:      0x00
+    }
+#else
+    {
+        length:             sizeof(usb_interface_descriptor),
+        type:               USB_INTERFACE_DESCRIPTOR_TYPE,
+        interface_id:       0,
+        alternate_setting:  0,
+        number_endpoints:   2,
+        interface_class:    USBS_SERIAL_DATA_IFACE_CLASS,
+        interface_subclass: 0x00,
+        interface_protocol: 0x00,
+        interface_str:      0x00
+    }
+#endif
+};
+
+// ----- Endpoint Descriptors -----
+
+static const usb_endpoint_descriptor usb_endpoints[] =
+{ 
+#ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+    // Interrupt in endpoint
+    {
+        sizeof(usb_endpoint_descriptor),
+        USB_ENDPOINT_DESCRIPTOR_TYPE,
+        USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN | INTR_EP_NUM,
+        USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+        0x40,
+        0,
+        255
+    },
+#endif
+
+    // Tx (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
+    },
+
+    // Rx (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
+    }
+};
+
+// ----- 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_SERIAL_DEVICE_CLASS,
+        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:    USBS_SERIAL_NUM_IFACE,
+    total_number_endpoints:     USBS_SERIAL_NUM_ENDP,
+    total_number_strings:       3,
+    configurations:             &usb_configuration,
+    interfaces:                 usb_interface,
+    endpoints:                  usb_endpoints,
+    strings:                    (const unsigned char **) usb_strings
+};
+
+// --------------------------------------------------------------------------
+// USBS Serial Data
+// --------------------------------------------------------------------------
+
+usbs_control_endpoint* usbs_serial_ep0 = EP0;
+
+// Lock for the state.
+cyg_mutex_t usbs_serial_lock;   
+
+// Condition variable for state changes
+cyg_cond_t  usbs_serial_state_cond;
+
+int usbs_serial_state;
+
+usbs_serial usbs_ser0 = {
+    tx_ep:      TX_EP,
+    rx_ep:      RX_EP,
+    tx_result:  0,    
+    rx_result:  0,    
+};
+
+static void (*usbs_serial_app_state_change_fn)(struct usbs_control_endpoint*, 
+                                               void*, usbs_state_change, int) = 0;
+
+// --------------------------------------------------------------------------
+// Create a USB String Descriptor from a C string.
+
+void
+usbs_serial_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';
+    }
+}
+
+// --------------------------------------------------------------------------
+// ACM Class Handler
+//
+// For a Windows host, the device must, at least, respond to a SetLineCoding
+// request (0x20), otherwise Windows will report that it's unable to open the 
+// port. This request normally sets the standard serial parameters:
+//          baud rate, # stop bits, parity, and # data bits
+// If we're just making believe that we're a serial port to communicate with
+// the host via USB, then these values don't matter. So we ACK the request,
+// but ignore the parameters.
+// If we were actually creating a USB-serial converter, then we would need to
+// read these values and confire the serial port accordingly.
+// 
+// Similarly, the host can request the current settings through a 
+// GetLineCoding request (0x21). Since we're just faking it, we return some
+// arbitrary values: 38400,1,N,8
+
+#ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+
+static usbs_control_return 
+usbs_serial_acm_class_handler(usbs_control_endpoint* ep0, void* data)
+{
+	usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+	DBG("USB Serial ACM Class Handler: ");
+
+	usb_devreq	*req = (usb_devreq *) ep0->control_buffer;
+
+    static cyg_uint8 rsp_buf[32];
+    cyg_uint32 baud = 38400;  // Arbitrary, fake value to return to the host.
+
+	switch (req->request) {
+
+        case USBS_SERIAL_SET_LINE_CODING :
+                DBG("ACM Request: Set Line Coding\n");
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+        case USBS_SERIAL_GET_LINE_CODING :
+                DBG("ACM Request: Get Line Coding\n");
+                rsp_buf[0] = BYTE0_32(baud);
+                rsp_buf[1] = BYTE1_32(baud);
+                rsp_buf[2] = BYTE2_32(baud);
+                rsp_buf[3] = BYTE3_32(baud);
+                rsp_buf[4] = 0; // One stop bit
+                rsp_buf[5] = 0; // No parity
+                rsp_buf[6] = 8; // 8 data bits
+				ep0->buffer = rsp_buf;
+				ep0->buffer_size = 7;
+                result = USBS_CONTROL_RETURN_HANDLED;
+                break;
+
+		default :
+				DBG("*** Unhandled ACM Request: 0x%02X ***\n",
+					(unsigned) req->request);
+	}
+
+	return result;
+}
+#endif      // CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+
+// --------------------------------------------------------------------------
+// Callback for a USB state change
+
+void
+usbs_serial_state_change_handler(usbs_control_endpoint* ep, void* data,
+                                 usbs_state_change change, int prev_state)
+{
+    #if defined(CYGBLD_IO_USB_SLAVE_SERIAL_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_serial_state = usbs_serial_ep0->state;
+    cyg_cond_broadcast(&usbs_serial_state_cond);
+
+    if (usbs_serial_app_state_change_fn)
+        (*usbs_serial_app_state_change_fn)(ep, data, change, prev_state);
+}
+
+// --------------------------------------------------------------------------
+// Block the calling thread until the USB is configured.
+ 
+void
+usbs_serial_wait_until_configured(void)
+{
+    cyg_mutex_lock(&usbs_serial_lock);
+    while (usbs_serial_state != USBS_STATE_CONFIGURED)
+      cyg_cond_wait(&usbs_serial_state_cond);
+    cyg_mutex_unlock(&usbs_serial_lock);
+}
+
+// --------------------------------------------------------------------------
+// Determine if the device is currently configured.
+
+cyg_bool
+usbs_serial_is_configured(void)
+{
+    return usbs_serial_state == USBS_STATE_CONFIGURED;
+}
+
+// --------------------------------------------------------------------------
+// Callback for when a transmit is complete
+
+static void 
+usbs_serial_tx_complete(void *p, int result)
+{
+    usbs_serial* ser = (usbs_serial*) p;
+    ser->tx_result = result;
+    cyg_semaphore_post(&ser->tx_ready);
+}
+
+// --------------------------------------------------------------------------
+// Callback for when a receive is complete
+
+static void 
+usbs_serial_rx_complete(void *p, int result)
+{
+    usbs_serial* ser = (usbs_serial*) p;
+    ser->rx_result = result;
+    cyg_semaphore_post(&ser->rx_ready);
+}
+
+// --------------------------------------------------------------------------
+// Start an asynchronous transmit of a buffer.
+// 
+ 
+void
+usbs_serial_start_tx(usbs_serial* ser, const void* buf, int n)
+{
+    usbs_start_tx_buffer(ser->tx_ep, (unsigned char*) buf, n,
+                          usbs_serial_tx_complete, ser);
+}
+
+// --------------------------------------------------------------------------
+// Block the caller until the transmit is complete
+
+int
+usbs_serial_wait_for_tx(usbs_serial* ser)
+{
+    cyg_semaphore_wait(&ser->tx_ready);
+    return ser->tx_result;
+}
+
+// --------------------------------------------------------------------------
+// Perform a synchronous transmit and wait for it to complete.
+
+int
+usbs_serial_tx(usbs_serial* ser, const void* buf, int n)
+{
+    usbs_serial_start_tx(ser, buf, n);
+    return usbs_serial_wait_for_tx(ser);
+}
+
+// --------------------------------------------------------------------------
+// Start an asynchronous receive of a buffer.
+
+void
+usbs_serial_start_rx(usbs_serial* ser, void* buf, int n)
+{
+    usbs_start_rx_buffer(ser->rx_ep, (unsigned char*) buf, n,
+                          usbs_serial_rx_complete, ser);
+}
+
+// --------------------------------------------------------------------------
+// Block the caller until the receive is complete
+
+int
+usbs_serial_wait_for_rx(usbs_serial* ser)
+{
+    cyg_semaphore_wait(&ser->rx_ready);
+    return ser->rx_result;
+}
+
+// --------------------------------------------------------------------------
+// Perform a synchronous receive and wait for it to complete.
+
+int
+usbs_serial_rx(usbs_serial* ser, void* buf, int n)
+{
+    usbs_serial_start_rx(ser, buf, n);
+    return usbs_serial_wait_for_rx(ser);
+}
+
+// --------------------------------------------------------------------------
+// Initialize a serial port structure.
+
+void
+usbs_serial_init(usbs_serial* ser, usbs_tx_endpoint* tx_ep, usbs_rx_endpoint* rx_ep)
+{
+    ser->tx_ep = tx_ep;
+    ser->rx_ep = rx_ep;
+
+    cyg_semaphore_init(&ser->tx_ready, 0);
+    cyg_semaphore_init(&ser->rx_ready, 0);
+}
+
+// --------------------------------------------------------------------------
+// Start the USB subsystem
+
+void
+usbs_serial_start(void)
+{
+    usbs_serial_init(&usbs_ser0, TX_EP, RX_EP);
+
+    cyg_mutex_init(&usbs_serial_lock);
+    cyg_cond_init(&usbs_serial_state_cond, &usbs_serial_lock);
+
+    // Make the mfg & product names into USB string descriptors
+
+    usbs_serial_create_str_descriptor(mfg_str_descr, 
+                                      CYGDAT_IO_USB_SLAVE_SERIAL_MFG_STR);
+    usbs_serial_create_str_descriptor(product_str_descr, 
+                                      CYGDAT_IO_USB_SLAVE_SERIAL_PRODUCT_STR);
+
+    // ----- Set up enumeration & USB callbacks -----
+
+    usbs_serial_state = usbs_serial_ep0->state;
+
+    usbs_serial_ep0->enumeration_data   = &usb_enum_data;
+
+    if (usbs_serial_ep0->state_change_fn)
+        usbs_serial_app_state_change_fn = usbs_serial_ep0->state_change_fn;
+
+    usbs_serial_ep0->state_change_fn = usbs_serial_state_change_handler;
+
+#ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
+    if (!usbs_serial_ep0->class_control_fn)
+        usbs_serial_ep0->class_control_fn = usbs_serial_acm_class_handler;
+#endif
+
+    // ----- Start USB subsystem -----
+
+    usbs_start(usbs_serial_ep0);
+}
+

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