2 LPCUSB, an USB device driver for LPC microcontrollers
3 Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
30 Bulk-only-transfer layer for mass storage.
32 This layers sits between the generic USB layers and the SCSI layer
33 and performs data transfer according to the BOT protocol.
40 #include "../usb/usbapi.h"
47 // Command block wrapper structure
53 U32 dwCBWDataTransferLength;
62 // Command status wrapper structure
74 // States of BOT state machine
89 #define CBW_SIGNATURE 0x43425355 /**< magic word in CBW */
90 #define CSW_SIGNATURE 0x53425355 /**< magic word in CSW */
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 */
96 static U32 dwTransferSize; /**< total size of data transfer */
97 static U32 dwOffset; /**< offset in current data transfer */
100 static EBotState eState;
104 // Prepares a CSW, to be sent on next bulk-IN interrupt
106 // @param [in] bStatus CSW status
108 static void mscbotSendCSW (U8 bStatus)
112 iResidue = CBW.dwCBWDataTransferLength - dwTransferSize;
115 CSW.dwCSWSignature = CSW_SIGNATURE;
116 CSW.dwCSWTag = CBW.dwCBWTag;
117 CSW.dwCSWDataResidue = MAX (iResidue, 0);
118 CSW.bmCSWStatus = bStatus;
120 DBG ("CSW: status=%x, residue=%d\n", bStatus, CSW.dwCSWDataResidue);
127 // Checks if CBW is valid and meaningful
129 // @param [in] pCBW Command block wrapper
130 // @param [in] iLen Length of CBW
132 // @return TRUE if valid and meaningful
134 static BOOL mscbotCheckCBW (TCBW *pCBW, int iLen)
141 DBG ("Invalid length (%d)\n", iLen);
145 if (pCBW->dwCBWSignature != CBW_SIGNATURE)
147 DBG ("Invalid signature %x\n", pCBW->dwCBWSignature);
154 if (pCBW->bCBWLun != 0)
156 DBG ("Invalid LUN %d\n", pCBW->bCBWLun);
160 if ((pCBW->bCBWCBLength < 1) || (pCBW->bCBWCBLength > 16))
162 DBG ("Invalid CB len %d\n", pCBW->bCBWCBLength);
171 // Local function to stall ongoing transfer
173 // Which endpoint to stall is determined by looking at the transfer
174 // direction intended by the host.
176 static void mscbotBOTStall (void)
178 if ((CBW.bmCBWFlags & 0x80) || (CBW.dwCBWDataTransferLength == 0))
179 usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE);
181 usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE);
185 // mscbotHandleDataIn
186 // Handles data from device-to-host
188 static void mscbotHandleDataIn (void)
193 // Process data for host in SCSI layer
195 if ((pbData = mscscsiHandleData (CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset)) == NULL)
198 mscbotSendCSW (STATUS_FAILED);
203 // Send data to host?
205 if (dwOffset < dwTransferSize)
207 iChunk = MIN (64, dwTransferSize - dwOffset);
208 usbHardwareEndpointWrite (MSC_BULK_IN_EP, pbData, iChunk);
215 if (dwOffset == dwTransferSize)
217 if (dwOffset != CBW.dwCBWDataTransferLength)
219 DBG ("stalling DIN");
226 mscbotSendCSW (STATUS_PASSED);
231 // mscbotHandleDataOut
232 // Handles data from host-to-device
234 static void mscbotHandleDataOut (void)
238 if (dwOffset < dwTransferSize)
240 iChunk = usbHardwareEndpointRead (MSC_BULK_OUT_EP, pbData, dwTransferSize - dwOffset);
241 pbData = mscscsiHandleData (CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset);
246 mscbotSendCSW (STATUS_FAILED);
256 if (dwOffset == dwTransferSize)
258 if (dwOffset != CBW.dwCBWDataTransferLength)
260 DBG ("stalling DOUT");
264 mscbotSendCSW (STATUS_PASSED);
269 // Resets the BOT state machine
271 void mscbotReset (void)
273 DBG ("BOT reset in state %d\n", eState);
279 // Handles the BOT bulk OUT endpoint
281 // @param [in] bEP Endpoint number
282 // @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc)
284 int mscbotBulkOut (U8 bEP, U8 bEPStatus)
286 unsigned int iLen, iChunk;
287 BOOL fHostIn, fDevIn;
290 // Ignore events on stalled EP
292 if (bEPStatus & EP_STATUS_STALLED)
299 iLen = usbHardwareEndpointRead (bEP, (U8 *)&CBW, sizeof(CBW));
301 if (!mscbotCheckCBW(&CBW, iLen))
303 usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE);
304 usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE);
309 DBG ("CBW: len=%d, flags=%x, cmd=%x, cmdlen=%d\n", CBW.dwCBWDataTransferLength, CBW.bmCBWFlags, CBW.CBWCB[0], CBW.bCBWCBLength);
313 fHostIn = ((CBW.bmCBWFlags & 0x80) != 0);
316 // Verify request. Unknown command if NULL
318 if ((pbData = mscscsiHandleCmd (CBW.CBWCB, CBW.bCBWCBLength, &iLen, &fDevIn)) == NULL)
321 mscbotSendCSW (STATUS_FAILED);
326 // Rule: if device and host disagree on direction, send CSW with status 2
328 if ((iLen > 0) && ((fHostIn && !fDevIn) || (!fHostIn && fDevIn)))
330 DBG ("Host and device disagree on direction\n");
332 mscbotSendCSW (STATUS_PHASE_ERR);
337 // Rule: if D > H, send CSW with status 2
339 if (iLen > CBW.dwCBWDataTransferLength)
341 DBG ("Negative residue\n");
343 mscbotSendCSW (STATUS_PHASE_ERR);
347 if (((dwTransferSize = iLen) == 0) || fDevIn)
350 // Data from device-to-host
353 mscbotHandleDataIn ();
358 // Data from host-to-device
367 mscbotHandleDataOut ();
374 iChunk = usbHardwareEndpointRead (bEP, NULL, 0);
375 DBG ("Phase error in state %d, %d bytes\n", eState, iChunk);
382 usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE);
388 DBG ("Invalid state %d\n", eState);
398 // Handles the BOT bulk IN endpoint
400 // @param [in] bEP Endpoint number
401 // @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc)
403 int mscbotBulkIn (U8 bEP __attribute__ ((unused)), U8 bEPStatus)
405 if (bEPStatus & EP_STATUS_STALLED)
411 // Ignore possibly old ACKs
418 mscbotHandleDataIn ();
422 // Wait for an IN token, then send the CSW
425 usbHardwareEndpointWrite (MSC_BULK_IN_EP, (U8 *)&CSW, 13);
433 usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE);
437 DBG ("Invalid state %d\n", eState);