/* Copyright (C) 2005,2006 David Decotigny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _SOS_BLKDEV_H_ #define _SOS_BLKDEV_H_ /** * @file blkdev.h * * Interface between the VFS and the "block" devices (real devices and * their partitions). The following functions provide the mechanisms * to bind the "block device" nodes (@see mknod) to their device * driver. * * The "blkdev" layer is to be perceived as a FS-agnostic layer both * below and on top of the FS that binds the special "block device" * nodes to a set of system-wide block_read/block_write functions and * which supports the cache of blocks for the device. * * The differences between the char device and the block device layers are: * - A character device is byte-stream-oriented: one can easily fetch * the data character by character. Theoretically, this stream has * no limitation in size: it has a beginning but not necessarly an * end. One can not necessarly rewind the stream, that is seek * anywhere in it (as for a stream of audio data coming from a * soundcard for example). But for some character devices, it is * possible, though (it is possible to seek anywhere in /dev/zero * for example). For some other devices, it is possible to seek only * in some authorized regions (as for /dev/kmem for example: only * the mapped kernel regions are available). * - A block device is block-oriented: one can easily fetch fixed-size * blocks of data anywhere inside the device, possibly in a random * order. The capacity of the device is limited: there is a defined * number of contiguous blocks reachable in the device, one cannot * seek beyond its limits. * - A block device is well suited to a cache layer: it is possible to * cache the most used blocks inside memory to speed up the accesses * to data. It doesn't make sense to do so on stream-oriented devices * like character devices. * - A character device is limited to the interaction between * user-mode programs and the devices themselves. The kernel only acts * as an intermediary and does not take care of the operations * carried out. * - A block device is used as the underlying data support for a * filesystem. As a result, it must be managed by the kernel itself * since the FS code resides inside the kernel. It must offer an API * compatible with kernel-space interaction. * * A partition has the same properties as a disk: both are block * devices. The main difference is that a disk really "owns" its * operations (ie the read/write/ioctl operations are defined by him) * and its block cache, whereas a partition shares them with its * parent disk/partition. The partitions may be nested: a disk can * have partitions, which can have sub-partitions, which can have * sub-sub-partittions, etc. As a consequence, a parent disk/partition * cannot be unregistered as long as it owns any child partitions. The * only constraint being that the partitions must fit completely * inside their parent partition/disk. However, no check is made * regarding partitions overlapping one another... * * This implementation is based on two subsystems: * - the block cache to accelerate accesses to the hardware by caching * the most frequently used blocks in main memory * - the page cache to guarantee consistency between read/write * accesses and MMU access of mapped pages (mmap API). This is a * simple dictionary of the device's mapped pages: offset -> mapped * page. The read/write vs MMU consistency is achieved in a simple * manner: before each read/write accesses, we first try to use the * page cache, and eventually the block cache if the data is not * mapped by anybody. */ /* Forward declaration */ struct sos_blockdev_instance; #include /** * The fundamental callbacks for a real block device (ie not a * partition). */ struct sos_blockdev_operations { /** @note MANDATORY */ sos_ret_t (*read_block)(void * blockdev_instance_custom_data, sos_vaddr_t dest_buf /* Kernel address */, sos_luoffset_t block_offset); /** @note Optional (may be NULL) */ sos_ret_t (*write_block)(void * blockdev_instance_custom_data, sos_vaddr_t src_buf /* Kernel address */, sos_luoffset_t block_offset); /** * @note Optional (may be NULL) * @note Also called when an ioctl is made to a partition */ sos_ret_t (*ioctl)(void * blockdev_instance_custom_data, int req_id, sos_ui32_t req_arg /* Usually: sos_uaddr_t */); }; /* * Functions restricted to block device driver code */ sos_ret_t sos_blockdev_subsystem_setup(void); /** * Contrary to Character devices, block devices are registered * individually: any single registration corresponds to a driver for a * single device instance */ sos_ret_t sos_blockdev_register_disk (sos_ui32_t device_class, sos_ui32_t device_instance, sos_size_t block_size, sos_lcount_t number_of_blocks, sos_count_t cache_size_in_blocks, struct sos_blockdev_operations * blockdev_ops, void * blockdev_instance_custom_data); /** * @param index_of_first_block is the index of the first block * relative to parent_bd, NOT relative to the top-most disk block * device */ sos_ret_t sos_blockdev_register_partition(sos_ui32_t device_class, sos_ui32_t device_instance, struct sos_blockdev_instance * parent_bd, sos_luoffset_t index_of_first_block, sos_lcount_t number_of_blocks, void * blockdev_instance_custom_data); sos_ret_t sos_blockdev_unregister_device (sos_ui32_t device_class, sos_ui32_t device_instance); /** * Flush all caches of all devices to disk */ sos_ret_t sos_blockdev_sync_all_devices(void); /** * Increments the instance's reference counter: this will make any * blockdev_unregister return BUSY. As a consequence, the following * operations on blockdev instances should be as fast as possible and * call release_instance as early as possible */ struct sos_blockdev_instance * sos_blockdev_ref_instance(sos_ui32_t device_class, sos_ui32_t device_instance); sos_ret_t sos_blockdev_release_instance(struct sos_blockdev_instance * blockdev); /** Read data from disk and use it directly in the kernel. Mostly used by the partition drivers to identify the partitions of a disk and register them */ sos_ret_t sos_blockdev_kernel_read(struct sos_blockdev_instance * blockdev, sos_luoffset_t offset, sos_vaddr_t dest_buf, sos_size_t * /* in/out */len); /** * Write data to disk directly from the kernel. Mostly used by the * partition drivers to modify the partitions of a disk and register * them. * * @note The operation is normally NOT needed (fdisk is a userspace * program) and is NOT synchronous */ sos_ret_t sos_blockdev_kernel_write(struct sos_blockdev_instance * blockdev, sos_luoffset_t offset, sos_vaddr_t src_buf, sos_size_t * /* in/out */len); /** * Flush the modified blocks back to hardware (both block and page * cache are flusehd) */ sos_ret_t sos_blockdev_sync(struct sos_blockdev_instance * blockdev); /* * Callbacks and functions restricted to fs.c internals */ /** * Update the FS node ops_blockdev callbacks after an FS * allocate_new_node or fetch_node_from_disk, in order to point to * the block layer API functions */ sos_ret_t sos_blockdev_helper_ref_new_fsnode(struct sos_fs_node * this); sos_ret_t sos_blockdev_helper_release_fsnode(struct sos_fs_node * this); sos_ret_t sos_blockdev_helper_sync_fsnode(struct sos_fs_node * this); #endif