raw.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:8k
- /*
- * 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);
- static struct file_operations raw_fops = {
- read: raw_read,
- write: raw_write,
- open: raw_open,
- release: raw_release,
- };
- 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;
- }
- /*
- * 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;
- 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 ((*offp & sector_mask) || (size & sector_mask))
- goto out_free;
- err = 0;
- if (size)
- err = -ENXIO;
- if ((*offp >> 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 = *offp >> 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 += transferred;
- err = transferred;
- }
- out_free:
- if (!new_iobuf)
- clear_bit(0, &filp->f_iobuf_lock);
- else
- free_kiovec(1, &iobuf);
- out:
- return err;
- }