NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | ATTRIBUTES | SEE ALSO
#include <ddi/ata/ata.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 employment of ATA device drivers.
The ATA bus driver offers an API for ATA device drivers, like disk, cdrom and scanner. This ATA bus API is an abstraction of the low-level ATA bus services and covers the following ATA functional services:
ATA command execution.
ATAPI packet command execution.
The data transfer (if any) can be made in Programmed I/O (PIO), Multiword DMA and Ultra DMA mode.
Like all other drivers, the ATA bus driver's drv_probe(), drv_bind() and drv_init() routines are called at bus probing, binding and initialization phases respectively, by its parent bus. In the drv_init() routine, the ATA bus driver will scan the ATA bus to probe for connected devices (ATA or ATAPI). For each probed device found, the ATA bus driver will create a device node in the device tree, populate the node with standard ATA properties, and bind, if possible, an ATA device driver to the node. To achieve this, the ATA bus driver will scan through the microkernel driver registry looking for an ATA device driver to claim the corresponding ATA device class. If such a device driver is found, the ATA bus driver will launch the corresponding drv_init() routine directly. The drv_probe() and drv_bind() routines will NOT be called as ATA/ATAPI devices are already probed, and ATA device drivers are already bound. The ATA bus driver can be considered as an init only driver.
As mentioned, an ATA 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 the required ATA device type in its registry entry. For example, an ATA disk driver must use the ata-disk class, while an ATA cdrom driver will use the ata-cdrom class. It must also specify the lowest version number of the ATA bus interface required to run correctly. Following is an example of a registry entry for an ATA disk driver:
static DrvRegEntry ataDiskDrv = { ATA_DISK_DRV_NAME, "ATA Disk device driver [#ident \"@(#)atadisk.c 1.2 00/03/27 SMI\"]", ATA_DISK_CLASS, /* device class */ ATA_VERSION_INITIAL, /* required bus version */ NULL, /* probe method */ NULL, /* bind method */ drv_init, /* init method */ drv_unload /* unload method */ };
This registration allows the ATA bus driver to call back the disk device driver's drv_init() routine at the bus initialization phase. Please note that the drv_probe() and drv_bind() routines are set to NULL as they are not called by the ATA bus driver.
Within its drv_init() routine, the ATA device driver may establish a connection with the parent ATA bus driver, as described below.
Once this connection is established, the ATA device driver may use services provided by the ATA bus driver. Note that a connection to the bus driver can be established only within the driver's drv_init() routine.
If the drv_init() routine is defined, the ATA 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 Context", below, to check the list of ATA 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 ATA device node, created by the ATA bus driver. This node identifies the ATA probed device in the device tree.
The ATA bus operations structure, which defines the ATA bus API and its version.
The ATA bus identifier. This opaque value is used to pass back when calling the AtaBusOps.open operation to connect the device driver to the ATA bus.
From this point, AtaBusOps.open must be the first call issued by the ATA device driver to the ATA bus driver:
KnError (*open) (AtaId busId, DevNode devNode, AtaEventHandler eventHandler, void* cookie, AtaDevId* devId);
This establishes a connection to the bus identified by busId. The connection is identified by the devId value, returned in the last argument. This devId identifier is an argument for many other services defined in the AtaBusOps structure. See Allowed Calling Contexts below.
busId and devNode are given by the ATA bus driver as parameters to the driver's drv_init() routine. eventHandler is a handler in the device driver which is invoked by the ATA bus driver when an ATA 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 the first argument. This event handler takes two additional parameters, an event type specific argument (which is always NULL for the ATA 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 device 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 svDeviceEvent()) that the device is going to be shut down and should then return from the event handler. Once the last driver client has released the device entry, 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 by invoking AtaBusOps.close(). Note that the ATA_DEV_SHUTDOWN event may be used by a bus driver to confiscate, or re-allocate bus resources. In this case, the ATA bus driver will re-invoke the device driver's drv_init() routine 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 those for ATA_DEV_SHUTDOWN except that the device hardware must not be accessed by the driver.
Notifies a device driver that the last command executed on this device has failed, and the device has been reset. This is normally due to a badly formatted command, or a hardware fault.
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.
On success, AtaBusOps.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 AtaBusOps structure. Some of these services also return an Ops structure defining new services on a new object instance, for example a DMA memory region. Others just perform a simple service for the device driver, without giving access to a subpart of the API.
On failure, AtaBusOps.open returns an error code as follows:
A connection is already open for the given device node. For example, the device node can be opened only once.
The system is out of memory.
For any other reasons. For example, if the device node is invalid.
To release the connection with the ATA bus, the driver must call the AtaBusOps.close() service.
void (*close) (AtaDevId devId);
After being disconnected from the ATA bus, the device driver can no longer use any of the ATA bus API services.
Once a connection is established, the ATA device driver can execute ATA commands. The ATA protocol defines a set of commands which can be executed by an ATA/ATAPI device. The ATA device driver must use the ATA bus API services to submit commands to be executed on the device.
All ATA command executions are asynchronous. This means that when the device driver sends an ATA command, the service routine returns immediately without waiting for command completion. When the command is finished the device driver will be notified by the ATA bus driver.
Once a command has been issued, the device driver must not send another command but must wait for the command completion notification. Some of these commands involve data transfer between the device and the main memory. This data transfer can be executed in either Programmed I/O (PIO) or DMA mode:
In PIO mode, the driver reads/writes data from/to the device. To achieve this, the I/O service routines provided by its parent bus are used by the ATA bus driver.
In DMA mode, the driver programs the embedded DMA engine to transfer data from/to the device. Once the transfer has started, the cpu is free for other tasks. To use the DMA mode, the device driver must first allocate a memory region suitable for DMA transfer, and use the operations provided by the bus driver on this region.
KnError (*ata_cmd_send) (AtaDevId devId, AtaCmd* command, AtaDoneHandler handler, void* cookie);
This routine is used by the device driver to execute an ATA command to an ATA device. The data transfer, if any, will be done using PIO mode. The devId parameter specifies the opened device. The handler parameter is a pointer to a call-back routine which is executed when the command is finished. The cookie parameter is passed to this handler as its first argument. The second parameter of the handler is returned by the ATA bus driver and contains a pointer to an AtaStatus, containing the exit command and error status. The command parameter describes the command to be executed, in this case ataCmd:
typedef struct ataCmd { uint8_f features; uint8_f sectorCount; uint8_f sectorNum; uint8_f cylLow; uint8_f cylHigh; uint8_f devHead; uint8_f command; union { void* buffer; AtaSize offset; } data; uint32_f timeout; } AtaCmd;
The features, sectorCount, sectorNum, cylLow, cylHigh, devHead and command fields will be loaded by the ATA bus driver into corresponding low level controller registers. For PIO data transfer, the data.buffer field specifies the data memory address. For DMA data transfer, the data.offset field is used to specify the offset in the memory region previously allocated. The timeout field specifies the maximum time, in milliseconds, allowed for this command. If the command does not end before this timeout value the device is reset and the ATA_DEV_RESET event is notified to the device driver.
Note that if the timeout command expires and the device driver is notified of an ATA_DEV_RESET event, the handler is not called.
On success K_OK is returned. Otherwise AtaBusOps.ata_cmd_send returns an error code as follows:
The system is out of memory.
The device id is invalid.
For any other errors.
KnError (*atapi_cmd_send) (AtaDevId devId, AtapiCmd* command, AtapiDoneHandler handler, void* cookie);
The AtaBusOps.atapi_cmd_send service routine must be used on an ATAPI device. devId is returned by AtaBusOps.open. The command parameter describes the command to be executed.
typedef struct atapiCmd { uint8_f cmd[16]; uint16_f count; AtapiDir direction; union { void* buffer; AtaSize offset; } data; Bool overlap; uint8_f tag; uint32_f timeout; } AtapiCmd;
The cmd field contains 16 bytes describing the packet command to be executed. All unused bytes must be set to 0. The count field indicates the size of the transfer, if any. This data transfer will be done in PIO mode. The direction field indicates which way the transfer will go (device to memory, memory to device). The buffer field specifies the data memory address. For DMA data transfer, the data.offset field is used to specify the offset in the memory region previously allocated. The overlap field indicates whether the command is to be overlapped by another. In this case, the tag field is used to specify the tag associated to that request. The timeout field has the same meaning as in AtaBusOps.ata_cmd_send.
When the ATA bus driver receives an atapi_cmd_send command, it will initiate an ATA PACKET command by itself. Then, when the device is ready, the bus driver will give the 16 bytes of the Packet command to the device.
The handler parameter is a pointer to a call-back routine in the device driver which is executed when the command is finished. The cookie parameter is passed to this handler as its first argument. The handler returns in the second parameter a pointer to an AtapiStatus structure which provides to the device driver a complete exit command status. Note that if the command timeout expires and the ATA_DEV_RESET event is notified to the device driver, the handler is not called. In case of error (CHECK CONDITION status), the ATA bus driver will try to recover by itself and generate an ATAPI REQUEST SENSE to the device. The returned sense data is returned in the sense field of the AtapiStatus structure.
On success K_OK is returned. Otherwise AtaBusOps.atapi_cmd_send returns an error code as follows:
The system is out of memory.
The device id is invalid.
For any other errors.
void (*mask) (AtaDevId devId); void (*unmask) (AtaDevId devId);
The AtaBusOps.mask and AtaBusOps.unmask service routines are used to prevent and allow the execution of the completed handler. The device id, devId, is returned by AtaBusOps.open. The AtaBusOps.mask / AtaBusOps.unmask pair may be used at either base or interrupt level. Note that the AtaBusOps.mask / AtaBusOps.unmask pairs must not be nested.
KnError (*dma_alloc) (AtaDevId devId, AtaSize dmaSize, AtaDmaAttr dmaAttr, AtaDmaOps** dmaOps, AtaDmaId* dmaId);
The AtaBusOps.dma_alloc service routine is used to allocate a memory region suitable for DMA transfer. devId is returned by AtaBusOps.open. dmaSize specifies the size of the region to allocate in bytes. The dmaAttr parameter specifies the DMA transfer type. A combination of the following flags is allowed:
The region is used for DMA read transfer (device to memory).
The region is used for DMA write transfer (memory to device).
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 the first argument to further calls of the AtaDmaOps services. The AtaDmaOps service routines are defined as follows:
typedef struct AtaDmaOps { void* (*virt_addr) (AtaDmaId dmaId); KnError (*ata_cmd_send) (AtaDmaId dmaId, AtaCmd* command, AtaDoneHandler handler, void* cookie); KnError (*atapi_cmd_send) (AtaDmaId dmaId, AtapiCmd* command, AtapiDoneHandler handler, void* cookie)); } AtaDmaOps;
On success AtaBusOps.dma_alloc returns K_OK. Otherwise, AtaBusOps.dma_alloc returns an error code as follows:
A size of zero was specified.
Invalid or unsupported memory attributes.
The system is out of memory.
Not enough virtual address space to map.
The device is not DMA capable.
void (*dma_free) (AtaDmaId dmaId);
When a driver no longer needs an allocated DMA region, or before closing the connection to the ATA bus, it should release the DMA region. The AtaBusOps.dma_free is used to release a DMA region previously allocated with AtaBusOps.dma_alloc.
void* (*virt_addr) (AtaDmaId dma_id);
The AtaDmaOps.virt_addr routine returns the virtual start address of a given DMA region (specified by dma_id). The device driver uses this address to access the DMA region using CPU instructions.
The AtaDmaOps.ata_cmd_send and AtaDmaOps.atapi_cmd_send service routines behave as previously described, except that the data transfer will take place in DMA mode instead of PIO. Please see above for argument description and returned error codes.
In the table below, the Alias column specifies the alias name which should be used by an ATA device driver referencing the property name. The Name column specifies the property name ASCII string. The Value column specifies the type of property value. In the m/o column, m flags mandatory properties and o flags optional properties.
Alias | Name | Value | m/o |
---|---|---|---|
ATA_PROP_DEV_CLASS | "dev-class" | char* | o |
ATA_PROP_DEV_NUM | "dev-num" | AtaPropDevNum | o |
ATA_PROP_DEV_MODE | "dev-mode" | AtaMode | o |
ATA_PROP_DEV_CURRENT_MODE | "dev-curr-mode" | AtaMode | o |
ATA_PROP_DEV_IDENT | "dev-ident" | struct ataIdentData | o |
The ATA_PROP_DEV_CLASS property is dynamically assigned to an ATA/ATAPI device node once it has been probed by the ATA bus driver. It contains the device class (ATA_DISK_CLASS, ATAPI_CDROM_CLASS,...).
The ATA_PROP_DEV_NUM property is dynamically assigned to an ATA/ATAPI device node once it has been probed by the ATA bus driver. It is an 8 bit quantity containing the port number (0/1) in the high nibble, and the device number (0/1) in the low one.
The ATA_PROP_DEV_MODE property contains the desired ATA transfer mode. The ATA bus driver implementation will select which ATA transfer mode number to use by default if this property is not present (see below).
The ATA_PROP_DEV_CURRENT_MODE property contains the selected ATA transfer mode. This property is dynamically added to the device node by the ATA bus driver at initialization time, depending on the optional ATA_PROP_DEV_MODE property and the device's supported modes.
The ATA_PROP_DEV_IDENT property is dynamically assigned to the device node once it has been probed by the ATA bus driver. At initialization time, the ATA bus driver probes the bus for devices and, for each detected device, executes the IDENTIFY DEVICE command, for ATA devices, and IDENTIFY PACKET DEVICE, for ATAPI devices. The output of these commands is given to the device driver in this property.
Services | Base level | DKI thread | Interrupt | Blocking |
---|---|---|---|---|
AtaBusOps.open | - | + | - | + |
AtaBusOps.close | - | + | - | + |
AtaBusOps.ata_cmd_send | + | + | + | - |
AtaBusOps.atapi_cmd_send | + | + | + | - |
AtaBusOps.mask | + | + | + | - |
AtaBusOps.unmask | + | + | + | - |
AtaBusOps.dma_alloc | - | + | - | + |
AtaBusOps.dma_free | - | + | - | + |
AtaDmaOps.virt_addr | + | + | + | - |
AtaDmaOps.ata_cmd_send | + | + | + | - |
AtaDmaOps.atapi_cmd_send | + | + | + | - |
See attributes(5) for descriptions of the following attributes:
ATTRIBUTE TYPE | ATTRIBUTE VALUE |
---|---|
Interface Stability | Evolving |
svDriverRegister(9DKI), svMemAlloc(9DKI), svPhysAlloc(9DKI), svPhysMap(9DKI), svDkiThreadCall(9DKI), svTimeoutSet(9DKI)
NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | ATTRIBUTES | SEE ALSO