Subversion Repositories Kolibri OS

Rev

Rev 5363 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 9571 $
  9.  
  10.  
  11. ; Initialization of the USB subsystem.
  12. ; Provides usb_init procedure, includes all needed files.
  13.  
  14. ; General notes:
  15. ; * There is one entry point for external kernel code: usb_init is called
  16. ;   from initialization code and initializes USB subsystem.
  17. ; * There are several entry points for API; see the docs for description.
  18. ; * There are several functions which are called from controller-specific
  19. ;   parts of USB subsystem. The most important is usb_new_device,
  20. ;   which is called when a new device has been connected (over some time),
  21. ;   has been reset and is ready to start configuring.
  22. ; * IRQ handlers are very restricted. They can not take any locks,
  23. ;   since otherwise a deadlock is possible: imagine that a code has taken the
  24. ;   lock and was interrupted by IRQ handler. Now IRQ handler would wait for
  25. ;   releasing the lock, and a lock owner would wait for exiting IRQ handler
  26. ;   to get the control.
  27. ; * Thus, there is the special USB thread which processes almost all activity.
  28. ;   IRQ handlers do the minimal processing and wake this thread.
  29. ; * Also the USB thread wakes occasionally to process tasks which can be
  30. ;   predicted without interrupts. These include e.g. a periodic roothub
  31. ;   scanning in UHCI and initializing in USB_CONNECT_DELAY ticks
  32. ;   after connecting a new device.
  33. ; * The main procedure of USB thread, usb_thread_proc, does all its work
  34. ;   by querying usb_hardware_func.ProcessDeferred for every controller
  35. ;   and usb_hub_process_deferred for every hub.
  36. ;   ProcessDeferred does controller-specific actions and calculates the time
  37. ;   when it should be invoked again, possibly infinite.
  38. ;   usb_thread_proc selects the minimum from all times returned by
  39. ;   ProcessDeferred and sleeps until this moment is reached or the thread
  40. ;   is awakened by IRQ handler.
  41.  
  42. iglobal
  43. uhci_service_name:
  44.         db      'UHCI',0
  45. ohci_service_name:
  46.         db      'OHCI',0
  47. ehci_service_name:
  48.         db      'EHCI',0
  49. xhci_service_name:
  50.         db      'XHCI',0
  51. endg
  52.  
  53. ; Initializes the USB subsystem.
  54. proc usb_init
  55. ; 1. Initialize all locks.
  56.         mov     ecx, usb_controllers_list_mutex
  57.         call    mutex_init
  58. ; 2. Kick off BIOS from all USB controllers, calling the corresponding function
  59. ; *hci_kickoff_bios. Also count USB controllers for the next step.
  60. ; Note: USB1 companion(s) must go before the corresponding EHCI controller,
  61. ; otherwise BIOS could see a device moving from EHCI to a companion;
  62. ; first, this always wastes time;
  63. ; second, some BIOSes are buggy, do not expect that move and try to refer to
  64. ; previously-assigned controller instead of actual; sometimes that leads to
  65. ; hangoff.
  66. ; Thus, process controllers in PCI order.
  67.         mov     esi, pcidev_list
  68.         push    0
  69. .kickoff:
  70.         mov     esi, [esi+PCIDEV.fd]
  71.         cmp     esi, pcidev_list
  72.         jz      .done_kickoff
  73.         cmp     word [esi+PCIDEV.class+1], 0x0C03
  74.         jnz     .kickoff
  75.         mov     ebx, uhci_service_name
  76.         cmp     byte [esi+PCIDEV.class], 0x00
  77.         jz      .do_kickoff
  78.         mov     ebx, ohci_service_name
  79.         cmp     byte [esi+PCIDEV.class], 0x10
  80.         jz      .do_kickoff
  81.         mov     ebx, ehci_service_name
  82.         cmp     byte [esi+PCIDEV.class], 0x20
  83.         jz      .do_kickoff
  84.         mov     ebx, xhci_service_name
  85.         cmp     byte [esi+PCIDEV.class], 0x30
  86.         jnz     .kickoff
  87. .do_kickoff:
  88.         inc     dword [esp]
  89.         push    ebx esi
  90.         stdcall get_service, ebx
  91.         pop     esi ebx
  92.         test    eax, eax
  93.         jz      .driver_fail
  94.         mov     edx, [eax+USBSRV.usb_func]
  95.         cmp     [edx+usb_hardware_func.Version], USBHC_VERSION
  96.         jnz     .driver_invalid
  97.         mov     [esi+PCIDEV.owner], eax
  98.         call    [edx+usb_hardware_func.BeforeInit]
  99.         jmp     .kickoff
  100. .driver_fail:
  101.         DEBUGF 1,'K : failed to load driver %s\n',ebx
  102.         jmp     .kickoff
  103. .driver_invalid:
  104.         DEBUGF 1,'K : driver %s has wrong version\n',ebx
  105.         jmp     .kickoff
  106. .done_kickoff:
  107.         pop     eax
  108. ; 3. If no controllers were found, exit.
  109. ; Otherwise, run the USB thread.
  110.         test    eax, eax
  111.         jz      .nothing
  112.         call    create_usb_thread
  113.         jz      .nothing
  114. ; 4. Initialize all USB controllers, calling usb_init_controller for each.
  115. ; Note: USB1 companion(s) should go before the corresponding EHCI controller,
  116. ; although this is not strictly necessary (this way, a companion would not try
  117. ; to initialize high-speed device only to see a disconnect when EHCI takes
  118. ; control).
  119. ; Thus, process all EHCI controllers in the first loop, all USB1 controllers
  120. ; in the second loop. (One loop in reversed PCI order could also be used,
  121. ; but seems less natural.)
  122. ; 4a. Loop over all PCI devices, call usb_init_controller
  123. ; for all EHCI controllers.
  124.         mov     eax, pcidev_list
  125. .scan_xhci:
  126.         mov     eax, [eax+PCIDEV.fd]
  127.         cmp     eax, pcidev_list
  128.         jz      .done_xhci
  129.         cmp     [eax+PCIDEV.class], 0x0C0330
  130.         jnz     .scan_xhci
  131.         call    usb_init_controller
  132.         jmp     .scan_xhci
  133. .done_xhci:
  134.         mov     eax, pcidev_list
  135. .scan_ehci:
  136.         mov     eax, [eax+PCIDEV.fd]
  137.         cmp     eax, pcidev_list
  138.         jz      .done_ehci
  139.         cmp     [eax+PCIDEV.class], 0x0C0320
  140.         jnz     .scan_ehci
  141.         call    usb_init_controller
  142.         jmp     .scan_ehci
  143. .done_ehci:
  144. ; 4b. Loop over all PCI devices, call usb_init_controller
  145. ; for all UHCI and OHCI controllers.
  146.         mov     eax, pcidev_list
  147. .scan_usb1:
  148.         mov     eax, [eax+PCIDEV.fd]
  149.         cmp     eax, pcidev_list
  150.         jz      .done_usb1
  151.         cmp     [eax+PCIDEV.class], 0x0C0300
  152.         jz      @f
  153.         cmp     [eax+PCIDEV.class], 0x0C0310
  154.         jnz     .scan_usb1
  155. @@:
  156.         call    usb_init_controller
  157.         jmp     .scan_usb1
  158. .done_usb1:
  159. .nothing:
  160.         ret
  161. endp
  162.  
  163. uglobal
  164. align 4
  165. usb_event       dd      ?
  166. endg
  167.  
  168. ; Helper function for usb_init. Creates and initializes the USB thread.
  169. proc create_usb_thread
  170. ; 1. Create the thread.
  171.         push    edi
  172.         movi    ebx, 1
  173.         mov     ecx, usb_thread_proc
  174.         xor     edx, edx
  175.         call    new_sys_threads
  176.         pop     edi
  177. ; If failed, say something to the debug board and return with ZF set.
  178.         test    eax, eax
  179.         jns     @f
  180.         DEBUGF 1,'K : cannot create kernel thread for USB, error %d\n',eax
  181. .clear:
  182.         xor     eax, eax
  183.         jmp     .nothing
  184. @@:
  185. ; 2. Wait while the USB thread initializes itself.
  186. @@:
  187.         call    change_task
  188.         cmp     [usb_event], 0
  189.         jz      @b
  190. ; 3. If initialization failed, the USB thread sets [usb_event] to -1.
  191. ; Return with ZF set or cleared corresponding to the result.
  192.         cmp     [usb_event], -1
  193.         jz      .clear
  194. .nothing:
  195.         ret
  196. endp
  197.  
  198. ; Helper function for IRQ handlers. Wakes the USB thread if ebx is nonzero.
  199. proc usb_wakeup_if_needed
  200.         test    ebx, ebx
  201.         jz      usb_wakeup.nothing
  202. usb_wakeup:
  203.         xor     edx, edx
  204.         mov     eax, [usb_event]
  205.         mov     ebx, [eax+EVENT.id]
  206.         xor     esi, esi
  207.         call    raise_event
  208. .nothing:
  209.         ret
  210. endp
  211.  
  212. ; Main loop of the USB thread.
  213. proc usb_thread_proc
  214. ; 1. Initialize: create event to allow wakeup by interrupt handlers.
  215.         xor     esi, esi
  216.         mov     ecx, MANUAL_DESTROY
  217.         call    create_event
  218.         test    eax, eax
  219.         jnz     @f
  220. ; If failed, set [usb_event] to -1 and terminate myself.
  221.         dbgstr 'cannot create event for USB thread'
  222.         or      [usb_event], -1
  223.         jmp     sys_end
  224. @@:
  225.         mov     [usb_event], eax
  226.         push    -1              ; initial timeout: infinite
  227. usb_thread_wait:
  228. ; 2. Main loop: wait for either wakeup event or timeout.
  229.         pop     ecx             ; get timeout
  230.         mov     eax, [usb_event]
  231.         mov     ebx, [eax+EVENT.id]
  232.         call    wait_event_timeout
  233.         push    -1              ; default timeout: infinite
  234. ; 3. Main loop: call worker functions of all controllers;
  235. ; if some function schedules wakeup in timeout less than the current value,
  236. ; replace that value with the returned timeout.
  237.         mov     esi, usb_controllers_list
  238. @@:
  239.         mov     esi, [esi+usb_controller.Next]
  240.         cmp     esi, usb_controllers_list
  241.         jz      .controllers_done
  242.         mov     eax, [esi+usb_controller.HardwareFunc]
  243.         call    [eax+usb_hardware_func.ProcessDeferred]
  244.         cmp     [esp], eax
  245.         jb      @b
  246.         mov     [esp], eax
  247.         jmp     @b
  248. .controllers_done:
  249. ; 4. Main loop: call hub worker function for all hubs,
  250. ; similarly calculating minimum of all returned timeouts.
  251. ; When done, continue to 2.
  252.         mov     esi, usb_hubs_list
  253. @@:
  254.         mov     esi, [esi+usb_hub.Next]
  255.         cmp     esi, usb_hubs_list
  256.         jz      usb_thread_wait
  257.         call    usb_hub_process_deferred
  258.         cmp     [esp], eax
  259.         jb      @b
  260.         mov     [esp], eax
  261.         jmp     @b
  262. endp
  263.  
  264. iglobal
  265. align 4
  266. usb_controllers_list:
  267.         dd      usb_controllers_list
  268.         dd      usb_controllers_list
  269. usb_hubs_list:
  270.         dd      usb_hubs_list
  271.         dd      usb_hubs_list
  272. endg
  273. uglobal
  274. align 4
  275. usb_controllers_list_mutex      MUTEX
  276. endg
  277.  
  278. include "memory.inc"
  279. include "common.inc"
  280. include "hccommon.inc"
  281. include "pipe.inc"
  282. include "protocol.inc"
  283. include "hub.inc"
  284.