NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | PROPERTIES | ALLOWED CALLING CONTEXTS | ATTRIBUTES | SEE ALSO
#include <ddi/pci/pci.h>
The function or functions documented here may not be used safely in all application contexts with all APIs provided in the ChorusOS 5.0 product.
See API(5FEA) for details.
DDI
Provides an API for the development of PCI bus device drivers.
The PCI bus driver offers an API for PCI device drivers' development. This PCI bus API is an abstraction of the low-level PCI bus services and covers the following PCI functional modules:
PCI interrupts management.
Access to the PCI configuration space.
Access to the PIO and memory-mapped device registers.
Access to the PCI devices' memory.
DMA management.
First of all, a PCI device driver must register itself in the microkernel driver registry.
This should be done from its main routine using svDriverRegister.
The driver must set the bus class to "pci" in its registry entry, and specify the lowest version number of the PCI bus interface required to run correctly.
This registration allows the PCI bus driver to call back the PCI device driver's drv_probe, drv_bind and drv_init routines at bus probing, binding and initialization phases, respectively.
Within the drv_probe or drv_init routine, the PCI device driver may establish a connection with the parent PCI bus driver as described below.
Once this connection is established, the PCI device driver may use services provided by the PCI bus driver. Note that a connection to the bus driver can be established only within the driver's drv_probe or drv_init routine.
Initialization Connection
If the drv_init routine is defined, the PCI bus driver invokes it at bus initialization time. Note that the drv_init routine is called in the DKI thread context (refer to the table "Allowed Calling Contexts" to check the list of QUICC bus services allowed in that context). The drv_init routine is defined if the drv_init field of the driver registry entry is set to a non-zero value.
The drv_init routine is called with three arguments:
The PCI device driver's own node, which identifies the node associated to the device in the device tree.
The PCI bus operations structure, which defines the PCI bus API and its version.
The PCI bus identifier, which is an opaque to pass back when calling the PciBusOps.open operation to connect the device driver to the PCI bus.
From this point, PciBusOps.open must be the first call issued by the PCI device driver to the PCI bus driver:
KnError (*open) (PciId busId, DevNode devNode, PciEventHandler eventHandler, PciLoadHandler loadHandler, void* cookie, PciDevId* devId);
This establishes a connection to the bus, identified by the devId value returned in the last argument. This devId identifier is an argument for many other services defined in the PciBusOps structure.
busId and devNode are given by the PCI bus driver as parameters to the driver's drv_init routine. eventHandler is a handler in the device driver which is invoked by the PCI bus driver, when a PCI bus event occurs. Note that eventHandler is optional and must be set to NULL when it is not implemented by the device driver. The cookie argument is passed back to this handler as first argument. This PCI bus event handler takes two additional parameters, an event type specific argument (which is always NULL for the PCI bus events), and the bus event type which is one of the following:
Notifies a device driver that the system is going to be shut down. The device driver should reset the device hardware and return from the event handler. Note that the driver must neither notify clients nor release used resources.
Notifies a device driver that the device is going to be shut down. The device driver should notify driver clients (via svDeviceShutDown ) that the device is going to be shut down and should then return from the event handler. Once the device entry is released by the last driver client, the device registry module invokes a driver call-back handler. Within this handler, the device driver should reset the device hardware, release all used resources and close the bus connection invoking PciBusOps.close. Note that the PCI_DEV_SHUTDOWN event may be used by a bus driver in order to confiscate (or to re-allocate) bus resources. In this case, the PCI bus driver will invoke the driver's drv_init routine again once the bus resources are re-allocated for the device.
Notifies a device driver that the device has been removed from the bus and therefore the device driver instance has to be shut down. The actions taken by the driver are similar to the PCI_DEV_SHUTDOWN case except that the device hardware must not be accessed by the driver.
Notifies the device driver that a system error has been detected on the bus. Actions taken by the driver are driver implementation specific.
Notifies a device driver that PciBusOps.vga_palette_snoop_enable has been issued to the PCI bus driver and the PCI_CMD_PALETTE_SNOOP bit within the bus command register is already set. Basically, this type of event means that there is another (downstream) device on the PCI system that is ready to claim transactions to the VGA palette registers. If a given device does not implement VGA palette snooping or the PCI_CMD_PALETTE_SNOOP bit is already set in the device command register, the device driver must ignore this type of event and simply return K_ENOTIMP. Otherwise, the device driver must set this bit (within the device command register) and return K_OK.
Notifies the device driver that PciBusOps.vga_palette_snoop_disable has been issued to the PCI bus driver and the PCI_CMD_PALETTE_SNOOP bit within the bus command register is already set. Basically, this type of event means that a (downstream) device on the PCI system which previously claimed transactions to VGA palette registers is going to be shut down and therefore the PCI system is looking for another device to claim these transactions in the future. If a given device does not implement the VGA palette snooping or the PCI_CMD_PALETTE_SNOOP bit is already cleared in the device command register, the device driver must ignore this type of event and simply return K_ENOTIMP. Otherwise, the device driver must clear this bit (within the device command register) and return K_OK.
Added to PCI_VERSION_1, see the Common Bus DDI (bus(9DDI)).
As the event handler may be executed in the context of an interrupt, its implementation must be restricted to the API allowed at interrupt level. loadHandler is a handler in the device driver which is invoked by the PCI bus driver, when a new driver appears in the system (as in when a new driver is downloaded at run time). Note that loadHandler is optional and must be set to NULL when it is not implemented by the device driver. The cookie argument is passed back to this handler as unique argument. Typically, a leaf PCI device driver is not affected by this type of event.
loadHandler is usually used by a PCI nexus driver (such as a PCI/ISA bus bridge) to apply a newly downloaded driver to its child devices which are not yet serviced. Note that loadHandler is called in the DKI thread context.
On success, PciBusOps.open returns K_OK and a valid identifier is returned in the devId argument. This identifier must be used to call other services defined in the PciBusOps structure. Some of these services also return an "Ops" structure defining new services on a new object instance, for example an I/O or a memory region. Others just perform a simple service for the device driver, without giving access to a subpart of the API.
On failure, PciBusOps.open returns an error code as follows:
The device node is invalid, for example, the device node is not a child of the bus node.
A connection is already open for the given device node.
The system is out of memory.
To release the connection with the PCI bus, the driver must call the PciBusOps.close service.
void (*close) (PciDevId devId);
After being disconnected from the PCI bus, the device driver can no longer use any of the PCI bus API services.
Probe Connection
If the drv_probe routine is defined (the probe routine is defined if the drv_probe field of DrvRegEntry is set to a non-zero value) the PCI bus driver invokes it at bus probing time. Note that the drv_probe routine is called in the DKI thread context (refer to the table "Allowed Calling Context" to check the list of QUICC bus services allowed in that context).
The drv_probe routine is called with three arguments:
The PCI bus node, which identifies the node associated to the parent PCI bus in the device tree.
The PCI bus operations structure, which defines the PCI bus API and its version.
The PCI bus identifier, which is an opaque to pass back when calling the PciBusOpsopen routine to connect the device driver to the PCI bus.
The drv_probe routine is called first by the bus driver. This allows the PCI device driver to discover a device (which can be serviced by the device driver) on the bus and create a device node for this device in the device tree. If drv_probe creates a node corresponding to a device residing on the bus, it should put properties specifying needed bus resources on the device node (for example, "intr", "io-regs" ).
After checking the device node properties, the PCI bus driver will allocate bus resources for the device (if needed) and/or will check resource conflicts with other devices residing on the bus.
The drv_bind routine is called by the bus driver when the probing phase is complete. It allows the device driver to perform a driver to device binding. Typically, a PCI device driver examines the vendor and device identifier properties in order to check whether a given device can be serviced by the driver. If the check is positive, the driver binds itself to the device. A driver initiated binding is performed by putting the "driver" property into the device node. The "driver" property value is a NULLterminated ASCII string specifying the driver name.
The driver name specified in the property must match the driver name specified in the driver registry entry. Under these conditions, the PCI bus driver will invoke the driver's drv_init routine for this device node as described above.
The drv_init routine is invoked by the bus driver if, and only if, the bus resources required for the device are successfully allocated/checked by the bus driver.
The drv_bind routine is called in the context of a DKI thread. It is therefore only possible to invoke PCI bus services that are allowed in the DKI thread context.
Devices residing on the PCI bus are discovered by accessing the PCI configuration space and analyzing the PCI header structure. Note that the PCI bus driver provides services to access the PCI configuration space, but they may be used only when a connection is established between the PCI bus driver and the PCI device driver (see section Accessing PCI configuration space).
In order to establish this type of connection, the device driver has to create a device node and attach it to the bus node. This device node may be temporary, but it is mandatory as an argument to the PciBusOps.open routine. When the probing process is finished and the connection is closed, depending on the probing results, the device driver should either delete the temporary device node or change it into a real device node. The latter case means that a device which may be serviced by the driver will be found on the bus.
The device driver should avoid creating redundant device nodes. In particular, when the device driver discovers a device through the PCI configuration space and wants to create a device node corresponding to this device, it must ensure that there is no existing device node (among the bus child nodes) which represents the same device.
If a connection to the PCI bus is established by the drv_probe routine, it should be closed (via PciBusOps.close ) before returning from the routine.
To summarize, a basic scenario of the PCI device driver's drv_probe routine may be considered as follows:
Create a temporary node and attach it to the bus node.
Open a connection to the PCI bus driver ( PciBusOps.open).
Read the PCI configuration headers looking for the given device and vendor ID.
Once the header matches the given device and vendor ID:
Go through the bus child nodes looking for a node corresponding to the configuration header. The "bus-num" property (attached to the bus node) and the "dev-num", "func-num" properties (attached to the device node) specify the configuration header address within the PCI configuration space.
If the node corresponding to the configuration header does not exist, create a new device node and attach it to the bus node (or use the temporary node), put the mandatory PCI properties on the node and (optionally) add extra properties.
Detach and delete the temporary node (if it was not changed into a real device node).
Close the connection to the PCI bus driver.
Return to the PCI bus driver.
To perform I/O access to registers of a PCI device, a PCI driver must:
Map the device's registers into an I/O region
Use services defined by the mapped I/O region to access registers
Unmap the region when access is no longer needed
Map Device I/O Registers
The PciBusOps.io_map service is used to map an I/O region from a PCI device to enable access to this region:
KnError (*io_map) (PciDevId devId, PciPropIoRegs* ioRegs, PciErrHandler errHandler, void* errCookie, PciIoOps** ioOps, PciIoId* ioId);
devId is returned by PciBusOps.open. The ioRegs structure defines the I/O region to map.
This structure is an element of the array stored in a property of the device node. This property should be retrieved by the driver using the device tree API dtreePropFind, dtreePropLength, dtreePropValue, prior to calling PciBusOps.io_map.
The name of the property used to describe device I/O regions is "io-regs", its value contains an array of PciPropIoRegs structures:
typedef struct { PciIoSpace space; PciAddr addr; PciSize size; PciAddr mask; PciSize barOff; /* New in PCI_VERSION_1 */ } PciPropIoRegs;
The space field specifies the PCI address space where the registers reside:
PCI PIO space
PCI memory space
The addr field specifies the PCI start address of the register's range. The size field specifies the register's range size in bytes.
The mask field specifies which address bits are fixed/floating. This field is used by the PCI bus driver at the resource allocation stage in order to determine the device address decoder constraints. A bit set to zero within mask specifies a fixed address bit within addr. In other words, the corresponding bit within an allocated address must be the same as within addr. A bit set to one within mask specifies a floating address bit, that is, any value of the corresponding bit within an allocated address is acceptable.
The barOff field specifies the register offset in the configuration header where this I/O property should be defined/applied (Base Address Register Offset).
When the PciPropIoRegs structure is passed to PciBusOps.io_map the I/O register range is already allocated and therefore the mask field is meaningless.
The errHandler argument is a handler in the device driver, which is invoked by the PCI bus driver, when a PCI bus error occurs while accessing the mapped region. errCookie is passed back to the errHandler as first argument. (see section PCI Error Handling.)
On success, K_OK is returned and appropriate I/O services are returned in the ioOps argument. An identifier for the mapped I/O region is also returned in ioId. This identifier must be used as first argument to further calls to the PciIoOps services.
On failure, PciBusOps.io_map returns an error code as follows:
A size of zero was specified.
Invalid or unsupported PCI address space.
Not enough virtual address space to map I/O registers.
The system is out of memory.
Note that, before accessing mapped I/O registers, the device driver should initialize an appropriate base address register within the device configuration header correctly.
Perform I/O Access to a Mapped I/O Region
Once the region is successfully mapped, the driver can use the services defined by the PciIoOps structure. The PCI bus provides four service routine sets to access a mapped I/O region:
PciIoOps.load_8/16/32/64
PciIoOps.store_8/16/32/64
PciIoOps.read_8/16/32/64
PciIoOps.write_8/16/32/64
There are four service routines in each set which deal with I/O registers of different widths. The _8, _16, _32 or _64 suffix indicates the data size of the transfer on the PCI bus. In all service routines provided by PciIoOps the ioId argument identifies the mapped I/O region. The offset argument specifies the register offset, in bytes, within the mapped region. All these routines handle byte swapping in cases where the endian should be different for the PCI bus and CPU.
Load From a Register
The PciIoOps.load_xx routine set returns a value loaded from a device register.
uintxx_f (*load_xx) (PciIoId ioId, PciSize offset);
The size of the returned value and the data transfer on the PCI bus is specified by the _xx suffix.
Store to a Register
The PciIoOps.store_xx routine set stores a given value into a device register.
void (*store_xx) (PciIoId ioId, PciSize offset, uintxx_f value);
The size of the given value and the data transfer on the PCI bus is specified by the _xx suffix.
Multiple Read from a Register
The PciIoOps.read_xx routine set loads values from a device register and sequentially writes them into a memory buffer.
void (*read_xx) (PciIoId ioId, PciSize offset, uintxx_f* buf, PciSize count);
The size of each value loaded and of each data transfer on the PCI bus is specified by the _xx suffix. The count argument specifies the number of read transactions to perform. The buf argument specifies the address of the memory buffer. The size of this buffer must be at least ( count* size of each data transfer).
Multiple Write to a Register
The PciIoOps.write_xx routine set sequentially reads values from a memory buffer and stores them into a device register.
void (*write_xx) (PciIoId ioId, PciSize offset, uintxx_f* buf, PciSize count);
The size of each value stored and of each data transfer on the PCI bus is specified by the _xx suffix. The count argument specifies the number of write transactions to perform. The buf argument specifies the address of the memory buffer. The size of this buffer must be at least ( count* size of each data transfer).
The new version of the PCI Bus DDI (PCI_VERSION_1) adds the following new functions:
void (*read_swap_xx) (PciIoId io_id, PciSize off, uintxx_f* addr, PciSize cnt); void (*write_swap_xx) (PciIoId io_id, PciSize off, uintxx_f* addr, PciSize cnt);
where xx could be 1, 32, 64 for 2, 3 or 4 bytes to swap. They behave the same way as their non-swap counterpart, except of course that they swap bytes in the value(s) they handle versus the standard PCI little-endian format.
UnmapDevice I/O Registers
When a driver no longer needs access to the device I/O register, or before closing the connection to the PCI bus, it should unmap the I/O region used for these devices. The PciBusOps.io_unmap is used to unmap an I/O region previously mapped with PciBusOps.io_map, and to release any system resources used for this mapping.
void (*io_unmap) (PciIoId ioId);
The ioId argument identifies the region to unmap. When the PciBusOps.io_unmap function is called, the driver can no longer use any services defined for the unmapped I/O region.
To perform access to the memory of a PCI device, a PCI driver must:
Map device memory to the virtual memory space
Access device memory through the mapped region
Unmap the region when access is no longer needed
Map Device Memory
The PciBusOps.mem_map service is used to map a memory region from a PCI device, enabling access to this region.
KnError (*mem_map) (PciDevId devId, PciPropMemRgn* memRgn, PciMemAttr memAttr, PciErrHandler errHandler, void* errCookie, void** memAddr, PciMemId* memId);
devId is the identifier returned by PciBusOps.open. The memRgn structure defines the memory region to map. This structure is an element of the array stored in a property of the device node.
This property should be retrieved by the driver using the device tree API (dtreePropFind, dtreePropLength, dtreePropValue) prior to calling PciBusOps.mem_map.
The name of the property used to describe device memory regions is "mem-rgn", its value contains an array of PciPropMemRgn structures.
typedef struct { PciAddr addr; PciSize size; PciAddr mask; PciZize barOff; /* New in PCI_VERSION_1 */ } PciPropMemRgn;
The addr field specifies the PCI start address of the memory region. The size field specifies the memory region size in bytes. The mask field specifies the device address decoder constraints (see section Mapping device I/O registers).
The barOff field specifies the register offset in the configuration header where this Mem property should be defined/applied (Base Address Register Offset).
The memAttr argument specifies the mapping attributes. A combination of the following flags is allowed:
The memory region is readable.
The memory region is writable.
The memory region is executable.
The memory region is cacheable.
The memory region mapping inverts the byte order. Some MMUs support this type of mapping attribute. This feature may be used by a PCI device driver in order to avoid byte swapping within a memory mapped region. Note that PciBusOps.mem_map returns K_EINVAL if this type of feature is not supported.
errHandler is a handler in the device driver which is invoked by the PCI bus driver when a PCI bus error occurs while accessing the mapped region. errCookie is passed back to the errHandler as first argument. (see section PCI error Handling ) .
On success, K_OK is returned and the start virtual address of the mapped memory region is returned in the memAddr argument. An identifier for the mapped memory region is also returned in memId. This identifier must be used to unmap the memory region. On failure, PciBusOps.mem_map returns an error code as follows:
A size of zero was specified.
Invalid or unsupported memory mapping attributes.
Not enough virtual address space to map.
The system is out of memory.
Once the region is successfully mapped, the device driver may now access the device memory by dereferencing the returned pointer. Note that in this mode, the driver must handle endianning problems independently..
The PCI bus driver may support the 64-bit PCI memory space. In this case, a PCI device driver may use the PciBusOps.mem64_map service in order to map a 64-bit memory space region from a PCI device, enabling access to this region.
KnError (*mem64_map) (PciDevId devId, Pci64PropMemRgn* memRgn, PciMemAttr memAttr, Pci64ErrHandler errHandler, void* errCookie, void** memAddr, Pci64MemId* memId);
PciBusOps.mem64_map is analogous to PciBusOps.mem_map except the PCI address and size are 64-bit unsigned integer values.
The 64-bit PCI memory region is specified by the Pci64PropMemRgn structure.
typedef struct { Pci64Addr addr; Pci64Size size; Pci64Addr mask; PciZize barOff; /* New in PCI_VERSION_1 */ } Pci64PropMemRgn;
Note that PciBusOps.mem64_map returns K_ENOTIMP if the 64-bit PCI memory space is not supported by the PCI bus driver. Note that, before accessing a mapped memory region, the device driver should initialize an appropriate base address register within the device configuration header correctly (offset devined by the barOff field in version 1).
Unmap a Memory Region
When a driver no longer needs access to the device memory, or before closing the connection to the PCI bus, it should unmap the memory region. The PciBusOps.mem_unmap command is used to unmap a memory region previously mapped with PciBusOps.mem_map, and to release any system resources used for this mapping.
void (*mem_unmap) (PciMemId memId);
PciBusOps.mem64_unmap is used to unmap a region within the 64-bit PCI memory space, previously mapped with PciBusOps.mem64_map, and to release any system resources used for this mapping.
void (*mem64_unmap) (Pci64MemId memId);
The memId argument identifies the memory region to unmap. Once the memory region is unmapped, the driver is no longer allowed access to the region.
Direct Memory Access
To perform Direct Memory Access to the system memory, a PCI device driver must:
Allocate a DMA region on the bus.
Use services defined by the DMA region object to retrieve the DMA region properties (the virtual and PCI addresses)..
Program the device DMA engine to perform a DMA transfer.
Release the DMA region when it is no longer needed.
Allocate a DMA Region
The PciBusOps.dma_alloc service allocates a system memory region a of specified size and contiguously maps it to the supervisor address space.
KnError (*dma_alloc) (PciDevId devId, PciSize size, PciDmaAlign* dmaConstr, PciDmaAttr dmaAttr, PciMemAttr memAttr, PciErrHandler errHandler, void* errCookie, PciDmaOps** dmaOps, PciDmaId* dmaId);
The devId argument is the identifier returned by PciBusOps.open. The size argument is the requested size in bytes to allocate for the DMA memory region. The dmaConstr argument defines the address decoder constraints for the DMA memory region to allocate. Note that the dmaConstr argument may be set to NULL specifying that there are no constraints on the DMA region to allocate. Otherwise, it should point to a PciDmaAlignstructure shown below:
typedef struct PciDmaAlign { PciAddr addr; PciAddr alignment; PciAddr floating; } PciDmaAlign;
The addr field specifies the PCI start address of the DMA region. The alignment field is a mask which specifies constraints on the start address of the DMA region being allocated. A bit set within alignment specifies that the corresponding bit within the start address may take any value, that is, there are no specific constraints on that bit. A bit cleared within alignment specifies that the corresponding bit within the start address must take the same value as the corresponding bit of the addr field. This mask allows the caller to specify an alignment for the start address of the allocated DMA region, by zeroing the required number of least significant bits. By resetting the required number of most significant bits, the caller may also indicate where, in the PCI space, the memory must be allocated.
The floating field is a mask which indicates which bits of the returned address can vary while looking through the allocated DMA region for the required size. Bits cleared in the mask must be constant for all addresses in the range of the allocated DMA region. This mask may be used to specify that the allocated amount of memory must not span across a given address boundary.
The dmaAttr argument specifies the DMA transfer type. A combination of the following flags is allowed:
The region is used for DMA read transfer.
The region is used for DMA write transfer.
Allocate a synchronous DMA region for which no synchronization is needed between access from the DMA engine and the CPU.
This attribute may be not supported on all platforms. In this case, PciBusOps.dma_alloc returns K_EINVAL.
The memAttr argument specifies the mapping attributes (see section Mapping Device Memory).
errHandler is a handler in the device driver, which is invoked by the PCI bus driver when a PCI bus error occurs while accessing the DMA region. errCookie is passed back to the errHandler as first argument. (see section PCI Error Handling ) .
On success, K_OK is returned and appropriate services are returned in the dmaOps argument. An identifier for the DMA region is also returned in dmaId. This identifier must be used as first argument to further calls to the PciDmaOps services.
On failure, PciBusOps.dma_alloc returns an error code as follows:
A size of zero was specified.
Invalid or unsupported memory mapping attributes.
Not enough virtual address space to map.
The system is out of memory.
The PCI bus driver may support 64-bit PCI space. In this case, a PCI device driver may use the PciBusOps.dma64_alloc service in order to allocate a DMA region in the 64-bit PCI space:
KnError (*dma64_alloc) (PciDevId devId, Pci64Size size, Pci64DmaAlign* dmaConstr, PciDmaAttr dmaAttr, PciMemAttr memAttr, Pci64ErrHandler errHandler, void* errCookie, Pci64DmaOps** dmaOps, Pci64DmaId* dmaId);
PciBusOps.dma64_alloc is analogous to PciBusOps.dma_alloc except the PCI address and size are 64-bit unsigned integer values. The Pci64DmaOps services are analogous to the PciDmaOps ones. Note that PciBusOps.dma64_alloc returns K_ENOTIMP if the 64-bit PCI space is not supported by the PCI bus driver.
Get the Virtual Address of a DMA Region
The PciDmaOps.virt_addr routine returns the virtual start address of a given DMA region (specified by dmaId).
void* (*virt_addr) (PciDmaId dmaId);
The driver uses this address to access the DMA region using CPU instructions. Note that the driver should synchronize the region as appropriate, depending on the attributes used when allocating the region (see section Synchronizing the DMA Region).
Get the PCI Address of a DMA Region
The PciDmaOps.phys_addr routine returns the PCI start address of a given DMA region (specified by dmaId).
PciAddr (*phys_addr) (PciDmaId dmaId); Pci64Addr (*phys_addr) (PciDmaId dmaId);
The driver uses this address to program its DMA engine. Note that the driver should synchronize the region as appropriate, depending on the attributes used when allocating the region (see section Synchronizing the DMA region ).
Synchronize the DMA Region
If the DMA region was not allocated using PCI_DMA_SYNC attribute, the device driver should synchronize accesses to the same system memory (the DMA region) from the CPU and the DMA engine correctly.
Depending on the platform, these routines handle possible problems of cache coherency, DMA engine buffers, and others. PciDmaOps.read_sync is a barrier between CPU writes and DMA reads from a given DMA sub-region.
void (*read_sync) (PciDmaId dmaId, PciSize offset, PciSize size); void (*read_sync) (PciDmaId dmaId, Pci64Size offset, Pci64Size size);
PciDmaOps.write_sync is a barrier between DMA writes and CPU reads from a given DMA sub-region.
void (*write_sync) (PciDmaId dmaId, PciSize offset, PciSize size); void (*write_sync) (PciDmaId dmaId, Pci64Size offset, Pci64Size size);
For both routines: the dmaId argument identifies the DMA region. offset is the offset in bytes from the beginning of the DMA memory region. offset and size both define the sub-region to synchronize.
Release a DMA Region
When a driver no longer needs an allocated DMA region, or before closing the connection to the PCI bus, it should release the DMA region. The PciBusOps.dma_free is used to release a DMA region, previously allocated with PciBusOps.dma_alloc.
void (*dma_free) (PciDmaId dmaId);
The PciBusOps.dma64_free is used to release a DMA region, previously allocated with PciBusOps.dma64_alloc.
void (*dma64_free) (Pci64DmaId dmaId);
The dmaId argument identifies the DMA region to free. Once the DMA region is released, the driver may no longer use the DMA region.
To perform access to the PCI configuration space, a PCI driver must:
Map a PCI configuration header
Use services defined by the mapped PCI configuration header object
Unmap the PCI configuration header when access is no longer needed
Map a PCI Configuration Header
The PciBusOps.conf_map service is used to map a PCI configuration header enabling access to its contents.
KnError (*conf_map) (PciDevId devId, uint8_f busNum, uint8_f devNum, uint8_f funcNum, PciConfOps** confOps, PciConfId* confId);
devId is returned by PciBusOps.open.
The busNum, devNum and funcNum arguments specify the header address in the PCI configuration space. busNum specifies the bus number within the PCI system. It is available for a PCI driver as the "bus-num" property is attached to the parent bus node. The bus number is an integer value in a range from 0 to 15.
devNum specifies the device number on the bus. The device number is an integer value in a range from 0 to 31. funcNum specifies the function number within a multifunctional device. The function number is an integer value in a range from 0 to 3.
On success, K_OK is returned and appropriate PCI configuration services are returned in the confOps argument. An identifier for the mapped PCI configuration header is also returned in confId. This identifier must be used as first argument to further calls to the PciConfOps services.
On failure, PciBusOps.conf_map returns an error code as follows:
The configuration header with the given address does not exist.
The system is out of memory.
Access a PCI Configuration Header
Once the PCI configuration header is successfully mapped, the driver can use the services defined by the PciConfOps structure. The PCI bus provides two service routine sets to access a mapped PCI configuration header:
PciConfOps.load_8/16/32
PciConfOps.store_8/16/32
There are three service routines in each set which deal with different data sizes. The _8, _16 or _32 suffix indicates the location size.
In all service routines provided by PciConfOps the confId argument identifies the mapped PCI configuration header. The offset argument specifies the location offset (in bytes) within the PCI configuration header. offset must be in a range from 0 to 255. All these routines handle byte swapping should the endian be different for the PCI bus and CPU.
Load From a PCI Configuration Header Location
The PciConfOps.load_xx routine set returns a value loaded from a PCI configuration header location.
uintxx_f (*load_xx) (PciConfId confId, uintxx_f offset);
The size of the returned value is specified by the _xx suffix.
Store to a PCI Configuration Header Location
The PciConfOps.store_xxroutine set stores a given value into a PCI configuration header location.
void (*store_xx) (PciConfId confId, uint8_f offset, uintxx_f value);
The size of the given value is specified by the _xx suffix.
Unmap a PCI Configuration Header
When a driver no longer needs access to the PCI configuration header, or before closing the connection to the PCI bus, it should unmap the PCI configuration header.
The PciBusOps.conf_unmap is used to unmap a PCI configuration header previously mapped with PciBusOps.conf_map and to release any resources allocated for this mapping.
void (*conf_unmap) (PciConfId confId);
The confId argument identifies the PCI configuration header to unmap. When the PciBusOps.conf_unmap function is called, the driver may no longer use any services defined for the unmapped PCI configuration header.
To connect a handler to a PCI interrupt line, a PCI driver should:
Disable the interrupt at the device level, typically by accessing the device registers.
Attach a handler to the interrupt.
Enable the interrupt at the device level.
Use services defined by the attached interrupt object.
Disable the interrupt at the device level.
Detach the interrupt handler when no longer needed.
Attach a Handler to a PCI Interrupt Line
A PCI device driver can connect a handler to a PCI interrupt line by calling the PciBusOps.intr_attach service.
KnError (*intr_attach) (PciDevId devId, PciPropIntr* intr, PciIntrHandler intrHandler, void* intrCookie, PciIntrOps** intrOps, PciIntrId* intrId);
The devId argument identifies the connection with the PCI bus driver.
The intr argument indicates the PCI interrupt line to which the handler will be connected. There are four interrupt lines available on the PCI bus:
PCI_INTR_A
PCI_INTR_B
PCI_INTR_C
PCI_INTR_D
Typically, a PCI device driver should find the interrupt to attach to by looking up the "intr" property in the device node. The intrHandler argument is a handler in the device driver which is invoked by the PCI bus driver when the corresponding interrupt occurs on the bus.
typedef PciIntrStatus (*PciIntrHandler) (void* intrCookie);
The intrCookie is passed back to this interrupt handler as an argument.
This type of PCI interrupt handler must return a PciIntrStatus value which indicates to the bus driver whether the interrupt was claimed by the handler, and how it was handled:
Must be returned by the interrupt handler if there is no pending interrupt for the device.
Must be returned by the interrupt handler if a pending device interrupt has been serviced by the interrupt handler, and the interrupt has not been enabled (acknowledged) at PCI bus level (see section Enabling/Disabling a serviced interrupt).
Must be returned by the interrupt handler if a pending device interrupt has been serviced by the interrupt handler and the interrupt has been enabled (acknowledged) at PCI bus level (see section: Enabling/Disabling an Attached Interrupt).
On success, K_OK is returned and services defined on an attached interrupt object are returned in the intrOps argument. An identifier for the attached interrupt is also returned in intrId. This identifier must be used as first argument to further calls to the PciIntrOps services.
The PCI bus architecture allows an interrupt line to be shared among multiple devices residing on the bus. This means that multiple drivers may attach a handler to the same PCI interrupt line. In that case, when this type of interrupt occurs on the bus, all the attached handlers are called in an order which is not defined and is implementation specific. When called, each handler can test the device status to check whether the interrupt is triggered by the device it manages.
As an interrupt line is shareable, a driver must take into account that the interrupt it attaches to is enabled at the bus level, and that it may occur as soon as the attachment is complete. The fact that a driver could already have attached a handler to the interrupt, and enabled it, dictates this behavior. If this is not acceptable for a given driver, it must disable the interrupt at the device level, prior to attaching the handler. This ensures that the handler will not have to service an interrupt (the handler will return PCI_INTR_UNCLAIMED) until it is enabled again at device level.
Mask/Unmask an Attached Interrupt
The PciIntrOps.mask service routine masks the interrupt source specified by intrId. Note that PciIntrOps.mask does not guarantee that all other interrupt sources are still unmasked.
void (*mask) (PciIntrId intrId);
The PciIntrOps.unmask service routine unmasks the interrupt source previously masked by PciIntrOps.mask.
void (*unmask) (PciIntrId intrId);
Note that PciIntrOps.unmask does not guarantee that the interrupt source is unmasked immediately. The real interrupt source unmasking may be deferred.
The PciIntrOps.mask / PciIntrOps.unmask pair may be used at either base or interrupt level.
Note that the PciIntrOps.mask / PciIntrOps.unmask pairs must not be nested.
Enable/Disable a Serviced Interrupt
The PciIntrOps.enable and PciIntrOps.disable service routines are dedicated to interrupt handler usage only. In other words, these routines may be called only by an interrupt handler.
The PciIntrOps.enable service routine enables (and acknowledges) the bus interrupt source specified by intrId.
PciIntrStatus (*enable) (PciIntrId intrId);
PciIntrOps.enable returns either PCI_INTR_ACKNOWLEDGED or PCI_INTR_CLAIMED. The PCI_INTR_ACKNOWLEDGED return value means that the PCI bus driver has enabled (and acknowledged) interrupt at bus level. The PCI_INTR_CLAIMED return value means that the PCI bus driver has ignored the request and therefore the interrupt source is still disabled (and not acknowledged) at bus level.
The bus driver typically refuses an explicit interrupt acknowledgement (issued by an interrupt handler) for the shared interrupts. In this case, the bus driver will acknowledge interrupts only when all interrupt handlers have been called. In cases where the PciIntrOps.enable routine has been called by an interrupt handler, the handler must return the value which has been returned by PciIntrOps.enable. Once PciIntrOps.enable is called, drivers should be able to handle an immediate re-entrance in the interrupt handler code.
The PciIntrOps.disable service routine disables the interrupt source previously enabled by PciIntrOps.enable.
void (*disable) (PciIntrId intrId);
If PciIntrOps.enable returns PCI_INTR_ACKNOWLEDGED, the driver must call PciIntrOps.disable prior to returning from the interrupt handler.
When an interrupt occurs, the attached PciIntrHandler is invoked with the interrupt source disabled at bus level. This produces the same effect as callingPciIntrOps.disable just prior to the handler invocation.
Note that the interrupt handler must return to the bus driver in the same context as it was called, that is, with the interrupt source disabled at bus level. However, the called interrupt handler may use the PciIntrOps.enable/disable pair to allow the interrupt to be nested. This feature is typically used by a PCI-to-bus bridge driver when the secondary bus interrupts are multiplexed; for example, multiple secondary bus interrupts are reported through the same primary PCI bus interrupt.
Typically, an interrupt handler of this type of PCI-to-bus bridge driver would take the following actions:
Identify and disable the secondary bus interrupt source
Enable the primary PCI bus interrupt source ( enable )
Call handlers attached to the secondary bus interrupt source
Disable the primary PCI bus interrupt source ( disable )
Return from the interrupt handler
Detach an Attached Interrupt
When a driver no longer needs to handle an interrupt, or before closing the connection to the PCI bus, it should detach the handler attached to an interrupt line. The PciBusOps.intr_detach is used to detach a handler, previously attached with PciBusOps.intr_attach, and to release any resources allocated for this attachment.
void (*intr_detach) (PciIntrId intrId);
The intrId argument identifies the attachment to release. When the PciBusOps.intr_detach function is called, the driver may no longer use the identifier ( intrId ).
PCI Interrupt Acknowledge Transactions
The PciBusOps.intr_vector routine performs a PCI interrupt acknowledge transaction on the PCI bus and returns the interrupt vector delivered on the bus.
uint32_f (*intr_vector) (PciDevId devId);
The devId argument identifies the connection with the PCI bus driver.
In cases where the PCI bus driver does not implement this type of feature, the value 0xffffffff is returned, otherwise an interrupt vector/number is returned whose value (semantic) depends on the PCI device which responds to the cycle. Note that only one PCI device on the bus should respond to this type of interrupt acknowledgement cycle.
PciBusOps.intr_vector must be called only from the interrupt handler of the PCI device responding to the PCI interrupt acknowledgement transaction. Typically, this type of device implements PIC features for a sub-bus bridge.
When mapping PCI bus space, a PCI device driver gives an error handler which is invoked by the PCI bus driver when a bus error occurs. Error handlers are used for the following services:
PciBusOps.io_map
PciBusOps.mem_map
PciBusOps.mem64_map
PciBusOps.dma_map
PciBusOps.dma64_map
When a programmed or direct memory access is aborted because of a bus error, the PCI bus driver invokes the error handler which is connected for the PCI address and space concerned. A PciErrHandler is called with two arguments: errCookie and err. The errCookie is an opaque given by the caller and passed back when the handler is called.
The err argument points to the PciBusError structure.
typedef struct { PciErrorCode code; PciSize offset; } PciBusError;
The code field indicates the type of error that occurred. The offset field indicates the offset within the associated region at which the error occurred.
The error code can be as follows:
Unknown error, for example, the PCI bus driver is unable to determine the reason for the exception.
Invalid (that is, unsupported) access granularity has been used.
A parity error was detected on the PCI bus.
A master abort is signaled on the PCI bus, that is, a transaction has been aborted by a master because it has not been claimed by any target on the bus.
A target abort is detected on the PCI bus, for example, a claimed transaction has been aborted by a target.
For 64-bit PCI address space, a PciErrHandler is called with two arguments: errCookie and err64. The err64 argument points to the Pci64BusError structure.
typedef struct { PciErrorCode code; Pci64Size offset; } Pci64BusError;
As a bus error may be reported in the context of an exception or an interrupt, the implementation of the error handler must be restricted to the interrupt level API.
PCI to PCI Bridge Control Register
The 16-bit bridge control register is specific to the PCI-to-PCI bridge. Bits within the bridge control register may be set or cleared by a device driver using the PciBusOps.control_set and PciBusOps.control_clr routines respectively.
void (*control_set) (PciDevId devId, uint16_f ctrl_bits); void (*control_clr) (PciDevId devId, uint16_f ctrl_bits);
The devId argument identifies the connection with the PCI bus driver. The mask argument specifies the bits to set/clear in the bridge control register. mask is a combination of the following flags:
Flag when set/cleared, enables/disables the VGA address decoding (0x000a0000..0x000bffff)
Flag when set/cleared, enables/disables the positive decoding of non-aliased ISA addresses ([0x0000..0xffff] when A[9:8] are zero)
Note that the PciBusOps.control_set/control_clr routines are mainly dedicated to a PCI-to-ISA bridge driver. When the bridge control register is implemented, the PCI-to-PCI bus driver sets/clears the mask bits within its bridge control register and, in addition, propagates the bits setting/clearing upstream. In other words, the PCI-to-PCI bus driver invokes an appropriate routine of its parent PCI bus driver. In cases where the PCI bridge does not implement the bridge control register, the PCI bus driver provides an empty implementation of the PciBusOps.control_set and PciBusOps.control_clr routines.
A PCI system may have two video controllers present in the system: a VGA-compatible interface and another graphics controller. In this case, both devices implement a set of color palette registers at the same I/O addresses. When the CPU performs an I/O write to update the palette registers, the palette in both devices must be updated. On the other hand, only one device should claim this I/O transaction and another one should quietly snoop the write data on the bus.
Only a VGA-compatible device can claim read transactions to the palette registers, other graphics controllers do not participate at all in such transactions.
When these types of devices reside on different busses, the upstream device (which is closed to the host bus) should quietly snoop these transactions and the downstream device (which is far from the host bus) should claim these transactions (that is, participate as a target). Otherwise, the transactions will not be visible to the downstream device. Snooping only works when both devices reside along the same bus path on the system.
The PciBusOps.vga_palette_snoop_enable and PciBusOps.vga_palette_snoop_disable routines provide a means for graphics controllers to dynamically arbitrate palette snooping on the PCI system.
void (*vga_palette_snoop_enable) (PciDevId devId); void (*vga_palette_snoop_disable) (PciDevId devId);
In both routines, the devId argument identifies the connection with the PCI bus driver.
At bus initialization time, the PCI bus driver disables VGA palette snooping in its command register. At device initialization time, a graphics controller driver should disable VGA palette snooping in its command register and should invoke vga_palette_snoop_enable. This means that the video driver requests the PCI bus to enable delivery on the bus write transactions to the VGA palette registers.
The PCI bus driver checks whether VGA palette snooping is disabled on the bus and, if so, enables VGA palette snooping in its command register and calls vga_palette_snoop_enable on its parent PCI bus (if any) in order to propagate the VGA snooping enable request upstream on the PCI system. If VGA palette snooping is already enabled in the bus command register, the bus driver invokes the event handlers with the PCI_VGA_PALETTE_SNOOP_ENABLE event code.
The event handler behavior must be as follows. If a given device does not implement VGA palette snooping or the PCI_CMD_PALETTE_SNOOP bit is already set in the device command register, the device driver ignores the event and simply returns K_ENOTIMP. Otherwise, the device driver sets this bit (within the device command register) and returns K_OK. The iteration is interrupted and the bus driver returns from the vga_palette_snoop_enable routine, once an event handler returns K_OK.
This means that a device claiming write transactions to the VGA palette registers was found and was switched to snooping mode. Note that it may be the device which issued vga_palette_snoop_enable. If the iteration is performed and nobody returns K_OK, an error condition is met. This means that graphics controllers do not reside along the same bus path on the system. Under this type of error condition, VGA palette snooping does not work and the bus driver behavior is bus driver implementation specific.
When a graphics controller driver is going to be shut down, it should check whether it claims write transactions to the VGA palette registers; for example, whether the PCI_CMD_PALETTE_SNOOP bit is cleared in the device command register. If so, the driver should call vga_palette_snoop_disable prior to closing the connection to the PCI bus driver.
The PCI bus driver invokes the event handlers with the PCI_VGA_PALETTE_SNOOP_DISABLE event code.
The event handler behavior must be as follows. If a given device does not implement VGA palette snooping or the PCI_CMD_PALETTE_SNOOP bit is already cleared in the device command register, the device driver must ignore the event and simply return K_ENOTIMP. Otherwise, the device driver must clear this bit (within the device command register) and return K_OK. The iteration is interrupted and the bus driver returns from the vga_palette_snoop_disable routine, once an event handler returns K_OK. This means that a device snooping write transactions to the VGA palette registers was found and has been switched to claiming mode. Note that it will not be the device which issued vga_palette_snoop_disable because the PCI_CMD_PALETTE_SNOOP bit is cleared in its command register. If the iteration is performed and nobody returns K_OK, the PCI bus driver invokes vga_palette_snoop_disable on its parent PCI bus (if any) and then returns from the vga_palette_snoop_disable routine.
The alias column specifies the alias name which should be used by a PCI bus or device driver referencing the property name. The name column specifies the property name ASCII string. The value column specifies the type of property value. The bus column specifies properties specific to the PCI bus node. The dev column specifies properties specific to the PCI device node.
The m symbol in the bus/dev columns flags mandatory properties. The o symbol in the bus/dev columns flags optional properties. The - symbol in the bus/dev columns flags properties which are not applied to a given node type.
Note that if a node represents a PCI-to-PCI bridge device, both bus and dev properties are applied to the node. In other words, this type of node is simultaneously a PCI device node on the primary PCI bus and a PCI bus node on the secondary PCI bus.
Alias | Name | Value | Bus | Dev |
---|---|---|---|---|
PCI_PROP_INTR | "intr" | PciPropIntr[] | - | o |
PCI_PROP_IO_REGS | "io-regs" | PciPropIoRegs[] | - | o |
PCI_PROP_MEM_RGN | "mem-rgn" | PciPropMemRgn[] | - | o |
PCI64_PROP_MEM_RGN | "mem64-rgn" | Pci64PropMemRgn[] | - | o |
PCI_PROP_DMA_BURST | "dma-burst" | PciPropDmaBurst | m | - |
PCI_PROP_DMA_MIN_SIZE | "dma-min-size" | PciPropDmaMinSize | m | - |
PCI_PROP_DEV_ID | "dev-id" | PciPropDevId | o | m |
PCI_PROP_VEND_ID | "vend-id" | PciPropVendId | o | m |
PCI_PROP_BUS_NUM | "bus-num" | PciPropBusNum | m | - |
PCI_PROP_SUB_BUS_NUM | "sub-bus-num" | PciPropSubBusNum | o | - |
PCI_PROP_DEV_NUM | "dev-num" | PciPropDevNum | - | m |
PCI_PROP_FUNC_NUM | "func-num" | PciPropFuncNum | - | m |
PCI_PROP_BYTE_ORDER | "byte-order" | PciPropByteOrder | m | - |
PCI_PROP_CLOCK_FREQ | "clock-freq" | PciPropClockFreq | o | - |
When the value of the property is an array (for example, PciPropIntr[]), the size of the array defines the number of elements in the array. A PCI device driver may then iterate through the array in order to perform an action for each element (that is, to attach an interrupt handler to each device interrupt line). The size of the array is the size of the property value returned by dtreePropValue divided by the size of its element type (that is, sizeof(PciPropIntr).
KnError (*resource_alloc) (PciDevId dev_id, DevProperty prop);
devId is returned by PciBusOps.open.
prop specifies the bus resource being allocated The PciBusOps.resource_alloc service routine allocates a given bus resource and, if the allocation request is satisfied, updates the device node properties in order to add the newly allocated bus resource.
On success, PciBusOps.resource_alloc returns K_OK. On failure, one of the following error codes is returned:
Dynamic resource allocation is not supported by the bus driver.
The property name is unknown.
The property value is invalid.
The bus resource is not available.
The system is out of memory.
When a dynamically allocated bus resource is no longer being used, a PCI device driver may release it by calling the PciBusOps.resource_free service routine.
void (*resource_free) (PciDevId dev_id, DevProperty prop);
devId is returned by PciBusOps.open. prop specifies the bus resource being released. The PciBusOps.resource_free service routine releases a given bus resource and updates the device node properties in order to remove the bus resource being released. The following bus resource properties may be dynamically allocated and released:
PCI_PROP_INTR
PCI_PROP_IO_REGS
PCI_PROP_MEM_RGN
PCI_PROP_BUS_NUM
PCI64_PROP_MEM_RGN
PCI_PROP_SUB_BUS_NUM
Note that the PciBusOps.resource_alloc and PciBusOps.resource_free routines should not be used by a simple device driver. This type of driver should assume that all needed resources have already been allocated and specified as properties in the device node by the bus driver. The driver should only find the property and call an appropriate service routine to pass a pointer to the property value.
It is not always possible to determine all needed resources prior to device initialization. A typical case is a bus-to-bus bridge driver which will only discover devices residing on the secondary bus after the bus bridge hardware has been initialized. In this case, when drv_init is called, the bus-to-bus bridge node will only contain resources needed for the bus-to-bus bridge device itself (for example, internal bus-to-bus bridge registers). Once devices residing on the secondary bus are discovered, the bus-to-bus bridge driver would request additional primary bus resources in order to satisfy the resource requirements for these devices.
Another example is a bus which supports hot-pluggable devices. On this type of bus, the primary bus resources allocated by the hot-pluggable bus driver depend on devices currently plugged into the secondary bus. Usually, the resource requirements are changed when a hot-plug insertion/removal occurs. Note that dynamic resource allocation might also be used by a device driver which implements lazy resource allocation. In this type of driver, the bus resource allocation could be performed at open time using PciBusOps.resource_alloc. The dynamically allocated resources could then be released at close time using PciBusOps.resource_free.
The following table specifies the contexts in which a caller is allowed to invoke each service:
Services | Base level | DKI thread | Interrupt | Blocking |
---|---|---|---|---|
PciBusOps.open | - | + | - | + |
PciBusOps.close | - | + | - | + |
PciBusOps.intr_attach | - | + | - | + |
PciBusOps.intr_detach | - | + | - | + |
PciBusOps.intr_vector | - | + | - | + |
PciBusOps.io_map | - | + | - | + |
PciBusOps.io_unmap | - | + | - | + |
PciBusOps.mem_map | - | + | - | + |
PciBusOps.mem_unmap | - | + | - | + |
PciBusOps.mem64_map | - | + | - | + |
PciBusOps.mem64_unmap | - | + | - | + |
PciBusOps.dma_alloc | - | + | - | + |
PciBusOps.dma_free | - | + | - | + |
PciBusOps.dma64_alloc | - | + | - | + |
PciBusOps.dma64_free | - | + | - | + |
PciBusOps.conf_map | - | + | - | + |
PciBusOps.conf_unmap | - | + | - | + |
PciBusOps.vga_palette_snoop_enable | + | + | - | - |
PciBusOps.vga_palette_snoop_disable | + | + | - | - |
PciBusOps.control_set | + | + | - | - |
PciBusOps.control_clr | + | + | - | - |
PciBusOps.resource_alloc | - | + | - | + |
PciBusOps.resource_free | - | + | - | + |
PciIoOps.store_xx | + | + | + | - |
PciIoOps.read_xx | + | + | + | - |
PciIoOps.write_xx | + | + | + | - |
PciDmaOps.phys_addr | + | + | + | - |
PciDmaOps.virt_addr | + | + | + | - |
PciDmaOps.read_sync | + | + | + | - |
PciDmaOps.write_sync | + | + | + | - |
PciIntrOps.mask | + | + | + | - |
PciIntrOps.unmask | + | + | + | - |
PciIntrOps.enable | - | - | + | - |
PciIntrOps.disable | - | - | + | - |
See attributes(5) for descriptions of the following attributes:
ATTRIBUTE TYPE | ATTRIBUTE VALUE |
---|---|
Interface Stability | Evolving |
dtreeNodeRoot(9DKI), svDriverRegister(9DKI), svMemAlloc(9DKI), svPhysAlloc(9DKI), svPhysMap(9DKI), svDkiThreadCall(9DKI), svTimeoutSet(9DKI), usecBusyWait(9DKI), DISABLE_PREEMPT(9DKI)
NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | PROPERTIES | ALLOWED CALLING CONTEXTS | ATTRIBUTES | SEE ALSO