/*
 *      Copyright (C) 1993-1995 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: /home/bas/distr/ftape-2.03b/RCS/kernel-interface.c,v $
 $Author: bas $
 *
 $Revision: 1.55 $
 $Date: 1995/05/27 12:10:05 $
 $State: Beta $
 *
 *      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.55 1995/05/27 12:10:05 bas Beta $";

#include "kernel-version.h"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/major.h>

#include "ftape.h"
#include "kernel-interface.h"
#include "ftape-read.h"
#include "ftape-write.h"
#include "ftape-io.h"
#include "ftape-ctl.h"
#include "ftape-rw.h"


/*      Global vars.
 */
char *tape_buffer = NULL;
char kernel_version[] = UTS_RELEASE;

/*      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);
static int ftape_lseek( struct inode* ino, struct file* filep,
                       off_t offset, int origin);
#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 = {
  ftape_lseek,			/* 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 */
};

/*      Called by modules package when installing the driver
 */
int
init_module( void) {
  TRACE_FUN( 5, "init_module");

  printk( "ftape v2.03b 27/05/95 (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)"
         "\n QIC-117 driver for QIC-40/80/3010/3020 tape drives\n"
         " Compiled for kernel version %s\n", kernel_version);
  TRACE( 3, "installing QIC-117 ftape driver...");
  if (register_chrdev( QIC117_TAPE_MAJOR, "ftape", &ftape_cdev)) {
    TRACE( 1, "register_chrdev failed");
    TRACE_EXIT;
    return -EIO;
  }
  /*    Get address of tape buffer, already aligned in kernel.
   */
  tape_buffer = (char*) (((long) ftape_big_buffer +
                          (BUFF_SIZE - 1)) & ~(BUFF_SIZE - 1));
  TRACEx2( 3, "%d tape_buffers @ %p", NR_FTAPE_BUFFERS, tape_buffer);
  TRACEx1( 3, "init_module @ 0x%p", init_module);
  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();
  TRACE_EXIT;
#if KERNEL_VERSION >= 1001085
  register_symtab(0);           /* remove global ftape symbols */
#endif
  return 0;
}

/*      Called by modules package when removing the driver
 */
void
cleanup_module( void) {
  TRACE_FUN( 5, "cleanup_module");

  if (unregister_chrdev( QIC117_TAPE_MAJOR, "ftape") != 0) {
    TRACE( 3, "failed");
  } else {
    TRACE( 3, "successful");
  }
  TRACE_EXIT;
}

/*      Open ftape device
 */
static int
ftape_open( struct inode* ino, struct file* filep)
{
  TRACE_FUN( 4, "ftape_open");
  int result;
  MOD_INC_USE_COUNT;            /* lock module in memory */

  TRACEi( 5, "called for minor", MINOR( ino->i_rdev));
  if (busy_flag) {
    TRACE( 1, "failed: already busy");
    MOD_DEC_USE_COUNT;          /* unlock module in memory */
    TRACE_EXIT;
    return -EBUSY;
  }
  if ((MINOR( ino->i_rdev) & ~FTAPE_NO_REWIND) > 3) {
    TRACE( 1, "failed: illegal unit nr");
    MOD_DEC_USE_COUNT;          /* unlock module in memory */
    TRACE_EXIT;
    return -ENXIO;
  }
  if (ftape_unit == -1 || FTAPE_UNIT != (MINOR( ino->i_rdev) & 3)) {
    /*  Other selection than last time
     */
    ftape_init_driver();
  }
  ftape_unit = MINOR( ino->i_rdev);
  ftape_failure = 0;            /* allow tape operations */
  old_sigmask = current->blocked;
  current->blocked = _BLOCK_ALL;
  result = _ftape_open();
  if (result < 0) {
    TRACE( 1, "_ftape_open failed");
    current->blocked = old_sigmask; /* restore mask */
    MOD_DEC_USE_COUNT;          /* unlock module in memory */
    TRACE_EXIT;
    return result;
  } else {
    busy_flag = 1;
    /*  Mask signals that will disturb proper operation of the
     *  program that is calling.
     */
    current->blocked = old_sigmask | _DO_BLOCK;
    TRACE_EXIT;
    return 0;
  }
}

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

  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit) {
    TRACE( 1, "failed: not busy or wrong unit");
    TRACE_EXIT;
    return;                     /* keep busy_flag !(?) */
  }
  current->blocked = _BLOCK_ALL;
  result = _ftape_close();
  if (result < 0) {
    TRACE( 1, "_ftape_close failed");
  }
  ftape_failure = 1;            /* inhibit any operation but open */
  busy_flag = 0;
  current->blocked = old_sigmask; /* restore before open state */
  TRACE_EXIT;
  MOD_DEC_USE_COUNT;            /* unlock module in memory */
}

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

  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit || ftape_failure) {
    TRACE( 1, "failed: not busy, failure or wrong unit");
    TRACE_EXIT;
    return -EIO;
  }
  old_sigmask = current->blocked; /* save mask */
  current->blocked = _BLOCK_ALL;
  /* This will work as long as sizeof( void*) == sizeof( long)
   */
  result = _ftape_ioctl( command, (void*) arg);
  current->blocked = old_sigmask; /* restore mask */
  TRACE_EXIT;
  return result;
}

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

  TRACEi( 5, "called with count:", req_len);
  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit || ftape_failure) {
    TRACE( 1, "failed: not busy, failure or wrong unit");
    TRACE_EXIT;
    return -EIO;
  }
  old_sigmask = current->blocked; /* save mask */
  current->blocked = _BLOCK_ALL;
  result = _ftape_read( buff, req_len);
  TRACEi( 7, "return with count:", result);
  current->blocked = old_sigmask; /* restore mask */
  TRACE_EXIT;
  return result;
}

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

  TRACEi( 5, "called with count:", req_len);
  if (!busy_flag || MINOR( ino->i_rdev) != ftape_unit || ftape_failure) {
    TRACE( 1, "failed: not busy, failure or wrong unit");
    TRACE_EXIT;
    return -EIO;
  }
  old_sigmask = current->blocked; /* save mask */
  current->blocked = _BLOCK_ALL;
  result = _ftape_write( buff, req_len);
  TRACEi( 7, "return with count:", result);
  current->blocked = old_sigmask; /* restore mask */
  TRACE_EXIT;
  return result;
}

/*      Seek tape device - not implemented !
 */
static int
ftape_lseek( struct inode* ino, struct file* filep, off_t offset, int origin)
{
  return -ESPIPE;
}

