Bool DriveLock(CDrive *drive) {//Make this task have exclusive access to drive & BlkDev. DriveCheck(drive); BlkDevLock(drive->bd); if (!Bt(&drive->locked_flags, DVlf_LOCKED) || drive->owning_task != Fs) { while (LBts(&drive->locked_flags, DVlf_LOCKED)) Yield; drive->owning_task = Fs; return TRUE; } else return FALSE; } Bool DriveUnlock(CDrive *drive, Bool reset=FALSE) {//Release exclusive lock on access to drive & BlkDev. DriveCheck(drive); if (Bt(&drive->locked_flags, DVlf_LOCKED) && drive->owning_task == Fs) { BlkDevUnlock(drive->bd, reset); drive->owning_task = NULL; LBtr(&drive->locked_flags, DVlf_LOCKED); Yield; //Prevent deadlock return TRUE; } else return FALSE; } U0 DrivesRelease() {//When task dies, release all owned drvs. I64 i; CDrive *drive; for (i = 0; i < DRIVES_NUM; i++) { drive = &blkdev.drvs[i]; if (drive->owning_task == Fs && drive->drive_signature == DRIVE_SIGNATURE_VAL) DriveUnlock(drive, TRUE); } } CDrive *DriveMakeFreeSlot(U8 drv_let) {//Make a slot free for a new drive, like during Mount(). I64 i = Letter2Letter(drv_let) - 'A'; CDrive *res; if (!(0 <= i < DRIVES_NUM)) throw('Drive'); res = &blkdev.drvs[i]; MemSet(res, 0, sizeof(CDrive)); res->drv_let = 'A' + i; return res; } U8 DriveNextFreeLet(U8 first_drive_let='C') {//Locate free slot for new drive, like during Mount(). I64 i = Letter2Letter(first_drive_let) - 'A', type = Letter2BlkDevType(first_drive_let); if (!(0 <= i < DRIVES_NUM)) throw('Drive'); do if (blkdev.drvs[i].drive_signature != DRIVE_SIGNATURE_VAL) { if (Letter2BlkDevType(i + 'A') != type) throw('Drive'); else return i + 'A'; } while (++i < DRIVES_NUM); throw('Drive'); return 0; //Never gets here. } U0 DriveDel(CDrive *drive) {//Delete drive if (drive->fs_type == FSt_REDSEA && drive->next_free) RedSeaFreeFreeList(drive); Free(drive->cur_fat_blk); Free(drive->fis); MemSet(drive, 0, sizeof(CDrive)); } U0 DriveBlkDevDel(CBlkDev *bd) {//Delete drives of BlkDev I64 i; CDrive *drive; for (i = 0; i < DRIVES_NUM; i++) { drive = &blkdev.drvs[i]; if (drive->bd == bd) DriveDel(drive); } } U0 DriveFATBlkAlloc(CDrive *drive) { DriveCheck(drive); Free(drive->cur_fat_blk); drive->cur_fat_blk = SysMAlloc(BLK_SIZE); drive->cur_fat_blk_num = 0; drive->fat_blk_dirty = 0; BlkRead(drive, drive->cur_fat_blk, drive->fat1, 1); } U0 DriveFATBlkClean(CDrive *drive, I64 fat_sel=3) { if ((drive->fs_type == FSt_FAT32 || drive->fs_type == FSt_REDSEA) && Bt(&drive->fat_blk_dirty, 0)) { if (drive->fat1 == drive->fat2) { BlkWrite(drive, drive->cur_fat_blk, drive->fat1 + drive->cur_fat_blk_num, 1); LBtr(&drive->fat_blk_dirty, 0); } else { if (fat_sel == 3 || !fat_sel) BlkWrite(drive, drive->cur_fat_blk, drive->fat1 + drive->cur_fat_blk_num, 1); if (fat_sel == 3 || fat_sel == 1) { BlkWrite(drive, drive->cur_fat_blk, drive->fat2 + drive->cur_fat_blk_num, 1); LBtr(&drive->fat_blk_dirty, 0); } } } } U0 DriveFATBlkSet(CDrive *drive, I64 c, I64 fat_sel=3) { I64 fat_blk_num; if (c == INVALID_CLUS) throw('Drive'); switch (drive->fs_type) { case FSt_FAT32: fat_blk_num = c >> (BLK_SIZE_BITS - 2); break; case FSt_REDSEA: fat_blk_num = (c-drive->data_area) >> (BLK_SIZE_BITS + 3); break; default: throw('Drive'); } if (fat_blk_num != drive->cur_fat_blk_num) { DriveFATBlkClean(drive, fat_sel); drive->cur_fat_blk_num = fat_blk_num; if (fat_sel == 3 || !fat_sel) BlkRead(drive, drive->cur_fat_blk, drive->fat1 + drive->cur_fat_blk_num, 1); else BlkRead(drive, drive->cur_fat_blk, drive->fat2 + drive->cur_fat_blk_num, 1); } } CDrive *DriveCheck(CDrive *drive, Bool except=TRUE) {//Check for valid drive. Throw exception. if (!drive || drive->drive_signature != DRIVE_SIGNATURE_VAL) { if (except) throw('Drive'); else return NULL; } else return drive; } U8 Drive2Letter(CDrive *drive=NULL) {//Drive pointer to Drive letter. if (!drive) drive = Fs->cur_dv; DriveCheck(drive); return drive->drv_let; } U8 Letter2Letter(U8 drv_let=0) {//Drive letter to Drive letter. if (!drv_let) drv_let = Drive2Letter(Fs->cur_dv); else if (drv_let == ':') drv_let = blkdev.boot_drive_let; else if (drv_let == '~') drv_let = *blkdev.home_dir; return ToUpper(drv_let); } I64 Letter2BlkDevType(U8 drv_let) {//Drive letter to BlkDev Type. drv_let=0 not allowed. See BDT_NULL. drv_let = Letter2Letter(drv_let); if ('A' <= drv_let <= 'B') return BDT_RAM; if ('C' <= drv_let <= 'L') return BDT_ATA; if ('M' <= drv_let <= 'P') return BDT_ISO_FILE_READ; if ('Q' <= drv_let <= 'S') return BDT_ISO_FILE_WRITE; if ('T' <= drv_let <= 'Z') return BDT_ATAPI; return BDT_NULL; } CDrive *Letter2Drive(U8 drv_let=0, Bool except=TRUE) {//Drive letter to Drive pointer. CDrive *drive; if (!drv_let) drive = Fs->cur_dv; else { drv_let = Letter2Letter(drv_let); if (!('A' <= drv_let <= 'Z')) { if (except) throw('Drive'); else return NULL; } drive = blkdev.let_to_drive[drv_let - 'A']; } return DriveCheck(drive, except); } CBlkDev *DriveIsWritable(U8 drv_let=0, Bool except=FALSE) {//Is drive writable? CBlkDev *bd; if (!(bd = Letter2BlkDev(drv_let, except)) || bd->flags & BDF_READ_ONLY) { if (except) throw('Drive'); else return NULL; } else return bd; } U0 DiskCacheInvalidate(CDrive *drive) {//Needed for removable media. Called by DiskChange(). Bool unlock; CBlkDev *bd = drive->bd; DriveCheck(drive); try { unlock = DriveLock(drive); BlkDevInit(bd); if (bd->flags & BDF_READ_CACHE) DiskCacheInvalidate2(drive); if (bd->type == BDT_ATAPI && !(bd->flags & BDF_READ_ONLY_OVERRIDE)) ISOInit(drive, (32767 / bd->blk_size + 1) * bd->blk_size >> BLK_SIZE_BITS); if (unlock) DriveUnlock(drive); } catch if (unlock) DriveUnlock(drive); } U0 DiskChange(U8 drv_let=0) {//Change disk. (Needed for removable media.) CDrive *drive = Letter2Drive(drv_let); CBlkDev *bd = drive->bd; if (!(bd->flags & BDF_INITIALIZED)) BlkDevInit(bd); else if (bd->flags & BDF_REMOVABLE) { if (bd->type == BDT_ATAPI) AHCIAtaInit(bd); //TODO: This is a kludge for QEMU? DiskCacheInvalidate(drive); } Drive(drv_let); RedSeaFreeFreeList(drive); } Bool Drive(U8 drv_let=0) {//Change drive. You can set drive with Cd() as well. CDrive *drive = Letter2Drive(drv_let); CBlkDev *bd; bd = BlkDevCheck(drive->bd); if (drive != Fs->cur_dv) { if (bd->flags & BDF_REMOVABLE && !(bd->flags & BDF_INITIALIZED)) DiskChange(Drive2Letter(drive)); if (bd->type == BDT_RAM || bd->type == BDT_ISO_FILE_READ || bd->type == BDT_ISO_FILE_WRITE) BlkDevInit(bd); } Fs->cur_dv = drive; Free(Fs->cur_dir); Fs->cur_dir = StrNew("/"); switch (drive->fs_type) { case FSt_REDSEA: case FSt_ISO9660: case FSt_FAT32: return TRUE; default: PrintErr("File System Not Supported\n"); return FALSE; } } U8 *DriveSerialNum(U8 drv_let=0) {//20 bytes max. CBlkDev *bd = Letter2BlkDev(drv_let); U16 *st, *res = NULL; I64 i; if (bd->dev_id_record) { st = CAlloc(20 + 1); for (i = 0; i < 10; i++) st[i] = EndianU16(bd->dev_id_record[10 + i]); res = MStrUtil(st, SUF_REM_LEADING | SUF_REM_TRAILING); Free(st); } return res; } U8 *DriveModelNum(U8 drv_let=0) {//40 bytes max. CBlkDev *bd = Letter2BlkDev(drv_let); U16 *st, *res = NULL; I64 i; if (bd->dev_id_record) { st = CAlloc(40 + 1); for (i = 0; i < 20; i++) st[i] = EndianU16(bd->dev_id_record[27 + i]); res = MStrUtil(st, SUF_REM_LEADING | SUF_REM_TRAILING); Free(st); } return res; } U8 blkdev_text_attr[BDT_TYPES_NUM] = {BLACK, LTCYAN, WHITE, LTGREEN, LTRED, LTBLUE}; U8 drv_text_attr[3] = {BLACK, BLUE, RED}; U8 DriveTextAttrGet(U8 drv_let=0) {//Get color of drive. drv_let = Letter2Letter(drv_let); if ('A' <= drv_let <= 'Z') return blkdev_text_attr[Letter2BlkDevType(drv_let)] << 4 | drv_text_attr[drv_let % sizeof(drv_text_attr)]; else return BLACK << 4 | WHITE; } U0 DriveRep() {//Drive report. CDrive *drive; CBlkDev *bd; I64 ch, i, drv_let, attr; U8 *st; "\nDefined Drives:\n"; for (i = 0, drive = blkdev.drvs; i < DRIVES_NUM; i++, drive++) { if (drive->drive_signature == DRIVE_SIGNATURE_VAL) { bd = drive->bd; drv_let = Drive2Letter(drive); if (Bt(&drive->fs_type, FStf_DISABLE)) ch = '-'; else if (drv_let == blkdev.boot_drive_let) ch = ':'; else ch = '+'; attr = DriveTextAttrGet(drv_let); if (!IsRaw) "$FG,%d$$BG,%d$", attr & 15, attr >> 4; "%C %-9Z %-10Z ", drv_let, drive->fs_type & FSG_TYPE_MASK, "ST_DRIVE_TYPES", bd->type, "ST_BLKDEV_TYPES"; if (bd->ahci_port) "SATA Port: %02d", bd->port_num; '\n'; if (st = DriveModelNum(drv_let)) { " Model: %s\n", st; Free(st); } if (st = DriveSerialNum(drv_let)) { " Serial: %s\n", st; Free(st); } if (bd->type == BDT_ISO_FILE_READ || bd->type == BDT_ISO_FILE_WRITE) " File=\"%s\"\n", bd->file_disk_name; " Offset:\t\t\tOffset + Size:\n"; " %016X-%016X\n", drive->drv_offset, drive->drv_offset + drive->size - 1; if (!IsRaw) "$FG$$BG$"; } } "Home Dir:\"%s\"\n", blkdev.home_dir; }