diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/usb.h | 2 | ||||
-rw-r--r-- | firmware/export/usb_core.h | 5 | ||||
-rw-r--r-- | firmware/usb.c | 44 | ||||
-rw-r--r-- | firmware/usbstack/usb_class_driver.h | 6 | ||||
-rw-r--r-- | firmware/usbstack/usb_core.c | 29 | ||||
-rw-r--r-- | firmware/usbstack/usb_storage.c | 86 | ||||
-rw-r--r-- | firmware/usbstack/usb_storage.h | 5 |
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 { | |||
30 | USB_POWERED, | 30 | USB_POWERED, |
31 | USB_TRANSFER_COMPLETION, | 31 | USB_TRANSFER_COMPLETION, |
32 | USB_REQUEST_DISK, | 32 | USB_REQUEST_DISK, |
33 | USB_RELEASE_DISK, | ||
33 | USB_REQUEST_REBOOT | 34 | USB_REQUEST_REBOOT |
34 | }; | 35 | }; |
35 | 36 | ||
@@ -105,6 +106,7 @@ void usb_signal_transfer_completion(struct usb_transfer_completion_event_data* e | |||
105 | bool usb_driver_enabled(int driver); | 106 | bool usb_driver_enabled(int driver); |
106 | bool usb_exclusive_ata(void); /* ata is available for usb */ | 107 | bool usb_exclusive_ata(void); /* ata is available for usb */ |
107 | void usb_request_exclusive_ata(void); | 108 | void usb_request_exclusive_ata(void); |
109 | void usb_release_exclusive_ata(void); | ||
108 | #endif | 110 | #endif |
109 | 111 | ||
110 | #if defined(IPOD_COLOR) || defined(IPOD_4G) \ | 112 | #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 @@ | |||
21 | 21 | ||
22 | #ifndef BOOTLOADER | 22 | #ifndef BOOTLOADER |
23 | 23 | ||
24 | //#define USB_SERIAL | 24 | #define USB_SERIAL |
25 | #define USB_STORAGE | 25 | #define USB_STORAGE |
26 | #define USB_CHARGING_ONLY | 26 | #define USB_CHARGING_ONLY |
27 | #else /* BOOTLOADER */ | 27 | #else /* BOOTLOADER */ |
@@ -47,5 +47,8 @@ void usb_core_enable_driver(int driver,bool enabled); | |||
47 | bool usb_core_driver_enabled (int driver); | 47 | bool usb_core_driver_enabled (int driver); |
48 | void usb_core_handle_transfer_completion( | 48 | void usb_core_handle_transfer_completion( |
49 | struct usb_transfer_completion_event_data* event); | 49 | struct usb_transfer_completion_event_data* event); |
50 | #ifdef HAVE_HOTSWAP | ||
51 | void usb_core_hotswap_event(int volume,bool inserted); | ||
52 | #endif | ||
50 | #endif | 53 | #endif |
51 | 54 | ||
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) | |||
227 | num_acks_to_expect); | 227 | num_acks_to_expect); |
228 | } | 228 | } |
229 | break; | 229 | break; |
230 | case USB_RELEASE_DISK: | ||
231 | if(!waiting_for_ack) | ||
232 | { | ||
233 | /* Tell all threads that they have to back off the ATA. | ||
234 | We subtract one for our own thread. */ | ||
235 | num_acks_to_expect = | ||
236 | queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1; | ||
237 | waiting_for_ack = true; | ||
238 | DEBUGF("USB inserted. Waiting for ack from %d threads...\n", | ||
239 | num_acks_to_expect); | ||
240 | } | ||
241 | break; | ||
230 | #endif | 242 | #endif |
231 | case SYS_USB_CONNECTED_ACK: | 243 | case SYS_USB_CONNECTED_ACK: |
232 | if(waiting_for_ack) | 244 | if(waiting_for_ack) |
@@ -264,7 +276,6 @@ static void usb_thread(void) | |||
264 | case USB_EXTRACTED: | 276 | case USB_EXTRACTED: |
265 | #ifdef HAVE_USBSTACK | 277 | #ifdef HAVE_USBSTACK |
266 | usb_enable(false); | 278 | usb_enable(false); |
267 | exclusive_ata_access = false; | ||
268 | #ifdef HAVE_PRIORITY_SCHEDULING | 279 | #ifdef HAVE_PRIORITY_SCHEDULING |
269 | thread_set_priority(usb_thread_entry,PRIORITY_SYSTEM); | 280 | thread_set_priority(usb_thread_entry,PRIORITY_SYSTEM); |
270 | #endif | 281 | #endif |
@@ -292,13 +303,16 @@ static void usb_thread(void) | |||
292 | #endif | 303 | #endif |
293 | 304 | ||
294 | usb_state = USB_EXTRACTED; | 305 | usb_state = USB_EXTRACTED; |
295 | 306 | if(exclusive_ata_access) | |
296 | /* Tell all threads that we are back in business */ | 307 | { |
297 | num_acks_to_expect = | 308 | exclusive_ata_access = false; |
298 | queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1; | 309 | /* Tell all threads that we are back in business */ |
299 | waiting_for_ack = true; | 310 | num_acks_to_expect = |
300 | DEBUGF("USB extracted. Waiting for ack from %d threads...\n", | 311 | queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1; |
301 | num_acks_to_expect); | 312 | waiting_for_ack = true; |
313 | DEBUGF("USB extracted. Waiting for ack from %d threads...\n", | ||
314 | num_acks_to_expect); | ||
315 | } | ||
302 | break; | 316 | break; |
303 | 317 | ||
304 | case SYS_USB_DISCONNECTED_ACK: | 318 | case SYS_USB_DISCONNECTED_ACK: |
@@ -319,15 +333,19 @@ static void usb_thread(void) | |||
319 | } | 333 | } |
320 | break; | 334 | break; |
321 | 335 | ||
322 | #ifdef HAVE_MMC | 336 | #ifdef HAVE_HOTSWAP |
323 | case SYS_HOTSWAP_INSERTED: | 337 | case SYS_HOTSWAP_INSERTED: |
324 | case SYS_HOTSWAP_EXTRACTED: | 338 | case SYS_HOTSWAP_EXTRACTED: |
339 | #ifdef HAVE_USBSTACK | ||
340 | usb_core_hotswap_event(1,ev.id == SYS_HOTSWAP_INSERTED); | ||
341 | #else | ||
325 | if(usb_state == USB_INSERTED) | 342 | if(usb_state == USB_INSERTED) |
326 | { | 343 | { |
327 | usb_enable(false); | 344 | usb_enable(false); |
328 | usb_mmc_countdown = HZ/2; /* re-enable after 0.5 sec */ | 345 | usb_mmc_countdown = HZ/2; /* re-enable after 0.5 sec */ |
329 | } | 346 | } |
330 | break; | 347 | break; |
348 | #endif | ||
331 | 349 | ||
332 | case USB_REENABLE: | 350 | case USB_REENABLE: |
333 | if(usb_state == USB_INSERTED) | 351 | if(usb_state == USB_INSERTED) |
@@ -512,6 +530,14 @@ void usb_request_exclusive_ata(void) | |||
512 | } | 530 | } |
513 | } | 531 | } |
514 | 532 | ||
533 | void usb_release_exclusive_ata(void) | ||
534 | { | ||
535 | if(exclusive_ata_access) { | ||
536 | queue_post(&usb_queue, USB_RELEASE_DISK, 0); | ||
537 | exclusive_ata_access = false; | ||
538 | } | ||
539 | } | ||
540 | |||
515 | bool usb_exclusive_ata(void) | 541 | bool usb_exclusive_ata(void) |
516 | { | 542 | { |
517 | return exclusive_ata_access; | 543 | 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 { | |||
54 | able to handle it, it should ack the request, and return true. Otherwise | 54 | able to handle it, it should ack the request, and return true. Otherwise |
55 | it should return false. */ | 55 | it should return false. */ |
56 | bool (*control_request)(struct usb_ctrlrequest* req); | 56 | bool (*control_request)(struct usb_ctrlrequest* req); |
57 | |||
58 | #ifdef HAVE_HOTSWAP | ||
59 | /* Tells the driver that a hotswappable disk/card was inserted or | ||
60 | extracted */ | ||
61 | void (*notify_hotswap)(int volume, bool inserted); | ||
62 | #endif | ||
57 | }; | 63 | }; |
58 | 64 | ||
59 | #endif | 65 | #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] = | |||
199 | .init = usb_storage_init, | 199 | .init = usb_storage_init, |
200 | .disconnect = NULL, | 200 | .disconnect = NULL, |
201 | .transfer_complete = usb_storage_transfer_complete, | 201 | .transfer_complete = usb_storage_transfer_complete, |
202 | .control_request = usb_storage_control_request | 202 | .control_request = usb_storage_control_request, |
203 | #ifdef HAVE_HOTSWAP | ||
204 | .notify_hotswap = usb_storage_notify_hotswap, | ||
205 | #endif | ||
203 | }, | 206 | }, |
204 | #endif | 207 | #endif |
205 | #ifdef USB_SERIAL | 208 | #ifdef USB_SERIAL |
@@ -213,7 +216,10 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] = | |||
213 | .init = usb_serial_init, | 216 | .init = usb_serial_init, |
214 | .disconnect = usb_serial_disconnect, | 217 | .disconnect = usb_serial_disconnect, |
215 | .transfer_complete = usb_serial_transfer_complete, | 218 | .transfer_complete = usb_serial_transfer_complete, |
216 | .control_request = usb_serial_control_request | 219 | .control_request = usb_serial_control_request, |
220 | #ifdef HAVE_HOTSWAP | ||
221 | .notify_hotswap = NULL, | ||
222 | #endif | ||
217 | }, | 223 | }, |
218 | #endif | 224 | #endif |
219 | #ifdef USB_CHARGING_ONLY | 225 | #ifdef USB_CHARGING_ONLY |
@@ -227,7 +233,10 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] = | |||
227 | .init = NULL, | 233 | .init = NULL, |
228 | .disconnect = NULL, | 234 | .disconnect = NULL, |
229 | .transfer_complete = NULL, | 235 | .transfer_complete = NULL, |
230 | .control_request = NULL | 236 | .control_request = NULL, |
237 | #ifdef HAVE_HOTSWAP | ||
238 | .notify_hotswap = NULL, | ||
239 | #endif | ||
231 | }, | 240 | }, |
232 | #endif | 241 | #endif |
233 | }; | 242 | }; |
@@ -386,6 +395,20 @@ bool usb_core_driver_enabled(int driver) | |||
386 | return drivers[driver].enabled; | 395 | return drivers[driver].enabled; |
387 | } | 396 | } |
388 | 397 | ||
398 | #ifdef HAVE_HOTSWAP | ||
399 | void usb_core_hotswap_event(int volume,bool inserted) | ||
400 | { | ||
401 | int i; | ||
402 | for(i=0;i<USB_NUM_DRIVERS;i++) { | ||
403 | if(drivers[i].enabled && | ||
404 | drivers[i].notify_hotswap!=NULL) | ||
405 | { | ||
406 | drivers[i].notify_hotswap(volume,inserted); | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | #endif | ||
411 | |||
389 | static void usb_core_set_serial_function_id(void) | 412 | static void usb_core_set_serial_function_id(void) |
390 | { | 413 | { |
391 | int id = 0; | 414 | 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 { | |||
262 | SENDING_CSW | 262 | SENDING_CSW |
263 | } state = WAITING_FOR_COMMAND; | 263 | } state = WAITING_FOR_COMMAND; |
264 | 264 | ||
265 | static bool check_disk_present(int volume) | ||
266 | { | ||
267 | unsigned char sector[512]; | ||
268 | return ata_read_sectors(IF_MV2(volume,)0,1,sector) == 0; | ||
269 | } | ||
270 | |||
271 | static void try_release_ata(void) | ||
272 | { | ||
273 | /* Check if there is a connected drive left. If not, | ||
274 | release excusive access */ | ||
275 | bool canrelease=true; | ||
276 | int i; | ||
277 | for(i=0;i<NUM_VOLUMES;i++) { | ||
278 | if(ejected[i]==false){ | ||
279 | canrelease=false; | ||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | if(canrelease) { | ||
284 | logf("scsi release ata"); | ||
285 | usb_release_exclusive_ata(); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | #ifdef HAVE_HOTSWAP | ||
290 | void usb_storage_notify_hotswap(int volume,bool inserted) | ||
291 | { | ||
292 | logf("notify %d",inserted); | ||
293 | if(inserted && check_disk_present(volume)) { | ||
294 | ejected[volume] = false; | ||
295 | } | ||
296 | else { | ||
297 | ejected[volume] = true; | ||
298 | try_release_ata(); | ||
299 | } | ||
300 | |||
301 | } | ||
302 | #endif | ||
303 | |||
304 | void usb_storage_reconnect(void) | ||
305 | { | ||
306 | int i; | ||
307 | for(i=0;i<NUM_VOLUMES;i++) | ||
308 | ejected[i] = !check_disk_present(i); | ||
309 | |||
310 | usb_request_exclusive_ata(); | ||
311 | } | ||
312 | |||
265 | /* called by usb_code_init() */ | 313 | /* called by usb_code_init() */ |
266 | void usb_storage_init(void) | 314 | void usb_storage_init(void) |
267 | { | 315 | { |
268 | int i; | 316 | int i; |
269 | for(i=0;i<NUM_VOLUMES;i++) | 317 | for(i=0;i<NUM_VOLUMES;i++) { |
270 | ejected[i]=false; | 318 | ejected[i] = !check_disk_present(i); |
319 | } | ||
271 | logf("usb_storage_init done"); | 320 | logf("usb_storage_init done"); |
272 | } | 321 | } |
273 | 322 | ||
323 | |||
274 | int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size, | 324 | int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size, |
275 | int interface_number,int endpoint) | 325 | int interface_number,int endpoint) |
276 | { | 326 | { |
@@ -348,7 +398,7 @@ void usb_storage_transfer_complete(bool in,int status,int length) | |||
348 | int result = ata_write_sectors(IF_MV2(cur_cmd.lun,) | 398 | int result = ata_write_sectors(IF_MV2(cur_cmd.lun,) |
349 | cur_cmd.sector, | 399 | cur_cmd.sector, |
350 | MIN(BUFFER_SIZE/SECTOR_SIZE, | 400 | MIN(BUFFER_SIZE/SECTOR_SIZE, |
351 | cur_cmd.count), | 401 | cur_cmd.count), |
352 | cur_cmd.data[cur_cmd.data_select]); | 402 | cur_cmd.data[cur_cmd.data_select]); |
353 | if(result != 0) { | 403 | if(result != 0) { |
354 | send_csw(UMS_STATUS_FAIL); | 404 | send_csw(UMS_STATUS_FAIL); |
@@ -508,7 +558,7 @@ static void send_and_read_next(void) | |||
508 | cur_cmd.last_result = ata_read_sectors(IF_MV2(cur_cmd.lun,) | 558 | cur_cmd.last_result = ata_read_sectors(IF_MV2(cur_cmd.lun,) |
509 | cur_cmd.sector, | 559 | cur_cmd.sector, |
510 | MIN(BUFFER_SIZE/SECTOR_SIZE, | 560 | MIN(BUFFER_SIZE/SECTOR_SIZE, |
511 | cur_cmd.count), | 561 | cur_cmd.count), |
512 | cur_cmd.data[cur_cmd.data_select]); | 562 | cur_cmd.data[cur_cmd.data_select]); |
513 | } | 563 | } |
514 | } | 564 | } |
@@ -520,8 +570,8 @@ static void handle_scsi(struct command_block_wrapper* cbw) | |||
520 | TODO: support 48-bit LBA */ | 570 | TODO: support 48-bit LBA */ |
521 | 571 | ||
522 | unsigned int length = cbw->data_transfer_length; | 572 | unsigned int length = cbw->data_transfer_length; |
523 | unsigned int block_size; | 573 | unsigned int block_size = 0; |
524 | unsigned int block_count; | 574 | unsigned int block_count = 0; |
525 | bool lun_present=true; | 575 | bool lun_present=true; |
526 | #ifdef ONLY_EXPOSE_CARD_SLOT | 576 | #ifdef ONLY_EXPOSE_CARD_SLOT |
527 | unsigned char lun = cbw->lun+1; | 577 | unsigned char lun = cbw->lun+1; |
@@ -536,9 +586,8 @@ static void handle_scsi(struct command_block_wrapper* cbw) | |||
536 | block_count = cinfo->numblocks; | 586 | block_count = cinfo->numblocks; |
537 | } | 587 | } |
538 | else { | 588 | else { |
539 | lun_present=false; | 589 | ejected[lun] = true; |
540 | block_size = 0; | 590 | try_release_ata(); |
541 | block_count = 0; | ||
542 | } | 591 | } |
543 | #else | 592 | #else |
544 | unsigned short* identify = ata_get_identify(); | 593 | unsigned short* identify = ata_get_identify(); |
@@ -562,8 +611,8 @@ static void handle_scsi(struct command_block_wrapper* cbw) | |||
562 | if(!usb_exclusive_ata()) { | 611 | if(!usb_exclusive_ata()) { |
563 | send_csw(UMS_STATUS_FAIL); | 612 | send_csw(UMS_STATUS_FAIL); |
564 | cur_sense_data.sense_key=SENSE_NOT_READY; | 613 | cur_sense_data.sense_key=SENSE_NOT_READY; |
565 | cur_sense_data.asc=ASC_NOT_READY; | 614 | cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT; |
566 | cur_sense_data.ascq=ASCQ_BECOMING_READY; | 615 | cur_sense_data.ascq=0; |
567 | break; | 616 | break; |
568 | } | 617 | } |
569 | if(lun_present) { | 618 | if(lun_present) { |
@@ -732,12 +781,17 @@ static void handle_scsi(struct command_block_wrapper* cbw) | |||
732 | 781 | ||
733 | case SCSI_START_STOP_UNIT: | 782 | case SCSI_START_STOP_UNIT: |
734 | logf("scsi start_stop unit %d",lun); | 783 | logf("scsi start_stop unit %d",lun); |
735 | if((cbw->command_block[4] & 0xf0) == 0) | 784 | if((cbw->command_block[4] & 0xf0) == 0) /*load/eject bit is valid*/ |
736 | { /* Process start and eject bits */ | 785 | { /* Process start and eject bits */ |
737 | if((cbw->command_block[4] & 0x01) == 0 && | 786 | logf("scsi load/eject"); |
738 | (cbw->command_block[4] & 0x02) != 0) /* Stop and eject */ | 787 | if((cbw->command_block[4] & 0x01) == 0) /* Don't start */ |
739 | { | 788 | { |
740 | ejected[lun]=true; | 789 | if((cbw->command_block[4] & 0x02) != 0) /* eject */ |
790 | { | ||
791 | logf("scsi eject"); | ||
792 | ejected[lun]=true; | ||
793 | try_release_ata(); | ||
794 | } | ||
741 | } | 795 | } |
742 | } | 796 | } |
743 | send_csw(UMS_STATUS_GOOD); | 797 | send_csw(UMS_STATUS_GOOD); |
@@ -828,7 +882,7 @@ static void handle_scsi(struct command_block_wrapper* cbw) | |||
828 | cur_cmd.last_result = ata_read_sectors(IF_MV2(cur_cmd.lun,) | 882 | cur_cmd.last_result = ata_read_sectors(IF_MV2(cur_cmd.lun,) |
829 | cur_cmd.sector, | 883 | cur_cmd.sector, |
830 | MIN(BUFFER_SIZE/SECTOR_SIZE, | 884 | MIN(BUFFER_SIZE/SECTOR_SIZE, |
831 | cur_cmd.count), | 885 | cur_cmd.count), |
832 | cur_cmd.data[cur_cmd.data_select]); | 886 | cur_cmd.data[cur_cmd.data_select]); |
833 | send_and_read_next(); | 887 | send_and_read_next(); |
834 | } | 888 | } |
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); | |||
27 | void usb_storage_init(void); | 27 | void usb_storage_init(void); |
28 | void usb_storage_transfer_complete(bool in,int state,int length); | 28 | void usb_storage_transfer_complete(bool in,int state,int length); |
29 | bool usb_storage_control_request(struct usb_ctrlrequest* req); | 29 | bool usb_storage_control_request(struct usb_ctrlrequest* req); |
30 | #ifdef HAVE_HOTSWAP | ||
31 | void usb_storage_notify_hotswap(int volume,bool inserted); | ||
32 | #endif | ||
33 | |||
34 | void usb_storage_reconnect(void); | ||
30 | 35 | ||
31 | #endif | 36 | #endif |
32 | 37 | ||