# HG changeset patch # User jld # Date 1289567946 0 # Node ID faa94c77900cac06a4108c8d25b1389607d67cc7 # Parent 1a427c29fd0fccca717e6929bfdc328a44dc5b19 * ecos.db, io/usb/msd/slave/current/*: Initial check-in of USB mass storage function driver. Contributed by Christophe Coutand. [ Bugzilla 1000987 ] diff --git a/packages/ChangeLog b/packages/ChangeLog --- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,11 @@ +2010-11-12 John Dallaway + + * ecos.db: Add USB mass storage function driver package. + +2010-10-27 John Dallaway + + * ecos.db: Add STM32 USB slave driver package. + 2010-05-30 Christophe Coutand * ecos.db: Add AT91 ADC driver. diff --git a/packages/NEWS b/packages/NEWS --- a/packages/NEWS +++ b/packages/NEWS @@ -1,3 +1,5 @@ +* USB mass storage function driver by Christophe Coutand +* AT91 USB slave driver halting endpoint support by Christophe Coutand * USB slave device driver for STM32 by Chris Holgate * Support for dynamic data endpoint configuration in USB serial function device package by John Dallaway diff --git a/packages/ecos.db b/packages/ecos.db --- a/packages/ecos.db +++ b/packages/ecos.db @@ -1533,6 +1533,14 @@ package CYGPKG_IO_USB_SLAVE_SERIAL { as serial devices to the host." } +package CYGPKG_IO_USB_SLAVE_MSD { + alias { "USB slave-side mass-storage drivers" usbs_msd } + directory io/usb/msd/slave + script usbs_msd.cdl + description "Support for USB peripherals that present themselves + as mass storage devices to the host." +} + package CYGPKG_DEVS_USB_SA11X0 { alias { "Device-driver for the SA11X0 on-chip USB support" usb_sa11x0 } hardware diff --git a/packages/io/usb/msd/slave/current/ChangeLog b/packages/io/usb/msd/slave/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/ChangeLog @@ -0,0 +1,36 @@ +2010-11-12 Christophe Coutand + + *include/usbs_msd_opcode.h + *include/usbs_msd_io.h + *include/usbs_msd.h + *include/usbs_msd.inl + *include/usbs_msd_scsi.h + *tests/usbs_test_ramdisk.c + *src/usbs_msd_handler.c + *src/usbs_msd_scsi.c + *src/usbs_msd.c + *cdl/usbs_msd.cdl: Initial contribution. [ Bugzilla 1000987 ] + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010 Free Software Foundation, Inc. +// +// This program 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. +// +// This program 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 this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/io/usb/msd/slave/current/cdl/usbs_msd.cdl b/packages/io/usb/msd/slave/current/cdl/usbs_msd.cdl new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/cdl/usbs_msd.cdl @@ -0,0 +1,363 @@ +# ==================================================================== +# +# usbs_msd.cdl +# +# USB slave-side mass storage package. +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2008, 2009, 2010 Free Software Foundation, 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., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +## +## 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): USB Slave Mass Storage, ccoutand +# +# Contributors: jld +# Date: 2010-06-04 +# +#####DESCRIPTIONEND#### +# ==================================================================== + +cdl_package CYGPKG_IO_USB_SLAVE_MSD { + + display "USB slave mass storage support" + include_dir "cyg/io/usb" + parent CYGPKG_IO_USB_SLAVE + + requires { CYGPKG_KERNEL } + requires { CYGPKG_IO_DISK } + requires { CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS >= 1 } + requires { CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS >= 1 } + + compile usbs_msd.c usbs_msd_handler.c usbs_msd_scsi.c + + implements CYGINT_IO_USB_SLAVE_CLIENTS + + define_proc { + puts $::cdl_system_header "#define CYGDAT_IO_USB_SLAVE_MSD_INL " + } + + description " + The USB slave mass storage device (MSD) package supports + the development of USB peripherals which act has mass storage + device to the host machine. The actual storage must be implemented + on top of the disk package." + + # + # Mass Storage Device 0 + # + cdl_component CYGPKG_IO_USB_SLAVE_MSD0 { + display "USB slave mass storage support for device 0" + flavor bool + default_value 1 + description " + This option implement the USB MSD device 0." + + cdl_option CYGDAT_IO_USB_SLAVE_MSD0_SUB_CLASS_TYPE { + display "Mass-storage Device USB Sub Class" + flavor data + default_value { "SCSI" } + legal_values { "SCSI" "RBC" } + description " + Specify the transport protocols and command code systems + transported by the interface. Currently only the SCSI + transparent command set is implemented." + } + + cdl_option CYGDAT_IO_USB_SLAVE_MSD0_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_MSD0_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_component CYGPKG_IO_USB_SLAVE_MSD0_STATIC_EP { + display "Use static I/O endpoint structures" + flavor bool + default_value 1 + description " + Enables the use of static I/O endpoint structures. Some + USB slave device drivers implement dynamic endpoint + configuration and therefore require this component to be + disabled." + + cdl_option CYGDAT_IO_USB_SLAVE_MSD0_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 CYGDAT_IO_USB_SLAVE_MSD0_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_ep2, + usbs_sa11x0_ep2, etc" + } + } + + cdl_option CYGNUM_IO_USB_SLAVE_MSD0_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 CYGNUM_IO_USB_SLAVE_MSD0_TX_EP_MAX_PACKET_SIZE { + display "Tx (USB IN) endpoint sze" + flavor data + default_value 64 + legal_values { 32 64 128 256 512 } + description " + The size of the Tx EP hardware buffer on the specific USB + chip used." + } + + cdl_option CYGNUM_IO_USB_SLAVE_MSD0_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 CYGNUM_IO_USB_SLAVE_MSD0_RX_EP_MAX_PACKET_SIZE { + display "Rx (USB OUT) endpoint size" + flavor data + default_value 64 + legal_values { 32 64 128 256 512} + description " + The size of the Rx EP hardware buffer on the specific USB + chip used." + } + + cdl_option CYGNUM_IO_USB_SLAVE_MSD0_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_MSD0_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_MSD0_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_MSD0_PRODUCT_STR { + display "The Device Product Name" + flavor data + default_value { "\"eCos USB Mass Storage 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 CYGDAT_IO_USB_SLAVE_MSD0_SERIAL_STR { + display "The Device Product Serial Number" + flavor data + default_value { "\"123456789AB\"" } + description " + The standard USB enumeration allows for a serial number." + } + + cdl_option CYGDAT_IO_USB_SLAVE_MSD0_LUN0_NAME { + display "LUN0 entry name" + flavor data + default_value { "\"/dev/ramdisk0/1\"" } + description " + Specify the logical unit 0 devtab entry name. The default value + correspond to the partition 1 of the RAM disk created in the + USBS mass storage example application." + } + + cdl_option CYGOPT_IO_USB_SLAVE_MSD0_BUSPOWERED { + display "The Device is bus powered" + default_value 0 + flavor bool + description " + Tells the host whether the Device is a bus powered + device or a self powered device." + } + + cdl_option CYGNUM_IO_USB_SLAVE_MSD0_CURRENTDRAW { + display "Maximum current (in mA) drawn from USB bus" + flavor data + default_value 100 + legal_values 1 to 500 + requires { (CYGNUM_IO_USB_SLAVE_MSD_CURRENTDRAW > 100) + implies CYGOPT_IO_USB_SLAVE_MSD_BUSPOWERED } + description " + The maximum current drawn by the Device from the USB bus. + It should report the peak value. A self powered device + can draw up to 100mA, a bus powered device can draw up to + 500mA. (If it is sometimes below 100mA and sometimes over, + then a transition could be done at runtime between self and + bus powered modes, but that would be complicated and the + device would have to return from configured to addressed + state. For details, see the USB specification.)" + } + + } + + cdl_option CYGBLD_IO_USB_SLAVE_MSD_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_option CYGBLD_IO_USB_SLAVE_MSD_TRACE { + display "Enable trace output from the driver" + default_value 0 + flavor bool + description " + Allow tracing of every USB transaction for debugging + purpose." + } + + cdl_option CYGSEM_IO_USB_SLAVE_MSD_STALL_ENABLE { + display "Enable application to stall endpoints" + default_value 0 + flavor bool + description " + A mass storage device must stall one or both bulk endpoints in + several situations. However, not all USB device drivers seems + to implement this functionality correctly. Hence this option is + disable be default. Testing shows that both Linux and Windows can + handle mass storage with stall disable." + } + + cdl_component CYGPKG_IO_USB_SLAVE_MSD_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_MSD_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_SLAVE_MSD_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_MSD_EXAMPLES { + display "Build example programs" + no_define + description " + Enabling this option will cause the example program + to be built using the normal test case infrastructure." + + cdl_option CYGPKG_IO_USB_SLAVE_MSD_TESTS { + display "USBS MSD example/test program" + no_define + flavor data + calculated { "tests/usbs_test_ramdisk" } + description " + usbs_test_ramdisk creates a tiny FAT12 file-system + on top of a 'ram disk' device. The USB MSD service is + started and the disk should appear at the host side as + a regular mass storage device. If the FAT file-system + package is included, some files will be created prior to + the USB MSD device to be started." + } + } + + } +} diff --git a/packages/io/usb/msd/slave/current/include/usbs_msd.h b/packages/io/usb/msd/slave/current/include/usbs_msd.h new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/include/usbs_msd.h @@ -0,0 +1,282 @@ +#ifndef CYGONCE_USBS_MSD_H +#define CYGONCE_USBS_MSD_H +//========================================================================== +// +// include/usbs_msd.h +// +// Description of the USB slave-side MSD +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// Contributors: +// Date: 2010-06-03 +// Purpose: +// Description: USB slave-side MSD support +// +// +//####DESCRIPTIONEND#### +//========================================================================== + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// ---------------------------------------------------------------------------- +// The Mass Storage class requests +// +#define USBS_MSD_CLASS_REQ_ADSC 0x00 +#define USBS_MSD_CLASS_REQ_GET_REQUEST 0xFC +#define USBS_MSD_CLASS_REQ_PUT_REQUEST 0xFD +#define USBS_MSD_CLASS_REQ_GET_MAX_LUN 0xFE +#define USBS_MSD_CLASS_REQ_BOMSR 0xFF + +// ---------------------------------------------------------------------------- +// Subclass values +// +#define USBS_MSD_SUB_CLASS_RBC 0x01 // Reduced Block Commands, typically for flash device +#define USBS_MSD_SUB_CLASS_SFF8020I 0x02 // For CDROM devices +#define USBS_MSD_SUB_CLASS_QIC_157 0x03 // QIC-157, Tape devices +#define USBS_MSD_SUB_CLASS_UFI 0x04 // For Floppy Drive Device +#define USBS_MSD_SUB_CLASS_SFF8070I 0x05 // For Floppy Drive Device +#define USBS_MSD_SUB_CLASS_SCSI 0x06 // SCSI transparent + +#define USBS_MSD_DATA_IFACE_CLASS 0x8 // mass-storage + +// ---------------------------------------------------------------------------- +// Command Wrapper values +// +#define USBS_MSD_CSW_STATUS_PASSED 0x0 +#define USBS_MSD_CSW_STATUS_FAILED 0x1 + +#define USBS_MSD_CBW_SIGNATURE 0x43425355 +#define USBS_MSD_CSW_SIGNATURE 0x53425355 + +#define USBS_MSD_CBW_HOST2DEVICE 0x00 +#define USBS_MSD_CBW_DEVICE2HOST 0x80 + +#define USBS_MSD_DEVICE_CLASS 0 +#define USBS_MSD_NUM_IFACE 1 +#define USBS_MSD_NUM_ENDP 2 + +// ---------------------------------------------------------------------------- +// Helper macros +// +#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 USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH(interfaces, endpoints) \ + (USB_CONFIGURATION_DESCRIPTOR_LENGTH + \ + ((interfaces) * USB_INTERFACE_DESCRIPTOR_LENGTH) + \ + ((endpoints) * USB_ENDPOINT_DESCRIPTOR_LENGTH)) + +// ---------------------------------------------------------------------------- +// Interface values +// +#define USBS_MSD_INTERFACE_PROTO 0x50 // Bulk mode + +#define USBS_MSD_CBW_MAX_LUN 2 +#define USBS_MSD_CBW_MIN_SCSI_CMD_LEN 1 +#define USBS_MSD_CBW_MAX_SCSI_CMD_LEN 16 + +#define USBS_MSD_DISK_SECTOR_SIZE 512 + +// ---------------------------------------------------------------------------- +// Mass Storage task constant +// +#define USBS_MSD_THREAD_STACK_SIZE 2048 +#define USBS_MSD_THREAD_STACK_PRIORITY 15 + +// command block wrapper +typedef struct usbs_msd_cbw { + cyg_uint32 signature; + cyg_uint32 tag; + cyg_uint32 data_transfert_len; + cyg_uint8 flags; + cyg_uint8 lun; + struct cb { + cyg_uint8 len; + cyg_uint8 data[16]; + } cb; +} __attribute__((packed)) usbs_msd_cbw; + +// command status wrapper +typedef struct usbs_msd_csw { + cyg_uint32 signature; + cyg_uint32 tag; + cyg_uint32 data_residue; + cyg_uint8 status; +} __attribute__((packed)) usbs_msd_csw; + +// command block wrapper state +typedef enum { + CYG_USBS_MSD_WAIT, + CYG_USBS_MSD_DATA_IN, + CYG_USBS_MSD_DATA_OUT +} usbs_msd_state_t; + +// End-point type +typedef enum { + CYG_USBS_EP0, + CYG_USBS_EP_RX, + CYG_USBS_EP_TX +} usbs_msd_ep_t; + +// End-point status / control +typedef struct usbs_msd_ep_sc_t { + cyg_sem_t ready; + cyg_int32 result; +} usbs_msd_ep_sc_t; + +// Storage devices +typedef struct usbs_msd_lun { + cyg_io_handle_t handle[USBS_MSD_CBW_MAX_LUN]; + const char * name[USBS_MSD_CBW_MAX_LUN]; + cyg_uint8 max_lun; +} usbs_msd_lun; + +typedef struct usbs_msd usbs_msd; + +// Sub Class Handler +typedef cyg_int32 (*handler_cmd_fn)( usbs_msd * ); +typedef bool (*handler_init_fn)(void **); + +// End-point functions +typedef cyg_int32 (*send_fn) ( usbs_msd *, const void * , cyg_int32 ); +typedef cyg_int32 (*receive_fn)( usbs_msd *, const void * , cyg_int32 ); +typedef cyg_int32 (*stall_fn) ( usbs_msd* msd, usbs_msd_ep_t ep ); + +// Mass-storage device structure +struct usbs_msd { + // Specify end-points to be used + usbs_control_endpoint* ctrl_ep; + usbs_tx_endpoint* tx_ep; + usbs_rx_endpoint* rx_ep; + + cyg_uint8 tx_ep_num; + cyg_uint8 rx_ep_num; + + cyg_bool static_ep; + + // Enumeration data for this device + usbs_enumeration_data* enum_data; + + // Enumeration string + const char* mfg_str; + const char* product_str; + const char* serial_str; + char* enum_mfg_str; + char* enum_product_str; + char* enum_serial_str; + + // End-point internal status/control + usbs_msd_ep_sc_t rx; + usbs_msd_ep_sc_t tx; + + // Lock for the state + cyg_mutex_t lock; + + // Condition variable for state changes + cyg_cond_t state_cond; + + // State of the USB interface + cyg_uint32 usb_state; + + // Data transport-phase buffer + cyg_uint8 buffer[USBS_MSD_DISK_SECTOR_SIZE]; + + // State of the MSD + usbs_msd_state_t state; + + void (*app_state_change_fn)(struct usbs_control_endpoint*, + void*, usbs_state_change, int); + + // Pointer to handler data + void* handler_data; + + // Commands Handler + handler_cmd_fn handler_cmd; + + // Initialize Command Handler + handler_init_fn handler_init; + + send_fn send; + receive_fn receive; + stall_fn stall; + + // Mass-Storage Service + char serv_stack[USBS_MSD_THREAD_STACK_SIZE]; + cyg_handle_t serv_handle; + cyg_thread serv_thread; + char* serv_name; + + // Storage Device + usbs_msd_lun* lun; + + // Command Block Wrapper + usbs_msd_cbw cbw; + + // Command Status Wrapper + usbs_msd_csw csw; +}; + +// ---------------------------------------------------------------------------- +// A C interface to the MSD USB code. +// This is the interface for internal code + +// Block the calling thread until the host configures the USB device. +void usbs_msd_wait_until_configured( usbs_msd* ); + +// Determines if the USB subsystem is configured +cyg_bool usbs_msd_is_configured( usbs_msd* ); + +// MSD main thread +void usbs_msd_handler( cyg_addrword_t ); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CYGONCE_USBS_MSD_H + diff --git a/packages/io/usb/msd/slave/current/include/usbs_msd.inl b/packages/io/usb/msd/slave/current/include/usbs_msd.inl new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/include/usbs_msd.inl @@ -0,0 +1,241 @@ +//========================================================================== +// +// usbs_msd.inl +// +// Support for slave-side USB mass storage devices +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// +// Contributors: jld +// Date: 2010-06-06 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#ifndef CYGONCE_IO_USB_SLAVE_MSD_INL +#define CYGONCE_IO_USB_SLAVE_MSD_INL + +#define MFG_STR_INDEX '\x01' +#define PRODUCT_STR_INDEX '\x02' +#define SERIAL_STR_INDEX '\x03' + +#define USB_MAX_STR_LEN 256 + +// Declare Mass Storage Device 0 +#ifdef CYGPKG_IO_USB_SLAVE_MSD0 + +#ifdef CYGDAT_IO_USB_SLAVE_MSD0_SUB_CLASS_TYPE_SCSI +#define USBS_MSD0_DATA_IFACE_SUB_CLASS USBS_MSD_SUB_CLASS_SCSI +#else +# error "Only SCSI Sub Class is implemented" +#endif + +// ----- USB external definition +extern usbs_control_endpoint CYGDAT_IO_USB_SLAVE_MSD0_EP0; +#if defined(CYGPKG_IO_USB_SLAVE_MSD0_STATIC_EP) +extern usbs_tx_endpoint CYGDAT_IO_USB_SLAVE_MSD0_TX_EP; +extern usbs_rx_endpoint CYGDAT_IO_USB_SLAVE_MSD0_RX_EP; +#endif + + +// ----- Configuration Descriptor ----- + +static const usb_configuration_descriptor usbs_msd0_configuration = { + length : sizeof(usb_configuration_descriptor), + type : USB_CONFIGURATION_DESCRIPTOR_TYPE, + total_length_lo : + USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_LO(USBS_MSD_NUM_IFACE, + USBS_MSD_NUM_ENDP), + total_length_hi : + USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_HI(USBS_MSD_NUM_IFACE, + USBS_MSD_NUM_ENDP), + number_interfaces : USBS_MSD_NUM_IFACE, + configuration_id : 1, + configuration_str : 0, +#ifdef CYGOPT_IO_USB_SLAVE_MSD0_BUSPOWERED + attributes : (USB_CONFIGURATION_DESCRIPTOR_ATTR_REQUIRED), +#else + attributes : (USB_CONFIGURATION_DESCRIPTOR_ATTR_REQUIRED | + USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED), +#endif + max_power : ((CYGNUM_IO_USB_SLAVE_MSD0_CURRENTDRAW+1) >> 1) +}; + +// ----- Interface Descriptor ----- + +static const usb_interface_descriptor usbs_msd0_interface[] = { + { + length: sizeof(usb_interface_descriptor), + type: USB_INTERFACE_DESCRIPTOR_TYPE, + interface_id: 0, + alternate_setting: 0, + number_endpoints: 2, + interface_class: USBS_MSD_DATA_IFACE_CLASS, + interface_subclass: USBS_MSD0_DATA_IFACE_SUB_CLASS, + interface_protocol: USBS_MSD_INTERFACE_PROTO, + interface_str: 0x00 + } +}; + +// ----- Endpoint Descriptors ----- + +static const usb_endpoint_descriptor usbs_msd0_endpoints[] = +{ + // Tx (Bulk IN) Endpoint Descriptor + { + sizeof(usb_endpoint_descriptor), + USB_ENDPOINT_DESCRIPTOR_TYPE, + USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN | \ + CYGNUM_IO_USB_SLAVE_MSD0_TX_EP_NUM, + USB_ENDPOINT_DESCRIPTOR_ATTR_BULK, + LO_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_TX_EP_MAX_PACKET_SIZE), + HI_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_TX_EP_MAX_PACKET_SIZE), + 0 + }, + + // Rx (Bulk OUT) Endpoint Descriptor + { + sizeof(usb_endpoint_descriptor), + USB_ENDPOINT_DESCRIPTOR_TYPE, + USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT | \ + CYGNUM_IO_USB_SLAVE_MSD0_RX_EP_NUM, + USB_ENDPOINT_DESCRIPTOR_ATTR_BULK, + LO_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_RX_EP_MAX_PACKET_SIZE), + HI_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_RX_EP_MAX_PACKET_SIZE), + 0 + } +}; + +// ----- String Descriptors ----- + +static char usbs_msd0_mfg_str_descr[USB_MAX_STR_LEN], + usbs_msd0_product_str_descr[USB_MAX_STR_LEN], + usbs_msd0_serial_str_descr[USB_MAX_STR_LEN]; + +static const char* usbs_msd0_strings[] = { + "\x04\x03\x09\x04", + usbs_msd0_mfg_str_descr, + usbs_msd0_product_str_descr, + usbs_msd0_serial_str_descr +}; + +// ----- Enumeration Data w/ Device Descriptor ----- + +static usbs_enumeration_data usbs_msd0_enum_data = { + { + length: sizeof(usb_device_descriptor), + type: USB_DEVICE_DESCRIPTOR_TYPE, + usb_spec_lo: 0x00, + usb_spec_hi: 0x02, + device_class: USBS_MSD_DEVICE_CLASS, + device_subclass: 0, + device_protocol: 0, + max_packet_size: CYGNUM_IO_USB_SLAVE_MSD0_EP0_MAX_PACKET_SIZE, + vendor_lo: LO_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_VENDOR_ID), + vendor_hi: HI_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_VENDOR_ID), + product_lo: LO_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_PRODUCT_ID), + product_hi: HI_BYTE_16(CYGNUM_IO_USB_SLAVE_MSD0_PRODUCT_ID), + device_lo: 0x00, + device_hi: 0x00, + manufacturer_str: MFG_STR_INDEX, + product_str: PRODUCT_STR_INDEX, + serial_number_str: SERIAL_STR_INDEX, // must be > 0 for mass-storage + number_configurations: 1 + }, + + total_number_interfaces: USBS_MSD_NUM_IFACE, + total_number_endpoints: USBS_MSD_NUM_ENDP, + total_number_strings: 3, + configurations: &usbs_msd0_configuration, + interfaces: usbs_msd0_interface, + endpoints: usbs_msd0_endpoints, + strings: (const unsigned char **) usbs_msd0_strings +}; + +// Logical Unit +usbs_msd_lun msd0_lun = { +#ifdef CYGDAT_IO_USB_SLAVE_MSD0_LUN0_NAME + .name[0] = CYGDAT_IO_USB_SLAVE_MSD0_LUN0_NAME, +#else + .name[0] = NULL, +#endif +#ifdef CYGDAT_IO_USB_SLAVE_MSD0_LUN1_NAME + .name[1] = CYGDAT_IO_USB_SLAVE_MSD0_LUN1_NAME +#else + .name[1] = NULL +#endif +}; + + +// USBS Mass Storage Device 0 +usbs_msd msd0 = { +#ifdef CYGDAT_IO_USB_SLAVE_MSD0_SUB_CLASS_TYPE_SCSI + .handler_cmd = usbs_msd_scsi_handle_cmd, + .handler_init = usbs_msd_scsi_init, +#else + .handler_cmd = NULL, + .handler_init = NULL, +#endif + .ctrl_ep = &CYGDAT_IO_USB_SLAVE_MSD0_EP0, +#if defined(CYGPKG_IO_USB_SLAVE_MSD0_STATIC_EP) + .tx_ep = &CYGDAT_IO_USB_SLAVE_MSD0_TX_EP, + .rx_ep = &CYGDAT_IO_USB_SLAVE_MSD0_RX_EP, + .static_ep = true, +#else + .static_ep = false, +#endif + .tx_ep_num = CYGNUM_IO_USB_SLAVE_MSD0_TX_EP_NUM, + .rx_ep_num = CYGNUM_IO_USB_SLAVE_MSD0_RX_EP_NUM, + .lun = &msd0_lun, + .handler_data = NULL, + .serv_name = "msd0_service", + .enum_data = &usbs_msd0_enum_data, + .enum_mfg_str = &usbs_msd0_mfg_str_descr[0], + .enum_product_str = &usbs_msd0_product_str_descr[0], + .enum_serial_str = &usbs_msd0_serial_str_descr[0], + .mfg_str = CYGDAT_IO_USB_SLAVE_MSD0_MFG_STR, + .product_str = CYGDAT_IO_USB_SLAVE_MSD0_PRODUCT_STR, + .serial_str = CYGDAT_IO_USB_SLAVE_MSD0_SERIAL_STR +}; + +#endif // CYGPKG_IO_USB_SLAVE_MSD0 + +#endif // CYGONCE_IO_USB_SLAVE_MSD_INL diff --git a/packages/io/usb/msd/slave/current/include/usbs_msd_io.h b/packages/io/usb/msd/slave/current/include/usbs_msd_io.h new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/include/usbs_msd_io.h @@ -0,0 +1,66 @@ +#ifndef CYGONCE_USBS_MSD_IO_H +#define CYGONCE_USBS_MSD_IO_H +//========================================================================== +// +// include/usbs_msd_io.h +// +// Application API for the USB slave-side MSD device +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// Contributors: +// Date: 2010-06-04 +// Purpose: +// Description: USB slave-side MSD support +// +// +//####DESCRIPTIONEND#### +//========================================================================== + +#ifdef __cplusplus +extern "C" { +#endif + +// Starts the USB subsystem +bool usbs_msd_start(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CYGONCE_USBS_MSD_IO_H + diff --git a/packages/io/usb/msd/slave/current/include/usbs_msd_opcode.h b/packages/io/usb/msd/slave/current/include/usbs_msd_opcode.h new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/include/usbs_msd_opcode.h @@ -0,0 +1,114 @@ +#ifndef CYGONCE_USBS_MSD_OPCODE_H +#define CYGONCE_USBS_MSD_OPCODE_H +//========================================================================== +// +// include/usbs_msd_opcode.h +// +// SCSI support for the USB slave-side MSD device +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// Contributors: +// Date: 2010-06-02 +// Purpose: +// Description: USB slave-side MSD support +// +// +//####DESCRIPTIONEND#### +//========================================================================== + +#ifdef __cplusplus +extern "C" { +#endif + +// SCSI commands from SBC-2 or SBC-3 +#define USBS_SCSI_FORMAT_UNIT 0x04 +#define USBS_SCSI_INQUIRY 0x12 +#define USBS_SCSI_MODE_SELECT_6 0x15 +#define USBS_SCSI_MODE_SELECT_10 0x55 +#define USBS_SCSI_SENSE_6 0x1A +#define USBS_SCSI_SENSE_10 0x5A +#define USBS_SCSI_READ_6 0x08 +#define USBS_SCSI_READ_10 0x28 +#define USBS_SCSI_READ_12 0xA8 +#define USBS_SCSI_READ_CAPACITY 0x25 +#define USBS_SCSI_READ_FORMAT_CAPACITIES 0x23 +#define USBS_SCSI_READ_TOC 0x43 +#define USBS_SCSI_REPORT_LUNS 0xA0 +#define USBS_SCSI_REQUEST_SENSE 0x03 +#define USBS_SCSI_SEND_DIAGNOSTIC 0x1D +#define USBS_SCSI_START_STOP_UNIT 0x1B +#define USBS_SCSI_SYNC_CACHE_10 0x35 +#define USBS_SCSI_TEST_UNIT_READY 0x00 +#define USBS_SCSI_VERIFY_10 0x2F +#define USBS_SCSI_WRITE_6 0x0A +#define USBS_SCSI_WRITE_10 0x2A +#define USBS_SCSI_WRITE_12 0xAA +#define USBS_SCSI_PREVENT_ALLOW_REMOVAL 0x1E + +// Sense result +#define USBS_MSD_SCSI_SENSE_NOT_READY 0x2 +#define USBS_MSD_SCSI_SENSE_MEDIUM_ERROR 0x3 +#define USBS_MSD_SCSI_SENSE_ILLEGAL_REQUEST 0x5 +#define USBS_MSD_SCSI_SENSE_UNIT_ATTENTION 0x6 +#define USBS_MSD_SCSI_SENSE_ASC_LOGICAL_BLOCK_ADDR_OUT_OF_RANGE 0x21 +#define USBS_MSD_SCSI_SENSE_ASCQ_LOGICAL_BLOCK_ADDR_OUT_OF_RANGE 0x0 +#define USBS_MSD_SCSI_SENSE_ASC_INVALID_OPCODE 0x20 +#define USBS_MSD_SCSI_SENSE_ASCQ_INVALID_OPCODE 0x0 +#define USBS_MSD_SCSI_SENSE_ASC_MEDIUM_NOT_PRESENT 0x3a +#define USBS_MSD_SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT 0x00 +#define USBS_MSD_SCSI_SENSE_ASC_PERIPHERAL_DEVICE_WRITE_FAULT 0x03 +#define USBS_MSD_SCSI_SENSE_ASCQ_PERIPHERAL_DEVICE_WRITE_FAULT 0x00 +#define USBS_MSD_SCSI_SENSE_ASC_UNRECOVERED_READ_ERROR 0x11 +#define USBS_MSD_SCSI_SENSE_ASCQ_UNRECOVERED_READ_ERROR 0x00 +#define USBS_MSD_SCSI_SENSE_ASC_WRITE_ERROR 0x0C +#define USBS_MSD_SCSI_SENSE_ASCQ_WRITE_ERROR 0x00 +#define USBS_MSD_SCSI_SENSE_ASC_WRITE_PROTECTED 0x27 +#define USBS_MSD_SCSI_SENSE_ASCQ_WRITE_PROTECTED 0x00 +#define USBS_MSD_SCSI_SENSE_ASC_INVALID_FIELD_IN_CBD 0x24 + +// Mode Page +#define USBS_MSD_SCSI_MODE_PAGE_CODE_ALL 0x3F +#define USBS_MSD_SCSI_MODE_PAGE_CODE_IEC 0x1C +#define USBS_MSD_SCSI_MODE_PAGE_CODE_CACHING 0x08 +#define USBS_MSD_SCSI_MODE_PAGE_CODE_FDM 0x05 + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CYGONCE_USBS_MSD_OPCODE_H diff --git a/packages/io/usb/msd/slave/current/include/usbs_msd_scsi.h b/packages/io/usb/msd/slave/current/include/usbs_msd_scsi.h new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/include/usbs_msd_scsi.h @@ -0,0 +1,143 @@ +#ifndef CYGONCE_USBS_MSD_SCSI_H +#define CYGONCE_USBS_MSD_SCSI_H +//========================================================================== +// +// include/usbs_msd_scsi.h +// +// SCSI support for the USB slave-side MSD device +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// Contributors: +// Date: 2010-06-02 +// Purpose: +// Description: USB slave-side MSD support +// +// +//####DESCRIPTIONEND#### +//========================================================================== + +#ifdef __cplusplus +extern "C" { +#endif + +// The standard INQUIRY data shall contain at least 36 bytes +// This is the reduced structure for Mass Storage Devices +typedef struct +{ + cyg_uint8 peripheral; // Device Type + cyg_uint8 rmb; // Removable Media Bit + cyg_uint8 version; // Version Field + cyg_uint8 resp_data_format; // Response Data Format + cyg_uint8 additional_len; // Additional Length + cyg_uint8 sccstp; // SCC Supported (include embedded storage array) + cyg_uint8 bque; // Basic Queuing + cyg_uint8 cmdque; // Command Queuing + cyg_uint8 vendor_id[8]; + cyg_uint8 product_id[16]; + cyg_uint8 product_rev[4]; +} msd_scsi_inq_resp; + +#define USBS_SCSI_DIRECT_ACCESS_BLOCK_DEVICE 0x00 +#define USBS_SCSI_REMOVABLE_DEVICE 0x80 + +// Fixed-format Sense Data ( defined as a 18 bytes array ) +typedef struct +{ + cyg_uint8 byte[18]; +} msd_scsi_req_sense_resp; + +// Set Valid information field +#define USBS_MSD_SCSI_SET_SENSE_VALID( ptr_sense, _valid_ ) \ +{ \ + if( _valid_ ) \ + ptr_sense[0] |= 0x80; \ + else \ + ptr_sense[0] &= ~0x80; \ +}; + +// Set Response Code field +#define USBS_MSD_SCSI_SET_SENSE_RESP_CODE( ptr_sense, _code_ ) \ +{ \ + cyg_uint8 byte = ptr_sense[0] & 0x80; \ + ptr_sense[0] = ((_code_ & 0x7F) | byte); \ +}; + +// Set Sense Key field +#define USBS_MSD_SCSI_SET_SENSE_KEY( ptr_sense, _key_ ) \ +{ \ + cyg_uint8 byte = ptr_sense[0] & 0xF0; \ + ptr_sense[0] = ((_key_ & 0x0F) | byte); \ +}; + +// Set Additional Sense Length field +#define USBS_MSD_SCSI_SET_SENSE_ASL( ptr_sense, _asl_ ) \ +{ \ + ptr_sense[7] = _asl_; \ +}; + +// Set Additional Sense Code field +#define USBS_MSD_SCSI_SET_SENSE_ASC( ptr_sense, _asc_ ) \ +{ \ + ptr_sense[12] = _asc_; \ +}; + +// Set Additional Sense Code Qualifier field +#define USBS_MSD_SCSI_SET_SENSE_ASCQ( ptr_sense, _ascq_ ) \ +{ \ + ptr_sense[13] = _ascq_; \ +}; + +// Init Sense Data structure +#define USBS_MSD_SCSI_INIT_SENSE_DATA( _array_ ) \ +{ \ + cyg_int32 i; \ + for(i = 0; i < sizeof(msd_scsi_req_sense_resp); i++) \ + _array_[i] = 0; \ + USBS_MSD_SCSI_SET_SENSE_VALID( _array_, 1); \ + USBS_MSD_SCSI_SET_SENSE_RESP_CODE( _array_, 0x70 ); \ + USBS_MSD_SCSI_SET_SENSE_ASC( _array_, 0x0a ); \ +}; + +cyg_int32 usbs_msd_scsi_handle_cmd( usbs_msd * ); +bool usbs_msd_scsi_init( void ** ctxt ); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CYGONCE_USBS_MSD_SCSI_H diff --git a/packages/io/usb/msd/slave/current/src/usbs_msd.c b/packages/io/usb/msd/slave/current/src/usbs_msd.c new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/src/usbs_msd.c @@ -0,0 +1,471 @@ +//========================================================================== +// +// usbs_msd.c +// +// Support for slave-side USB mass storage devices. +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008, 2010 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// +// Contributors: jld +// Date: 2010-05-27 +// +//####DESCRIPTIONEND#### +// +//========================================================================== +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CYGDAT_IO_USB_SLAVE_MSD0_SUB_CLASS_TYPE_SCSI +#include +#endif + +#include + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_DEBUG) +#define DBG diag_printf +#else +#define DBG (1) ? (void)0 : diag_printf +#endif + +#include CYGDAT_IO_USB_SLAVE_MSD_INL // Declaration of the Mass Storage devices + +// -------------------------------------------------------------------------- +// Create a USB String Descriptor from a C string. +// +void +usbs_msd_create_str_descriptor(char descr[], const char *str) +{ + cyg_uint32 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; icontrol_buffer; + usbs_msd *msd = (usbs_msd *) callback_data; + static cyg_uint8 rsp_buf[32]; + + DBG("USB MSD Class Handler: "); + + switch (req->request) { + + case USBS_MSD_CLASS_REQ_GET_MAX_LUN : + DBG("USB MSD Request: Get MAX LUN %d\n", \ + msd->lun->max_lun); + rsp_buf[0] = msd->lun->max_lun - 1; + ep0->buffer = rsp_buf; + ep0->buffer_size = 1; + result = USBS_CONTROL_RETURN_HANDLED; + break; + + case USBS_MSD_CLASS_REQ_BOMSR : + DBG("USB MSD Request: Reset\n"); + // FIXME : Handle Reset + ep0->buffer_size = 0; + result = USBS_CONTROL_RETURN_HANDLED; + break; + + default : + DBG("*** USB MSD Request: 0x%02X not implemented !***\n", + (unsigned) req->request); + } + + return result; +} + + +// -------------------------------------------------------------------------- +// Callback for a USB state change +// +void +usbs_msd_state_change_handler(usbs_control_endpoint* ep, void* callback_data, + usbs_state_change change, int prev_state) +{ + usbs_msd *msd = (usbs_msd *) callback_data; + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_DEBUG) + const char *STATE_CHG_STR[] = { "Detached", "Attached", "Powered", "Reset", + "Addressed", "Configured", "Deconfigured", + "Suspended", "Resumed" }; + + if (change > 0 && change < 10) { + DBG("### %d:%s ###\n", change, STATE_CHG_STR[(int) change-1]); + } + else { + DBG("### %d ###\n", change); + } +#endif + + // Called from DSR, cond broadcast should be ok without mutex lock + msd->usb_state = msd->ctrl_ep->state; + + cyg_cond_broadcast( &msd->state_cond ); + + if (msd->app_state_change_fn) + (*msd->app_state_change_fn)(ep, callback_data, change, prev_state); + +} + + +// -------------------------------------------------------------------------- +// Block the calling thread until the USB is configured. + // +void +usbs_msd_wait_until_configured( usbs_msd* msd ) +{ + cyg_mutex_lock( &msd->lock ); + while ( msd->usb_state != USBS_STATE_CONFIGURED ) + cyg_cond_wait( &msd->state_cond ); + + // Dynamic end-points configuration + if ( msd->static_ep == false ) + { + msd->tx_ep = usbs_get_tx_endpoint( msd->ctrl_ep, msd->tx_ep_num ); + msd->rx_ep = usbs_get_rx_endpoint( msd->ctrl_ep, msd->rx_ep_num ); + DBG("tx_ep = %p\n", msd->tx_ep); + DBG("rx_ep = %p\n", msd->rx_ep); + } + + cyg_mutex_unlock( &msd->lock ); +} + + +// -------------------------------------------------------------------------- +// Determine if the device is currently configured. +// +cyg_bool +usbs_msd_is_configured( usbs_msd* msd ) +{ + return msd->usb_state == USBS_STATE_CONFIGURED; +} + + +// -------------------------------------------------------------------------- +// Callback for when a transmit / stall is complete +// +static void +usbs_msd_tx_complete( void *p, cyg_int32 result ) +{ + usbs_msd* msd = (usbs_msd*) p; + msd->tx.result = result; + cyg_semaphore_post( &msd->tx.ready ); +} + + +// -------------------------------------------------------------------------- +// Callback for when a receive / stall is complete +// +static void +usbs_msd_rx_complete( void *p, cyg_int32 result ) +{ + usbs_msd* msd = (usbs_msd*) p; + msd->rx.result = result; + cyg_semaphore_post( &msd->rx.ready ); +} + + +// -------------------------------------------------------------------------- +// Start an asynchronous transmit of a buffer. +// +static void +usbs_msd_start_tx(usbs_msd* msd, const void* buf, cyg_int32 n) +{ + usbs_start_tx_buffer( msd->tx_ep, (unsigned char*) buf, n, + usbs_msd_tx_complete, msd ); +} + + +// -------------------------------------------------------------------------- +// Block the caller until the transmit / stall is complete +// +static int +usbs_msd_wait_for_tx(usbs_msd* msd) +{ + cyg_semaphore_wait( &msd->tx.ready ); + return msd->tx.result; +} + + +// -------------------------------------------------------------------------- +// Perform a synchronous transmit and wait for it to complete. +// +static cyg_int32 +usbs_msd_tx(usbs_msd* msd, const void* buf, cyg_int32 n) +{ + usbs_msd_start_tx( msd, buf, n ); + return usbs_msd_wait_for_tx( msd ); +} + + +// -------------------------------------------------------------------------- +// Start an asynchronous receive of a buffer. +// +static void +usbs_msd_start_rx(usbs_msd* msd, void* buf, cyg_int32 n) +{ + usbs_start_rx_buffer( msd->rx_ep, (unsigned char*) buf, n, + usbs_msd_rx_complete, msd ); +} + + +// -------------------------------------------------------------------------- +// Block the caller until the receive / stall is complete +// +static cyg_int32 +usbs_msd_wait_for_rx(usbs_msd* msd) +{ + cyg_semaphore_wait( &msd->rx.ready ); + return msd->rx.result; +} + + +// -------------------------------------------------------------------------- +// Perform a synchronous receive and wait for it to complete +// +static cyg_int32 +usbs_msd_rx(usbs_msd* msd, void* buf, cyg_int32 n) +{ + usbs_msd_start_rx( msd, buf, n ); + return usbs_msd_wait_for_rx( msd ); +} + + +// -------------------------------------------------------------------------- +// Halt end-point +// +static void +usbs_msd_set_ep_halted( usbs_msd* msd , usbs_msd_ep_t ep, cyg_bool state ) +{ + if( ep == CYG_USBS_EP_RX ) + usbs_set_rx_endpoint_halted( msd->rx_ep , state ); + else if( ep == CYG_USBS_EP_TX ) + usbs_set_tx_endpoint_halted( msd->tx_ep , state ); +} + + +// -------------------------------------------------------------------------- +// Perform a synchronous end-point halt and wait for host to restart it +// +static cyg_int32 +usbs_msd_ep_stall(usbs_msd* msd, usbs_msd_ep_t ep ) +{ + cyg_sem_t *ready; + cyg_int32 *result; + +#if !defined(CYGSEM_IO_USB_SLAVE_MSD_STALL_ENABLE) + return 1; +#endif + + if( ep == CYG_USBS_EP_RX ) + { + ready = &msd->rx.ready; + result = &msd->rx.result; + } + else if( ep == CYG_USBS_EP_TX ) + { + ready = &msd->tx.ready; + result = &msd->tx.result; + } + else + return 0; + + // Halt End-point + usbs_msd_set_ep_halted( msd, ep , 1 ); + + // Wait + if( ep == CYG_USBS_EP_RX ) + usbs_start_rx_endpoint_wait( msd->rx_ep, usbs_msd_rx_complete, msd ); + else if( ep == CYG_USBS_EP_TX ) + usbs_start_tx_endpoint_wait( msd->tx_ep, usbs_msd_tx_complete, msd ); + + cyg_semaphore_wait( ready ); + + return ( *result ); + +} + + +// -------------------------------------------------------------------------- +// Initialize a Mass Storage Device structure +// +static bool +usbs_msd_init( usbs_msd* msd ) +{ + cyg_io_handle_t handle; + cyg_uint8 i = 0; + + if( msd->ctrl_ep == NULL ) + return false; + + // Lookup storage devices + msd->lun->max_lun = 0; + for (i = 0; i < USBS_MSD_CBW_MAX_LUN; i++) + { + if ( msd->lun->name[i] != NULL ) + { + DBG("USB Slave MSD: Lookup %s \n", msd->lun->name[i] ); + if( cyg_io_lookup( msd->lun->name[i] , &handle ) != ENOERR ) + return false; + msd->lun->handle[i] = handle; + msd->lun->max_lun++; + } + } + + msd->tx.result = 0; + msd->rx.result = 0; + + // Initialize TX and RX semaphore + cyg_semaphore_init( &msd->tx.ready, 0 ); + cyg_semaphore_init( &msd->rx.ready, 0 ); + + cyg_mutex_init( &msd->lock ); + + cyg_cond_init( &msd->state_cond, &msd->lock ); + + // Make USB string descriptors + usbs_msd_create_str_descriptor( \ + msd->enum_mfg_str,\ + msd->mfg_str ); + usbs_msd_create_str_descriptor( \ + msd->enum_product_str,\ + msd->product_str ); + usbs_msd_create_str_descriptor( \ + msd->enum_serial_str,\ + msd->serial_str ); + + // Set up enumeration & USB call-backs + msd->usb_state = msd->ctrl_ep->state; + + msd->ctrl_ep->enumeration_data = msd->enum_data; + + if ( msd->ctrl_ep->state_change_fn ) + msd->app_state_change_fn = msd->ctrl_ep->state_change_fn; + + msd->ctrl_ep->state_change_fn = usbs_msd_state_change_handler; + msd->ctrl_ep->state_change_data = (void*) msd; + msd->ctrl_ep->class_control_fn = usbs_msd_class_handler; + msd->ctrl_ep->class_control_data = (void*) msd; + + if ( msd->handler_init ) + msd->handler_init( &msd->handler_data ); + + msd->send = usbs_msd_tx; + msd->receive = usbs_msd_rx; + msd->stall = usbs_msd_ep_stall; + + // Setup initial state + msd->state = CYG_USBS_MSD_WAIT; + + return true; +} + + +// -------------------------------------------------------------------------- +// Initialize a Mass Storage Device structure +// +static void +usbs_msd_start_service(usbs_msd* msd) +{ + + cyg_thread_create( USBS_MSD_THREAD_STACK_PRIORITY , &usbs_msd_handler, \ + (cyg_addrword_t) msd, msd->serv_name, msd->serv_stack, \ + USBS_MSD_THREAD_STACK_SIZE, &msd->serv_handle, \ + &msd->serv_thread); + + cyg_thread_resume( msd->serv_handle ); + +} + + +// -------------------------------------------------------------------------- +// Start the USB subsystem +// +bool +usbs_msd_start(void) +{ + +#ifdef CYGPKG_IO_USB_SLAVE_MSD0 + + DBG("USB Slave Mass-Storage 0 starts\n"); + + if ( usbs_msd_init( &msd0 ) == false ) + { + DBG("USB Slave Mass-Storage 0 startup failed\n"); + return false; + } + + // ----- Start USB subsystem ----- + // + usbs_start( msd0.ctrl_ep ); + + usbs_msd_start_service( &msd0 ); + +#endif + + return true; +} + + + + diff --git a/packages/io/usb/msd/slave/current/src/usbs_msd_handler.c b/packages/io/usb/msd/slave/current/src/usbs_msd_handler.c new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/src/usbs_msd_handler.c @@ -0,0 +1,266 @@ +//========================================================================== +// +// usbs_msd_handler.c +// +// Support for slave-side USB mass storage devices. +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// +// Date: 2010-06-02 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // for memcpy + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_DEBUG) +#define DBG diag_printf +#else +#define DBG (1) ? (void)0 : diag_printf +#endif + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_TRACE) +#define TRACE diag_printf +#else +#define TRACE (1) ? (void)0 : diag_printf +#endif + +#define CONSTRUCT_CSW( _csw_, _tag_ ) \ +{ \ + _csw_.tag = _tag_; \ + _csw_.signature = USBS_MSD_CSW_SIGNATURE; \ +}; + + +// Validate a command block wrapper +// CBW is sent from host in little endian format, hence it +// must be converted to the CPU endianness +// +static inline bool +usbs_msd_validate_cbw(usbs_msd_cbw *cbw, cyg_int32 len) +{ + + // Convert to CPU endianness + cbw->signature = CYG_LE32_TO_CPU( cbw->signature ); + cbw->tag = CYG_LE32_TO_CPU( cbw->tag ); + cbw->data_transfert_len = CYG_LE32_TO_CPU( cbw->data_transfert_len ); + + if( len != sizeof( usbs_msd_cbw ) || + cbw->signature != USBS_MSD_CBW_SIGNATURE ) + { + DBG("USB Slave MSD: CBW signature %x, len %d\n\r", \ + cbw->signature, len); + return false; + } + else + return true; +} + + +// Validate a command block wrapper content +// +static inline bool +usbs_msd_validate_cbw_data(usbs_msd_cbw *cbw) +{ + if( ( cbw->lun < USBS_MSD_CBW_MAX_LUN ) && \ + ( cbw->cb.len <= USBS_MSD_CBW_MAX_SCSI_CMD_LEN ) && \ + ( cbw->cb.len >= USBS_MSD_CBW_MIN_SCSI_CMD_LEN ) && \ + ( ( cbw->flags == USBS_MSD_CBW_HOST2DEVICE ) || \ + ( cbw->flags == USBS_MSD_CBW_DEVICE2HOST ) ) ) + { + return true; + } + else + { + DBG("USB Slave MSD: CBW Invalid data, LUN %d, len %d, flag %x\n\r", \ + cbw->lun, cbw->cb.len, cbw->flags); + } + return false; +} + + +// Send Command Status Wrapper +// +static inline cyg_int32 +usbs_msd_send_csw( usbs_msd* msd ) +{ + // Convert to host endianness + msd->csw.signature = CYG_CPU_TO_LE32( msd->csw.signature ); + msd->csw.tag = CYG_CPU_TO_LE32( msd->csw.tag ); + msd->csw.data_residue = CYG_CPU_TO_LE32( msd->csw.data_residue ); + + return msd->send( msd, &msd->csw, sizeof( usbs_msd_csw )); +} + + +// Receive Command Status Wrapper +// +static cyg_int32 +usbs_msd_wait_for_cbw( usbs_msd *msd ) +{ + cyg_uint8 cbw[sizeof( usbs_msd_cbw ) + 1]; + cyg_int32 n; + + TRACE("USB Slave MSD: Wait for CBW\n\r"); + n = msd->receive( msd, cbw, sizeof( cbw ) ); + + TRACE("USB Slave MSD: CBW received, len %d\n\r", n); + + if ( n < 0) + return n; + + memcpy(&msd->cbw, cbw, sizeof( usbs_msd_cbw )); + + if ( ( true == usbs_msd_validate_cbw( &msd->cbw, n ) ) && \ + ( true == usbs_msd_validate_cbw_data( &msd->cbw ) ) ) + { + CONSTRUCT_CSW( msd->csw , msd->cbw.tag ); + + if( msd->cbw.flags == USBS_MSD_CBW_DEVICE2HOST ) + { + msd->state = CYG_USBS_MSD_DATA_OUT; + } + else if ( msd->cbw.flags == USBS_MSD_CBW_HOST2DEVICE ) + { + msd->state = CYG_USBS_MSD_DATA_IN; + } + } + + return n; +} + +// USB slave side Mass-Storage Handler +// +void +usbs_msd_handler( cyg_addrword_t data ) +{ + usbs_msd* msd = (usbs_msd*) data; + cyg_int32 transfered = 0; + cyg_int32 received = 0; + + while(1) + { + + // Wait for device to be configured + DBG("USB Slave MSD: Wait for device configuration\n\r"); + usbs_msd_wait_until_configured( msd ); + cyg_thread_delay( (cyg_tick_count_t) 10 ); + + while(1) + { + + switch( msd->state ) + { + case CYG_USBS_MSD_WAIT: + // FIXME, check for return values + // For the time being, -EPIPE and -EAGAIN triggers the + // driver to wait for the device to be configured again. + received = usbs_msd_wait_for_cbw( msd ); + if ( received < 0 ) + break; + // Fall through + + case CYG_USBS_MSD_DATA_IN: + case CYG_USBS_MSD_DATA_OUT: + // Handle data transfer IN and OUT + transfered = msd->handler_cmd( msd ); + + // Data transfer failed because of -EPIPE or -EAGAIN. For + // the time being just wait for the device to be re-configured + if(transfered < 0) + { + DBG("USB Slave MSD: Data transfer error\n\r"); + msd->state = CYG_USBS_MSD_WAIT; + break; + } + + // Compute the data residue value to be recorded in the + // Command Status Wrapper + msd->csw.data_residue = msd->cbw.data_transfert_len - transfered; + + // Check if all data has been transfered / received to / from + // the host + if( msd->csw.data_residue != 0 ) + { + DBG("USB Slave MSD: Transfer not complete %d / %d\n\r", \ + msd->cbw.data_transfert_len, transfered); + + // Stall endpoint, this is a waiting call until the host has + // cleared the stall. AT91 USB driver does not seem to handle + // it! + msd->stall( msd, CYG_USBS_EP_TX ); +#if defined(CYGSEM_IO_USB_SLAVE_MSD_STALL_ENABLE) + TRACE("USB Slave MSD: Stall completed\n\r"); +#endif + } + + // Complete the transaction by sending the command status + usbs_msd_send_csw( msd ); + + TRACE("USB Slave MSD: CSW sent\n\r"); + + // Wait for next command + msd->state = CYG_USBS_MSD_WAIT; + break; + + default: + DBG("USB Slave MSD: Invalid state\n\r"); + break; + } + + if( ( transfered < 0 ) || ( received < 0 ) ) + break; + + } + } +} + + diff --git a/packages/io/usb/msd/slave/current/src/usbs_msd_scsi.c b/packages/io/usb/msd/slave/current/src/usbs_msd_scsi.c new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/src/usbs_msd_scsi.c @@ -0,0 +1,510 @@ +//========================================================================== +// +// usbs_msd_scsi.c +// +// Support for slave-side USB mass storage SCSI devices. +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// +// Date: 2010-06-02 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include // for memcpy + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_DEBUG) +#define DBG diag_printf +#else +#define DBG (1) ? (void)0 : diag_printf +#endif + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_TRACE) +#define TRACE diag_printf +#else +#define TRACE (1) ? (void)0 : diag_printf +#endif + + +const msd_scsi_inq_resp inq_resp = { + USBS_SCSI_DIRECT_ACCESS_BLOCK_DEVICE, // Direct Access Block device + USBS_SCSI_REMOVABLE_DEVICE, // Device is removable + 0x04, // Comply with SPC-2 + 0x02, // Standard format + 0x20, // Response is 0x20 + 4 bytes + 0x00, + 0x00, + 0x00, + {'e','C','o','s',' ',' ',' ',' '}, + {'M','a','s','s',' ','S','t','o','r','a','g','e',' ',' ',' ',' '}, + {'0','.','0','1'}, +}; + + +// Handle Mode Sense Command +// +static inline void +usbs_msd_scsi_update_sense(msd_scsi_req_sense_resp *sense, \ + cyg_uint8 asc, cyg_uint8 ascq, cyg_uint8 key ) +{ + USBS_MSD_SCSI_SET_SENSE_ASC( sense->byte , asc ); + USBS_MSD_SCSI_SET_SENSE_ASCQ( sense->byte , ascq ); + USBS_MSD_SCSI_SET_SENSE_KEY( sense->byte , key ); +} + + +// Handle Inquiry Request Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_inquiry( usbs_msd * msd ) +{ + cyg_int32 tx_bytes = sizeof(msd_scsi_inq_resp); + + TRACE("USB Slave MSD: SCSI inquiry\n\r"); + + // Copy over to message buffer + memcpy( msd->buffer, (cyg_uint8 *) &inq_resp, tx_bytes ); + + tx_bytes = msd->send( msd, msd->buffer, tx_bytes ); + + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + + return tx_bytes; +} + + +// Handle Read Capacity Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_capacity( usbs_msd * msd ) +{ + cyg_disk_info_t disk_info; + cyg_int32 tx_bytes = sizeof(cyg_uint32) << 1; + cyg_uint32 len = sizeof(cyg_disk_info_t); + cyg_uint32 key = CYG_IO_GET_CONFIG_DISK_INFO; + cyg_io_handle_t lun = msd->lun->handle[msd->cbw.lun]; + + TRACE("USB Slave MSD: SCSI capacity\n\r"); + + // Get the configuration of a device + cyg_io_get_config( lun, key, &disk_info, &len ); + + msd->buffer[0] = BYTE3_32( disk_info.blocks_num ); + msd->buffer[1] = BYTE2_32( disk_info.blocks_num ); + msd->buffer[2] = BYTE1_32( disk_info.blocks_num ); + msd->buffer[3] = BYTE0_32( disk_info.blocks_num ); + + msd->buffer[4] = BYTE3_32( disk_info.block_size ); + msd->buffer[5] = BYTE2_32( disk_info.block_size ); + msd->buffer[6] = BYTE1_32( disk_info.block_size ); + msd->buffer[7] = BYTE0_32( disk_info.block_size ); + + tx_bytes = msd->send( msd, msd->buffer, tx_bytes ); + + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + + return tx_bytes; +} + + +// Handle Read Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_read( usbs_msd * msd, msd_scsi_req_sense_resp *sense ) +{ + Cyg_ErrNo ret; + cyg_uint32 transfert_len = 0, pos = 0, len = 1; + cyg_int32 ret_len = 0; + cyg_int32 tx_byte = 0; + cyg_io_handle_t lun = msd->lun->handle[msd->cbw.lun]; + + TRACE("USB Slave MSD: SCSI read\n\r"); + + pos = ( msd->cbw.cb.data[2] << 24 ) | \ + ( msd->cbw.cb.data[3] << 16 ) | \ + ( msd->cbw.cb.data[4] << 8 ) | \ + msd->cbw.cb.data[5]; + + transfert_len = ( msd->cbw.cb.data[7] << 8 ) | msd->cbw.cb.data[8]; + + // Read data from a block device + while ( transfert_len > 0 ) { + if( ( ret = cyg_io_bread( lun, msd->buffer, &len, pos)) != ENOERR ) + break; + tx_byte = msd->send( msd, msd->buffer, (USBS_MSD_DISK_SECTOR_SIZE) ); + TRACE("USB Slave MSD: Read %d block, %d bytes\n\r", transfert_len, tx_byte); + ret_len += tx_byte; + // Substract 1 sector + transfert_len -= 1; + pos += 1; + } + + if ( ret == ENOERR ) + { + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + } + else + { + msd->csw.status = USBS_MSD_CSW_STATUS_FAILED; + usbs_msd_scsi_update_sense( sense, \ + USBS_MSD_SCSI_SENSE_ASC_UNRECOVERED_READ_ERROR, \ + USBS_MSD_SCSI_SENSE_ASCQ_UNRECOVERED_READ_ERROR, \ + USBS_MSD_SCSI_SENSE_MEDIUM_ERROR ); + } + + return ret_len; +} + + +// Handle Write Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_write( usbs_msd * msd , msd_scsi_req_sense_resp *sense ) +{ + Cyg_ErrNo ret; + cyg_uint32 transfert_len = 0, pos = 0, len = 1; + cyg_int32 ret_len = 0; + cyg_int32 rx_byte = 0; + cyg_io_handle_t lun = msd->lun->handle[msd->cbw.lun]; + + DBG("USB Slave MSD: SCSI Write\n\r"); + + pos = ( msd->cbw.cb.data[2] << 24 ) | \ + ( msd->cbw.cb.data[3] << 16 ) | \ + ( msd->cbw.cb.data[4] << 8 ) | \ + msd->cbw.cb.data[5]; + + transfert_len = ( msd->cbw.cb.data[7] << 8 ) | msd->cbw.cb.data[8]; + + // Read data from a block device + while ( transfert_len > 0 ) { + rx_byte = msd->receive( msd, msd->buffer, (USBS_MSD_DISK_SECTOR_SIZE) ); + if( ( ret = cyg_io_bwrite( lun, msd->buffer, &len, pos)) != ENOERR ) + break; + TRACE("USB Slave MSD: Write %d block, %d bytes\n\r", transfert_len, rx_byte); + ret_len += ( rx_byte ); + // Substract 1 sector + transfert_len -= 1; + pos += 1; + } + + if ( ret == ENOERR ) + { + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + } + else + { + msd->csw.status = USBS_MSD_CSW_STATUS_FAILED; + usbs_msd_scsi_update_sense( sense, \ + USBS_MSD_SCSI_SENSE_ASC_WRITE_ERROR, \ + USBS_MSD_SCSI_SENSE_ASCQ_WRITE_ERROR, \ + USBS_MSD_SCSI_SENSE_MEDIUM_ERROR ); + } + + return ret_len; +} + + +// Handle Request Sense Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_req_sense( usbs_msd * msd, \ + msd_scsi_req_sense_resp *sense ) +{ + cyg_uint8 i = 0; + cyg_int32 tx_bytes = sizeof(msd_scsi_req_sense_resp); + + TRACE("USB Slave MSD: SCSI request sense\n\r"); + + // Copy over to message buffer + for(i = 0 ; i < tx_bytes ; i++) + msd->buffer[i] = sense->byte[i]; + + tx_bytes = msd->send( msd, msd->buffer, tx_bytes ); + + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + + return tx_bytes; +} + + +// Handle Mode Sense Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_sense( usbs_msd * msd, \ + msd_scsi_req_sense_resp *sense ) +{ + cyg_int32 tx_bytes = 0x0; + DBG("USB Slave MSD: SCSI sense\n\r"); + + msd->buffer[0] = 0x03; // Number of byte in the following message + msd->buffer[1] = 0x00; // Media type + msd->buffer[2] = 0x00; // Not Write protected, no cache-control-bit support + msd->buffer[3] = 0x00; // No Mode parameter + + // FIXME : handle different option correctly. For the time being, + // just respond with minimum data + if( ( msd->cbw.cb.data[2] & 0x3F ) == \ + USBS_MSD_SCSI_MODE_PAGE_CODE_ALL ) + { + DBG("USB Slave MSD: SCSI sense all\n\r"); + + tx_bytes = msd->send( msd, msd->buffer, 4 ); + + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + } + else + { + DBG("USB Slave MSD: SCSI sense page: %x\n\r", \ + msd->cbw.cb.data[2]); + + tx_bytes = msd->send( msd, msd->buffer, 4 ); + + msd->csw.status = USBS_MSD_CSW_STATUS_FAILED; + } + + return tx_bytes; +} + + +// Handle Test Unit Ready Command +// +static inline cyg_int32 +usbs_msd_scsi_handle_test_unit_ready( usbs_msd * msd , \ + msd_scsi_req_sense_resp *sense ) +{ + cyg_disk_info_t disk_info; + Cyg_ErrNo ret; + cyg_uint32 len = sizeof(cyg_disk_info_t); + cyg_uint32 key = CYG_IO_GET_CONFIG_DISK_INFO; + cyg_io_handle_t lun = msd->lun->handle[msd->cbw.lun]; + + TRACE("USB Slave MSD: SCSI test unit ready\n\r"); + + // Get the configuration of a device + ret = cyg_io_get_config( lun, key, &disk_info, &len); + + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + + USBS_MSD_SCSI_INIT_SENSE_DATA( sense->byte ); + + // check if disk is connected / mounted here + if( disk_info.connected == false ) + { + DBG("USB Slave MSD: Disk not mounted \n\r"); + usbs_msd_scsi_update_sense( sense, \ + USBS_MSD_SCSI_SENSE_ASC_MEDIUM_NOT_PRESENT, \ + USBS_MSD_SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT, \ + USBS_MSD_SCSI_SENSE_UNIT_ATTENTION); + + msd->csw.status = USBS_MSD_CSW_STATUS_FAILED; + } + + // No data to transfer + return 0; +} + + +// Windows host operating system uses this command. Usually if +// a command is not supported, the CSW should return failure. +// Since no data is sent in the data transfer phase, the TX +// endpoint shall be stalled. Since some USB driver in eCos +// do not support endpoint stall, is it required to respond +// with some data, otherwise the driver would freeze. In the +// following case, we fill the data structure but report +// failure in the CSW. +#if !defined(CYGSEM_IO_USB_SLAVE_MSD_STALL_ENABLE) +// Handle Read Format Capacities +// +static inline cyg_int32 +usbs_msd_scsi_read_format_capacities( usbs_msd * msd ) +{ + cyg_int32 i = 0; + DBG("USB Slave MSD: SCSI read format capacities\n\r"); + + for(i = 0 ; i < msd->cbw.data_transfert_len; i++) + { + msd->buffer[i] = 0; + } + + i = msd->send( msd, msd->buffer, i ); + + msd->csw.status = USBS_MSD_CSW_STATUS_FAILED; + + return i; +} +#endif + + +// Handle Verify +// +static inline cyg_int32 +usbs_msd_scsi_handle_verify( usbs_msd * msd ) +{ + DBG("USB Slave MSD: SCSI test unit verify\n\r"); + + // FIXME: Should do something here? + // Return success, command is optional + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + + // No data to transfer + return 0; +} + + +// Handle Medium Removal +// +static inline cyg_int32 +usbs_msd_scsi_handle_removal( usbs_msd * msd ) +{ + DBG("USB Slave MSD: SCSI Prevent / Allow device removal, LUN %d, flag %d %d\n\r", + ( msd->cbw.cb.data[1] & 0xf0 >> 4 ) , ( msd->cbw.cb.data[4] & 0x01 ), + msd->cbw.data_transfert_len ); + + // Support for this command is not mandatory. + // No specific handling of this command is implemented. + // Maybe mount / umount the disc? + msd->csw.status = USBS_MSD_CSW_STATUS_PASSED; + + // No data to transfer + return 0; +} + + +// Handle SCSI command +// +cyg_int32 +usbs_msd_scsi_handle_cmd( usbs_msd * msd ) +{ + bool ret; + cyg_uint8 cmd = msd->cbw.cb.data[0]; + msd_scsi_req_sense_resp *sense = (msd_scsi_req_sense_resp *) msd->handler_data; + + switch( cmd ) + { + case USBS_SCSI_INQUIRY: + ret = usbs_msd_scsi_handle_inquiry( msd ); + break; + + case USBS_SCSI_READ_CAPACITY: + ret = usbs_msd_scsi_handle_capacity( msd ); + break; + + case USBS_SCSI_READ_10: + ret = usbs_msd_scsi_handle_read(msd , sense ); + break; + + case USBS_SCSI_WRITE_10: + ret = usbs_msd_scsi_handle_write( msd , sense ); + break; + + case USBS_SCSI_REQUEST_SENSE: + ret = usbs_msd_scsi_handle_req_sense( msd , sense ); + break; + + case USBS_SCSI_SENSE_6: + ret = usbs_msd_scsi_handle_sense( msd , sense); + break; + + case USBS_SCSI_TEST_UNIT_READY: + ret = usbs_msd_scsi_handle_test_unit_ready( msd , sense ); + break; + + case USBS_SCSI_VERIFY_10: + ret = usbs_msd_scsi_handle_verify( msd ); + break; + +#if !defined(CYGSEM_IO_USB_SLAVE_MSD_STALL_ENABLE) + case USBS_SCSI_READ_FORMAT_CAPACITIES: + ret = usbs_msd_scsi_read_format_capacities( msd ); + break; +#endif + + case USBS_SCSI_PREVENT_ALLOW_REMOVAL: + ret = usbs_msd_scsi_handle_removal( msd ); + break; + + default: + // Use for all commands not implemented, not + // supported + DBG("USB Slave MSD: SCSI illegal request %x \n\r", cmd ); + USBS_MSD_SCSI_INIT_SENSE_DATA( sense->byte ); + usbs_msd_scsi_update_sense( sense, \ + USBS_MSD_SCSI_SENSE_ASC_INVALID_OPCODE, \ + USBS_MSD_SCSI_SENSE_ASCQ_INVALID_OPCODE, \ + USBS_MSD_SCSI_SENSE_ILLEGAL_REQUEST); + msd->csw.status = USBS_MSD_CSW_STATUS_FAILED; + ret = 0; + break; + + } + + return ret; + +} + +// SCSI initialization +// +bool +usbs_msd_scsi_init( void ** ctxt ) +{ + static msd_scsi_req_sense_resp sense; + + DBG("USB Slave MSD: SCSI init %p\n", &sense); + + USBS_MSD_SCSI_INIT_SENSE_DATA( sense.byte ); + + *ctxt = &sense; + + return true; +} diff --git a/packages/io/usb/msd/slave/current/tests/usbs_test_ramdisk.c b/packages/io/usb/msd/slave/current/tests/usbs_test_ramdisk.c new file mode 100644 --- /dev/null +++ b/packages/io/usb/msd/slave/current/tests/usbs_test_ramdisk.c @@ -0,0 +1,738 @@ +//========================================================================== +// +// usb_test_ramdisk.c +// +// Example application for the USB MSD layer in eCos. +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2008 Free Software Foundation, 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., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. +// +// 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): ccoutand +// Contributors: +// Date: 2010-06-27 +// Description: USB Slave Mass Storage example application. +// +//####DESCRIPTIONEND#### +//=========================================================================== + +#include +#include + +#if defined(CYGPKG_FS_FAT) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CYGPKG_FS_FAT) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(CYGBLD_IO_USB_SLAVE_MSD_DEBUG) +#define DBG diag_printf +#else +#define DBG (1) ? (void)0 : diag_printf +#endif + +// ---------------------------------------------------------------------------- +// FAT12 file-system FBR, FAT and ROOT directory entries are hardcoded +// and later copied over to the RAM disk +// + +#define USBS_MSD_RAMDISK_BLOCKLEN 512 +#define USBS_MSD_RAMDISK_NUM_BLOCK 32 + +// Allocate space for the RAM disk +static char ram_disk[ USBS_MSD_RAMDISK_BLOCKLEN * USBS_MSD_RAMDISK_NUM_BLOCK ] __attribute__((aligned(4))); + +// FAT12 Boot Record +const char fat12_br[] = { + 0xEB, 0x3C, 0x90, // Field for Bootable Media + 0x4D, 0x53, 0x57, 0x49, 0x4E, 0x34, 0x2E, 0x31, // MSWIN4.1 + 0x00, 0x02, // 512 bytes sector + 0x01, // 1 sector per cluster + 0x01, 0x00, // 1 reserved sector (including MBR) + 0x02, // 2 FATs + 0x40, 0x00, // 64 entries + USBS_MSD_RAMDISK_NUM_BLOCK, 0x00, // Number of sectors is less than 32K: 32 sectors + 0xF0, // Removable media + 0x01, 0x00, // 1sector per FAT + 0x00, 0x00, // Sector per track (not used in LBA) + 0x00, 0x00, // Number of Heads (not used in LBA) + 0x00, 0x00, 0x00, 0x00, // Hidden sector + 0x00, 0x00, 0x00, 0x00, // Number of sectors is more than 32K + 0x00, // Logical drive number + 0x00, // Reserved + 0x29, // Extended boot signature + 0x04, 0x03, 0x02, 0x01, // Serial Number + 0x65, 0x43, 0x6F, 0x73, 0x20, 0x64, 0x69, 0x73, // VOLUME: eCos disk + 0x6B, 0x20, 0x20, + 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20 // FAT12 +}; + +// MBR and Volume sector signature +const char mbr_signature[] = { + 0x55, 0xAA +}; + +// Partition table +const char partition_tbl[] = { + // First (FAT12) + 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, (USBS_MSD_RAMDISK_NUM_BLOCK-1), 0x00, 0x00, 0x00, + // Second + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Third + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Fourth + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// Root directory (eCos-3.0) +const char root_dir[] = { + 0x65, 0x43, 0x6F, 0x73, 0x2d, 0x33, 0x2e, 0x30, + 0x20, 0x20, 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x9e, + 0x65, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// File-system Allocation Table +const char fat[] = { + 0xF0, 0xff, 0xff +}; + +// Initialize FAT12 +static void +init_fat12_disk( char *start_addr ) +{ + char * dst = start_addr; + + // Copy partition table + dst = start_addr + 446; + memcpy(dst, partition_tbl, 64); + + // Copy signature + dst = start_addr + (USBS_MSD_RAMDISK_BLOCKLEN - 2 ); + memcpy(dst, mbr_signature, 2); + + // Copy FBR + dst = start_addr + USBS_MSD_RAMDISK_BLOCKLEN; + memcpy(dst, fat12_br, 62); + + // Copy signature + dst = start_addr + (( USBS_MSD_RAMDISK_BLOCKLEN * 2 ) - 2 ); + memcpy(dst, mbr_signature, 2); + + // Copy FAT0 + dst = start_addr + ( USBS_MSD_RAMDISK_BLOCKLEN * 2); + memcpy(dst, fat, 3); + + // Copy FAT1 + dst = start_addr + ( USBS_MSD_RAMDISK_BLOCKLEN * 3); + memcpy(dst, fat, 3); + + // Copy Root directory + dst = start_addr + ( USBS_MSD_RAMDISK_BLOCKLEN * 4); + memcpy(dst, root_dir, 32); + +} + + +// ---------------------------------------------------------------------------- +// Create a tiny "RAM disk" device +// +// + +#define RAM_DISK_NAME "/dev/ramdisk0/" + +typedef struct { + volatile cyg_uint8 *base; + cyg_uint16 size; +} ram_disk_info_t; + +static cyg_bool ram_disk_init(struct cyg_devtab_entry *tab); + +static Cyg_ErrNo ram_disk_bread(disk_channel *chan, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + + +static Cyg_ErrNo ram_disk_bwrite(disk_channel *chan, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + +static Cyg_ErrNo ram_disk_get_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + +static Cyg_ErrNo ram_disk_set_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + +static Cyg_ErrNo ram_disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); + +DISK_FUNS(ram_disk_funs, + ram_disk_bread, + ram_disk_bwrite, + ram_disk_get_config, + ram_disk_set_config +); + +DISK_CONTROLLER(ram_disk_controller, ram_disk_controller); + +#define RAM_DISK_INSTANCE(_number_,_base_,_mbr_supp_,_name_) \ +static ram_disk_info_t ram_disk_info##_number_ = { \ + base: (volatile cyg_uint8 *)_base_, \ +}; \ +DISK_CHANNEL(ram_disk_channel##_number_, \ + ram_disk_funs, \ + ram_disk_info##_number_, \ + ram_disk_controller, \ + _mbr_supp_, \ + 1 \ +); \ +BLOCK_DEVTAB_ENTRY(ram_disk_io##_number_, \ + _name_, \ + 0, \ + &cyg_io_disk_devio, \ + ram_disk_init, \ + ram_disk_lookup, \ + &ram_disk_channel##_number_ \ +); + +RAM_DISK_INSTANCE(0, 0, true, RAM_DISK_NAME); + + +// ---------------------------------------------------------------------------- +static void +read_data(volatile cyg_uint8 *base, + void *buf, + cyg_uint32 len) +{ + cyg_uint8 *dst = (cyg_uint8 *)buf; + cyg_uint8 *src = (cyg_uint8 *)base; + cyg_uint32 i = 0; + + for (i = 0; i < len; i += 1) + { + *dst = *src; + dst = dst + 1; src = src + 1; + } + +} + +static void +write_data(volatile cyg_uint8 *base, + const void *buf, + cyg_uint16 len) +{ + cyg_uint8 *dst = (cyg_uint8 *)base; + cyg_uint8 *src = (cyg_uint8 *)buf; + cyg_uint32 i = 0; + + for (i = 0; i < len; i += 1) + { + *dst = *src; + dst = dst + 1; src = src + 1; + } +} + +// ---------------------------------------------------------------------------- +static cyg_bool +ram_disk_init(struct cyg_devtab_entry *tab) +{ + disk_channel *chan = (disk_channel *) tab->priv; + ram_disk_info_t *info = (ram_disk_info_t *) chan->dev_priv; + cyg_disk_identify_t ident; + + if (chan->init) + return true; + + info->base = (volatile cyg_uint8 *) &ram_disk[0]; + info->size = USBS_MSD_RAMDISK_BLOCKLEN * USBS_MSD_RAMDISK_NUM_BLOCK; + + DBG("RAM DISK ( %p ) init\n", info->base); + + init_fat12_disk( &ram_disk[0] ); + + ident.cylinders_num = 0; + ident.heads_num = 0; + ident.lba_sectors_num = USBS_MSD_RAMDISK_NUM_BLOCK-2; + ident.phys_block_size = USBS_MSD_RAMDISK_BLOCKLEN; + ident.max_transfer = USBS_MSD_RAMDISK_BLOCKLEN; + + if (!(chan->callbacks->disk_init)(tab)) + return false; + + if (ENOERR != (chan->callbacks->disk_connected)(tab, &ident)) + return false; + + return true; +} + +static Cyg_ErrNo +ram_disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + disk_channel *chan = (disk_channel *) (*tab)->priv; + return (chan->callbacks->disk_lookup)(tab, sub_tab, name); +} + +static Cyg_ErrNo +ram_disk_bread(disk_channel *chan, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num) +{ + ram_disk_info_t *info = (ram_disk_info_t *)chan->dev_priv; + volatile cyg_uint8 *src = info->base + USBS_MSD_RAMDISK_BLOCKLEN * block_num; + cyg_uint32 len_byte = len * USBS_MSD_RAMDISK_BLOCKLEN; + + if ( len_byte == 0 ) + return EINVAL; + + read_data(src, buf, len_byte); + + return ENOERR; +} + +static Cyg_ErrNo +ram_disk_bwrite(disk_channel *chan, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num) +{ + ram_disk_info_t *info = (ram_disk_info_t *)chan->dev_priv; + volatile cyg_uint8 *dst = info->base + USBS_MSD_RAMDISK_BLOCKLEN * block_num; + cyg_uint32 len_byte = len * USBS_MSD_RAMDISK_BLOCKLEN; + + if ( len_byte == 0 ) + return EINVAL; + + write_data(dst, buf, len_byte); + + return ENOERR; +} + +static Cyg_ErrNo +ram_disk_get_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + return -EINVAL; +} + +static Cyg_ErrNo +ram_disk_set_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + return -EINVAL; +} + + +// ---------------------------------------------------------------------------- +// In case FAT file-system package is included, this application will try +// to play with the file-system before it is mounted as a mass storage device. +// The following code is a copy of the fatfs1.c from the fs/test directory +// +#if defined(CYGPKG_FS_FAT) + +#define SHOW_RESULT( _fn, _res ) \ + DBG(": " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):""); + +#define IOSIZE 100 + +//========================================================================== + +void checkcwd( const char *cwd ) +{ + static char cwdbuf[PATH_MAX]; + char *ret; + + ret = getcwd( cwdbuf, sizeof(cwdbuf)); + if( ret == NULL ) SHOW_RESULT( getcwd, (int)ret ); + + if( strcmp( cwdbuf, cwd ) != 0 ) + { + DBG( "cwdbuf %s cwd %s\n",cwdbuf, cwd ); + CYG_TEST_FAIL( "Current directory mismatch"); + } +} + +//========================================================================== + +static void listdir( char *name, int statp, int numexpected, int *numgot ) +{ + int err; + DIR *dirp; + int num=0; + + DBG(": reading directory %s\n",name); + + dirp = opendir( name ); + if( dirp == NULL ) SHOW_RESULT( opendir, -1 ); + + for(;;) + { + struct dirent *entry = readdir( dirp ); + + if( entry == NULL ) + break; + num++; + DBG(": entry %14s",entry->d_name); +#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE + DBG(" d_type %2x", entry->d_type); +#endif + if( statp ) + { + char fullname[PATH_MAX]; + struct stat sbuf; + + if( name[0] ) + { + strcpy(fullname, name ); + if( !(name[0] == '/' && name[1] == 0 ) ) + strcat(fullname, "/" ); + } + else fullname[0] = 0; + + strcat(fullname, entry->d_name ); + + err = stat( fullname, &sbuf ); + if( err < 0 ) + { + if( errno == ENOSYS ) + DBG(" "); + else SHOW_RESULT( stat, err ); + } + else + { + DBG(" [mode %08x ino %08x nlink %d size %ld]", + sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,(long)sbuf.st_size); + } +#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE + if ((entry->d_type & S_IFMT) != (sbuf.st_mode & S_IFMT)) + CYG_TEST_FAIL("File mode's don't match between dirent and stat"); +#endif + } + + DBG("\n"); + } + + err = closedir( dirp ); + if( err < 0 ) SHOW_RESULT( stat, err ); + if (numexpected >= 0 && num != numexpected) + CYG_TEST_FAIL("Wrong number of dir entries\n"); + if ( numgot != NULL ) + *numgot = num; +} + +//========================================================================== + +static void createfile( char *name, size_t size ) +{ + char buf[IOSIZE]; + int fd; + ssize_t wrote; + int i; + int err; + + DBG(": create file %s size %zd \n",name,size); + + err = access( name, F_OK ); + if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err ); + + for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256; + + fd = open( name, O_WRONLY|O_CREAT ); + if( fd < 0 ) SHOW_RESULT( open, fd ); + + while( size > 0 ) + { + ssize_t len = size; + if ( len > IOSIZE ) len = IOSIZE; + + wrote = write( fd, buf, len ); + if( wrote != len ) SHOW_RESULT( write, (int)wrote ); + + size -= wrote; + } + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void checkfile( char *name ) +{ + char buf[IOSIZE]; + int fd; + ssize_t done; + int i; + int err; + off_t pos = 0; + + DBG(": check file %s\n",name); + + err = access( name, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd = open( name, O_RDONLY ); + if( fd < 0 ) SHOW_RESULT( open, fd ); + + for(;;) + { + done = read( fd, buf, IOSIZE ); + if( done < 0 ) SHOW_RESULT( read, (int)done ); + + if( done == 0 ) break; + + for( i = 0; i < done; i++ ) + if( buf[i] != i%256 ) + { + DBG("buf[%ld+%d](%02x) != %02x\n",pos,i,buf[i],i%256); + CYG_TEST_FAIL("Data read not equal to data written\n"); + } + + pos += done; + } + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void copyfile( char *name2, char *name1 ) +{ + + int err; + char buf[IOSIZE]; + int fd1, fd2; + ssize_t done, wrote; + + DBG(": copy file %s -> %s\n",name2,name1); + + err = access( name1, F_OK ); + if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err ); + + err = access( name2, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd1 = open( name1, O_WRONLY|O_CREAT ); + if( fd1 < 0 ) SHOW_RESULT( open, fd1 ); + + fd2 = open( name2, O_RDONLY ); + if( fd2 < 0 ) SHOW_RESULT( open, fd2 ); + + for(;;) + { + done = read( fd2, buf, IOSIZE ); + if( done < 0 ) SHOW_RESULT( read, (int)done ); + + if( done == 0 ) break; + + wrote = write( fd1, buf, done ); + if( wrote != done ) SHOW_RESULT( write, (int) wrote ); + + if( wrote != done ) break; + } + + err = close( fd1 ); + if( err < 0 ) SHOW_RESULT( close, err ); + + err = close( fd2 ); + if( err < 0 ) SHOW_RESULT( close, err ); + +} + +//========================================================================== + +static void comparefiles( char *name2, char *name1 ) +{ + int err; + char buf1[IOSIZE]; + char buf2[IOSIZE]; + int fd1, fd2; + ssize_t done1, done2; + int i; + + DBG(": compare files %s == %s\n",name2,name1); + + err = access( name1, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + err = access( name1, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd1 = open( name1, O_RDONLY ); + if( fd1 < 0 ) SHOW_RESULT( open, fd1 ); + + fd2 = open( name2, O_RDONLY ); + if( fd2 < 0 ) SHOW_RESULT( open, fd2 ); + + for(;;) + { + done1 = read( fd1, buf1, IOSIZE ); + if( done1 < 0 ) SHOW_RESULT( read, (int)done1 ); + + done2 = read( fd2, buf2, IOSIZE ); + if( done2 < 0 ) SHOW_RESULT( read, (int)done2 ); + + if( done1 != done2 ) + DBG("Files different sizes\n"); + + if( done1 == 0 ) break; + + for( i = 0; i < done1; i++ ) + if( buf1[i] != buf2[i] ) + { + DBG("buf1[%d](%02x) != buf1[%d](%02x)\n",i,buf1[i],i,buf2[i]); + CYG_TEST_FAIL("Data in files not equal\n"); + } + } + + err = close( fd1 ); + if( err < 0 ) SHOW_RESULT( close, err ); + + err = close( fd2 ); + if( err < 0 ) SHOW_RESULT( close, err ); + +} + +#endif + + + +// ---------------------------------------------------------------------------- +// Entry point of the test application. If the FAT file-system package is +// included, the file-system is mounted and tested before the USB Mass Storage +// service is started. +// +// Once the mass storage device is mounted on the host computer, 2 files +// (foo and fee) and one directory (bar) should appear in the device +// + +int cyg_start(void) +{ +#if defined(CYGPKG_FS_FAT) + int err; + int existingdirents=-1; +#endif + + DBG("Start USB MSD application\n\r"); + +#if defined(CYGPKG_FS_FAT) + + // Mount RAM disk partition 1 + err = mount( "/dev/ramdisk0/1", "/", "fatfs" ); + + if( err < 0 ) + SHOW_RESULT( mount, err ); + + err = chdir( "/" ); + if( err < 0 ) + SHOW_RESULT( chdir, err ); + + checkcwd( "/" ); + + // Display list of all files/directories from root + listdir( "/", true, -1, &existingdirents ); + + // Play around with the file-system, create / copy / + // compare files + createfile( "/foo", 1000 ); + checkfile( "foo" ); + copyfile( "foo", "fee" ); + checkfile( "fee" ); + comparefiles( "foo", "/fee" ); + DBG(": mkdir bar\n"); + + // Create new directory + err = mkdir( "/bar", 0 ); + if( err < 0 ) + SHOW_RESULT( mkdir, err ); + + // Display list of all files/directories from root + listdir( "/" , true, existingdirents+3, NULL ); + + // Umount file-system + err = umount( "/" ); + + if( err < 0 ) + SHOW_RESULT( umount, err ); + +#endif + + // Start Mass Storage Service + usbs_msd_start(); + + // Start scheduler + cyg_scheduler_start(); + +}