Merge remote-tracking branch 'upstream/master' into edpt_ISO_xfer
This commit is contained in:
		| @@ -95,7 +95,8 @@ static void _prep_out_transaction (cdcd_interface_t* p_cdc) | ||||
|   // fifo can be changed before endpoint is claimed | ||||
|   available = tu_fifo_remaining(&p_cdc->rx_ff); | ||||
|  | ||||
|   if ( available >= sizeof(p_cdc->epout_buf) )  { | ||||
|   if ( available >= sizeof(p_cdc->epout_buf) ) | ||||
|   { | ||||
|     usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); | ||||
|   }else | ||||
|   { | ||||
| @@ -432,25 +433,27 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ | ||||
|   // Received new data | ||||
|   if ( ep_addr == p_cdc->ep_out ) | ||||
|   { | ||||
|     // TODO search for wanted char first for better performance | ||||
|     for(uint32_t i=0; i<xferred_bytes; i++) | ||||
|     tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, xferred_bytes); | ||||
|      | ||||
|     // Check for wanted char and invoke callback if needed | ||||
|     if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) ) | ||||
|     { | ||||
|       tu_fifo_write(&p_cdc->rx_ff, &p_cdc->epout_buf[i]); | ||||
|  | ||||
|       // Check for wanted char and invoke callback if needed | ||||
|       if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) ) | ||||
|       for ( uint32_t i = 0; i < xferred_bytes; i++ ) | ||||
|       { | ||||
|         tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); | ||||
|         if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) ) | ||||
|         { | ||||
|           tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|      | ||||
|     // invoke receive callback (if there is still data) | ||||
|     if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); | ||||
|  | ||||
|     if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); | ||||
|      | ||||
|     // prepare for OUT transaction | ||||
|     _prep_out_transaction(p_cdc); | ||||
|   } | ||||
|  | ||||
|    | ||||
|   // Data sent to host, we continue to fetch from tx fifo to send. | ||||
|   // Note: This will cause incorrect baudrate set in line coding. | ||||
|   //       Though maybe the baudrate is not really important !!! | ||||
|   | ||||
| @@ -303,118 +303,177 @@ typedef enum | ||||
| //--------------------------------------------------------------------+ | ||||
| // HID KEYCODE | ||||
| //--------------------------------------------------------------------+ | ||||
| #define HID_KEY_NONE               0x00 | ||||
| #define HID_KEY_A                  0x04 | ||||
| #define HID_KEY_B                  0x05 | ||||
| #define HID_KEY_C                  0x06 | ||||
| #define HID_KEY_D                  0x07 | ||||
| #define HID_KEY_E                  0x08 | ||||
| #define HID_KEY_F                  0x09 | ||||
| #define HID_KEY_G                  0x0A | ||||
| #define HID_KEY_H                  0x0B | ||||
| #define HID_KEY_I                  0x0C | ||||
| #define HID_KEY_J                  0x0D | ||||
| #define HID_KEY_K                  0x0E | ||||
| #define HID_KEY_L                  0x0F | ||||
| #define HID_KEY_M                  0x10 | ||||
| #define HID_KEY_N                  0x11 | ||||
| #define HID_KEY_O                  0x12 | ||||
| #define HID_KEY_P                  0x13 | ||||
| #define HID_KEY_Q                  0x14 | ||||
| #define HID_KEY_R                  0x15 | ||||
| #define HID_KEY_S                  0x16 | ||||
| #define HID_KEY_T                  0x17 | ||||
| #define HID_KEY_U                  0x18 | ||||
| #define HID_KEY_V                  0x19 | ||||
| #define HID_KEY_W                  0x1A | ||||
| #define HID_KEY_X                  0x1B | ||||
| #define HID_KEY_Y                  0x1C | ||||
| #define HID_KEY_Z                  0x1D | ||||
| #define HID_KEY_1                  0x1E | ||||
| #define HID_KEY_2                  0x1F | ||||
| #define HID_KEY_3                  0x20 | ||||
| #define HID_KEY_4                  0x21 | ||||
| #define HID_KEY_5                  0x22 | ||||
| #define HID_KEY_6                  0x23 | ||||
| #define HID_KEY_7                  0x24 | ||||
| #define HID_KEY_8                  0x25 | ||||
| #define HID_KEY_9                  0x26 | ||||
| #define HID_KEY_0                  0x27 | ||||
| #define HID_KEY_RETURN             0x28 | ||||
| #define HID_KEY_ESCAPE             0x29 | ||||
| #define HID_KEY_BACKSPACE          0x2A | ||||
| #define HID_KEY_TAB                0x2B | ||||
| #define HID_KEY_SPACE              0x2C | ||||
| #define HID_KEY_MINUS              0x2D | ||||
| #define HID_KEY_EQUAL              0x2E | ||||
| #define HID_KEY_BRACKET_LEFT       0x2F | ||||
| #define HID_KEY_BRACKET_RIGHT      0x30 | ||||
| #define HID_KEY_BACKSLASH          0x31 | ||||
| #define HID_KEY_EUROPE_1           0x32 | ||||
| #define HID_KEY_SEMICOLON          0x33 | ||||
| #define HID_KEY_APOSTROPHE         0x34 | ||||
| #define HID_KEY_GRAVE              0x35 | ||||
| #define HID_KEY_COMMA              0x36 | ||||
| #define HID_KEY_PERIOD             0x37 | ||||
| #define HID_KEY_SLASH              0x38 | ||||
| #define HID_KEY_CAPS_LOCK          0x39 | ||||
| #define HID_KEY_F1                 0x3A | ||||
| #define HID_KEY_F2                 0x3B | ||||
| #define HID_KEY_F3                 0x3C | ||||
| #define HID_KEY_F4                 0x3D | ||||
| #define HID_KEY_F5                 0x3E | ||||
| #define HID_KEY_F6                 0x3F | ||||
| #define HID_KEY_F7                 0x40 | ||||
| #define HID_KEY_F8                 0x41 | ||||
| #define HID_KEY_F9                 0x42 | ||||
| #define HID_KEY_F10                0x43 | ||||
| #define HID_KEY_F11                0x44 | ||||
| #define HID_KEY_F12                0x45 | ||||
| #define HID_KEY_PRINT_SCREEN       0x46 | ||||
| #define HID_KEY_SCROLL_LOCK        0x47 | ||||
| #define HID_KEY_PAUSE              0x48 | ||||
| #define HID_KEY_INSERT             0x49 | ||||
| #define HID_KEY_HOME               0x4A | ||||
| #define HID_KEY_PAGE_UP            0x4B | ||||
| #define HID_KEY_DELETE             0x4C | ||||
| #define HID_KEY_END                0x4D | ||||
| #define HID_KEY_PAGE_DOWN          0x4E | ||||
| #define HID_KEY_ARROW_RIGHT        0x4F | ||||
| #define HID_KEY_ARROW_LEFT         0x50 | ||||
| #define HID_KEY_ARROW_DOWN         0x51 | ||||
| #define HID_KEY_ARROW_UP           0x52 | ||||
| #define HID_KEY_NUM_LOCK           0x53 | ||||
| #define HID_KEY_KEYPAD_DIVIDE      0x54 | ||||
| #define HID_KEY_KEYPAD_MULTIPLY    0x55 | ||||
| #define HID_KEY_KEYPAD_SUBTRACT    0x56 | ||||
| #define HID_KEY_KEYPAD_ADD         0x57 | ||||
| #define HID_KEY_KEYPAD_ENTER       0x58 | ||||
| #define HID_KEY_KEYPAD_1           0x59 | ||||
| #define HID_KEY_KEYPAD_2           0x5A | ||||
| #define HID_KEY_KEYPAD_3           0x5B | ||||
| #define HID_KEY_KEYPAD_4           0x5C | ||||
| #define HID_KEY_KEYPAD_5           0x5D | ||||
| #define HID_KEY_KEYPAD_6           0x5E | ||||
| #define HID_KEY_KEYPAD_7           0x5F | ||||
| #define HID_KEY_KEYPAD_8           0x60 | ||||
| #define HID_KEY_KEYPAD_9           0x61 | ||||
| #define HID_KEY_KEYPAD_0           0x62 | ||||
| #define HID_KEY_KEYPAD_DECIMAL     0x63 | ||||
| #define HID_KEY_EUROPE_2           0x64 | ||||
| #define HID_KEY_APPLICATION        0x65 | ||||
| #define HID_KEY_POWER              0x66 | ||||
| #define HID_KEY_KEYPAD_EQUAL       0x67 | ||||
| #define HID_KEY_F13                0x68 | ||||
| #define HID_KEY_F14                0x69 | ||||
| #define HID_KEY_F15                0x6A | ||||
| #define HID_KEY_CONTROL_LEFT       0xE0 | ||||
| #define HID_KEY_SHIFT_LEFT         0xE1 | ||||
| #define HID_KEY_ALT_LEFT           0xE2 | ||||
| #define HID_KEY_GUI_LEFT           0xE3 | ||||
| #define HID_KEY_CONTROL_RIGHT      0xE4 | ||||
| #define HID_KEY_SHIFT_RIGHT        0xE5 | ||||
| #define HID_KEY_ALT_RIGHT          0xE6 | ||||
| #define HID_KEY_GUI_RIGHT          0xE7 | ||||
| #define HID_KEY_NONE                      0x00 | ||||
| #define HID_KEY_A                         0x04 | ||||
| #define HID_KEY_B                         0x05 | ||||
| #define HID_KEY_C                         0x06 | ||||
| #define HID_KEY_D                         0x07 | ||||
| #define HID_KEY_E                         0x08 | ||||
| #define HID_KEY_F                         0x09 | ||||
| #define HID_KEY_G                         0x0A | ||||
| #define HID_KEY_H                         0x0B | ||||
| #define HID_KEY_I                         0x0C | ||||
| #define HID_KEY_J                         0x0D | ||||
| #define HID_KEY_K                         0x0E | ||||
| #define HID_KEY_L                         0x0F | ||||
| #define HID_KEY_M                         0x10 | ||||
| #define HID_KEY_N                         0x11 | ||||
| #define HID_KEY_O                         0x12 | ||||
| #define HID_KEY_P                         0x13 | ||||
| #define HID_KEY_Q                         0x14 | ||||
| #define HID_KEY_R                         0x15 | ||||
| #define HID_KEY_S                         0x16 | ||||
| #define HID_KEY_T                         0x17 | ||||
| #define HID_KEY_U                         0x18 | ||||
| #define HID_KEY_V                         0x19 | ||||
| #define HID_KEY_W                         0x1A | ||||
| #define HID_KEY_X                         0x1B | ||||
| #define HID_KEY_Y                         0x1C | ||||
| #define HID_KEY_Z                         0x1D | ||||
| #define HID_KEY_1                         0x1E | ||||
| #define HID_KEY_2                         0x1F | ||||
| #define HID_KEY_3                         0x20 | ||||
| #define HID_KEY_4                         0x21 | ||||
| #define HID_KEY_5                         0x22 | ||||
| #define HID_KEY_6                         0x23 | ||||
| #define HID_KEY_7                         0x24 | ||||
| #define HID_KEY_8                         0x25 | ||||
| #define HID_KEY_9                         0x26 | ||||
| #define HID_KEY_0                         0x27 | ||||
| #define HID_KEY_ENTER                     0x28 | ||||
| #define HID_KEY_ESCAPE                    0x29 | ||||
| #define HID_KEY_BACKSPACE                 0x2A | ||||
| #define HID_KEY_TAB                       0x2B | ||||
| #define HID_KEY_SPACE                     0x2C | ||||
| #define HID_KEY_MINUS                     0x2D | ||||
| #define HID_KEY_EQUAL                     0x2E | ||||
| #define HID_KEY_BRACKET_LEFT              0x2F | ||||
| #define HID_KEY_BRACKET_RIGHT             0x30 | ||||
| #define HID_KEY_BACKSLASH                 0x31 | ||||
| #define HID_KEY_EUROPE_1                  0x32 | ||||
| #define HID_KEY_SEMICOLON                 0x33 | ||||
| #define HID_KEY_APOSTROPHE                0x34 | ||||
| #define HID_KEY_GRAVE                     0x35 | ||||
| #define HID_KEY_COMMA                     0x36 | ||||
| #define HID_KEY_PERIOD                    0x37 | ||||
| #define HID_KEY_SLASH                     0x38 | ||||
| #define HID_KEY_CAPS_LOCK                 0x39 | ||||
| #define HID_KEY_F1                        0x3A | ||||
| #define HID_KEY_F2                        0x3B | ||||
| #define HID_KEY_F3                        0x3C | ||||
| #define HID_KEY_F4                        0x3D | ||||
| #define HID_KEY_F5                        0x3E | ||||
| #define HID_KEY_F6                        0x3F | ||||
| #define HID_KEY_F7                        0x40 | ||||
| #define HID_KEY_F8                        0x41 | ||||
| #define HID_KEY_F9                        0x42 | ||||
| #define HID_KEY_F10                       0x43 | ||||
| #define HID_KEY_F11                       0x44 | ||||
| #define HID_KEY_F12                       0x45 | ||||
| #define HID_KEY_PRINT_SCREEN              0x46 | ||||
| #define HID_KEY_SCROLL_LOCK               0x47 | ||||
| #define HID_KEY_PAUSE                     0x48 | ||||
| #define HID_KEY_INSERT                    0x49 | ||||
| #define HID_KEY_HOME                      0x4A | ||||
| #define HID_KEY_PAGE_UP                   0x4B | ||||
| #define HID_KEY_DELETE                    0x4C | ||||
| #define HID_KEY_END                       0x4D | ||||
| #define HID_KEY_PAGE_DOWN                 0x4E | ||||
| #define HID_KEY_ARROW_RIGHT               0x4F | ||||
| #define HID_KEY_ARROW_LEFT                0x50 | ||||
| #define HID_KEY_ARROW_DOWN                0x51 | ||||
| #define HID_KEY_ARROW_UP                  0x52 | ||||
| #define HID_KEY_NUM_LOCK                  0x53 | ||||
| #define HID_KEY_KEYPAD_DIVIDE             0x54 | ||||
| #define HID_KEY_KEYPAD_MULTIPLY           0x55 | ||||
| #define HID_KEY_KEYPAD_SUBTRACT           0x56 | ||||
| #define HID_KEY_KEYPAD_ADD                0x57 | ||||
| #define HID_KEY_KEYPAD_ENTER              0x58 | ||||
| #define HID_KEY_KEYPAD_1                  0x59 | ||||
| #define HID_KEY_KEYPAD_2                  0x5A | ||||
| #define HID_KEY_KEYPAD_3                  0x5B | ||||
| #define HID_KEY_KEYPAD_4                  0x5C | ||||
| #define HID_KEY_KEYPAD_5                  0x5D | ||||
| #define HID_KEY_KEYPAD_6                  0x5E | ||||
| #define HID_KEY_KEYPAD_7                  0x5F | ||||
| #define HID_KEY_KEYPAD_8                  0x60 | ||||
| #define HID_KEY_KEYPAD_9                  0x61 | ||||
| #define HID_KEY_KEYPAD_0                  0x62 | ||||
| #define HID_KEY_KEYPAD_DECIMAL            0x63 | ||||
| #define HID_KEY_EUROPE_2                  0x64 | ||||
| #define HID_KEY_APPLICATION               0x65 | ||||
| #define HID_KEY_POWER                     0x66 | ||||
| #define HID_KEY_KEYPAD_EQUAL              0x67 | ||||
| #define HID_KEY_F13                       0x68 | ||||
| #define HID_KEY_F14                       0x69 | ||||
| #define HID_KEY_F15                       0x6A | ||||
| #define HID_KEY_F16                       0x6B | ||||
| #define HID_KEY_F17                       0x6C | ||||
| #define HID_KEY_F18                       0x6D | ||||
| #define HID_KEY_F19                       0x6E | ||||
| #define HID_KEY_F20                       0x6F | ||||
| #define HID_KEY_F21                       0x70 | ||||
| #define HID_KEY_F22                       0x71 | ||||
| #define HID_KEY_F23                       0x72 | ||||
| #define HID_KEY_F24                       0x73 | ||||
| #define HID_KEY_EXECUTE                   0x74 | ||||
| #define HID_KEY_HELP                      0x75 | ||||
| #define HID_KEY_MENU                      0x76 | ||||
| #define HID_KEY_SELECT                    0x77 | ||||
| #define HID_KEY_STOP                      0x78 | ||||
| #define HID_KEY_AGAIN                     0x79 | ||||
| #define HID_KEY_UNDO                      0x7A | ||||
| #define HID_KEY_CUT                       0x7B | ||||
| #define HID_KEY_COPY                      0x7C | ||||
| #define HID_KEY_PASTE                     0x7D | ||||
| #define HID_KEY_FIND                      0x7E | ||||
| #define HID_KEY_MUTE                      0x7F | ||||
| #define HID_KEY_VOLUME_UP                 0x80 | ||||
| #define HID_KEY_VOLUME_DOWN               0x81 | ||||
| #define HID_KEY_LOCKING_CAPS_LOCK         0x82 | ||||
| #define HID_KEY_LOCKING_NUM_LOCK          0x83 | ||||
| #define HID_KEY_LOCKING_SCROLL_LOCK       0x84 | ||||
| #define HID_KEY_KEYPAD_COMMA              0x85 | ||||
| #define HID_KEY_KEYPAD_EQUAL_SIGN         0x86 | ||||
| #define HID_KEY_KANJI1                    0x87 | ||||
| #define HID_KEY_KANJI2                    0x88 | ||||
| #define HID_KEY_KANJI3                    0x89 | ||||
| #define HID_KEY_KANJI4                    0x8A | ||||
| #define HID_KEY_KANJI5                    0x8B | ||||
| #define HID_KEY_KANJI6                    0x8C | ||||
| #define HID_KEY_KANJI7                    0x8D | ||||
| #define HID_KEY_KANJI8                    0x8E | ||||
| #define HID_KEY_KANJI9                    0x8F | ||||
| #define HID_KEY_LANG1                     0x90 | ||||
| #define HID_KEY_LANG2                     0x91 | ||||
| #define HID_KEY_LANG3                     0x92 | ||||
| #define HID_KEY_LANG4                     0x93 | ||||
| #define HID_KEY_LANG5                     0x94 | ||||
| #define HID_KEY_LANG6                     0x95 | ||||
| #define HID_KEY_LANG7                     0x96 | ||||
| #define HID_KEY_LANG8                     0x97 | ||||
| #define HID_KEY_LANG9                     0x98 | ||||
| #define HID_KEY_ALTERNATE_ERASE           0x99 | ||||
| #define HID_KEY_SYSREQ_ATTENTION          0x9A | ||||
| #define HID_KEY_CANCEL                    0x9B | ||||
| #define HID_KEY_CLEAR                     0x9C | ||||
| #define HID_KEY_PRIOR                     0x9D | ||||
| #define HID_KEY_RETURN                    0x9E | ||||
| #define HID_KEY_SEPARATOR                 0x9F | ||||
| #define HID_KEY_OUT                       0xA0 | ||||
| #define HID_KEY_OPER                      0xA1 | ||||
| #define HID_KEY_CLEAR_AGAIN               0xA2 | ||||
| #define HID_KEY_CRSEL_PROPS               0xA3 | ||||
| #define HID_KEY_EXSEL                     0xA4 | ||||
| // RESERVED					                      0xA5-DF | ||||
| #define HID_KEY_CONTROL_LEFT              0xE0 | ||||
| #define HID_KEY_SHIFT_LEFT                0xE1 | ||||
| #define HID_KEY_ALT_LEFT                  0xE2 | ||||
| #define HID_KEY_GUI_LEFT                  0xE3 | ||||
| #define HID_KEY_CONTROL_RIGHT             0xE4 | ||||
| #define HID_KEY_SHIFT_RIGHT               0xE5 | ||||
| #define HID_KEY_ALT_RIGHT                 0xE6 | ||||
| #define HID_KEY_GUI_RIGHT                 0xE7 | ||||
|  | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
|   | ||||
| @@ -61,6 +61,51 @@ typedef enum | ||||
|   MIDI_JACK_EXTERNAL = 0x02 | ||||
| } midi_jack_type_t; | ||||
|  | ||||
| typedef enum | ||||
| { | ||||
|   MIDI_CIN_MISC              = 0, | ||||
|   MIDI_CIN_CABLE_EVENT       = 1, | ||||
|   MIDI_CIN_SYSCOM_2BYTE      = 2, // 2 byte system common message e.g MTC, SongSelect | ||||
|   MIDI_CIN_SYSCOM_3BYTE      = 3, // 3 byte system common message e.g SPP | ||||
|   MIDI_CIN_SYSEX_START       = 4, // SysEx starts or continue | ||||
|   MIDI_CIN_SYSEX_END_1BYTE   = 5, // SysEx ends with 1 data, or 1 byte system common message | ||||
|   MIDI_CIN_SYSEX_END_2BYTE   = 6, // SysEx ends with 2 data | ||||
|   MIDI_CIN_SYSEX_END_3BYTE   = 7, // SysEx ends with 3 data | ||||
|   MIDI_CIN_NOTE_ON           = 8, | ||||
|   MIDI_CIN_NOTE_OFF          = 9, | ||||
|   MIDI_CIN_POLY_KEYPRESS     = 10, | ||||
|   MIDI_CIN_CONTROL_CHANGE    = 11, | ||||
|   MIDI_CIN_PROGRAM_CHANGE    = 12, | ||||
|   MIDI_CIN_CHANNEL_PRESSURE  = 13, | ||||
|   MIDI_CIN_PITCH_BEND_CHANGE = 14, | ||||
|   MIDI_CIN_1BYTE_DATA = 15 | ||||
| } midi_code_index_number_t; | ||||
|  | ||||
| // MIDI 1.0 status byte | ||||
| enum | ||||
| { | ||||
|   //------------- System Exclusive -------------// | ||||
|   MIDI_STATUS_SYSEX_START                    = 0xF0, | ||||
|   MIDI_STATUS_SYSEX_END                      = 0xF7, | ||||
|  | ||||
|   //------------- System Common -------------// | ||||
|   MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME = 0xF1, | ||||
|   MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER   = 0xF2, | ||||
|   MIDI_STATUS_SYSCOM_SONG_SELECT             = 0xF3, | ||||
|   // F4, F5 is undefined | ||||
|   MIDI_STATUS_SYSCOM_TUNE_REQUEST            = 0xF6, | ||||
|  | ||||
|   //------------- System RealTime  -------------// | ||||
|   MIDI_STATUS_SYSREAL_TIMING_CLOCK           = 0xF8, | ||||
|   // 0xF9 is undefined | ||||
|   MIDI_STATUS_SYSREAL_START                  = 0xFA, | ||||
|   MIDI_STATUS_SYSREAL_CONTINUE               = 0xFB, | ||||
|   MIDI_STATUS_SYSREAL_STOP                   = 0xFC, | ||||
|   // 0xFD is undefined | ||||
|   MIDI_STATUS_SYSREAL_ACTIVE_SENSING         = 0xFE, | ||||
|   MIDI_STATUS_SYSREAL_SYSTEM_RESET           = 0xFF, | ||||
| }; | ||||
|  | ||||
| /// MIDI Interface Header Descriptor | ||||
| typedef struct TU_ATTR_PACKED | ||||
| { | ||||
|   | ||||
| @@ -38,12 +38,26 @@ | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   uint8_t buffer[4]; | ||||
|   uint8_t index; | ||||
|   uint8_t total; | ||||
| }midid_stream_t; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   uint8_t itf_num; | ||||
|   uint8_t ep_in; | ||||
|   uint8_t ep_out; | ||||
|  | ||||
|   // For Stream read()/write() API | ||||
|   // Messages are always 4 bytes long, queue them for reading and writing so the | ||||
|   // callers can use the Stream interface with single-byte read/write calls. | ||||
|   midid_stream_t stream_write; | ||||
|   midid_stream_t stream_read; | ||||
|  | ||||
|   /*------------- From this point, data is not cleared by bus reset -------------*/ | ||||
|   // FIFO | ||||
|   tu_fifo_t rx_ff; | ||||
| @@ -56,16 +70,6 @@ typedef struct | ||||
|   osal_mutex_def_t tx_ff_mutex; | ||||
|   #endif | ||||
|  | ||||
|   // Messages are always 4 bytes long, queue them for reading and writing so the | ||||
|   // callers can use the Stream interface with single-byte read/write calls. | ||||
|   uint8_t write_buffer[4]; | ||||
|   uint8_t write_buffer_length; | ||||
|   uint8_t write_target_length; | ||||
|  | ||||
|   uint8_t read_buffer[4]; | ||||
|   uint8_t read_buffer_length; | ||||
|   uint8_t read_target_length; | ||||
|  | ||||
|   // Endpoint Transfer buffer | ||||
|   CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE]; | ||||
|   CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE]; | ||||
| @@ -120,53 +124,77 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num) | ||||
|   return tu_fifo_count(&_midid_itf[itf].rx_ff); | ||||
| } | ||||
|  | ||||
| uint32_t tud_midi_n_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize) | ||||
| uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize) | ||||
| { | ||||
|   (void) cable_num; | ||||
|   TU_VERIFY(bufsize, 0); | ||||
|  | ||||
|   uint8_t* buf8 = (uint8_t*) buffer; | ||||
|  | ||||
|   midid_interface_t* midi = &_midid_itf[itf]; | ||||
|   midid_stream_t* stream = &midi->stream_read; | ||||
|  | ||||
|   // Fill empty buffer | ||||
|   if (midi->read_buffer_length == 0) { | ||||
|     if (!tud_midi_n_receive(itf, midi->read_buffer)) return 0; | ||||
|   uint32_t total_read = 0; | ||||
|   while( bufsize ) | ||||
|   { | ||||
|     // Get new packet from fifo, then set packet expected bytes | ||||
|     if ( stream->total == 0 ) | ||||
|     { | ||||
|       // return if there is no more data from fifo | ||||
|       if ( !tud_midi_n_packet_read(itf, stream->buffer) ) return total_read; | ||||
|  | ||||
|     uint8_t code_index = midi->read_buffer[0] & 0x0f; | ||||
|     // We always copy over the first byte. | ||||
|     uint8_t count = 1; | ||||
|     // Ignore subsequent bytes based on the code. | ||||
|     if (code_index != 0x5 && code_index != 0xf) { | ||||
|       count = 2; | ||||
|       if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) { | ||||
|         count = 3; | ||||
|       uint8_t const code_index = stream->buffer[0] & 0x0f; | ||||
|  | ||||
|       // MIDI 1.0 Table 4-1: Code Index Number Classifications | ||||
|       switch(code_index) | ||||
|       { | ||||
|         case MIDI_CIN_MISC: | ||||
|         case MIDI_CIN_CABLE_EVENT: | ||||
|           // These are reserved and unused, possibly issue somewhere, skip this packet | ||||
|           return 0; | ||||
|         break; | ||||
|  | ||||
|         case MIDI_CIN_SYSEX_END_1BYTE: | ||||
|         case MIDI_CIN_1BYTE_DATA: | ||||
|           stream->total = 1; | ||||
|         break; | ||||
|  | ||||
|         case MIDI_CIN_SYSCOM_2BYTE     : | ||||
|         case MIDI_CIN_SYSEX_END_2BYTE  : | ||||
|         case MIDI_CIN_PROGRAM_CHANGE   : | ||||
|         case MIDI_CIN_CHANNEL_PRESSURE : | ||||
|           stream->total = 2; | ||||
|         break; | ||||
|  | ||||
|         default: | ||||
|           stream->total = 3; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     midi->read_buffer_length = count; | ||||
|     // Copy data up to bufsize | ||||
|     uint32_t const count = tu_min32(stream->total - stream->index, bufsize); | ||||
|  | ||||
|     // Skip the header (1st byte) in the buffer | ||||
|     memcpy(buf8, stream->buffer + 1 + stream->index, count); | ||||
|  | ||||
|     total_read += count; | ||||
|     stream->index += count; | ||||
|     buf8 += count; | ||||
|     bufsize -= count; | ||||
|  | ||||
|     // complete current event packet, reset stream | ||||
|     if ( stream->total == stream->index ) | ||||
|     { | ||||
|       stream->index = 0; | ||||
|       stream->total = 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   uint32_t n = midi->read_buffer_length - midi->read_target_length; | ||||
|   if (bufsize < n) n = bufsize; | ||||
|  | ||||
|   // Skip the header in the buffer | ||||
|   memcpy(buffer, midi->read_buffer + 1 + midi->read_target_length, n); | ||||
|   midi->read_target_length += n; | ||||
|  | ||||
|   if (midi->read_target_length == midi->read_buffer_length) { | ||||
|     midi->read_buffer_length = 0; | ||||
|     midi->read_target_length = 0; | ||||
|   } | ||||
|  | ||||
|   return n; | ||||
|   return total_read; | ||||
| } | ||||
|  | ||||
| void tud_midi_n_read_flush (uint8_t itf, uint8_t cable_num) | ||||
| { | ||||
|   (void) cable_num; | ||||
|   midid_interface_t* p_midi = &_midid_itf[itf]; | ||||
|   tu_fifo_clear(&p_midi->rx_ff); | ||||
|   _prep_out_transaction(p_midi); | ||||
| } | ||||
|  | ||||
| bool tud_midi_n_receive (uint8_t itf, uint8_t packet[4]) | ||||
| bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4]) | ||||
| { | ||||
|   midid_interface_t* p_midi = &_midid_itf[itf]; | ||||
|   uint32_t num_read = tu_fifo_read_n(&p_midi->rx_ff, packet, 4); | ||||
| @@ -174,10 +202,6 @@ bool tud_midi_n_receive (uint8_t itf, uint8_t packet[4]) | ||||
|   return (num_read == 4); | ||||
| } | ||||
|  | ||||
| void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) { | ||||
|   tu_fifo_write_n(&midi->rx_ff, buffer, bufsize); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // WRITE API | ||||
| //--------------------------------------------------------------------+ | ||||
| @@ -193,7 +217,8 @@ static uint32_t write_flush(midid_interface_t* midi) | ||||
|   TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 ); | ||||
|  | ||||
|   uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE); | ||||
|   if (count > 0) | ||||
|  | ||||
|   if (count) | ||||
|   { | ||||
|     TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 ); | ||||
|     return count; | ||||
| @@ -205,88 +230,131 @@ static uint32_t write_flush(midid_interface_t* midi) | ||||
|   } | ||||
| } | ||||
|  | ||||
| uint32_t tud_midi_n_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) | ||||
| uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) | ||||
| { | ||||
|   midid_interface_t* midi = &_midid_itf[itf]; | ||||
|   if (midi->itf_num == 0) { | ||||
|     return 0; | ||||
|   } | ||||
|   TU_VERIFY(midi->itf_num, 0); | ||||
|  | ||||
|   midid_stream_t* stream = &midi->stream_write; | ||||
|  | ||||
|   uint32_t total_written = 0; | ||||
|   uint32_t i = 0; | ||||
|   while (i < bufsize) { | ||||
|     uint8_t data = buffer[i]; | ||||
|     if (midi->write_buffer_length == 0) { | ||||
|         uint8_t msg = data >> 4; | ||||
|         midi->write_buffer[1] = data; | ||||
|         midi->write_buffer_length = 2; | ||||
|         // Check to see if we're still in a SysEx transmit. | ||||
|         if (midi->write_buffer[0] == 0x4) { | ||||
|             if (data == 0xf7) { | ||||
|                 midi->write_buffer[0] = 0x5; | ||||
|                 midi->write_target_length = 2; | ||||
|             } else { | ||||
|                 midi->write_target_length = 4; | ||||
|             } | ||||
|         } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) { | ||||
|             midi->write_buffer[0] = cable_num << 4 | msg; | ||||
|             midi->write_target_length = 4; | ||||
|         } else if (msg == 0xf) { | ||||
|             if (data == 0xf0) { | ||||
|                 midi->write_buffer[0] = 0x4; | ||||
|                 midi->write_target_length = 4; | ||||
|             } else if (data == 0xf1 || data == 0xf3) { | ||||
|                 midi->write_buffer[0] = 0x2; | ||||
|                 midi->write_target_length = 3; | ||||
|             } else if (data == 0xf2) { | ||||
|                 midi->write_buffer[0] = 0x3; | ||||
|                 midi->write_target_length = 4; | ||||
|             } else { | ||||
|                 midi->write_buffer[0] = 0x5; | ||||
|                 midi->write_target_length = 2; | ||||
|             } | ||||
|         } else { | ||||
|             // Pack individual bytes if we don't support packing them into words. | ||||
|             midi->write_buffer[0] = cable_num << 4 | 0xf; | ||||
|             midi->write_buffer[2] = 0; | ||||
|             midi->write_buffer[3] = 0; | ||||
|             midi->write_buffer_length = 2; | ||||
|             midi->write_target_length = 2; | ||||
|   while ( i < bufsize ) | ||||
|   { | ||||
|     uint8_t const data = buffer[i]; | ||||
|  | ||||
|     if ( stream->index == 0 ) | ||||
|     { | ||||
|       // new event packet | ||||
|  | ||||
|       uint8_t const msg = data >> 4; | ||||
|  | ||||
|       stream->index = 2; | ||||
|       stream->buffer[1] = data; | ||||
|  | ||||
|       // Check to see if we're still in a SysEx transmit. | ||||
|       if ( stream->buffer[0] == MIDI_CIN_SYSEX_START ) | ||||
|       { | ||||
|         if ( data == MIDI_STATUS_SYSEX_END ) | ||||
|         { | ||||
|           stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; | ||||
|           stream->total = 2; | ||||
|         } | ||||
|     } else { | ||||
|         midi->write_buffer[midi->write_buffer_length] = data; | ||||
|         midi->write_buffer_length += 1; | ||||
|         // See if this byte ends a SysEx. | ||||
|         if (midi->write_buffer[0] == 0x4 && data == 0xf7) { | ||||
|             midi->write_buffer[0] = 0x4 + (midi->write_buffer_length - 1); | ||||
|             midi->write_target_length = midi->write_buffer_length; | ||||
|         else | ||||
|         { | ||||
|           stream->total = 4; | ||||
|         } | ||||
|       } | ||||
|       else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE ) | ||||
|       { | ||||
|         // Channel Voice Messages | ||||
|         stream->buffer[0] = (cable_num << 4) | msg; | ||||
|         stream->total = 4; | ||||
|       } | ||||
|       else if ( msg == 0xf ) | ||||
|       { | ||||
|         // System message | ||||
|         if ( data == MIDI_STATUS_SYSEX_START ) | ||||
|         { | ||||
|           stream->buffer[0] = MIDI_CIN_SYSEX_START; | ||||
|           stream->total = 4; | ||||
|         } | ||||
|         else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT ) | ||||
|         { | ||||
|           stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE; | ||||
|           stream->total = 3; | ||||
|         } | ||||
|         else if ( data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER ) | ||||
|         { | ||||
|           stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE; | ||||
|           stream->total = 4; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|           stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; | ||||
|           stream->total = 2; | ||||
|         } | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         // Pack individual bytes if we don't support packing them into words. | ||||
|         stream->buffer[0] = cable_num << 4 | 0xf; | ||||
|         stream->buffer[2] = 0; | ||||
|         stream->buffer[3] = 0; | ||||
|         stream->index = 2; | ||||
|         stream->total = 2; | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       // On-going (buffering) packet | ||||
|  | ||||
|       TU_ASSERT(stream->index < 4, total_written); | ||||
|       stream->buffer[stream->index] = data; | ||||
|       stream->index++; | ||||
|  | ||||
|       // See if this byte ends a SysEx. | ||||
|       if ( stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END ) | ||||
|       { | ||||
|         stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1); | ||||
|         stream->total = stream->index; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (midi->write_buffer_length == midi->write_target_length) { | ||||
|         uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->write_buffer, 4); | ||||
|         if (written < 4) { | ||||
|             TU_ASSERT( written == 0 ); | ||||
|             break; | ||||
|         } | ||||
|         midi->write_buffer_length = 0; | ||||
|     // Send out packet | ||||
|     if ( stream->index == stream->total ) | ||||
|     { | ||||
|       // zeroes unused bytes | ||||
|       for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0; | ||||
|  | ||||
|       uint16_t const count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4); | ||||
|  | ||||
|       // complete current event packet, reset stream | ||||
|       stream->index = stream->total = 0; | ||||
|  | ||||
|       // fifo overflow, here we assume FIFO is multiple of 4 and didn't check remaining before writing | ||||
|       if ( count != 4 ) break; | ||||
|  | ||||
|       // updated written if succeeded | ||||
|       total_written = i; | ||||
|     } | ||||
|  | ||||
|     i++; | ||||
|   } | ||||
|  | ||||
|   write_flush(midi); | ||||
|  | ||||
|   return i; | ||||
|   return total_written; | ||||
| } | ||||
|  | ||||
| bool tud_midi_n_send (uint8_t itf, uint8_t const packet[4]) | ||||
| bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]) | ||||
| { | ||||
|   midid_interface_t* midi = &_midid_itf[itf]; | ||||
|   if (midi->itf_num == 0) { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   if (tu_fifo_remaining(&midi->tx_ff) < 4) | ||||
|     return false; | ||||
|   if (tu_fifo_remaining(&midi->tx_ff) < 4) return false; | ||||
|  | ||||
|   tu_fifo_write_n(&midi->tx_ff, packet, 4); | ||||
|   write_flush(midi); | ||||
| @@ -332,9 +400,9 @@ void midid_reset(uint8_t rhport) | ||||
| uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len) | ||||
| { | ||||
|   // 1st Interface is Audio Control v1 | ||||
|   TU_VERIFY(TUSB_CLASS_AUDIO                      == desc_itf->bInterfaceClass    && | ||||
|             AUDIO_SUBCLASS_CONTROL                == desc_itf->bInterfaceSubClass && | ||||
|             AUDIO_FUNC_PROTOCOL_CODE_UNDEF        == desc_itf->bInterfaceProtocol, 0); | ||||
|   TU_VERIFY(TUSB_CLASS_AUDIO               == desc_itf->bInterfaceClass    && | ||||
|             AUDIO_SUBCLASS_CONTROL         == desc_itf->bInterfaceSubClass && | ||||
|             AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0); | ||||
|  | ||||
|   uint16_t drv_len = tu_desc_len(desc_itf); | ||||
|   uint8_t const * p_desc = tu_desc_next(desc_itf); | ||||
| @@ -350,9 +418,9 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint | ||||
|   TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0); | ||||
|   tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc; | ||||
|  | ||||
|   TU_VERIFY(TUSB_CLASS_AUDIO                      == desc_midi->bInterfaceClass    && | ||||
|             AUDIO_SUBCLASS_MIDI_STREAMING         == desc_midi->bInterfaceSubClass && | ||||
|             AUDIO_FUNC_PROTOCOL_CODE_UNDEF        == desc_midi->bInterfaceProtocol, 0); | ||||
|   TU_VERIFY(TUSB_CLASS_AUDIO               == desc_midi->bInterfaceClass    && | ||||
|             AUDIO_SUBCLASS_MIDI_STREAMING  == desc_midi->bInterfaceSubClass && | ||||
|             AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0); | ||||
|  | ||||
|   // Find available interface | ||||
|   midid_interface_t * p_midi = NULL; | ||||
|   | ||||
| @@ -59,29 +59,64 @@ | ||||
| // Application API (Multiple Interfaces) | ||||
| // CFG_TUD_MIDI > 1 | ||||
| //--------------------------------------------------------------------+ | ||||
| bool     tud_midi_n_mounted    (uint8_t itf); | ||||
| uint32_t tud_midi_n_available  (uint8_t itf, uint8_t cable_num); | ||||
| uint32_t tud_midi_n_read       (uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize); | ||||
| void     tud_midi_n_read_flush (uint8_t itf, uint8_t cable_num); | ||||
| uint32_t tud_midi_n_write      (uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize); | ||||
|  | ||||
| static inline | ||||
| uint32_t tud_midi_n_write24    (uint8_t itf, uint8_t cable_num, uint8_t b1, uint8_t b2, uint8_t b3); | ||||
| // Check if midi interface is mounted | ||||
| bool     tud_midi_n_mounted      (uint8_t itf); | ||||
|  | ||||
| bool tud_midi_n_receive        (uint8_t itf, uint8_t packet[4]); | ||||
| bool tud_midi_n_send           (uint8_t itf, uint8_t const packet[4]); | ||||
| // Get the number of bytes available for reading | ||||
| uint32_t tud_midi_n_available    (uint8_t itf, uint8_t cable_num); | ||||
|  | ||||
| // Read byte stream              (legacy) | ||||
| uint32_t tud_midi_n_stream_read  (uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize); | ||||
|  | ||||
| // Write byte Stream             (legacy) | ||||
| uint32_t tud_midi_n_stream_write (uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize); | ||||
|  | ||||
| // Read event packet             (4 bytes) | ||||
| bool     tud_midi_n_packet_read  (uint8_t itf, uint8_t packet[4]); | ||||
|  | ||||
| // Write event packet            (4 bytes) | ||||
| bool     tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]); | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Application API (Single Interface) | ||||
| //--------------------------------------------------------------------+ | ||||
| static inline bool     tud_midi_mounted    (void); | ||||
| static inline uint32_t tud_midi_available  (void); | ||||
| static inline uint32_t tud_midi_read       (void* buffer, uint32_t bufsize); | ||||
| static inline void     tud_midi_read_flush (void); | ||||
| static inline uint32_t tud_midi_write      (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize); | ||||
| static inline uint32_t tud_midi_write24    (uint8_t cable_num, uint8_t b1, uint8_t b2, uint8_t b3); | ||||
| static inline bool     tud_midi_receive    (uint8_t packet[4]); | ||||
| static inline bool     tud_midi_send       (uint8_t const packet[4]); | ||||
| static inline bool     tud_midi_mounted      (void); | ||||
| static inline uint32_t tud_midi_available    (void); | ||||
|  | ||||
| static inline uint32_t tud_midi_stream_read  (void* buffer, uint32_t bufsize); | ||||
| static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize); | ||||
|  | ||||
| static inline bool     tud_midi_packet_read  (uint8_t packet[4]); | ||||
| static inline bool     tud_midi_packet_write (uint8_t const packet[4]); | ||||
|  | ||||
| //------------- Deprecated API name  -------------// | ||||
| // TODO remove after 0.10.0 release | ||||
|  | ||||
| TU_ATTR_DEPRECATED("tud_midi_read() is renamed to tud_midi_stream_read()") | ||||
| static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize) | ||||
| { | ||||
|   return tud_midi_stream_read(buffer, bufsize); | ||||
| } | ||||
|  | ||||
| TU_ATTR_DEPRECATED("tud_midi_write() is renamed to tud_midi_stream_write()") | ||||
| static inline uint32_t tud_midi_write(uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) | ||||
| { | ||||
|   return tud_midi_stream_write(cable_num, buffer, bufsize); | ||||
| } | ||||
|  | ||||
|  | ||||
| TU_ATTR_DEPRECATED("tud_midi_send() is renamed to tud_midi_packet_write()") | ||||
| static inline bool tud_midi_send(uint8_t packet[4]) | ||||
| { | ||||
|   return tud_midi_packet_write(packet); | ||||
| } | ||||
|  | ||||
| TU_ATTR_DEPRECATED("tud_midi_receive() is renamed to tud_midi_packet_read()") | ||||
| static inline bool tud_midi_receive(uint8_t packet[4]) | ||||
| { | ||||
|   return tud_midi_packet_read(packet); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Application Callback API (weak is optional) | ||||
| @@ -92,12 +127,6 @@ TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf); | ||||
| // Inline Functions | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| static inline uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t cable_num, uint8_t b1, uint8_t b2, uint8_t b3) | ||||
| { | ||||
|   uint8_t msg[3] = { b1, b2, b3 }; | ||||
|   return tud_midi_n_write(itf, cable_num, msg, 3); | ||||
| } | ||||
|  | ||||
| static inline bool tud_midi_mounted (void) | ||||
| { | ||||
|   return tud_midi_n_mounted(0); | ||||
| @@ -108,35 +137,24 @@ static inline uint32_t tud_midi_available (void) | ||||
|   return tud_midi_n_available(0, 0); | ||||
| } | ||||
|  | ||||
| static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize) | ||||
| static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize) | ||||
| { | ||||
|   return tud_midi_n_read(0, 0, buffer, bufsize); | ||||
|   return tud_midi_n_stream_read(0, 0, buffer, bufsize); | ||||
| } | ||||
|  | ||||
| static inline void tud_midi_read_flush (void) | ||||
| static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) | ||||
| { | ||||
|   tud_midi_n_read_flush(0, 0); | ||||
|   return tud_midi_n_stream_write(0, cable_num, buffer, bufsize); | ||||
| } | ||||
|  | ||||
| static inline uint32_t tud_midi_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) | ||||
| static inline bool tud_midi_packet_read (uint8_t packet[4]) | ||||
| { | ||||
|   return tud_midi_n_write(0, cable_num, buffer, bufsize); | ||||
|   return tud_midi_n_packet_read(0, packet); | ||||
| } | ||||
|  | ||||
| static inline uint32_t tud_midi_write24 (uint8_t cable_num, uint8_t b1, uint8_t b2, uint8_t b3) | ||||
| static inline bool tud_midi_packet_write (uint8_t const packet[4]) | ||||
| { | ||||
|   uint8_t msg[3] = { b1, b2, b3 }; | ||||
|   return tud_midi_write(cable_num, msg, 3); | ||||
| } | ||||
|  | ||||
| static inline bool tud_midi_receive (uint8_t packet[4]) | ||||
| { | ||||
|   return tud_midi_n_receive(0, packet); | ||||
| } | ||||
|  | ||||
| static inline bool tud_midi_send (uint8_t const packet[4]) | ||||
| { | ||||
|   return tud_midi_n_send(0, packet); | ||||
|   return tud_midi_n_packet_write(0, packet); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
|   | ||||
| @@ -131,9 +131,9 @@ typedef struct | ||||
|   uint8_t ep_int_in; | ||||
|   // IN buffer is only used for first packet, not the remainder | ||||
|   // in order to deal with prepending header | ||||
|   uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE]; | ||||
|   CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE]; | ||||
|   // OUT buffer receives one packet at a time | ||||
|   uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE]; | ||||
|   CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE]; | ||||
|   uint32_t transfer_size_remaining; // also used for requested length for bulk IN. | ||||
|   uint32_t transfer_size_sent;      // To keep track of data bytes that have been queued in FIFO (not header bytes) | ||||
|  | ||||
| @@ -145,7 +145,7 @@ typedef struct | ||||
|   usbtmc_capabilities_specific_t const * capabilities; | ||||
| } usbtmc_interface_state_t; | ||||
|  | ||||
| static usbtmc_interface_state_t usbtmc_state = | ||||
| CFG_TUSB_MEM_SECTION static usbtmc_interface_state_t usbtmc_state = | ||||
| { | ||||
|     .itf_id = 0xFF, | ||||
| }; | ||||
|   | ||||
| @@ -53,13 +53,11 @@ enum | ||||
|  | ||||
| enum | ||||
| { | ||||
|   // Endpoint number is fixed (8) for ISOOUT and ISOIN. | ||||
|   EP_ISO_NUM = 8, | ||||
|   // CBI endpoints count | ||||
|   EP_COUNT = 8 | ||||
|   EP_ISO_NUM   = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN | ||||
|   EP_CBI_COUNT = 8  // Control Bulk Interrupt endpoints count | ||||
| }; | ||||
|  | ||||
| // Transfer descriptor | ||||
| // Transfer Descriptor | ||||
| typedef struct | ||||
| { | ||||
|   uint8_t* buffer; | ||||
| @@ -67,9 +65,10 @@ typedef struct | ||||
|   volatile uint16_t actual_len; | ||||
|   uint16_t  mps; // max packet size | ||||
|  | ||||
|   // nrf52840 will auto ACK OUT packet after DMA is done | ||||
|   // nRF will auto accept OUT packet after DMA is done | ||||
|   // indicate packet is already ACK | ||||
|   volatile bool data_received; | ||||
|  | ||||
|   // Set to true when data was transferred from RAM to ISO IN output buffer. | ||||
|   // New data can be put in ISO IN output buffer after SOF. | ||||
|   bool iso_in_transfer_ready; | ||||
| @@ -81,7 +80,7 @@ static struct | ||||
| { | ||||
|   // All 8 endpoints including control IN & OUT (offset 1) | ||||
|   // +1 for ISO endpoints | ||||
|   xfer_td_t xfer[EP_COUNT + 1][2]; | ||||
|   xfer_td_t xfer[EP_CBI_COUNT + 1][2]; | ||||
|  | ||||
|   // Number of pending DMA that is started but not handled yet by dcd_int_handler(). | ||||
|   // Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1. | ||||
| @@ -133,7 +132,7 @@ static void edpt_dma_start(volatile uint32_t* reg_startep) | ||||
|         { | ||||
|           ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT; | ||||
|  | ||||
|           for (uint8_t i=0; i<EP_COUNT; i++) | ||||
|           for (uint8_t i=0; i<EP_CBI_COUNT; i++) | ||||
|           { | ||||
|             ended += NRF_USBD->EVENTS_ENDEPIN[i] + NRF_USBD->EVENTS_ENDEPOUT[i]; | ||||
|           } | ||||
| @@ -166,27 +165,6 @@ static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) | ||||
|   return &_dcd.xfer[epnum][dir]; | ||||
| } | ||||
|  | ||||
| /*------------- CBI OUT Transfer -------------*/ | ||||
|  | ||||
| // Prepare for a CBI transaction OUT, call at the start | ||||
| // Allow ACK incoming data | ||||
| static void xact_out_prepare(uint8_t epnum) | ||||
| { | ||||
|   if ( epnum == 0 ) | ||||
|   { | ||||
|     NRF_USBD->TASKS_EP0RCVOUT = 1; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // Write zero value to SIZE register will allow hw to ACK (accept data) | ||||
|     // If it is not already done by DMA | ||||
|     // SIZE.ISOOUT can also be accessed this way | ||||
|     NRF_USBD->SIZE.EPOUT[epnum] = 0; | ||||
|   } | ||||
|  | ||||
|   __ISB(); __DSB(); | ||||
| } | ||||
|  | ||||
| // Start DMA to move data from Endpoint -> RAM | ||||
| static void xact_out_dma(uint8_t epnum) | ||||
| { | ||||
| @@ -217,15 +195,14 @@ static void xact_out_dma(uint8_t epnum) | ||||
|  | ||||
|     edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]); | ||||
|   } | ||||
|  | ||||
|   xfer->buffer     += xact_len; | ||||
|   xfer->actual_len += xact_len; | ||||
| } | ||||
|  | ||||
| /*------------- CBI IN Transfer -------------*/ | ||||
|  | ||||
| // Prepare for a CBI transaction IN, call at the start | ||||
| // it start DMA to transfer data from RAM -> Endpoint | ||||
| static void xact_in_prepare(uint8_t epnum) | ||||
| static void xact_in_dma(uint8_t epnum) | ||||
| { | ||||
|   xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN); | ||||
|  | ||||
| @@ -327,6 +304,9 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) | ||||
|     { | ||||
|       NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum); | ||||
|       NRF_USBD->EPOUTEN |= TU_BIT(epnum); | ||||
|  | ||||
|       // Write any value to SIZE register will allow nRF to ACK/accept data | ||||
|       NRF_USBD->SIZE.EPOUT[epnum] = 0; | ||||
|     }else | ||||
|     { | ||||
|       NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum); | ||||
| @@ -438,20 +418,31 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t | ||||
|   } | ||||
|   else if ( dir == TUSB_DIR_OUT ) | ||||
|   { | ||||
|     if ( xfer->data_received ) | ||||
|     if ( epnum == 0 ) | ||||
|     { | ||||
|       // nrf52840 auto ACK OUT packet after DMA is done | ||||
|       // Data already received previously --> trigger DMA to copy to SRAM | ||||
|       xact_out_dma(epnum); | ||||
|     } | ||||
|     else | ||||
|       // Accept next Control Out packet | ||||
|       NRF_USBD->TASKS_EP0RCVOUT = 1; | ||||
|     }else | ||||
|     { | ||||
|       xact_out_prepare(epnum); | ||||
|       if ( xfer->data_received ) | ||||
|       { | ||||
|         // Data may already be received previously | ||||
|         xfer->data_received = false; | ||||
|  | ||||
|         // start DMA to copy to SRAM | ||||
|         xact_out_dma(epnum); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         // nRF auto accept next Bulk/Interrupt OUT packet | ||||
|         // nothing to do | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     xact_in_prepare(epnum); | ||||
|     // Start DMA to copy data from RAM -> Endpoint | ||||
|     xact_in_dma(epnum); | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| @@ -477,6 +468,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   (void) rhport; | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   if ( epnum != 0 && epnum != EP_ISO_NUM ) | ||||
|   { | ||||
| @@ -486,6 +478,10 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) | ||||
|     // reset data toggle to DATA0 | ||||
|     NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr; | ||||
|  | ||||
|     // Write any value to SIZE register will allow nRF to ACK/accept data | ||||
|     // Drop any pending data | ||||
|     if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0; | ||||
|  | ||||
|     __ISB(); __DSB(); | ||||
|   } | ||||
| } | ||||
| @@ -594,17 +590,18 @@ void dcd_int_handler(uint8_t rhport) | ||||
|   // Setup tokens are specific to the Control endpoint. | ||||
|   if ( int_status & USBD_INTEN_EP0SETUP_Msk ) | ||||
|   { | ||||
|     uint8_t const setup[8] = { | ||||
|         NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH, | ||||
|         NRF_USBD->WINDEXL       , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH | ||||
|     uint8_t const setup[8] = | ||||
|     { | ||||
|       NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH, | ||||
|       NRF_USBD->WINDEXL       , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH | ||||
|     }; | ||||
|  | ||||
|     // nrf5x hw auto handle set address, there is no need to inform usb stack | ||||
|     tusb_control_request_t const * request = (tusb_control_request_t const *) setup; | ||||
|  | ||||
|     if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient && | ||||
|     if ( !(TUSB_REQ_RCPT_DEVICE   == request->bmRequestType_bit.recipient && | ||||
|            TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && | ||||
|            TUSB_REQ_SET_ADDRESS == request->bRequest) ) | ||||
|            TUSB_REQ_SET_ADDRESS   == request->bRequest) ) | ||||
|     { | ||||
|       dcd_event_setup_received(0, setup, true); | ||||
|     } | ||||
| @@ -620,15 +617,15 @@ void dcd_int_handler(uint8_t rhport) | ||||
|    * For CBI OUT: | ||||
|    *  - Host -> Endpoint | ||||
|    *      EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPOUT[i] | ||||
|    *      to start DMA. This step can occur automatically (without sw), | ||||
|    *      which means data may or may not ready (data_received flag). | ||||
|    *      to start DMA. For Bulk/Interrupt, this step can occur automatically (without sw), | ||||
|    *      which means data may or may not be ready (data_received flag). | ||||
|    *  - Endpoint -> RAM | ||||
|    *      ENDEPOUT[i] interrupted, transaction complete, sw prepare next transaction | ||||
|    * | ||||
|    * For CBI IN: | ||||
|    *  - RAM -> Endpoint | ||||
|    *      ENDEPIN[i] interrupted indicate DMA is complete. HW will start | ||||
|    *      to move daat to host | ||||
|    *      to move data to host | ||||
|    *  - Endpoint -> Host | ||||
|    *      EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPIN[i]. | ||||
|    *      Transaction is complete, sw prepare next transaction | ||||
| @@ -640,27 +637,31 @@ void dcd_int_handler(uint8_t rhport) | ||||
|  | ||||
|   /* CBI OUT: Endpoint -> SRAM (aka transaction complete) | ||||
|    * Note: Since nRF controller auto ACK next packet without SW awareness | ||||
|    * We must handle this stage before Host -> Endpoint just in case | ||||
|    * 2 event happens at once | ||||
|    * ISO OUT: Transaction must fit in single packed, it can be shorter then total | ||||
|    * We must handle this stage before Host -> Endpoint just in case 2 event happens at once | ||||
|    * | ||||
|    * ISO OUT: Transaction must fit in single packet, it can be shorter then total | ||||
|    * len if Host decides to sent fewer bytes, it this case transaction is also | ||||
|    * complete and next transfer is not initiated here like for CBI. | ||||
|    */ | ||||
|   for(uint8_t epnum=0; epnum<EP_COUNT+1; epnum++) | ||||
|   for(uint8_t epnum=0; epnum<EP_CBI_COUNT+1; epnum++) | ||||
|   { | ||||
|     if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum)) | ||||
|     { | ||||
|       xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); | ||||
|       uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT; | ||||
|  | ||||
|       // Data in endpoint has been consumed | ||||
|       xfer->data_received = false; | ||||
|  | ||||
|       // Transfer complete if transaction len < Max Packet Size or total len is transferred | ||||
|       if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) ) | ||||
|       { | ||||
|         // Prepare for next transaction | ||||
|         xact_out_prepare(epnum); | ||||
|         if ( epnum == 0 ) | ||||
|         { | ||||
|           // Accept next Control Out packet | ||||
|           NRF_USBD->TASKS_EP0RCVOUT = 1; | ||||
|         }else | ||||
|         { | ||||
|           // nRF auto accept next Bulk/Interrupt OUT packet | ||||
|           // nothing to do | ||||
|         } | ||||
|       }else | ||||
|       { | ||||
|         xfer->total_len = xfer->actual_len; | ||||
| @@ -673,7 +674,7 @@ void dcd_int_handler(uint8_t rhport) | ||||
|     // Ended event for CBI IN : nothing to do | ||||
|   } | ||||
|  | ||||
|   // Endpoint <-> Host | ||||
|   // Endpoint <-> Host ( In & OUT ) | ||||
|   if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) ) | ||||
|   { | ||||
|     uint32_t data_status = NRF_USBD->EPDATASTATUS; | ||||
| @@ -687,9 +688,9 @@ void dcd_int_handler(uint8_t rhport) | ||||
|     bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK); | ||||
|  | ||||
|     // CBI In: Endpoint -> Host (transaction complete) | ||||
|     for(uint8_t epnum=0; epnum<8; epnum++) | ||||
|     for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++) | ||||
|     { | ||||
|       if ( tu_bit_test(data_status, epnum ) || ( epnum == 0 && is_control_in) ) | ||||
|       if ( tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in) ) | ||||
|       { | ||||
|         xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN); | ||||
|  | ||||
| @@ -697,8 +698,8 @@ void dcd_int_handler(uint8_t rhport) | ||||
|  | ||||
|         if ( xfer->actual_len < xfer->total_len ) | ||||
|         { | ||||
|           // prepare next transaction | ||||
|           xact_in_prepare(epnum); | ||||
|           // Start DMA to copy next data packet | ||||
|           xact_in_dma(epnum); | ||||
|         } else | ||||
|         { | ||||
|           // CBI IN complete | ||||
| @@ -708,9 +709,9 @@ void dcd_int_handler(uint8_t rhport) | ||||
|     } | ||||
|  | ||||
|     // CBI OUT: Host -> Endpoint | ||||
|     for(uint8_t epnum=0; epnum<8; epnum++) | ||||
|     for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++) | ||||
|     { | ||||
|       if ( tu_bit_test(data_status, 16+epnum ) || ( epnum == 0 && is_control_out) ) | ||||
|       if ( tu_bit_test(data_status, 16+epnum) || (epnum == 0 && is_control_out) ) | ||||
|       { | ||||
|         xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); | ||||
|  | ||||
| @@ -719,7 +720,7 @@ void dcd_int_handler(uint8_t rhport) | ||||
|           xact_out_dma(epnum); | ||||
|         }else | ||||
|         { | ||||
|           // Data overflow !!! Nah, nrf52840 will auto ACK OUT packet after DMA is done | ||||
|           // Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet | ||||
|           // Mark this endpoint with data received | ||||
|           xfer->data_received = true; | ||||
|         } | ||||
|   | ||||
| @@ -26,7 +26,8 @@ | ||||
|  | ||||
| #include "tusb_option.h" | ||||
|  | ||||
| #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) | ||||
| #if TUSB_OPT_DEVICE_ENABLED && \ | ||||
|     (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) | ||||
|  | ||||
| #include "device/dcd.h" | ||||
| #include "dcd_lpc17_40.h" | ||||
|   | ||||
| @@ -24,8 +24,8 @@ | ||||
|  * This file is part of the TinyUSB stack. | ||||
|  */ | ||||
|  | ||||
| #ifndef _TUSB_DCD_LPC175X_6X_H_ | ||||
| #define _TUSB_DCD_LPC175X_6X_H_ | ||||
| #ifndef _TUSB_DCD_LPC17_40_H_ | ||||
| #define _TUSB_DCD_LPC17_40_H_ | ||||
|  | ||||
| #include "common/tusb_common.h" | ||||
|  | ||||
| @@ -149,4 +149,4 @@ enum { | ||||
|  } | ||||
| #endif | ||||
|  | ||||
| #endif /* _TUSB_DCD_LPC175X_6X_H_ */ | ||||
| #endif | ||||
|   | ||||
| @@ -26,7 +26,8 @@ | ||||
|  | ||||
| #include "tusb_option.h" | ||||
|  | ||||
| #if (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) | ||||
| #if TUSB_OPT_HOST_ENABLED && \ | ||||
|     (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) | ||||
|  | ||||
| #include "chip.h" | ||||
|  | ||||
|   | ||||
							
								
								
									
										736
									
								
								src/portable/renesas/usba/dcd_usba.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										736
									
								
								src/portable/renesas/usba/dcd_usba.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,736 @@ | ||||
| /*  | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Koji Kitayama | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  * This file is part of the TinyUSB stack. | ||||
|  */ | ||||
|  | ||||
| #include "tusb_option.h" | ||||
|  | ||||
| #if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X ) | ||||
|  | ||||
| #include "device/dcd.h" | ||||
| #include "iodefine.h" | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO TYPEDEF CONSTANT ENUM DECLARATION | ||||
| //--------------------------------------------------------------------+ | ||||
| #define SYSTEM_PRCR_PRC1     (1<<1) | ||||
| #define SYSTEM_PRCR_PRKEY    (0xA5u<<8) | ||||
|  | ||||
| #define USB_FIFOSEL_TX       ((uint16_t)(1u<<5)) | ||||
| #define USB_FIFOSEL_MBW_8    ((uint16_t)(0u<<10)) | ||||
| #define USB_FIFOSEL_MBW_16   ((uint16_t)(1u<<10)) | ||||
| #define USB_IS0_CTSQ         ((uint16_t)(7u)) | ||||
| #define USB_IS0_DVSQ         ((uint16_t)(7u<<4)) | ||||
| #define USB_IS0_VALID        ((uint16_t)(1u<<3)) | ||||
| #define USB_IS0_BRDY         ((uint16_t)(1u<<8)) | ||||
| #define USB_IS0_NRDY         ((uint16_t)(1u<<9)) | ||||
| #define USB_IS0_BEMP         ((uint16_t)(1u<<10)) | ||||
| #define USB_IS0_CTRT         ((uint16_t)(1u<<11)) | ||||
| #define USB_IS0_DVST         ((uint16_t)(1u<<12)) | ||||
| #define USB_IS0_SOFR         ((uint16_t)(1u<<13)) | ||||
| #define USB_IS0_RESM         ((uint16_t)(1u<<14)) | ||||
| #define USB_IS0_VBINT        ((uint16_t)(1u<<15)) | ||||
| #define USB_IS1_SACK         ((uint16_t)(1u<<4)) | ||||
| #define USB_IS1_SIGN         ((uint16_t)(1u<<5)) | ||||
| #define USB_IS1_EOFERR       ((uint16_t)(1u<<6)) | ||||
| #define USB_IS1_ATTCH        ((uint16_t)(1u<<11)) | ||||
| #define USB_IS1_DTCH         ((uint16_t)(1u<<12)) | ||||
| #define USB_IS1_BCHG         ((uint16_t)(1u<<14)) | ||||
| #define USB_IS1_OVRCR        ((uint16_t)(1u<<15)) | ||||
|  | ||||
| #define USB_IS0_CTSQ_MSK     (7u) | ||||
| #define USB_IS0_CTSQ_SETUP   (1u) | ||||
| #define USB_IS0_DVSQ_DEF     (1u<<4) | ||||
| #define USB_IS0_DVSQ_ADDR    (2u<<4) | ||||
| #define USB_IS0_DVSQ_SUSP    (4u<<4) | ||||
|  | ||||
| #define USB_PIPECTR_PID_NAK   (0u) | ||||
| #define USB_PIPECTR_PID_BUF   (1u) | ||||
| #define USB_PIPECTR_PID_STALL (2u) | ||||
| #define USB_PIPECTR_CCPL      (1u<<2) | ||||
| #define USB_PIPECTR_SQMON     (1u<<6) | ||||
| #define USB_PIPECTR_SQCLR     (1u<<8) | ||||
| #define USB_PIPECTR_ACLRM     (1u<<9) | ||||
| #define USB_PIPECTR_INBUFM    (1u<<14) | ||||
| #define USB_PIPECTR_BSTS      (1u<<15) | ||||
|  | ||||
| #define USB_FIFOCTR_DTLN     (0x1FF) | ||||
| #define USB_FIFOCTR_FRDY     (1u<<13) | ||||
| #define USB_FIFOCTR_BCLR     (1u<<14) | ||||
| #define USB_FIFOCTR_BVAL     (1u<<15) | ||||
|  | ||||
| #define USB_PIPECFG_SHTNAK   (1u<<7) | ||||
| #define USB_PIPECFG_DBLB     (1u<<9) | ||||
| #define USB_PIPECFG_BULK     (1u<<14) | ||||
| #define USB_PIPECFG_ISO      (3u<<14) | ||||
| #define USB_PIPECFG_INT      (2u<<14) | ||||
|  | ||||
| #define FIFO_REQ_CLR         (1u) | ||||
| #define FIFO_COMPLETE        (1u<<1) | ||||
|  | ||||
| typedef struct { | ||||
|   union { | ||||
|     struct { | ||||
|       uint16_t      : 8; | ||||
|       uint16_t TRCLR: 1; | ||||
|       uint16_t TRENB: 1; | ||||
|       uint16_t      : 0; | ||||
|     }; | ||||
|     uint16_t TRE; | ||||
|   }; | ||||
|   uint16_t TRN; | ||||
| } reg_pipetre_t; | ||||
|  | ||||
| typedef union { | ||||
|   struct { | ||||
|     volatile uint16_t u8: 8; | ||||
|     volatile uint16_t   : 0; | ||||
|   }; | ||||
|   volatile uint16_t u16; | ||||
| } hw_fifo_t; | ||||
|  | ||||
| typedef struct TU_ATTR_PACKED | ||||
| { | ||||
|   uintptr_t addr;      /* the start address of a transfer data buffer */ | ||||
|   uint16_t  length;    /* the number of bytes in the buffer */ | ||||
|   uint16_t  remaining; /* the number of bytes remaining in the buffer */ | ||||
|   struct { | ||||
|     uint32_t ep  : 8;  /* an assigned endpoint address */ | ||||
|     uint32_t     : 0; | ||||
|   }; | ||||
| } pipe_state_t; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   pipe_state_t pipe[9]; | ||||
|   uint8_t ep[2][16];   /* a lookup table for a pipe index from an endpoint address */ | ||||
| } dcd_data_t; | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // INTERNAL OBJECT & FUNCTION DECLARATION | ||||
| //--------------------------------------------------------------------+ | ||||
| CFG_TUSB_MEM_SECTION static dcd_data_t _dcd; | ||||
|  | ||||
| static uint32_t disable_interrupt(void) | ||||
| { | ||||
|   uint32_t pswi; | ||||
|   pswi = __builtin_rx_mvfc(0) & 0x010000; | ||||
|   __builtin_rx_clrpsw('I'); | ||||
|   return pswi; | ||||
| } | ||||
|  | ||||
| static void enable_interrupt(uint32_t pswi) | ||||
| { | ||||
|   __builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi); | ||||
| } | ||||
|  | ||||
| static unsigned find_pipe(unsigned xfer) | ||||
| { | ||||
|   switch (xfer) { | ||||
|   case TUSB_XFER_ISOCHRONOUS: | ||||
|     for (int i = 1; i <= 2; ++i) { | ||||
|       if (0 == _dcd.pipe[i].ep) return  i; | ||||
|     } | ||||
|     break; | ||||
|   case TUSB_XFER_BULK: | ||||
|     for (int i = 3; i <= 5; ++i) { | ||||
|       if (0 == _dcd.pipe[i].ep) return  i; | ||||
|     } | ||||
|     for (int i = 1; i <= 1; ++i) { | ||||
|       if (0 == _dcd.pipe[i].ep) return  i; | ||||
|     } | ||||
|     break; | ||||
|   case TUSB_XFER_INTERRUPT: | ||||
|     for (int i = 6; i <= 9; ++i) { | ||||
|       if (0 == _dcd.pipe[i].ep) return  i; | ||||
|     } | ||||
|     break; | ||||
|   default: | ||||
|     /* No support for control transfer */ | ||||
|     break; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static volatile uint16_t* get_pipectr(unsigned num) | ||||
| { | ||||
|   volatile uint16_t *ctr = NULL; | ||||
|   if (num) { | ||||
|     ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD; | ||||
|     ctr += num - 1; | ||||
|   } else { | ||||
|     ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD; | ||||
|   } | ||||
|   return ctr; | ||||
| } | ||||
|  | ||||
| static volatile reg_pipetre_t* get_pipetre(unsigned num) | ||||
| { | ||||
|   volatile reg_pipetre_t* tre = NULL; | ||||
|   if ((1 <= num) && (num <= 5)) { | ||||
|     tre = (volatile reg_pipetre_t*)&USB0.PIPE1TRE.WORD; | ||||
|     tre += num - 1; | ||||
|   } | ||||
|   return tre; | ||||
| } | ||||
|  | ||||
| static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) | ||||
| { | ||||
|   (void)rhport; | ||||
|   volatile uint16_t *ctr = NULL; | ||||
|   const unsigned epn   = tu_edpt_number(ep_addr); | ||||
|   if (epn) { | ||||
|     const unsigned dir = tu_edpt_dir(ep_addr); | ||||
|     const unsigned num = _dcd.ep[dir][epn]; | ||||
|     if (num) { | ||||
|       ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD; | ||||
|       ctr += num - 1; | ||||
|     } | ||||
|   } else { | ||||
|     ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD; | ||||
|   } | ||||
|   return ctr; | ||||
| } | ||||
|  | ||||
| static unsigned wait_for_pipe_ready(void) | ||||
| { | ||||
|   unsigned ctr; | ||||
|   do { | ||||
|     ctr = USB0.D0FIFOCTR.WORD; | ||||
|   } while (!(ctr & USB_FIFOCTR_FRDY)); | ||||
|   return ctr; | ||||
| } | ||||
|  | ||||
| static unsigned select_pipe(unsigned num, unsigned attr) | ||||
| { | ||||
|   USB0.PIPESEL.WORD  = num; | ||||
|   USB0.D0FIFOSEL.WORD = num | attr; | ||||
|   while (!(USB0.D0FIFOSEL.BIT.CURPIPE != num)) ; | ||||
|   return wait_for_pipe_ready(); | ||||
| } | ||||
|  | ||||
| /* 1 less than mps bytes were written to FIFO | ||||
|  * 2 no bytes were written to FIFO | ||||
|  * 0 mps bytes were written to FIFO */ | ||||
| static int fifo_write(volatile void *fifo, pipe_state_t* pipe, unsigned mps) | ||||
| { | ||||
|   unsigned rem  = pipe->remaining; | ||||
|   if (!rem) return 2; | ||||
|   unsigned len  = TU_MIN(rem, mps); | ||||
|  | ||||
|   hw_fifo_t *reg = (hw_fifo_t*)fifo; | ||||
|   uintptr_t addr = pipe->addr + pipe->length - rem; | ||||
|   if (addr & 1u) { | ||||
|     /* addr is not 2-byte aligned */ | ||||
|     reg->u8 = *(const uint8_t *)addr; | ||||
|     ++addr; | ||||
|     --len; | ||||
|   } | ||||
|   while (len >= 2) { | ||||
|     reg->u16 = *(const uint16_t *)addr; | ||||
|     addr += 2; | ||||
|     len  -= 2; | ||||
|   } | ||||
|   if (len) { | ||||
|     reg->u8 = *(const uint8_t *)addr; | ||||
|     ++addr; | ||||
|   } | ||||
|   if (rem < mps) return 1; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* 1 less than mps bytes were read from FIFO | ||||
|  * 2 the end of the buffer reached. | ||||
|  * 0 mps bytes were read from FIFO */ | ||||
| static int fifo_read(volatile void *fifo, pipe_state_t* pipe, unsigned mps, size_t len) | ||||
| { | ||||
|   unsigned rem  = pipe->remaining; | ||||
|   if (!rem) return 2; | ||||
|   if (rem < len) len = rem; | ||||
|   pipe->remaining = rem - len; | ||||
|  | ||||
|   hw_fifo_t *reg = (hw_fifo_t*)fifo; | ||||
|   uintptr_t addr = pipe->addr; | ||||
|   unsigned  loop = len; | ||||
|   while (loop--) { | ||||
|     *(uint8_t *)addr = reg->u8; | ||||
|     ++addr; | ||||
|   } | ||||
|   pipe->addr = addr; | ||||
|   if (rem < mps)  return 1; | ||||
|   if (rem == len) return 2; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static void process_setup_packet(uint8_t rhport) | ||||
| { | ||||
|   uint16_t setup_packet[4]; | ||||
|   if (0 == (USB0.INTSTS0.WORD & USB_IS0_VALID)) return; | ||||
|   USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR; | ||||
|   setup_packet[0] = USB0.USBREQ.WORD; | ||||
|   setup_packet[1] = USB0.USBVAL; | ||||
|   setup_packet[2] = USB0.USBINDX; | ||||
|   setup_packet[3] = USB0.USBLENG; | ||||
|   USB0.INTSTS0.WORD = ~USB_IS0_VALID; | ||||
|   dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet[0], true); | ||||
| } | ||||
|  | ||||
| static void process_status_completion(uint8_t rhport) | ||||
| { | ||||
|   uint8_t ep_addr; | ||||
|   /* Check the data stage direction */ | ||||
|   if (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) { | ||||
|     /* IN transfer. */ | ||||
|     ep_addr = tu_edpt_addr(0, TUSB_DIR_IN); | ||||
|   } else { | ||||
|     /* OUT transfer. */ | ||||
|     ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT); | ||||
|   } | ||||
|   dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true); | ||||
| } | ||||
|  | ||||
| static bool process_edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   pipe_state_t *pipe = &_dcd.pipe[0]; | ||||
|   /* configure fifo direction and access unit settings */ | ||||
|   if (ep_addr) { /* IN, 2 bytes */ | ||||
|     USB0.CFIFOSEL.WORD = USB_FIFOSEL_TX | USB_FIFOSEL_MBW_16; | ||||
|     while (!(USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX)) ; | ||||
|   } else {       /* OUT, a byte */ | ||||
|     USB0.CFIFOSEL.WORD = USB_FIFOSEL_MBW_8; | ||||
|     while (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) ; | ||||
|   } | ||||
|   if (total_bytes) { | ||||
|     pipe->addr      = (uintptr_t)buffer; | ||||
|     pipe->length    = total_bytes; | ||||
|     pipe->remaining = total_bytes; | ||||
|     if (ep_addr) { /* IN */ | ||||
|       TU_ASSERT(USB0.DCPCTR.BIT.BSTS && (USB0.USBREQ.WORD & 0x80)); | ||||
|       if (fifo_write(&USB0.CFIFO.WORD, pipe, 64)) { | ||||
|         USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL; | ||||
|       } | ||||
|     } | ||||
|     USB0.DCPCTR.WORD = USB_PIPECTR_PID_BUF; | ||||
|   } else { | ||||
|     /* ZLP */ | ||||
|     pipe->addr       = 0; | ||||
|     pipe->length     = 0; | ||||
|     pipe->remaining  = 0; | ||||
|     USB0.DCPCTR.WORD = USB_PIPECTR_CCPL | USB_PIPECTR_PID_BUF; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static void process_edpt0_bemp(uint8_t rhport) | ||||
| { | ||||
|   pipe_state_t *pipe = &_dcd.pipe[0]; | ||||
|   const unsigned rem = pipe->remaining; | ||||
|   if (rem > 64) { | ||||
|     pipe->remaining = rem - 64; | ||||
|     int r = fifo_write(&USB0.CFIFO.WORD, &_dcd.pipe[0], 64); | ||||
|     if (r) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL; | ||||
|     return; | ||||
|   } | ||||
|   pipe->addr      = 0; | ||||
|   pipe->remaining = 0; | ||||
|   dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), | ||||
|                           pipe->length, XFER_RESULT_SUCCESS, true); | ||||
| } | ||||
|  | ||||
| static void process_edpt0_brdy(uint8_t rhport) | ||||
| { | ||||
|   size_t len = USB0.CFIFOCTR.BIT.DTLN; | ||||
|   int cplt = fifo_read(&USB0.CFIFO.WORD, &_dcd.pipe[0], 64, len); | ||||
|   if (cplt || (len < 64)) { | ||||
|     if (2 != cplt) { | ||||
|       USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR; | ||||
|     } | ||||
|     dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), | ||||
|                             _dcd.pipe[0].length - _dcd.pipe[0].remaining, | ||||
|                             XFER_RESULT_SUCCESS, true); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static bool process_pipe_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   const unsigned epn = tu_edpt_number(ep_addr); | ||||
|   const unsigned dir = tu_edpt_dir(ep_addr); | ||||
|   const unsigned num = _dcd.ep[dir][epn]; | ||||
|  | ||||
|   TU_ASSERT(num); | ||||
|  | ||||
|   pipe_state_t *pipe = &_dcd.pipe[num]; | ||||
|   pipe->addr      = (uintptr_t)buffer; | ||||
|   pipe->length    = total_bytes; | ||||
|   pipe->remaining = total_bytes; | ||||
|  | ||||
|   USB0.PIPESEL.WORD  = num; | ||||
|   const unsigned mps = USB0.PIPEMAXP.WORD; | ||||
|   if (dir) { /* IN */ | ||||
|     USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_16; | ||||
|     while (!(USB0.D0FIFOSEL.BIT.CURPIPE != num)) ; | ||||
|     int r = fifo_write(&USB0.D0FIFO.WORD, pipe, mps); | ||||
|     if (r) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL; | ||||
|     USB0.D0FIFOSEL.WORD = 0; | ||||
|   } else { | ||||
|     volatile reg_pipetre_t *pt = get_pipetre(num); | ||||
|     if (pt) { | ||||
|       volatile uint16_t *ctr = get_pipectr(num); | ||||
|       if (*ctr & 0x3) *ctr = USB_PIPECTR_PID_NAK; | ||||
|       pt->TRE   = TU_BIT(8); | ||||
|       pt->TRN   = (total_bytes + mps - 1) / mps; | ||||
|       pt->TRENB = 1; | ||||
|       *ctr = USB_PIPECTR_PID_BUF; | ||||
|     } | ||||
|   } | ||||
|   //  TU_LOG1("X %x %d\r\n", ep_addr, total_bytes); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static void process_pipe_brdy(uint8_t rhport, unsigned num) | ||||
| { | ||||
|   pipe_state_t *pipe = &_dcd.pipe[num]; | ||||
|   if (tu_edpt_dir(pipe->ep)) { /* IN */ | ||||
|     select_pipe(num, USB_FIFOSEL_MBW_16); | ||||
|     const unsigned mps = USB0.PIPEMAXP.WORD; | ||||
|     unsigned rem       = pipe->remaining; | ||||
|     rem               -= TU_MIN(rem, mps); | ||||
|     pipe->remaining    = rem; | ||||
|     if (rem) { | ||||
|       int r = 0; | ||||
|       r = fifo_write(&USB0.D0FIFO.WORD, pipe, mps); | ||||
|       if (r) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL; | ||||
|       USB0.D0FIFOSEL.WORD = 0; | ||||
|       return; | ||||
|     } | ||||
|     USB0.D0FIFOSEL.WORD = 0; | ||||
|     pipe->addr      = 0; | ||||
|     pipe->remaining = 0; | ||||
|     dcd_event_xfer_complete(rhport, pipe->ep, pipe->length, | ||||
|                             XFER_RESULT_SUCCESS, true); | ||||
|   } else { | ||||
|     const unsigned ctr = select_pipe(num, USB_FIFOSEL_MBW_8); | ||||
|     const unsigned len = ctr & USB_FIFOCTR_DTLN; | ||||
|     const unsigned mps = USB0.PIPEMAXP.WORD; | ||||
|     int cplt = fifo_read(&USB0.D0FIFO.WORD, pipe, mps, len); | ||||
|     if (cplt || (len < mps)) { | ||||
|       if (2 != cplt) { | ||||
|         USB0.D0FIFO.WORD = USB_FIFOCTR_BCLR; | ||||
|       } | ||||
|       USB0.D0FIFOSEL.WORD = 0; | ||||
|       dcd_event_xfer_complete(rhport, pipe->ep, | ||||
|                               pipe->length - pipe->remaining, | ||||
|                               XFER_RESULT_SUCCESS, true); | ||||
|       return; | ||||
|     } | ||||
|     USB0.D0FIFOSEL.WORD = 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void process_bus_reset(uint8_t rhport) | ||||
| { | ||||
|   USB0.BEMPENB.WORD   = 1; | ||||
|   USB0.BRDYENB.WORD   = 1; | ||||
|   USB0.CFIFOCTR.WORD  = USB_FIFOCTR_BCLR; | ||||
|   USB0.D0FIFOSEL.WORD = 0; | ||||
|   USB0.D1FIFOSEL.WORD = 0; | ||||
|   volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1CTR.WORD)); | ||||
|   volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1TRE.WORD)); | ||||
|   for (int i = 1; i <= 5; ++i) { | ||||
|     USB0.PIPESEL.WORD  = i; | ||||
|     USB0.PIPECFG.WORD  = 0; | ||||
|     *ctr = USB_PIPECTR_ACLRM; | ||||
|     *ctr = 0; | ||||
|     ++ctr; | ||||
|     *tre = TU_BIT(8); | ||||
|     tre += 2; | ||||
|   } | ||||
|   for (int i = 6; i <= 9; ++i) { | ||||
|     USB0.PIPESEL.WORD  = i; | ||||
|     USB0.PIPECFG.WORD  = 0; | ||||
|     *ctr = USB_PIPECTR_ACLRM; | ||||
|     *ctr = 0; | ||||
|     ++ctr; | ||||
|   } | ||||
|   tu_varclr(&_dcd); | ||||
|   dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true); | ||||
| } | ||||
|  | ||||
| static void process_set_address(uint8_t rhport) | ||||
| { | ||||
|   const uint32_t addr = USB0.USBADDR.BIT.USBADDR; | ||||
|   if (!addr) return; | ||||
|   const tusb_control_request_t setup_packet = { | ||||
|     .bmRequestType = 0, | ||||
|     .bRequest      = 5, | ||||
|     .wValue        = addr, | ||||
|     .wIndex        = 0, | ||||
|     .wLength       = 0, | ||||
|   }; | ||||
|   dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet, true); | ||||
| } | ||||
|  | ||||
| /*------------------------------------------------------------------*/ | ||||
| /* Device API | ||||
|  *------------------------------------------------------------------*/ | ||||
| void dcd_init(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|   /* Enable USB0 */ | ||||
|   uint32_t pswi = disable_interrupt(); | ||||
|   SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1; | ||||
|   MSTP(USB0) = 0; | ||||
|   SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY; | ||||
|   enable_interrupt(pswi); | ||||
|   USB0.SYSCFG.BIT.SCKE = 1; | ||||
|   while (!USB0.SYSCFG.BIT.SCKE) ; | ||||
|   USB0.SYSCFG.BIT.DRPD = 0; | ||||
|   USB0.SYSCFG.BIT.DCFM = 0; | ||||
|   USB0.SYSCFG.BIT.USBE = 1; | ||||
|  | ||||
|   IR(USB0, USBI0)   = 0; | ||||
|  | ||||
|   /* Setup default control pipe */ | ||||
|   USB0.DCPMAXP.BIT.MXPS  = 64; | ||||
|   USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP | USB_IS0_DVST | USB_IS0_CTRT; | ||||
|   USB0.BEMPENB.WORD = 1; | ||||
|   USB0.BRDYENB.WORD = 1; | ||||
|  | ||||
|   if (USB0.INTSTS0.BIT.VBSTS) { | ||||
|     dcd_connect(rhport); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void dcd_int_enable(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|   IEN(USB0, USBI0) = 1; | ||||
| } | ||||
|  | ||||
| void dcd_int_disable(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|   IEN(USB0, USBI0) = 0; | ||||
| } | ||||
|  | ||||
| void dcd_set_address(uint8_t rhport, uint8_t dev_addr) | ||||
| { | ||||
|   (void)rhport; | ||||
|   (void)dev_addr; | ||||
| } | ||||
|  | ||||
| void dcd_remote_wakeup(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|   /* TODO */ | ||||
| } | ||||
|  | ||||
| void dcd_connect(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|   USB0.SYSCFG.BIT.DPRPU = 1; | ||||
| } | ||||
|  | ||||
| void dcd_disconnect(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|   USB0.SYSCFG.BIT.DPRPU = 0; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Endpoint API | ||||
| //--------------------------------------------------------------------+ | ||||
| bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   const unsigned ep_addr = ep_desc->bEndpointAddress; | ||||
|   const unsigned epn     = tu_edpt_number(ep_addr); | ||||
|   const unsigned dir     = tu_edpt_dir(ep_addr); | ||||
|   const unsigned xfer    = ep_desc->bmAttributes.xfer; | ||||
|  | ||||
|   const unsigned mps = ep_desc->wMaxPacketSize.size; | ||||
|   if (xfer == TUSB_XFER_ISOCHRONOUS && mps > 256) { | ||||
|     /* USBa supports up to 256 bytes */ | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   const unsigned num = find_pipe(xfer); | ||||
|   if (!num) return false; | ||||
|   _dcd.pipe[num].ep = ep_addr; | ||||
|   _dcd.ep[dir][epn] = num; | ||||
|  | ||||
|   /* setup pipe */ | ||||
|   dcd_int_disable(rhport); | ||||
|   USB0.PIPESEL.WORD  = num; | ||||
|   USB0.PIPEMAXP.WORD = mps; | ||||
|   volatile uint16_t *ctr = get_pipectr(num); | ||||
|   *ctr = USB_PIPECTR_ACLRM; | ||||
|   *ctr = 0; | ||||
|   unsigned cfg = (dir << 4) | epn; | ||||
|   if (xfer == TUSB_XFER_BULK) { | ||||
|     cfg |= USB_PIPECFG_BULK | USB_PIPECFG_SHTNAK | USB_PIPECFG_DBLB; | ||||
|   } else if (xfer == TUSB_XFER_INTERRUPT) { | ||||
|     cfg |= USB_PIPECFG_INT; | ||||
|   } else { | ||||
|     cfg |= USB_PIPECFG_ISO | USB_PIPECFG_DBLB; | ||||
|   } | ||||
|   USB0.PIPECFG.WORD  = cfg; | ||||
|   USB0.BRDYSTS.WORD  = 0x1FFu ^ TU_BIT(num); | ||||
|   USB0.BRDYENB.WORD |= TU_BIT(num); | ||||
|   if (dir || (xfer != TUSB_XFER_BULK)) { | ||||
|     *ctr = USB_PIPECTR_PID_BUF; | ||||
|   } | ||||
|   //  TU_LOG1("O %d %x %x\r\n", USB0.PIPESEL.WORD, USB0.PIPECFG.WORD, USB0.PIPEMAXP.WORD); | ||||
|   dcd_int_enable(rhport); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   (void)rhport; | ||||
|   const unsigned epn = tu_edpt_number(ep_addr); | ||||
|   const unsigned dir = tu_edpt_dir(ep_addr); | ||||
|   const unsigned num = _dcd.ep[dir][epn]; | ||||
|  | ||||
|   USB0.BRDYENB.WORD &= ~TU_BIT(num); | ||||
|   volatile uint16_t *ctr = get_pipectr(num); | ||||
|   *ctr = 0; | ||||
|   USB0.PIPESEL.WORD = num; | ||||
|   USB0.PIPECFG.WORD = 0; | ||||
|   _dcd.pipe[num].ep = 0; | ||||
|   _dcd.ep[dir][epn] = 0; | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) | ||||
| { | ||||
|   bool r; | ||||
|   const unsigned epn = tu_edpt_number(ep_addr); | ||||
|   dcd_int_disable(rhport); | ||||
|   if (0 == epn) { | ||||
|     r = process_edpt0_xfer(rhport, ep_addr, buffer, total_bytes); | ||||
|   } else { | ||||
|     r = process_pipe_xfer(rhport, ep_addr, buffer, total_bytes); | ||||
|   } | ||||
|   dcd_int_enable(rhport); | ||||
|   return r; | ||||
| } | ||||
|  | ||||
| void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr); | ||||
|   if (!ctr) return; | ||||
|   dcd_int_disable(rhport); | ||||
|   const uint32_t pid = *ctr & 0x3; | ||||
|   *ctr = pid | USB_PIPECTR_PID_STALL; | ||||
|   *ctr = USB_PIPECTR_PID_STALL; | ||||
|   dcd_int_enable(rhport); | ||||
| } | ||||
|  | ||||
| void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr); | ||||
|   if (!ctr) return; | ||||
|   dcd_int_disable(rhport); | ||||
|   *ctr = USB_PIPECTR_SQCLR; | ||||
|  | ||||
|   if (tu_edpt_dir(ep_addr)) { /* IN */ | ||||
|     *ctr = USB_PIPECTR_PID_BUF; | ||||
|   } else { | ||||
|     const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)]; | ||||
|     USB0.PIPESEL.WORD  = num; | ||||
|     if (USB0.PIPECFG.BIT.TYPE != 1) { | ||||
|       *ctr = USB_PIPECTR_PID_BUF; | ||||
|     } | ||||
|   } | ||||
|   dcd_int_enable(rhport); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // ISR | ||||
| //--------------------------------------------------------------------+ | ||||
| void dcd_int_handler(uint8_t rhport) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   unsigned is0 = USB0.INTSTS0.WORD; | ||||
|   /* clear bits except VALID */ | ||||
|   USB0.INTSTS0.WORD = USB_IS0_VALID; | ||||
|   if (is0 & USB_IS0_VBINT) { | ||||
|     if (USB0.INTSTS0.BIT.VBSTS) { | ||||
|       dcd_connect(rhport); | ||||
|     } else { | ||||
|       dcd_disconnect(rhport); | ||||
|     } | ||||
|   } | ||||
|   if (is0 & USB_IS0_DVST) { | ||||
|     switch (is0 & USB_IS0_DVSQ) { | ||||
|     case USB_IS0_DVSQ_DEF: | ||||
|       process_bus_reset(rhport); | ||||
|       break; | ||||
|     case USB_IS0_DVSQ_ADDR: | ||||
|       process_set_address(rhport); | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if (is0 & USB_IS0_CTRT) { | ||||
|     if (is0 & USB_IS0_CTSQ_SETUP) { | ||||
|       /* A setup packet has been received. */ | ||||
|       process_setup_packet(rhport); | ||||
|     } else if (0 == (is0 & USB_IS0_CTSQ_MSK)) { | ||||
|       /* A ZLP has been sent/received. */ | ||||
|       process_status_completion(rhport); | ||||
|     } | ||||
|   } | ||||
|   if (is0 & USB_IS0_BEMP) { | ||||
|     const unsigned s = USB0.BEMPSTS.WORD; | ||||
|     USB0.BEMPSTS.WORD = 0; | ||||
|     if (s & 1) { | ||||
|       process_edpt0_bemp(rhport); | ||||
|     } | ||||
|   } | ||||
|   if (is0 & USB_IS0_BRDY) { | ||||
|     const unsigned m = USB0.BRDYENB.WORD; | ||||
|     unsigned s       = USB0.BRDYSTS.WORD & m; | ||||
|     USB0.BRDYSTS.WORD = 0; | ||||
|     if (s & 1) { | ||||
|       process_edpt0_brdy(rhport); | ||||
|       s &= ~1; | ||||
|     } | ||||
|     while (s) { | ||||
|       const unsigned num = __builtin_ctz(s); | ||||
|       process_pipe_brdy(rhport, num); | ||||
|       s &= ~TU_BIT(num); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										931
									
								
								src/portable/silabs/efm32/dcd_efm32.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										931
									
								
								src/portable/silabs/efm32/dcd_efm32.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,931 @@ | ||||
| /*  | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2021 Rafael Silva (@perigoso) | ||||
|  * Copyright (c) 2021 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  * This file is part of the TinyUSB stack. | ||||
|  */ | ||||
|  | ||||
| #include "tusb_option.h" | ||||
|  | ||||
| #if TUSB_OPT_DEVICE_ENABLED && ( \ | ||||
|   (CFG_TUSB_MCU == OPT_MCU_EFM32GG) || \ | ||||
|   (CFG_TUSB_MCU == OPT_MCU_EFM32GG11) || \ | ||||
|   (CFG_TUSB_MCU == OPT_MCU_EFM32GG12) ) | ||||
|  | ||||
| /* Silabs */ | ||||
| #include "em_device.h" | ||||
|  | ||||
| #include "device/dcd.h" | ||||
|  | ||||
| /*  | ||||
|  * Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval) | ||||
|  * We disable SOF for now until needed later on | ||||
|  */ | ||||
| #define USE_SOF     0 | ||||
|  | ||||
| /*  | ||||
|  * Number of endpoints | ||||
|  * 12 software-configurable endpoints (6 IN, 6 OUT) in addition to endpoint 0 | ||||
|  */ | ||||
| #define EP_COUNT        7 | ||||
|  | ||||
| /* FIFO size in bytes */ | ||||
| #define EP_FIFO_SIZE    2048 | ||||
|  | ||||
| /* Max number of IN EP FIFOs */ | ||||
| #define EP_FIFO_NUM     7 | ||||
|  | ||||
| /* */ | ||||
| typedef struct { | ||||
|     uint8_t *buffer; | ||||
|     uint16_t total_len; | ||||
|     uint16_t queued_len; | ||||
|     uint16_t max_size; | ||||
|     bool short_packet; | ||||
| } xfer_ctl_t; | ||||
|  | ||||
| static uint32_t _setup_packet[2]; | ||||
|  | ||||
| #define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] | ||||
| static xfer_ctl_t xfer_status[EP_COUNT][2]; | ||||
|  | ||||
| /* Keep count of how many FIFOs are in use */ | ||||
| static uint8_t _allocated_fifos = 1; /* FIFO0 is always in use */ | ||||
|  | ||||
| static volatile uint32_t* tx_fifo[EP_FIFO_NUM] = { | ||||
|   USB->FIFO0D, | ||||
|   USB->FIFO1D, | ||||
|   USB->FIFO2D, | ||||
|   USB->FIFO3D, | ||||
|   USB->FIFO4D, | ||||
|   USB->FIFO5D, | ||||
|   USB->FIFO6D, | ||||
| }; | ||||
|  | ||||
| /* Register Helpers */ | ||||
| #define DCTL_WO_BITMASK     (USB_DCTL_CGOUTNAK | USB_DCTL_SGOUTNAK | USB_DCTL_CGNPINNAK | USB_DCTL_SGNPINNAK) | ||||
| #define GUSBCFG_WO_BITMASK  (USB_GUSBCFG_CORRUPTTXPKT) | ||||
| #define DEPCTL_WO_BITMASK   (USB_DIEP_CTL_CNAK | USB_DIEP_CTL_SNAK | USB_DIEP_CTL_SETD0PIDEF | USB_DIEP_CTL_SETD1PIDOF) | ||||
|  | ||||
| /* Will either return an unused FIFO number, or 0 if all are used. */ | ||||
| static uint8_t get_free_fifo(void) | ||||
| { | ||||
|   if(_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
| static void flush_rx_fifo(void) | ||||
| { | ||||
|   USB->GRSTCTL = USB_GRSTCTL_RXFFLSH; | ||||
|   while(USB->GRSTCTL & USB_GRSTCTL_RXFFLSH); | ||||
| }  | ||||
| */ | ||||
|  | ||||
| static void flush_tx_fifo(uint8_t fifo_num) | ||||
| { | ||||
|   USB->GRSTCTL = USB_GRSTCTL_TXFFLSH | (fifo_num << _USB_GRSTCTL_TXFNUM_SHIFT); | ||||
|   while(USB->GRSTCTL & USB_GRSTCTL_TXFFLSH); | ||||
| } | ||||
|  | ||||
| /* Setup the control endpoint 0. */ | ||||
| static void bus_reset(void) | ||||
| { | ||||
|   USB->DOEP0CTL |= USB_DIEP_CTL_SNAK; | ||||
|   for(uint8_t i = 0; i < EP_COUNT - 1; i++) | ||||
|   { | ||||
|     USB->DOEP[i].CTL |= USB_DIEP_CTL_SNAK; | ||||
|   } | ||||
|    | ||||
|   /* reset address */ | ||||
|   USB->DCFG &= ~_USB_DCFG_DEVADDR_MASK; | ||||
|  | ||||
|   USB->DAINTMSK |= USB_DAINTMSK_OUTEPMSK0 | USB_DAINTMSK_INEPMSK0; | ||||
|   USB->DOEPMSK |= USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK; | ||||
|   USB->DIEPMSK |= USB_DIEPMSK_TIMEOUTMSK | USB_DIEPMSK_XFERCOMPLMSK; | ||||
|  | ||||
|   /*  | ||||
|    * - All EP OUT shared a unique OUT FIFO which uses | ||||
|    *   * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets). | ||||
|    *   * 2 locations for OUT endpoint control words. | ||||
|    *   * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes) | ||||
|    *   * 1 location for global NAK (not required/used here). | ||||
|    *   * It is recommended to allocate 2 times the largest packet size, therefore | ||||
|    *  Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52 | ||||
|    */ | ||||
|   flush_tx_fifo(_USB_GRSTCTL_TXFNUM_FALL);  // Flush All | ||||
|   USB->GRXFSIZ = 52; | ||||
|  | ||||
|   /* Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) */ | ||||
|   USB->GNPTXFSIZ = (16 << _USB_GNPTXFSIZ_NPTXFINEPTXF0DEP_SHIFT) | (USB->GRXFSIZ & _USB_GNPTXFSIZ_NPTXFSTADDR_MASK); | ||||
|  | ||||
|   /* Ready to receive SETUP packet */ | ||||
|   USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_SUPCNT_SHIFT); | ||||
|  | ||||
|   USB->GINTMSK |= USB_GINTMSK_IEPINTMSK | USB_GINTMSK_OEPINTMSK; | ||||
| } | ||||
|  | ||||
| static void enum_done_processing(void) | ||||
| { | ||||
|   /* Maximum packet size for EP 0 is set for both directions by writing DIEPCTL */ | ||||
|   if((USB->DSTS & _USB_DSTS_ENUMSPD_MASK) == USB_DSTS_ENUMSPD_FS) | ||||
|   {  | ||||
|     /* Full Speed (PHY on 48 MHz) */ | ||||
|     USB->DOEP0CTL = (USB->DOEP0CTL & ~_USB_DOEP0CTL_MPS_MASK) | _USB_DOEP0CTL_MPS_64B; /* Maximum Packet Size 64 bytes */ | ||||
|     USB->DOEP0CTL &= ~_USB_DOEP0CTL_STALL_MASK; /* clear Stall */ | ||||
|     xfer_status[0][TUSB_DIR_OUT].max_size = 64; | ||||
|     xfer_status[0][TUSB_DIR_IN].max_size = 64; | ||||
|   } | ||||
|   else | ||||
|   {  | ||||
|     /* Low Speed (PHY on 6 MHz) */ | ||||
|     USB->DOEP0CTL = (USB->DOEP0CTL & ~_USB_DOEP0CTL_MPS_MASK) | _USB_DOEP0CTL_MPS_8B; /* Maximum Packet Size 64 bytes */ | ||||
|     USB->DOEP0CTL &= ~_USB_DOEP0CTL_STALL_MASK; /* clear Stall */ | ||||
|     xfer_status[0][TUSB_DIR_OUT].max_size = 8; | ||||
|     xfer_status[0][TUSB_DIR_IN].max_size = 8; | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| /*------------------------------------------------------------------*/ | ||||
| /* Controller API                                                   */ | ||||
| /*------------------------------------------------------------------*/ | ||||
| void dcd_init(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   /* Reset Core */ | ||||
|   USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK; | ||||
|   USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE); | ||||
|  | ||||
|   /* Core Soft Reset */ | ||||
|   USB->GRSTCTL |= USB_GRSTCTL_CSFTRST; | ||||
|   while(USB->GRSTCTL & USB_GRSTCTL_CSFTRST); | ||||
|  | ||||
|   while(!(USB->GRSTCTL & USB_GRSTCTL_AHBIDLE)); | ||||
|  | ||||
|   /* Enable PHY pins */ | ||||
|   USB->ROUTE = USB_ROUTE_PHYPEN; | ||||
|  | ||||
|   dcd_disconnect(rhport); | ||||
|  | ||||
|   /*  | ||||
|    * Set device speed (Full speed PHY) | ||||
|    * Stall on non-zero len status OUT packets (ctrl transfers) | ||||
|    * periodic frame interval to 80%  | ||||
|    */ | ||||
|   USB->DCFG = (USB->DCFG & ~(_USB_DCFG_DEVSPD_MASK | _USB_DCFG_PERFRINT_MASK)) | USB_DCFG_DEVSPD_FS | USB_DCFG_NZSTSOUTHSHK; | ||||
|    | ||||
|   /* Enable Global Interrupts */ | ||||
|   USB->GAHBCFG = (USB->GAHBCFG & ~_USB_GAHBCFG_HBSTLEN_MASK) | USB_GAHBCFG_GLBLINTRMSK; | ||||
|  | ||||
|   /* Force Device Mode */ | ||||
|   USB->GUSBCFG = (USB->GUSBCFG & ~(GUSBCFG_WO_BITMASK | USB_GUSBCFG_FORCEHSTMODE)) | USB_GUSBCFG_FORCEDEVMODE; | ||||
|  | ||||
|   /* No Overrides */ | ||||
|   USB->GOTGCTL &= ~(USB_GOTGCTL_BVALIDOVVAL | USB_GOTGCTL_BVALIDOVEN | USB_GOTGCTL_VBVALIDOVVAL); | ||||
|  | ||||
|   /* Ignore frame numbers on ISO transfers. */ | ||||
|   USB->DCTL = (USB->DCTL & ~DCTL_WO_BITMASK) | USB_DCTL_IGNRFRMNUM; | ||||
|  | ||||
|   /* Setting SNAKs */ | ||||
|   USB->DOEP0CTL |= USB_DIEP_CTL_SNAK; | ||||
|   for(uint8_t i = 0; i < EP_COUNT - 1; i++) | ||||
|   { | ||||
|     USB->DOEP[i].CTL |= USB_DIEP_CTL_SNAK; | ||||
|   } | ||||
|  | ||||
|   /* D. Interruption masking */ | ||||
|   /* Disable all device interrupts */ | ||||
|   USB->DIEPMSK  = 0; | ||||
|   USB->DOEPMSK  = 0; | ||||
|   USB->DAINTMSK = 0; | ||||
|   USB->DIEPEMPMSK = 0; | ||||
|   USB->GINTMSK = 0; | ||||
|   USB->GOTGINT = ~0U; /* clear OTG ints */ | ||||
|   USB->GINTSTS = ~0U; /* clear pending ints */ | ||||
|   USB->GINTMSK = USB_GINTMSK_MODEMISMSK  | | ||||
|               #if USE_SOF | ||||
|                  USB_GINTMSK_SOFMSK      | | ||||
|               #endif | ||||
|                  USB_GINTMSK_ERLYSUSPMSK | | ||||
|                  USB_GINTMSK_USBSUSPMSK  | | ||||
|                  USB_GINTMSK_USBRSTMSK   | | ||||
|                  USB_GINTMSK_ENUMDONEMSK | | ||||
|                  USB_GINTMSK_RESETDETMSK | | ||||
|                  USB_GINTMSK_DISCONNINTMSK; | ||||
|  | ||||
|   NVIC_ClearPendingIRQ(USB_IRQn); | ||||
|  | ||||
|   dcd_connect(rhport); | ||||
| } | ||||
|  | ||||
| void dcd_set_address(uint8_t rhport, uint8_t dev_addr) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   USB->DCFG = (USB->DCFG & ~_USB_DCFG_DEVADDR_MASK) | (dev_addr << _USB_DCFG_DEVADDR_SHIFT); | ||||
|  | ||||
|   /* Response with status after changing device address */ | ||||
|   dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); | ||||
| } | ||||
|  | ||||
| void dcd_remote_wakeup(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
| } | ||||
|  | ||||
| void dcd_connect(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   /* connect by enabling internal pull-up resistor on D+/D- */ | ||||
|   USB->DCTL &= ~(DCTL_WO_BITMASK | USB_DCTL_SFTDISCON); | ||||
| } | ||||
|  | ||||
| void dcd_disconnect(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   /* disconnect by disabling internal pull-up resistor on D+/D- */ | ||||
|   USB->DCTL = (USB->DCTL & ~(DCTL_WO_BITMASK)) | USB_DCTL_SFTDISCON; | ||||
| } | ||||
|  | ||||
| /*------------------------------------------------------------------*/ | ||||
| /* DCD Endpoint Port                                                */ | ||||
| /*------------------------------------------------------------------*/ | ||||
| void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   if(dir == TUSB_DIR_IN) | ||||
|   { | ||||
|     if(epnum == 0) | ||||
|     { | ||||
|       USB->DIEP0CTL = (USB->DIEP0CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP0CTL_SNAK | USB_DIEP0CTL_STALL; | ||||
|  | ||||
|       flush_tx_fifo(_USB_GRSTCTL_TXFNUM_F0); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       /* Only disable currently enabled non-control endpoint */ | ||||
|       if(USB->DIEP[epnum - 1].CTL & USB_DIEP_CTL_EPENA)  | ||||
|       { | ||||
|         USB->DIEP[epnum - 1].CTL = (USB->DIEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_EPDIS | USB_DIEP_CTL_SNAK | USB_DIEP_CTL_STALL; | ||||
|         while(!(USB->DIEP[epnum - 1].INT & USB_DIEP_INT_EPDISBLD)); | ||||
|         USB->DIEP[epnum - 1].INT |= USB_DIEP_INT_EPDISBLD; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         USB->DIEP[epnum - 1].CTL = (USB->DIEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_SNAK | USB_DIEP_CTL_STALL; | ||||
|       } | ||||
|  | ||||
|       /* Flush the FIFO */ | ||||
|       uint8_t const fifo_num = ((USB->DIEP[epnum - 1].CTL & _USB_DIEP_CTL_TXFNUM_MASK) >> _USB_DIEP_CTL_TXFNUM_SHIFT); | ||||
|       flush_tx_fifo(fifo_num); | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     if(epnum == 0) | ||||
|     { | ||||
|       USB->DOEP0CTL = (USB->DOEP0CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP0CTL_STALL; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       /* Only disable currently enabled non-control endpoint */ | ||||
|       if(USB->DOEP[epnum - 1].CTL & USB_DIEP_CTL_EPENA)  | ||||
|       { | ||||
|         /* Asserting GONAK is required to STALL an OUT endpoint. */ | ||||
|         USB->DCTL |= USB_DCTL_SGOUTNAK; | ||||
|         while(!(USB->GINTSTS & USB_GINTSTS_GOUTNAKEFF)); | ||||
|          | ||||
|         /* Disable the endpoint. Note that only STALL and not SNAK is set here. */ | ||||
|         USB->DOEP[epnum - 1].CTL = (USB->DOEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_EPDIS | USB_DIEP_CTL_STALL; | ||||
|         while(USB->DOEP[epnum - 1].INT & USB_DIEP_INT_EPDISBLD); | ||||
|         USB->DOEP[epnum - 1].INT |= USB_DIEP_INT_EPDISBLD; | ||||
|  | ||||
|         /* Allow other OUT endpoints to keep receiving. */ | ||||
|         USB->DCTL |= USB_DCTL_CGOUTNAK; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         USB->DIEP[epnum - 1].CTL = (USB->DIEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_STALL; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   if(dir == TUSB_DIR_IN) | ||||
|   { | ||||
|     if(epnum == 0) | ||||
|     { | ||||
|       USB->DIEP0CTL &= ~(DEPCTL_WO_BITMASK | USB_DIEP0CTL_STALL); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       USB->DIEP[epnum - 1].CTL &= ~(DEPCTL_WO_BITMASK | USB_DIEP_CTL_STALL); | ||||
|      | ||||
|       /* Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints. */ | ||||
|       uint8_t eptype = (USB->DIEP[epnum - 1].CTL & _USB_DIEP_CTL_EPTYPE_MASK) >> _USB_DIEP_CTL_EPTYPE_SHIFT; | ||||
|  | ||||
|       if((eptype == _USB_DIEP_CTL_EPTYPE_BULK) || (eptype == _USB_DIEP_CTL_EPTYPE_INT)) | ||||
|       { | ||||
|         USB->DIEP[epnum - 1].CTL |= USB_DIEP_CTL_SETD0PIDEF; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     if(epnum == 0) | ||||
|     { | ||||
|       USB->DOEP0CTL &= ~(DEPCTL_WO_BITMASK | USB_DOEP0CTL_STALL); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       USB->DOEP[epnum - 1].CTL &= ~(DEPCTL_WO_BITMASK | USB_DOEP_CTL_STALL); | ||||
|      | ||||
|       /* Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints. */ | ||||
|       uint8_t eptype = (USB->DOEP[epnum - 1].CTL & _USB_DOEP_CTL_EPTYPE_MASK) >> _USB_DOEP_CTL_EPTYPE_SHIFT; | ||||
|  | ||||
|       if((eptype == _USB_DOEP_CTL_EPTYPE_BULK) || (eptype == _USB_DOEP_CTL_EPTYPE_INT)) | ||||
|       { | ||||
|         USB->DOEP[epnum - 1].CTL |= USB_DOEP_CTL_SETD0PIDEF; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); | ||||
|   uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); | ||||
|  | ||||
|   TU_ASSERT(p_endpoint_desc->wMaxPacketSize.size <= 64); | ||||
|   TU_ASSERT(epnum < EP_COUNT); | ||||
|   TU_ASSERT(epnum != 0); | ||||
|  | ||||
|   xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); | ||||
|   xfer->max_size = p_endpoint_desc->wMaxPacketSize.size; | ||||
|  | ||||
|   if(dir == TUSB_DIR_OUT) | ||||
|   { | ||||
|     USB->DOEP[epnum - 1].CTL |= USB_DOEP_CTL_USBACTEP | | ||||
|                                 (p_endpoint_desc->bmAttributes.xfer << _USB_DOEP_CTL_EPTYPE_SHIFT) | | ||||
|                                 (p_endpoint_desc->wMaxPacketSize.size << _USB_DOEP_CTL_MPS_SHIFT); | ||||
|     USB->DAINTMSK |= (1 << (_USB_DAINTMSK_OUTEPMSK0_SHIFT + epnum)); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     uint8_t fifo_num = get_free_fifo(); | ||||
|     TU_ASSERT(fifo_num != 0); | ||||
|  | ||||
|     USB->DIEP[epnum - 1].CTL &= ~(_USB_DIEP_CTL_TXFNUM_MASK | _USB_DIEP_CTL_EPTYPE_MASK | USB_DIEP_CTL_SETD0PIDEF | _USB_DIEP_CTL_MPS_MASK); | ||||
|     USB->DIEP[epnum - 1].CTL |= USB_DIEP_CTL_USBACTEP | | ||||
|                                 (fifo_num << _USB_DIEP_CTL_TXFNUM_SHIFT) | | ||||
|                                 (p_endpoint_desc->bmAttributes.xfer << _USB_DIEP_CTL_EPTYPE_SHIFT) | | ||||
|                                 ((p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) ? USB_DIEP_CTL_SETD0PIDEF : 0) | | ||||
|                                 (p_endpoint_desc->wMaxPacketSize.size << 0); | ||||
|  | ||||
|     USB->DAINTMSK |= (1 << epnum); | ||||
|  | ||||
|     /* Both TXFD and TXSA are in unit of 32-bit words. */ | ||||
|     /* IN FIFO 0 was configured during enumeration, hence the "+ 16". */ | ||||
|     uint16_t const allocated_size = (USB->GRXFSIZ & _USB_GRXFSIZ_RXFDEP_MASK) + 16; | ||||
|     uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1); | ||||
|     uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1); | ||||
|  | ||||
|     /* DIEPTXF starts at FIFO #1. */ | ||||
|     volatile uint32_t* usb_dieptxf = &USB->DIEPTXF1; | ||||
|     usb_dieptxf[epnum - 1] = (fifo_size << _USB_DIEPTXF1_INEPNTXFDEP_SHIFT) | fifo_offset; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) | ||||
| { | ||||
|   (void)rhport; | ||||
|  | ||||
|   uint8_t const epnum = tu_edpt_number(ep_addr); | ||||
|   uint8_t const dir   = tu_edpt_dir(ep_addr); | ||||
|  | ||||
|   xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); | ||||
|   xfer->buffer       = buffer; | ||||
|   xfer->total_len    = total_bytes; | ||||
|   xfer->queued_len   = 0; | ||||
|   xfer->short_packet = false; | ||||
|  | ||||
|   uint16_t num_packets = (total_bytes / xfer->max_size); | ||||
|   uint8_t short_packet_size = total_bytes % xfer->max_size; | ||||
|  | ||||
|   // Zero-size packet is special case. | ||||
|   if(short_packet_size > 0 || (total_bytes == 0)) | ||||
|   { | ||||
|     num_packets++; | ||||
|   } | ||||
|  | ||||
|   // IN and OUT endpoint xfers are interrupt-driven, we just schedule them | ||||
|   // here. | ||||
|   if(dir == TUSB_DIR_IN) | ||||
|   { | ||||
|     if(epnum == 0) | ||||
|     { | ||||
|       // A full IN transfer (multiple packets, possibly) triggers XFRC. | ||||
|       USB->DIEP0TSIZ = (num_packets << _USB_DIEP0TSIZ_PKTCNT_SHIFT) | total_bytes; | ||||
|       USB->DIEP0CTL |= USB_DIEP0CTL_EPENA | USB_DIEP0CTL_CNAK; // Enable | CNAK | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       // A full IN transfer (multiple packets, possibly) triggers XFRC. | ||||
|       USB->DIEP[epnum - 1].TSIZ = (num_packets << _USB_DIEP_TSIZ_PKTCNT_SHIFT) | total_bytes; | ||||
|       USB->DIEP[epnum - 1].CTL |= USB_DIEP_CTL_EPENA | USB_DIEP_CTL_CNAK; // Enable | CNAK | ||||
|     } | ||||
|      | ||||
|     // Enable fifo empty interrupt only if there are something to put in the fifo. | ||||
|     if(total_bytes != 0) | ||||
|     { | ||||
|       USB->DIEPEMPMSK |= (1 << epnum); | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     if(epnum == 0) | ||||
|     { | ||||
|       // A full IN transfer (multiple packets, possibly) triggers XFRC. | ||||
|       USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP0TSIZ_XFERSIZE_MASK) << _USB_DOEP0TSIZ_XFERSIZE_SHIFT); | ||||
|       USB->DOEP0CTL |= USB_DOEP0CTL_EPENA | USB_DOEP0CTL_CNAK; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       // A full IN transfer (multiple packets, possibly) triggers XFRC. | ||||
|       USB->DOEP[epnum - 1].TSIZ |= (1 << _USB_DOEP_TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP_TSIZ_XFERSIZE_MASK) << _USB_DOEP_TSIZ_XFERSIZE_SHIFT); | ||||
|       USB->DOEP[epnum - 1].CTL |= USB_DOEP_CTL_EPENA | USB_DOEP_CTL_CNAK; | ||||
|     } | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /*------------------------------------------------------------------*/ | ||||
| /* IRQ                                                              */ | ||||
| /*------------------------------------------------------------------*/ | ||||
| void dcd_int_enable(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   NVIC_EnableIRQ(USB_IRQn); | ||||
| } | ||||
|  | ||||
| void dcd_int_disable(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   NVIC_DisableIRQ(USB_IRQn); | ||||
| } | ||||
|  | ||||
| static void receive_packet(xfer_ctl_t *xfer, uint16_t xfer_size) | ||||
| { | ||||
|   uint16_t remaining = xfer->total_len - xfer->queued_len; | ||||
|   uint16_t to_recv_size; | ||||
|  | ||||
|   if(remaining <= xfer->max_size) | ||||
|   { | ||||
|     /* Avoid buffer overflow. */ | ||||
|     to_recv_size = (xfer_size > remaining) ? remaining : xfer_size; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     /* Room for full packet, choose recv_size based on what the microcontroller claims. */ | ||||
|     to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size; | ||||
|   } | ||||
|  | ||||
|   uint8_t to_recv_rem = to_recv_size % 4; | ||||
|   uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem; | ||||
|  | ||||
|   /* Do not assume xfer buffer is aligned. */ | ||||
|   uint8_t *base = (xfer->buffer + xfer->queued_len); | ||||
|  | ||||
|   /* This for loop always runs at least once- skip if less than 4 bytes to collect. */ | ||||
|   if(to_recv_size >= 4) | ||||
|   { | ||||
|     for(uint16_t i = 0; i < to_recv_size_aligned; i += 4) | ||||
|     { | ||||
|       uint32_t tmp = (*USB->FIFO0D); | ||||
|       base[i] = tmp & 0x000000FF; | ||||
|       base[i + 1] = (tmp & 0x0000FF00) >> 8; | ||||
|       base[i + 2] = (tmp & 0x00FF0000) >> 16; | ||||
|       base[i + 3] = (tmp & 0xFF000000) >> 24; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Do not read invalid bytes from RX FIFO. */ | ||||
|   if(to_recv_rem != 0) | ||||
|   { | ||||
|     uint32_t tmp = (*USB->FIFO0D); | ||||
|     uint8_t *last_32b_bound = base + to_recv_size_aligned; | ||||
|  | ||||
|     last_32b_bound[0] = tmp & 0x000000FF; | ||||
|     if(to_recv_rem > 1) | ||||
|     { | ||||
|       last_32b_bound[1] = (tmp & 0x0000FF00) >> 8; | ||||
|     } | ||||
|     if(to_recv_rem > 2) | ||||
|     { | ||||
|       last_32b_bound[2] = (tmp & 0x00FF0000) >> 16; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   xfer->queued_len += xfer_size; | ||||
|  | ||||
|   /* Per USB spec, a short OUT packet (including length 0) is always */ | ||||
|   /* indicative of the end of a transfer (at least for ctl, bulk, int). */ | ||||
|   xfer->short_packet = (xfer_size < xfer->max_size); | ||||
| } | ||||
|  | ||||
| static void transmit_packet(xfer_ctl_t *xfer, uint8_t fifo_num) | ||||
| { | ||||
|   uint16_t remaining; | ||||
|   if(fifo_num == 0) | ||||
|   { | ||||
|     remaining = (USB->DIEP0TSIZ & 0x7FFFFU) >> _USB_DIEP0TSIZ_XFERSIZE_SHIFT; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     remaining = (USB->DIEP[fifo_num - 1].TSIZ & 0x7FFFFU) >> _USB_DIEP_TSIZ_XFERSIZE_SHIFT; | ||||
|   } | ||||
|   xfer->queued_len = xfer->total_len - remaining; | ||||
|  | ||||
|   uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining; | ||||
|   uint8_t to_xfer_rem = to_xfer_size % 4; | ||||
|   uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem; | ||||
|  | ||||
|   /* Buffer might not be aligned to 32b, so we need to force alignment by copying to a temp var. */ | ||||
|   uint8_t *base = (xfer->buffer + xfer->queued_len); | ||||
|  | ||||
|   /* This for loop always runs at least once- skip if less than 4 bytes to send off. */ | ||||
|   if(to_xfer_size >= 4) | ||||
|   { | ||||
|     for(uint16_t i = 0; i < to_xfer_size_aligned; i += 4) | ||||
|     { | ||||
|       uint32_t tmp = base[i] | (base[i + 1] << 8) | (base[i + 2] << 16) | (base[i + 3] << 24); | ||||
|       *tx_fifo[fifo_num] = tmp; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Do not read beyond end of buffer if not divisible by 4. */ | ||||
|   if(to_xfer_rem != 0) | ||||
|   { | ||||
|     uint32_t tmp = 0; | ||||
|     uint8_t *last_32b_bound = base + to_xfer_size_aligned; | ||||
|  | ||||
|     tmp |= last_32b_bound[0]; | ||||
|     if(to_xfer_rem > 1) | ||||
|     { | ||||
|       tmp |= (last_32b_bound[1] << 8); | ||||
|     } | ||||
|     if(to_xfer_rem > 2) | ||||
|     { | ||||
|       tmp |= (last_32b_bound[2] << 16); | ||||
|     } | ||||
|  | ||||
|     *tx_fifo[fifo_num] = tmp; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void read_rx_fifo(void) | ||||
| { | ||||
|   /* | ||||
|    * Pop control word off FIFO (completed xfers will have 2 control words, | ||||
|    * we only pop one ctl word each interrupt). | ||||
|    */ | ||||
|   uint32_t const ctl_word = USB->GRXSTSP; | ||||
|   uint8_t  const pktsts   = (ctl_word & _USB_GRXSTSP_PKTSTS_MASK) >> _USB_GRXSTSP_PKTSTS_SHIFT; | ||||
|   uint8_t  const epnum    = (ctl_word & _USB_GRXSTSP_CHNUM_MASK ) >> _USB_GRXSTSP_CHNUM_SHIFT; | ||||
|   uint16_t const bcnt     = (ctl_word & _USB_GRXSTSP_BCNT_MASK  ) >> _USB_GRXSTSP_BCNT_SHIFT; | ||||
|  | ||||
|   switch(pktsts) | ||||
|   { | ||||
|     case 0x01: /* Global OUT NAK (Interrupt) */ | ||||
|       break; | ||||
|  | ||||
|     case 0x02: | ||||
|     {  | ||||
|       /* Out packet recvd */ | ||||
|       xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); | ||||
|       receive_packet(xfer, bcnt); | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     case 0x03: | ||||
|       /* Out packet done (Interrupt) */ | ||||
|       break; | ||||
|  | ||||
|     case 0x04:  | ||||
|       /* Step 2: Setup transaction completed (Interrupt) */ | ||||
|       /* After this event, OEPINT interrupt will occur with SETUP bit set */ | ||||
|       if(epnum == 0) | ||||
|       { | ||||
|         USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_SUPCNT_SHIFT); | ||||
|       } | ||||
|        | ||||
|       break; | ||||
|  | ||||
|     case 0x06: | ||||
|     {  | ||||
|       /* Step1: Setup data packet received */ | ||||
|  | ||||
|       /* | ||||
|        * We can receive up to three setup packets in succession, but | ||||
|        * only the last one is valid. Therefore we just overwrite it | ||||
|        */ | ||||
|       _setup_packet[0] = (*USB->FIFO0D); | ||||
|       _setup_packet[1] = (*USB->FIFO0D); | ||||
|     } | ||||
|     break; | ||||
|  | ||||
|     default:  | ||||
|       /* Invalid, breakpoint. */ | ||||
|       TU_BREAKPOINT(); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void handle_epout_ints(void) | ||||
| { | ||||
|   // GINTSTS will be cleared with DAINT == 0 | ||||
|   // DAINT for a given EP clears when DOEPINTx is cleared. | ||||
|   // DOEPINT will be cleared when DAINT's out bits are cleared. | ||||
|  | ||||
|   for(uint8_t n = 0; n < EP_COUNT; n++) | ||||
|   { | ||||
|     xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); | ||||
|  | ||||
|     if(n == 0) | ||||
|     { | ||||
|       if(USB->DAINT & (1 << (_USB_DAINT_OUTEPINT0_SHIFT + n))) | ||||
|       { | ||||
|         // SETUP packet Setup Phase done. | ||||
|         if((USB->DOEP0INT & USB_DOEP0INT_SETUP)) | ||||
|         { | ||||
|           USB->DOEP0INT = USB_DOEP0INT_STUPPKTRCVD | USB_DOEP0INT_SETUP; // clear | ||||
|           dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true); | ||||
|         } | ||||
|  | ||||
|         // OUT XFER complete (single packet).q | ||||
|         if(USB->DOEP0INT & USB_DOEP0INT_XFERCOMPL) | ||||
|         { | ||||
|           USB->DOEP0INT = USB_DOEP0INT_XFERCOMPL; | ||||
|  | ||||
|           // Transfer complete if short packet or total len is transferred | ||||
|           if(xfer->short_packet || (xfer->queued_len == xfer->total_len)) | ||||
|           { | ||||
|             xfer->short_packet = false; | ||||
|             dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true); | ||||
|           } | ||||
|           else | ||||
|           { | ||||
|             // Schedule another packet to be received. | ||||
|             USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP0TSIZ_XFERSIZE_MASK) << _USB_DOEP0TSIZ_XFERSIZE_SHIFT); | ||||
|             USB->DOEP0CTL |= USB_DOEP0CTL_EPENA | USB_DOEP0CTL_CNAK; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       if(USB->DAINT & (1 << (_USB_DAINT_OUTEPINT0_SHIFT + n))) | ||||
|       { | ||||
|         // SETUP packet Setup Phase done. | ||||
|         if((USB->DOEP[n - 1].INT & USB_DOEP_INT_SETUP)) | ||||
|         { | ||||
|           USB->DOEP[n - 1].INT = USB_DOEP_INT_STUPPKTRCVD | USB_DOEP_INT_SETUP; // clear | ||||
|           dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true); | ||||
|         } | ||||
|  | ||||
|         // OUT XFER complete (single packet).q | ||||
|         if(USB->DOEP[n - 1].INT & USB_DOEP_INT_XFERCOMPL) | ||||
|         { | ||||
|           USB->DOEP[n - 1].INT = USB_DOEP_INT_XFERCOMPL; | ||||
|  | ||||
|           // Transfer complete if short packet or total len is transferred | ||||
|           if(xfer->short_packet || (xfer->queued_len == xfer->total_len)) | ||||
|           { | ||||
|             xfer->short_packet = false; | ||||
|             dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true); | ||||
|           } | ||||
|           else | ||||
|           { | ||||
|             // Schedule another packet to be received. | ||||
|             USB->DOEP[n - 1].TSIZ |= (1 << _USB_DOEP_TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP_TSIZ_XFERSIZE_MASK) << _USB_DOEP_TSIZ_XFERSIZE_SHIFT); | ||||
|             USB->DOEP[n - 1].CTL |= USB_DOEP_CTL_EPENA | USB_DOEP_CTL_CNAK; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void handle_epin_ints(void) | ||||
| { | ||||
|  | ||||
|   for(uint32_t n = 0; n < EP_COUNT; n++) | ||||
|   { | ||||
|     xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN]; | ||||
|  | ||||
|     if(n == 0) | ||||
|     { | ||||
|       if(USB->DAINT & (1 << n)) | ||||
|       { | ||||
|         /* IN XFER complete (entire xfer). */ | ||||
|         if(USB->DIEP0INT & USB_DIEP0INT_XFERCOMPL) | ||||
|         { | ||||
|           USB->DIEP0INT = USB_DIEP0INT_XFERCOMPL; | ||||
|           dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); | ||||
|         } | ||||
|  | ||||
|         /* XFER FIFO empty */ | ||||
|         if(USB->DIEP0INT & USB_DIEP0INT_TXFEMP) | ||||
|         { | ||||
|           USB->DIEP0INT = USB_DIEP0INT_TXFEMP; | ||||
|           transmit_packet(xfer, n); | ||||
|  | ||||
|           /* Turn off TXFE if all bytes are written. */ | ||||
|           if(xfer->queued_len == xfer->total_len) | ||||
|           { | ||||
|             USB->DIEPEMPMSK  &= ~(1 << n); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         /* XFER Timeout */ | ||||
|         if(USB->DIEP0INT & USB_DIEP0INT_TIMEOUT) | ||||
|         { | ||||
|           /* Clear interrupt or enpoint will hang. */ | ||||
|           USB->DIEP0INT = USB_DIEP0INT_TIMEOUT; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       if(USB->DAINT & (1 << n)) | ||||
|       { | ||||
|         /* IN XFER complete (entire xfer). */ | ||||
|         if(USB->DIEP[n - 1].INT & USB_DIEP_INT_XFERCOMPL) | ||||
|         { | ||||
|           USB->DIEP[n - 1].INT = USB_DIEP_INT_XFERCOMPL; | ||||
|           dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); | ||||
|         } | ||||
|  | ||||
|         /* XFER FIFO empty */ | ||||
|         if(USB->DIEP[n - 1].INT & USB_DIEP_INT_TXFEMP) | ||||
|         { | ||||
|           USB->DIEP[n - 1].INT = USB_DIEP_INT_TXFEMP; | ||||
|           transmit_packet(xfer, n); | ||||
|  | ||||
|           /* Turn off TXFE if all bytes are written. */ | ||||
|           if(xfer->queued_len == xfer->total_len) | ||||
|           { | ||||
|             USB->DIEPEMPMSK  &= ~(1 << n); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         /* XFER Timeout */ | ||||
|         if(USB->DIEP[n - 1].INT & USB_DIEP_INT_TIMEOUT) | ||||
|         { | ||||
|           /* Clear interrupt or enpoint will hang. */ | ||||
|           USB->DIEP[n - 1].INT = USB_DIEP_INT_TIMEOUT; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void dcd_int_handler(uint8_t rhport) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   const uint32_t int_status = USB->GINTSTS; | ||||
|  | ||||
|   /* USB Reset */ | ||||
|   if(int_status & USB_GINTSTS_USBRST) | ||||
|   { | ||||
|     /* start of reset */ | ||||
|     USB->GINTSTS = USB_GINTSTS_USBRST; | ||||
|     /* FIFOs will be reassigned when the endpoints are reopen */ | ||||
|     _allocated_fifos = 1; | ||||
|     bus_reset(); | ||||
|   } | ||||
|  | ||||
|   /* Reset detected Interrupt */ | ||||
|   if(int_status & USB_GINTSTS_RESETDET) | ||||
|   { | ||||
|     USB->GINTSTS = USB_GINTSTS_RESETDET; | ||||
|     bus_reset(); | ||||
|   } | ||||
|  | ||||
|   /* Enumeration Done */ | ||||
|   if(int_status & USB_GINTSTS_ENUMDONE) | ||||
|   { | ||||
|     /* This interrupt is considered the end of reset. */ | ||||
|     USB->GINTSTS = USB_GINTSTS_ENUMDONE; | ||||
|     enum_done_processing(); | ||||
|     dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); | ||||
|   } | ||||
|  | ||||
|   /* OTG Interrupt */ | ||||
|   if(int_status & USB_GINTSTS_OTGINT) | ||||
|   { | ||||
|     /* OTG INT bit is read-only */ | ||||
|  | ||||
|     uint32_t const otg_int = USB->GOTGINT; | ||||
|  | ||||
|     if(otg_int & USB_GOTGINT_SESENDDET) | ||||
|     { | ||||
|       dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); | ||||
|     } | ||||
|  | ||||
|     USB->GOTGINT = otg_int; | ||||
|   } | ||||
|  | ||||
|   #if USE_SOF | ||||
|   if(int_status & USB_GINTSTS_SOF) | ||||
|   { | ||||
|     USB->GINTSTS = USB_GINTSTS_SOF; | ||||
|     dcd_event_bus_signal(0, DCD_EVENT_SOF, true); | ||||
|   } | ||||
|   #endif | ||||
|  | ||||
|   /* RxFIFO Non-Empty */ | ||||
|   if(int_status & USB_GINTSTS_RXFLVL) | ||||
|   { | ||||
|     /* RXFLVL bit is read-only */ | ||||
|  | ||||
|     /* Mask out RXFLVL while reading data from FIFO */ | ||||
|     USB->GINTMSK &= ~USB_GINTMSK_RXFLVLMSK; | ||||
|     read_rx_fifo(); | ||||
|     USB->GINTMSK |= USB_GINTMSK_RXFLVLMSK; | ||||
|   } | ||||
|  | ||||
|   /* OUT Endpoints Interrupt */ | ||||
|   if(int_status & USB_GINTMSK_OEPINTMSK) | ||||
|   { | ||||
|     /* OEPINT is read-only */ | ||||
|     handle_epout_ints(); | ||||
|   } | ||||
|  | ||||
|   /* IN Endpoints Interrupt */ | ||||
|   if(int_status & USB_GINTMSK_IEPINTMSK) | ||||
|   { | ||||
|     /* IEPINT bit read-only */ | ||||
|     handle_epin_ints(); | ||||
|   } | ||||
|  | ||||
|   /* unhandled */ | ||||
|   USB->GINTSTS |= USB_GINTSTS_CURMOD      | | ||||
|                   USB_GINTSTS_MODEMIS     | | ||||
|                   USB_GINTSTS_OTGINT      | | ||||
|                   USB_GINTSTS_NPTXFEMP    | | ||||
|                   USB_GINTSTS_GINNAKEFF   | | ||||
|                   USB_GINTSTS_GOUTNAKEFF  | | ||||
|                   USB_GINTSTS_ERLYSUSP    | | ||||
|                   USB_GINTSTS_USBSUSP     | | ||||
|                   USB_GINTSTS_ISOOUTDROP  | | ||||
|                   USB_GINTSTS_EOPF        | | ||||
|                   USB_GINTSTS_EPMIS       | | ||||
|                   USB_GINTSTS_INCOMPISOIN | | ||||
|                   USB_GINTSTS_INCOMPLP    | | ||||
|                   USB_GINTSTS_FETSUSP     | | ||||
|                   USB_GINTSTS_PTXFEMP; | ||||
| } | ||||
|  | ||||
| #endif | ||||
| @@ -28,7 +28,7 @@ | ||||
| #define _TUSB_OPTION_H_ | ||||
|  | ||||
| #define TUSB_VERSION_MAJOR     0 | ||||
| #define TUSB_VERSION_MINOR     8 | ||||
| #define TUSB_VERSION_MINOR     9 | ||||
| #define TUSB_VERSION_REVISION  0 | ||||
| #define TUSB_VERSION_STRING    TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION) | ||||
|  | ||||
| @@ -103,6 +103,14 @@ | ||||
| // NXP Kinetis | ||||
| #define OPT_MCU_MKL25ZXX         1200 ///< NXP MKL25Zxx | ||||
|  | ||||
| // Silabs | ||||
| #define OPT_MCU_EFM32GG          1300 ///< Silabs EFM32GG | ||||
| #define OPT_MCU_EFM32GG11        1301 ///< Silabs EFM32GG11 | ||||
| #define OPT_MCU_EFM32GG12        1302 ///< Silabs EFM32GG12 | ||||
|  | ||||
| // Renesas RX | ||||
| #define OPT_MCU_RX63X            1400 ///< Renesas RX63N/631 | ||||
|  | ||||
| /** @} */ | ||||
|  | ||||
| /** \defgroup group_supported_os Supported RTOS | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Reinhard Panhuber
					Reinhard Panhuber