Corrected CDC class bootloader to fix a few bugs, changed address counter to store...
[pub/USBasp.git] / Demos / Keyboard / Keyboard.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2009.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.fourwalledcubicle.com
7 */
8
9 /*
10 Copyright 2009 Denver Gingerich (denver [at] ossguy [dot] com)
11 Based on code by Dean Camera (dean [at] fourwalledcubicle [dot] com)
12
13 Permission to use, copy, modify, and distribute this software
14 and its documentation for any purpose and without fee is hereby
15 granted, provided that the above copyright notice appear in all
16 copies and that both that the copyright notice and this
17 permission notice and warranty disclaimer appear in supporting
18 documentation, and that the name of the author not be used in
19 advertising or publicity pertaining to distribution of the
20 software without specific, written prior permission.
21
22 The author disclaim all warranties with regard to this
23 software, including all implied warranties of merchantability
24 and fitness. In no event shall the author be liable for any
25 special, indirect or consequential damages or any damages
26 whatsoever resulting from loss of use, data or profits, whether
27 in an action of contract, negligence or other tortious action,
28 arising out of or in connection with the use or performance of
29 this software.
30 */
31
32 /** \file
33 *
34 * Main source file for the Keyboard demo. This file contains the main tasks of the demo and
35 * is responsible for the initial application hardware configuration.
36 */
37
38 #include "Keyboard.h"
39
40 /* Project Tags, for reading out using the ButtLoad project */
41 BUTTLOADTAG(ProjName, "LUFA Keyboard App");
42 BUTTLOADTAG(BuildTime, __TIME__);
43 BUTTLOADTAG(BuildDate, __DATE__);
44 BUTTLOADTAG(LUFAVersion, "LUFA V" LUFA_VERSION_STRING);
45
46 /* Scheduler Task List */
47 TASK_LIST
48 {
49 { Task: USB_USBTask , TaskStatus: TASK_STOP },
50 { Task: USB_Keyboard_Report , TaskStatus: TASK_STOP },
51 };
52
53 /* Global Variables */
54 /** Indicates what report mode the host has requested, true for normal HID reporting mode, false for special boot
55 * protocol reporting mode.
56 */
57 bool UsingReportProtocol = true;
58
59 /** Current Idle period. This is set by the host via a Set Idle HID class request to silence the device's reports
60 * for either the entire idle duration, or until the report status changes (e.g. the user moves the mouse).
61 */
62 uint8_t IdleCount = 0;
63
64 /** Current Idle period remaining. When the IdleCount value is set, this tracks the remaining number of idle
65 * milliseconds. This is seperate to the IdleCount timer and is incremented and compared as the host may request
66 * the current idle period via a Get Idle HID class request, thus its value must be preserved.
67 */
68 uint16_t IdleMSRemaining = 0;
69
70
71 /** Main program entry point. This routine configures the hardware required by the application, then
72 * starts the scheduler to run the application tasks.
73 */
74 int main(void)
75 {
76 /* Disable watchdog if enabled by bootloader/fuses */
77 MCUSR &= ~(1 << WDRF);
78 wdt_disable();
79
80 /* Disable clock division */
81 clock_prescale_set(clock_div_1);
82
83 /* Hardware Initialization */
84 Joystick_Init();
85 LEDs_Init();
86
87 /* Millisecond timer initialization, with output compare interrupt enabled for the idle timing */
88 OCR0A = 0x7D;
89 TCCR0A = (1 << WGM01);
90 TCCR0B = ((1 << CS01) | (1 << CS00));
91 TIMSK0 = (1 << OCIE0A);
92
93 /* Indicate USB not ready */
94 UpdateStatus(Status_USBNotReady);
95
96 /* Initialize Scheduler so that it can be used */
97 Scheduler_Init();
98
99 /* Initialize USB Subsystem */
100 USB_Init();
101
102 /* Scheduling - routine never returns, so put this last in the main function */
103 Scheduler_Start();
104 }
105
106 /** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and
107 * starts the library USB task to begin the enumeration and USB management process.
108 */
109 EVENT_HANDLER(USB_Connect)
110 {
111 /* Start USB management task */
112 Scheduler_SetTaskMode(USB_USBTask, TASK_RUN);
113
114 /* Indicate USB enumerating */
115 UpdateStatus(Status_USBEnumerating);
116 }
117
118 /** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via
119 * the status LEDs and stops the USB management and Keyboard reporting tasks.
120 */
121 EVENT_HANDLER(USB_Disconnect)
122 {
123 /* Stop running keyboard reporting and USB management tasks */
124 Scheduler_SetTaskMode(USB_Keyboard_Report, TASK_STOP);
125 Scheduler_SetTaskMode(USB_USBTask, TASK_STOP);
126
127 /* Indicate USB not ready */
128 UpdateStatus(Status_USBNotReady);
129 }
130
131 /** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration
132 * of the USB device after enumeration - the device endpoints are configured and the keyboard reporting task started.
133 */
134 EVENT_HANDLER(USB_ConfigurationChanged)
135 {
136 /* Setup Keyboard Keycode Report Endpoint */
137 Endpoint_ConfigureEndpoint(KEYBOARD_EPNUM, EP_TYPE_INTERRUPT,
138 ENDPOINT_DIR_IN, KEYBOARD_EPSIZE,
139 ENDPOINT_BANK_SINGLE);
140
141 /* Setup Keyboard LED Report Endpoint */
142 Endpoint_ConfigureEndpoint(KEYBOARD_LEDS_EPNUM, EP_TYPE_INTERRUPT,
143 ENDPOINT_DIR_OUT, KEYBOARD_EPSIZE,
144 ENDPOINT_BANK_SINGLE);
145
146 /* Indicate USB connected and ready */
147 UpdateStatus(Status_USBReady);
148
149 /* Default to report protocol on connect */
150 UsingReportProtocol = true;
151
152 /* Start Keyboard reporting task */
153 Scheduler_SetTaskMode(USB_Keyboard_Report, TASK_RUN);
154 }
155
156 /** Event handler for the USB_UnhandledControlPacket event. This is used to catch standard and class specific
157 * control requests that are not handled internally by the USB library (including the HID commands, which are
158 * all issued via the control endpoint), so that they can be handled appropriately for the application.
159 */
160 EVENT_HANDLER(USB_UnhandledControlPacket)
161 {
162 /* Handle HID Class specific requests */
163 switch (bRequest)
164 {
165 case REQ_GetReport:
166 if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
167 {
168 USB_KeyboardReport_Data_t KeyboardReportData;
169
170 /* Create the next keyboard report for transmission to the host */
171 GetNextReport(&KeyboardReportData);
172
173 /* Ignore report type and ID number value */
174 Endpoint_Discard_Word();
175
176 /* Ignore unused Interface number value */
177 Endpoint_Discard_Word();
178
179 /* Read in the number of bytes in the report to send to the host */
180 uint16_t wLength = Endpoint_Read_Word_LE();
181
182 /* If trying to send more bytes than exist to the host, clamp the value at the report size */
183 if (wLength > sizeof(KeyboardReportData))
184 wLength = sizeof(KeyboardReportData);
185
186 Endpoint_ClearSetupReceived();
187
188 /* Write the report data to the control endpoint */
189 Endpoint_Write_Control_Stream_LE(&KeyboardReportData, wLength);
190
191 /* Finalize the stream transfer to send the last packet or clear the host abort */
192 Endpoint_ClearSetupOUT();
193 }
194
195 break;
196 case REQ_SetReport:
197 if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
198 {
199 Endpoint_ClearSetupReceived();
200
201 /* Wait until the LED report has been sent by the host */
202 while (!(Endpoint_IsSetupOUTReceived()));
203
204 /* Read in the LED report from the host */
205 uint8_t LEDStatus = Endpoint_Read_Byte();
206
207 /* Process the incomming LED report */
208 ProcessLEDReport(LEDStatus);
209
210 /* Clear the endpoint data */
211 Endpoint_ClearSetupOUT();
212
213 /* Acknowledge status stage */
214 while (!(Endpoint_IsSetupINReady()));
215 Endpoint_ClearSetupIN();
216 }
217
218 break;
219 case REQ_GetProtocol:
220 if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
221 {
222 Endpoint_ClearSetupReceived();
223
224 /* Write the current protocol flag to the host */
225 Endpoint_Write_Byte(UsingReportProtocol);
226
227 /* Send the flag to the host */
228 Endpoint_ClearSetupIN();
229
230 /* Acknowledge status stage */
231 while (!(Endpoint_IsSetupOUTReceived()));
232 Endpoint_ClearSetupOUT();
233 }
234
235 break;
236 case REQ_SetProtocol:
237 if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
238 {
239 /* Read in the wValue parameter containing the new protocol mode */
240 uint16_t wValue = Endpoint_Read_Word_LE();
241
242 Endpoint_ClearSetupReceived();
243
244 /* Set or clear the flag depending on what the host indicates that the current Protocol should be */
245 UsingReportProtocol = (wValue != 0x0000);
246
247 /* Acknowledge status stage */
248 while (!(Endpoint_IsSetupINReady()));
249 Endpoint_ClearSetupIN();
250 }
251
252 break;
253 case REQ_SetIdle:
254 if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
255 {
256 /* Read in the wValue parameter containing the idle period */
257 uint16_t wValue = Endpoint_Read_Word_LE();
258
259 Endpoint_ClearSetupReceived();
260
261 /* Get idle period in MSB */
262 IdleCount = (wValue >> 8);
263
264 /* Acknowledge status stage */
265 while (!(Endpoint_IsSetupINReady()));
266 Endpoint_ClearSetupIN();
267 }
268
269 break;
270 case REQ_GetIdle:
271 if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
272 {
273 Endpoint_ClearSetupReceived();
274
275 /* Write the current idle duration to the host */
276 Endpoint_Write_Byte(IdleCount);
277
278 /* Send the flag to the host */
279 Endpoint_ClearSetupIN();
280
281 /* Acknowledge status stage */
282 while (!(Endpoint_IsSetupOUTReceived()));
283 Endpoint_ClearSetupOUT();
284 }
285
286 break;
287 }
288 }
289
290 /** ISR for the timer 0 compare vector. This ISR fires once each millisecond, and increments the
291 * scheduler elapsed idle period counter when the host has set an idle period.
292 */
293 ISR(TIMER0_COMPA_vect, ISR_BLOCK)
294 {
295 /* One millisecond has elapsed, decrement the idle time remaining counter if it has not already elapsed */
296 if (IdleMSRemaining)
297 IdleMSRemaining--;
298 }
299
300 /** Fills the given HID report data structure with the next HID report to send to the host.
301 *
302 * \param ReportData Pointer to a HID report data structure to be filled
303 *
304 * \return Boolean true if the new report differs from the last report, false otherwise
305 */
306 bool GetNextReport(USB_KeyboardReport_Data_t* ReportData)
307 {
308 static uint8_t PrevJoyStatus = 0;
309 uint8_t JoyStatus_LCL = Joystick_GetStatus();
310 bool InputChanged = false;
311
312 /* Clear the report contents */
313 memset(ReportData, 0, sizeof(USB_KeyboardReport_Data_t));
314
315 if (JoyStatus_LCL & JOY_UP)
316 ReportData->KeyCode[0] = 0x04; // A
317 else if (JoyStatus_LCL & JOY_DOWN)
318 ReportData->KeyCode[0] = 0x05; // B
319
320 if (JoyStatus_LCL & JOY_LEFT)
321 ReportData->KeyCode[0] = 0x06; // C
322 else if (JoyStatus_LCL & JOY_RIGHT)
323 ReportData->KeyCode[0] = 0x07; // D
324
325 if (JoyStatus_LCL & JOY_PRESS)
326 ReportData->KeyCode[0] = 0x08; // E
327
328 /* Check if the new report is different to the previous report */
329 InputChanged = (uint8_t)(PrevJoyStatus ^ JoyStatus_LCL);
330
331 /* Save the current joystick status for later comparison */
332 PrevJoyStatus = JoyStatus_LCL;
333
334 /* Return whether the new report is different to the previous report or not */
335 return InputChanged;
336 }
337
338 /** Processes a given LED report mask from the host and sets the board LEDs to match.
339 *
340 * \param LEDReport LED mask from the host, containing a mask of what LEDs are set
341 */
342 void ProcessLEDReport(uint8_t LEDReport)
343 {
344 uint8_t LEDMask = LEDS_LED2;
345
346 if (LEDReport & 0x01) // NUM Lock
347 LEDMask |= LEDS_LED1;
348
349 if (LEDReport & 0x02) // CAPS Lock
350 LEDMask |= LEDS_LED3;
351
352 if (LEDReport & 0x04) // SCROLL Lock
353 LEDMask |= LEDS_LED4;
354
355 /* Set the status LEDs to the current Keyboard LED status */
356 LEDs_SetAllLEDs(LEDMask);
357 }
358
359 /** Function to manage status updates to the user. This is done via LEDs on the given board, if available, but may be changed to
360 * log to a serial port, or anything else that is suitable for status updates.
361 *
362 * \param CurrentStatus Current status of the system, from the Keyboard_StatusCodes_t enum
363 */
364 void UpdateStatus(uint8_t CurrentStatus)
365 {
366 uint8_t LEDMask = LEDS_NO_LEDS;
367
368 /* Set the LED mask to the appropriate LED mask based on the given status code */
369 switch (CurrentStatus)
370 {
371 case Status_USBNotReady:
372 LEDMask = (LEDS_LED1);
373 break;
374 case Status_USBEnumerating:
375 LEDMask = (LEDS_LED1 | LEDS_LED2);
376 break;
377 case Status_USBReady:
378 LEDMask = (LEDS_LED2 | LEDS_LED4);
379 break;
380 }
381
382 /* Set the board LEDs to the new LED mask */
383 LEDs_SetAllLEDs(LEDMask);
384 }
385
386 /** Function to manage HID report generation and transmission to the host, when in report mode. */
387 TASK(USB_Keyboard_Report)
388 {
389 USB_KeyboardReport_Data_t KeyboardReportData;
390 bool SendReport;
391
392 /* Create the next keyboard report for transmission to the host */
393 SendReport = GetNextReport(&KeyboardReportData);
394
395 /* Check if the idle period is set and has elapsed */
396 if (IdleCount && !(IdleMSRemaining))
397 {
398 /* Idle period elapsed, indicate that a report must be sent */
399 SendReport = true;
400
401 /* Reset the idle time remaining counter, must multiply by 4 to get the duration in milliseconds */
402 IdleMSRemaining = (IdleCount << 2);
403 }
404
405 /* Check if the USB system is connected to a host */
406 if (USB_IsConnected)
407 {
408 /* Select the Keyboard Report Endpoint */
409 Endpoint_SelectEndpoint(KEYBOARD_EPNUM);
410
411 /* Check if Keyboard Endpoint Ready for Read/Write, and if we should send a report */
412 if (Endpoint_ReadWriteAllowed() && SendReport)
413 {
414 /* Write Keyboard Report Data */
415 Endpoint_Write_Stream_LE(&KeyboardReportData, sizeof(KeyboardReportData));
416
417 /* Finalize the stream transfer to send the last packet */
418 Endpoint_ClearCurrentBank();
419 }
420
421 /* Select the Keyboard LED Report Endpoint */
422 Endpoint_SelectEndpoint(KEYBOARD_LEDS_EPNUM);
423
424 /* Check if Keyboard LED Endpoint Ready for Read/Write */
425 if (Endpoint_ReadWriteAllowed())
426 {
427 /* Read in the LED report from the host */
428 uint8_t LEDStatus = Endpoint_Read_Byte();
429
430 /* Process the incomming LED report */
431 ProcessLEDReport(LEDStatus);
432
433 /* Handshake the OUT Endpoint - clear endpoint and ready for next report */
434 Endpoint_ClearCurrentBank();
435 }
436 }
437 }