v0.1 board believed to be reading Vbat, Pressure, and X/Y/Z correctly now,
[fw/openalt] / usbmass / mscscsi.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 /**
29   @file
30
31   This is the SCSI layer of the USB mass storage application example.
32   This layer depends directly on the blockdev layer.
33   
34   Windows peculiarities:
35   * Size of REQUEST SENSE CDB is 12 bytes instead of expected 6
36   * Windows requires VERIFY(10) command to do a format.
37     This command is not mandatory in the SBC/SBC-2 specification.
38 */
39
40 #include "FreeRTOS.h"
41
42 #include <string.h>   // memcpy
43
44 #include "mscdebug.h"
45 #include "mscblock.h"
46 #include "mscscsi.h"
47
48 //
49 //
50 //
51 #define BLOCKSIZE   512
52
53 //
54 //  SBC2 mandatory SCSI commands
55 //
56 #define SCSI_CMD_TEST_UNIT_READY  0x00
57 #define SCSI_CMD_REQUEST_SENSE    0x03
58 #define SCSI_CMD_FORMAT_UNIT      0x04
59 #define SCSI_CMD_READ_6           0x08  /* not implemented yet */
60 #define SCSI_CMD_INQUIRY          0x12
61 #define SCSI_CMD_SEND_DIAGNOSTIC  0x1D  /* not implemented yet */
62 #define SCSI_CMD_READ_CAPACITY_10 0x25
63 #define SCSI_CMD_READ_10          0x28
64 #define SCSI_CMD_REPORT_LUNS      0xa0  /* not implemented yet */
65
66 //
67 //  SBC2 optional SCSI commands
68 //
69 #define SCSI_CMD_WRITE_6          0x0a  /* not implemented yet */
70 #define SCSI_CMD_WRITE_10         0x2a
71 #define SCSI_CMD_VERIFY_10        0x2f  /* required for windows format */
72
73 //
74 //  Sense codes
75 //
76 #define WRITE_ERROR           0x030C00
77 #define READ_ERROR            0x031100
78 #define INVALID_CMD_OPCODE    0x052000
79 #define INVALID_FIELD_IN_CDB  0x052400
80
81 //
82 //  Sense code, which is set on error conditions
83 //  Hex: 00aabbcc, where aa=KEY, bb=ASC, cc=ASCQ
84 //
85 static U32 dwSense;  
86
87 static const U8   abInquiry[] = 
88 {
89   0x00,   // PDT = direct-access device
90   0x80,   // removeable medium bit = set
91   0x05,   // version = complies to SPC3
92   0x02,   // response data format = SPC3
93   0x1F,   // additional length
94   0x00,
95   0x00,
96   0x00,
97   'L','P','C','U','S','B',' ',' ',  // vendor
98   'M','a','s','s',' ','s','t','o',  // product
99   'r','a','g','e',' ',' ',' ',' ',
100   '0','.','1',' '                   // revision
101 };
102
103 //
104 //  Data for "request sense" command. The 0xFF are filled in later
105 //
106 static const U8 abSense[] = { 0x70, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0A, 
107                               0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
108                               0x00, 0x00 };
109
110 //
111 //  Buffer for holding one block of disk data
112 //
113 static U8 abBlockBuf [512];
114
115 typedef struct 
116 {
117   U8    bOperationCode;
118   U8    abLBA [3];
119   U8    bLength;
120   U8    bControl;
121
122 TCDB6;
123
124 //
125 //
126 //
127 void mscscsiReset (void)
128 {
129   dwSense = 0;
130 }
131
132 //
133 //  SCSIHandleCmd
134 //    Verifies a SCSI CDB and indicates the direction and amount of data
135 //    that the device wants to transfer.
136 //    
137 //  If this call fails, a sense code is set in dwSense.
138 //
139 //  IN  pbCDB     - Command data block
140 //      iCDBLen   - Command data block len
141 //  OUT *piRspLen - Length of intended response data:
142 //      *pfDevIn  - TRUE if data is transferred from device-to-host
143 //  
144 //  Returns a pointer to the data exchange buffer if successful,
145 //  return NULL otherwise.
146 //
147 U8 *mscscsiHandleCmd (U8 *pbCDB, U8 iCDBLen, unsigned int *piRspLen, BOOL *pfDevIn)
148 {
149   static const U8 aiCDBLen [] = {6, 10, 10, 0, 16, 12, 0, 0};
150   int   i;
151   TCDB6 *pCDB;
152   U32   dwLen, dwLBA;
153   U8    bGroupCode;
154
155   pCDB = (TCDB6 *) pbCDB;
156
157   // default direction is from device to host
158   *pfDevIn = TRUE;
159
160   // check CDB length
161   bGroupCode = (pCDB->bOperationCode >> 5) & 0x7;
162
163   if (iCDBLen < aiCDBLen[bGroupCode]) 
164   {
165     DBG("Invalid CBD len (expected %d)!\n", aiCDBLen[bGroupCode]);
166     return NULL;
167   }
168
169   switch (pCDB->bOperationCode) 
170   {
171     case SCSI_CMD_TEST_UNIT_READY :
172       DBG ("TEST UNIT READY\n");
173       *piRspLen = 0;
174       break;
175
176     case SCSI_CMD_REQUEST_SENSE :
177       DBG ("REQUEST SENSE (%06X)\n", dwSense);
178       *piRspLen = MIN (18, pCDB->bLength);
179       break;
180
181     case SCSI_CMD_FORMAT_UNIT :
182       DBG ("FORMAT UNIT %02X\n", pbCDB[1]);
183       *piRspLen = 0;
184       break;
185
186     case SCSI_CMD_INQUIRY :
187       DBG ("INQUIRY\n");
188       *piRspLen = MIN (36, pCDB->bLength);
189       break;
190
191     case SCSI_CMD_READ_CAPACITY_10 :
192       DBG ("READ CAPACITY\n");
193       *piRspLen = 8;
194       break;
195
196     case SCSI_CMD_READ_10 :
197       dwLBA = (pbCDB [2] << 24) | (pbCDB [3] << 16) | (pbCDB [4] << 8) | (pbCDB [5]);
198       dwLen = (pbCDB [7] << 8) | pbCDB [8];
199       DBG ("READ10, LBA=%d, len=%d\n", dwLBA, dwLen);
200       *piRspLen = dwLen * BLOCKSIZE;
201       break;
202
203     case SCSI_CMD_WRITE_10 :
204       dwLBA = (pbCDB [2] << 24) | (pbCDB [3] << 16) | (pbCDB [4] << 8) | (pbCDB [5]);
205       dwLen = (pbCDB [7] << 8) | pbCDB [8];
206       DBG ("WRITE10, LBA=%d, len=%d\n", dwLBA, dwLen);
207       *piRspLen = dwLen * BLOCKSIZE;
208       *pfDevIn = FALSE;
209       break;
210
211     case SCSI_CMD_VERIFY_10 :
212       DBG ("VERIFY10\n");
213       if ((pbCDB [1] & (1 << 1)) != 0) 
214       {
215         DBG ("BYTCHK not supported\n");
216         return NULL;
217       }
218       *piRspLen = 0;
219       break;
220
221     default :
222       DBG ("Unhandled SCSI: ");    
223       for (i = 0; i < iCDBLen; i++)
224         DBG (" %02X", pbCDB[i]);
225       DBG ("\n");
226
227       dwSense = INVALID_CMD_OPCODE;
228       *piRspLen = 0;
229       return NULL;
230   }
231
232   return abBlockBuf;
233 }
234
235 //
236 //  SCSIHandleData
237 //    Handles a block of SCSI data.
238 //    
239 //  IN      pbCDB    . Command data block
240 //          iCDBLen  - Command data block len
241 //  IN/OUT  pbData   - Data buffer
242 //  IN      dwOffset - Offset in data
243 //  
244 //  Returns a pointer to the next data to be exchanged if successful,
245 //  returns NULL otherwise.
246 //
247 U8 *mscscsiHandleData (U8 *pbCDB, U8 iCDBLen __attribute__ ((unused)), U8 *pbData, U32 dwOffset)
248 {
249   TCDB6 *pCDB;
250   U32   dwLBA;
251   U32   dwBufPos, dwBlockNr;
252   U32   dwDevSize, dwMaxBlock;
253
254   pCDB = (TCDB6 *)pbCDB;
255
256   switch (pCDB->bOperationCode) 
257   {
258     case SCSI_CMD_TEST_UNIT_READY :
259       if (dwSense != 0) 
260         return NULL;
261       break;
262
263     case SCSI_CMD_REQUEST_SENSE :
264       memcpy (pbData, abSense, 18);
265       pbData [2]  = (dwSense >> 16) & 0xff;
266       pbData [12] = (dwSense >> 8) & 0xff;
267       pbData [13] = (dwSense >> 0) & 0xff;
268       dwSense = 0;
269       break;
270
271     case SCSI_CMD_FORMAT_UNIT :
272       break;
273
274     case SCSI_CMD_INQUIRY :
275       memcpy (pbData, abInquiry, sizeof(abInquiry));
276       break;
277
278     case SCSI_CMD_READ_CAPACITY_10:
279       mscblockGetSize (&dwDevSize);
280       dwMaxBlock = (dwDevSize - 1) / 512;
281       pbData [0] = (dwMaxBlock >> 24) & 0xff;
282       pbData [1] = (dwMaxBlock >> 16) & 0xff;
283       pbData [2] = (dwMaxBlock >> 8) & 0xff;
284       pbData [3] = (dwMaxBlock >> 0) & 0xff;
285       pbData [4] = (BLOCKSIZE >> 24) & 0xff;
286       pbData [5] = (BLOCKSIZE >> 16) & 0xff;
287       pbData [6] = (BLOCKSIZE >> 8) & 0xff;
288       pbData [7] = (BLOCKSIZE >> 0) & 0xff;
289       break;
290
291     case SCSI_CMD_READ_10 :
292       dwLBA = (pbCDB [2] << 24) | (pbCDB [3] << 16) | (pbCDB [4] << 8) | (pbCDB [5]);
293       dwBufPos = (dwOffset & (BLOCKSIZE - 1));
294       if (dwBufPos == 0) 
295       {
296         dwBlockNr = dwLBA + (dwOffset / BLOCKSIZE);
297         DBG ("R");
298         if (mscblockRead(dwBlockNr, abBlockBuf) < 0) 
299         {
300           dwSense = READ_ERROR;
301           DBG ("mscblockRead failed\n");
302           return NULL;
303         }
304       }
305
306       return abBlockBuf + dwBufPos ;
307
308     case SCSI_CMD_WRITE_10:
309       dwLBA = (pbCDB[2] << 24) | (pbCDB[3] << 16) | (pbCDB[4] << 8) | (pbCDB[5]);
310
311       dwBufPos = ((dwOffset + 64) & (BLOCKSIZE - 1));
312       if (dwBufPos == 0) 
313       {
314         dwBlockNr = dwLBA + (dwOffset / BLOCKSIZE);
315         DBG ("W");
316         if (mscblockWrite (dwBlockNr, abBlockBuf) < 0) 
317         {
318           dwSense = WRITE_ERROR;
319           DBG ("mscblockWrite failed\n");
320           return NULL;
321         }
322       }
323       return abBlockBuf + dwBufPos;
324
325     case SCSI_CMD_VERIFY_10 :
326       break;
327
328     default :
329       dwSense = INVALID_CMD_OPCODE;
330       return NULL;
331   }
332
333   return abBlockBuf;
334 }