/*
 *      Copyright (C) 1993 Bas Laarhoven.

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, 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; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

 $Source: /usr/src/distr/ftape-0.9.7/RCS/kernel-interface.c,v $
 $Author: bas $
 *
 $Revision: 1.6 $
 $Date: 1993/12/04 11:52:41 $
 $State: Alpha $
 *
 *      This file contains the code that interfaces the kernel
 *      for the QIC-40/80 floppy-tape driver for Linux.
 */

static char RCSid[] = "$Id: kernel-interface.c,v 1.6 1993/12/04 11:52:41 bas Alpha $";


#include <linux/errno.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/kernel.h>
#include <linux/signal.h>

#include "ftape-ioctl.h"
#include "ftape-rw.h"
#include "ftape-io.h"
#include "kernel-interface.h"

/*      Global vars.
 */
char *tape_buffer = NULL;
char kernel_version[] = "0.99.14a";

/*      Local vars.
 */
static int busy_flag = 0;
static int old_sigmask;

static int ftape_open( struct inode* ino, struct file* filep);
static void ftape_close( struct inode* ino, struct file* filep);
static int ftape_ioctl( struct inode* ino, struct file* filep, 
                       unsigned int command, unsigned long arg);
static int ftape_read( struct inode* ino, struct file* fp, char* buff, int req_len);
static int ftape_write( struct inode* ino, struct file* fp, char* buff, int req_len);
#if 0
static int ftape_select( void);
static int ftape_mmap( int dev, unsigned off, int prot);
#else
# define ftape_select NULL
# define ftape_mmap NULL
#endif

static struct file_operations ftape_cdev = {
  NULL,				/* lseek */
  ftape_read, 			/* read */
  ftape_write,			/* write */
  NULL,				/* readdir */
  ftape_select, 		/* select */
  ftape_ioctl,			/* ioctl */
  ftape_mmap, 		        /* mmap */
  ftape_open,			/* open */
  ftape_close,			/* release */
  NULL,                         /* fsync */
};

#define SIG_BLOCK_ALL   (0xffffffff)

/*      Called by modules package when installing the driver
 */
int
init_module( void) {

  TRACE( -1, "init_module", "installing QIC-117 ftape driver...");
  if (register_chrdev( TAPE_QIC_117_MAJOR, "mt", &ftape_cdev)) {
    TRACE( -1, "init_module", "register_chrdev failed");
    return -EIO;
  }
  /*    Align buffer in kernel space at 32Kb boundary (what a waste!).
   */
  tape_buffer = (char*) (( (long) ftape_big_buffer + 0x7fffl) & ~0x7fffl);
  TRACElx( 2, "init_module", "ftape_big_buffer    @", (unsigned long) ftape_big_buffer);
  TRACElx( 2, "init_module", "aligned tape_buffer @", (unsigned long) tape_buffer);

  busy_flag = 0;
  ftape_unit = -1;
  ftape_failure = 1;            /* inhibit any operation but open */
  
  udelay_calibrate();           /* must be before fdc_wait_calibrate ! */
  fdc_wait_calibrate();

  ftape_new_cartridge();        /* init some tape related variables */

  return 0;
}

/*      Called by modules package when removing the driver
 */
void
cleanup_module( void) {

  if (unregister_chrdev( TAPE_QIC_117_MAJOR, "mt") != 0) {
    TRACE( -1, "cleanup_module", "failed");
  } else {
    TRACE( -1, "cleanup_module", "succeeded");
  }
}

/*      Open ftape device
 */
int
ftape_open( struct inode* ino, struct file* filep)
{
  int result;

  TRACE( 4, "ftape_open", "called");

  if (busy_flag) {
    TRACE( 0, "ftape_open", "failed: already busy");
    return -EBUSY;
  }
  ftape_unit = MINOR( ino->i_rdev);
  if ((ftape_unit & ~4) > 0) {
    TRACE( 0, "ftape_open", "failed: illegal unit nr");
    return -ENXIO;
  }
  ftape_failure = 0;            /* allow tape operations */
  old_sigmask = sys_sgetmask();
  sys_ssetmask( SIG_BLOCK_ALL);

  result = _ftape_open();

  if (result < 0) {
    TRACE( 0, "ftape_open", "_ftape_open failed");
    sys_ssetmask( old_sigmask); /* restore mask */
    return result;
  } else {
    busy_flag = 1;
    sys_ssetmask( old_sigmask | 1 << (SIGPIPE - 1));
    return 0;
  }
}

/*      Close ftape device
 */
void
ftape_close( struct inode* ino, struct file* filep)
{
  int result;

  TRACE( 4, "ftape_close", "called");

  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit) {
    TRACE( 0, "ftape_close", "failed: not busy or wrong unit");
    return;                     /* keep busy_flag !(?) */
  }
  sys_ssetmask( SIG_BLOCK_ALL);

  result = _ftape_close();
  if (result < 0) {
    TRACE( 0, "ftape_close", "_ftape_close failed");
  }
  ftape_failure = 1;            /* inhibit any operation but open */
  busy_flag = 0;
  sys_ssetmask( old_sigmask);   /* restore before open state */
}

/*      Ioctl for ftape device
 */
int
ftape_ioctl( struct inode* ino, struct file* filep, 
            unsigned int command, unsigned long arg)
{
  int result = -EIO;
  int old_sigmask;

  TRACElx( 4, "ftape_ioctl", "called with code:", command);

  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit || ftape_failure) {
    TRACE( 0, "ftape_ioctl", "failed: not busy, failure or wrong unit");
    return -EIO;
  }
  old_sigmask = sys_sgetmask(); /* save mask */
  sys_ssetmask( SIG_BLOCK_ALL);

  /* This will work as long as sizeof( void*) == sizeof( long)
   */
  result = _ftape_ioctl( command, (void*) arg);

  sys_ssetmask( old_sigmask);   /* restore mask */
  return result;
}

/*      Read from tape device
 */
int
ftape_read( struct inode* ino, struct file* fp, char* buff, int req_len)
{
  int result = -EIO;
  int old_sigmask;

  TRACEi( 10, "ftape_read", "called with count:", req_len);

  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit || ftape_failure) {
    TRACE( 0, "ftape_read", "failed: not busy, failure or wrong unit");
    return -EIO;
  }
  old_sigmask = sys_sgetmask(); /* save mask */
  sys_ssetmask( SIG_BLOCK_ALL);

  result = _ftape_read( buff, req_len);

  if (req_len != result) {
    TRACEi( 10, "ftape_read", "partial read count:", result);
  }
  sys_ssetmask( old_sigmask);   /* restore mask */
  return result;
}

/*      Write to tape device
 */
int
ftape_write( struct inode* ino, struct file* fp, char* buff, int req_len)
{
  int result = -EIO;
  int old_sigmask;

  TRACEi( 10, "ftape_write", "called with count:", req_len);

  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit || ftape_failure) {
    TRACE( 0, "ftape_write", "failed: not busy, failure or wrong unit");
    return -EIO;
  }
  old_sigmask = sys_sgetmask(); /* save mask */
  sys_ssetmask( SIG_BLOCK_ALL);

  result = _ftape_write( buff, req_len);

  if (req_len != result) {
    TRACEi( 10, "ftape_write", "partial write count:", result);
  }
  sys_ssetmask( old_sigmask);   /* restore mask */
  return result;
}
