Changed stream wait timeout counter to be 16-bit, so that very long timeout periods...
[pub/USBasp.git] / Demos / KeyboardMouse / KeyboardMouse.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 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11 Copyright 2009 Denver Gingerich (denver [at] ossguy [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 KeyboardMouse demo. This file contains the main tasks of the demo and
35 * is responsible for the initial application hardware configuration.
36 */
37
38 #include "KeyboardMouse.h"
39
40 /* Project Tags, for reading out using the ButtLoad project */
41 BUTTLOADTAG(ProjName, "LUFA MouseKBD 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_Mouse , TaskStatus: TASK_RUN },
51 { Task: USB_Keyboard , TaskStatus: TASK_RUN },
52 };
53
54 /* Global Variables */
55 /** Global structure to hold the current keyboard interface HID report, for transmission to the host */
56 USB_KeyboardReport_Data_t KeyboardReportData;
57
58 /** Global structure to hold the current mouse interface HID report, for transmission to the host */
59 USB_MouseReport_Data_t MouseReportData;
60
61 /** Main program entry point. This routine configures the hardware required by the application, then
62 * starts the scheduler to run the USB management task.
63 */
64 int main(void)
65 {
66 /* Disable watchdog if enabled by bootloader/fuses */
67 MCUSR &= ~(1 << WDRF);
68 wdt_disable();
69
70 /* Disable Clock Division */
71 SetSystemClockPrescaler(0);
72
73 /* Hardware Initialization */
74 Joystick_Init();
75 LEDs_Init();
76
77 /* Indicate USB not ready */
78 UpdateStatus(Status_USBNotReady);
79
80 /* Initialize Scheduler so that it can be used */
81 Scheduler_Init();
82
83 /* Initialize USB Subsystem */
84 USB_Init();
85
86 /* Scheduling - routine never returns, so put this last in the main function */
87 Scheduler_Start();
88 }
89
90 /** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and
91 * starts the library USB task to begin the enumeration and USB management process.
92 */
93 EVENT_HANDLER(USB_Connect)
94 {
95 /* Start USB management task */
96 Scheduler_SetTaskMode(USB_USBTask, TASK_RUN);
97
98 /* Indicate USB enumerating */
99 UpdateStatus(Status_USBEnumerating);
100 }
101
102 /** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via
103 * the status LEDs and stops the USB management task.
104 */
105 EVENT_HANDLER(USB_Disconnect)
106 {
107 /* Stop running HID reporting and USB management tasks */
108 Scheduler_SetTaskMode(USB_USBTask, TASK_STOP);
109
110 /* Indicate USB not ready */
111 UpdateStatus(Status_USBNotReady);
112 }
113
114 /** Event handler for the USB_ConfigurationChanged event. This is fired when the host sets the current configuration
115 * of the USB device after enumeration, and configures the keyboard and mouse device endpoints.
116 */
117 EVENT_HANDLER(USB_ConfigurationChanged)
118 {
119 /* Setup Keyboard Report Endpoint */
120 Endpoint_ConfigureEndpoint(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT,
121 ENDPOINT_DIR_IN, HID_EPSIZE,
122 ENDPOINT_BANK_SINGLE);
123
124 /* Setup Keyboard LED Report Endpoint */
125 Endpoint_ConfigureEndpoint(KEYBOARD_OUT_EPNUM, EP_TYPE_INTERRUPT,
126 ENDPOINT_DIR_OUT, HID_EPSIZE,
127 ENDPOINT_BANK_SINGLE);
128
129 /* Setup Mouse Report Endpoint */
130 Endpoint_ConfigureEndpoint(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT,
131 ENDPOINT_DIR_IN, HID_EPSIZE,
132 ENDPOINT_BANK_SINGLE);
133
134 /* Indicate USB connected and ready */
135 UpdateStatus(Status_USBReady);
136 }
137
138 /** Event handler for the USB_UnhandledControlPacket event. This is used to catch standard and class specific
139 * control requests that are not handled internally by the USB library (including the HID commands, which are
140 * all issued via the control endpoint), so that they can be handled appropriately for the application.
141 */
142 EVENT_HANDLER(USB_UnhandledControlPacket)
143 {
144 uint8_t* ReportData;
145 uint8_t ReportSize;
146
147 /* Handle HID Class specific requests */
148 switch (bRequest)
149 {
150 case REQ_GetReport:
151 if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
152 {
153 Endpoint_Ignore_Word();
154
155 uint16_t wIndex = Endpoint_Read_Word_LE();
156
157 /* Determine if it is the mouse or the keyboard data that is being requested */
158 if (!(wIndex))
159 {
160 ReportData = (uint8_t*)&KeyboardReportData;
161 ReportSize = sizeof(KeyboardReportData);
162 }
163 else
164 {
165 ReportData = (uint8_t*)&MouseReportData;
166 ReportSize = sizeof(MouseReportData);
167 }
168
169 /* Read in the number of bytes in the report to send to the host */
170 uint16_t wLength = Endpoint_Read_Word_LE();
171
172 /* If trying to send more bytes than exist to the host, clamp the value at the report size */
173 if (wLength > ReportSize)
174 wLength = ReportSize;
175
176 Endpoint_ClearSetupReceived();
177
178 /* Write the report data to the control endpoint */
179 Endpoint_Write_Control_Stream_LE(ReportData, wLength);
180
181 /* Clear the report data afterwards */
182 memset(ReportData, 0, ReportSize);
183
184 /* Finalize the stream transfer to send the last packet or clear the host abort */
185 Endpoint_ClearSetupOUT();
186 }
187
188 break;
189 case REQ_SetReport:
190 if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
191 {
192 Endpoint_ClearSetupReceived();
193
194 /* Wait until the LED report has been sent by the host */
195 while (!(Endpoint_IsSetupOUTReceived()));
196
197 /* Read in the LED report from the host */
198 uint8_t LEDStatus = Endpoint_Read_Byte();
199 uint8_t LEDMask = LEDS_LED2;
200
201 if (LEDStatus & 0x01) // NUM Lock
202 LEDMask |= LEDS_LED1;
203
204 if (LEDStatus & 0x02) // CAPS Lock
205 LEDMask |= LEDS_LED3;
206
207 if (LEDStatus & 0x04) // SCROLL Lock
208 LEDMask |= LEDS_LED4;
209
210 /* Set the status LEDs to the current HID LED status */
211 LEDs_SetAllLEDs(LEDMask);
212
213 /* Clear the endpoint data */
214 Endpoint_ClearSetupOUT();
215
216 /* Wait until the host is ready to receive the request confirmation */
217 while (!(Endpoint_IsSetupINReady()));
218
219 /* Handshake the request by sending an empty IN packet */
220 Endpoint_ClearSetupIN();
221 }
222
223 break;
224 }
225 }
226
227 /** Function to manage status updates to the user. This is done via LEDs on the given board, if available, but may be changed to
228 * log to a serial port, or anything else that is suitable for status updates.
229 *
230 * \param CurrentStatus Current status of the system, from the KeyboardMouse_StatusCodes_t enum
231 */
232 void UpdateStatus(uint8_t CurrentStatus)
233 {
234 uint8_t LEDMask = LEDS_NO_LEDS;
235
236 /* Set the LED mask to the appropriate LED mask based on the given status code */
237 switch (CurrentStatus)
238 {
239 case Status_USBNotReady:
240 LEDMask = (LEDS_LED1);
241 break;
242 case Status_USBEnumerating:
243 LEDMask = (LEDS_LED1 | LEDS_LED2);
244 break;
245 case Status_USBReady:
246 LEDMask = (LEDS_LED2 | LEDS_LED4);
247 break;
248 }
249
250 /* Set the board LEDs to the new LED mask */
251 LEDs_SetAllLEDs(LEDMask);
252 }
253
254 /** Keyboard task. This generates the next keyboard HID report for the host, and transmits it via the
255 * keyboard IN endpoint when the host is ready for more data. Additionally, it processes host LED status
256 * reports sent to the device via the keyboard OUT reporting endpoint.
257 */
258 TASK(USB_Keyboard)
259 {
260 uint8_t JoyStatus_LCL = Joystick_GetStatus();
261
262 /* Check if HWB is not pressed, if so mouse mode enabled */
263 if (!(HWB_GetStatus()))
264 {
265 if (JoyStatus_LCL & JOY_UP)
266 KeyboardReportData.KeyCode[0] = 0x04; // A
267 else if (JoyStatus_LCL & JOY_DOWN)
268 KeyboardReportData.KeyCode[0] = 0x05; // B
269
270 if (JoyStatus_LCL & JOY_LEFT)
271 KeyboardReportData.KeyCode[0] = 0x06; // C
272 else if (JoyStatus_LCL & JOY_RIGHT)
273 KeyboardReportData.KeyCode[0] = 0x07; // D
274
275 if (JoyStatus_LCL & JOY_PRESS)
276 KeyboardReportData.KeyCode[0] = 0x08; // E
277 }
278
279 /* Check if the USB system is connected to a host and report protocol mode is enabled */
280 if (USB_IsConnected)
281 {
282 /* Select the Keyboard Report Endpoint */
283 Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
284
285 /* Check if Keyboard Endpoint Ready for Read/Write */
286 if (Endpoint_ReadWriteAllowed())
287 {
288 /* Write Keyboard Report Data */
289 Endpoint_Write_Stream_LE(&KeyboardReportData, sizeof(KeyboardReportData));
290
291 /* Finalize the stream transfer to send the last packet */
292 Endpoint_ClearCurrentBank();
293
294 /* Clear the report data afterwards */
295 memset(&KeyboardReportData, 0, sizeof(KeyboardReportData));
296 }
297
298 /* Select the Keyboard LED Report Endpoint */
299 Endpoint_SelectEndpoint(KEYBOARD_OUT_EPNUM);
300
301 /* Check if Keyboard LED Endpoint Ready for Read/Write */
302 if (Endpoint_ReadWriteAllowed())
303 {
304 /* Read in the LED report from the host */
305 uint8_t LEDStatus = Endpoint_Read_Byte();
306 uint8_t LEDMask = LEDS_LED2;
307
308 if (LEDStatus & 0x01) // NUM Lock
309 LEDMask |= LEDS_LED1;
310
311 if (LEDStatus & 0x02) // CAPS Lock
312 LEDMask |= LEDS_LED3;
313
314 if (LEDStatus & 0x04) // SCROLL Lock
315 LEDMask |= LEDS_LED4;
316
317 /* Set the status LEDs to the current Keyboard LED status */
318 LEDs_SetAllLEDs(LEDMask);
319
320 /* Handshake the OUT Endpoint - clear endpoint and ready for next report */
321 Endpoint_ClearCurrentBank();
322 }
323 }
324 }
325
326 /** Mouse task. This generates the next mouse HID report for the host, and transmits it via the
327 * mouse IN endpoint when the host is ready for more data.
328 */
329 TASK(USB_Mouse)
330 {
331 uint8_t JoyStatus_LCL = Joystick_GetStatus();
332
333 /* Check if HWB is pressed, if so mouse mode enabled */
334 if (HWB_GetStatus())
335 {
336 if (JoyStatus_LCL & JOY_UP)
337 MouseReportData.Y = 1;
338 else if (JoyStatus_LCL & JOY_DOWN)
339 MouseReportData.Y = -1;
340
341 if (JoyStatus_LCL & JOY_RIGHT)
342 MouseReportData.X = 1;
343 else if (JoyStatus_LCL & JOY_LEFT)
344 MouseReportData.X = -1;
345
346 if (JoyStatus_LCL & JOY_PRESS)
347 MouseReportData.Button = (1 << 0);
348 }
349
350 /* Check if the USB system is connected to a host and report protocol mode is enabled */
351 if (USB_IsConnected)
352 {
353 /* Select the Mouse Report Endpoint */
354 Endpoint_SelectEndpoint(MOUSE_IN_EPNUM);
355
356 /* Check if Mouse Endpoint Ready for Read/Write */
357 if (Endpoint_ReadWriteAllowed())
358 {
359 /* Write Mouse Report Data */
360 Endpoint_Write_Stream_LE(&MouseReportData, sizeof(MouseReportData));
361
362 /* Finalize the stream transfer to send the last packet */
363 Endpoint_ClearCurrentBank();
364
365 /* Clear the report data afterwards */
366 memset(&MouseReportData, 0, sizeof(MouseReportData));
367 }
368 }
369 }