blob: 39c3cae382283e8b931e825a6ced5a31b7ba5961 [file] [log] [blame]
brians0ab60bb2013-01-31 02:21:51 +00001/*
Brian Silverman6ad00b82013-03-27 19:02:38 -07002 LPCUSB, an USB device driver for LPC microcontrollers
3 Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
brians0ab60bb2013-01-31 02:21:51 +00004
Brian Silverman6ad00b82013-03-27 19:02:38 -07005 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
brians0ab60bb2013-01-31 02:21:51 +00007
Brian Silverman6ad00b82013-03-27 19:02:38 -07008 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
brians0ab60bb2013-01-31 02:21:51 +000015
Brian Silverman6ad00b82013-03-27 19:02:38 -070016 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
brians0ab60bb2013-01-31 02:21:51 +000026*/
27
28#include "FreeRTOS.h"
29#include "queue.h"
30#include "task.h"
31
32#include <stdio.h>
33#include <string.h>
34
Brian Silverman89e86362013-10-30 17:50:50 -070035#include "LPCUSB/usbapi.h"
36#include "LPCUSB/usbdebug.h"
37#include "LPCUSB/usbstruct.h"
38
39// This file is marked private and most of the functions in its associated .c
40// file started out static, but we want to use some of them to do frame handling
41// stuff because we do special stuff with it (handle it ourselves for reduced
42// jitter and actually deal with the frame number correctly), so it's nice to
43// have the utility functions for accessing the hardware available instead of
44// having to rewrite them.
45#include "LPCUSB/usbhw_lpc.h"
46unsigned char USBHwCmdRead(unsigned char bCmd);
47void Wait4DevInt(unsigned long dwIntr);
brians0ab60bb2013-01-31 02:21:51 +000048
49#include "LPC17xx.h"
50
Brian Silvermanf92396c2013-09-12 20:13:13 -070051#include "fill_packet.h"
Brian Silvermanc554a8f2013-03-31 19:07:49 -070052
Brian Silverman6ad00b82013-03-27 19:02:38 -070053#define usbMAX_SEND_BLOCK ( 20 / portTICK_RATE_MS )
54#define usbRXBUFFER_LEN ( 80 )
55#define usbTXBUFFER_LEN ( 600 )
brians0ab60bb2013-01-31 02:21:51 +000056
Brian Silverman70478d12013-10-11 17:54:58 -070057// Read the processor manual for picking these.
Brian Silverman6ad00b82013-03-27 19:02:38 -070058#define BULK_IN_EP 0x82
Brian Silvermanc554a8f2013-03-31 19:07:49 -070059#define BULK_OUT_EP 0x05
60#define ISOC_IN_EP 0x83
Brian Silverman2d21bb22013-10-25 15:52:42 -070061#define NUM_ENDPOINTS 3
brians0ab60bb2013-01-31 02:21:51 +000062
Brian Silverman6ad00b82013-03-27 19:02:38 -070063#define MAX_PACKET_SIZE 64
Brian Silverman2d21bb22013-10-25 15:52:42 -070064#define DATA_PACKET_SIZE DATA_STRUCT_SEND_SIZE
brians0ab60bb2013-01-31 02:21:51 +000065
Brian Silverman6ad00b82013-03-27 19:02:38 -070066#define LE_WORD(x) ((x)&0xFF),((x)>>8)
brians0ab60bb2013-01-31 02:21:51 +000067
68static xQueueHandle xRxedChars = NULL, xCharsForTx = NULL;
69
Brian Silverman70478d12013-10-11 17:54:58 -070070// This gets cleared each time the ISR is entered and then checked as it's
71// returning so that we can still yield from the ISR to a woken task but not
72// from the middle of the ISR like it would be if this was checked in each
73// endpoint handler that needs it.
74static portBASE_TYPE higher_priority_task_woken;
brians0ab60bb2013-01-31 02:21:51 +000075
Brian Silverman70478d12013-10-11 17:54:58 -070076static const unsigned char abDescriptors[] = {
Austin Schuh63d0e9b2013-03-27 04:43:14 +000077// Device descriptor
Brian Silverman6ad00b82013-03-27 19:02:38 -070078 0x12,
79 DESC_DEVICE,
80 LE_WORD(0x0200), // bcdUSB
81 0xFF, // bDeviceClass
82 0x00, // bDeviceSubClass
83 0x00, // bDeviceProtocol
84 MAX_PACKET_SIZE0, // bMaxPacketSize
85 LE_WORD(0x1424), // idVendor
86 LE_WORD(0xd243), // idProduct
87 LE_WORD(0x0153), // bcdDevice
88 0x03, // iManufacturer
89 0x02, // iProduct
90 0x01, // iSerialNumber
91 0x01, // bNumConfigurations
brians0ab60bb2013-01-31 02:21:51 +000092
Austin Schuh63d0e9b2013-03-27 04:43:14 +000093// Configuration descriptor
Brian Silverman6ad00b82013-03-27 19:02:38 -070094 0x09,
95 DESC_CONFIGURATION,
Brian Silverman70478d12013-10-11 17:54:58 -070096 LE_WORD(9 + 9 + 7 * NUM_ENDPOINTS), // wTotalLength
Brian Silverman6ad00b82013-03-27 19:02:38 -070097 0x01, // bNumInterfaces
98 0x01, // bConfigurationValue
99 0x00, // iConfiguration
100 0xC0, // bmAttributes
101 0x32, // bMaxPower
Austin Schuh63d0e9b2013-03-27 04:43:14 +0000102// Data class interface descriptor
Brian Silverman6ad00b82013-03-27 19:02:38 -0700103 0x09,
104 DESC_INTERFACE,
105 0x00, // bInterfaceNumber
106 0x00, // bAlternateSetting
Brian Silverman70478d12013-10-11 17:54:58 -0700107 NUM_ENDPOINTS, // bNumEndPoints
Brian Silverman6ad00b82013-03-27 19:02:38 -0700108 0x0A, // bInterfaceClass = data
109 0x00, // bInterfaceSubClass
110 0x00, // bInterfaceProtocol
111 0x00, // iInterface
Austin Schuh63d0e9b2013-03-27 04:43:14 +0000112// Debug EP OUT
Brian Silverman6ad00b82013-03-27 19:02:38 -0700113 0x07,
114 DESC_ENDPOINT,
Brian Silverman70478d12013-10-11 17:54:58 -0700115 BULK_OUT_EP, // bEndpointAddress
Brian Silverman6ad00b82013-03-27 19:02:38 -0700116 0x02, // bmAttributes = bulk
117 LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
118 0x00, // bInterval
Austin Schuh63d0e9b2013-03-27 04:43:14 +0000119// Debug EP in
Brian Silverman6ad00b82013-03-27 19:02:38 -0700120 0x07,
121 DESC_ENDPOINT,
122 BULK_IN_EP, // bEndpointAddress
123 0x02, // bmAttributes = bulk
124 LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
125 0x00, // bInterval
Brian Silvermanc554a8f2013-03-31 19:07:49 -0700126 // isoc data EP IN
127 0x07,
128 DESC_ENDPOINT,
129 ISOC_IN_EP, // bEndpointAddress
130 0x0D, // bmAttributes = isoc, synchronous, data endpoint
Brian Silverman2d21bb22013-10-25 15:52:42 -0700131 LE_WORD(DATA_PACKET_SIZE), // wMaxPacketSize
Brian Silverman70478d12013-10-11 17:54:58 -0700132 0x01, // bInterval
brians0ab60bb2013-01-31 02:21:51 +0000133
Brian Silverman6ad00b82013-03-27 19:02:38 -0700134 // string descriptors
135 0x04,
136 DESC_STRING,
137 LE_WORD(0x0409),
brians0ab60bb2013-01-31 02:21:51 +0000138
Brian Silverman6ad00b82013-03-27 19:02:38 -0700139 0x0E,
140 DESC_STRING,
141 'A', 0, 'S', 0, 'C', 0, 'H', 0, 'U', 0, 'H', 0,
brians0ab60bb2013-01-31 02:21:51 +0000142
Brian Silverman6ad00b82013-03-27 19:02:38 -0700143 0x14,
144 DESC_STRING,
145 'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'n', 0, 's', 0, 'o', 0, 'r', 0,
brians0ab60bb2013-01-31 02:21:51 +0000146
Brian Silverman6ad00b82013-03-27 19:02:38 -0700147 0x12,
148 DESC_STRING,
149 'A', 0, 'O', 0, 'S', 0, '_', 0, 'G', 0, 'y', 0, 'r', 0, 'o', 0,
brians0ab60bb2013-01-31 02:21:51 +0000150
151// terminating zero
Brian Silverman6ad00b82013-03-27 19:02:38 -0700152 0
brians0ab60bb2013-01-31 02:21:51 +0000153};
154
Brian Silverman89e86362013-10-30 17:50:50 -0700155// Enables interrupts to write data instead of NAKing on the bulk in endpoints.
156// This is in a centralized place so that other NAK interrupts can be enabled
157// all of the time easily in the future.
158static void bulk_in_nak_int(int have_data) {
159 USBHwNakIntEnable(have_data ? INACK_BI : 0);
160}
brians0ab60bb2013-01-31 02:21:51 +0000161
162/**
163 * Local function to handle incoming bulk data
164 *
165 * @param [in] bEP
166 * @param [in] bEPStatus
167 */
168static void DebugOut(unsigned char bEP, unsigned char bEPStatus) {
169 int i, iLen;
brians0ab60bb2013-01-31 02:21:51 +0000170 unsigned char abBulkBuf[64];
171
172 (void) bEPStatus;
173
174 // get data from USB into intermediate buffer
175 iLen = USBHwEPRead(bEP, abBulkBuf, sizeof(abBulkBuf));
176 for (i = 0; i < iLen; i++) {
177 // put into queue
Brian Silverman70478d12013-10-11 17:54:58 -0700178 xQueueSendFromISR(xRxedChars, &abBulkBuf[i], &higher_priority_task_woken);
brians0ab60bb2013-01-31 02:21:51 +0000179 }
brians0ab60bb2013-01-31 02:21:51 +0000180}
181
182
183/**
184 * Local function to handle outgoing bulk data
185 *
186 * @param [in] bEP
187 * @param [in] bEPStatus
188 */
189static void DebugIn(unsigned char bEP, unsigned char bEPStatus) {
190 int i, iLen;
brians0ab60bb2013-01-31 02:21:51 +0000191 unsigned char abBulkBuf[64];
192
193 (void) bEPStatus;
194
195 if (uxQueueMessagesWaitingFromISR(xCharsForTx) == 0) {
Brian Silverman89e86362013-10-30 17:50:50 -0700196 // no more data
197 bulk_in_nak_int(0);
brians0ab60bb2013-01-31 02:21:51 +0000198 return;
199 }
200
201 // get bytes from transmit FIFO into intermediate buffer
202 for (i = 0; i < MAX_PACKET_SIZE; i++) {
Brian Silverman70478d12013-10-11 17:54:58 -0700203 if (xQueueReceiveFromISR(xCharsForTx, &abBulkBuf[i],
204 &higher_priority_task_woken) != pdPASS) {
brians0ab60bb2013-01-31 02:21:51 +0000205 break;
206 }
207 }
208 iLen = i;
209
210 // send over USB
211 if (iLen > 0) {
212 USBHwEPWrite(bEP, abBulkBuf, iLen);
213 }
brians0ab60bb2013-01-31 02:21:51 +0000214}
215
Austin Schuh63d0e9b2013-03-27 04:43:14 +0000216
brians0ab60bb2013-01-31 02:21:51 +0000217/**
218 * Writes one character to VCOM port
219 *
220 * @param [in] c character to write
221 * @returns character written, or EOF if character could not be written
222 */
223int VCOM_putcharFromISR(int c, long *lHigherPriorityTaskWoken) {
Brian Silverman6ad00b82013-03-27 19:02:38 -0700224 char cc = (char) c;
brians0ab60bb2013-01-31 02:21:51 +0000225
Brian Silverman6ad00b82013-03-27 19:02:38 -0700226 if (xQueueSendFromISR(xCharsForTx, &cc,
227 lHigherPriorityTaskWoken) == pdPASS) {
228 return c;
229 } else {
230 return EOF;
231 }
brians0ab60bb2013-01-31 02:21:51 +0000232}
233
234int VCOM_putchar(int c) {
Brian Silverman6ad00b82013-03-27 19:02:38 -0700235 char cc = (char) c;
brians0ab60bb2013-01-31 02:21:51 +0000236
Brian Silverman6ad00b82013-03-27 19:02:38 -0700237 // Don't block if not connected to USB.
238 if (xQueueSend(xCharsForTx, &cc,
Brian Silvermanc554a8f2013-03-31 19:07:49 -0700239 USBIsConnected() ? usbMAX_SEND_BLOCK : 0) == pdPASS) {
Brian Silverman6ad00b82013-03-27 19:02:38 -0700240 return c;
241 } else {
242 return EOF;
243 }
brians0ab60bb2013-01-31 02:21:51 +0000244}
245
246
247/**
248 * Reads one character from VCOM port
249 *
250 * @returns character read, or EOF if character could not be read
251 */
252int VCOM_getchar(void) {
Brian Silverman6ad00b82013-03-27 19:02:38 -0700253 unsigned char c;
brians0ab60bb2013-01-31 02:21:51 +0000254
Brian Silverman6ad00b82013-03-27 19:02:38 -0700255 /* Block the task until a character is available. */
256 if(xQueueReceive(xRxedChars, &c, 0) == pdTRUE){ //portMAX_DELAY);
257 return c;
258 }
259 return -1;
brians0ab60bb2013-01-31 02:21:51 +0000260}
261
Brian Silverman89e86362013-10-30 17:50:50 -0700262// Instead of registering an lpcusb handler for this, we do it ourself so that
263// we can get the timing jitter down.
264static void HandleFrame(void) {
265 USB->USBDevIntClr = FRAME;
brians0ab60bb2013-01-31 02:21:51 +0000266
Brian Silverman89e86362013-10-30 17:50:50 -0700267 static struct DataStruct sensor_values;
268 fillSensorPacket(&sensor_values);
269
270 static int32_t current_frame = -1;
271 static int guessed_frames = 0;
272
273 uint8_t error_status = USBHwCmdRead(CMD_DEV_READ_ERROR_STATUS);
274 if (error_status & PID_ERR) {
275 ++guessed_frames;
Brian Silvermanc554a8f2013-03-31 19:07:49 -0700276 } else {
Brian Silverman89e86362013-10-30 17:50:50 -0700277 int16_t read_frame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);
278 USB->USBCmdCode = 0x00000200 | (CMD_DEV_READ_CUR_FRAME_NR << 16);
279 Wait4DevInt(CDFULL);
280 read_frame |= USB->USBCmdData << 8;
281
282 if (current_frame < 0) {
283 current_frame = read_frame;
284 guessed_frames = 0;
285 } else {
286 static const uint32_t kMaxReadFrame = 0x800;
287 static const uint32_t kReadMask = kMaxReadFrame - 1;
288 if ((current_frame & kReadMask) == read_frame) {
289 // This seems like it must mean that we didn't receive the SOF token.
290 ++guessed_frames;
291 } else {
292 guessed_frames = 0;
293 int16_t difference =
294 read_frame - (int16_t)((current_frame + 1) & kReadMask);
295 // If we're off by only a little.
296 if (difference > -10 && difference < 10) {
297 current_frame = ((current_frame + 1) & ~kReadMask) | read_frame;
298 // If we're ahead by only a little but we wrapped.
299 } else if (difference > kMaxReadFrame - 10) {
300 current_frame =
301 ((current_frame & ~kReadMask) - kMaxReadFrame) | read_frame;
302 // If we're behind by only a little but the packet counter wrapped.
303 } else if (difference < -(kMaxReadFrame - 10)) {
304 current_frame =
305 ((current_frame & ~kReadMask) + kMaxReadFrame) | read_frame;
306 } else {
307 // Give up and reset.
308 current_frame = -1;
309 }
310 }
311 }
Austin Schuh63d0e9b2013-03-27 04:43:14 +0000312 }
Brian Silvermanc554a8f2013-03-31 19:07:49 -0700313
Brian Silverman89e86362013-10-30 17:50:50 -0700314 sensor_values.frame_number = current_frame + guessed_frames;
315 sensor_values.unknown_frame = guessed_frames > 10;
316
317 USBHwEPWrite(ISOC_IN_EP, (unsigned char *)&sensor_values, DATA_PACKET_SIZE);
318
319 if (uxQueueMessagesWaitingFromISR(xCharsForTx) > 0) {
320 // Data to send is available so enable interrupt instead of NAK.
321 bulk_in_nak_int(1);
322 } else {
323 bulk_in_nak_int(0);
324 }
325}
326
327void USB_IRQHandler(void) {
328 higher_priority_task_woken = pdFALSE;
329 uint32_t status = SC->USBIntSt;
330 if (status & USB_INT_REQ_HP) {
331 // We set the frame interrupt to get routed to the high priority line.
332 HandleFrame();
333 }
334 //if (status & USB_INT_REQ_LP) {
335 // Call lpcusb to let it handle all of the other interrupts.
336 USBHwISR();
337 //}
338 portEND_SWITCHING_ISR(higher_priority_task_woken);
brians0ab60bb2013-01-31 02:21:51 +0000339}
340
Brian Silvermand36b7d32013-10-24 15:56:47 -0700341void usb_init(void) {
brians0ab60bb2013-01-31 02:21:51 +0000342 DBG("Initialising USB stack\n");
343
344 xRxedChars = xQueueCreate(usbRXBUFFER_LEN, sizeof(char));
345 xCharsForTx = xQueueCreate(usbTXBUFFER_LEN, sizeof(char));
346
347 if ((xRxedChars == NULL) || (xCharsForTx == NULL)) {
348 /* Not enough heap available to create the buffer queues, can't do
349 anything so just delete ourselves. */
350 vTaskDelete(NULL);
351 }
352
Brian Silverman70478d12013-10-11 17:54:58 -0700353 // Initialise the USB stack.
brians0ab60bb2013-01-31 02:21:51 +0000354 USBInit();
355
356 // register descriptors
357 USBRegisterDescriptors(abDescriptors);
358
359 // register class request handler
360 //USBRegisterRequestHandler(REQTYPE_TYPE_CLASS,
361 // HandleClassRequest, abClassReqData);
362
363 // register endpoint handlers
brians0ab60bb2013-01-31 02:21:51 +0000364 USBHwRegisterEPIntHandler(BULK_IN_EP, DebugIn);
365 USBHwRegisterEPIntHandler(BULK_OUT_EP, DebugOut);
366
Brian Silverman89e86362013-10-30 17:50:50 -0700367 USB->USBDevIntPri = 1; // route frame interrupt to high priority line
368 USB->USBDevIntEn |= FRAME; // enable frame interrupt
369
brians0ab60bb2013-01-31 02:21:51 +0000370 // register frame handler
Brian Silverman89e86362013-10-30 17:50:50 -0700371 //USBHwRegisterFrameHandler(USBFrameHandler);
brians0ab60bb2013-01-31 02:21:51 +0000372
brians0ab60bb2013-01-31 02:21:51 +0000373 DBG("Starting USB communication\n");
374
375 NVIC_SetPriority(USB_IRQn, configUSB_INTERRUPT_PRIORITY);
376 NVIC_EnableIRQ(USB_IRQn);
377
378 // connect to bus
379
380 DBG("Connecting to USB bus\n");
381 USBHwConnect(TRUE);
382
Brian Silvermand36b7d32013-10-24 15:56:47 -0700383 // Enable USB. The PC has probably disconnected it now.
384 USBHwAllowConnect();
brians0ab60bb2013-01-31 02:21:51 +0000385}