| # 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, it will release either the current thread |
| waiting on it, or the next thread to wait on it. Even after that thread |
| unblocks, however, the interrupt is still logically triggered, and will not be |
| re-armed and re-enabled until it has been acknowledged. Synchronous users of |
| interrupt objects acknowledge their IRQs by waiting on the interrupt object |
| _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 `ZX_VIRTUAL_INTERRUPT_UNTRIGGERED` |
| |
| 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 ack'ed by the consumer, |
| either via the next call to `zx_interrupt_wait()` (synchronous usage), or via |
| a call to `zx_interrupt_ack()` (asynchronous usage). |
| + 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 |