blob: bbb0b67948e1305fab9017a475583a1ff5525414 [file] [log] [blame]
/*
* linux/drivers/char/raw.c
*
* Front-end raw character devices. These can be bound to any block
* devices to provide genuine Unix raw character device semantics.
*
* We reserve minor number 0 for a control interface. ioctl()s on this
* device are used to bind the other minor numbers to block devices.
*/
#include <linux/fs.h>
#include <linux/iobuf.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/raw.h>
#include <linux/capability.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#define dprintk(x...)
typedef struct raw_device_data_s {
struct block_device *binding;
int inuse, sector_size, sector_bits;
struct semaphore mutex;
} raw_device_data_t;
static raw_device_data_t raw_devices[256];
static ssize_t rw_raw_dev(int rw, struct file *, char *, size_t, loff_t *);
ssize_t raw_read(struct file *, char *, size_t, loff_t *);
ssize_t raw_write(struct file *, const char *, size_t, loff_t *);
int raw_open(struct inode *, struct file *);
int raw_release(struct inode *, struct file *);
int raw_ctl_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
int raw_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
static struct file_operations raw_fops = {
read: raw_read,
write: raw_write,
open: raw_open,
release: raw_release,
ioctl: raw_ioctl,
};
static struct file_operations raw_ctl_fops = {
ioctl: raw_ctl_ioctl,
open: raw_open,
};
static int __init raw_init(void)
{
int i;
register_chrdev(RAW_MAJOR, "raw", &raw_fops);
for (i = 0; i < 256; i++)
init_MUTEX(&raw_devices[i].mutex);
return 0;
}
__initcall(raw_init);
/*
* Open/close code for raw IO.
*/
int raw_open(struct inode *inode, struct file *filp)
{
int minor;
struct block_device * bdev;
kdev_t rdev; /* it should eventually go away */
int err;
int sector_size;
int sector_bits;
minor = MINOR(inode->i_rdev);
/*
* Is it the control device?
*/
if (minor == 0) {
filp->f_op = &raw_ctl_fops;
return 0;
}
if (!filp->f_iobuf) {
err = alloc_kiovec(1, &filp->f_iobuf);
if (err)
return err;
}
down(&raw_devices[minor].mutex);
/*
* No, it is a normal raw device. All we need to do on open is
* to check that the device is bound, and force the underlying
* block device to a sector-size blocksize.
*/
bdev = raw_devices[minor].binding;
err = -ENODEV;
if (!bdev)
goto out;
atomic_inc(&bdev->bd_count);
rdev = to_kdev_t(bdev->bd_dev);
err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW);
if (err)
goto out;
/*
* Don't change the blocksize if we already have users using
* this device
*/
if (raw_devices[minor].inuse++)
goto out;
/*
* Don't interfere with mounted devices: we cannot safely set
* the blocksize on a device which is already mounted.
*/
sector_size = 512;
if (is_mounted(rdev)) {
if (blksize_size[MAJOR(rdev)])
sector_size = blksize_size[MAJOR(rdev)][MINOR(rdev)];
} else {
if (hardsect_size[MAJOR(rdev)])
sector_size = hardsect_size[MAJOR(rdev)][MINOR(rdev)];
}
set_blocksize(rdev, sector_size);
raw_devices[minor].sector_size = sector_size;
for (sector_bits = 0; !(sector_size & 1); )
sector_size>>=1, sector_bits++;
raw_devices[minor].sector_bits = sector_bits;
out:
up(&raw_devices[minor].mutex);
return err;
}
int raw_release(struct inode *inode, struct file *filp)
{
int minor;
struct block_device *bdev;
minor = MINOR(inode->i_rdev);
down(&raw_devices[minor].mutex);
bdev = raw_devices[minor].binding;
raw_devices[minor].inuse--;
up(&raw_devices[minor].mutex);
blkdev_put(bdev, BDEV_RAW);
return 0;
}
/* Forward ioctls to the underlying block device. */
int raw_ioctl(struct inode *inode,
struct file *flip,
unsigned int command,
unsigned long arg)
{
int minor = minor(inode->i_rdev), err;
struct block_device *b;
if (minor < 1 || minor > 255)
return -ENODEV;
b = raw_devices[minor].binding;
err = -EINVAL;
if (b && b->bd_inode && b->bd_op && b->bd_op->ioctl) {
err = b->bd_op->ioctl(b->bd_inode, NULL, command, arg);
}
return err;
}
/*
* Deal with ioctls against the raw-device control interface, to bind
* and unbind other raw devices.
*/
int raw_ctl_ioctl(struct inode *inode,
struct file *flip,
unsigned int command,
unsigned long arg)
{
struct raw_config_request rq;
int err = 0;
int minor;
switch (command) {
case RAW_SETBIND:
case RAW_GETBIND:
/* First, find out which raw minor we want */
err = copy_from_user(&rq, (void *) arg, sizeof(rq));
if (err)
break;
minor = rq.raw_minor;
if (minor <= 0 || minor > MINORMASK) {
err = -EINVAL;
break;
}
if (command == RAW_SETBIND) {
/*
* This is like making block devices, so demand the
* same capability
*/
if (!capable(CAP_SYS_ADMIN)) {
err = -EPERM;
break;
}
/*
* For now, we don't need to check that the underlying
* block device is present or not: we can do that when
* the raw device is opened. Just check that the
* major/minor numbers make sense.
*/
if ((rq.block_major == NODEV &&
rq.block_minor != NODEV) ||
rq.block_major > MAX_BLKDEV ||
rq.block_minor > MINORMASK) {
err = -EINVAL;
break;
}
down(&raw_devices[minor].mutex);
if (raw_devices[minor].inuse) {
up(&raw_devices[minor].mutex);
err = -EBUSY;
break;
}
if (raw_devices[minor].binding)
bdput(raw_devices[minor].binding);
raw_devices[minor].binding =
bdget(kdev_t_to_nr(MKDEV(rq.block_major, rq.block_minor)));
up(&raw_devices[minor].mutex);
} else {
struct block_device *bdev;
kdev_t dev;
bdev = raw_devices[minor].binding;
if (bdev) {
dev = to_kdev_t(bdev->bd_dev);
rq.block_major = MAJOR(dev);
rq.block_minor = MINOR(dev);
} else {
rq.block_major = rq.block_minor = 0;
}
err = copy_to_user((void *) arg, &rq, sizeof(rq));
}
break;
default:
err = -EINVAL;
}
return err;
}
ssize_t raw_read(struct file *filp, char * buf,
size_t size, loff_t *offp)
{
return rw_raw_dev(READ, filp, buf, size, offp);
}
ssize_t raw_write(struct file *filp, const char *buf,
size_t size, loff_t *offp)
{
return rw_raw_dev(WRITE, filp, (char *) buf, size, offp);
}
#define SECTOR_BITS 9
#define SECTOR_SIZE (1U << SECTOR_BITS)
#define SECTOR_MASK (SECTOR_SIZE - 1)
ssize_t rw_raw_dev(int rw, struct file *filp, char *buf,
size_t size, loff_t *offp)
{
struct kiobuf * iobuf;
int new_iobuf;
int err = 0;
unsigned long blocknr, blocks;
size_t transferred;
int iosize;
int i;
int minor;
kdev_t dev;
unsigned long limit;
loff_t off = *offp;
int sector_size, sector_bits, sector_mask;
int max_sectors;
/*
* First, a few checks on device size limits
*/
minor = MINOR(filp->f_dentry->d_inode->i_rdev);
new_iobuf = 0;
iobuf = filp->f_iobuf;
if (test_and_set_bit(0, &filp->f_iobuf_lock)) {
/*
* A parallel read/write is using the preallocated iobuf
* so just run slow and allocate a new one.
*/
err = alloc_kiovec(1, &iobuf);
if (err)
goto out;
new_iobuf = 1;
}
dev = to_kdev_t(raw_devices[minor].binding->bd_dev);
sector_size = raw_devices[minor].sector_size;
sector_bits = raw_devices[minor].sector_bits;
sector_mask = sector_size- 1;
max_sectors = KIO_MAX_SECTORS >> (sector_bits - 9);
if (blk_size[MAJOR(dev)])
limit = (((loff_t) blk_size[MAJOR(dev)][MINOR(dev)]) << BLOCK_SIZE_BITS) >> sector_bits;
else
limit = INT_MAX;
dprintk ("rw_raw_dev: dev %d:%d (+%d)\n",
MAJOR(dev), MINOR(dev), limit);
err = -EINVAL;
if ((off & sector_mask) || (size & sector_mask))
goto out_free;
err = 0;
if (size)
err = -ENXIO;
if ((off >> sector_bits) >= limit)
goto out_free;
/*
* Split the IO into KIO_MAX_SECTORS chunks, mapping and
* unmapping the single kiobuf as we go to perform each chunk of
* IO.
*/
transferred = 0;
blocknr = off >> sector_bits;
while (size > 0) {
blocks = size >> sector_bits;
if (blocks > max_sectors)
blocks = max_sectors;
if (blocks > limit - blocknr)
blocks = limit - blocknr;
if (!blocks)
break;
iosize = blocks << sector_bits;
err = map_user_kiobuf(rw, iobuf, (unsigned long) buf, iosize);
if (err)
break;
for (i=0; i < blocks; i++)
iobuf->blocks[i] = blocknr++;
err = brw_kiovec(rw, 1, &iobuf, dev, iobuf->blocks, sector_size);
if (rw == READ && err > 0)
mark_dirty_kiobuf(iobuf, err);
if (err >= 0) {
transferred += err;
size -= err;
buf += err;
}
unmap_kiobuf(iobuf);
if (err != iosize)
break;
}
if (transferred) {
*offp = off + transferred;
err = transferred;
}
out_free:
if (!new_iobuf)
clear_bit(0, &filp->f_iobuf_lock);
else
free_kiovec(1, &iobuf);
out:
return err;
}