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 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;iVersions = 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 }