summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Gevaerts <frank@gevaerts.be>2008-03-10 20:55:24 +0000
committerFrank Gevaerts <frank@gevaerts.be>2008-03-10 20:55:24 +0000
commit745133014e6161c4d8c7a7eab137b7d9b1174c55 (patch)
tree0731cf8a30b17dae164e4240e48b28782cdf5a06
parent9d32b6aa1776db210d5c01589704a65238240df2 (diff)
downloadrockbox-745133014e6161c4d8c7a7eab137b7d9b1174c55.tar.gz
rockbox-745133014e6161c4d8c7a7eab137b7d9b1174c55.tar.bz2
rockbox-745133014e6161c4d8c7a7eab137b7d9b1174c55.zip
make the usb storage driver handle hotswap correctly, and exit the usb screen once all drives are "ejected" (either as a command from the OS or physically)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16617 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/export/usb.h2
-rw-r--r--firmware/export/usb_core.h5
-rw-r--r--firmware/usb.c44
-rw-r--r--firmware/usbstack/usb_class_driver.h6
-rw-r--r--firmware/usbstack/usb_core.c29
-rw-r--r--firmware/usbstack/usb_storage.c86
-rw-r--r--firmware/usbstack/usb_storage.h5
7 files changed, 148 insertions, 29 deletions
diff --git a/firmware/export/usb.h b/firmware/export/usb.h
index 4dae7c2e68..a80dd82667 100644
--- a/firmware/export/usb.h
+++ b/firmware/export/usb.h
@@ -30,6 +30,7 @@ enum {
USB_POWERED,
USB_TRANSFER_COMPLETION,
USB_REQUEST_DISK,
+ USB_RELEASE_DISK,
USB_REQUEST_REBOOT
};
@@ -105,6 +106,7 @@ void usb_signal_transfer_completion(struct usb_transfer_completion_event_data* e
bool usb_driver_enabled(int driver);
bool usb_exclusive_ata(void); /* ata is available for usb */
void usb_request_exclusive_ata(void);
+void usb_release_exclusive_ata(void);
#endif
#if defined(IPOD_COLOR) || defined(IPOD_4G) \
diff --git a/firmware/export/usb_core.h b/firmware/export/usb_core.h
index a1701bc3f8..fd55ac8782 100644
--- a/firmware/export/usb_core.h
+++ b/firmware/export/usb_core.h
@@ -21,7 +21,7 @@
#ifndef BOOTLOADER
-//#define USB_SERIAL
+#define USB_SERIAL
#define USB_STORAGE
#define USB_CHARGING_ONLY
#else /* BOOTLOADER */
@@ -47,5 +47,8 @@ void usb_core_enable_driver(int driver,bool enabled);
bool usb_core_driver_enabled (int driver);
void usb_core_handle_transfer_completion(
struct usb_transfer_completion_event_data* event);
+#ifdef HAVE_HOTSWAP
+void usb_core_hotswap_event(int volume,bool inserted);
+#endif
#endif
diff --git a/firmware/usb.c b/firmware/usb.c
index d59cbc3d15..7c43a8ba90 100644
--- a/firmware/usb.c
+++ b/firmware/usb.c
@@ -227,6 +227,18 @@ static void usb_thread(void)
num_acks_to_expect);
}
break;
+ case USB_RELEASE_DISK:
+ if(!waiting_for_ack)
+ {
+ /* Tell all threads that they have to back off the ATA.
+ We subtract one for our own thread. */
+ num_acks_to_expect =
+ queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
+ waiting_for_ack = true;
+ DEBUGF("USB inserted. Waiting for ack from %d threads...\n",
+ num_acks_to_expect);
+ }
+ break;
#endif
case SYS_USB_CONNECTED_ACK:
if(waiting_for_ack)
@@ -264,7 +276,6 @@ static void usb_thread(void)
case USB_EXTRACTED:
#ifdef HAVE_USBSTACK
usb_enable(false);
- exclusive_ata_access = false;
#ifdef HAVE_PRIORITY_SCHEDULING
thread_set_priority(usb_thread_entry,PRIORITY_SYSTEM);
#endif
@@ -292,13 +303,16 @@ static void usb_thread(void)
#endif
usb_state = USB_EXTRACTED;
-
- /* Tell all threads that we are back in business */
- num_acks_to_expect =
- queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
- waiting_for_ack = true;
- DEBUGF("USB extracted. Waiting for ack from %d threads...\n",
- num_acks_to_expect);
+ if(exclusive_ata_access)
+ {
+ exclusive_ata_access = false;
+ /* Tell all threads that we are back in business */
+ num_acks_to_expect =
+ queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
+ waiting_for_ack = true;
+ DEBUGF("USB extracted. Waiting for ack from %d threads...\n",
+ num_acks_to_expect);
+ }
break;
case SYS_USB_DISCONNECTED_ACK:
@@ -319,15 +333,19 @@ static void usb_thread(void)
}
break;
-#ifdef HAVE_MMC
+#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
+#ifdef HAVE_USBSTACK
+ usb_core_hotswap_event(1,ev.id == SYS_HOTSWAP_INSERTED);
+#else
if(usb_state == USB_INSERTED)
{
usb_enable(false);
usb_mmc_countdown = HZ/2; /* re-enable after 0.5 sec */
}
break;
+#endif
case USB_REENABLE:
if(usb_state == USB_INSERTED)
@@ -512,6 +530,14 @@ void usb_request_exclusive_ata(void)
}
}
+void usb_release_exclusive_ata(void)
+{
+ if(exclusive_ata_access) {
+ queue_post(&usb_queue, USB_RELEASE_DISK, 0);
+ exclusive_ata_access = false;
+ }
+}
+
bool usb_exclusive_ata(void)
{
return exclusive_ata_access;
diff --git a/firmware/usbstack/usb_class_driver.h b/firmware/usbstack/usb_class_driver.h
index 631d5a3bc1..beeec86fb7 100644
--- a/firmware/usbstack/usb_class_driver.h
+++ b/firmware/usbstack/usb_class_driver.h
@@ -54,6 +54,12 @@ struct usb_class_driver {
able to handle it, it should ack the request, and return true. Otherwise
it should return false. */
bool (*control_request)(struct usb_ctrlrequest* req);
+
+#ifdef HAVE_HOTSWAP
+ /* Tells the driver that a hotswappable disk/card was inserted or
+ extracted */
+ void (*notify_hotswap)(int volume, bool inserted);
+#endif
};
#endif
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
index e0aaa9b2f4..aa4686500d 100644
--- a/firmware/usbstack/usb_core.c
+++ b/firmware/usbstack/usb_core.c
@@ -199,7 +199,10 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
.init = usb_storage_init,
.disconnect = NULL,
.transfer_complete = usb_storage_transfer_complete,
- .control_request = usb_storage_control_request
+ .control_request = usb_storage_control_request,
+#ifdef HAVE_HOTSWAP
+ .notify_hotswap = usb_storage_notify_hotswap,
+#endif
},
#endif
#ifdef USB_SERIAL
@@ -213,7 +216,10 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
.init = usb_serial_init,
.disconnect = usb_serial_disconnect,
.transfer_complete = usb_serial_transfer_complete,
- .control_request = usb_serial_control_request
+ .control_request = usb_serial_control_request,
+#ifdef HAVE_HOTSWAP
+ .notify_hotswap = NULL,
+#endif
},
#endif
#ifdef USB_CHARGING_ONLY
@@ -227,7 +233,10 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
.init = NULL,
.disconnect = NULL,
.transfer_complete = NULL,
- .control_request = NULL
+ .control_request = NULL,
+#ifdef HAVE_HOTSWAP
+ .notify_hotswap = NULL,
+#endif
},
#endif
};
@@ -386,6 +395,20 @@ bool usb_core_driver_enabled(int driver)
return drivers[driver].enabled;
}
+#ifdef HAVE_HOTSWAP
+void usb_core_hotswap_event(int volume,bool inserted)
+{
+ int i;
+ for(i=0;i<USB_NUM_DRIVERS;i++) {
+ if(drivers[i].enabled &&
+ drivers[i].notify_hotswap!=NULL)
+ {
+ drivers[i].notify_hotswap(volume,inserted);
+ }
+ }
+}
+#endif
+
static void usb_core_set_serial_function_id(void)
{
int id = 0;
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index 58578d958b..06d999d6de 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -262,15 +262,65 @@ static enum {
SENDING_CSW
} state = WAITING_FOR_COMMAND;
+static bool check_disk_present(int volume)
+{
+ unsigned char sector[512];
+ return ata_read_sectors(IF_MV2(volume,)0,1,sector) == 0;
+}
+
+static void try_release_ata(void)
+{
+ /* Check if there is a connected drive left. If not,
+ release excusive access */
+ bool canrelease=true;
+ int i;
+ for(i=0;i<NUM_VOLUMES;i++) {
+ if(ejected[i]==false){
+ canrelease=false;
+ break;
+ }
+ }
+ if(canrelease) {
+ logf("scsi release ata");
+ usb_release_exclusive_ata();
+ }
+}
+
+#ifdef HAVE_HOTSWAP
+void usb_storage_notify_hotswap(int volume,bool inserted)
+{
+ logf("notify %d",inserted);
+ if(inserted && check_disk_present(volume)) {
+ ejected[volume] = false;
+ }
+ else {
+ ejected[volume] = true;
+ try_release_ata();
+ }
+
+}
+#endif
+
+void usb_storage_reconnect(void)
+{
+ int i;
+ for(i=0;i<NUM_VOLUMES;i++)
+ ejected[i] = !check_disk_present(i);
+
+ usb_request_exclusive_ata();
+}
+
/* called by usb_code_init() */
void usb_storage_init(void)
{
int i;
- for(i=0;i<NUM_VOLUMES;i++)
- ejected[i]=false;
+ for(i=0;i<NUM_VOLUMES;i++) {
+ ejected[i] = !check_disk_present(i);
+ }
logf("usb_storage_init done");
}
+
int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size,
int interface_number,int endpoint)
{
@@ -348,7 +398,7 @@ void usb_storage_transfer_complete(bool in,int status,int length)
int result = ata_write_sectors(IF_MV2(cur_cmd.lun,)
cur_cmd.sector,
MIN(BUFFER_SIZE/SECTOR_SIZE,
- cur_cmd.count),
+ cur_cmd.count),
cur_cmd.data[cur_cmd.data_select]);
if(result != 0) {
send_csw(UMS_STATUS_FAIL);
@@ -508,7 +558,7 @@ static void send_and_read_next(void)
cur_cmd.last_result = ata_read_sectors(IF_MV2(cur_cmd.lun,)
cur_cmd.sector,
MIN(BUFFER_SIZE/SECTOR_SIZE,
- cur_cmd.count),
+ cur_cmd.count),
cur_cmd.data[cur_cmd.data_select]);
}
}
@@ -520,8 +570,8 @@ static void handle_scsi(struct command_block_wrapper* cbw)
TODO: support 48-bit LBA */
unsigned int length = cbw->data_transfer_length;
- unsigned int block_size;
- unsigned int block_count;
+ unsigned int block_size = 0;
+ unsigned int block_count = 0;
bool lun_present=true;
#ifdef ONLY_EXPOSE_CARD_SLOT
unsigned char lun = cbw->lun+1;
@@ -536,9 +586,8 @@ static void handle_scsi(struct command_block_wrapper* cbw)
block_count = cinfo->numblocks;
}
else {
- lun_present=false;
- block_size = 0;
- block_count = 0;
+ ejected[lun] = true;
+ try_release_ata();
}
#else
unsigned short* identify = ata_get_identify();
@@ -562,8 +611,8 @@ static void handle_scsi(struct command_block_wrapper* cbw)
if(!usb_exclusive_ata()) {
send_csw(UMS_STATUS_FAIL);
cur_sense_data.sense_key=SENSE_NOT_READY;
- cur_sense_data.asc=ASC_NOT_READY;
- cur_sense_data.ascq=ASCQ_BECOMING_READY;
+ cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
+ cur_sense_data.ascq=0;
break;
}
if(lun_present) {
@@ -732,12 +781,17 @@ static void handle_scsi(struct command_block_wrapper* cbw)
case SCSI_START_STOP_UNIT:
logf("scsi start_stop unit %d",lun);
- if((cbw->command_block[4] & 0xf0) == 0)
+ if((cbw->command_block[4] & 0xf0) == 0) /*load/eject bit is valid*/
{ /* Process start and eject bits */
- if((cbw->command_block[4] & 0x01) == 0 &&
- (cbw->command_block[4] & 0x02) != 0) /* Stop and eject */
+ logf("scsi load/eject");
+ if((cbw->command_block[4] & 0x01) == 0) /* Don't start */
{
- ejected[lun]=true;
+ if((cbw->command_block[4] & 0x02) != 0) /* eject */
+ {
+ logf("scsi eject");
+ ejected[lun]=true;
+ try_release_ata();
+ }
}
}
send_csw(UMS_STATUS_GOOD);
@@ -828,7 +882,7 @@ static void handle_scsi(struct command_block_wrapper* cbw)
cur_cmd.last_result = ata_read_sectors(IF_MV2(cur_cmd.lun,)
cur_cmd.sector,
MIN(BUFFER_SIZE/SECTOR_SIZE,
- cur_cmd.count),
+ cur_cmd.count),
cur_cmd.data[cur_cmd.data_select]);
send_and_read_next();
}
diff --git a/firmware/usbstack/usb_storage.h b/firmware/usbstack/usb_storage.h
index 34bc0144dd..40f8ed9248 100644
--- a/firmware/usbstack/usb_storage.h
+++ b/firmware/usbstack/usb_storage.h
@@ -27,6 +27,11 @@ void usb_storage_init_connection(int interface,int endpoint);
void usb_storage_init(void);
void usb_storage_transfer_complete(bool in,int state,int length);
bool usb_storage_control_request(struct usb_ctrlrequest* req);
+#ifdef HAVE_HOTSWAP
+void usb_storage_notify_hotswap(int volume,bool inserted);
+#endif
+
+void usb_storage_reconnect(void);
#endif