July 2007 | Clifton Labs Inc. | All text is available under the terms of the GNU Free Documentation License . Some Code Segments from Atmel. Revision 0.2 USB Mass Storage & CDC Interfaces Abstract Contents It is convenient to be able to have a mass storage device functioning, and also have two way communication to and from the same device over the same physical connection. As the mass storage device takes 2 endpoints (in addition to the required control endpoint 0) and it is possible to have uart over usb through the cdc interface (which requires 3 endpoints). We use a standard development kit and existing code examples to show the feasibility of creating such a device. The following tutorial describes the integration of the Atmel Mass Storage and CDC examples as well as the driver modification which must take place in order to have a device which successfully functions as a composite device simultaneously allowing a mass storage device and com communication. Abstract 1 Pre-requisites 1 Environment Setup 2 Configuration Explained 2 Adding CDC Task 2 USB Config Code 3-5 Driver Setup 5 Pre-requisites *Helpful but can be done without depending on circumstances. " There's always another way" Environment Setup After downloading both the Mass Storage and CDC demos from Atmel insure that both work on you device as expected (the CDC demo will come with the needed driver and assuming you have Win 2K or XP you'll have a Mass Storage driver as well). Create a new project by copying the Atmel Mass Storage Project to a new location. Your must then modify the *.aps project file located in the gcc directory. You must replace all instances of "C:\Atmel\at90usbl28-..." to your current path location. Now using AVR studio open the aps file and compile it. It should produce no errors. Now examine both demo folders (CDC and Mass Storage) and incorporate all files that are unique to the CDC demo into the Mass Storage demo. (A file listing of the merged demos can be found at the end of this tutorial. Adding the CDC task to the Makefile and scheduler The USB Device, Interface, and Endpoint configuration. Edit the makefile (located in the gcc directory) to include On the OBJECTS = line Uart_usb_lib.o uart_lib.o and cdc_task.o to In the ##Build section uart_drv.o: /../common/lib_mcu/uart_drv.c $(CC) $(INCLUDES) $(CFLAGS) -c $< uart_lib.o: /common/lib_mcu/uart_lib.c $(CC) $(INCLUDES) $(CFLAGS) -c $< uart_usb_lib.o: ../uart_usb_lib.c $(CC) $(INCLUDES) $(CFLAGS) -c $< cdc_task.o: ../cdc_task.c $(CC) $(INCLUDES) $(CFLAGS) -c $< Now the easy part, open conf_scheduler.h and add the following lines: #define Scheduler_task_3_init cdc_task_init #define Scheduler_task_3 cdc_task Make sure everything compiles correctly Before we integrate the cdc task into the usb specific requests, we should first define and understand the configuration of the device (at its many levels). Below are depictions of the two standard Mass Storage and CDC configurations. era c n> OJ to to CO rh o ft) era 0 n o ora c 0 r+ ■Bl o First and foremost, Microsoft cannot access multiple configurations simultaneously. Therefore we must keep 1 configuration. The first and seemly straightforward solution for integration is to have a configuration with three interfaces. Unfortunately, unless you want to write custom drivers it doesn't work (several claims have been made online that you can use a feature in XP though no substantiation of the claims has been seen). The solution is depicted in figure 3 (The code for defining this is found on the following pages). ora c 0 w CO 0 3 Q_ O a o 3 r+ 0 —l ■ti 0 O 0 in 3 USB Configuration (code) ( changes | additions) config.h tfdefine BAUDRATE 38400 if define UART_U2 tfdefine NB_MS_BEFORE_FLUSH 50 ftdefine REPEATE_KEY_PRESSED 100 conf_usb.h #define NB ENDPOINTS 6 #define EP MS IN 4 //Mass Storage In tfdefine EP MS OUT 5 //Mass Storage Out ftdefine TX_EP 0x01 //CDC IN ftdefine RX_EP 0x02 //CDC OUT ftdefine INT_EP 0x03 //Interrupt ftdefine Usb_suspend_action suspend_action(); Extern void suspend action(void): usb_descriptors.h (configuration parameters) // USB Device descriptor tfdefine USB_SPECIFICATION 0x0200 tfdefine DEVICE_CLASS 0x00 tfdefine DEVICE_SUB_CLASS 0 tfdefine DEVICE_PROTOCOL 0 tfdefine EPCONTROLLENGTH 64 tfdefine VENDORJD OxXXXX // CHANGE tfdefine PRODUCTJD OxYYYY // CHANGE tfdefine RELEASE NUMBER 0x1000 tfdefine MAN INDEX 0x01 tfdefine PRODJNDEX 0x02 tfdefine SN INDEX 0x03 tfdefine NB_CONFIGU RATION 1 // CONFIGURATION tfdefine NB INTERFACE 2 tfdefine CONF_NB 1 tfdefine CONFJNDEX 0 tfdefine CONF_ATTRIBUTES USB_CONFIG_BUSPOWERED tfdefine MAX_POWER 50 // START OF CDC INTERFACE // //Interface 1 descriptor tfdefine INTERFACE0 NB 0 tfdefine ALTERNATE0 0 tfdefine NBENDPOINTO 3 tfdefine INTERFACEOCLASS 0x02 tfdefine INTERFACE0_SUB_CLASS 0x02 tfdefine INTERFACEO PROTOCOL 0x00 tfdefine INTERFACEO INDEX 0 //USB ENDPOINT 3 descriptor //INTERUPTIN tfdefine TX_EP_SIZE 0x20 tfdefine ENDPOINT NB 3 (0x80 | INT_EP) tfdefine EP_ATTRIBUTES_3 0x03 tfdefine EP_SIZE_3 0x08 tfdefine EP INTERVAL 3 0x10 //USB ENDPOINT 1 descriptor //BULK OUT tfdefine ENDPOINT NB1 RX_EP tfdefine EP ATTRIBUTES1 0x02 tfdefine EP_SIZE_1 0x40 tfdefine EPINTERVAL1 0x00 //USB ENDPOINT 2 descriptor //BULK IN tfdefine ENDPOINT NB 2 (0x80 | TX_EP) tfdefine EP_ATTRIBUTES_2 0x02 tfdefine EP_SIZE_2 0x40 tfdefine EPINTERVAL2 0x00 // END OF CDC INTERFACE // // START OF USB MS INTERFACE // USB Interface descriptor tfdefine INTERFACE1NB 1 tfdefine ALTERNATE1 0 tfdefine NB ENDPOINT1 2 tfdefine INTERFACE1CLASS 0x08 // Mass Storage Class tfdefine INTERFACE1_SUB_CLASS 0x06 tfdefine INTERFACE1PROTOCOL 0x50 tfdefine INTERFACE1INDEX 0 // USB Endpoint 4 descriptor FS tfdefine ENDPOINT NB 4 EP_MS_OUT tfdefine EP_ATTRIBUTES_4 0x02 tfdefine EPINLENGTH 64 tfdefine EP_SIZE_4 EP IN LENGTH tfdefine EP_INTERVAL_4 0x00 // USB Endpoint 5 descriptor FS tfdefine ENDPOINT_NB_5 (EP_MS_IN | 0x80) tfdefine EP_ATTRIBUTES_5 0x02 tfdefine EP IN LENGTH 64 tfdefine EP_SIZE_5 EP IN LENGTH tfdefine EP INTERVAL 5 0x00 The Author and Clifton Labs, Inc. assume no responsibility for damage caused to device, host system or anything else for that matter. Some of the code segments herein belong to ATMEL (though not integrated in this fashion). All original code is found in the examples stated earlier, and code here is deals with configuration and modification of the device, interface and endpoint configurations. Please reference their legal notice. Clifton Labs, Inc. 7450 Montgomery Rd, Suite 300 Cincinnati, Ohio 45236 Mike Borowczak borowcm [at] cliftonlabs [dot] com Computer Engineering Services & Solutions Online at www.cliftonlabs.com typedef struct { S_usb_configuration_descriptor cfg; S_usb_interface_descriptor ifcO; U8 CS_INTERFACE[19]; S_usb_endpoint_descriptor ep3; S_usb_endpoint_descriptor epl; S_usb_endpoint_descriptor ep2; S_usb_interface_descriptor ifcl; S_usb_endpoint_descriptor ep4; S_usb_endpoint_descriptor ep5; } S_usb_user_configuration_descriptor; USB Configuration (code) cont. Usb_descriptors.c // usb_user_configuration_descriptor FS code S_usb_user_configuration_descriptor usb_conf_desc = { { sizeof(S_usb_configuration_descriptor) , CONFIGURATION_DESCRIPTOR / Usb_write_word_enum_struc(sizeof(usb_conf _desc)) , NBJNTERFACE , CONF_NB , CONFJNDEX , CONF_ATTRIBUTES , MAX_POWER } / { sizeof(S_usb_interface_descriptor) , INTERFACE_DESCRIPTOR , INTERFACEO_NB , ALTERNATEO , NB_ENDPOINTO , INTERFACEO_CLASS , INTERFACEO_SUB_CLASS , INTERFACEO_PROTOCOL , INTERFACEOJNDEX } { 0x05, 0x24, 0x00, 0x10, 0x01, 0x05, 0x24, 0x01, 0x03, 0x01, 0x04, 0x24, 0x02, 0x06,0x05, 0x24, 0x06, 0x00, 0x01 } / { sizeof(S_usb_endpoint_descriptor) , ENDPOINT_DESCRIPTOR , ENDPOINT_NB_3 , EP_ATTRIBUTES_3 , Usb_write_word_enum_struc(EP_SIZE_3) , EP_INTERVAL_3 } / { sizeof(S_usb_endpoint_descriptor) , ENDPOINT_DESCRIPTOR , ENDPOINT_NB_l , EP_ATTRIBUTES_1 , Usb_write_word_enum_struc(EP_SIZE_l) , EP_INTERVAL_1 } / { sizeof(S_usb_endpoint_descriptor) , ENDPOINT_DESCRIPTOR , ENDPOINT_NB_2 , EP_ATTRIBUTES_2 , Usb_write_word_enum_struc(EP_SIZE_2) , EP_INTERVAL_2 } { sizeof(S_usb_interface_descriptor) , INTERFACE_DESCRIPTOR , INTERFACE1_NB , ALTERNATE1 , NB_ENDP0INT1 , INTERFACE1_CLASS , INTERFACE1_SUB_CLASS , INTERFACEl_PROTOCOL , INTERFACE1JNDEX } / { sizeof(S_usb_endpoint_descriptor) , ENDPOINT_DESCRIPTOR , ENDPOINT_NB_4 , EP_ATTRIBUTES_4 , Usb_write_word_enum_struc(EP_SIZE_4) , EP_INTERVAL_4 } t { sizeof(S_usb_endpoint_descriptor) , ENDPOINT_DESCRIPTOR , ENDPOINT_NB_5 , EP_ATTRIBUTES_5 , Usb_write_word_enum_struc(EP_SIZE_5) , EP_INTERVAL_5 } }; Config 1 USB Configuration (code) cont. Driver Setup: Step 1: Follow directions in CDC example code to install the custom driver normally (this should have been done earlier when you tested the example prior to messing around with all of this :) Step 2: Edit the .inf file and add one line: %ATMEL_CDC%=Reader, USB\VID_XXXX&PI D_YY YY & M I _0 0 Step 3: Find and backup your usbstore.inf file now add the following line under the Microsoft section %GenericBulkOnly.DeviceDesc%=U SBSTOR_BULK, U S B\V I D_XXXX& P I D_YYYY& M I _0 1 XXXX and YYYY are the values specifies in your usb_descriptor.h file Step 4: Backup your registry, use regedt32 goto: HKEY_LOCAL_MACHINE -^System ->ControlSetOOX (all of them) +->Enum ++->USB (delete all references to your device) ++->USBSTOR (delete all references to your device) [Changing the security may be needed but is easy to do] Step 5: Program and Start you device as normal, you should be asked to install software (always do it manually and it should work fine (i.e. don't let the online software tell you which driver to use). WARNING: Before disconnecting your device YOU MUST uninstall the AT90USBxxx CDC USB to UART MGM (COM x) otherwise you'll have to manually delete registry entries. Usb_specific_request.c Usb_specific_request.c (cont) #include "lib_mcu\uart\uart_lib.h" void cdc get line coding(void) extern S_line_coding line_coding; { Usb_ack_receive_setup(); switch(request) Usb_write_byte(LSBO(line_coding.dwDTERate)); { Usb_write_byte(LSBl(line_coding.dwDTERate)); ... Usb_write_byte(LSB2(line_coding.dwDTERate)); case GET_LINE_CODING: Usb_write_byte(LSB3(line_coding.dwDTERate)); cdc_get_line_coding(); Usb_write_byte(line_coding.bCharFormat); return TRUE; Usb_write_byte(line_coding.bParityType); break; Usb_write_byte(line_coding.bDataBits); case SET_LINE_CODING: Usb_send_controlJn(); cdc_set_line_coding(); while(!(ls_usb_read_control_enabled())); return TRUE; //Usb_clear_tx_complete(); break; while(!ls_usb_receive_out()); case SET_CONTROL_LINE_STATE: Usb ack receive out(); cdc_set_control_line_state(); } return TRUE; break; void cdc set line coding (void) default: { return FALSE; Usb_ack_receive_setup(); break; while (!(ls_usb_receive_out())); } LSBO(line_coding.dwDTERate) = Usb_read_byte(); void usb user endpoint init(U8conf nb) LSBl(line_coding.dwDTERate) = { Usb_read_byte(); usb_configure_endpoint(EP_MS_IN, \ LSB2(line_coding.dwDTERate) = TYPE_BULK, \ Usb_read_byte(); DIRECTIONJN, \ LSB3(line_coding.dwDTERate) = ep_size, \ Usb_read_byte(); TWO_BANKS, \ line_coding.bCharFormat = Usb_read_byte(); NYET_ENABLED); line_coding.bParityType = Usb_read_byte(); usb_configure_endpoint(EP_MS_OUT, \ line_coding.bDataBits = Usb_read_byte(); TYPE_BULK, \ Usb_ack_receive_out(); DIRECTION_OUT, \ Usb_send_controlJn(); ep_size, \ while(!(ls_usb_read_control_enabled())); TWO_BANKS, \ #ifdef UART_U2 NYET_ENABLED); usb_configure_endpoint(INT_EP, \ Uart_set_baudrate((line_coding.dwDTERate)/2); TYPEJNTERRUPT, \ #else DIRECTIONJN, \ Uart_set_baudrate(line_coding.dwDTERate); SIZE_32, \ #endif ONE_BANK, \ } NYET_ENABLED); usb_configure_endpoint(TX_EP, \ TYPE_BULK, \ void cdc_set_control_line_state (void) DIRECTIONJN, \ SIZE 32, \ ONE_BANK, \ Usb specific request. h NYET_ENABLED); usb_configure_endpoint(RX_EP, \ #define GET LINE CODING 0x21 TYPE_BULK, \ #define SET LINE CODING 0x20 DIRECTION_OUT, \ #define SET CONTROL LINE STATE 0x22 SIZE 32, \ TWO_BANKS, \ void cdc get line coding(); NYET_ENABLED); void cdc_setJine_coding(); void cdc set control line state (void); Usb_reset_endpoint(EP_MS_IN); Usb_reset_endpoint(EP_MS_OUT); typedef struct Usb_reset_endpoint(INT_EP); { U32 dwDTERate; Usb_reset_endpoint(TX_EP); Usb_reset_endpoint(RX_EP); } U8 bCharFormat; U8 bParityType; U8 bDataBits; }SJine_coding;