* driver due to an interrupt or a timer event is stored in a variable
* of type idetape_tape_t, defined below.
*/
-typedef struct {
- ide_drive_t *drive;
+typedef struct ide_tape_obj {
+ ide_drive_t *drive;
+ ide_driver_t *driver;
+ struct gendisk *disk;
+ struct kref kref;
+
/*
* Since a typical character device operation requires more
* than one packet command, we provide here enough memory
int debug_level;
} idetape_tape_t;
+static DECLARE_MUTEX(idetape_ref_sem);
+
+static struct class *idetape_sysfs_class;
+
+#define to_ide_tape(obj) container_of(obj, struct ide_tape_obj, kref)
+
+#define ide_tape_g(disk) \
+ container_of((disk)->private_data, struct ide_tape_obj, driver)
+
+static struct ide_tape_obj *ide_tape_get(struct gendisk *disk)
+{
+ struct ide_tape_obj *tape = NULL;
+
+ down(&idetape_ref_sem);
+ tape = ide_tape_g(disk);
+ if (tape)
+ kref_get(&tape->kref);
+ up(&idetape_ref_sem);
+ return tape;
+}
+
+static void ide_tape_release(struct kref *);
+
+static void ide_tape_put(struct ide_tape_obj *tape)
+{
+ down(&idetape_ref_sem);
+ kref_put(&tape->kref, ide_tape_release);
+ up(&idetape_ref_sem);
+}
+
/*
* Tape door status
*/
#define IDETAPE_ERROR_FILEMARK 102
#define IDETAPE_ERROR_EOD 103
-/*
- * idetape_chrdev_t provides the link between out character device
- * interface and our block device interface and the corresponding
- * ide_drive_t structure.
- */
-typedef struct {
- ide_drive_t *drive;
-} idetape_chrdev_t;
-
/*
* The following is used to format the general configuration word of
* the ATAPI IDENTIFY DEVICE command.
* The variables below are used for the character device interface.
* Additional state variables are defined in our ide_drive_t structure.
*/
-static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES];
+static struct ide_tape_obj * idetape_devs[MAX_HWIFS * MAX_DRIVES];
+
+#define ide_tape_f(file) ((file)->private_data)
+
+static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i)
+{
+ struct ide_tape_obj *tape = NULL;
+
+ down(&idetape_ref_sem);
+ tape = idetape_devs[i];
+ if (tape)
+ kref_get(&tape->kref);
+ up(&idetape_ref_sem);
+ return tape;
+}
/*
* Function declarations
}
#endif /* IDETAPE_DEBUG_BUGS */
+ rq->rq_disk = tape->disk;
rq->buffer = NULL;
rq->special = (void *)stage->bh;
tape->active_data_request = rq;
*/
static void idetape_queue_pc_head (ide_drive_t *drive, idetape_pc_t *pc,struct request *rq)
{
+ struct ide_tape_obj *tape = drive->driver_data;
+
idetape_init_rq(rq, REQ_IDETAPE_PC1);
rq->buffer = (char *) pc;
+ rq->rq_disk = tape->disk;
(void) ide_do_drive_cmd(drive, rq, ide_preempt);
}
#ifdef CONFIG_BLK_DEV_IDEDMA
/* Begin DMA, if necessary */
if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags))
- hwif->ide_dma_start(drive);
+ hwif->dma_start(drive);
#endif
/* Send the actual packet */
HWIF(drive)->atapi_output_bytes(drive, pc->c, 12);
(void)__ide_dma_off(drive);
}
if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
- dma_ok = !hwif->ide_dma_setup(drive);
+ dma_ok = !hwif->dma_setup(drive);
if (IDE_CONTROL_REG)
hwif->OUTB(drive->ctl, IDE_CONTROL_REG);
if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2))
set_bit(IDETAPE_IGNORE_DSC, &tape->flags);
+ if (drive->post_reset == 1) {
+ set_bit(IDETAPE_IGNORE_DSC, &tape->flags);
+ drive->post_reset = 0;
+ }
+
if (tape->tape_still_time > 100 && tape->tape_still_time < 200)
tape->measure_insert_time = 1;
if (time_after(jiffies, tape->insert_time))
tape->dsc_polling_start = jiffies;
tape->dsc_polling_frequency = tape->best_dsc_rw_frequency;
tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
- } else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
+ } else if (time_after(jiffies, tape->dsc_timeout)) {
printk(KERN_ERR "ide-tape: %s: DSC timeout\n",
tape->name);
if (rq->cmd[0] & REQ_IDETAPE_PC2) {
}
#endif /* IDETAPE_DEBUG_BUGS */
rq->waiting = &wait;
+ rq->end_io = blk_end_sync_rq;
spin_unlock_irq(&tape->spinlock);
wait_for_completion(&wait);
/* The stage and its struct request have been deallocated */
*/
static int __idetape_queue_pc_tail (ide_drive_t *drive, idetape_pc_t *pc)
{
+ struct ide_tape_obj *tape = drive->driver_data;
struct request rq;
idetape_init_rq(&rq, REQ_IDETAPE_PC1);
rq.buffer = (char *) pc;
+ rq.rq_disk = tape->disk;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
} else if (!(tape->sense_key == 2 && tape->asc == 4 &&
(tape->ascq == 1 || tape->ascq == 8)))
return -EIO;
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ / 10);
+ msleep(100);
}
return -EIO;
}
#endif /* IDETAPE_DEBUG_BUGS */
idetape_init_rq(&rq, cmd);
+ rq.rq_disk = tape->disk;
rq.special = (void *)bh;
rq.sector = tape->first_frame_position;
rq.nr_sectors = rq.current_nr_sectors = blocks;
return 0;
}
-/*
- * idetape_pre_reset is called before an ATAPI/ATA software reset.
- */
-static void idetape_pre_reset (ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
- if (tape != NULL)
- set_bit(IDETAPE_IGNORE_DSC, &tape->flags);
-}
-
/*
* idetape_space_over_filemarks is now a bit more complicated than just
* passing the command to the tape since we may have crossed some
static ssize_t idetape_chrdev_read (struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- ide_drive_t *drive = file->private_data;
- idetape_tape_t *tape = drive->driver_data;
+ struct ide_tape_obj *tape = ide_tape_f(file);
+ ide_drive_t *drive = tape->drive;
ssize_t bytes_read,temp, actually_read = 0, rc;
#if IDETAPE_DEBUG_LOG
static ssize_t idetape_chrdev_write (struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- ide_drive_t *drive = file->private_data;
- idetape_tape_t *tape = drive->driver_data;
+ struct ide_tape_obj *tape = ide_tape_f(file);
+ ide_drive_t *drive = tape->drive;
ssize_t retval, actually_written = 0;
/* The drive is write protected. */
*/
static int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- ide_drive_t *drive = file->private_data;
- idetape_tape_t *tape = drive->driver_data;
+ struct ide_tape_obj *tape = ide_tape_f(file);
+ ide_drive_t *drive = tape->drive;
struct mtop mtop;
struct mtget mtget;
struct mtpos mtpos;
idetape_pc_t pc;
int retval;
- nonseekable_open(inode, filp);
+ /*
+ * We really want to do nonseekable_open(inode, filp); here, but some
+ * versions of tar incorrectly call lseek on tapes and bail out if that
+ * fails. So we disallow pread() and pwrite(), but permit lseeks.
+ */
+ filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
+
#if IDETAPE_DEBUG_LOG
printk(KERN_INFO "ide-tape: Reached idetape_chrdev_open\n");
#endif /* IDETAPE_DEBUG_LOG */
if (i >= MAX_HWIFS * MAX_DRIVES)
return -ENXIO;
- drive = idetape_chrdevs[i].drive;
- tape = drive->driver_data;
- filp->private_data = drive;
- if (test_and_set_bit(IDETAPE_BUSY, &tape->flags))
- return -EBUSY;
+ if (!(tape = ide_tape_chrdev_get(i)))
+ return -ENXIO;
+
+ drive = tape->drive;
+
+ filp->private_data = tape;
+
+ if (test_and_set_bit(IDETAPE_BUSY, &tape->flags)) {
+ retval = -EBUSY;
+ goto out_put_tape;
+ }
+
retval = idetape_wait_ready(drive, 60 * HZ);
if (retval) {
clear_bit(IDETAPE_BUSY, &tape->flags);
printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name);
- return retval;
+ goto out_put_tape;
}
idetape_read_position(drive);
if ((filp->f_flags & O_ACCMODE) == O_WRONLY ||
(filp->f_flags & O_ACCMODE) == O_RDWR) {
clear_bit(IDETAPE_BUSY, &tape->flags);
- return -EROFS;
+ retval = -EROFS;
+ goto out_put_tape;
}
}
idetape_restart_speed_control(drive);
tape->restart_speed_control_req = 0;
return 0;
+
+out_put_tape:
+ ide_tape_put(tape);
+ return retval;
}
static void idetape_write_release (ide_drive_t *drive, unsigned int minor)
*/
static int idetape_chrdev_release (struct inode *inode, struct file *filp)
{
- ide_drive_t *drive = filp->private_data;
- idetape_tape_t *tape;
+ struct ide_tape_obj *tape = ide_tape_f(filp);
+ ide_drive_t *drive = tape->drive;
idetape_pc_t pc;
unsigned int minor = iminor(inode);
}
}
clear_bit(IDETAPE_BUSY, &tape->flags);
+ ide_tape_put(tape);
unlock_kernel();
return 0;
}
int stage_size;
struct sysinfo si;
- memset(tape, 0, sizeof (idetape_tape_t));
spin_lock_init(&tape->spinlock);
- drive->driver_data = tape;
- /* An ATAPI device ignores DRDY */
- drive->ready_stat = 0;
drive->dsc_overlap = 1;
#ifdef CONFIG_BLK_DEV_IDEPCI
if (HWIF(drive)->pci_dev != NULL) {
/* Seagate Travan drives do not support DSC overlap. */
if (strstr(drive->id->model, "Seagate STT3401"))
drive->dsc_overlap = 0;
- tape->drive = drive;
tape->minor = minor;
tape->name[0] = 'h';
tape->name[1] = 't';
idetape_add_settings(drive);
}
-static int idetape_cleanup (ide_drive_t *drive)
+static void ide_tape_remove(ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
- int minor = tape->minor;
- unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
- if (test_bit(IDETAPE_BUSY, &tape->flags) || drive->usage ||
- tape->first_stage != NULL || tape->merge_stage_size) {
- spin_unlock_irqrestore(&ide_lock, flags);
- return 1;
- }
- idetape_chrdevs[minor].drive = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
- DRIVER(drive)->busy = 0;
- (void) ide_unregister_subdriver(drive);
+ ide_unregister_subdriver(drive, tape->driver);
+
+ ide_unregister_region(tape->disk);
+
+ ide_tape_put(tape);
+}
+
+static void ide_tape_release(struct kref *kref)
+{
+ struct ide_tape_obj *tape = to_ide_tape(kref);
+ ide_drive_t *drive = tape->drive;
+ struct gendisk *g = tape->disk;
+
+ BUG_ON(tape->first_stage != NULL || tape->merge_stage_size);
+
+ drive->dsc_overlap = 0;
drive->driver_data = NULL;
+ class_device_destroy(idetape_sysfs_class,
+ MKDEV(IDETAPE_MAJOR, tape->minor));
+ class_device_destroy(idetape_sysfs_class,
+ MKDEV(IDETAPE_MAJOR, tape->minor + 128));
devfs_remove("%s/mt", drive->devfs_name);
devfs_remove("%s/mtn", drive->devfs_name);
- devfs_unregister_tape(drive->disk->number);
- kfree (tape);
- drive->disk->fops = ide_fops;
- return 0;
+ devfs_unregister_tape(g->number);
+ idetape_devs[tape->minor] = NULL;
+ g->private_data = NULL;
+ put_disk(g);
+ kfree(tape);
}
#ifdef CONFIG_PROC_FS
}
static ide_proc_entry_t idetape_proc[] = {
+ { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL },
{ "name", S_IFREG|S_IRUGO, proc_idetape_read_name, NULL },
{ NULL, 0, NULL, NULL }
};
#endif
-static int idetape_attach(ide_drive_t *drive);
+static int ide_tape_probe(ide_drive_t *);
-/*
- * IDE subdriver functions, registered with ide.c
- */
static ide_driver_t idetape_driver = {
- .owner = THIS_MODULE,
- .name = "ide-tape",
+ .gen_driver = {
+ .owner = THIS_MODULE,
+ .name = "ide-tape",
+ .bus = &ide_bus_type,
+ },
+ .probe = ide_tape_probe,
+ .remove = ide_tape_remove,
.version = IDETAPE_VERSION,
.media = ide_tape,
- .busy = 1,
.supports_dsc_overlap = 1,
- .cleanup = idetape_cleanup,
.do_request = idetape_do_request,
.end_request = idetape_end_request,
- .pre_reset = idetape_pre_reset,
+ .error = __ide_error,
+ .abort = __ide_abort,
.proc = idetape_proc,
- .attach = idetape_attach,
- .drives = LIST_HEAD_INIT(idetape_driver.drives),
};
/*
static int idetape_open(struct inode *inode, struct file *filp)
{
- ide_drive_t *drive = inode->i_bdev->bd_disk->private_data;
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct ide_tape_obj *tape;
+ ide_drive_t *drive;
+
+ if (!(tape = ide_tape_get(disk)))
+ return -ENXIO;
+
+ drive = tape->drive;
+
drive->usage++;
+
return 0;
}
static int idetape_release(struct inode *inode, struct file *filp)
{
- ide_drive_t *drive = inode->i_bdev->bd_disk->private_data;
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct ide_tape_obj *tape = ide_tape_g(disk);
+ ide_drive_t *drive = tape->drive;
+
drive->usage--;
+
+ ide_tape_put(tape);
+
return 0;
}
unsigned int cmd, unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
- ide_drive_t *drive = bdev->bd_disk->private_data;
- int err = generic_ide_ioctl(file, bdev, cmd, arg);
+ struct ide_tape_obj *tape = ide_tape_g(bdev->bd_disk);
+ ide_drive_t *drive = tape->drive;
+ int err = generic_ide_ioctl(drive, file, bdev, cmd, arg);
if (err == -EINVAL)
err = idetape_blkdev_ioctl(drive, cmd, arg);
return err;
.ioctl = idetape_ioctl,
};
-static int idetape_attach (ide_drive_t *drive)
+static int ide_tape_probe(ide_drive_t *drive)
{
idetape_tape_t *tape;
+ struct gendisk *g;
int minor;
if (!strstr("ide-tape", drive->driver_req))
printk(KERN_WARNING "ide-tape: Use drive %s with ide-scsi emulation and osst.\n", drive->name);
printk(KERN_WARNING "ide-tape: OnStream support will be removed soon from ide-tape!\n");
}
- tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL);
+ tape = (idetape_tape_t *) kzalloc (sizeof (idetape_tape_t), GFP_KERNEL);
if (tape == NULL) {
printk(KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name);
goto failed;
}
- if (ide_register_subdriver(drive, &idetape_driver)) {
- printk(KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name);
- kfree(tape);
- goto failed;
- }
- for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++)
+
+ g = alloc_disk(1 << PARTN_BITS);
+ if (!g)
+ goto out_free_tape;
+
+ ide_init_disk(g, drive);
+
+ ide_register_subdriver(drive, &idetape_driver);
+
+ kref_init(&tape->kref);
+
+ tape->drive = drive;
+ tape->driver = &idetape_driver;
+ tape->disk = g;
+
+ g->private_data = &tape->driver;
+
+ drive->driver_data = tape;
+
+ down(&idetape_ref_sem);
+ for (minor = 0; idetape_devs[minor]; minor++)
;
+ idetape_devs[minor] = tape;
+ up(&idetape_ref_sem);
+
idetape_setup(drive, tape, minor);
- idetape_chrdevs[minor].drive = drive;
+
+ class_device_create(idetape_sysfs_class, NULL,
+ MKDEV(IDETAPE_MAJOR, minor), &drive->gendev, "%s", tape->name);
+ class_device_create(idetape_sysfs_class, NULL,
+ MKDEV(IDETAPE_MAJOR, minor + 128), &drive->gendev, "n%s", tape->name);
devfs_mk_cdev(MKDEV(HWIF(drive)->major, minor),
S_IFCHR | S_IRUGO | S_IWUGO,
S_IFCHR | S_IRUGO | S_IWUGO,
"%s/mtn", drive->devfs_name);
- drive->disk->number = devfs_register_tape(drive->devfs_name);
- drive->disk->fops = &idetape_block_ops;
+ g->number = devfs_register_tape(drive->devfs_name);
+ g->fops = &idetape_block_ops;
+ ide_register_region(g);
+
return 0;
+
+out_free_tape:
+ kfree(tape);
failed:
- return 1;
+ return -ENODEV;
}
MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver");
static void __exit idetape_exit (void)
{
- ide_unregister_driver(&idetape_driver);
+ driver_unregister(&idetape_driver.gen_driver);
+ class_destroy(idetape_sysfs_class);
unregister_chrdev(IDETAPE_MAJOR, "ht");
}
-/*
- * idetape_init will register the driver for each tape.
- */
-static int idetape_init (void)
+static int __init idetape_init(void)
{
+ int error = 1;
+ idetape_sysfs_class = class_create(THIS_MODULE, "ide_tape");
+ if (IS_ERR(idetape_sysfs_class)) {
+ idetape_sysfs_class = NULL;
+ printk(KERN_ERR "Unable to create sysfs class for ide tapes\n");
+ error = -EBUSY;
+ goto out;
+ }
+
if (register_chrdev(IDETAPE_MAJOR, "ht", &idetape_fops)) {
printk(KERN_ERR "ide-tape: Failed to register character device interface\n");
- return -EBUSY;
+ error = -EBUSY;
+ goto out_free_class;
}
- ide_register_driver(&idetape_driver);
+
+ error = driver_register(&idetape_driver.gen_driver);
+ if (error)
+ goto out_free_driver;
+
return 0;
+
+out_free_driver:
+ driver_unregister(&idetape_driver.gen_driver);
+out_free_class:
+ class_destroy(idetape_sysfs_class);
+out:
+ return error;
}
+MODULE_ALIAS("ide:*m-tape*");
module_init(idetape_init);
module_exit(idetape_exit);
MODULE_ALIAS_CHARDEV_MAJOR(IDETAPE_MAJOR);