= IOC device API = [[PageOutline]] == __A) General principles__ == This device allows the kernel to access various external mass storage peripherals such as a magnetic hard disk or a SD card, that can store blocks of data in a linear array of sectors indexed by a simple ''lba'' (logic block address). The block size is supposed to be 512 bytes. It supports a first ''user'' API, used by the user-level system calls, implementing four operation types : the '''IOC_READ''', '''IOC_WRITE''', '''IOC_SYNC_READ''', and '''IOC_SYNC_WRITE''' operations move a given number of contiguous blocks between the block device and a kernel memory buffer. This API is detailed in section C below. * The '''asynchronous''' READ and WRITE operations are not directly executed by the client thread. The READ and WRITE requests are registered in the waiting queue rooted in the IOC chdev descriptor. These requests are actually handled by a dedicated server thread running in the cluster containing the chdev descriptor, that call the ''ioc_driver_cmd()'' function. * The '''synchronous''' SYNC_READ and SYNC_WRITE operations does not use the waiting queue, and does not use the server thread. The client thread calls directly the ''ioc_driver_cmd()'' function. Most IOC device implementation have a DMA capability, and the data transfer is not done by the software, but is actually done by the hardware device. The IOC IRQ signaling the transfer completion is routed to the core executing the server thread, and handled by the ''ioc_driver_isr()'' function (ISR stand for Interrupt Service Routine). In the IOC-RDK implementation the IOC block device is actually implemented as physical memory. This IOC-RDK implementation does not use DMA, and does not use an IRQ. The data transfer is directly executed by the ''ioc_driver_cmd()'' software function. To access the various drivers, this FBF device defines a lower-level ''driver'' API, that is detailed in section D below. All IOC device structures and access functions are defined in the [https://www-soc.lip6.fr/trac/almos-mkh/browser/trunk/kernel/devices/dev_ioc.c dev_ioc.c] et [https://www-soc.lip6.fr/trac/almos-mkh/browser/trunk/kernel/devices/dev_ioc.h dev_ioc.h] files. == __B) Initialisation__ == The IOC device '''dev_ioc_init()''' function makes the following initializations : * It selects a core in cluster containing the IOC chdev to execute the IOC server thread. * it links the IOC IRQ to the core executing the IOC server thread. * it initialises the IOC specific fields of the chdev descriptor. * it initialises the implementation specific IOC hardware device, * it initializes the specific software data structures required by a given hardware implementation. It must be called by a local thread. == __C) User API__ == The '''void dev_ioc_move_data()''' blocking function moves contiguous blocks of data between the block device, indexed by the argument, and a kernel buffer, identified by the argument. * The '''cmd_type''' argument defines the operation type. * The '''buffer_xp''' argument is an extended pointer on the kernel buffer. * The '''lba''' argument is the first block index in block device. * The '''count''' argument is the number of blocks to move. The I/O operation is always blocking for the calling thread, but the blocking policy depends on the operation type. === C.1) Asynchronous operations === Almos-mkh uses the '''asynchronous''', READ & WRITE operations, for most data transfers between the file system on the IOC, and the file system cache in memory. The scenario is the following : 1. When a client thread request an I/O operation, the request is registered in the ''ioc_command_t'' structure embedded in the client thread descriptor, and the client thread registers itself in the waiting queue rooted in the IOC chdev. Then the client thread blocks on the THREAD_BLOCKED_IO condition, and deschedules. 1. The DEV server thread attached to the IOC device descriptor handles all commands registered in the IOC device waiting queue. For each pending request, it calls the IOC driver CMD (command) function to ask the hardware device to do the transfer. Then, the server thread blocks on the THREAD_BLOCKED_ISR condition, and deschedules. 1. When the I/O operation completed, the IOC driver ISR (Interrupt Service Routine) signaling completion reactivates the server thread. 1. The server thread reactivates the client thread, and handle the next request in the IOC waiting queue, or reschedules if the queue is empty. Note : According to the scheduler policy, the DEV thread has an higher priority than any user thread, but it is not selected when the associated waiting queue is empty. === C.2) Synchronous operations === Almost-mkh uses the '''synchronous''' SYNC_READ and SYNC_WRITE operations in the kernel_init() function, or to synchronously update the FAT (both the FAT mapper in memory, and the FAT on IOC device), or the directory files on IOC device. These synchronous operations use neither the IOC device waiting queue, nor the DEV server thread, and the IRQ. The client thread does not deschedules : it registers the arguments in the IOC command structure embedded in the client thread descriptor, and calls directly the blocking IOC driver CMD function, == __D) Driver API__ == All IOC drivers must define three functions : * void '''ioc_driver_init( chdev_t *ioc_chdev ) * void '''ioc_driver_cmd'''( xptr_t thread_xp ) * void '''ioc_driver_isr'''( chdev_t * ioc_chdev ) The ioc_driver_cmd()'' function arguments are actually defined in the ''ioc_command_t'' structure embedded in the client thread descriptor. One command contains four informations: - '''type''' : operation type (defined below) - '''count''' : number of contiguous blocks to be moved. - '''buffer_xp''' : extended pointer on kernel buffer. - '''lba''' : logic block address (index of block in hardware device). The four operation types for the IOC driver(s) are the following: - '''IOC_WRITE''' : move blocks from a kernel buffer to the hardware device, with a descheduling policy. - '''IOC_READ''' : move blocks from the hardware device to a kernel buffer, with a descheduling policy. - '''IOC_SYNC_WRITE''' : move blocks from a kernel buffer to the hardware device, without descheduling. - '''IOC_SYNC_READ''' : move blocks from the hardware device to a kernel buffer, without descheduling. For asynchronous operations the ''ioc_driver_cmd()'' function is called by the server thread. It must block and deschedule after launching the I/O transfer. The I/O operation status is reported in the command by the ISR, and the server thread is re-activated by the ISR. For synchronous operations, the ''ioc_driver_cmd()'' function is called by the client thread. It mask the IOC IRQ, poll the BDV status register until I/O transfer completion, and report status in the command.