Cleaner solution to plugin-included core files.
[kugel-rb.git] / firmware / usb.c
blobec47e0653c2e492ff72c30d3d7485ebb162c6a9b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * iPod driver based on code from the ipodlinux project - http://ipodlinux.org
13 * Adapted for Rockbox in January 2006
14 * Original file: podzilla/usb.c
15 * Copyright (C) 2005 Adam Johnston
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
25 ****************************************************************************/
26 #include "config.h"
27 #include "cpu.h"
28 #include "kernel.h"
29 #include "thread.h"
30 #include "system.h"
31 #include "debug.h"
32 #include "storage.h"
33 #include "fat.h"
34 #include "disk.h"
35 #include "panic.h"
36 #include "lcd.h"
37 #include "usb.h"
38 #include "button.h"
39 #include "sprintf.h"
40 #include "string.h"
41 #include "usb-target.h"
42 #ifdef HAVE_USBSTACK
43 #include "usb_core.h"
44 #endif
45 #include "logf.h"
47 /* Conditions under which we want the entire driver */
48 #if !defined(BOOTLOADER) || (CONFIG_CPU == SH7034) || \
49 (defined(TOSHIBA_GIGABEAT_S) && defined(USE_ROCKBOX_USB) && defined(USB_STORAGE)) || \
50 (defined(HAVE_USBSTACK) && (defined(CREATIVE_ZVx) || \
51 defined(CPU_TCC77X) || defined(CPU_TCC780X))) || \
52 (CONFIG_USBOTG == USBOTG_JZ4740)
53 #define USB_FULL_INIT
54 #endif
56 #ifdef HAVE_LCD_BITMAP
57 bool do_screendump_instead_of_usb = false;
58 #if defined(USB_FULL_INIT) && defined(BOOTLOADER)
59 static void screen_dump(void) {}
60 #else
61 void screen_dump(void); /* Nasty again. Defined in apps/ too */
62 #endif
63 #endif
65 #if !defined(SIMULATOR) && !defined(USB_NONE)
67 #define NUM_POLL_READINGS (HZ/5)
68 static int countdown;
70 static int usb_state;
72 #if (CONFIG_STORAGE & STORAGE_MMC) && defined(USB_FULL_INIT)
73 static int usb_mmc_countdown = 0;
74 #endif
76 /* FIXME: The extra 0x800 is consumed by fat_mount() when the fsinfo
77 needs updating */
78 #ifdef USB_FULL_INIT
79 static long usb_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
80 static const char usb_thread_name[] = "usb";
81 static unsigned int usb_thread_entry = 0;
82 #endif
83 static struct event_queue usb_queue;
84 static int last_usb_status;
85 static bool usb_monitor_enabled;
86 #ifdef HAVE_USBSTACK
87 static bool exclusive_storage_access;
88 #endif
91 #if defined(IPOD_COLOR) || defined(IPOD_4G) \
92 || defined(IPOD_MINI) || defined(IPOD_MINI2G)
93 static int firewire_countdown;
94 static bool last_firewire_status;
95 #endif
97 #ifdef USB_FULL_INIT
98 #ifndef HAVE_USBSTACK
99 static void usb_slave_mode(bool on)
101 int rc;
103 if(on)
105 DEBUGF("Entering USB slave mode\n");
106 storage_soft_reset();
107 storage_init();
108 storage_enable(false);
109 usb_enable(true);
111 else
113 DEBUGF("Leaving USB slave mode\n");
115 /* Let the ISDx00 settle */
116 sleep(HZ*1);
118 usb_enable(false);
120 rc = storage_init();
121 if(rc)
122 panicf("storage: %d",rc);
124 rc = disk_mount_all();
125 if (rc <= 0) /* no partition */
126 panicf("mount: %d",rc);
129 #endif
131 static void try_reboot(void)
133 #ifdef HAVE_DISK_STORAGE
134 storage_sleepnow(); /* Immediately spindown the disk. */
135 sleep(HZ*2);
136 #endif
138 #ifdef IPOD_ARCH /* The following code is based on ipodlinux */
139 #if CONFIG_CPU == PP5020
140 memcpy((void *)0x40017f00, "diskmode\0\0hotstuff\0\0\1", 21);
141 #elif CONFIG_CPU == PP5022
142 memcpy((void *)0x4001ff00, "diskmode\0\0hotstuff\0\0\1", 21);
143 #endif /* CONFIG_CPU */
144 #endif /* IPOD_ARCH */
146 system_reboot(); /* Reboot */
149 static void usb_thread(void)
151 int num_acks_to_expect = -1;
152 bool waiting_for_ack;
153 struct queue_event ev;
155 waiting_for_ack = false;
157 while(1)
159 queue_wait(&usb_queue, &ev);
160 switch(ev.id)
162 #ifdef USB_DRIVER_CLOSE
163 case USB_QUIT:
164 return;
165 #endif
166 #ifdef HAVE_USBSTACK
167 case USB_TRANSFER_COMPLETION:
168 usb_core_handle_transfer_completion((struct usb_transfer_completion_event_data*)ev.data);
169 break;
170 #endif
171 #ifdef HAVE_USB_POWER
172 case USB_POWERED:
173 usb_state = USB_POWERED;
174 break;
175 #endif
176 case USB_INSERTED:
177 #ifdef HAVE_LCD_BITMAP
178 if(do_screendump_instead_of_usb)
180 screen_dump();
182 else
183 #endif
184 #ifdef HAVE_USB_POWER
185 #if defined(IRIVER_H10) || defined (IRIVER_H10_5GB)
186 if((button_status() & ~USBPOWER_BTN_IGNORE) != USBPOWER_BUTTON)
187 #else
188 if((button_status() & ~USBPOWER_BTN_IGNORE) == USBPOWER_BUTTON)
189 #endif
191 usb_state = USB_POWERED;
192 #ifdef HAVE_USBSTACK
193 usb_core_enable_driver(USB_DRIVER_MASS_STORAGE,false);
194 usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY,true);
195 usb_enable(true);
196 #endif
198 else
199 #endif
201 #ifdef HAVE_USBSTACK
202 /* Set the state to USB_POWERED for now. if a real
203 connection is detected it will switch to USB_INSERTED */
204 usb_state = USB_POWERED;
205 usb_core_enable_driver(USB_DRIVER_MASS_STORAGE,true);
206 usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY,false);
207 usb_enable(true);
208 #else
209 /* Tell all threads that they have to back off the ATA.
210 We subtract one for our own thread. */
211 num_acks_to_expect =
212 queue_broadcast(SYS_USB_CONNECTED, 0) - 1;
213 waiting_for_ack = true;
214 DEBUGF("USB inserted. Waiting for ack from %d threads...\n",
215 num_acks_to_expect);
216 #endif
218 break;
219 #ifdef HAVE_USBSTACK
220 case USB_REQUEST_DISK:
221 if(!waiting_for_ack)
223 /* Tell all threads that they have to back off the ATA.
224 We subtract one for our own thread. */
225 num_acks_to_expect =
226 queue_broadcast(SYS_USB_CONNECTED, 0) - 1;
227 waiting_for_ack = true;
228 DEBUGF("USB inserted. Waiting for ack from %d threads...\n",
229 num_acks_to_expect);
231 break;
232 case USB_RELEASE_DISK:
233 if(!waiting_for_ack)
235 /* Tell all threads that they have to back off the ATA.
236 We subtract one for our own thread. */
237 num_acks_to_expect =
238 queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
239 waiting_for_ack = true;
240 DEBUGF("USB inserted. Waiting for ack from %d threads...\n",
241 num_acks_to_expect);
243 break;
244 #endif
245 case SYS_USB_CONNECTED_ACK:
246 if(waiting_for_ack)
248 num_acks_to_expect--;
249 if(num_acks_to_expect == 0)
251 DEBUGF("All threads have acknowledged the connect.\n");
252 #ifdef HAVE_USBSTACK
253 #ifndef USE_ROCKBOX_USB
254 /* until we have native mass-storage mode, we want to reboot on
255 usb host connect */
256 try_reboot();
257 #endif /* USE_ROCKBOX_USB */
258 #ifdef HAVE_PRIORITY_SCHEDULING
259 thread_set_priority(usb_thread_entry,PRIORITY_REALTIME);
260 #endif
261 exclusive_storage_access = true;
263 #else
264 usb_slave_mode(true);
265 cpu_idle_mode(true);
266 #endif
267 usb_state = USB_INSERTED;
268 waiting_for_ack = false;
270 else
272 DEBUGF("usb: got ack, %d to go...\n",
273 num_acks_to_expect);
276 break;
278 case USB_EXTRACTED:
279 #ifdef HAVE_USBSTACK
280 usb_enable(false);
281 #ifdef HAVE_PRIORITY_SCHEDULING
282 thread_set_priority(usb_thread_entry,PRIORITY_SYSTEM);
283 #endif
284 #endif
285 #ifdef HAVE_LCD_BITMAP
286 if(do_screendump_instead_of_usb)
287 break;
288 #endif
289 #ifdef HAVE_USB_POWER
290 if(usb_state == USB_POWERED)
292 usb_state = USB_EXTRACTED;
293 break;
295 #endif
296 #ifndef HAVE_USBSTACK
297 if(usb_state == USB_INSERTED)
299 /* Only disable the USB mode if we really have enabled it
300 some threads might not have acknowledged the
301 insertion */
302 usb_slave_mode(false);
303 cpu_idle_mode(false);
305 #endif
307 usb_state = USB_EXTRACTED;
308 #ifdef HAVE_USBSTACK
309 if(exclusive_storage_access)
311 int rc = disk_mount_all();
312 if (rc <= 0) /* no partition */
313 panicf("mount: %d",rc);
314 exclusive_storage_access = false;
315 #endif
316 /* Tell all threads that we are back in business */
317 num_acks_to_expect =
318 queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
319 waiting_for_ack = true;
320 DEBUGF("USB extracted. Waiting for ack from %d threads...\n",
321 num_acks_to_expect);
322 #ifdef HAVE_USBSTACK
324 #endif
325 break;
327 case SYS_USB_DISCONNECTED_ACK:
328 if(waiting_for_ack)
330 num_acks_to_expect--;
331 if(num_acks_to_expect == 0)
333 DEBUGF("All threads have acknowledged. "
334 "We're in business.\n");
335 waiting_for_ack = false;
337 else
339 DEBUGF("usb: got ack, %d to go...\n",
340 num_acks_to_expect);
343 break;
345 #ifdef HAVE_HOTSWAP
346 case SYS_HOTSWAP_INSERTED:
347 case SYS_HOTSWAP_EXTRACTED:
348 #ifdef HAVE_USBSTACK
349 usb_core_hotswap_event(1,ev.id == SYS_HOTSWAP_INSERTED);
350 #else
351 if(usb_state == USB_INSERTED)
353 usb_enable(false);
354 #if (CONFIG_STORAGE & STORAGE_MMC)
355 usb_mmc_countdown = HZ/2; /* re-enable after 0.5 sec */
356 #endif
358 #endif
359 break;
361 case USB_REENABLE:
362 if(usb_state == USB_INSERTED)
363 usb_enable(true); /* reenable only if still inserted */
364 break;
365 #endif /* HAVE_HOTSWAP */
366 case USB_REQUEST_REBOOT:
367 #ifdef HAVE_USB_POWER
368 if((button_status() & ~USBPOWER_BTN_IGNORE) != USBPOWER_BUTTON)
369 #endif
370 try_reboot();
371 break;
375 #endif
377 #ifdef HAVE_USBSTACK
378 void usb_signal_transfer_completion(struct usb_transfer_completion_event_data* event_data)
380 queue_post(&usb_queue, USB_TRANSFER_COMPLETION, (intptr_t)event_data);
382 #endif
384 #ifdef USB_FULL_INIT
385 static void usb_tick(void)
387 int current_status;
389 if(usb_monitor_enabled)
391 #if defined(IPOD_COLOR) || defined(IPOD_4G) \
392 || defined(IPOD_MINI) || defined(IPOD_MINI2G)
393 int current_firewire_status = firewire_detect();
394 if(current_firewire_status != last_firewire_status)
396 last_firewire_status = current_firewire_status;
397 firewire_countdown = NUM_POLL_READINGS;
399 else
401 /* Count down until it gets negative */
402 if(firewire_countdown >= 0)
403 firewire_countdown--;
405 /* Report to the thread if we have had 3 identical status
406 readings in a row */
407 if(firewire_countdown == 0)
409 queue_post(&usb_queue, USB_REQUEST_REBOOT, 0);
412 #endif
414 current_status = usb_detect();
416 /* Only report when the status has changed */
417 if(current_status != last_usb_status)
419 last_usb_status = current_status;
420 countdown = NUM_POLL_READINGS;
422 else
424 /* Count down until it gets negative */
425 if(countdown >= 0)
426 countdown--;
428 /* Report to the thread if we have had 3 identical status
429 readings in a row */
430 if(countdown == 0)
432 queue_post(&usb_queue, current_status, 0);
436 #if (CONFIG_STORAGE & STORAGE_MMC)
437 if(usb_mmc_countdown > 0)
439 usb_mmc_countdown--;
440 if (usb_mmc_countdown == 0)
441 queue_post(&usb_queue, USB_REENABLE, 0);
443 #endif
445 #endif
447 void usb_acknowledge(long id)
449 queue_post(&usb_queue, id, 0);
452 void usb_init(void)
454 usb_state = USB_EXTRACTED;
455 #ifdef HAVE_USBSTACK
456 exclusive_storage_access = false;
457 #endif
458 usb_monitor_enabled = false;
459 countdown = -1;
461 #if defined(IPOD_COLOR) || defined(IPOD_4G) \
462 || defined(IPOD_MINI) || defined(IPOD_MINI2G)
463 firewire_countdown = -1;
464 last_firewire_status = false;
465 #endif
467 usb_init_device();
468 #ifdef USB_FULL_INIT
469 usb_enable(false);
470 #endif
472 /* We assume that the USB cable is extracted */
473 last_usb_status = USB_EXTRACTED;
475 #ifdef USB_FULL_INIT
476 queue_init(&usb_queue, true);
478 usb_thread_entry = create_thread(usb_thread, usb_stack,
479 sizeof(usb_stack), 0, usb_thread_name
480 IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU));
482 tick_add_task(usb_tick);
483 #endif
487 void usb_wait_for_disconnect(struct event_queue *q)
489 #ifdef USB_FULL_INIT
490 struct queue_event ev;
492 /* Don't return until we get SYS_USB_DISCONNECTED */
493 while(1)
495 queue_wait(q, &ev);
496 if(ev.id == SYS_USB_DISCONNECTED)
498 usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
499 return;
502 #else
503 (void)q;
504 #endif /* USB_FULL_INIT */
507 int usb_wait_for_disconnect_w_tmo(struct event_queue *q, int ticks)
509 #ifdef USB_FULL_INIT
510 struct queue_event ev;
512 /* Don't return until we get SYS_USB_DISCONNECTED or SYS_TIMEOUT */
513 while(1)
515 queue_wait_w_tmo(q, &ev, ticks);
516 switch(ev.id)
518 case SYS_USB_DISCONNECTED:
519 usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
520 return 0;
521 break;
522 case SYS_TIMEOUT:
523 return 1;
524 break;
527 #else
528 (void)q; (void)ticks;
529 return 0;
530 #endif /* USB_FULL_INIT */
533 void usb_start_monitoring(void)
535 usb_monitor_enabled = true;
538 #ifdef USB_DRIVER_CLOSE
539 void usb_close(void)
541 unsigned int thread = usb_thread_entry;
542 usb_thread_entry = 0;
544 if (thread == 0)
545 return;
547 tick_remove_task(usb_tick);
548 usb_monitor_enabled = false;
550 queue_post(&usb_queue, USB_QUIT, 0);
551 thread_wait(thread);
553 #endif /* USB_DRIVER_CLOSE */
555 bool usb_inserted(void)
557 #ifdef HAVE_USB_POWER
558 return usb_state == USB_INSERTED || usb_state == USB_POWERED;
559 #else
560 return usb_state == USB_INSERTED;
561 #endif
564 #ifdef HAVE_USBSTACK
565 void usb_request_exclusive_ata(void)
567 /* This is not really a clean place to start boosting the cpu. but it's
568 * currently the best one. We want to get rid of having to boost the cpu
569 * for usb anyway */
570 trigger_cpu_boost();
571 if(!exclusive_storage_access) {
572 queue_post(&usb_queue, USB_REQUEST_DISK, 0);
576 void usb_release_exclusive_ata(void)
578 cancel_cpu_boost();
579 if(exclusive_storage_access) {
580 queue_post(&usb_queue, USB_RELEASE_DISK, 0);
581 exclusive_storage_access = false;
585 bool usb_exclusive_ata(void)
587 return exclusive_storage_access;
589 #endif
591 #ifdef HAVE_USB_POWER
592 bool usb_powered(void)
594 return usb_state == USB_POWERED;
596 #endif
598 #else
600 #ifdef USB_NONE
601 bool usb_inserted(void)
603 return false;
605 #endif
607 /* Dummy simulator functions */
608 void usb_acknowledge(long id)
610 id = id;
613 void usb_init(void)
617 void usb_start_monitoring(void)
621 int usb_detect(void)
623 return USB_EXTRACTED;
626 void usb_wait_for_disconnect(struct event_queue *q)
628 (void)q;
631 #endif /* USB_NONE or SIMULATOR */