usb device

Committer:
ppo
Date:
Sat May 14 17:24:10 2022 +0000
Revision:
0:c1e89c49eae5
commit

Who changed what in which revision?

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