diff options
-rw-r--r-- | bootloader/gigabeat-s.c | 10 | ||||
-rw-r--r-- | firmware/export/config/gigabeats.h | 13 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c | 44 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/usb-target.h | 13 | ||||
-rw-r--r-- | firmware/usbstack/usb_storage.c | 59 |
5 files changed, 97 insertions, 42 deletions
diff --git a/bootloader/gigabeat-s.c b/bootloader/gigabeat-s.c index 9c5ad93d1f..c29265ddad 100644 --- a/bootloader/gigabeat-s.c +++ b/bootloader/gigabeat-s.c @@ -71,11 +71,15 @@ static bool pause_if_button_pressed(bool pre_usb) if (pre_usb && !usb_plugged()) return false; - /* Exit if no button or only the menu (settings reset) button */ + /* Exit if no button or only select buttons that have other + * functions */ switch (button) { - case BUTTON_MENU: - case BUTTON_NONE: + case USB_BL_INSTALL_MODE_BTN: + if (!pre_usb) + break; /* Only before USB detect */ + case BUTTON_MENU: /* Settings reset */ + case BUTTON_NONE: /* Nothing pressed */ return true; } diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h index fcfa274314..cf560aa35f 100644 --- a/firmware/export/config/gigabeats.h +++ b/firmware/export/config/gigabeats.h @@ -190,8 +190,17 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER -#define USBPOWER_BUTTON BUTTON_MENU -#define USBPOWER_BTN_IGNORE BUTTON_POWER +#define USBPOWER_BUTTON BUTTON_MENU + +#ifndef BOOTLOADER +#define USBPOWER_BTN_IGNORE BUTTON_POWER +#else +/* Disable charging-only mode detection in bootloader */ +#define USBPOWER_BTN_IGNORE (BUTTON_MAIN | BUTTON_REMOTE) +#endif + +/* Button that exposures boot partition rather than data during session */ +#define USB_BL_INSTALL_MODE_BTN BUTTON_VOL_DOWN /* define this if the unit has a battery switch or battery can be removed * when running */ diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c index 016f24fc48..c52a9a6dec 100644 --- a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c @@ -19,7 +19,6 @@ * ****************************************************************************/ #include "config.h" -#include "cpu.h" #include "system.h" #include "kernel.h" #include "ata.h" @@ -31,8 +30,10 @@ #include "ccm-imx31.h" #include "avic-imx31.h" #include "power-gigabeat-s.h" +#include <string.h> static int usb_status = USB_EXTRACTED; +static bool bootloader_install_mode = false; static void enable_transceiver(bool enable) { @@ -106,6 +107,16 @@ void usb_enable(bool on) void usb_attach(void) { + bootloader_install_mode = false; + + if (usb_core_driver_enabled(USB_DRIVER_MASS_STORAGE)) + { + /* Check if this will be bootloader install mode, exposing the + * boot partition instead of the data partition */ + bootloader_install_mode = + (button_status() & USB_BL_INSTALL_MODE_BTN) != 0; + } + usb_drv_attach(); } @@ -133,3 +144,34 @@ void usb_drv_usb_detect_event(void) if (usb_drv_powered()) usb_status_event(USB_INSERTED); } + +/* Called when reading the MBR */ +void usb_fix_mbr(unsigned char *mbr) +{ + unsigned char* p = mbr + 0x1be; + char tmp[16]; + + /* The Gigabeat S factory partition table contains invalid values for the + "active" flag in the MBR. This prevents at least the Linux kernel + from accepting the partition table, so we fix it on-the-fly. */ + p[0x00] &= 0x80; + p[0x10] &= 0x80; + p[0x20] &= 0x80; + p[0x30] &= 0x80; + + if (bootloader_install_mode) + return; + + /* Windows ignores the partition flags and mounts the first partition it + sees when the device reports itself as removable. Swap the partitions + so the data partition appears to be partition 0. Mark the boot + partition 0 as hidden and make it partition 1. */ + + /* Mark the first partition as hidden */ + p[0x04] |= 0x10; + + /* Swap first and second partitions */ + memcpy(tmp, &p[0x00], 16); + memcpy(&p[0x00], &p[0x10], 16); + memcpy(&p[0x10], tmp, 16); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-target.h b/firmware/target/arm/imx31/gigabeat-s/usb-target.h index c93400ca0b..7931058241 100644 --- a/firmware/target/arm/imx31/gigabeat-s/usb-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/usb-target.h @@ -30,4 +30,17 @@ void usb_init_device(void); /* Read the immediate state of the cable from the PMIC */ bool usb_plugged(void); +/** Sector read/write filters **/ + +/* Filter some things in the MBR - see usb-gigabeat-s.c */ +void usb_fix_mbr(unsigned char *mbr); +#define USBSTOR_READ_SECTORS_FILTER() \ + ({ if (cur_cmd.sector == 0) \ + usb_fix_mbr(cur_cmd.data[cur_cmd.data_select]); \ + 0; }) + +/* Disallow MBR writes entirely since it was "fixed" in usb_fix_mbr */ +#define USBSTOR_WRITE_SECTORS_FILTER() \ + ({ cur_cmd.sector != 0 ? 0 : -1; }) + #endif /* USB_TARGET */ diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c index 005697f6fa..1ff3b1ec4c 100644 --- a/firmware/usbstack/usb_storage.c +++ b/firmware/usbstack/usb_storage.c @@ -32,6 +32,8 @@ #include "usb_storage.h" #include "timefuncs.h" +/* For sector filter macro definitions */ +#include "usb-target.h" /* Enable the following define to export only the SD card slot. This * is useful for USBCV MSC tests, as those are destructive. @@ -47,6 +49,15 @@ #define SECTOR_SIZE 512 #endif +/* These defaults allow the operation */ +#ifndef USBSTOR_READ_SECTORS_FILTER +#define USBSTOR_READ_SECTORS_FILTER() ({ 0; }) +#endif + +#ifndef USBSTOR_WRITE_SECTORS_FILTER +#define USBSTOR_WRITE_SECTORS_FILTER() ({ 0; }) +#endif + /* the ARC driver currently supports up to 64k USB transfers. This is * enough for efficient mass storage support, as commonly host OSes * don't do larger SCSI transfers anyway, so larger USB transfers @@ -342,23 +353,6 @@ static void yearday_to_daymonth(int yd, int y, int *d, int *m) *m = i; } -#ifdef TOSHIBA_GIGABEAT_S - -/* The Gigabeat S factory partition table contains invalid values for the - "active" flag in the MBR. This prevents at least the Linux kernel from - accepting the partition table, so we fix it on-the-fly. */ - -static void fix_mbr(unsigned char* mbr) -{ - unsigned char* p = mbr + 0x1be; - - p[0x00] &= 0x80; - p[0x10] &= 0x80; - p[0x20] &= 0x80; - p[0x30] &= 0x80; -} -#endif - static bool check_disk_present(IF_MD_NONVOID(int volume)) { #ifdef USB_USE_RAMDISK @@ -491,14 +485,7 @@ void usb_storage_init_connection(void) int i; for(i=0;i<storage_num_drives();i++) { -#ifdef TOSHIBA_GIGABEAT_S - /* As long as the Gigabeat S is a non-removable device, we need - to mark the device as locked to avoid usb_storage_try_release_ata() - to leave MSC mode while the device is in use */ - locked[i] = true; -#else locked[i] = false; -#endif ejected[i] = !check_disk_present(IF_MD(i)); queue_broadcast(SYS_USB_LUN_LOCKED, (i<<16)+0); } @@ -549,10 +536,15 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length) cur_cmd.data[cur_cmd.data_select], MIN(WRITE_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count)*SECTOR_SIZE); #else - int result = storage_write_sectors(IF_MD2(cur_cmd.lun,) + int result = USBSTOR_WRITE_SECTORS_FILTER(); + + if (result == 0) { + result = storage_write_sectors(IF_MD2(cur_cmd.lun,) cur_cmd.sector, MIN(WRITE_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count), cur_cmd.data[cur_cmd.data_select]); + } + if(result != 0) { send_csw(UMS_STATUS_FAIL); cur_sense_data.sense_key=SENSE_MEDIUM_ERROR; @@ -725,6 +717,11 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* des static void send_and_read_next(void) { + int result = USBSTOR_READ_SECTORS_FILTER(); + + if(result != 0 && cur_cmd.last_result == 0) + cur_cmd.last_result = result; + send_block_data(cur_cmd.data[cur_cmd.data_select], MIN(READ_BUFFER_SIZE,cur_cmd.count*SECTOR_SIZE)); @@ -742,7 +739,7 @@ static void send_and_read_next(void) ramdisk_buffer + cur_cmd.sector*SECTOR_SIZE, MIN(READ_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count)*SECTOR_SIZE); #else - int result = storage_read_sectors(IF_MD2(cur_cmd.lun,) + result = storage_read_sectors(IF_MD2(cur_cmd.lun,) cur_cmd.sector, MIN(READ_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count), cur_cmd.data[cur_cmd.data_select]); @@ -1104,12 +1101,6 @@ static void handle_scsi(struct command_block_wrapper* cbw) cur_cmd.sector, MIN(READ_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count), cur_cmd.data[cur_cmd.data_select]); - -#ifdef TOSHIBA_GIGABEAT_S - if(cur_cmd.sector == 0) { - fix_mbr(cur_cmd.data[cur_cmd.data_select]); - } -#endif #endif send_and_read_next(); } @@ -1262,9 +1253,5 @@ static void fill_inquiry(IF_MD_NONVOID(int lun)) tb.inquiry->Versions = 4; /* SPC-2 */ tb.inquiry->Format = 2; /* SPC-2/3 inquiry format */ -#ifdef TOSHIBA_GIGABEAT_S - tb.inquiry->DeviceTypeModifier = 0; -#else tb.inquiry->DeviceTypeModifier = DEVICE_REMOVABLE; -#endif } |