blob: fd25e620a7807faae931b33a886a2a74820b02ac [file] [log] [blame]
Austin Schuh41baf202022-01-01 14:33:40 -08001/*
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 */
44enum {
45 BLINK_NOT_MOUNTED = 250,
46 BLINK_MOUNTED = 1000,
47 BLINK_SUSPENDED = 2500,
48};
49
50static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
51
52void led_blinking_task(void);
53void hid_task(void);
54
55/*------------- MAIN -------------*/
56int 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
77void tud_mount_cb(void)
78{
79 blink_interval_ms = BLINK_MOUNTED;
80}
81
82// Invoked when device is unmounted
83void 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
91void 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
98void tud_resume_cb(void)
99{
100 blink_interval_ms = BLINK_MOUNTED;
101}
102
103//--------------------------------------------------------------------+
104// USB HID
105//--------------------------------------------------------------------+
106
107static 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
199void 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
226void 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
242uint16_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 )
256void 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//--------------------------------------------------------------------+
288void 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}