| # Interrupts | |
| ## NAME | |
| interrupts - Usermode I/O interrupt delivery | |
| ## SYNOPSIS | |
| Interrupt objects allow userspace to create, signal, and wait on | |
| hardware interrupts. | |
| ## DESCRIPTION | |
| Interrupt objects are objects typically used by drivers to receive notifications | |
| of pending interrupt requests (IRQs), typically generated by hardware units. | |
| Interrupts come in two main flavors, physical and virtual, and are slightly | |
| different from other Zircon kernel objects as they signal pending IRQs via a | |
| special set of kernel syscalls instead of relying on the standard signaling | |
| mechanisms. | |
| ### Physical Interrupts | |
| Interrupt objects are typically created by a highly privileged driver processes | |
| (frequently the platform bus driver, or the PCIe bus driver) during device | |
| enumeration, and then made available to the appropriate drivers via delegation. | |
| Objects representing physical edge and level triggered interrupts are created | |
| using `zx_interrupt_create()`, and require that various platform specific | |
| configuration options be passed (such as whether the interrupt is edge vs. | |
| level triggered, or active high vs. active low) in order properly configure the | |
| system's interrupt controller. | |
| Message signaled interrupts (`MSI`s, used by PCIe devices) are slightly | |
| different, and must be allocated and constructed using calls to | |
| `zx_msi_allocate()` and `zx_msi_create()`. | |
| Please refer to the reference documentation for these syscalls for more details. | |
| ### Virtual Interrupts | |
| Virtual interrupts are also created using `zx_interrupt_create()` and passing | |
| the `ZX_INTERRUPT_VIRTUAL` option. Virtual interrupt object are pure software | |
| constructs, and not signalled by hardware based interrupt requests. Instead, | |
| the `zx_interrupt_trigger()` syscall is used to trigger a virtual IRQ for the | |
| object. | |
| Virtual interrupts are typically used in one of two scenarios. | |
| The first is testing. Virtual interrupts use the same special set of syscalls | |
| for waiting and acknowledging that physical interrupt use, however they place | |
| triggering of IRQs under software control. This means that test | |
| code can create a virtual interrupt and pass it off to a driver under test, and | |
| the driver can simply use the interrupt object the way it always does with no | |
| code changes. The difference is that now the test framework has control of the | |
| object's interrupt request state, allowing it to mock various hardware behaviors | |
| during testing. | |
| The second is demultiplexing, usually for GPIO (General Purpose Input Output) | |
| interrupts. Many chips allow general purpose pins to be configured as interrupt | |
| sources to be attached to external devices who need to deliver IRQs to the | |
| system. Typically, configuration of these pins (whether used for interrupts or | |
| not) in addition to control of pins configured as interrupts (masking, | |
| acknowledging, and so on) is done using shared registers which represent banks | |
| of pins. Frequently sets of 16 or 32 pins share one set of registers, as well | |
| as a single physical interrupt tied to the system's interrupt controller. | |
| In systems such as this, there may be one driver responsible for configuring | |
| GPIO hardware and servicing the common GPIO physical interrupt, while other | |
| drivers are responsible for external hardware which may be connected to the main | |
| system using distinct pins which share a common GPIO bank. For example, | |
| consider a system which has an external chip to handle Bluetooth, and | |
| another chip which is an accelerometer. Each driver is separate from the GPIO | |
| driver, as well as from each other, and should be process isolated. However, | |
| the BT and accelerometer drivers each need a separate interrupt object to wait | |
| on, even though they are actually sharing a single physical interrupt under the | |
| control of the GPIO driver. | |
| The interrupts in this situation need to be "de-multiplexed" by the GPIO driver, | |
| and individually delegated to the appropriate drivers by using virtual | |
| interrupts. For each pin configured to be an interrupt, the GPIO driver can | |
| create a virtual interrupt which can be passed on to the appropriate task | |
| specific driver. When the common physical GPIO interrupt fires, the GPIO driver | |
| can identify the virtual interrupt(s) which represent any newly asserted IRQs | |
| and trigger them using `zx_interrupt_trigger()`. After the driver-user of the | |
| virtual interrupt has serviced its hardware and acknowledged the interrupt, the | |
| GPIO driver can then re-enable the interrupt in the proper GPIO register bank, | |
| and wait for a new IRQ via the shared physical interrupt. | |
| ### Waiting for and acknowledging interrupts | |
| Users may wait for an interrupt to be requested via an interrupt object either | |
| synchronously, by blocking a thread on the interrupt object, or asynchronously, | |
| by binding the interrupt to a Zircon port object where IRQs will be delivered | |
| using a port packet which can be read along with other packets sent to the port. | |
| Only one method may be used. An interrupt object cannot be both bound to a port | |
| and have a thread blocked on it at the same time. Additionally, when used in a | |
| synchronous fashion, only one thread may be blocked on an interrupt object at a | |
| time. Attempting to block a second thread will result in an error. Likewise, | |
| when used in an asynchronous fashion, the interrupt object may only be bound to | |
| one port at a time, no more. | |
| #### Synchronous waiting and acknowledgement | |
| After creating an interrupt object, users may block a thread and wait for an IRQ | |
| by calling `zx_interrupt_wait()`. If the interrupt object had already been | |
| triggered (either by a HW IRQ for a physical interrupt, or by a call to | |
| `zx_interrupt_trigger()` on a virtual interrupt), the call will return | |
| immediately. Otherwise, the thread will remain blocked until an IRQ is | |
| asserted, or the interrupt object is destroyed using a call to | |
| `zx_interrupt_destroy()`. | |
| When an interrupt object is triggered, if there is a thread currently waiting on | |
| it, that thread will be woken. Otherwise, if there is not already a thread | |
| waiting on the interrupt object, the next thread to call `zx_interrupt_wait` on | |
| the object will be immediately woken. Even after that thread wakes, the | |
| interrupt remains logically triggered and will not be re-armed and re-enabled | |
| until it has been acknowledged. A wait call will not return until the interrupt | |
| object is in a triggered state. Additionally, after a thread has returned from | |
| wait, a subsequent call to wait will acknowledge the previously triggered | |
| interrupt, causing the interrupt to become untriggered once again. The | |
| interrupt object will be re-armed at the interrupt controller level, and the | |
| thread will be unblocked when the next IRQ is asserted. | |
| #### Asynchronous waiting and acknowledgement | |
| Users may also wait for IRQs asynchronously by binding their interrupts to a | |
| Zircon port object. To do so, users call `zx_interrupt_bind()`, passing a | |
| handle to the interrupt object as well as a handle to a port which has been | |
| created using the `ZX_PORT_BIND_TO_INTERRUPT` option. Once an interrupt has | |
| been bound to a port: | |
| 1) IRQs will be signaled by the queueing of an interrupt port packet to the | |
| bound port. | |
| 2) Unlike standard Zircon signalling, after an interrupt has been bound to a | |
| port, no call needs to be made to `zx_object_wait_async()`. A port packet | |
| will be automatically delivered to the bound port any time an IRQ is | |
| asserted. | |
| 2) Subsequent calls to `zx_interrupt_bind()` will fail. Interrupt objects may | |
| only be bound to a single port at a time. | |
| 3) Calls to `zx_interrupt_wait()` will fail. Interrupt objects can be used | |
| either in a synchronous fashion, or an asynchronous fashion, but not both at | |
| once. | |
| 4) The interrupt object may be disconnected from its bound port by using | |
| the `zx_interrupt_unbind()`, after which it may be re-bound to a different | |
| port, or used synchronously via `zx_interrupt_wait()`. | |
| After a bound interrupt is triggered, and a port packet is delivered, no new | |
| packet will be delivered until the interrupt has been acknowledged. Unlike the | |
| synchronous pattern where interrupts are ack'ed with the next call to | |
| `zx_interrupt_wait()`, asynchronous users of interrupts objects must explicitly | |
| acknowledge interrupts using a call to `zx_interrupt_ack()`. Once the interrupt | |
| has been acknowledged, a new port packet may be delivered either the next time | |
| the IRQ becomes asserted, or immediately if there is already another IRQ | |
| pending. | |
| ### User Signals | |
| While interrupt objects do not use standard Zircon signals to notify users of | |
| IRQs, they are still Zircon kernel objects. As such, they still possess the | |
| standard set of eight "user signals" which can be set and cleared using calls to | |
| `zx_object_signal()`, and waited on using `zx_object_wait_one()`, | |
| `zx_object_wait_many()`, and `zx_object_wait_async()`, provided that the caller | |
| has a handle with sufficient rights. | |
| ### Virtual interrupts and the UNTRIGGERED signal | |
| In addition to user signals, virtual interrupt objects define one other standard | |
| zircon signal called `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED`. | |
| Unlike physical interrupts whose requests are automatically triggered by | |
| hardware when certain conditions are met, virtual interrupt objects need to be | |
| explicitly triggered by software. After a virtual interrupt is triggered, the | |
| software responsible for the triggering needs to know when the IRQ receiver has | |
| processed and acknowledged the IRQ so that it can be re-asserted at the proper | |
| point in time in the future. | |
| In order to provide this knowledge, we introduce the | |
| `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` signal. | |
| When a virtual interrupt is used for testing, this signal can be used to advance | |
| a test to the next phase. When used for demultiplexing interrupts, this signal | |
| can be used by the owner of the set of multiplexed interrupts to unmask and | |
| re-arm interrupts which share a physical interrupt, and then go back to waiting | |
| on the shared physical interrupt. | |
| The rules pertaining to the `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` signal are: | |
| + Immediately after being created, a virtual interrupt object is in the | |
| "untriggered" state, and the `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` signal will be | |
| asserted. | |
| + A call to `zx_interrupt_trigger()` will cause an interrupt object to enter the | |
| triggered state, and de-assert the `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` signal. | |
| + The signal will remain de-asserted until it has become acknowledged by the | |
| consumer. See ("Waiting for and acknowledging interrupts")[#waiting-for-and-acknowledging-interrupts]. | |
| + Observers of the signal use the standard `zx_object_wait_one()`, | |
| `zx_object_wait_many()`, and `zx_object_wait_async()` syscalls. | |
| Note that when an interrupt object is used with a port, it is possible for a | |
| virtual interrupt be in a triggered state, with another interrupt already | |
| pending. An initial call to `zx_interrupt_trigger()` will cause a port packet | |
| to be sent to the bound port immediately, while a subsequent call will pend | |
| another interrupt, but will not immediately trigger a port packet. | |
| In a situation such as this, when a user acknowledges the first port packet | |
| using a call to `zx_interrupt_ack()`, the object effectively moves from the | |
| triggered state to the untriggered state, but is then immediately triggered | |
| once again, delivering the second port packet, and transitioning from | |
| untriggered back to triggered. | |
| In the process, the `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` signal will essentially | |
| be "strobed". Any currently pending wait operations will be satisfied (threads | |
| blocked on the signal will be unblocked, posted async wait operations will | |
| deliver a port packet) however the object signal itself will be immediately | |
| de-asserted as the call to `zx_interrupt_ack()` unwinds. | |
| Physical interrupt objects _do not_ implement | |
| `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED`, and will never assert the signal. | |
| ## NOTES | |
| Interrupt Objects are private to the DDK and not generally available | |
| to userspace processes. | |
| ## SYSCALLS | |
| - [`zx_interrupt_create()`] - Create an interrupt object. | |
| - [`zx_interrupt_destroy()`] - Destroy an interrupt object. | |
| - [`zx_interrupt_bind()`] - Bind an interrupt object to a port object. | |
| - [`zx_interrupt_unbind()`] - Unbind a bound interrupt object from its port. | |
| - [`zx_interrupt_wait()`] - Wait for an IRQ to be asserted on an interrupt object. | |
| - [`zx_interrupt_trigger()`] - Trigger a virtual interrupt object. | |
| - [`zx_interrupt_ack()`] - Acknowledge a port-bound interrupt object and re-arm it. | |
| - [`zx_msi_allocate()`] - Allocate a range of MSI interrupt. | |
| - [`zx_msi_create()`] - Create an MSI interrupt object in an allocated range. | |
| [`zx_interrupt_create()`]: /reference/syscalls/interrupt_create.md | |
| [`zx_interrupt_destroy()`]: /reference/syscalls/interrupt_destroy.md | |
| [`zx_interrupt_bind()`]: /reference/syscalls/interrupt_bind.md | |
| [`zx_interrupt_unbind()`]: /reference/syscalls/interrupt_unbind.md | |
| [`zx_interrupt_wait()`]: /reference/syscalls/interrupt_wait.md | |
| [`zx_interrupt_trigger()`]: /reference/syscalls/interrupt_trigger.md | |
| [`zx_interrupt_ack()`]: /reference/syscalls/interrupt_ack.md | |
| [`zx_msi_allocate()`]: /reference/syscalls/msi_allocate.md | |
| [`zx_msi_create()`]: /reference/syscalls/msi_create.md |