TinyUSB MSCの改変
まずはマスストレージクラスの実装がどのように行われているか確認します。「msc_disk.c」にFATドライブとしての設定部分がまとめられています。ざっと眺めると下記のように設定されています。
- uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE]としてFATドライブのデータイメージをRAM上に置いている
- FAT12として、Boot sector、FAT、Root directory entry、README.TXT(コンテンツ)をmsc_disk[]の初期値として与えている
- DISK_BLOCK_NUM = 16, DISK_BLOCK_SIZE = 512でトータルの容量は8KB
- FATの数は”1″(通常は2であることが多いが実動作上の問題は無さそう)
- Root directory entryの数は16
ドライブとしての容量は8KBしかありませんので、RAMの総容量を鑑みて240KB迄拡張します。DISK_BLOCK_NUM = 480になりますが、FAT12の場合1つのチェーンに12bit(1.5バイト)ですからデフォルトの1block=512バイトでは340チェーン迄しか記録出来ないためこちらも2blockに拡張します。
加えて、ボリュームラベルとVID、README.TXTの内容とドライブにファイルが書き込まれたことを検出するためのフラグを追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ //****************************************************************************** //* customized for PIC writer USB * //* * //* history: * //* Sep16,2022 start coding. N.Mato * //* * //* Copyright(c)2022 Rigel Intelligence * //****************************************************************************** #include "bsp/board.h" #include "tusb.h" #if CFG_TUD_MSC // whether host does safe-eject static bool ejected = false; // Flag for writing a file to storage, change on root directory entry (Block2) volatile bool flag_rootdir_change = false; // Some MCU doesn't have enough 8KB SRAM to store the whole disk // We will use Flash as read-only disk with board that has // CFG_EXAMPLE_MSC_READONLY defined #define README_CONTENTS \ "'PicWriter USB' - PIC Microcontroller programming tool.\r\n\r\n\ It's simple to use, drag & drop or copy the 'HEX' file to this drive.\r\n\ Only PIC16F1823 is supported at this moment.\r\n" enum { DISK_BLOCK_NUM = 480, // 240KB DISK_BLOCK_SIZE = 512, ROOT_DIR_ENTRY = 3 // Block number of root directory entory }; #ifdef CFG_EXAMPLE_MSC_READONLY const #endif uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = { //------------- Block0: Boot Sector -------------// // BS_jmpBoot = 0xEB, 0x3C, 0x90 // BS_OEMName = "MSDOS5.0" // byte_per_sector = DISK_BLOCK_SIZE=0200h=512bytes; // sector_per_cluster = 1; reserved_sectors = 1; // BPB_RsvdSecCnt = 0001h=1 // fat_num = 01h=1 // BPB_RootEntCnt = 0010h=16; fat12_root_entry_num = 16; // BPB_TotSec16 = 01E0h=480. fat12_sector_num_16 = DISK_BLOCK_NUM; // Media_type = 0xf8 (HDD) // sector_per_fat = 0002h; // sector_per_track = 0001h; // num_of_head = 0001h; // num_hidden_sector = 00000000h; // large_sector = 00000000h; // physical_dive_no = 80h (HDD) // reserved = 00 // extended_signature = 29h // volume_serial = 00001234h // volume_label = 'PicWriter ' (11 chars) // file_system_type = 'FAT12 ' (8 chars) // FAT magic code at offset 510-511 (55-AA) { 0xEB, 0x3C, 0x90, 'M' , 'S' , 'D' , 'O' , 'S' , '5' , '.' , '0' , 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x10, 0x00, 0xE0, 0x01, 0xF8, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'P' , 'i' , 'c' , 'W' , 'r' , 'i' , 't' , 'e' , 'r' , ' ' , ' ' , 'F' , 'A' , 'T' , '1' , '2' , ' ' , ' ' , ' ' , 0x00, 0x00, // Zero up to 2 last bytes of FAT magic code 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA }, //------------- Block1: FAT12 Table(1) -------------// { 0xF8, 0xFF, 0xFF, 0xFF, 0x0F, 0x00 // first 2 entries must be FF8 FFF, // third entry is cluster end of readme file }, //------------- Block2: FAT12 Table(2) -------------// { 0x00 }, //------------- Block3: Root Directory -------------// { // first entry is volume label 'P' , 'i' , 'c' , 'W' , 'r' , 'i' , 't' , 'e' , 'r' , 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x55, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // second entry is readme file 'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x21, 0x00, 0x00, 0x00, 0x78, 0x2D, 0x55, 0x2D, 0x55, 0x00, 0x00, 0x00, 0x78, 0x2D, 0x55, 0x02, 0x00, sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes) }, //------------- Block4: Readme Content -------------// README_CONTENTS }; // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { (void) lun; const char vid[] = "PicWriterUSB"; const char pid[] = "Mass Storage"; const char rev[] = "1.0"; memcpy(vendor_id , vid, strlen(vid)); memcpy(product_id , pid, strlen(pid)); memcpy(product_rev, rev, strlen(rev)); printf("inquiry %s : %s%s\n", vid, pid, rev); // Inquiry command issued = USB conneccted ejected = false; } // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted bool tud_msc_test_unit_ready_cb(uint8_t lun) { //printf("test unit ready :ejected %d\n", (int )ejected); (void) lun; // RAM disk is ready until ejected if (ejected) { tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); return false; } return true; } // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size // Application update block count and block size void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { printf("capacity %dblocks, %dBytes/block\n", DISK_BLOCK_NUM, DISK_BLOCK_SIZE); (void) lun; *block_count = DISK_BLOCK_NUM; *block_size = DISK_BLOCK_SIZE; } // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { //printf("start_stop, load_eject=%d, start=%d\n", (int )load_eject, (int )start); (void) lun; (void) power_condition; if ( load_eject ) { if (start) { // load disk storage }else { // unload disk storage ejected = true; tud_umount_cb(); } } return true; } // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { //printf("read10:[%d]\n", lba); (void) lun; // out of ramdisk if ( lba >= DISK_BLOCK_NUM ) return -1; uint8_t const* addr = msc_disk[lba] + offset; memcpy(buffer, addr, bufsize); return bufsize; } bool tud_msc_is_writable_cb (uint8_t lun) { //printf("is_writable\n"); (void) lun; #ifdef CFG_EXAMPLE_MSC_READONLY return false; #else return true; #endif } // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and return number of written bytes int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { //printf("write10:[%d]\n", lba); (void) lun; // out of ramdisk if ( lba >= DISK_BLOCK_NUM ) return -1; #ifndef CFG_EXAMPLE_MSC_READONLY uint8_t* addr = msc_disk[lba] + offset; memcpy(addr, buffer, bufsize); #else (void) lba; (void) offset; (void) buffer; #endif // check root directory entry changed or not if( lba == ROOT_DIR_ENTRY ) flag_rootdir_change = true; return bufsize; } // Callback invoked when received an SCSI command not in built-in list below // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { //printf("scsi_cmd:[%02Xh]\n", scsi_cmd[0]); // read10 & write10 has their own callback and MUST not be handled here void const* response = NULL; int32_t resplen = 0; // most scsi handled is input bool in_xfer = true; switch (scsi_cmd[0]) { case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: // Host is about to read/write etc ... better not to disconnect disk resplen = 0; break; default: // Set Sense = Invalid Command Operation tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // negative means error -> tinyusb could stall and/or response with failed status resplen = -1; break; } // return resplen must not larger than bufsize if ( resplen > bufsize ) resplen = bufsize; if ( response && (resplen > 0) ) { if(in_xfer) { memcpy(buffer, response, resplen); }else { // SCSI output } } return resplen; } #endif |
あとはUSBのdescriptorをPicWriterUSBに合わせて変更します。
229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
//--------------------------------------------------------------------+ // String Descriptors //--------------------------------------------------------------------+ // array of pointer to string descriptors char const* string_desc_arr [] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) "PicWriterUSB", // 1: Manufacturer "PicWriterUSB Device", // 2: Product "DAE2022CDC00", // 3: Serials, should use chip ID "PicWriterUSB CDC", // 4: CDC Interface "PicWriterUSB MSC", // 5: MSC Interface }; |
3: Serialsはユニークな値を設定するべきかとは思いますが、とりあえず固定値を入れています。
再度ビルドを行いmsc_test.uf2にてRP2040を書き換えると、ボリュームラベルの変更と容量の拡大を確認できます。
コメント