re-enabling serial
[clinton/Smoothieware.git] / gcc4mbed / samples / MSTest / USBDevice / USBMSD / USBMSD.cpp
CommitLineData
cd011f58
AW
1/* Copyright (c) 2010-2011 mbed.org, MIT License\r
2*\r
3* Permission is hereby granted, free of charge, to any person obtaining a copy of this software\r
4* and associated documentation files (the "Software"), to deal in the Software without\r
5* restriction, including without limitation the rights to use, copy, modify, merge, publish,\r
6* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the\r
7* Software is furnished to do so, subject to the following conditions:\r
8*\r
9* The above copyright notice and this permission notice shall be included in all copies or\r
10* substantial portions of the Software.\r
11*\r
12* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING\r
13* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
14* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\r
15* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
16* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
17*/\r
18\r
19#include "stdint.h"\r
20#include "USBMSD.h"\r
21\r
22#define DISK_OK 0x00\r
23#define NO_INIT 0x01\r
24#define NO_DISK 0x02\r
25#define WRITE_PROTECT 0x04\r
26\r
27#define CBW_Signature 0x43425355\r
28#define CSW_Signature 0x53425355\r
29\r
30// SCSI Commands\r
31#define TEST_UNIT_READY 0x00\r
32#define REQUEST_SENSE 0x03\r
33#define FORMAT_UNIT 0x04\r
34#define INQUIRY 0x12\r
35#define MODE_SELECT6 0x15\r
36#define MODE_SENSE6 0x1A\r
37#define START_STOP_UNIT 0x1B\r
38#define MEDIA_REMOVAL 0x1E\r
39#define READ_FORMAT_CAPACITIES 0x23\r
40#define READ_CAPACITY 0x25\r
41#define READ10 0x28\r
42#define WRITE10 0x2A\r
43#define VERIFY10 0x2F\r
44#define READ12 0xA8\r
45#define WRITE12 0xAA\r
46#define MODE_SELECT10 0x55\r
47#define MODE_SENSE10 0x5A\r
48\r
49// MSC class specific requests\r
50#define MSC_REQUEST_RESET 0xFF\r
51#define MSC_REQUEST_GET_MAX_LUN 0xFE\r
52\r
53#define DEFAULT_CONFIGURATION (1)\r
54\r
55// max packet size\r
56#define MAX_PACKET MAX_PACKET_SIZE_EPBULK\r
57\r
58// CSW Status\r
59enum Status {\r
60 CSW_PASSED,\r
61 CSW_FAILED,\r
62 CSW_ERROR,\r
63};\r
64\r
65\r
66USBMSD::USBMSD(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release) {\r
67}\r
68\r
69\r
70\r
71// Called in ISR context to process a class specific request\r
72bool USBMSD::USBCallback_request(void) {\r
73\r
74 bool success = false;\r
75 CONTROL_TRANSFER * transfer = getTransferPtr();\r
76 static uint8_t maxLUN[1] = {0};\r
77\r
78 if (transfer->setup.bmRequestType.Type == CLASS_TYPE) {\r
79 switch (transfer->setup.bRequest) {\r
80 case MSC_REQUEST_RESET:\r
81 reset();\r
82 success = true;\r
83 break;\r
84 case MSC_REQUEST_GET_MAX_LUN:\r
85 transfer->remaining = 1;\r
86 transfer->ptr = maxLUN;\r
87 transfer->direction = DEVICE_TO_HOST;\r
88 success = true;\r
89 break;\r
90 default:\r
91 break;\r
92 }\r
93 }\r
94\r
95 return success;\r
96}\r
97\r
98\r
99bool USBMSD::connect() {\r
100\r
101 //disk initialization\r
102 if (disk_status() & NO_INIT) {\r
103 if (disk_initialize()) {\r
104 return false;\r
105 }\r
106 }\r
107\r
108 // get number of blocks\r
109 BlockCount = disk_sectors();\r
110\r
111 // get memory size\r
112 MemorySize = disk_size();\r
113\r
114 if (BlockCount >= 0) {\r
115 BlockSize = MemorySize / BlockCount;\r
116 if (BlockSize != 0) {\r
117 page = (uint8_t *)malloc(BlockSize * sizeof(uint8_t));\r
118 if (page == NULL)\r
119 return false;\r
120 }\r
121 } else {\r
122 return false;\r
123 }\r
124\r
125 //connect the device\r
126 USBDevice::connect();\r
127 return true;\r
128}\r
129\r
130\r
131void USBMSD::reset() {\r
132 stage = READ_CBW;\r
133}\r
134\r
135\r
136// Called in ISR context called when a data is received\r
137bool USBMSD::EP2_OUT_callback() {\r
138 uint16_t size = 0;\r
139 uint8_t buf[MAX_PACKET_SIZE_EPBULK];\r
140 readEP(EPBULK_OUT, buf, &size, MAX_PACKET_SIZE_EPBULK);\r
141 switch (stage) {\r
142 // the device has to decode the CBW received\r
143 case READ_CBW:\r
144 CBWDecode(buf, size);\r
145 break;\r
146\r
147 // the device has to receive data from the host\r
148 case PROCESS_CBW:\r
149 switch (cbw.CB[0]) {\r
150 case WRITE10:\r
151 case WRITE12:\r
152 memoryWrite(buf, size);\r
153 break;\r
154 case VERIFY10:\r
155 memoryVerify(buf, size);\r
156 break;\r
157 }\r
158 break;\r
159\r
160 // an error has occured: stall endpoint and send CSW\r
161 default:\r
162 stallEndpoint(EPBULK_OUT);\r
163 csw.Status = CSW_ERROR;\r
164 sendCSW();\r
165 break;\r
166 }\r
167\r
168 //reactivate readings on the OUT bulk endpoint\r
169 readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);\r
170 return true;\r
171}\r
172\r
173// Called in ISR context when a data has been transferred\r
174bool USBMSD::EP2_IN_callback() {\r
175 switch (stage) {\r
176\r
177 // the device has to send data to the host\r
178 case PROCESS_CBW:\r
179 switch (cbw.CB[0]) {\r
180 case READ10:\r
181 case READ12:\r
182 memoryRead();\r
183 break;\r
184 }\r
185 break;\r
186\r
187 //the device has to send a CSW\r
188 case SEND_CSW:\r
189 sendCSW();\r
190 break;\r
191\r
192 // an error has occured\r
193 case ERROR:\r
194 stallEndpoint(EPBULK_IN);\r
195 sendCSW();\r
196 break;\r
197\r
198 // the host has received the CSW -> we wait a CBW\r
199 case WAIT_CSW:\r
200 stage = READ_CBW;\r
201 break;\r
202 }\r
203 return true;\r
204}\r
205\r
206\r
207void USBMSD::memoryWrite (uint8_t * buf, uint16_t size) {\r
208\r
209 if ((addr + size) > MemorySize) {\r
210 size = MemorySize - addr;\r
211 stage = ERROR;\r
212 stallEndpoint(EPBULK_OUT);\r
213 }\r
214\r
215 // we fill an array in RAM of 1 block before writing it in memory\r
216 for (int i = 0; i < size; i++)\r
217 page[addr%BlockSize + i] = buf[i];\r
218\r
219 // if the array is filled, write it in memory\r
220 if (!((addr + size)%BlockSize)) {\r
221 if (!(disk_status() & WRITE_PROTECT)) {\r
222 disk_write((const char *)page, addr/BlockSize);\r
223 }\r
224 }\r
225\r
226 addr += size;\r
227 length -= size;\r
228 csw.DataResidue -= size;\r
229\r
230 if ((!length) || (stage != PROCESS_CBW)) {\r
231 csw.Status = (stage == ERROR) ? CSW_FAILED : CSW_PASSED;\r
232 sendCSW();\r
233 }\r
234}\r
235\r
236void USBMSD::memoryVerify (uint8_t * buf, uint16_t size) {\r
237 uint32_t n;\r
238\r
239 if ((addr + size) > MemorySize) {\r
240 size = MemorySize - addr;\r
241 stage = ERROR;\r
242 stallEndpoint(EPBULK_OUT);\r
243 }\r
244\r
245 // beginning of a new block -> load a whole block in RAM\r
246 if (!(addr%BlockSize))\r
247 disk_read((char *)page, addr/BlockSize);\r
248\r
249 // info are in RAM -> no need to re-read memory\r
250 for (n = 0; n < size; n++) {\r
251 if (page[addr%BlockSize + n] != buf[n]) {\r
252 memOK = false;\r
253 break;\r
254 }\r
255 }\r
256\r
257 addr += size;\r
258 length -= size;\r
259 csw.DataResidue -= size;\r
260\r
261 if ( !length || (stage != PROCESS_CBW)) {\r
262 csw.Status = (memOK && (stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED;\r
263 sendCSW();\r
264 }\r
265}\r
266\r
267\r
268bool USBMSD::inquiryRequest (void) {\r
269 uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01,\r
270 36 - 4, 0x80, 0x00, 0x00,\r
271 'M', 'B', 'E', 'D', '.', 'O', 'R', 'G',\r
272 'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ',\r
273 '1', '.', '0', ' ',\r
274 };\r
275 if (!write(inquiry, sizeof(inquiry))) {\r
276 return false;\r
277 }\r
278 return true;\r
279}\r
280\r
281\r
282bool USBMSD::readFormatCapacity() {\r
283 uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08,\r
284 (BlockCount >> 24) & 0xff,\r
285 (BlockCount >> 16) & 0xff,\r
286 (BlockCount >> 8) & 0xff,\r
287 (BlockCount >> 0) & 0xff,\r
288\r
289 0x02,\r
290 (BlockSize >> 16) & 0xff,\r
291 (BlockSize >> 8) & 0xff,\r
292 (BlockSize >> 0) & 0xff,\r
293 };\r
294 if (!write(capacity, sizeof(capacity))) {\r
295 return false;\r
296 }\r
297 return true;\r
298}\r
299\r
300\r
301bool USBMSD::readCapacity (void) {\r
302 uint8_t capacity[] = {\r
303 ((BlockCount - 1) >> 24) & 0xff,\r
304 ((BlockCount - 1) >> 16) & 0xff,\r
305 ((BlockCount - 1) >> 8) & 0xff,\r
306 ((BlockCount - 1) >> 0) & 0xff,\r
307\r
308 (BlockSize >> 24) & 0xff,\r
309 (BlockSize >> 16) & 0xff,\r
310 (BlockSize >> 8) & 0xff,\r
311 (BlockSize >> 0) & 0xff,\r
312 };\r
313 if (!write(capacity, sizeof(capacity))) {\r
314 return false;\r
315 }\r
316 return true;\r
317}\r
318\r
319bool USBMSD::write (uint8_t * buf, uint16_t size) {\r
320\r
321 if (size >= cbw.DataLength) {\r
322 size = cbw.DataLength;\r
323 }\r
324 stage = SEND_CSW;\r
325\r
326 if (!writeNB(EPBULK_IN, buf, size, MAX_PACKET_SIZE_EPBULK)) {\r
327 return false;\r
328 }\r
329\r
330 csw.DataResidue -= size;\r
331 csw.Status = CSW_PASSED;\r
332 return true;\r
333}\r
334\r
335\r
336bool USBMSD::modeSense6 (void) {\r
337 uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 };\r
338 if (!write(sense6, sizeof(sense6))) {\r
339 return false;\r
340 }\r
341 return true;\r
342}\r
343\r
344void USBMSD::sendCSW() {\r
345 csw.Signature = CSW_Signature;\r
346 writeNB(EPBULK_IN, (uint8_t *)&csw, sizeof(CSW), MAX_PACKET_SIZE_EPBULK);\r
347 stage = WAIT_CSW;\r
348}\r
349\r
350bool USBMSD::requestSense (void) {\r
351 uint8_t request_sense[] = {\r
352 0x70,\r
353 0x00,\r
354 0x05, // Sense Key: illegal request\r
355 0x00,\r
356 0x00,\r
357 0x00,\r
358 0x00,\r
359 0x0A,\r
360 0x00,\r
361 0x00,\r
362 0x00,\r
363 0x00,\r
364 0x30,\r
365 0x01,\r
366 0x00,\r
367 0x00,\r
368 0x00,\r
369 0x00,\r
370 };\r
371\r
372 if (!write(request_sense, sizeof(request_sense))) {\r
373 return false;\r
374 }\r
375\r
376 return true;\r
377}\r
378\r
379void USBMSD::fail() {\r
380 csw.Status = CSW_FAILED;\r
381 sendCSW();\r
382}\r
383\r
384\r
385void USBMSD::CBWDecode(uint8_t * buf, uint16_t size) {\r
386 if (size == sizeof(cbw)) {\r
387 memcpy((uint8_t *)&cbw, buf, size);\r
388 if (cbw.Signature == CBW_Signature) {\r
389 csw.Tag = cbw.Tag;\r
390 csw.DataResidue = cbw.DataLength;\r
391 if ((cbw.CBLength < 1) || (cbw.CBLength > 16) ) {\r
392 fail();\r
393 } else {\r
394 switch (cbw.CB[0]) {\r
395 case TEST_UNIT_READY:\r
396 testUnitReady();\r
397 break;\r
398 case REQUEST_SENSE:\r
399 requestSense();\r
400 break;\r
401 case INQUIRY:\r
402 inquiryRequest();\r
403 break;\r
404 case MODE_SENSE6:\r
405 modeSense6();\r
406 break;\r
407 case READ_FORMAT_CAPACITIES:\r
408 readFormatCapacity();\r
409 break;\r
410 case READ_CAPACITY:\r
411 readCapacity();\r
412 break;\r
413 case READ10:\r
414 case READ12:\r
415 if (infoTransfer()) {\r
416 if ((cbw.Flags & 0x80)) {\r
417 stage = PROCESS_CBW;\r
418 memoryRead();\r
419 } else {\r
420 stallEndpoint(EPBULK_OUT);\r
421 csw.Status = CSW_ERROR;\r
422 sendCSW();\r
423 }\r
424 }\r
425 break;\r
426 case WRITE10:\r
427 case WRITE12:\r
428 if (infoTransfer()) {\r
429 if (!(cbw.Flags & 0x80)) {\r
430 stage = PROCESS_CBW;\r
431 } else {\r
432 stallEndpoint(EPBULK_IN);\r
433 csw.Status = CSW_ERROR;\r
434 sendCSW();\r
435 }\r
436 }\r
437 break;\r
438 case VERIFY10:\r
439 if (!(cbw.CB[1] & 0x02)) {\r
440 csw.Status = CSW_PASSED;\r
441 sendCSW();\r
442 break;\r
443 }\r
444 if (infoTransfer()) {\r
445 if (!(cbw.Flags & 0x80)) {\r
446 stage = PROCESS_CBW;\r
447 memOK = true;\r
448 } else {\r
449 stallEndpoint(EPBULK_IN);\r
450 csw.Status = CSW_ERROR;\r
451 sendCSW();\r
452 }\r
453 }\r
454 break;\r
455 default:\r
456 fail();\r
457 break;\r
458 }\r
459 }\r
460 }\r
461 }\r
462}\r
463\r
464void USBMSD::testUnitReady (void) {\r
465\r
466 if (cbw.DataLength != 0) {\r
467 if ((cbw.Flags & 0x80) != 0) {\r
468 stallEndpoint(EPBULK_IN);\r
469 } else {\r
470 stallEndpoint(EPBULK_OUT);\r
471 }\r
472 }\r
473\r
474 csw.Status = CSW_PASSED;\r
475 sendCSW();\r
476}\r
477\r
478\r
479void USBMSD::memoryRead (void) {\r
480 uint32_t n;\r
481\r
482 n = (length > MAX_PACKET) ? MAX_PACKET : length;\r
483\r
484 if ((addr + n) > MemorySize) {\r
485 n = MemorySize - addr;\r
486 stage = ERROR;\r
487 }\r
488\r
489 // we read an entire block\r
490 if (!(addr%BlockSize))\r
491 disk_read((char *)page, addr/BlockSize);\r
492\r
493 // write data which are in RAM\r
494 writeNB(EPBULK_IN, &page[addr%BlockSize], n, MAX_PACKET_SIZE_EPBULK);\r
495\r
496 addr += n;\r
497 length -= n;\r
498\r
499 csw.DataResidue -= n;\r
500\r
501 if ( !length || (stage != PROCESS_CBW)) {\r
502 csw.Status = (stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED;\r
503 stage = (stage == PROCESS_CBW) ? SEND_CSW : stage;\r
504 }\r
505}\r
506\r
507\r
508bool USBMSD::infoTransfer (void) {\r
509 uint32_t n;\r
510\r
511 // Logical Block Address of First Block\r
512 n = (cbw.CB[2] << 24) | (cbw.CB[3] << 16) | (cbw.CB[4] << 8) | (cbw.CB[5] << 0);\r
513\r
514 addr = n * BlockSize;\r
515\r
516 // Number of Blocks to transfer\r
517 switch (cbw.CB[0]) {\r
518 case READ10:\r
519 case WRITE10:\r
520 case VERIFY10:\r
521 n = (cbw.CB[7] << 8) | (cbw.CB[8] << 0);\r
522 break;\r
523\r
524 case READ12:\r
525 case WRITE12:\r
526 n = (cbw.CB[6] << 24) | (cbw.CB[7] << 16) | (cbw.CB[8] << 8) | (cbw.CB[9] << 0);\r
527 break;\r
528 }\r
529\r
530 length = n * BlockSize;\r
531\r
532 if (!cbw.DataLength) { // host requests no data\r
533 csw.Status = CSW_FAILED;\r
534 sendCSW();\r
535 return false;\r
536 }\r
537\r
538 if (cbw.DataLength != length) {\r
539 if ((cbw.Flags & 0x80) != 0) {\r
540 stallEndpoint(EPBULK_IN);\r
541 } else {\r
542 stallEndpoint(EPBULK_OUT);\r
543 }\r
544\r
545 csw.Status = CSW_FAILED;\r
546 sendCSW();\r
547 return false;\r
548 }\r
549\r
550 return true;\r
551}\r
552\r
553\r
554\r
555\r
556\r
557// Called in ISR context\r
558// Set configuration. Return false if the\r
559// configuration is not supported.\r
560bool USBMSD::USBCallback_setConfiguration(uint8_t configuration) {\r
561 if (configuration != DEFAULT_CONFIGURATION) {\r
562 return false;\r
563 }\r
564\r
565 // Configure endpoints > 0\r
566 addEndpoint(EPBULK_IN, MAX_PACKET_SIZE_EPBULK);\r
567 addEndpoint(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);\r
568\r
569 //activate readings\r
570 readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);\r
571 return true;\r
572}\r
573\r
574\r
575uint8_t * USBMSD::stringIinterfaceDesc() {\r
576 static uint8_t stringIinterfaceDescriptor[] = {\r
577 0x08, //bLength\r
578 STRING_DESCRIPTOR, //bDescriptorType 0x03\r
579 'M',0,'S',0,'D',0 //bString iInterface - MSD\r
580 };\r
581 return stringIinterfaceDescriptor;\r
582}\r
583\r
584uint8_t * USBMSD::stringIproductDesc() {\r
585 static uint8_t stringIproductDescriptor[] = {\r
586 0x12, //bLength\r
587 STRING_DESCRIPTOR, //bDescriptorType 0x03\r
588 'M',0,'b',0,'e',0,'d',0,' ',0,'M',0,'S',0,'D',0 //bString iProduct - Mbed Audio\r
589 };\r
590 return stringIproductDescriptor;\r
591}\r
592\r
593\r
594uint8_t * USBMSD::configurationDesc() {\r
595 static uint8_t configDescriptor[] = {\r
596\r
597 // Configuration 1\r
598 9, // bLength\r
599 2, // bDescriptorType\r
600 LSB(9 + 9 + 7 + 7), // wTotalLength\r
601 MSB(9 + 9 + 7 + 7),\r
602 0x01, // bNumInterfaces\r
603 0x01, // bConfigurationValue: 0x01 is used to select this configuration\r
604 0x00, // iConfiguration: no string to describe this configuration\r
605 0xC0, // bmAttributes\r
606 100, // bMaxPower, device power consumption is 100 mA\r
607\r
608 // Interface 0, Alternate Setting 0, MSC Class\r
609 9, // bLength\r
610 4, // bDescriptorType\r
611 0x00, // bInterfaceNumber\r
612 0x00, // bAlternateSetting\r
613 0x02, // bNumEndpoints\r
614 0x08, // bInterfaceClass\r
615 0x06, // bInterfaceSubClass\r
616 0x50, // bInterfaceProtocol\r
617 0x04, // iInterface\r
618\r
619 // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13\r
620 7, // bLength\r
621 5, // bDescriptorType\r
622 PHY_TO_DESC(EPBULK_IN), // bEndpointAddress\r
623 0x02, // bmAttributes (0x02=bulk)\r
624 LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)\r
625 MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)\r
626 0, // bInterval\r
627\r
628 // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13\r
629 7, // bLength\r
630 5, // bDescriptorType\r
631 PHY_TO_DESC(EPBULK_OUT), // bEndpointAddress\r
632 0x02, // bmAttributes (0x02=bulk)\r
633 LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)\r
634 MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)\r
635 0 // bInterval\r
636 };\r
637 return configDescriptor;\r
638}