This patch series adds the ability to boot from USB flash drives. The first seven patches are just cleanups. The eighth adds the MSC support.
This is only preliminary support - I've booted under qemu using a "-usbdevice disk:foo" drive, and I've also booted my epia-cn with seabios and coreboot. However, handling of error conditions and timers still needs to be improved. Also, the current support is UHCI only (no ohci).
Further testing on real hardware would be appreciated.
-Kevin
Kevin O'Connor (8): Dynamically allocate each drive_g with malloc_fseg(). Move common "command data block" functions to new file blockcmd.c. Don't require a valid physical cylinders/heads/spt for logical mapping. Fix off by one error in strtcpy. Minor - arrange struct drive_s to clarify field roles. USB UHCI cleanups. Introduce helper functions for finding USB end-points. Initial support for booting from USB drives.
Makefile | 4 +- src/ata.c | 36 +++----- src/biosvar.h | 6 +- src/block.c | 69 ++++++--------- src/blockcmd.c | 78 +++++++++++++++++ src/blockcmd.h | 19 ++++ src/boot.c | 2 +- src/cdrom.c | 69 ++++----------- src/config.h | 6 +- src/disk.h | 33 +++---- src/floppy.c | 7 +- src/usb-hid.c | 26 ++---- src/usb-msc.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-msc.h | 25 ++++++ src/usb-uhci.c | 142 +++++++++++++++++++++++++++++-- src/usb-uhci.h | 4 +- src/usb.c | 81 +++++++++++++++++- src/usb.h | 6 ++ src/util.c | 2 +- 19 files changed, 706 insertions(+), 170 deletions(-) create mode 100644 src/blockcmd.c create mode 100644 src/usb-msc.c create mode 100644 src/usb-msc.h
This eliminates the limit on the number of available drives. It also allows for each driver to allocate additional custom fields. --- src/ata.c | 14 ++++++++++---- src/biosvar.h | 6 ++++-- src/block.c | 46 +++++++++++++--------------------------------- src/boot.c | 2 +- src/cdrom.c | 10 ++++++---- src/config.h | 4 +--- src/disk.h | 11 +++-------- src/floppy.c | 7 +++++-- 8 files changed, 43 insertions(+), 57 deletions(-)
diff --git a/src/ata.c b/src/ata.c index a27da56..f935e1f 100644 --- a/src/ata.c +++ b/src/ata.c @@ -775,9 +775,12 @@ init_drive_atapi(struct drive_s *dummy, u16 *buffer) return NULL;
// Success - setup as ATAPI. - struct drive_s *drive_g = allocDrive(); - if (! drive_g) + struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g)); + if (! drive_g) { + warn_noalloc(); return NULL; + } + memset(drive_g, 0, sizeof(*drive_g)); SET_GLOBAL(drive_g->cntl_id, dummy->cntl_id); extract_identify(drive_g, buffer); SET_GLOBAL(drive_g->type, DTYPE_ATAPI); @@ -821,9 +824,12 @@ init_drive_ata(struct drive_s *dummy, u16 *buffer) return NULL;
// Success - setup as ATA. - struct drive_s *drive_g = allocDrive(); - if (! drive_g) + struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g)); + if (! drive_g) { + warn_noalloc(); return NULL; + } + memset(drive_g, 0, sizeof(*drive_g)); SET_GLOBAL(drive_g->cntl_id, dummy->cntl_id); extract_identify(drive_g, buffer); SET_GLOBAL(drive_g->type, DTYPE_ATA); diff --git a/src/biosvar.h b/src/biosvar.h index d011966..c67503c 100644 --- a/src/biosvar.h +++ b/src/biosvar.h @@ -329,9 +329,11 @@ static inline u16 get_global_seg(void) { (var) = (val); \ } while (0) #if MODESEGMENT -#define ADJUST_GLOBAL_PTR(var) (var) +#define STORE_GLOBAL_PTR(var) (var) +#define RETRIEVE_GLOBAL_PTR(var) (var) #else -#define ADJUST_GLOBAL_PTR(var) ((typeof(var))((void*)var - BUILD_BIOS_ADDR)) +#define STORE_GLOBAL_PTR(var) ((typeof(var))((void*)var - BUILD_BIOS_ADDR)) +#define RETRIEVE_GLOBAL_PTR(var) ((typeof(var))((void*)var + BUILD_BIOS_ADDR)) #endif
diff --git a/src/block.c b/src/block.c index 3a9a68d..63aa368 100644 --- a/src/block.c +++ b/src/block.c @@ -16,30 +16,9 @@ struct drives_s Drives VAR16VISIBLE; struct drive_s * getDrive(u8 exttype, u8 extdriveoffset) { - // basic check : device has to be defined if (extdriveoffset >= ARRAY_SIZE(Drives.idmap[0])) return NULL; - - // Get the ata channel - u8 driveid = GET_GLOBAL(Drives.idmap[exttype][extdriveoffset]); - - // basic check : device has to be valid - if (driveid >= ARRAY_SIZE(Drives.drives)) - return NULL; - - return &Drives.drives[driveid]; -} - -struct drive_s * -allocDrive(void) -{ - int driveid = Drives.drivecount; - if (driveid >= ARRAY_SIZE(Drives.drives)) - return NULL; - Drives.drivecount++; - struct drive_s *drive_g = &Drives.drives[driveid]; - memset(drive_g, 0, sizeof(*drive_g)); - return drive_g; + return RETRIEVE_GLOBAL_PTR(GET_GLOBAL(Drives.idmap[exttype][extdriveoffset])); }
@@ -208,11 +187,12 @@ map_hd_drive(struct drive_s *drive_g) { // fill hdidmap u8 hdcount = GET_BDA(hdcount); - if (hdcount >= ARRAY_SIZE(Drives.idmap[0])) + if (hdcount >= ARRAY_SIZE(Drives.idmap[0])) { + warn_noalloc(); return; + } dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount); - int driveid = drive_g - Drives.drives; - SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid); + Drives.idmap[EXTTYPE_HD][hdcount] = STORE_GLOBAL_PTR(drive_g); SET_BDA(hdcount, hdcount + 1);
// Fill "fdpt" structure. @@ -221,22 +201,22 @@ map_hd_drive(struct drive_s *drive_g)
// Find spot to add a drive static void -add_ordered_drive(u8 *idmap, u8 *count, struct drive_s *drive_g) +add_ordered_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive_g) { if (*count >= ARRAY_SIZE(Drives.idmap[0])) { warn_noalloc(); return; } - u8 *pos = &idmap[*count]; + struct drive_s **pos = &idmap[*count]; *count = *count + 1; if (CONFIG_THREADS) { // Add to idmap with assured drive order. - u8 *end = pos; + struct drive_s **end = pos; for (;;) { - u8 *prev = pos - 1; + struct drive_s **prev = pos - 1; if (prev < idmap) break; - struct drive_s *prevdrive = &Drives.drives[*prev]; + struct drive_s *prevdrive = *prev; if (prevdrive->type < drive_g->type || (prevdrive->type == drive_g->type && prevdrive->cntl_id < drive_g->cntl_id)) @@ -246,7 +226,7 @@ add_ordered_drive(u8 *idmap, u8 *count, struct drive_s *drive_g) if (pos != end) memmove(pos+1, pos, (void*)end-(void*)pos); } - *pos = drive_g - Drives.drives; + *pos = STORE_GLOBAL_PTR(drive_g); }
// Map a cd @@ -312,6 +292,7 @@ describe_drive(struct drive_s *drive_g) int process_op(struct disk_op_s *op) { + ASSERT16(); u8 type = GET_GLOBAL(op->drive_g->type); switch (type) { case DTYPE_FLOPPY: @@ -355,9 +336,9 @@ __send_disk_op(struct disk_op_s *op_far, u16 op_seg) int send_disk_op(struct disk_op_s *op) { + ASSERT16(); if (! CONFIG_DRIVES) return -1; - ASSERT16();
return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op); } @@ -371,5 +352,4 @@ void drive_setup(void) { memset(&Drives, 0, sizeof(Drives)); - memset(&Drives.idmap, 0xff, sizeof(Drives.idmap)); } diff --git a/src/boot.c b/src/boot.c index 151b0d3..6b69dc2 100644 --- a/src/boot.c +++ b/src/boot.c @@ -321,7 +321,7 @@ boot_prep(void)
// Setup floppy boot order int override = IPL.bev[0].subchoice; - int tmp = Drives.idmap[EXTTYPE_FLOPPY][0]; + struct drive_s *tmp = Drives.idmap[EXTTYPE_FLOPPY][0]; Drives.idmap[EXTTYPE_FLOPPY][0] = Drives.idmap[EXTTYPE_FLOPPY][override]; Drives.idmap[EXTTYPE_FLOPPY][override] = tmp;
diff --git a/src/cdrom.c b/src/cdrom.c index 4e70729..06adefa 100644 --- a/src/cdrom.c +++ b/src/cdrom.c @@ -113,12 +113,14 @@ cdemu_setup(void) if (!CONFIG_CDROM_EMU) return;
- struct drive_s *drive_g = allocDrive(); - if (!drive_g) { + struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g)); + if (! drive_g) { + warn_noalloc(); cdemu_drive = NULL; return; } - cdemu_drive = ADJUST_GLOBAL_PTR(drive_g); + memset(drive_g, 0, sizeof(*drive_g)); + cdemu_drive = STORE_GLOBAL_PTR(drive_g); drive_g->type = DTYPE_CDEMU; drive_g->blksize = DISK_SECTOR_SIZE; drive_g->sectors = (u64)-1; @@ -327,7 +329,7 @@ cdrom_boot(int cdid) u8 media = buffer[0x21]; SET_EBDA2(ebda_seg, cdemu.media, media);
- SET_EBDA2(ebda_seg, cdemu.emulated_drive, ADJUST_GLOBAL_PTR(dop.drive_g)); + SET_EBDA2(ebda_seg, cdemu.emulated_drive, STORE_GLOBAL_PTR(dop.drive_g));
u16 boot_segment = *(u16*)&buffer[0x22]; if (!boot_segment) diff --git a/src/config.h b/src/config.h index c02d496..e359652 100644 --- a/src/config.h +++ b/src/config.h @@ -119,15 +119,13 @@ #define CONFIG_USE_SMM 1 // Maximum number of map entries in the e820 map #define CONFIG_MAX_E820 32 -// Space to reserve in f-segment for run-time built bios tables. +// Space to reserve in f-segment for dynamic allocations #define CONFIG_MAX_BIOSTABLE 2048 // Space to reserve in high-memory for tables #define CONFIG_MAX_HIGHTABLE (64*1024)
// Maximum number of ATA controllers to support #define CONFIG_MAX_ATA_INTERFACES 4 -// Maximum number of internal drives supported -#define CONFIG_MAX_DRIVES 8 // Largest supported externaly facing drive id #define CONFIG_MAX_EXTDRIVE 16
diff --git a/src/disk.h b/src/disk.h index ac5748e..635d511 100644 --- a/src/disk.h +++ b/src/disk.h @@ -173,7 +173,7 @@ struct chs_s { };
struct drive_s { - u8 type; // Detected type of drive (ata/atapi/none) + u8 type; // Driver type (DTYPE_*) u8 removable; // Removable device flag u16 blksize; // block size u32 cntl_id; @@ -205,14 +205,10 @@ struct drive_s { #define TRANSLATION_RECHS 3
struct drives_s { - // info on each internally handled drive - struct drive_s drives[CONFIG_MAX_DRIVES]; - u8 drivecount; - // - // map between bios floppy/hd/cd id and driveid index into drives[] + // map between bios floppy/hd/cd id and drive_s struct u8 floppycount; u8 cdcount; - u8 idmap[3][CONFIG_MAX_EXTDRIVE]; + struct drive_s *idmap[3][CONFIG_MAX_EXTDRIVE]; };
#define EXTTYPE_FLOPPY 0 @@ -230,7 +226,6 @@ struct drives_s { // block.c extern struct drives_s Drives; struct drive_s *getDrive(u8 exttype, u8 extdriveoffset); -struct drive_s *allocDrive(void); void setup_translation(struct drive_s *drive_g); void map_floppy_drive(struct drive_s *drive_g); void map_hd_drive(struct drive_s *drive_g); diff --git a/src/floppy.c b/src/floppy.c index dba3e00..d2e689c 100644 --- a/src/floppy.c +++ b/src/floppy.c @@ -96,9 +96,12 @@ addFloppy(int floppyid, int ftype, int driver) return NULL; }
- struct drive_s *drive_g = allocDrive(); - if (!drive_g) + struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g)); + if (!drive_g) { + warn_noalloc(); return NULL; + } + memset(drive_g, 0, sizeof(*drive_g)); drive_g->cntl_id = floppyid; drive_g->type = driver; drive_g->blksize = DISK_SECTOR_SIZE;
Move common "cdb" request functions to a new file. --- Makefile | 2 +- src/ata.c | 22 +++---------------- src/blockcmd.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/blockcmd.h | 5 ++++ src/cdrom.c | 59 ++++++++++------------------------------------------- 5 files changed, 83 insertions(+), 67 deletions(-) create mode 100644 src/blockcmd.c
diff --git a/Makefile b/Makefile index 17d4e1f..e087818 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ OUT=out/ # Source files SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \ kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \ - pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c \ + pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ usb.c usb-uhci.c usb-ohci.c usb-hid.c usb-hub.c paravirt.c SRC16=$(SRCBOTH) system.c disk.c apm.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ diff --git a/src/ata.c b/src/ata.c index f935e1f..3c57f9f 100644 --- a/src/ata.c +++ b/src/ata.c @@ -653,32 +653,18 @@ atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) fail: // Enable interrupts outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); - return ret; -} - -// Read sectors from the cdrom. -int -cdrom_read(struct disk_op_s *op) -{ - struct cdb_rwdata_10 cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_READ_10; - cmd.lba = htonl(op->lba); - cmd.count = htons(op->count); - return atapi_cmd_data(op, &cmd, CDROM_SECTOR_SIZE); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; }
// 16bit command demuxer for ATAPI cdroms. int process_atapi_op(struct disk_op_s *op) { - int ret; switch (op->command) { case CMD_READ: - ret = cdrom_read(op); - if (ret) - return DISK_RET_EBADTRACK; - return DISK_RET_SUCCESS; + return cdb_read(op); case CMD_FORMAT: case CMD_WRITE: return DISK_RET_EWRITEPROTECT; diff --git a/src/blockcmd.c b/src/blockcmd.c new file mode 100644 index 0000000..5efbdce --- /dev/null +++ b/src/blockcmd.c @@ -0,0 +1,62 @@ +// Support for several common scsi like command data block requests +// +// Copyright (C) 2010 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "util.h" // htonl +#include "disk.h" // struct disk_op_s +#include "blockcmd.h" // struct cdb_request_sense +#include "ata.h" // atapi_cmd_data + +static int +cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + u8 type = GET_GLOBAL(op->drive_g->type); + switch (type) { + case DTYPE_ATAPI: + return atapi_cmd_data(op, cdbcmd, blocksize); + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} + +// Request SENSE +int +cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_REQUEST_SENSE; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Request capacity +int +cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) +{ + struct cdb_read_capacity cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_CAPACITY; + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Read sectors. +int +cdb_read(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_10; + cmd.lba = htonl(op->lba); + cmd.count = htons(op->count); + return cdb_cmd_data(op, &cmd, CDROM_SECTOR_SIZE); +} diff --git a/src/blockcmd.h b/src/blockcmd.h index 28a4889..d645ebe 100644 --- a/src/blockcmd.h +++ b/src/blockcmd.h @@ -55,4 +55,9 @@ struct cdbres_request_sense { u32 reserved_0e; } PACKED;
+// blockcmd.c +int cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data); +int cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data); +int cdb_read(struct disk_op_s *op); + #endif // blockcmd.h diff --git a/src/cdrom.c b/src/cdrom.c index 06adefa..4e14a5c 100644 --- a/src/cdrom.c +++ b/src/cdrom.c @@ -182,45 +182,6 @@ cdemu_134b(struct bregs *regs) * CD booting ****************************************************************/
-// Request SENSE -static int -atapi_get_sense(struct disk_op_s *op, u8 *asc, u8 *ascq) -{ - struct cdb_request_sense cmd; - struct cdbres_request_sense data; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_REQUEST_SENSE; - cmd.length = sizeof(data); - op->count = 1; - op->buf_fl = &data; - int ret = atapi_cmd_data(op, &cmd, sizeof(data)); - if (ret) - return ret; - - *asc = data.asc; - *ascq = data.ascq; - return 0; -} - -// Request capacity -static int -atapi_read_capacity(struct disk_op_s *op, u32 *blksize, u32 *sectors) -{ - struct cdb_read_capacity cmd; - struct cdbres_read_capacity data; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_READ_CAPACITY; - op->count = 1; - op->buf_fl = &data; - int ret = atapi_cmd_data(op, &cmd, sizeof(data)); - if (ret) - return ret; - - *blksize = ntohl(data.blksize); - *sectors = ntohl(data.sectors); - return 0; -} - static int atapi_is_ready(struct disk_op_s *op) { @@ -229,7 +190,7 @@ atapi_is_ready(struct disk_op_s *op) /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT is * reported by the device. If the device reports "IN PROGRESS", * 30 seconds is added. */ - u32 blksize, sectors; + struct cdbres_read_capacity info; int in_progress = 0; u64 end = calc_future_tsc(5000); for (;;) { @@ -238,24 +199,24 @@ atapi_is_ready(struct disk_op_s *op) return -1; }
- int ret = atapi_read_capacity(op, &blksize, §ors); + int ret = cdb_read_capacity(op, &info); if (!ret) // Success break;
- u8 asc, ascq; - ret = atapi_get_sense(op, &asc, &ascq); + struct cdbres_request_sense sense; + ret = cdb_get_sense(op, &sense); if (ret) // Error - retry. continue;
// Sense succeeded. - if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ + if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */ dprintf(1, "Device reports MEDIUM NOT PRESENT\n"); return -1; }
- if (asc == 0x04 && ascq == 0x01 && !in_progress) { + if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) { /* IN PROGRESS OF BECOMING READY */ printf("Waiting for device to detect medium... "); /* Allow 30 seconds more */ @@ -264,6 +225,7 @@ atapi_is_ready(struct disk_op_s *op) } }
+ u32 blksize = ntohl(info.blksize), sectors = ntohl(info.sectors); if (blksize != GET_GLOBAL(op->drive_g->blksize)) { printf("Unsupported sector size %u\n", blksize); return -1; @@ -292,7 +254,7 @@ cdrom_boot(int cdid) dop.lba = 0x11; dop.count = 1; dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer); - ret = cdrom_read(&dop); + ret = cdb_read(&dop); if (ret) return 3;
@@ -307,7 +269,8 @@ cdrom_boot(int cdid)
// And we read the Boot Catalog dop.lba = lba; - ret = cdrom_read(&dop); + dop.count = 1; + ret = cdb_read(&dop); if (ret) return 7;
@@ -347,7 +310,7 @@ cdrom_boot(int cdid) dop.lba = lba; dop.count = DIV_ROUND_UP(nbsectors, 4); dop.buf_fl = MAKE_FLATPTR(boot_segment, 0); - ret = cdrom_read(&dop); + ret = cdb_read(&dop); if (ret) return 12;
Modern drives don't provide a "physical" chs report - try to detect that and force an LBA mapping in that case.
Also, don't require setup_translation be called only on ATA drives. --- src/block.c | 17 ++++++++++------- 1 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/block.c b/src/block.c index 63aa368..365053e 100644 --- a/src/block.c +++ b/src/block.c @@ -40,10 +40,15 @@ get_translation(struct drive_s *drive_g) return translation; }
- // On COREBOOT, use a heuristic to determine translation type. + // Otherwise use a heuristic to determine translation type. u16 heads = GET_GLOBAL(drive_g->pchs.heads); u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders); u16 spt = GET_GLOBAL(drive_g->pchs.spt); + u64 sectors = GET_GLOBAL(drive_g->sectors); + u64 psectors = (u64)heads * cylinders * spt; + if (!heads || !cylinders || !spt || psectors > sectors) + // pchs doesn't look valid - use LBA. + return TRANSLATION_LBA;
if (cylinders <= 1024 && heads <= 16 && spt <= 63) return TRANSLATION_NONE; @@ -58,9 +63,6 @@ setup_translation(struct drive_s *drive_g) u8 translation = get_translation(drive_g); SET_GLOBAL(drive_g->translation, translation);
- u8 ataid = GET_GLOBAL(drive_g->cntl_id); - u8 channel = ataid / 2; - u8 slave = ataid % 2; u16 heads = GET_GLOBAL(drive_g->pchs.heads); u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders); u16 spt = GET_GLOBAL(drive_g->pchs.spt); @@ -120,11 +122,12 @@ setup_translation(struct drive_s *drive_g) // clip to 1024 cylinders in lchs if (cylinders > 1024) cylinders = 1024; - dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d\n" - , channel, slave + dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n" + , drive_g , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt , desc - , cylinders, heads, spt); + , cylinders, heads, spt + , (u32)sectors);
SET_GLOBAL(drive_g->lchs.heads, heads); SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
The strtcpy function could overrun its output buffer. --- src/util.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/util.c b/src/util.c index b89a2b5..bbef995 100644 --- a/src/util.c +++ b/src/util.c @@ -262,7 +262,7 @@ char * strtcpy(char *dest, const char *src, size_t len) { char *d = dest; - while (len-- && *src != '\0') + while (--len && *src != '\0') *d++ = *src++; *d = '\0'; return dest;
--- src/disk.h | 23 ++++++++++++----------- 1 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/src/disk.h b/src/disk.h index 635d511..90ca04d 100644 --- a/src/disk.h +++ b/src/disk.h @@ -173,20 +173,21 @@ struct chs_s { };
struct drive_s { - u8 type; // Driver type (DTYPE_*) - u8 removable; // Removable device flag - u16 blksize; // block size + u8 type; // Driver type (DTYPE_*) + u8 floppy_type; // Type of floppy (only for floppy drives). + struct chs_s lchs; // Logical CHS + u64 sectors; // Total sectors count + + // Info for EDD calls + u16 blksize; // block size + struct chs_s pchs; // Physical CHS + u8 translation; // type of translation + + // Driver specific u32 cntl_id; u32 cntl_info; - u8 floppy_type; // Type of floppy (only for floppy drives). - + u8 removable; // Removable device flag char model[41]; - - u8 translation; // type of translation - struct chs_s lchs; // Logical CHS - struct chs_s pchs; // Physical CHS - - u64 sectors; // Total sectors count };
#define DISK_SECTOR_SIZE 512
Add barrier() calls before memory accesses that can be seen by controller. Fix TD_CTRL_ANY_ERROR macro definition. Other cleanups. --- src/usb-uhci.c | 30 +++++++++++++++++++++++------- src/usb-uhci.h | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/src/usb-uhci.c b/src/usb-uhci.c index c3ff744..ca51337 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -72,6 +72,7 @@ configure_uhci(struct usb_s *cntl) for (i=0; i<ARRAY_SIZE(fl->links); i++) fl->links[i] = (u32)intr_qh | UHCI_PTR_QH; cntl->uhci.framelist = fl; + barrier();
// Set the frame length to the default: 1 ms exactly outb(USBSOF_DEFAULT, cntl->uhci.iobase + USBSOF); @@ -187,6 +188,13 @@ wait_qh(struct usb_s *cntl, struct uhci_qh *qh) } }
+static void +uhci_waittick(void) +{ + // XXX - implement real tick detection. + msleep(2); +} + int uhci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize) @@ -233,15 +241,15 @@ uhci_control(u32 endp, int dir, const void *cmd, int cmdsize
// Transfer data struct uhci_qh *data_qh = cntl->uhci.qh; + barrier(); data_qh->element = (u32)&tds[0]; int ret = wait_qh(cntl, data_qh); if (ret) { data_qh->element = UHCI_PTR_TERM; - // XXX - leak tds - return ret; + uhci_waittick(); } free(tds); - return 0; + return ret; }
struct usb_pipe * @@ -262,11 +270,12 @@ uhci_alloc_intr_pipe(u32 endp, int frameexp) int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms); struct uhci_qh *qh = malloc_low(sizeof(*qh)); struct uhci_td *tds = malloc_low(sizeof(*tds) * count); - if (!qh || !tds || maxpacket > sizeof(tds[0].data)) { - free(qh); - free(tds); - return NULL; + if (!qh || !tds) { + warn_noalloc(); + goto fail; } + if (maxpacket > sizeof(tds[0].data)) + goto fail; qh->element = (u32)tds; int toggle = 0; int i; @@ -290,15 +299,21 @@ uhci_alloc_intr_pipe(u32 endp, int frameexp) // Add to existing interrupt entry. struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS); qh->link = intr_qh->link; + barrier(); intr_qh->link = (u32)qh | UHCI_PTR_QH; } else { int startpos = 1<<(frameexp-1); qh->link = fl->links[startpos]; + barrier(); for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) fl->links[i] = (u32)qh | UHCI_PTR_QH; }
return &qh->pipe; +fail: + free(qh); + free(tds); + return NULL; }
int @@ -324,6 +339,7 @@ uhci_poll_intr(struct usb_pipe *pipe, void *data)
// Reenable this td. u32 next = GET_FLATPTR(td->link); + barrier(); SET_FLATPTR(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS) | TD_CTRL_ACTIVE)); SET_FLATPTR(qh->next_td, (void*)(next & ~UHCI_PTR_BITS)); diff --git a/src/usb-uhci.h b/src/usb-uhci.h index 03ac9cd..d9c10ae 100644 --- a/src/usb-uhci.h +++ b/src/usb-uhci.h @@ -88,7 +88,7 @@ struct uhci_framelist { #define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ - TD_CTRL_BABBLE | TD_CTRL_CRCTIME | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIMEO | \ TD_CTRL_BITSTUFF) #define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
Introduce findEndPointDesc() and mkendpFromDesc() and refactor usb-hid code. --- src/usb-hid.c | 26 ++++++++------------------ src/usb.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/usb.h | 4 ++++ 3 files changed, 57 insertions(+), 18 deletions(-)
diff --git a/src/usb-hid.c b/src/usb-hid.c index 48dec8b..ae16686 100644 --- a/src/usb-hid.c +++ b/src/usb-hid.c @@ -54,30 +54,20 @@ usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax) return -1; dprintf(2, "usb_keyboard_setup %x\n", endp);
- struct usb_endpoint_descriptor *epdesc = (void*)&iface[1]; - for (;;) { - if ((void*)epdesc >= (void*)iface + imax - || epdesc->bDescriptorType == USB_DT_INTERFACE) { - dprintf(1, "No keyboard intr in?\n"); - return -1; - } - if (epdesc->bDescriptorType == USB_DT_ENDPOINT - && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN - && ((epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT) - && epdesc->wMaxPacketSize == 8) - break; - epdesc = (void*)epdesc + epdesc->bLength; + // Find intr in endpoint. + struct usb_endpoint_descriptor *epdesc = findEndPointDesc( + iface, imax, USB_ENDPOINT_XFER_INT, USB_DIR_IN); + if (!epdesc || epdesc->wMaxPacketSize != 8) { + dprintf(1, "No keyboard intr in?\n"); + return -1; } - u32 inendp = mkendp(endp2cntl(endp), endp2devaddr(endp) - , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK - , endp2speed(endp), epdesc->wMaxPacketSize); + u32 inendp = mkendpFromDesc(endp, epdesc);
// Enable "boot" protocol. int ret = set_protocol(endp, 1); if (ret) return -1; - // Only send reports on a new key event. + // Periodically send reports to enable key repeat. ret = set_idle(endp, KEYREPEATMS); if (ret) return -1; diff --git a/src/usb.c b/src/usb.c index cb6f391..1ed3c80 100644 --- a/src/usb.c +++ b/src/usb.c @@ -18,6 +18,12 @@
struct usb_s USBControllers[16] VAR16VISIBLE;
+ +/**************************************************************** + * Controller function wrappers + ****************************************************************/ + +// Send a message on a control pipe using the default control descriptor. static int send_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize) @@ -63,6 +69,40 @@ usb_poll_intr(struct usb_pipe *pipe, void *data) } }
+ +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Find the first endpoing of a given type in an interface description. +struct usb_endpoint_descriptor * +findEndPointDesc(struct usb_interface_descriptor *iface, int imax + , int type, int dir) +{ + struct usb_endpoint_descriptor *epdesc = (void*)&iface[1]; + for (;;) { + if ((void*)epdesc >= (void*)iface + imax + || epdesc->bDescriptorType == USB_DT_INTERFACE) { + return NULL; + } + if (epdesc->bDescriptorType == USB_DT_ENDPOINT + && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir + && (epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type) + return epdesc; + epdesc = (void*)epdesc + epdesc->bLength; + } +} + +// Build an encoded "endp" from an endpoint descriptor. +u32 +mkendpFromDesc(u32 endp, struct usb_endpoint_descriptor *epdesc) +{ + return mkendp(endp2cntl(endp), endp2devaddr(endp) + , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK + , endp2speed(endp), epdesc->wMaxPacketSize); +} + +// Send a message to the default control pipe of a device. int send_default_control(u32 endp, const struct usb_ctrlrequest *req, void *data) { @@ -144,6 +184,11 @@ set_configuration(u32 endp, u16 val) return send_default_control(endp, &req, NULL); }
+ +/**************************************************************** + * Initialization and enumeration + ****************************************************************/ + // Called for every found device - see if a driver is available for // this device and do setup if so. int diff --git a/src/usb.h b/src/usb.h index e4d0a66..925f8ae 100644 --- a/src/usb.h +++ b/src/usb.h @@ -39,6 +39,10 @@ int send_default_control(u32 endp, const struct usb_ctrlrequest *req , void *data); struct usb_pipe *alloc_intr_pipe(u32 endp, int period); int usb_poll_intr(struct usb_pipe *pipe, void *data); +struct usb_interface_descriptor; +struct usb_endpoint_descriptor *findEndPointDesc( + struct usb_interface_descriptor *iface, int imax, int type, int dir); +u32 mkendpFromDesc(u32 endp, struct usb_endpoint_descriptor *epdesc);
/****************************************************************
This patch adds initial support for USB Mass Storage Controllers. This includes support for bulk transfers on UHCI controllers. Code to detect a USB MSC device is added, and wrappers for sending "cdb" block commands over USB are added. The scsi "inquiry" command is also added. --- Makefile | 2 +- src/block.c | 6 ++ src/blockcmd.c | 18 ++++- src/blockcmd.h | 14 +++ src/config.h | 2 + src/disk.h | 1 + src/usb-msc.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-msc.h | 25 ++++++ src/usb-uhci.c | 114 ++++++++++++++++++++++++ src/usb-uhci.h | 2 + src/usb.c | 36 +++++++- src/usb.h | 2 + 12 files changed, 479 insertions(+), 4 deletions(-) create mode 100644 src/usb-msc.c create mode 100644 src/usb-msc.h
diff --git a/Makefile b/Makefile index e087818..b896b54 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ OUT=out/ SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \ kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \ pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ - usb.c usb-uhci.c usb-ohci.c usb-hid.c usb-hub.c paravirt.c + usb.c usb-uhci.c usb-ohci.c usb-hid.c usb-hub.c usb-msc.c paravirt.c SRC16=$(SRCBOTH) system.c disk.c apm.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ diff --git a/src/block.c b/src/block.c index 365053e..3b43f97 100644 --- a/src/block.c +++ b/src/block.c @@ -10,6 +10,7 @@ #include "cmos.h" // inb_cmos #include "util.h" // dprintf #include "ata.h" // process_ata_op +#include "usb-msc.h" // process_usb_op
struct drives_s Drives VAR16VISIBLE;
@@ -280,6 +281,9 @@ describe_drive(struct drive_s *drive_g) case DTYPE_RAMDISK: describe_ramdisk(drive_g); break; + case DTYPE_USB: + describe_usb(drive_g); + break; default: printf("Unknown"); break; @@ -308,6 +312,8 @@ process_op(struct disk_op_s *op) return process_ramdisk_op(op); case DTYPE_CDEMU: return process_cdemu_op(op); + case DTYPE_USB: + return process_usb_op(op); default: op->count = 0; return DISK_RET_EPARAM; diff --git a/src/blockcmd.c b/src/blockcmd.c index 5efbdce..48568e6 100644 --- a/src/blockcmd.c +++ b/src/blockcmd.c @@ -10,7 +10,9 @@ #include "disk.h" // struct disk_op_s #include "blockcmd.h" // struct cdb_request_sense #include "ata.h" // atapi_cmd_data +#include "usb-msc.h" // usb_cmd_data
+// Route command to low-level handler. static int cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) { @@ -18,12 +20,26 @@ cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) switch (type) { case DTYPE_ATAPI: return atapi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_USB: + return usb_cmd_data(op, cdbcmd, blocksize); default: op->count = 0; return DISK_RET_EPARAM; } }
+int +cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_INQUIRY; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + // Request SENSE int cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) @@ -58,5 +74,5 @@ cdb_read(struct disk_op_s *op) cmd.command = CDB_CMD_READ_10; cmd.lba = htonl(op->lba); cmd.count = htons(op->count); - return cdb_cmd_data(op, &cmd, CDROM_SECTOR_SIZE); + return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize)); } diff --git a/src/blockcmd.h b/src/blockcmd.h index d645ebe..903c435 100644 --- a/src/blockcmd.h +++ b/src/blockcmd.h @@ -32,6 +32,7 @@ struct cdbres_read_capacity { u32 blksize; } PACKED;
+#define CDB_CMD_INQUIRY 0x12 #define CDB_CMD_REQUEST_SENSE 0x03
struct cdb_request_sense { @@ -55,9 +56,22 @@ struct cdbres_request_sense { u32 reserved_0e; } PACKED;
+struct cdbres_inquiry { + u8 pdt; + u8 removable; + u8 reserved_02[2]; + u8 additional; + u8 reserved_05[3]; + char vendor[8]; + char product[16]; + char rev[4]; +} PACKED; + // blockcmd.c +int cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data); int cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data); int cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data); +int cdb_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data); int cdb_read(struct disk_op_s *op);
#endif // blockcmd.h diff --git a/src/config.h b/src/config.h index e359652..1362581 100644 --- a/src/config.h +++ b/src/config.h @@ -36,6 +36,8 @@ #define CONFIG_USB_UHCI 1 // Support USB OHCI controllers #define CONFIG_USB_OHCI 1 +// Support USB disks +#define CONFIG_USB_MSC 1 // Support USB hubs #define CONFIG_USB_HUB 1 // Support USB keyboards diff --git a/src/disk.h b/src/disk.h index 90ca04d..d87d71a 100644 --- a/src/disk.h +++ b/src/disk.h @@ -199,6 +199,7 @@ struct drive_s { #define DTYPE_ATAPI 0x03 #define DTYPE_RAMDISK 0x04 #define DTYPE_CDEMU 0x05 +#define DTYPE_USB 0x06
#define TRANSLATION_NONE 0 #define TRANSLATION_LBA 1 diff --git a/src/usb-msc.c b/src/usb-msc.c new file mode 100644 index 0000000..8d4a44e --- /dev/null +++ b/src/usb-msc.c @@ -0,0 +1,261 @@ +// Code for handling USB Mass Storage Controller devices. +// +// Copyright (C) 2010 Kevin O'Connor kevin@koconnor.net +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "util.h" // dprintf +#include "config.h" // CONFIG_USB_MSC +#include "usb-msc.h" // usb_msc_init +#include "usb.h" // struct usb_s +#include "biosvar.h" // GET_GLOBAL +#include "blockcmd.h" // cdb_read +#include "disk.h" // DTYPE_USB +#include "boot.h" // add_bcv_internal + +#define DESCSIZE 80 + +struct usbdrive_s { + struct drive_s drive; + struct usb_pipe *bulkin, *bulkout; + char *desc; +}; + + +/**************************************************************** + * Bulk-only drive command processing + ****************************************************************/ + +#define USB_CDB_SIZE 12 + +#define CBW_SIGNATURE 0x43425355 // USBC + +struct cbw_s { + u32 dCBWSignature; + u32 dCBWTag; + u32 dCBWDataTransferLength; + u8 bmCBWFlags; + u8 bCBWLUN; + u8 bCBWCBLength; + u8 CBWCB[16]; +} PACKED; + +#define CSW_SIGNATURE 0x53425355 // USBS + +struct csw_s { + u32 dCSWSignature; + u32 dCSWTag; + u32 dCSWDataResidue; + u8 bCSWStatus; +} PACKED; + +// Low-level usb command transmit function. +int +usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + dprintf(1, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_g, 0, op->count, blocksize, op->buf_fl); + struct usbdrive_s *udrive_g = container_of( + op->drive_g, struct usbdrive_s, drive); + struct usb_pipe *bulkin = GET_GLOBAL(udrive_g->bulkin); + struct usb_pipe *bulkout = GET_GLOBAL(udrive_g->bulkout); + + // Setup command block wrapper. + u32 bytes = blocksize * op->count; + struct cbw_s cbw; + memset(&cbw, 0, sizeof(cbw)); + cbw.dCBWSignature = CBW_SIGNATURE; + cbw.dCBWTag = 999; // XXX + cbw.dCBWDataTransferLength = bytes; + cbw.bmCBWFlags = USB_DIR_IN; // XXX + cbw.bCBWLUN = 0; // XXX + cbw.bCBWCBLength = USB_CDB_SIZE; + memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE); + + // Transfer cbw to device. + int ret = usb_send_bulk(bulkout, USB_DIR_OUT + , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw)); + if (ret) + goto fail; + + // Transfer data from device. + ret = usb_send_bulk(bulkin, USB_DIR_IN, op->buf_fl, bytes); + if (ret) + goto fail; + + // Transfer csw info. + struct csw_s csw; + ret = usb_send_bulk(bulkin, USB_DIR_IN + , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw)); + if (ret) + goto fail; + + if (!csw.bCSWStatus) + return DISK_RET_SUCCESS; + if (csw.bCSWStatus == 2) + goto fail; + + op->count -= csw.dCSWDataResidue / blocksize; + return DISK_RET_EBADTRACK; + +fail: + // XXX - reset connection + dprintf(1, "USB transmission failed\n"); + op->count = 0; + return DISK_RET_EBADTRACK; +} + + +/**************************************************************** + * Drive ops + ****************************************************************/ + +// 16bit command demuxer for ATAPI cdroms. +int +process_usb_op(struct disk_op_s *op) +{ + switch (op->command) { + case CMD_READ: + return cdb_read(op); + case CMD_FORMAT: + case CMD_WRITE: + return DISK_RET_EWRITEPROTECT; + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} + +void +describe_usb(struct drive_s *drive_g) +{ + struct usbdrive_s *udrive_g = container_of( + drive_g, struct usbdrive_s, drive); + printf("%s", udrive_g->desc); +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static int +setup_drive_cdrom(struct disk_op_s *op) +{ + op->drive_g->blksize = CDROM_SECTOR_SIZE; + op->drive_g->sectors = (u64)-1; + map_cd_drive(op->drive_g); + return 0; +} + +static int +setup_drive_hd(struct disk_op_s *op) +{ + struct cdbres_read_capacity info; + int ret = cdb_read_capacity(op, &info); + if (ret) + return ret; + // XXX - retry for some timeout? + + u32 blksize = ntohl(info.blksize), sectors = ntohl(info.sectors); + if (blksize != DISK_SECTOR_SIZE) { + if (blksize == CDROM_SECTOR_SIZE) + return setup_drive_cdrom(op); + dprintf(1, "Unsupported USB MSC block size %d\n", blksize); + return -1; + } + op->drive_g->blksize = blksize; + op->drive_g->sectors = sectors; + dprintf(1, "USB MSC blksize=%d sectors=%d\n", blksize, sectors); + + // Setup disk geometry translation. + setup_translation(op->drive_g); + + // Register with bcv system. + add_bcv_internal(op->drive_g); + + return 0; +} + +// Configure a usb msc device. +int +usb_msc_init(u32 endp, struct usb_interface_descriptor *iface, int imax) +{ + if (!CONFIG_USB_MSC) + return -1; + + // Verify right kind of device + if (iface->bInterfaceSubClass != US_SC_SCSI + || iface->bInterfaceProtocol != US_PR_BULK) { + dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + // Find bulk in and bulk out endpoints. + struct usb_endpoint_descriptor *indesc = findEndPointDesc( + iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_IN); + struct usb_endpoint_descriptor *outdesc = findEndPointDesc( + iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT); + if (!indesc || !outdesc) + goto fail; + u32 inendp = mkendpFromDesc(endp, indesc); + struct usb_pipe *bulkin = alloc_bulk_pipe(inendp); + u32 outendp = mkendpFromDesc(endp, outdesc); + struct usb_pipe *bulkout = alloc_bulk_pipe(outendp); + if (!bulkin || !bulkout) + goto fail; + + // Allocate drive structure. + char *desc = malloc_tmphigh(DESCSIZE); + struct usbdrive_s *udrive_g = malloc_fseg(sizeof(*udrive_g)); + if (!udrive_g || !desc) { + warn_noalloc(); + goto fail; + } + memset(udrive_g, 0, sizeof(*udrive_g)); + udrive_g->drive.type = DTYPE_USB; + udrive_g->bulkin = bulkin; + udrive_g->bulkout = bulkout; + + // Validate drive and find block size and sector count. + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_g = &udrive_g->drive; + struct cdbres_inquiry data; + int ret = cdb_get_inquiry(&dop, &data); + if (ret) + goto fail; + char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1]; + char rev[sizeof(data.rev)+1]; + int pdt = data.pdt & 0x1f; + int removable = !!(data.removable & 0x80); + dprintf(1, "USB MSC vendor='%s' product='%s' rev='%s'" + " type=%d removable=%d\n" + , strtcpy(vendor, data.vendor, sizeof(vendor)) + , strtcpy(product, data.product, sizeof(product)) + , strtcpy(rev, data.rev, sizeof(rev)) + , pdt, removable); + + if (pdt == USB_MSC_TYPE_CDROM) + ret = setup_drive_cdrom(&dop); + else + ret = setup_drive_hd(&dop); + if (ret) + goto fail; + + snprintf(desc, DESCSIZE, "USB Drive %s %s %s", vendor, product, rev); + udrive_g->desc = desc; + + return 0; +fail: + dprintf(1, "Unable to configure USB MSC device.\n"); + free(desc); + free(udrive_g); + return -1; +} diff --git a/src/usb-msc.h b/src/usb-msc.h new file mode 100644 index 0000000..4bad91f --- /dev/null +++ b/src/usb-msc.h @@ -0,0 +1,25 @@ +#ifndef __USB_MSC_H +#define __USB_MSC_H + +// usb-msc.c +struct disk_op_s; +int usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usb_interface_descriptor; +int usb_msc_init(u32 endp, struct usb_interface_descriptor *iface, int imax); +int process_usb_op(struct disk_op_s *op); +struct drive_s; +void describe_usb(struct drive_s *drive_g); + + +/**************************************************************** + * MSC flags + ****************************************************************/ + +#define US_SC_SCSI 0x06 + +#define US_PR_BULK 0x50 + +#define USB_MSC_TYPE_DISK 0x00 +#define USB_MSC_TYPE_CDROM 0x05 + +#endif // ush-msc.h diff --git a/src/usb-uhci.c b/src/usb-uhci.c index ca51337..881e345 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -253,6 +253,120 @@ uhci_control(u32 endp, int dir, const void *cmd, int cmdsize }
struct usb_pipe * +uhci_alloc_bulk_pipe(u32 endp) +{ + if (! CONFIG_USB_UHCI) + return NULL; + struct usb_s *cntl = endp2cntl(endp); + dprintf(7, "uhci_alloc_bulk_pipe %x\n", endp); + + // Allocate a queue head. + struct uhci_qh *qh = malloc_low(sizeof(*qh)); + if (!qh) { + warn_noalloc(); + return NULL; + } + qh->element = UHCI_PTR_TERM; + qh->next_td = 0; + qh->pipe.endp = endp; + + // Add queue head to controller list. + struct uhci_qh *data_qh = cntl->uhci.qh; + qh->link = data_qh->link; + barrier(); + data_qh->link = (u32)qh | UHCI_PTR_QH; + + return &qh->pipe; +} + +static int +wait_td(struct uhci_td *td) +{ + u64 end = calc_future_tsc(5000); // XXX - lookup real time. + u32 status; + for (;;) { + status = td->status; + if (!(status & TD_CTRL_ACTIVE)) + break; + if (check_time(end)) { + warn_timeout(); + return -1; + } + yield(); + } + if (status & TD_CTRL_ANY_ERROR) { + dprintf(1, "wait_td error - status=%x\n", status); + return -2; + } + return 0; +} + +#define STACKTDS 4 +#define TDALIGN 16 + +int +uhci_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize) +{ + struct uhci_qh *qh = container_of(pipe, struct uhci_qh, pipe); + u32 endp = GET_FLATPTR(qh->pipe.endp); + dprintf(7, "uhci_send_bulk qh=%p endp=%x dir=%d data=%p size=%d\n" + , qh, endp, dir, data, datasize); + int maxpacket = endp2maxsize(endp); + int lowspeed = endp2speed(endp); + int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7); + int toggle = (u32)GET_FLATPTR(qh->next_td); // XXX + + // Allocate 4 tds on stack (16byte aligned) + u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; + struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN); + memset(tds, 0, sizeof(*tds) * STACKTDS); + + // Enable tds + SET_FLATPTR(qh->element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + + int tdpos = 0; + while (datasize) { + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td); + if (ret) + goto fail; + + int transfer = datasize; + if (transfer > maxpacket) + transfer = maxpacket; + struct uhci_td *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) + , &tds[tdpos % STACKTDS]); + td->link = (transfer==datasize ? UHCI_PTR_TERM : (u32)nexttd_fl); + td->token = (uhci_explen(transfer) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_IN : USB_PID_OUT)); + td->buffer = data; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle ^= TD_TOKEN_TOGGLE; + + data += transfer; + datasize -= transfer; + } + int i; + for (i=0; i<STACKTDS; i++) { + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td); + if (ret) + goto fail; + } + + SET_FLATPTR(qh->next_td, (void*)toggle); // XXX + return 0; +fail: + dprintf(1, "uhci_send_bulk failed\n"); + SET_FLATPTR(qh->element, UHCI_PTR_TERM); + uhci_waittick(); + return -1; +} + +struct usb_pipe * uhci_alloc_intr_pipe(u32 endp, int frameexp) { if (! CONFIG_USB_UHCI) diff --git a/src/usb-uhci.h b/src/usb-uhci.h index d9c10ae..d7ebb4e 100644 --- a/src/usb-uhci.h +++ b/src/usb-uhci.h @@ -8,6 +8,8 @@ struct usb_s; void uhci_init(void *data); int uhci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize); +struct usb_pipe *uhci_alloc_bulk_pipe(u32 endp); +int uhci_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); struct usb_pipe *uhci_alloc_intr_pipe(u32 endp, int frameexp); int uhci_poll_intr(struct usb_pipe *pipe, void *data);
diff --git a/src/usb.c b/src/usb.c index 1ed3c80..cc3b201 100644 --- a/src/usb.c +++ b/src/usb.c @@ -13,6 +13,7 @@ #include "usb-ohci.h" // ohci_init #include "usb-hid.h" // usb_keyboard_setup #include "usb-hub.h" // usb_hub_init +#include "usb-msc.h" // usb_msc_init #include "usb.h" // struct usb_s #include "biosvar.h" // GET_GLOBAL
@@ -39,6 +40,33 @@ send_control(u32 endp, int dir, const void *cmd, int cmdsize }
struct usb_pipe * +alloc_bulk_pipe(u32 endp) +{ + struct usb_s *cntl = endp2cntl(endp); + switch (cntl->type) { + default: + case USB_TYPE_UHCI: + return uhci_alloc_bulk_pipe(endp); + case USB_TYPE_OHCI: + return NULL; + } +} + +int +usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize) +{ + u32 endp = GET_FLATPTR(pipe->endp); + struct usb_s *cntl = endp2cntl(endp); + switch (cntl->type) { + default: + case USB_TYPE_UHCI: + return uhci_send_bulk(pipe, dir, data, datasize); + case USB_TYPE_OHCI: + return -1; + } +} + +struct usb_pipe * alloc_intr_pipe(u32 endp, int period) { struct usb_s *cntl = endp2cntl(endp); @@ -220,6 +248,7 @@ configure_usb_device(struct usb_s *cntl, int lowspeed) if ((iface->bInterfaceClass != USB_CLASS_HID || iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT || iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD) + && (iface->bInterfaceClass != USB_CLASS_MASS_STORAGE) && (iface->bInterfaceClass != USB_CLASS_HUB)) // Not a supported device. goto fail; @@ -237,8 +266,11 @@ configure_usb_device(struct usb_s *cntl, int lowspeed) free(config); return usb_hub_init(endp); } - ret = usb_keyboard_init(endp, iface, ((void*)config + config->wTotalLength - - (void*)iface)); + int imax = (void*)config + config->wTotalLength - (void*)iface; + if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) + ret = usb_msc_init(endp, iface, imax); + else + ret = usb_keyboard_init(endp, iface, imax); if (ret) goto fail;
diff --git a/src/usb.h b/src/usb.h index 925f8ae..d947891 100644 --- a/src/usb.h +++ b/src/usb.h @@ -37,6 +37,8 @@ int configure_usb_device(struct usb_s *cntl, int lowspeed); struct usb_ctrlrequest; int send_default_control(u32 endp, const struct usb_ctrlrequest *req , void *data); +int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); +struct usb_pipe *alloc_bulk_pipe(u32 endp); struct usb_pipe *alloc_intr_pipe(u32 endp, int period); int usb_poll_intr(struct usb_pipe *pipe, void *data); struct usb_interface_descriptor;
Kevin O'Connor wrote:
UHCI only (no ohci).
If SeaBIOS is GPL you might reuse the OHCI patch for libpayload.
//Peter
On Thu, Feb 18, 2010 at 11:11:07AM +0100, Peter Stuge wrote:
Kevin O'Connor wrote:
UHCI only (no ohci).
If SeaBIOS is GPL you might reuse the OHCI patch for libpayload.
SeaBIOS is LGPLv3.
SeaBIOS already has an OHCI driver. This patch did not add the "bulk" transport capability to the OHCI driver. Doing "bulk" transports from 16bit mode on an OHCI controller will require some hacks - otherwise it's not difficult to add.
However, I wanted to distribute a working prototype before going on to other parts.
Cheers, -Kevin