diff options
| -rw-r--r-- | README.md | 17 | ||||
| -rw-r--r-- | dos/chud.c | 84 | ||||
| -rw-r--r-- | dos/chudisk.c | 22 | ||||
| -rw-r--r-- | dos/chudisk.h | 14 | ||||
| -rw-r--r-- | dos/inc/dosapi.h | 4 | ||||
| -rw-r--r-- | dos/utest.c | 194 | ||||
| -rw-r--r-- | inc/chuver.h | 2 | ||||
| -rw-r--r-- | linux/chudisk.c | 46 | ||||
| -rw-r--r-- | linux/makefile | 5 | ||||
| -rwxr-xr-x | linux/mkdev.sh | 2 |
10 files changed, 300 insertions, 90 deletions
@@ -114,7 +114,7 @@ decide which drive letters you prefer for the partitions on your pendrive. For that, go to Device Manager, Disk Drives, and select the CH375 USB Disk as follows: - + Partitions will be assigned to disk letters in order. @@ -180,17 +180,22 @@ You can then use/mount the `/dev/chuda1`, `/dev/chuda2` devices as usual. ## Usage +Load in the usual way, or put into `/etc/modules`. E.g., + +```` +modprobe chudisk io=0x260 +```` + The driver exposes the following parameters: * `io` the io address for your CH375, defaults to 0x260. -* `major` allows overriding the device's major number, in the rare case you -have a conflict. If you do this, also update `mkdev.sh`. +* `major` allows overriding the device's major number, in case you +have a conflict with the default (63). If you do this, also update `mkdev.sh`. * `force` will load the driver even if we can't find a CH375 at the specified address. * `softblk` allows setting the "block size" used for transfers. -Linux defaults to 1024. Higher values provide higher speed at the cost -of latency. Must be power of two. - +Linux defaults to 1024. Higher values provide higher throughput at the cost +of latency. Must be power of two, max is 4K. # Future work @@ -20,11 +20,13 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <errno.h> #include <dos.h> #include <dosfunc.h> #include <io.h> #include "inc/mbr.h" +#include "inc/dosapi.h" #include "inc/dosdrv.h" #include "chudisk.h" @@ -56,11 +58,10 @@ static int ioctl_read_control(uint8_t drive, void *buffer, int len) regs.x.dx = FP_OFF(buffer); intdos(®s, ®s); - if (regs.x.cflag) { - return -1; + return -1; // Generic error, but errno should be properly set } else { - return regs.x.ax; + return regs.x.ax; // Number of bytes read } } @@ -74,7 +75,6 @@ static int ioctl_write_control(uint8_t drive, void *buffer, int len) regs.x.dx = FP_OFF(buffer); intdos(®s, ®s); - if (regs.x.cflag) { return -1; } else { @@ -99,8 +99,67 @@ static int ioctl_read_abs_sector(uint8_t drive, void *buffer, uint32_t sector, i regs.x.dx = FP_OFF(&ciod); intdos(®s, ®s); - - return regs.x.cflag ? -1 : 0; + if (regs.x.cflag) { + struct DOSERROR err; + // Get the extended error from DOS, more useful than a simple EACCES + // and translate to watcom's errno + switch (dosexterr(&err)) { + case DOS_ERROR_INVALID_FUNCTION: + case DOS_ERROR_BAD_COMMAND: + errno = EINVAL; + break; + case DOS_ERROR_FILE_NOT_FOUND: + case DOS_ERROR_PATH_NOT_FOUND: + case DOS_ERROR_NO_MORE_FILES: + errno = ENOENT; + break; + case DOS_ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case DOS_ERROR_INVALID_DRIVE: + case DOS_ERROR_BAD_UNIT: + errno = ENODEV; + break; + case DOS_ERROR_INVALID_HANDLE: + errno = EBADF; + break; + case DOS_ERROR_BAD_FORMAT: + case DOS_ERROR_NOT_DOS_DISK: + errno = ENOEXEC; + break; + case DOS_ERROR_SEEK: + case DOS_ERROR_SECTOR_NOT_FOUND: + errno = ESPIPE; + break; + case DOS_ERROR_INVALID_DATA: + errno = ERANGE; + break; + case DOS_ERROR_NOT_READY: + errno = EAGAIN; + break; + case DOS_ERROR_READ_FAULT: + case DOS_ERROR_WRITE_FAULT: + case DOS_ERROR_GEN_FAILURE: + case DOS_ERROR_CRC: + errno = EIO; + break; + case DOS_ERROR_ACCESS_DENIED: + errno = EACCES; + break; + case DOS_ERROR_HANDLE_DISK_FULL: + errno = ENOSPC; + break; + case DOS_ERROR_WRITE_PROTECT: + errno = EROFS; + break; + default: + errno = EFAULT; + break; + } + return -errno; + } else { + return 0; // No error + } } static void dos_disk_reset() @@ -123,6 +182,11 @@ static int parse_chudisk_control_string(const char *b, int len) } i = 0; #endif + // signature + if (b[i] != 'C' || b[i+1] != 'H') { + return -1; + } + i+=2; while (i < len) { char c = b[i++]; switch (c) { @@ -345,7 +409,7 @@ static void usage() int main(int argc, const char *argv[]) { - static char buffer[16]; + static char buffer[32]; int i; unsigned part; @@ -399,10 +463,12 @@ int main(int argc, const char *argv[]) dos_disk_reset(); // Flush caches before we swap the drive... i = 0; - buffer[i++] = 'S'; + buffer[i++] = 'C'; // Signature + buffer[i++] = 'H'; + buffer[i++] = 'S'; // Partition start sector memcpy(&buffer[i], &partinfo[part-1].start_sector, sizeof(uint32_t)); i+=sizeof(uint32_t); - buffer[i++] = 'L'; + buffer[i++] = 'L'; // Partition len memcpy(&buffer[i], &partinfo[part-1].num_sectors, sizeof(uint32_t)); i+=sizeof(uint32_t); buffer[i] = '\0'; diff --git a/dos/chudisk.c b/dos/chudisk.c index c7c0968..d822110 100644 --- a/dos/chudisk.c +++ b/dos/chudisk.c @@ -598,10 +598,13 @@ void interrupt_routine() return; } // If there's enough space, return our internal config string. - if (saved_drs->cmd.ioctlrw.len >= 3 + 2 + 1 + 5 + 1) { + if (saved_drs->cmd.ioctlrw.len >= 2 + /*B*/3 + /*3*/2 + /*R*/2 + /*S*/5 + /*L*/5 + 1) { uint8_t far * buf = saved_drs->cmd.ioctlrw.buffer; int len = 0; - // B = base port (2 byte) + // signature (2 bytes) + buf[len++] = 'C'; + buf[len++] = 'H'; + // B = base port (2 bytes) buf[len++] = 'B'; _fmemcpy(&buf[len], &ch375_dev.io_base, sizeof(ch375_dev.io_base)); len += sizeof(ch375_dev.io_base); @@ -615,6 +618,10 @@ void interrupt_routine() buf[len++] = 'S'; _fmemcpy(&buf[len], &data.unit[saved_drs->unit].start_sector, sizeof(uint32_t)); len += sizeof(uint32_t); + // L = partition len (4 bytes) + buf[len++] = 'L'; + _fmemcpy(&buf[len], &data.unit[saved_drs->unit].sectors, sizeof(uint32_t)); + len += sizeof(uint32_t); buf[len++] = '\0'; saved_drs->cmd.ioctlrw.len = len; } else { @@ -632,7 +639,14 @@ void interrupt_routine() if (saved_drs->cmd.ioctlrw.len >= 2) { uint8_t far * buf = saved_drs->cmd.ioctlrw.buffer; int i = 0; - do { + if (buf[i] != 'C' || buf[i+1] != 'H') { + dprintf(" invalid signature\n"); + saved_drs->cmd.ioctlrw.len = 0; + saved_drs->status = DOS_DD_ERR_UNKNOWN_COMMAND | DOS_DD_STATUS_ERROR_DONE; + return; + } + i += 2; + while (i < saved_drs->cmd.ioctlrw.len) { char c = buf[i++]; switch (c) { case 'S': // Partition start (5 bytes) @@ -659,7 +673,7 @@ void interrupt_routine() saved_drs->status = DOS_DD_ERR_WRITE_FAULT | DOS_DD_STATUS_ERROR_DONE; return; } - } while (i < saved_drs->cmd.ioctlrw.len); + } } else { saved_drs->cmd.ioctlrw.len = 0; saved_drs->status = DOS_DD_ERR_WRITE_FAULT | DOS_DD_STATUS_ERROR_DONE; diff --git a/dos/chudisk.h b/dos/chudisk.h index 1d2fbe4..bc7f12d 100644 --- a/dos/chudisk.h +++ b/dos/chudisk.h @@ -54,7 +54,7 @@ extern struct chudisk_data struct chudisk_unit { /** Whether the partition exists (start_sector points to it) */ bool exists; - /** Note in theory may be zero (superfloppy) */ + /** Start sector of partition. Note in theory may be zero (superfloppy) */ uint32_t start_sector; /** Number of sectors for this partition. */ uint32_t sectors; @@ -78,15 +78,21 @@ extern uint8_t wait_for_ch375_result(unsigned int max_ticks); // TODO Not sure we can really happily add custom minors here, // but then again DOS outright refuses to accept any other major enum CHUDISK_IOCTL_DISK_MINOR { + /** Read an absolute disk sector. + * This ignores any configured partition on this unit. */ CHUDISK_IOCTL_DISK_READ_ABS_SECTOR = 0xC1 }; typedef _Packed union CHUDISK_IOCTL_DISK_DATA { + /* @see CHUDISK_IOCTL_DISK_READ_ABS_SECTOR */ struct CHUDISK_RWABSSECTOR { - uint32_t sector; - uint8_t sectors; - void far * buffer; + /** Absolute starting sector. */ + uint32_t sector; + /** Number of sectors to read. */ + uint8_t sectors; + /** Buffer to read data into. */ + void far * buffer; } rwabs; } CHUDISK_IOCTL_DISK_DATA; diff --git a/dos/inc/dosapi.h b/dos/inc/dosapi.h index 18dbde0..8295660 100644 --- a/dos/inc/dosapi.h +++ b/dos/inc/dosapi.h @@ -57,7 +57,9 @@ enum dos_error { DOS_ERROR_SEEK = 25, DOS_ERROR_NOT_DOS_DISK = 26, DOS_ERROR_SECTOR_NOT_FOUND = 27, - + DOS_ERROR_OUT_OF_PAPER = 28, + DOS_ERROR_WRITE_FAULT = 29, + DOS_ERROR_READ_FAULT = 30, DOS_ERROR_GEN_FAILURE = 31, DOS_ERROR_HANDLE_EOF = 38, diff --git a/dos/utest.c b/dos/utest.c index 7aa8750..b4ce7f3 100644 --- a/dos/utest.c +++ b/dos/utest.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <time.h> #include <dos.h> @@ -108,6 +109,35 @@ void chip_sleep() ch375_enter_sleep(&ch375_dev); } +void chip_version() +{ + printf("IC Version: 0x%hx\n", ch375_get_version(&ch375_dev)); +} + +void chip_reg(const char *arg_reg, const char *arg_value) +{ + int reg = arg_reg ? strtoul(arg_reg, NULL, 16) : -1; + int value = arg_value ? strtoul(arg_value, NULL, 16) : -1; + + if (reg >= 0) { + if (value >= 0) { + ch375_write_reg(&ch375_dev, reg, value); + printf("Reg[%2x] = %2x\n", reg, ch375_read_reg(&ch375_dev, reg)); + } else { + printf("Reg[%2x] = %2x\n", reg, ch375_read_reg(&ch375_dev, reg)); + } + } else { + for (reg = 0; reg < 0xFF; ) { + int max = reg + 16; + printf("Regs[%2x..%2x] =", reg, max-1); + for (; reg < max; reg++) { + printf(" %2x", ch375_read_reg(&ch375_dev, reg)); + } + printf("\n"); + } + } +} + void usb_host_off() { printf("Set usb mode disabled...\n"); @@ -251,13 +281,9 @@ int disk() int i = 0; if (ready) { - printf("Disk ready\n"); + printf("Disk already ready\n"); } else { - printf("Disk not ready\n"); - - if (!usb_host_connect()) { - return EXIT_FAILURE; - } + printf("Disk not ready, so trying to init\n"); if (!init_disk()) { return EXIT_FAILURE; @@ -328,6 +354,35 @@ int disk_read(uint32_t lba) return EXIT_SUCCESS; } +static void disk_reads(const char *arg) +{ + unsigned long lba = 0; + if (arg) { + lba = strtoul(arg, NULL, 10); + } + disk_read(lba); +} + +int disk_set_lun(int lun) +{ + uint8_t max_lun = ch375_read_reg(&ch375_dev, CH375_REG_DISK_MAX_LUN); + uint8_t cur_lun = ch375_read_reg(&ch375_dev, CH375_REG_DISK_LUN); + + printf("Disk max LUNs = %u , cur LUN = %u\n", max_lun, cur_lun); + + if (lun >= 0) { + ch375_write_reg(&ch375_dev, CH375_REG_DISK_LUN, lun); + printf("Set cur LUN to %u\n", lun); + } + + return EXIT_SUCCESS; +} + +void disk_set_luns(const char *luns) +{ + disk_set_lun(luns ? atoi(luns) : -1); +} + int autou() { uint8_t status; @@ -374,7 +429,7 @@ int autou() return EXIT_SUCCESS; } -int host() +int host_descr() { usb_device_descriptor *dev_descr; int n; @@ -437,63 +492,102 @@ int host() return EXIT_SUCCESS; } +typedef void (*cmd_func)(); +typedef void (*cmd_func1)(const char *arg1); +typedef void (*cmd_func2)(const char *arg1, const char *arg2); + +struct cmd { + const char *opt; + cmd_func func; + const char *arg1; + const char *arg2; +} cmds[] = { + {"disk", (cmd_func) disk}, + {"dread", (cmd_func)disk_reads, "[sector]"}, + {"dlun", (cmd_func)disk_set_luns, "[lun]"}, + {"dsize", (cmd_func)disk_size}, + {"dready", (cmd_func)disk_ready}, + + {"auto", (cmd_func)autou}, + {"host", (cmd_func)usb_host_on}, + {"hdescr", (cmd_func)host_descr}, + {"hconn", (cmd_func)usb_host_connect}, + {"hoff", (cmd_func)usb_host_off}, + {"hreset", (cmd_func)usb_host_reset}, + + {"reg", (cmd_func)chip_reg, "[reg]", "[value]"}, + + {"sleep", (cmd_func)chip_sleep}, + {"reset", (cmd_func)chip_reset}, + {"int", (cmd_func)dump_int_status}, + {"vers", (cmd_func)chip_version}, +}; + +#define NUM_COMMANDS (sizeof(cmds)/sizeof(struct cmd)) + +static void usage() +{ + int i, j; + printf("Usage: utest <cmd> <args>\n"); + printf("Supported commands: \n"); + for (i = 0; i < NUM_COMMANDS; i++) { + struct cmd *cmd = &cmds[i]; + printf(" %s", cmd->opt); + if (cmd->arg1) printf(" %s", cmd->arg1); + if (cmd->arg2) printf(" %s", cmd->arg2); + printf("\n"); + } + printf("To change IO port, set CH375_PORT env var\n"); +} + +static struct cmd * find_cmd(const char *arg) +{ + int i; + for (i = 0; i < NUM_COMMANDS; i++) { + struct cmd *cmd = &cmds[i]; + if (stricmp(cmd->opt, arg) == 0) { + return cmd; + } + } + return NULL; +} + int main(int argc, const char *argv[]) { - char mode = '?'; + const char *mode = argc > 1 ? argv[1] : 0; char *port = getenv("CH375_PORT"); + int rc; + struct cmd *cmd; if (port) { unsigned long lport = strtoul(port, NULL, 16); ch375_dev.io_base = lport; } - if (argc > 1) { - mode = argv[1][0]; + if (!mode || stricmp(mode, "help") == 0 || stricmp(mode, "?") == 0) { + usage(); + return EXIT_FAILURE; + } + + + cmd = find_cmd(mode); + if (!cmd) { + printf("Unrecognized command: %s\n", mode); + return EXIT_FAILURE; } - printf("Test mode %c using IO port 0x%x\n", mode, ch375_dev.io_base); + printf("\"%s\", using IO port 0x%x\n", cmd->opt, ch375_dev.io_base); dump_int_status(); - switch (mode) { - case 'd': - return disk(); - case 'e': - return disk_read(0); - case 'a': - return autou(); - case 'm': - return host(); - case 'h': - usb_host_on(); - break; - case 'c': - usb_host_connect(); - break; - case 'o': - usb_host_off(); - break; - case 'b': - usb_host_reset(); - break; - case 's': - chip_sleep(); - break; - case 'r': - chip_reset(); - break; - case 'i': - dump_int_status(); - break; - case 'v': - printf("IC Version: 0x%hx\n", ch375_get_version(&ch375_dev)); - break; - case '?': - printf("test modes: d e a m h c o b s r i v\n"); - break; - default: - printf("Unknown mode %c\n", mode); - return EXIT_FAILURE; + if (!cmd->arg1) { + cmd->func(); + } else if (cmd->arg1 && !cmd->arg2) { + cmd_func1 func = (cmd_func1) cmd->func; + func(argc > 2 ? argv[2] : 0); + } else if (cmd->arg1 && cmd->arg2) { + cmd_func2 func = (cmd_func2) cmd->func; + func(argc > 2 ? argv[2] : 0, argc > 3 ? argv[3] : 0); } return EXIT_SUCCESS; diff --git a/inc/chuver.h b/inc/chuver.h index f816114..855eaef 100644 --- a/inc/chuver.h +++ b/inc/chuver.h @@ -2,6 +2,6 @@ #define CHUVER_H #define CHUDISK_VERSION_MAJOR 1 -#define CHUDISK_VERSION_MINOR 0x0 +#define CHUDISK_VERSION_MINOR 0x01 #endif /* CHUVER_H */ diff --git a/linux/chudisk.c b/linux/chudisk.c index 31dd9e5..3d6f79b 100644 --- a/linux/chudisk.c +++ b/linux/chudisk.c @@ -28,8 +28,8 @@ /* module parameters */ /** configurable major number */ -/* by default, reuse the one from Mylex DAC960 PCI RAID controller; sixth controller" */ -static short major = 53; +/* by default, reuse one in the experimental space */ +static short major = 63; /** IO base */ static short io = 0x260; /** skip device proving */ @@ -39,7 +39,7 @@ static int softblk = 1024; /* for blk.h: (needs to be defined before blk.h/lnxabs.h is included) */ #define MAJOR_NR major -#define DEVICE_NAME "CH375" +#define DEVICE_NAME "chud" #define DEVICE_REQUEST do_chudisk_request #define DEVICE_NR(device) (MINOR(device)>>CHUDISK_MINOR_SHIFT) #define DEVICE_ON(device) @@ -48,9 +48,9 @@ static int softblk = 1024; #include "lnxabs.h" #include "ch375lnx.h" +#include <chuver.h> #include <scsi.h> -#define ENABLE_DLOG 0 #if ENABLE_DLOG #define dprintf(args...) printk(KERN_DEBUG args) #else @@ -87,7 +87,7 @@ static struct ch375 chudisk_dev[CHUDISK_MAX_UNITS]; /** main gendisk layer data structure, which handles partioning */ static struct gendisk chudisk_gendisk = { - .major_name = "chud", + .major_name = DEVICE_NAME, .minor_shift = CHUDISK_MINOR_SHIFT, .max_p = CHUDISK_MAX_PARTITIONS, .max_nr = CHUDISK_MAX_UNITS, @@ -148,7 +148,7 @@ static unsigned disk_read_sectors(struct ch375 *dev, uint8_t *buffer, uint32_t l uint8_t result; unsigned total = 0; // Total bytes written - dprintf("disk read buf=%p sector=%lu num=%hu\n", buffer, lba, num); + dprintf("disk read buf=%p sector=%u num=%hu\n", buffer, lba, num); // Initiate read operation ch375_disk_read(dev, lba, num); @@ -197,7 +197,7 @@ static unsigned disk_write_sectors(struct ch375 *dev, uint8_t *buffer, uint32_t uint8_t result; unsigned total = 0; // Total bytes written - dprintf("disk write buf=%p sector=%lu num=%hu\n", buffer, lba, num); + dprintf("disk write buf=%p sector=%u num=%hu\n", buffer, lba, num); // Initiate operation ch375_disk_write(dev, lba, num); @@ -387,7 +387,7 @@ static int chudisk_revalidate(kdev_t devno) blk_size = ntohl(*(uint32_t*)&buffer[4]); sector_cnt = ntohl(*(uint32_t*)&buffer[0]); - dprintf("chudisk: sector size %u num sectors %u\n", blk_size, sector_cnt); + dprintf("chudisk[%d]: sector size %u num sectors %u\n", unit, blk_size, sector_cnt); // TODO Support non-default block_size dev->sec_size = blk_size; @@ -566,7 +566,8 @@ static __init int chudisk_init(void) return -ENXIO; } - printk(KERN_INFO "chudisk: found CH375 v.%x at 0x%hx, using major %d\n", + printk(KERN_INFO "chudisk: v%x.%x, found CH375 v.%x at 0x%hx, using major %d\n", + CHUDISK_VERSION_MAJOR, CHUDISK_VERSION_MINOR, ch375_get_version(dev), dev->io_base, MAJOR_NR); // Set defaults for unit @@ -585,7 +586,8 @@ static __init int chudisk_init(void) blksize_size[MAJOR_NR] = chudisk_blksizes; hardsect_size[MAJOR_NR] = chudisk_hardsectsizes; - chudisk_gendisk.major = major; + // Add ourselves to the genhd linked list + chudisk_gendisk.major = MAJOR_NR; chudisk_gendisk.nr_real = 1; chudisk_gendisk.next = gendisk_head; gendisk_head = &chudisk_gendisk; @@ -597,11 +599,20 @@ static __init int chudisk_init(void) static void chudisk_exit(void) { + struct ch375 *dev = &chudisk_dev[0]; struct gendisk **gdp; + int i; - unregister_blkdev(MAJOR_NR, "chud"); - release_region(io, 2); + // Flush buffers for all minors + for (i = 0; i < CHUDISK_MAX_MINORS; i++) { + kdev_t mdevno = MKDEV(MAJOR_NR, i); + fsync_dev(mdevno); + invalidate_inodes_dev(mdevno); + invalidate_buffers(mdevno); + } + + // Remove from genhd list for(gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) { if (*gdp == &chudisk_gendisk) { *gdp = (*gdp)->next; @@ -609,14 +620,23 @@ static void chudisk_exit(void) } } + // Remove from blkdev blk_dev[MAJOR_NR].request_fn = NULL; blk_size[MAJOR_NR] = NULL; blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; + unregister_blkdev(MAJOR_NR, "chud"); + + // Turn off the chip + ch375_set_usb_mode(dev, CH375_USB_MODE_HOST_DISABLED); + mdelay(1); + ch375_enter_sleep(dev); + + // And unregister driver + release_region(io, 2); dprintf("chudisk: bye\n"); } - module_init(chudisk_init); module_exit(chudisk_exit); diff --git a/linux/makefile b/linux/makefile index f293cc1..9e07d27 100644 --- a/linux/makefile +++ b/linux/makefile @@ -2,6 +2,9 @@ MODDIR := /lib/modules/$(shell uname -r) INCLUDE := -isystem $(MODDIR)/build/include -isystem ../inc CFLAGS := -O2 -D__KERNEL__ -DMODULE -Wall $(INCLUDE) +ifeq ($(debug),1) +CFLAGS += -DENABLE_DLOG +endif ifneq (,$(wildcard $(MODDIR)/build/include/linux/modversions.h)) CFLAGS += -DMODVERSIONS endif @@ -16,7 +19,7 @@ clean: install: chudisk.o install -m0644 chudisk.o $(MODDIR)/block - depmod -A + depmod -a uninstall: rm -f $(MODDIR)/block/chudisk.o diff --git a/linux/mkdev.sh b/linux/mkdev.sh index d1393e6..1a05b91 100755 --- a/linux/mkdev.sh +++ b/linux/mkdev.sh @@ -1,6 +1,6 @@ #!/bin/sh -major=53 +major=63 if [ $# -gt 0 ]; then major=$1 fi |
