v0.1 board believed to be reading Vbat, Pressure, and X/Y/Z correctly now,
[fw/openalt] / usbmass / mscbot.c
1 /*
2   LPCUSB, an USB device driver for LPC microcontrollers 
3   Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7
8   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.
15
16   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.
26 */
27
28 /** @file
29
30   Bulk-only-transfer layer for mass storage.
31   
32   This layers sits between the generic USB layers and the SCSI layer
33   and performs data transfer according to the BOT protocol.
34 */
35
36 #include "FreeRTOS.h"
37
38 #include <string.h>
39
40 #include "../usb/usbapi.h"
41
42 #include "mscdebug.h"
43 #include "mscbot.h"
44 #include "mscscsi.h"
45
46 //
47 // Command block wrapper structure
48 //
49 typedef struct 
50 {
51   U32   dwCBWSignature;
52   U32   dwCBWTag;
53   U32   dwCBWDataTransferLength;
54   U8    bmCBWFlags;
55   U8    bCBWLun;
56   U8    bCBWCBLength;
57   U8    CBWCB[16];
58
59 TCBW;
60
61 //
62 //  Command status wrapper structure 
63 //
64 typedef struct 
65 {
66   U32   dwCSWSignature;
67   U32   dwCSWTag;
68   U32   dwCSWDataResidue;
69   U8    bmCSWStatus;
70
71 TCSW;
72
73 //
74 //  States of BOT state machine 
75 //
76 typedef enum 
77 {
78   eCBW,
79   eDataOut,
80   eDataIn,
81   eCSW,
82   eStalled
83
84 EBotState;
85
86 //
87 //
88 //
89 #define CBW_SIGNATURE 0x43425355    /**< magic word in CBW */
90 #define CSW_SIGNATURE 0x53425355    /**< magic word in CSW */
91
92 #define STATUS_PASSED   0x00    /**< successful transfer */
93 #define STATUS_FAILED   0x01    /**< failed transfer */
94 #define STATUS_PHASE_ERR  0x02    /**< conflict between host and device */
95
96 static U32 dwTransferSize;   /**< total size of data transfer */
97 static U32 dwOffset;     /**< offset in current data transfer */
98 static TCBW CBW;
99 static TCSW CSW;
100 static EBotState  eState;
101 static U8 *pbData;
102
103 //
104 //  Prepares a CSW, to be sent on next bulk-IN interrupt
105 //    
106 //  @param [in] bStatus CSW status
107 //
108 static void mscbotSendCSW (U8 bStatus)
109 {
110   int iResidue;
111   
112   iResidue = CBW.dwCBWDataTransferLength - dwTransferSize;
113   
114   // construct CSW
115   CSW.dwCSWSignature = CSW_SIGNATURE;
116   CSW.dwCSWTag = CBW.dwCBWTag;
117   CSW.dwCSWDataResidue = MAX (iResidue, 0);
118   CSW.bmCSWStatus = bStatus;
119
120   DBG ("CSW: status=%x, residue=%d\n", bStatus, CSW.dwCSWDataResidue);
121
122   // next state
123   eState = eCSW;
124 }
125
126 //
127 //  Checks if CBW is valid and meaningful
128 //    
129 //  @param [in] pCBW  Command block wrapper
130 //  @param [in] iLen  Length of CBW
131 //      
132 //  @return TRUE if valid and meaningful
133 //
134 static BOOL mscbotCheckCBW (TCBW *pCBW, int iLen)
135 {
136   //
137   // CBW valid?
138   //
139   if (iLen != 31) 
140   {
141     DBG ("Invalid length (%d)\n", iLen);
142     return FALSE;
143   }
144
145   if (pCBW->dwCBWSignature != CBW_SIGNATURE) 
146   {
147     DBG ("Invalid signature %x\n", pCBW->dwCBWSignature);
148     return FALSE;
149   }
150
151   //
152   // CBW meaningful?
153   //
154   if (pCBW->bCBWLun != 0) 
155   {
156     DBG ("Invalid LUN %d\n", pCBW->bCBWLun);
157     return FALSE;
158   }
159
160   if ((pCBW->bCBWCBLength < 1) || (pCBW->bCBWCBLength > 16)) 
161   {
162     DBG ("Invalid CB len %d\n", pCBW->bCBWCBLength);
163     return FALSE;
164   }
165
166   return TRUE;
167 }
168
169 //
170 //  mscbotBOTStall
171 //    Local function to stall ongoing transfer
172 //    
173 //  Which endpoint to stall is determined by looking at the transfer
174 //  direction intended by the host.
175 //
176 static void mscbotBOTStall (void)
177 {
178   if ((CBW.bmCBWFlags & 0x80) || (CBW.dwCBWDataTransferLength == 0)) 
179     usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE);
180   else 
181     usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE);
182 }
183
184 //
185 //  mscbotHandleDataIn
186 //    Handles data from device-to-host
187 //    
188 static void mscbotHandleDataIn (void)
189 {
190   int iChunk;
191   
192   //
193   //  Process data for host in SCSI layer
194   //
195   if ((pbData = mscscsiHandleData (CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset)) == NULL)
196   {
197     mscbotBOTStall ();
198     mscbotSendCSW (STATUS_FAILED);
199     return;
200   }
201
202   //
203   //  Send data to host?
204   //
205   if (dwOffset < dwTransferSize) 
206   {
207     iChunk = MIN (64, dwTransferSize - dwOffset);
208     usbHardwareEndpointWrite (MSC_BULK_IN_EP, pbData, iChunk);
209     dwOffset += iChunk;
210   }
211   
212   //
213   // are we done now?
214   //
215   if (dwOffset == dwTransferSize) 
216   {
217     if (dwOffset != CBW.dwCBWDataTransferLength) 
218     {
219       DBG ("stalling DIN");
220       mscbotBOTStall ();
221     }
222
223     //
224     //  Done
225     //
226     mscbotSendCSW (STATUS_PASSED);
227   }
228 }
229
230 //
231 //  mscbotHandleDataOut
232 //    Handles data from host-to-device
233 //
234 static void mscbotHandleDataOut (void)
235 {
236   int iChunk;
237
238   if (dwOffset < dwTransferSize) 
239   {
240     iChunk = usbHardwareEndpointRead (MSC_BULK_OUT_EP, pbData, dwTransferSize - dwOffset);
241     pbData = mscscsiHandleData (CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset);
242
243     if (pbData == NULL) 
244     {
245       mscbotBOTStall ();
246       mscbotSendCSW (STATUS_FAILED);
247       return;
248     }
249
250     dwOffset += iChunk;
251   }
252
253   //
254   //  Are we done now?
255   //
256   if (dwOffset == dwTransferSize) 
257   {
258     if (dwOffset != CBW.dwCBWDataTransferLength) 
259     {
260       DBG ("stalling DOUT");
261       mscbotBOTStall ();
262     }
263
264     mscbotSendCSW (STATUS_PASSED);
265   }
266 }
267     
268 //
269 //  Resets the BOT state machine
270 //
271 void mscbotReset (void)
272 {
273   DBG ("BOT reset in state %d\n", eState);
274   eState = eCBW;
275   mscscsiReset ();
276 }
277
278 //
279 //   Handles the BOT bulk OUT endpoint
280 //     
281 //   @param [in] bEP     Endpoint number
282 //   @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc)
283 //     
284 int mscbotBulkOut (U8 bEP, U8 bEPStatus)
285 {
286   unsigned int iLen, iChunk;
287   BOOL fHostIn, fDevIn;
288
289   //
290   //  Ignore events on stalled EP
291   //
292   if (bEPStatus & EP_STATUS_STALLED)
293     return 0;
294
295   switch (eState) 
296   {
297     case eCBW:
298       {
299         iLen = usbHardwareEndpointRead (bEP, (U8 *)&CBW, sizeof(CBW));
300
301         if (!mscbotCheckCBW(&CBW, iLen)) 
302         {
303           usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE);
304           usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE);
305           eState = eStalled;
306           break;
307         }
308
309         DBG ("CBW: len=%d, flags=%x, cmd=%x, cmdlen=%d\n", CBW.dwCBWDataTransferLength, CBW.bmCBWFlags, CBW.CBWCB[0], CBW.bCBWCBLength);
310
311         dwOffset = 0;
312         dwTransferSize = 0;
313         fHostIn = ((CBW.bmCBWFlags & 0x80) != 0);
314
315         //
316         //  Verify request.  Unknown command if NULL
317         //
318         if ((pbData = mscscsiHandleCmd (CBW.CBWCB, CBW.bCBWCBLength, &iLen, &fDevIn)) == NULL)
319         {
320           mscbotBOTStall ();
321           mscbotSendCSW (STATUS_FAILED);
322           break;      
323         }
324
325         //
326         //  Rule: if device and host disagree on direction, send CSW with status 2
327         //
328         if ((iLen > 0) && ((fHostIn && !fDevIn) || (!fHostIn && fDevIn))) 
329         {
330           DBG ("Host and device disagree on direction\n");
331           mscbotBOTStall ();
332           mscbotSendCSW (STATUS_PHASE_ERR);
333           break;
334         }
335
336         //
337         //  Rule: if D > H, send CSW with status 2
338         //
339         if (iLen > CBW.dwCBWDataTransferLength) 
340         {
341           DBG ("Negative residue\n");
342           mscbotBOTStall ();
343           mscbotSendCSW (STATUS_PHASE_ERR);
344           break;
345         }
346
347         if (((dwTransferSize = iLen) == 0) || fDevIn) 
348         {
349           //
350           //  Data from device-to-host
351           //
352           eState = eDataIn;
353           mscbotHandleDataIn ();
354         }
355         else 
356         {
357           //
358           //  Data from host-to-device
359           //
360           eState = eDataOut;
361         }
362       }
363       break;
364
365     case eDataOut:
366       {
367         mscbotHandleDataOut ();
368       }
369       break;
370
371     case eDataIn:
372     case eCSW:
373       {
374         iChunk = usbHardwareEndpointRead (bEP, NULL, 0);
375         DBG ("Phase error in state %d, %d bytes\n", eState, iChunk);
376         eState = eCBW;
377       }
378       break;
379
380     case eStalled:
381       {
382         usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE);
383       }
384       break;
385
386     default:
387       {
388         DBG ("Invalid state %d\n", eState);
389         ASSERT (FALSE);
390       }
391       break;
392   }
393
394   return 0;
395 }
396
397 //
398 //   Handles the BOT bulk IN endpoint
399 //     
400 //   @param [in] bEP     Endpoint number
401 //   @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc)
402 //     
403 int mscbotBulkIn (U8 bEP __attribute__ ((unused)), U8 bEPStatus)
404 {
405   if (bEPStatus & EP_STATUS_STALLED)
406     return 0;
407
408   switch (eState) 
409   {
410     //
411     //  Ignore possibly old ACKs
412     //
413     case eCBW:
414     case eDataOut:
415       break;
416
417     case eDataIn:
418       mscbotHandleDataIn ();
419       break;
420
421     //
422     //  Wait for an IN token, then send the CSW
423     //
424     case eCSW:
425       usbHardwareEndpointWrite (MSC_BULK_IN_EP, (U8 *)&CSW, 13);
426       eState = eCBW;
427       break;
428
429     //
430     //  Keep stalling
431     //
432     case eStalled:
433       usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE);
434       break;
435
436     default:
437       DBG ("Invalid state %d\n", eState);
438       ASSERT (FALSE);
439       break;
440   }
441
442   return 0;
443 }