00001 /********************************************************************************* 00002 00003 Copyright 2006-2008 MakingThings 00004 00005 Licensed under the Apache License, 00006 Version 2.0 (the "License"); you may not use this file except in compliance 00007 with the License. You may obtain a copy of the License at 00008 00009 http://www.apache.org/licenses/LICENSE-2.0 00010 00011 Unless required by applicable law or agreed to in writing, software distributed 00012 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 00013 CONDITIONS OF ANY KIND, either express or implied. See the License for 00014 the specific language governing permissions and limitations under the License. 00015 00016 *********************************************************************************/ 00017 00018 /** \file usb.c 00019 USB Subsystem. 00020 Functions for controlling the USB port on the Make Controller Board. 00021 */ 00022 00023 #include "config.h" 00024 #ifdef MAKE_CTRL_USB 00025 00026 #include "usb.h" 00027 #include "rtos.h" 00028 #include "stdio.h" 00029 #include "USB-CDC.h" 00030 #include "queue.h" 00031 #include "string.h" 00032 00033 // SLIP codes 00034 #define END 0300 // indicates end of packet 00035 #define ESC 0333 // indicates byte stuffing 00036 #define ESC_END 0334 // ESC ESC_END means END data byte 00037 #define ESC_ESC 0335 // ESC ESC_ESC means ESC data byte 00038 00039 extern xQueueHandle xTxCDC; 00040 extern xQueueHandle xRxCDC; 00041 00042 static int Usb_Users; 00043 // set by the running task when it gets started 00044 int Usb_Running; 00045 00046 #define mainUSB_PRIORITY ( 4 ) 00047 #define mainUSB_TASK_STACK ( 600 ) 00048 #define TICKRATE 1000 00049 00050 void vUSBCDCTask( void *pvParameters ); 00051 static void Usb_WriteInternal(xBULKBUFFER *pq); 00052 Usb_* Usb = NULL; 00053 00054 /** \defgroup USB USB 00055 The USB subsystem provides access to USB Virtual Serial Port (CDC) functionality. 00056 00057 On OS X, the system USB CDC driver is used - no external drivers are needed. 00058 An entry in \b /dev is created - similar to <b>/dev/cu.usbmodem.xxxx</b>. It may be opened for reading and 00059 writing like a regular file using the standard POSIX open(), close(), read(), write() functions. 00060 00061 On Windows, the first time the device is seen, it needs 00062 to be pointed to a .INF file containing additional information - the \b make_controller_kit.inf in 00063 the same directory as this file. Once Windows sets this up, the device can be opened as a normal 00064 COM port. See also the \b mchelper codebase for code snippets. 00065 00066 \todo expand usb support to include emulation of other kinds of USB device - mouse, keyboard, etc. 00067 * \ingroup Core 00068 * @{ 00069 */ 00070 00071 /** 00072 Sets whether the Usb subsystem is active. 00073 @param state An integer specifying the active state of the Usb system - 1 (on) or 0 (off). 00074 @return Zero on success. 00075 */ 00076 int Usb_SetActive( int state ) 00077 { 00078 if ( state ) 00079 { 00080 if ( Usb_Users++ == 0 ) 00081 { 00082 // Create the USB task. 00083 Usb = MallocWait( sizeof( Usb_ ), 100 ); 00084 TaskCreate( vUSBCDCTask, "USB", mainUSB_TASK_STACK, NULL, mainUSB_PRIORITY ); 00085 00086 while ( !Usb_Running ) 00087 Sleep( 100 ); 00088 } 00089 } 00090 else 00091 { 00092 if ( Usb_Users ) 00093 { 00094 if ( --Usb_Users == 0 ) 00095 { 00096 /* kill the task, someday */ 00097 } 00098 } 00099 } 00100 return CONTROLLER_OK; 00101 } 00102 00103 /** 00104 Read the active state of the Usb system. 00105 @return State - 1 (on) or 0 (off). 00106 */ 00107 int Usb_GetActive( ) 00108 { 00109 return Usb_Users > 0; 00110 } 00111 00112 00113 /** 00114 Read from the USB port. 00115 Pass in a pointer to a char buffer to read into, and the number of characters requested to read. 00116 This function will return immediately with the number of characters read. 00117 @param buffer A pointer to a char buffer to read into 00118 @param length An integer specifying the number of characters to read 00119 @return The number of characters successfully read. 00120 */ 00121 int Usb_Read(char *buffer, int length) 00122 { 00123 static xBULKBUFFER DeQueue; 00124 static int queueRemaining = 0; // whether or not there's anything left over from last time. 00125 static unsigned char *queuePtr = DeQueue.Data; // pointer into our block buffer 00126 char *bufferPtr = buffer; // pointer into the buffer passed in 00127 00128 while( bufferPtr - buffer < length ) 00129 { 00130 if( queueRemaining ) // first check if there's anything left from a previous read 00131 { 00132 if( queuePtr - DeQueue.Data < DeQueue.Count ) 00133 *bufferPtr++ = *queuePtr++; 00134 else 00135 queueRemaining = 0; 00136 } 00137 else // nothing left to read, so grab more from the queue 00138 { 00139 if ( xQueueReceive( xRxCDC, &DeQueue, 0 ) == pdTRUE ) // got a new message from USB waiting on the queue 00140 { 00141 queueRemaining = 1; 00142 queuePtr = DeQueue.Data; 00143 } 00144 else // nothing was available so we're done 00145 break; 00146 } 00147 } 00148 return bufferPtr - buffer; 00149 } 00150 00151 /** 00152 Write to the USB port. 00153 Pass in a pointer to a char buffer to from, and the number of characters requested to write. 00154 @param buffer A pointer to a char buffer to write from 00155 @param length An integer specifying the number of characters to write 00156 @return The number of characters successfully written 00157 */ 00158 int Usb_Write( char* buffer, int length ) 00159 { 00160 if ( !Usb_Users ) 00161 Usb_SetActive( 1 ); 00162 00163 xBULKBUFFER q; 00164 q.Count = 0; 00165 int count = length; 00166 00167 // load bytes into the BULKBUFFER and when it's full, send it and continue loading 00168 while( length-- ) 00169 { 00170 q.Data[q.Count++] = *buffer++; 00171 if(q.Count == EP_FIFO) 00172 Usb_WriteInternal(&q); 00173 } 00174 00175 // if bytes have been loaded into the BULKBUFFER but not sent because it's not full yet, send it now 00176 if( q.Count ) 00177 Usb_WriteInternal(&q); 00178 00179 return count; 00180 } 00181 00182 // the outgoing USB hardware can write a block of 64 (EP_FIFO) bytes. 00183 // test the buffer's size and send it if we're full. 00184 void Usb_WriteInternal(xBULKBUFFER *pq) 00185 { 00186 while(xQueueSend( xTxCDC, pq, 0) != pdPASS) 00187 Sleep( usbSHORTEST_DELAY ); 00188 pq->Count = 0; 00189 } 00190 00191 /** 00192 Write to the USB port using SLIP codes to packetize messages. 00193 SLIP (Serial Line Internet Protocol) is a way to separate one "packet" from another on an open serial connection. 00194 This is the way OSC messages are sent over USB, for example. SLIP uses a simple start/end byte and an escape 00195 byte in case your data actually contains the start/end byte. Pass your normal buffer to this function to 00196 have the SLIP codes inserted and then write it out over USB. 00197 00198 Check the Wikipedia description of SLIP at http://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol 00199 @param buffer A pointer to a char buffer to write from 00200 @param length An integer specifying the number of characters to write 00201 @return The number of characters successfully written 00202 */ 00203 int Usb_SlipSend( char* buffer, int length ) 00204 { 00205 if( length > MAX_OUTGOING_SLIP_PACKET ) 00206 return CONTROLLER_ERROR_INSUFFICIENT_RESOURCES; 00207 00208 char *obp = Usb->slipSendBuffer, *bp = buffer; 00209 int count = length; 00210 00211 *obp++ = (char)END; 00212 00213 while( count-- ) 00214 { 00215 switch(*bp) 00216 { 00217 // if it's the same code as an END character, we send a special 00218 //two character code so as not to make the receiver think we sent an END 00219 case END: 00220 *obp++ = (char) ESC; 00221 *obp++ = (char) ESC_END; 00222 break; 00223 // if it's the same code as an ESC character, we send a special 00224 //two character code so as not to make the receiver think we sent an ESC 00225 case ESC: 00226 *obp++ = (char) ESC; 00227 *obp++ = (char) ESC_ESC; 00228 break; 00229 //otherwise, just send the character 00230 default: 00231 *obp++ = *bp; 00232 } 00233 bp++; 00234 } 00235 00236 *obp++ = END; // tell the receiver that we're done sending the packet 00237 int sendLength = obp - Usb->slipSendBuffer; 00238 Usb_Write( Usb->slipSendBuffer, sendLength ); 00239 00240 return CONTROLLER_OK; 00241 } 00242 00243 /** 00244 Read from the USB port using SLIP codes to de-packetize messages. 00245 SLIP (Serial Line Internet Protocol) is a way to separate one "packet" from another on an open serial connection. 00246 This is the way OSC messages are sent over USB, for example. SLIP uses a simple start/end byte and an escape 00247 byte in case your data actually contains the start/end byte. This function will block until it has received a 00248 complete SLIP encoded message, and will pass back the original message with the SLIP codes removed. 00249 00250 Check the Wikipedia description of SLIP at http://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol 00251 @param buffer A pointer to a char buffer to read into 00252 @param length An integer specifying the number of characters to read 00253 @return The number of characters successfully written 00254 */ 00255 int Usb_SlipReceive( char* buffer, int length ) 00256 { 00257 if( length > MAX_INCOMING_SLIP_PACKET ) 00258 return CONTROLLER_ERROR_INSUFFICIENT_RESOURCES; 00259 00260 int started = 0, finished = 0, count = 0, i; 00261 char *pbp = Usb->slipReadBuffer; 00262 static int bufRemaining = 0; 00263 char *bp = buffer; 00264 00265 while ( count < length ) 00266 { 00267 if( !bufRemaining ) // if there's nothing left over from last time, get more 00268 { 00269 bufRemaining = Usb_Read( Usb->slipReadBuffer, MAX_INCOMING_SLIP_PACKET ); 00270 pbp = Usb->slipReadBuffer; 00271 } 00272 00273 for( i = 0; i < bufRemaining; i++ ) 00274 { 00275 switch( *pbp ) 00276 { 00277 case END: 00278 if( started && count ) // it was the END byte 00279 finished = true; 00280 else // skipping all starting END bytes 00281 started = true; 00282 break; 00283 case ESC: 00284 // get the next byte. if it's not an ESC_END or ESC_ESC, it's a 00285 // malformed packet. http://tools.ietf.org/html/rfc1055 says just 00286 // drop it in the packet in this case 00287 pbp++; 00288 if( started ) 00289 { 00290 if( *pbp == ESC_END ) 00291 { 00292 *bp++ = END; 00293 count++; 00294 break; 00295 } 00296 else if( *pbp == ESC_ESC ) 00297 { 00298 *bp++ = ESC; 00299 count++; 00300 break; 00301 } 00302 } 00303 // no break here 00304 default: 00305 if( started ) 00306 { 00307 *bp++ = *pbp; 00308 count++; 00309 } 00310 break; 00311 } 00312 pbp++; 00313 bufRemaining--; 00314 if( finished ) 00315 return count; 00316 } 00317 Sleep(1); 00318 } 00319 return CONTROLLER_ERROR_BAD_FORMAT; // should never get here 00320 } 00321 00322 /** @} 00323 */ 00324 00325 #ifdef OSC 00326 00327 #include "osc.h" 00328 #include "string.h" 00329 #include "stdio.h" 00330 00331 // Need a list of property names 00332 // MUST end in zero 00333 static char* UsbOsc_Name = "usb"; 00334 static char* UsbOsc_PropertyNames[] = { "active", 0 }; // must have a trailing 0 00335 00336 int UsbOsc_PropertySet( int property, int value ); 00337 int UsbOsc_PropertyGet( int property ); 00338 00339 // Returns the name of the subsystem 00340 const char* UsbOsc_GetName( ) 00341 { 00342 return UsbOsc_Name; 00343 } 00344 00345 // Now getting a message. This is actually a part message, with the first 00346 // part (the subsystem) already parsed off. 00347 int UsbOsc_ReceiveMessage( int channel, char* message, int length ) 00348 { 00349 return Osc_IntReceiverHelper( channel, message, length, 00350 UsbOsc_Name, 00351 UsbOsc_PropertySet, UsbOsc_PropertyGet, 00352 UsbOsc_PropertyNames ); 00353 } 00354 00355 // Set the index Usb, property with the value 00356 int UsbOsc_PropertySet( int property, int value ) 00357 { 00358 switch ( property ) 00359 { 00360 case 0: 00361 Usb_SetActive( value ); 00362 break; 00363 } 00364 return CONTROLLER_OK; 00365 } 00366 00367 // Get the index Usb, property 00368 int UsbOsc_PropertyGet( int property ) 00369 { 00370 int value = 0; 00371 switch ( property ) 00372 { 00373 case 0: 00374 value = Usb_GetActive( ); 00375 break; 00376 } 00377 00378 return value; 00379 } 00380 00381 #endif // OSC 00382 00383 #endif // MAKE_CTRL_USB 00384 00385
The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.