/*
 * nasd_linux_drive_client.c
 *
 * NASD Drive Client API Linux Kernel Module
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>

#if defined(LINUX) && defined(KERNEL) && defined(MODULE) /* should always be */

#include <linux/module.h>

#include <nasd/nasd_sys.h>
#include <nasd/nasd_pdev.h>

#define NASD_EXPORT_SYMBOL(_symname_) EXPORT_SYMBOL(_symname_)

#include <nasd/nasd_linux_drive_client_syms.ver.h>

int pdmajor = 0; /* major device number, 0 = "assign me one" */

static char *devname = "nasdkp";

MODULE_AUTHOR("Jim Zelenka <jimz+@cs.cmu.edu>");
MODULE_DESCRIPTION("NASD Drive Client API");
MODULE_PARM(pdmajor, "i");

int
nasd_drive_client_open(
  struct inode  *inode,
  struct file   *file)
{
  int unit, ret;
  NASDpd_t *npd;
  kdev_t dev;

  dev = inode->i_rdev;

  NASDPD_CLI_GETDEV(dev, npd, unit);
  if (npd == NULL)
    return(-ENODEV);

  ret = nasdpd_real_open(npd);

  if (ret == 0) {
    MOD_INC_USE_COUNT;
    return(0);
  }

  return(-ret);
}

int
nasd_drive_client_release(
  struct inode  *inode,
  struct file   *file)
{
  int unit, ret;
  NASDpd_t *npd;
  kdev_t dev;

  dev = inode->i_rdev;

  NASDPD_CLI_GETDEV(dev, npd, unit);
  if (npd == NULL)
    return(-ENODEV);

  ret = nasdpd_real_close(npd);

  if (ret == 0) {
    MOD_DEC_USE_COUNT;
    return(0);
  }

  return(-ret);
}

int
nasd_drive_client_ioctl(
  struct inode   *inode,
  struct file    *file,
  unsigned int    cmd,
  unsigned long   arg)
{
  int size, unit, ret, ret2;
  NASDpd_t *npd;
  void *karg;
  kdev_t dev;

  size = _IOC_SIZE(cmd);
  NASD_ASSERT(size >= 0);
  NASD_ASSERT(size <= NASD_MAX_IOCTL_SIZE);

  dev = inode->i_rdev;

  NASDPD_CLI_GETDEV(dev, npd, unit);
  if (npd == NULL) { return(-ENODEV); }

  ret = nasd_sys_wire_buf((void *)arg, size, &karg, NASD_WIRE_DIR_ALL);
  if (ret) { return(-ret); }

  ret = nasdpd_real_ioctl(npd, cmd, karg);

  ret2 = nasd_sys_unwire_buf((void *)arg, size, karg, NASD_WIRE_DIR_ALL);

  if (ret)  { return(-ret);  }
  if (ret2) { return(-ret2); }
  return(0);
}

static struct file_operations nasdpd_fops = {
  NULL,                      /* lseek */
  NULL,                      /* read */
  NULL,                      /* write */
  NULL,                      /* readdir */
  NULL,                      /* select */
  nasd_drive_client_ioctl,   /* ioctl */
  NULL,                      /* mmap */
  nasd_drive_client_open,    /* open */
  NULL,                      /* flush */
  nasd_drive_client_release, /* like close */
  NULL,                      /* fsync */
  NULL,                      /* fasync */
  NULL,                      /* disk change */
  NULL                       /* revalidate (block dev only) */
};

/*
 * AFAICT, the caller expects this to return
 * 0=success, nonzero=error, and doesn't use the
 * nonzero return.
 */
int
init_module()
{
  nasd_status_t rc;
  int ret;

  rc = nasdpd_init();
  if (rc) {
    return(1);
  }

  if ((pdmajor >= MAX_CHRDEV) || (pdmajor < 0)) {
    printk(KERN_ERR "nasd_drive_client: %d is not a valid major number\n",
      pdmajor);
    printk(KERN_ERR "nasd_drive_client: load fails\n");
    return(1);
  }

  ret = register_chrdev(pdmajor, devname, &nasdpd_fops);
  if (ret < 0) {
    printk(KERN_ERR "nasd_drive_client: got %d registering %s at %d\n",
      ret, devname, pdmajor);
    printk(KERN_ERR "nasd_drive_client: load fails\n");
    return(ret);
  }

  if (pdmajor == 0) {
    pdmajor = ret;
  }
  else {
    NASD_ASSERT(ret == 0);
  }

  printk("nasd_drive_client: registered major number %d for %s\n",
    pdmajor, devname);

  nasd_linux_set_kpdev_support(1);

  return(0);
}

void
cleanup_module()
{
  nasd_linux_set_kpdev_support(0);
  unregister_chrdev(pdmajor, devname);
}

#endif /* LINUX && KERNEL && MODULE */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
