/* * CHU - driver for CH375 in disk mode * Copyright (C) 2024 Javier S. Pedro * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "inc/dosapi.h" #include "inc/dosint.h" #include "inc/bda.h" #include "chudisk.h" #include "ch375.h" #include "dlog.h" #include "cio.h" #include "utils.h" static int parse_int(const char far * far *s) { int i = 0; while (**s >= '0' && **s <= '9') { i = (i * 10) + (**s - '0'); (*s)++; } if (**s == ' ' || **s == '\r' || **s == '\n') { return i; } else { return -1; } } static int parse_hex(const char far * far *s) { int i = 0; while ((**s >= '0' && **s <= '9') || (**s >= 'A' && **s <= 'F') || (**s >= 'a' && **s <= 'f')) { if (**s >= '0' && **s <= '9') i = (i * 16) + (**s - '0'); else if (**s >= 'A' && **s <= 'F') i = (i * 16) + (**s - 'A'); else if (**s >= 'a' && **s <= 'f') i = (i * 16) + (**s - 'a'); (*s)++; } if (**s == ' ' || **s == '\r' || **s == '\n') { return i; } else { return -1; } } static bool parse_cmdline(const char far *cmdline) { // Source command line string // E.g. DEVICE.SYS /N3\r\n const char far *s = cmdline; // Skip filename part (search for first sep) while (*s != ' ' && *s != '\r' && *s != '\n') { s++; } if (*s == '\r' || *s == '\n') return true; // No arguments // Now skip whitespace while (*s == ' ') { s++; } // Now handle options like /N3 /X4 while (*s != '\r' && *s != '\n') { char c = *s++, opt; int num; switch (c) { case '/': opt = *s++; switch (opt) { case 'N': case 'n': // number of units num = parse_int(&s); if (num > 0 && num <= MAX_UNITS) { num_units = num; } else { c_printf("invalid value '%d' for option '%c'\n", num, opt); return false; } break; case 'P': case 'p': // IO port num = parse_hex(&s); if (num > 0) { ch375_port = num; } else { c_printf("invalid value '%d' for option '%c'\n", num, opt); return false; } break; case 'R': case 'r': // Read only read_only = true; break; case '3': // Show FAT32 partitions show_fat32 = true; break; default: c_printf("invalid option '%c'\n", opt); return false; } break; case ' ': break; default: c_printf("invalid syntax '%c'\n", c); return false; } } dprintf("Configured num drives: %hu\n", num_units); return true; } static void fill_initial_bpb(DOS_BPB *bpb) { // Dummy, only used during driver init // Most likely these values can be anything you want, // as long as it remotely looks like a valid FAT16 BPB bpb->bytesPerSector = 512; bpb->sectorsPerCluster = 1; bpb->reservedSectors = 0; bpb->numberOfFATs = 2; bpb->rootDirEntries = 16; bpb->numberOfSectors = 512; bpb->mediaType = DOS_BPB_MEDIA_HDD; bpb->sectorsPerFAT = 1; bpb->sectorsPerTrack = 63; bpb->numberOfHeads = 255; bpb->hiddenSectors = 0; bpb->numberOfSectorsLarge = 0; memcpy(bpb->fat16.volumeLabel, "NO NAME ", 11); } void driver_init(DOS_DRS __far *drs) { unsigned i; uint16_t dos_version; uint8_t dos_major, dos_minor; uint8_t hw_version; dlog_init(); // Defaults ch375_port = 0x260; num_units = 1; read_only = false; show_fat32 = false; // If DOS > 7.1, enable FAT32 by default dos_version = dos_get_version(); dos_major = dos_version & 0xFF; dos_minor = (dos_version >> 8) & 0xFF; dprintf("DOS version %d.%d\n", dos_major, dos_minor); if (dos_major > 7 || (dos_major == 7 && dos_minor >= 10)) { show_fat32 = true; } // Parse command line arguments if (!parse_cmdline(drs->cmd.init.in.cmdline)) { c_puts("CHUDISK: Syntax error in cmdline arguments"); goto fail; } // Try to initialize CH375 dputs("CH375 reset..."); ch375_reset(); bda_wait_ticks(3); // wait 100-150ms dputs("CH375 reset wait done"); if (ch375_check_exists(0xA5) != (uint8_t)~0xA5) { c_printf("CHUDISK: CH375 not detected at @%x", ch375_port); goto fail; } hw_version = ch375_get_version(); // Do not really set up the chip until it is used ch375_set_usb_mode(CH375_USB_MODE_HOST_DISABLED); ch375_enter_sleep(); data.ready = false; data.sectors = 0; data.bytesPerSector = 0; // Build the template BPBs drs->cmd.init.out.number_units = num_units; for (i = 0; i < num_units; i++) { fill_initial_bpb(&data.unit[i].bpb); data.bpb_ptrs[i] = &data.unit[i].bpb; } drs->cmd.init.out.bpbs = data.bpb_ptrs; drs->cmd.init.out.break_addr = MK_FP(get_cs(), FP_OFF(&resident_end)); drs->status = DOS_DD_STATUS_DONE; c_printf("CHUDISK v.0 (HW v.%x), drives", hw_version); for (i = 0; i < num_units; i++) { c_printf(" %c:", drive_index_to_letter(drs->cmd.init.in.first_drive_number + i)); } c_putc('\n'); return; fail: drs->status = DOS_DD_STATUS_DONE | DOS_DD_STATUS_ERROR | DOS_DD_ERR_GENERAL_FAILURE; drs->cmd.init.out.number_units = 0; drs->cmd.init.out.break_addr = MK_FP(get_cs(), 0); // Deallocate everything return; }