patch-2.4.2 linux/drivers/s390/char/tapechar.c
Next file: linux/drivers/s390/char/tapechar.h
Previous file: linux/drivers/s390/char/tapeblock.h
Back to the patch index
Back to the overall index
- Lines: 698
- Date:
Tue Feb 13 14:13:44 2001
- Orig file:
v2.4.1/linux/drivers/s390/char/tapechar.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.1/linux/drivers/s390/char/tapechar.c linux/drivers/s390/char/tapechar.c
@@ -0,0 +1,697 @@
+
+/***************************************************************************
+ *
+ * drivers/s390/char/tapechar.c
+ * character device frontend for tape device driver
+ *
+ * S390 version
+ * Copyright (C) 2000 IBM Corporation
+ * Author(s): Tuan Ngo-Anh <ngoanh@de.ibm.com>
+ * Carsten Otte <cotte@de.ibm.com>
+ *
+ * UNDER CONSTRUCTION: Work in progress...:-)
+ ****************************************************************************
+ */
+
+#include "tapedefs.h"
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <asm/ccwcache.h> /* CCW allocations */
+#include <asm/s390dyn.h>
+#include <asm/debug.h>
+#include <linux/mtio.h>
+#include <asm/uaccess.h>
+#include <linux/compatmac.h>
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+#include "tape.h"
+#include "tapechar.h"
+
+#define PRINTK_HEADER "TCHAR:"
+
+/*
+ * file operation structure for tape devices
+ */
+static struct file_operations tape_fops =
+{
+ // owner : THIS_MODULE,
+ llseek:NULL, /* lseek - default */
+ read:tape_read, /* read */
+ write:tape_write, /* write */
+ readdir:NULL, /* readdir - bad */
+ poll:NULL, /* poll */
+ ioctl:tape_ioctl, /* ioctl */
+ mmap:NULL, /* mmap */
+ open:tape_open, /* open */
+ flush:NULL, /* flush */
+ release:tape_release, /* release */
+ fsync:NULL, /* fsync */
+ fasync:NULL, /* fasync */
+ lock:NULL,
+};
+
+int tape_major = TAPE_MAJOR;
+
+void
+tapechar_setup (tape_info_t * tape)
+{
+ // nothing to do
+}
+
+void
+tapechar_init (void)
+{
+ int result;
+ tape_frontend_t *charfront,*temp;
+ tape_info_t* tape;
+
+ tape_init();
+
+ /* Register the tape major number to the kernel */
+ result = register_chrdev (tape_major, "tape", &tape_fops);
+
+ if (result < 0) {
+ PRINT_WARN (KERN_ERR "tape: can't get major %d\n", tape_major);
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,3,"c:initfail");
+ debug_text_event (tape_debug_area,3,"regchrfail");
+#endif /* TAPE_DEBUG */
+ panic ("no major number available for tape char device");
+ }
+ if (tape_major == 0)
+ tape_major = result; /* accept dynamic major number */
+ PRINT_WARN (KERN_ERR " tape gets major %d for character device\n", result);
+ charfront = kmalloc (sizeof (tape_frontend_t), GFP_KERNEL);
+ if (charfront == NULL) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,3,"c:initfail");
+ debug_text_event (tape_debug_area,3,"no mem");
+#endif /* TAPE_DEBUG */
+ panic ("no major number available for tape char device");
+ }
+ charfront->device_setup = tapechar_setup;
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,3,"c:init ok");
+#endif /* TAPE_DEBUG */
+ charfront->next=NULL;
+ if (first_frontend==NULL) {
+ first_frontend=charfront;
+ } else {
+ temp=first_frontend;
+ while (temp->next!=NULL)
+ temp=temp->next;
+ temp->next=charfront;
+ }
+ tape=first_tape_info;
+ while (tape!=NULL) {
+ tapechar_setup(tape);
+ tape=tape->next;
+ }
+}
+
+void
+tapechar_uninit (void)
+{
+ unregister_chrdev (tape_major, "tape");
+}
+
+/*
+ * Tape device read function
+ */
+static ssize_t
+tape_read (struct file *filp, char *data, size_t count, loff_t * ppos)
+{
+ long lockflags;
+ tape_info_t *tape;
+ size_t block_size;
+ ccw_req_t *cqr;
+ int rc;
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:read");
+#endif /* TAPE_DEBUG */
+ tape = first_tape_info;
+ while ((tape != NULL) && (tape->rew_filp != filp) && (tape->nor_filp != filp))
+ tape = (tape_info_t *) tape->next;
+ if (tape == NULL) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:nodev");
+#endif /* TAPE_DEBUG */
+ return -ENODEV;
+ }
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:ppos wrong");
+#endif /* TAPE_DEBUG */
+ return -EOVERFLOW; /* errno=75 Value too large for def. data type */
+ }
+ if (tape->block_size == 0) {
+ block_size = count;
+ } else {
+ block_size = tape->block_size;
+ }
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:nbytes:");
+ debug_int_event (tape_debug_area,6,block_size);
+#endif
+ cqr = tape->discipline->read_block (data, block_size, tape);
+ if (!cqr) {
+ return -ENOBUFS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->cqr = cqr;
+ tape->wanna_wakeup=0;
+ rc = do_IO (tape->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+ if (rc) {
+ tapestate_set(tape,TS_IDLE);
+ kfree (cqr);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return rc;
+ }
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ wait_event_interruptible (tape->wq,tape->wanna_wakeup);
+ tape->cqr = NULL;
+ tape->discipline->free_read_block (cqr, tape);
+ if (signal_pending (current)) {
+ tapestate_set (tape, TS_IDLE);
+ return -ERESTARTSYS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ if (tapestate_get (tape) == TS_FAILED) {
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return tape->rc;
+ }
+ if (tapestate_get (tape) != TS_DONE) {
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return -EIO;
+ }
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:rbytes:");
+ debug_int_event (tape_debug_area,6,block_size - tape->devstat.rescnt);
+#endif /* TAPE_DEBUG */
+ filp->f_pos += block_size - tape->devstat.rescnt;
+ return block_size - tape->devstat.rescnt;
+}
+
+/*
+ * Tape device write function
+ */
+static ssize_t
+tape_write (struct file *filp, const char *data, size_t count, loff_t * ppos)
+{
+ long lockflags;
+ tape_info_t *tape;
+ size_t block_size;
+ ccw_req_t *cqr;
+ int nblocks, i, rc;
+ size_t written = 0;
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:write");
+#endif
+ tape = first_tape_info;
+ while ((tape != NULL) && (tape->nor_filp != filp) && (tape->rew_filp != filp))
+ tape = (tape_info_t *) tape->next;
+ if (tape == NULL)
+ return -ENODEV;
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:ppos wrong");
+#endif
+ return -EOVERFLOW; /* errno=75 Value too large for def. data type */
+ }
+ if ((tape->block_size != 0) && (count % tape->block_size != 0))
+ return -EIO;
+ if (tape->block_size == 0) {
+ block_size = count;
+ nblocks = 1;
+ } else {
+ block_size = tape->block_size;
+ nblocks = count / (tape->block_size);
+ }
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:nbytes:");
+ debug_int_event (tape_debug_area,6,block_size);
+ debug_text_event (tape_debug_area,6,"c:nblocks:");
+ debug_int_event (tape_debug_area,6,nblocks);
+#endif
+ for (i = 0; i < nblocks; i++) {
+ cqr = tape->discipline->write_block (data + i * block_size, block_size, tape);
+ if (!cqr) {
+ return -ENOBUFS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->cqr = cqr;
+ tape->wanna_wakeup=0;
+ rc = do_IO (tape->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ wait_event_interruptible (tape->wq,tape->wanna_wakeup);
+ tape->cqr = NULL;
+ tape->discipline->free_write_block (cqr, tape);
+ if (signal_pending (current)) {
+ tapestate_set (tape, TS_IDLE);
+ return -ERESTARTSYS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ if (tapestate_get (tape) == TS_FAILED) {
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return tape->rc;
+ }
+ if (tapestate_get (tape) != TS_DONE) {
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return -EIO;
+ }
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:wbytes:");
+ debug_int_event (tape_debug_area,6,block_size - tape->devstat.rescnt);
+#endif
+ filp->f_pos += block_size - tape->devstat.rescnt;
+ written += block_size - tape->devstat.rescnt;
+ if (tape->devstat.rescnt > 0)
+ return written;
+ }
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:wtotal:");
+ debug_int_event (tape_debug_area,6,written);
+#endif
+ return written;
+}
+
+static int
+tape_mtioctop (struct file *filp, short mt_op, int mt_count)
+{
+ tape_info_t *tape;
+ ccw_req_t *cqr = NULL;
+ int rc;
+ long lockflags;
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:mtio");
+ debug_text_event (tape_debug_area,6,"c:ioop:");
+ debug_int_event (tape_debug_area,6,mt_op);
+ debug_text_event (tape_debug_area,6,"c:arg:");
+ debug_int_event (tape_debug_area,6,mt_count);
+#endif
+ tape = first_tape_info;
+ while ((tape != NULL) && (tape->rew_filp != filp) && (tape->nor_filp != filp))
+ tape = (tape_info_t *) tape->next;
+ if (tape == NULL)
+ return -ENODEV;
+ switch (mt_op) {
+ case MTREW: // rewind
+
+ cqr = tape->discipline->mtrew (tape, mt_count);
+ break;
+ case MTOFFL: // put drive offline
+
+ cqr = tape->discipline->mtoffl (tape, mt_count);
+ break;
+ case MTUNLOAD: // unload the tape
+
+ cqr = tape->discipline->mtunload (tape, mt_count);
+ break;
+ case MTWEOF: // write tapemark
+
+ cqr = tape->discipline->mtweof (tape, mt_count);
+ break;
+ case MTFSF: // forward space file
+
+ cqr = tape->discipline->mtfsf (tape, mt_count);
+ break;
+ case MTBSF: // backward space file
+
+ cqr = tape->discipline->mtbsf (tape, mt_count);
+ break;
+ case MTFSFM: // forward space file, stop at BOT side
+
+ cqr = tape->discipline->mtfsfm (tape, mt_count);
+ break;
+ case MTBSFM: // backward space file, stop at BOT side
+
+ cqr = tape->discipline->mtbsfm (tape, mt_count);
+ break;
+ case MTFSR: // forward space file
+
+ cqr = tape->discipline->mtfsr (tape, mt_count);
+ break;
+ case MTBSR: // backward space file
+
+ cqr = tape->discipline->mtbsr (tape, mt_count);
+ break;
+ case MTNOP:
+ cqr = tape->discipline->mtnop (tape, mt_count);
+ break;
+ case MTEOM: // postion at the end of portion
+
+ case MTRETEN: // retension the tape
+
+ cqr = tape->discipline->mteom (tape, mt_count);
+ break;
+ case MTERASE:
+ cqr = tape->discipline->mterase (tape, mt_count);
+ break;
+ case MTSETDENSITY:
+ cqr = tape->discipline->mtsetdensity (tape, mt_count);
+ break;
+ case MTSEEK:
+ cqr = tape->discipline->mtseek (tape, mt_count);
+ break;
+ case MTSETDRVBUFFER:
+ cqr = tape->discipline->mtsetdrvbuffer (tape, mt_count);
+ break;
+ case MTLOCK:
+ cqr = tape->discipline->mtsetdrvbuffer (tape, mt_count);
+ break;
+ case MTUNLOCK:
+ cqr = tape->discipline->mtsetdrvbuffer (tape, mt_count);
+ break;
+ case MTLOAD:
+ cqr = tape->discipline->mtload (tape, mt_count);
+ break;
+ case MTCOMPRESSION:
+ cqr = tape->discipline->mtcompression (tape, mt_count);
+ break;
+ case MTSETPART:
+ cqr = tape->discipline->mtsetpart (tape, mt_count);
+ break;
+ case MTMKPART:
+ cqr = tape->discipline->mtmkpart (tape, mt_count);
+ break;
+ case MTTELL: // return number of block relative to current file
+
+ cqr = tape->discipline->mttell (tape, mt_count);
+ break;
+ case MTSETBLK:
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->block_size = mt_count;
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:setblk:");
+ debug_int_event (tape_debug_area,6,mt_count);
+#endif
+ return 0;
+ case MTRESET:
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->kernbuf = tape->userbuf = NULL;
+ tapestate_set (tape, TS_IDLE);
+ tape->block_size = 0;
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:devreset:");
+ debug_int_event (tape_debug_area,6,tape->blk_minor);
+#endif
+ return 0;
+ default:
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:inv.mtio");
+#endif
+ return -EINVAL;
+ }
+ if (cqr == NULL) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:ccwg fail");
+#endif
+ return -ENOSPC;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->cqr = cqr;
+ tape->wanna_wakeup=0;
+ rc = do_IO (tape->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ wait_event_interruptible (tape->wq,tape->wanna_wakeup);
+ tape->cqr = NULL;
+ if (tape->kernbuf != NULL) {
+ kfree (tape->kernbuf);
+ tape->kernbuf = NULL;
+ }
+ tape_free_request (cqr);
+ if (signal_pending (current)) {
+ tapestate_set (tape, TS_IDLE);
+ return -ERESTARTSYS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ if (((mt_op == MTEOM) || (mt_op == MTRETEN)) && (tapestate_get (tape) == TS_FAILED))
+ tapestate_set (tape, TS_DONE);
+ if (tapestate_get (tape) == TS_FAILED) {
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return tape->rc;
+ }
+ if (tapestate_get (tape) != TS_DONE) {
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ return -EIO;
+ }
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ switch (mt_op) {
+ case MTRETEN: //need to rewind the tape after moving to eom
+
+ return tape_mtioctop (filp, MTREW, 1);
+ case MTFSFM: //need to skip back over the filemark
+
+ return tape_mtioctop (filp, MTBSFM, 1);
+ case MTBSF: //need to skip forward over the filemark
+
+ return tape_mtioctop (filp, MTFSF, 1);
+ }
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:mtio done");
+#endif
+ return 0;
+}
+
+/*
+ * Tape device io controls.
+ */
+static int
+tape_ioctl (struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ long lockflags;
+ tape_info_t *tape;
+ ccw_req_t *cqr;
+ struct mtop op; /* structure for MTIOCTOP */
+ struct mtpos pos; /* structure for MTIOCPOS */
+ struct mtget get;
+
+ int rc;
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:ioct");
+#endif
+ tape = first_tape_info;
+ while ((tape != NULL) &&
+ (tape->rew_minor != MINOR (inode->i_rdev)) &&
+ (tape->nor_minor != MINOR (inode->i_rdev)))
+ tape = (tape_info_t *) tape->next;
+ if (tape == NULL) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:nodev");
+#endif
+ return -ENODEV;
+ }
+ // check for discipline ioctl overloading
+ if ((rc = tape->discipline->discipline_ioctl_overload (inode, filp, cmd, arg))
+ != -EINVAL) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:ioverloa");
+#endif
+ return rc;
+ }
+
+ switch (cmd) {
+ case MTIOCTOP: /* tape op command */
+ if (copy_from_user (&op, (char *) arg, sizeof (struct mtop)))
+ return -EFAULT;
+ return (tape_mtioctop (filp, op.mt_op, op.mt_count));
+ case MTIOCPOS: /* query tape position */
+ cqr = tape->discipline->mttell (tape, 0);
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->cqr = cqr;
+ tape->wanna_wakeup=0;
+ do_IO (tape->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ wait_event_interruptible (tape->wq,tape->wanna_wakeup);
+ pos.mt_blkno = tape->rc;
+ tape->cqr = NULL;
+ if (tape->kernbuf != NULL) {
+ kfree (tape->kernbuf);
+ tape->kernbuf = NULL;
+ }
+ tape_free_request (cqr);
+ if (signal_pending (current)) {
+ tapestate_set (tape, TS_IDLE);
+ return -ERESTARTSYS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ if (copy_to_user ((char *) arg, &pos, sizeof (struct mtpos)))
+ return -EFAULT;
+ return 0;
+ case MTIOCGET:
+ get.mt_erreg = tape->rc;
+ cqr = tape->discipline->mttell (tape, 0);
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tape->cqr = cqr;
+ tape->wanna_wakeup=0;
+ do_IO (tape->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ wait_event_interruptible (tape->wq,tape->wanna_wakeup);
+ get.mt_blkno = tape->rc;
+ get.mt_fileno = 0;
+ get.mt_type = MT_ISUNKNOWN;
+ get.mt_resid = tape->devstat.rescnt;
+ get.mt_dsreg = tape->devstat.ii.sense.data[3];
+ get.mt_gstat = 0;
+ if (tape->devstat.ii.sense.data[1] & 0x08)
+ get.mt_gstat &= GMT_BOT (1); // BOT
+
+ if (tape->devstat.ii.sense.data[1] & 0x02)
+ get.mt_gstat &= GMT_WR_PROT (1); // write protected
+
+ if (tape->devstat.ii.sense.data[1] & 0x40)
+ get.mt_gstat &= GMT_ONLINE (1); //drive online
+
+ tape->cqr = NULL;
+ if (tape->kernbuf != NULL) {
+ kfree (tape->kernbuf);
+ tape->kernbuf = NULL;
+ }
+ tape_free_request (cqr);
+ if (signal_pending (current)) {
+ tapestate_set (tape, TS_IDLE);
+ return -ERESTARTSYS;
+ }
+ s390irq_spin_lock_irqsave (tape->devinfo.irq, lockflags);
+ tapestate_set (tape, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (tape->devinfo.irq, lockflags);
+ if (copy_to_user ((char *) arg, &get, sizeof (struct mtget)))
+ return -EFAULT;
+ return 0;
+ default:
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,3,"c:ioct inv");
+#endif
+ return -EINVAL;
+ }
+}
+
+/*
+ * Tape device open function.
+ */
+static int
+tape_open (struct inode *inode, struct file *filp)
+{
+ tape_info_t *ti;
+ kdev_t dev;
+ long lockflags;
+
+ inode = filp->f_dentry->d_inode;
+ ti = first_tape_info;
+ while ((ti != NULL) &&
+ (ti->rew_minor != MINOR (inode->i_rdev)) &&
+ (ti->nor_minor != MINOR (inode->i_rdev)))
+ ti = (tape_info_t *) ti->next;
+ if (ti == NULL)
+ return -ENODEV;
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:open:");
+ debug_int_event (tape_debug_area,6,ti->blk_minor);
+#endif
+ s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+ if (tapestate_get (ti) != TS_UNUSED) {
+ s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:dbusy");
+#endif
+ return -EBUSY;
+ }
+ tapestate_set (ti, TS_IDLE);
+ s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+
+ dev = MKDEV (tape_major, MINOR (inode->i_rdev)); /* Get the device */
+ s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+ if (ti->rew_minor == MINOR (inode->i_rdev))
+ ti->rew_filp = filp; /* save for later reference */
+ else
+ ti->nor_filp = filp;
+ filp->private_data = ti; /* save the dev.info for later reference */
+ s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif /* MODULE */
+ return 0;
+}
+
+/*
+ * Tape device release function.
+ */
+static int
+tape_release (struct inode *inode, struct file *filp)
+{
+ long lockflags;
+ tape_info_t *ti,*lastti;
+ ccw_req_t *cqr = NULL;
+ int rc;
+
+ inode = filp->f_dentry->d_inode;
+ ti = first_tape_info;
+ while ((ti != NULL) && (ti->rew_minor != MINOR (inode->i_rdev)) && (ti->nor_minor != MINOR (inode->i_rdev)))
+ ti = (tape_info_t *) ti->next;
+ if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) {
+ if (ti==first_tape_info) {
+ first_tape_info=ti->next;
+ } else {
+ lastti=first_tape_info;
+ while (lastti->next!=ti) lastti=lastti->next;
+ lastti->next=ti->next;
+ }
+ kfree(ti);
+ return 0;
+ }
+ if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:notidle!");
+#endif
+ return -ENXIO; /* error in tape_release */
+ }
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:release:");
+ debug_int_event (tape_debug_area,6,ti->blk_minor);
+#endif
+ if (ti->rew_minor == MINOR (inode->i_rdev)) {
+ cqr = ti->discipline->mtrew (ti, 1);
+ if (cqr != NULL) {
+#ifdef TAPE_DEBUG
+ debug_text_event (tape_debug_area,6,"c:rewrelea");
+#endif
+ s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+ tapestate_set (ti, TS_REW_RELEASE_INIT);
+ ti->cqr = cqr;
+ ti->wanna_wakeup=0;
+ rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+ s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+ wait_event (ti->wq,ti->wanna_wakeup);
+ ti->cqr = NULL;
+ tape_free_request (cqr);
+ }
+ }
+ s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+ tapestate_set (ti, TS_UNUSED);
+ s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif /* MODULE */
+ return 0;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)