blob: c90bd0d8eac0a587362c03343b8472eb3379f8b0 [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
Brian Silverman441d55c2013-10-31 19:27:23 -0700263// we can get the timing jitter down and deal with the frame number right.
Brian Silverman89e86362013-10-30 17:50:50 -0700264static 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
Brian Silverman441d55c2013-10-31 19:27:23 -0700270 // What the last good frame number that we got was.
271 // Values <0 are uninitialized.
Brian Silverman89e86362013-10-30 17:50:50 -0700272 static int32_t current_frame = -1;
Brian Silverman441d55c2013-10-31 19:27:23 -0700273 // How many extra frames we're guessing happened since we got a good one.
Brian Silverman89e86362013-10-30 17:50:50 -0700274 static int guessed_frames = 0;
275
276 uint8_t error_status = USBHwCmdRead(CMD_DEV_READ_ERROR_STATUS);
277 if (error_status & PID_ERR) {
278 ++guessed_frames;
Brian Silvermanc554a8f2013-03-31 19:07:49 -0700279 } else {
Brian Silverman89e86362013-10-30 17:50:50 -0700280 int16_t read_frame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);
281 USB->USBCmdCode = 0x00000200 | (CMD_DEV_READ_CUR_FRAME_NR << 16);
282 Wait4DevInt(CDFULL);
283 read_frame |= USB->USBCmdData << 8;
284
285 if (current_frame < 0) {
286 current_frame = read_frame;
287 guessed_frames = 0;
288 } else {
Brian Silverman441d55c2013-10-31 19:27:23 -0700289 // All of the complicated stuff in here tracks the frame number from
290 // hardware (which comes from the SOF tokens sent out by the host) except
291 // deal with it if we miss a couple or get off by a little bit (and reset
292 // completely if we get off by a lot or miss a lot in a row).
293
Brian Silverman89e86362013-10-30 17:50:50 -0700294 static const uint32_t kMaxReadFrame = 0x800;
295 static const uint32_t kReadMask = kMaxReadFrame - 1;
296 if ((current_frame & kReadMask) == read_frame) {
297 // This seems like it must mean that we didn't receive the SOF token.
298 ++guessed_frames;
299 } else {
300 guessed_frames = 0;
Brian Silverman441d55c2013-10-31 19:27:23 -0700301 // The frame number that we think we should have gotten.
302 int32_t expected_frame = current_frame + guessed_frames + 1;
Brian Silverman89e86362013-10-30 17:50:50 -0700303 int16_t difference =
Brian Silverman441d55c2013-10-31 19:27:23 -0700304 read_frame - (int16_t)(expected_frame & kReadMask);
Brian Silverman89e86362013-10-30 17:50:50 -0700305 // If we're off by only a little.
306 if (difference > -10 && difference < 10) {
Brian Silverman441d55c2013-10-31 19:27:23 -0700307 current_frame = (expected_frame & ~kReadMask) | read_frame;
308 // If we're ahead by only a little (or dead on) but we wrapped.
Brian Silverman89e86362013-10-30 17:50:50 -0700309 } else if (difference > kMaxReadFrame - 10) {
310 current_frame =
Brian Silverman441d55c2013-10-31 19:27:23 -0700311 ((expected_frame & ~kReadMask) - kMaxReadFrame) | read_frame;
312 // If we're behind by only a little (or dead on) but the number in the
313 // token wrapped.
Brian Silverman89e86362013-10-30 17:50:50 -0700314 } else if (difference < -(kMaxReadFrame - 10)) {
315 current_frame =
Brian Silverman441d55c2013-10-31 19:27:23 -0700316 ((expected_frame & ~kReadMask) + kMaxReadFrame) | read_frame;
Brian Silverman89e86362013-10-30 17:50:50 -0700317 } else {
Brian Silverman441d55c2013-10-31 19:27:23 -0700318 // We're way off, so give up and reset.
Brian Silverman89e86362013-10-30 17:50:50 -0700319 current_frame = -1;
320 }
321 }
322 }
Austin Schuh63d0e9b2013-03-27 04:43:14 +0000323 }
Brian Silvermanc554a8f2013-03-31 19:07:49 -0700324
Brian Silverman89e86362013-10-30 17:50:50 -0700325 sensor_values.frame_number = current_frame + guessed_frames;
326 sensor_values.unknown_frame = guessed_frames > 10;
327
328 USBHwEPWrite(ISOC_IN_EP, (unsigned char *)&sensor_values, DATA_PACKET_SIZE);
329
330 if (uxQueueMessagesWaitingFromISR(xCharsForTx) > 0) {
331 // Data to send is available so enable interrupt instead of NAK.
332 bulk_in_nak_int(1);
333 } else {
334 bulk_in_nak_int(0);
335 }
336}
337
338void USB_IRQHandler(void) {
339 higher_priority_task_woken = pdFALSE;
340 uint32_t status = SC->USBIntSt;
341 if (status & USB_INT_REQ_HP) {
342 // We set the frame interrupt to get routed to the high priority line.
343 HandleFrame();
344 }
345 //if (status & USB_INT_REQ_LP) {
346 // Call lpcusb to let it handle all of the other interrupts.
347 USBHwISR();
348 //}
349 portEND_SWITCHING_ISR(higher_priority_task_woken);
brians0ab60bb2013-01-31 02:21:51 +0000350}
351
Brian Silvermand36b7d32013-10-24 15:56:47 -0700352void usb_init(void) {
brians0ab60bb2013-01-31 02:21:51 +0000353 DBG("Initialising USB stack\n");
354
355 xRxedChars = xQueueCreate(usbRXBUFFER_LEN, sizeof(char));
356 xCharsForTx = xQueueCreate(usbTXBUFFER_LEN, sizeof(char));
357
358 if ((xRxedChars == NULL) || (xCharsForTx == NULL)) {
359 /* Not enough heap available to create the buffer queues, can't do
360 anything so just delete ourselves. */
361 vTaskDelete(NULL);
362 }
363
Brian Silverman70478d12013-10-11 17:54:58 -0700364 // Initialise the USB stack.
brians0ab60bb2013-01-31 02:21:51 +0000365 USBInit();
366
367 // register descriptors
368 USBRegisterDescriptors(abDescriptors);
369
370 // register class request handler
371 //USBRegisterRequestHandler(REQTYPE_TYPE_CLASS,
372 // HandleClassRequest, abClassReqData);
373
374 // register endpoint handlers
brians0ab60bb2013-01-31 02:21:51 +0000375 USBHwRegisterEPIntHandler(BULK_IN_EP, DebugIn);
376 USBHwRegisterEPIntHandler(BULK_OUT_EP, DebugOut);
377
Brian Silverman89e86362013-10-30 17:50:50 -0700378 USB->USBDevIntPri = 1; // route frame interrupt to high priority line
379 USB->USBDevIntEn |= FRAME; // enable frame interrupt
380
brians0ab60bb2013-01-31 02:21:51 +0000381 // register frame handler
Brian Silverman89e86362013-10-30 17:50:50 -0700382 //USBHwRegisterFrameHandler(USBFrameHandler);
brians0ab60bb2013-01-31 02:21:51 +0000383
brians0ab60bb2013-01-31 02:21:51 +0000384 DBG("Starting USB communication\n");
385
386 NVIC_SetPriority(USB_IRQn, configUSB_INTERRUPT_PRIORITY);
387 NVIC_EnableIRQ(USB_IRQn);
388
389 // connect to bus
390
391 DBG("Connecting to USB bus\n");
392 USBHwConnect(TRUE);
393
Brian Silvermand36b7d32013-10-24 15:56:47 -0700394 // Enable USB. The PC has probably disconnected it now.
395 USBHwAllowConnect();
brians0ab60bb2013-01-31 02:21:51 +0000396}