Browse Source

first skeleton of firmware

Thomas Buck 2 years ago
parent
commit
47b93052e5

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+build

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
1
+[submodule "firmware/pico-sdk"]
2
+	path = firmware/pico-sdk
3
+	url = https://github.com/raspberrypi/pico-sdk

+ 9
- 0
README.md View File

@@ -0,0 +1,9 @@
1
+# Trackball
2
+
3
+Custom 3D-printed PC trackball.
4
+
5
+See the `hardware` directory for OpenSCAD design files.
6
+
7
+The `firmware` directory contains the required Raspberry Pi Pico source code.
8
+
9
+Further directions can be found in the READMEs in these directories.

+ 34
- 0
firmware/CMakeLists.txt View File

@@ -0,0 +1,34 @@
1
+cmake_minimum_required(VERSION 3.13)
2
+
3
+# initialize pico-sdk from submodule
4
+# note: this must happen before project()
5
+include(pico-sdk/pico_sdk_init.cmake)
6
+
7
+project(trackball)
8
+
9
+# initialize the Raspberry Pi Pico SDK
10
+pico_sdk_init()
11
+
12
+add_executable(trackball)
13
+
14
+target_sources(trackball PUBLIC
15
+    src/main.c
16
+    src/usb.c
17
+    src/usb_descriptors.c
18
+)
19
+
20
+# Make sure TinyUSB can find tusb_config.h
21
+target_include_directories(trackball PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
22
+
23
+# pull in common dependencies
24
+target_link_libraries(trackball
25
+    pico_stdlib
26
+    tinyusb_device
27
+    tinyusb_board
28
+)
29
+
30
+# fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
31
+target_compile_definitions(trackball PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)
32
+
33
+# create map/bin/hex/uf2 file etc.
34
+pico_add_extra_outputs(trackball)

+ 24
- 0
firmware/README.md View File

@@ -0,0 +1,24 @@
1
+# RP2040 Trackball Firmware
2
+
3
+For use with Raspberry Pi Pico boards.
4
+
5
+Adapted from the `dev_hid_composite` pico-example [from GitHub](https://github.com/raspberrypi/pico-examples/tree/master/usb/device/dev_hid_composite).
6
+
7
+Supports PMW3360 optical mouse sensors.
8
+
9
+## Quick Start
10
+
11
+When compiling for the first time, check out the required git submodules.
12
+
13
+    git submodule update --init
14
+    cd pico-sdk
15
+    git submodule update --init
16
+
17
+Then do this to build.
18
+
19
+    mkdir build
20
+    cd build
21
+    cmake ..
22
+    make trackball
23
+
24
+And flash the resulting `trackball.uf2` file to your Pico.

+ 118
- 0
firmware/include/tusb_config.h View File

@@ -0,0 +1,118 @@
1
+/*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ *
24
+ */
25
+
26
+#ifndef _TUSB_CONFIG_H_
27
+#define _TUSB_CONFIG_H_
28
+
29
+#ifdef __cplusplus
30
+ extern "C" {
31
+#endif
32
+
33
+//--------------------------------------------------------------------
34
+// COMMON CONFIGURATION
35
+//--------------------------------------------------------------------
36
+
37
+// defined by board.mk
38
+#ifndef CFG_TUSB_MCU
39
+  #error CFG_TUSB_MCU must be defined
40
+#endif
41
+
42
+// RHPort number used for device can be defined by board.mk, default to port 0
43
+#ifndef BOARD_DEVICE_RHPORT_NUM
44
+  #define BOARD_DEVICE_RHPORT_NUM     0
45
+#endif
46
+
47
+// RHPort max operational speed can defined by board.mk
48
+// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
49
+#ifndef BOARD_DEVICE_RHPORT_SPEED
50
+  #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
51
+       CFG_TUSB_MCU == OPT_MCU_NUC505  || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
52
+    #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_HIGH_SPEED
53
+  #else
54
+    #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_FULL_SPEED
55
+  #endif
56
+#endif
57
+
58
+// Device mode with rhport and speed defined by board.mk
59
+#if   BOARD_DEVICE_RHPORT_NUM == 0
60
+  #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
61
+#elif BOARD_DEVICE_RHPORT_NUM == 1
62
+  #define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
63
+#else
64
+  #error "Incorrect RHPort configuration"
65
+#endif
66
+
67
+#ifndef CFG_TUSB_OS
68
+#define CFG_TUSB_OS               OPT_OS_NONE
69
+#endif
70
+
71
+// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
72
+// #define CFG_TUSB_DEBUG           0
73
+
74
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
75
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
76
+ * into those specific section.
77
+ * e.g
78
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
79
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
80
+ */
81
+#ifndef CFG_TUSB_MEM_SECTION
82
+#define CFG_TUSB_MEM_SECTION
83
+#endif
84
+
85
+#ifndef CFG_TUSB_MEM_ALIGN
86
+#define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4)))
87
+#endif
88
+
89
+//--------------------------------------------------------------------
90
+// DEVICE CONFIGURATION
91
+//--------------------------------------------------------------------
92
+
93
+#ifndef CFG_TUD_ENDPOINT0_SIZE
94
+#define CFG_TUD_ENDPOINT0_SIZE    64
95
+#endif
96
+
97
+//------------- CLASS -------------//
98
+#define CFG_TUD_HID               1
99
+#define CFG_TUD_CDC               1
100
+#define CFG_TUD_MSC               0
101
+#define CFG_TUD_MIDI              0
102
+#define CFG_TUD_VENDOR            0
103
+
104
+// HID buffer size Should be sufficient to hold ID (if any) + Data
105
+#define CFG_TUD_HID_EP_BUFSIZE    16
106
+
107
+// CDC FIFO size of TX and RX
108
+#define CFG_TUD_CDC_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
109
+#define CFG_TUD_CDC_TX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
110
+
111
+// CDC Endpoint transfer buffer size, more is faster
112
+#define CFG_TUD_CDC_EP_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
113
+
114
+#ifdef __cplusplus
115
+ }
116
+#endif
117
+
118
+#endif /* _TUSB_CONFIG_H_ */

+ 37
- 0
firmware/include/usb_descriptors.h View File

@@ -0,0 +1,37 @@
1
+/* 
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+
25
+#ifndef USB_DESCRIPTORS_H_
26
+#define USB_DESCRIPTORS_H_
27
+
28
+enum
29
+{
30
+  REPORT_ID_KEYBOARD = 1,
31
+  REPORT_ID_MOUSE,
32
+  REPORT_ID_CONSUMER_CONTROL,
33
+  REPORT_ID_GAMEPAD,
34
+  REPORT_ID_COUNT
35
+};
36
+
37
+#endif /* USB_DESCRIPTORS_H_ */

+ 1
- 0
firmware/pico-sdk

@@ -0,0 +1 @@
1
+Subproject commit 2e6142b15b8a75c1227dd3edbe839193b2bf9041

+ 9
- 0
firmware/src/main.c View File

@@ -0,0 +1,9 @@
1
+/**
2
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3
+ *
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+
7
+#include "pico/stdlib.h"
8
+
9
+

+ 302
- 0
firmware/src/usb.c View File

@@ -0,0 +1,302 @@
1
+/* 
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ *
24
+ */
25
+
26
+#include <stdlib.h>
27
+#include <stdio.h>
28
+#include <string.h>
29
+
30
+#include "bsp/board.h"
31
+#include "tusb.h"
32
+
33
+#include "usb_descriptors.h"
34
+
35
+//--------------------------------------------------------------------+
36
+// MACRO CONSTANT TYPEDEF PROTYPES
37
+//--------------------------------------------------------------------+
38
+
39
+/* Blink pattern
40
+ * - 250 ms  : device not mounted
41
+ * - 1000 ms : device mounted
42
+ * - 2500 ms : device is suspended
43
+ */
44
+enum  {
45
+  BLINK_NOT_MOUNTED = 250,
46
+  BLINK_MOUNTED = 1000,
47
+  BLINK_SUSPENDED = 2500,
48
+};
49
+
50
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
51
+
52
+void led_blinking_task(void);
53
+void hid_task(void);
54
+
55
+/*------------- MAIN -------------*/
56
+int main(void)
57
+{
58
+  board_init();
59
+  tusb_init();
60
+
61
+  while (1)
62
+  {
63
+    tud_task(); // tinyusb device task
64
+    led_blinking_task();
65
+
66
+    hid_task();
67
+  }
68
+
69
+  return 0;
70
+}
71
+
72
+//--------------------------------------------------------------------+
73
+// Device callbacks
74
+//--------------------------------------------------------------------+
75
+
76
+// Invoked when device is mounted
77
+void tud_mount_cb(void)
78
+{
79
+  blink_interval_ms = BLINK_MOUNTED;
80
+}
81
+
82
+// Invoked when device is unmounted
83
+void tud_umount_cb(void)
84
+{
85
+  blink_interval_ms = BLINK_NOT_MOUNTED;
86
+}
87
+
88
+// Invoked when usb bus is suspended
89
+// remote_wakeup_en : if host allow us  to perform remote wakeup
90
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
91
+void tud_suspend_cb(bool remote_wakeup_en)
92
+{
93
+  (void) remote_wakeup_en;
94
+  blink_interval_ms = BLINK_SUSPENDED;
95
+}
96
+
97
+// Invoked when usb bus is resumed
98
+void tud_resume_cb(void)
99
+{
100
+  blink_interval_ms = BLINK_MOUNTED;
101
+}
102
+
103
+//--------------------------------------------------------------------+
104
+// USB HID
105
+//--------------------------------------------------------------------+
106
+
107
+static void send_hid_report(uint8_t report_id, uint32_t btn)
108
+{
109
+  // skip if hid is not ready yet
110
+  if ( !tud_hid_ready() ) return;
111
+
112
+  switch(report_id)
113
+  {
114
+    case REPORT_ID_KEYBOARD:
115
+    {
116
+      // use to avoid send multiple consecutive zero report for keyboard
117
+      static bool has_keyboard_key = false;
118
+
119
+      if ( btn )
120
+      {
121
+        uint8_t keycode[6] = { 0 };
122
+        keycode[0] = HID_KEY_A;
123
+
124
+        tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
125
+        has_keyboard_key = true;
126
+      }else
127
+      {
128
+        // send empty key report if previously has key pressed
129
+        if (has_keyboard_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
130
+        has_keyboard_key = false;
131
+      }
132
+    }
133
+    break;
134
+
135
+    case REPORT_ID_MOUSE:
136
+    {
137
+      int8_t const delta = 5;
138
+
139
+      // no button, right + down, no scroll, no pan
140
+      tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
141
+    }
142
+    break;
143
+
144
+    case REPORT_ID_CONSUMER_CONTROL:
145
+    {
146
+      // use to avoid send multiple consecutive zero report
147
+      static bool has_consumer_key = false;
148
+
149
+      if ( btn )
150
+      {
151
+        // volume down
152
+        uint16_t volume_down = HID_USAGE_CONSUMER_VOLUME_DECREMENT;
153
+        tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &volume_down, 2);
154
+        has_consumer_key = true;
155
+      }else
156
+      {
157
+        // send empty key report (release key) if previously has key pressed
158
+        uint16_t empty_key = 0;
159
+        if (has_consumer_key) tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &empty_key, 2);
160
+        has_consumer_key = false;
161
+      }
162
+    }
163
+    break;
164
+
165
+    case REPORT_ID_GAMEPAD:
166
+    {
167
+      // use to avoid send multiple consecutive zero report for keyboard
168
+      static bool has_gamepad_key = false;
169
+
170
+      hid_gamepad_report_t report =
171
+      {
172
+        .x   = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,
173
+        .hat = 0, .buttons = 0
174
+      };
175
+
176
+      if ( btn )
177
+      {
178
+        report.hat = GAMEPAD_HAT_UP;
179
+        report.buttons = GAMEPAD_BUTTON_A;
180
+        tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
181
+
182
+        has_gamepad_key = true;
183
+      }else
184
+      {
185
+        report.hat = GAMEPAD_HAT_CENTERED;
186
+        report.buttons = 0;
187
+        if (has_gamepad_key) tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
188
+        has_gamepad_key = false;
189
+      }
190
+    }
191
+    break;
192
+
193
+    default: break;
194
+  }
195
+}
196
+
197
+// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
198
+// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
199
+void hid_task(void)
200
+{
201
+  // Poll every 10ms
202
+  const uint32_t interval_ms = 10;
203
+  static uint32_t start_ms = 0;
204
+
205
+  if ( board_millis() - start_ms < interval_ms) return; // not enough time
206
+  start_ms += interval_ms;
207
+
208
+  uint32_t const btn = board_button_read();
209
+
210
+  // Remote wakeup
211
+  if ( tud_suspended() && btn )
212
+  {
213
+    // Wake up host if we are in suspend mode
214
+    // and REMOTE_WAKEUP feature is enabled by host
215
+    tud_remote_wakeup();
216
+  }else
217
+  {
218
+    // Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
219
+    send_hid_report(REPORT_ID_KEYBOARD, btn);
220
+  }
221
+}
222
+
223
+// Invoked when sent REPORT successfully to host
224
+// Application can use this to send the next report
225
+// Note: For composite reports, report[0] is report ID
226
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len)
227
+{
228
+  (void) instance;
229
+  (void) len;
230
+
231
+  uint8_t next_report_id = report[0] + 1;
232
+
233
+  if (next_report_id < REPORT_ID_COUNT)
234
+  {
235
+    send_hid_report(next_report_id, board_button_read());
236
+  }
237
+}
238
+
239
+// Invoked when received GET_REPORT control request
240
+// Application must fill buffer report's content and return its length.
241
+// Return zero will cause the stack to STALL request
242
+uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
243
+{
244
+  // TODO not Implemented
245
+  (void) instance;
246
+  (void) report_id;
247
+  (void) report_type;
248
+  (void) buffer;
249
+  (void) reqlen;
250
+
251
+  return 0;
252
+}
253
+
254
+// Invoked when received SET_REPORT control request or
255
+// received data on OUT endpoint ( Report ID = 0, Type = 0 )
256
+void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
257
+{
258
+  (void) instance;
259
+
260
+  if (report_type == HID_REPORT_TYPE_OUTPUT)
261
+  {
262
+    // Set keyboard LED e.g Capslock, Numlock etc...
263
+    if (report_id == REPORT_ID_KEYBOARD)
264
+    {
265
+      // bufsize should be (at least) 1
266
+      if ( bufsize < 1 ) return;
267
+
268
+      uint8_t const kbd_leds = buffer[0];
269
+
270
+      if (kbd_leds & KEYBOARD_LED_CAPSLOCK)
271
+      {
272
+        // Capslock On: disable blink, turn led on
273
+        blink_interval_ms = 0;
274
+        board_led_write(true);
275
+      }else
276
+      {
277
+        // Caplocks Off: back to normal blink
278
+        board_led_write(false);
279
+        blink_interval_ms = BLINK_MOUNTED;
280
+      }
281
+    }
282
+  }
283
+}
284
+
285
+//--------------------------------------------------------------------+
286
+// BLINKING TASK
287
+//--------------------------------------------------------------------+
288
+void led_blinking_task(void)
289
+{
290
+  static uint32_t start_ms = 0;
291
+  static bool led_state = false;
292
+
293
+  // blink is disabled
294
+  if (!blink_interval_ms) return;
295
+
296
+  // Blink every interval ms
297
+  if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
298
+  start_ms += blink_interval_ms;
299
+
300
+  board_led_write(led_state);
301
+  led_state = 1 - led_state; // toggle
302
+}

+ 240
- 0
firmware/src/usb_descriptors.c View File

@@ -0,0 +1,240 @@
1
+/* 
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ *
24
+ */
25
+
26
+#include "tusb.h"
27
+#include "usb_descriptors.h"
28
+
29
+/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
30
+ * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
31
+ *
32
+ * Auto ProductID layout's Bitmap:
33
+ *   [MSB]         HID | MSC | CDC          [LSB]
34
+ */
35
+#define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) )
36
+#define USB_PID           (0x4200 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
37
+                           _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
38
+
39
+#define USB_VID   0xCafe
40
+#define USB_BCD   0x0200
41
+
42
+//--------------------------------------------------------------------+
43
+// Device Descriptors
44
+//--------------------------------------------------------------------+
45
+tusb_desc_device_t const desc_device =
46
+{
47
+    .bLength            = sizeof(tusb_desc_device_t),
48
+    .bDescriptorType    = TUSB_DESC_DEVICE,
49
+    .bcdUSB             = USB_BCD,
50
+
51
+    // Use Interface Association Descriptor (IAD) for CDC
52
+    // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
53
+    .bDeviceClass       = TUSB_CLASS_MISC,
54
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
55
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
56
+
57
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
58
+
59
+    .idVendor           = USB_VID,
60
+    .idProduct          = USB_PID,
61
+    .bcdDevice          = 0x0100,
62
+
63
+    .iManufacturer      = 0x01,
64
+    .iProduct           = 0x02,
65
+    .iSerialNumber      = 0x03,
66
+
67
+    .bNumConfigurations = 0x01
68
+};
69
+
70
+// Invoked when received GET DEVICE DESCRIPTOR
71
+// Application return pointer to descriptor
72
+uint8_t const * tud_descriptor_device_cb(void)
73
+{
74
+  return (uint8_t const *) &desc_device;
75
+}
76
+
77
+//--------------------------------------------------------------------+
78
+// HID Report Descriptor
79
+//--------------------------------------------------------------------+
80
+
81
+uint8_t const desc_hid_report[] =
82
+{
83
+  TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD         )),
84
+  TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE            )),
85
+  TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(REPORT_ID_CONSUMER_CONTROL )),
86
+  TUD_HID_REPORT_DESC_GAMEPAD ( HID_REPORT_ID(REPORT_ID_GAMEPAD          ))
87
+};
88
+
89
+// Invoked when received GET HID REPORT DESCRIPTOR
90
+// Application return pointer to descriptor
91
+// Descriptor contents must exist long enough for transfer to complete
92
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance)
93
+{
94
+  (void) instance;
95
+  return desc_hid_report;
96
+}
97
+
98
+//--------------------------------------------------------------------+
99
+// Configuration Descriptor
100
+//--------------------------------------------------------------------+
101
+
102
+enum
103
+{
104
+  ITF_NUM_CDC = 0,
105
+  ITF_NUM_CDC_DATA,
106
+  ITF_NUM_HID,
107
+  ITF_NUM_TOTAL
108
+};
109
+
110
+#define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN)
111
+
112
+#define EPNUM_HID       0x81
113
+#define EPNUM_CDC_NOTIF 0x82
114
+#define EPNUM_CDC_OUT   0x02
115
+#define EPNUM_CDC_IN    0x83
116
+
117
+uint8_t const desc_configuration[] =
118
+{
119
+  // Config number, interface count, string index, total length, attribute, power in mA
120
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
121
+
122
+  // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
123
+  TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
124
+
125
+  // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
126
+  TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
127
+};
128
+
129
+#if TUD_OPT_HIGH_SPEED
130
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
131
+
132
+// other speed configuration
133
+uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
134
+
135
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
136
+tusb_desc_device_qualifier_t const desc_device_qualifier =
137
+{
138
+  .bLength            = sizeof(tusb_desc_device_qualifier_t),
139
+  .bDescriptorType    = TUSB_DESC_DEVICE_QUALIFIER,
140
+  .bcdUSB             = USB_BCD,
141
+
142
+  .bDeviceClass       = TUSB_CLASS_MISC,
143
+  .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
144
+  .bDeviceProtocol    = MISC_PROTOCOL_IAD,
145
+
146
+  .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
147
+  .bNumConfigurations = 0x01,
148
+  .bReserved          = 0x00
149
+};
150
+
151
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
152
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
153
+// device_qualifier descriptor describes information about a high-speed capable device that would
154
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
155
+uint8_t const* tud_descriptor_device_qualifier_cb(void)
156
+{
157
+  return (uint8_t const*) &desc_device_qualifier;
158
+}
159
+
160
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
161
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
162
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
163
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
164
+{
165
+  (void) index; // for multiple configurations
166
+
167
+  // other speed config is basically configuration with type = OHER_SPEED_CONFIG
168
+  memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN);
169
+  desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
170
+
171
+  // this example use the same configuration for both high and full speed mode
172
+  return desc_other_speed_config;
173
+}
174
+
175
+#endif // highspeed
176
+
177
+// Invoked when received GET CONFIGURATION DESCRIPTOR
178
+// Application return pointer to descriptor
179
+// Descriptor contents must exist long enough for transfer to complete
180
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
181
+{
182
+  (void) index; // for multiple configurations
183
+
184
+  // This example use the same configuration for both high and full speed mode
185
+  return desc_configuration;
186
+}
187
+
188
+//--------------------------------------------------------------------+
189
+// String Descriptors
190
+//--------------------------------------------------------------------+
191
+
192
+// array of pointer to string descriptors
193
+char const* string_desc_arr [] =
194
+{
195
+  (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
196
+  "xythobuz",                    // 1: Manufacturer
197
+  "Trackball",                   // 2: Product
198
+  "123456",                      // 3: Serials, should use chip ID
199
+  "Debug Serial",                // 4: CDC Interface
200
+};
201
+
202
+static uint16_t _desc_str[32];
203
+
204
+// Invoked when received GET STRING DESCRIPTOR request
205
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
206
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
207
+{
208
+  (void) langid;
209
+
210
+  uint8_t chr_count;
211
+
212
+  if ( index == 0)
213
+  {
214
+    memcpy(&_desc_str[1], string_desc_arr[0], 2);
215
+    chr_count = 1;
216
+  }else
217
+  {
218
+    // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
219
+    // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
220
+
221
+    if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
222
+
223
+    const char* str = string_desc_arr[index];
224
+
225
+    // Cap at max char
226
+    chr_count = strlen(str);
227
+    if ( chr_count > 31 ) chr_count = 31;
228
+
229
+    // Convert ASCII string into UTF-16
230
+    for(uint8_t i=0; i<chr_count; i++)
231
+    {
232
+      _desc_str[1+i] = str[i];
233
+    }
234
+  }
235
+
236
+  // first byte is length (including header), second byte is string type
237
+  _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
238
+
239
+  return _desc_str;
240
+}

Loading…
Cancel
Save