/*
 * nasd_pdrive_client_kpdev.c
 *
 * RPC-specific client code for using kpdev
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,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>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_pdev.h>

#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>

#define KEYCPY(_src_,_dest_) bcopy((char *)(_src_), (char *)(_dest_), NASD_KEY_SZ)

/*
 * When binding with no param (ie, NASD_BIND_DEFAULT) we'll just
 * open /dev/nasdkp0, then /dev/nasdkp1, and so on, until we get
 * one that no one is using.
 */
#define TRY_KPDEVS 16

/* it will be zeroes by virtue of being a static global */
static nasd_capability_t nasd_cl_kpdev_bogus_cap;

nasd_status_t
nasd_cl_kpdev_bind(
  nasd_drive_handle_t   handle,
  char                 *drive_name,
  char                 *portnum,
  int                   binding_type,
  void                 *binding_param,
  int                   binding_param_len)
{
  nasd_drive_param_kpdev_t *param;
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_unbind_t kpunbind;
  struct hostent he, *hep;
  NASDPD_bind_t kpbind;
  nasd_options_t opts;
  struct in_addr addr;
  char kpdevname[64];
  nasd_status_t rc;
  int ret, i, n;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  if (binding_type == NASD_BIND_KPDEV) {
    /*
     * Must have the binding_param and binding_param_len
     * to pass in, not just the devname. This means that
     * NASD_BIND_KPDEV won't work for NASD_BIND_DEFAULT.
     */
    if (binding_param_len != sizeof(nasd_drive_param_kpdev_t)) {
      return(NASD_BAD_BINDING_PARAM_LEN);
    }
  }

  if (binding_param_len == 0) {
    /*
     * Probably NASD_BIND_DEFAULT. We'll try to do something clever.
     */
    param = NULL;
  }
  else if (binding_param_len != sizeof(nasd_drive_param_kpdev_t)) {
    /*
     * We've got a nonzero param_len that still makes
     * no sense. Punt.
     */
    return(NASD_BAD_BINDING_PARAM_LEN);
  }
  else {
    param = (nasd_drive_param_kpdev_t *)binding_param;
  }

  if (isdigit(drive_name[0])) {
    strcpy(kpbind.drive_name, drive_name);
  }
  else {
    rc = nasd_gethostbyname_r(drive_name, &he);
    if (rc)
      return(rc);
    hep = &he;
    bcopy((char *)hep->h_addr_list[0], (char *)&addr.s_addr, 4);
    strcpy(kpbind.drive_name, inet_ntoa(addr));
  }

  /*
   * Determine if kpdev support (or any support)
   * is loaded.
   */
  ret = nasd_srv(NASD_SC_GET_OPTIONS, &opts);
  if (ret) {
    return(NASD_NO_KERNEL_SUPPORT);
  }
  if (!(opts.opts1&NASD_OPTS1_KPDEV)) {
    return(NASD_NO_KPDEV_SUPPORT);
  }

  if (param) {
    rpch->fd = open(param->devname, O_RDWR);
  }
  else {
    /*
     * Iterate looking for a kpdev we can use.
     */
    rpch->fd = (-1);
    for(i=0;i<TRY_KPDEVS;i++) {
      sprintf(kpdevname, "/dev/nasdkp%d", i);
      rpch->fd = open(kpdevname, O_RDWR);
      if (rpch->fd < 0)
        continue;
      ret = ioctl(rpch->fd, NASDPD_GET_OPENCOUNT, &n);
      if (ret < 0) {
        close(rpch->fd);
        rpch->fd = (-1);
        continue;
      }
      if (n > 1) {
        /* someone else has it open */
        close(rpch->fd);
        rpch->fd = (-1);
        continue;
      }
      break;
    }
  }
  if (rpch->fd < 0) {
    return(NASD_RPC_LOCAL_FAIL);
  }
  strcpy(kpbind.drive_port, portnum);

  if (binding_type == NASD_BIND_KPDEV) {
    kpbind.binding_type = param->binding_type;
    kpbind.binding_param = param->binding_param;
    kpbind.binding_param_len = param->binding_param_len;
  }
  else {
    kpbind.binding_type = binding_type;
    kpbind.binding_param = NULL;
    kpbind.binding_param_len = 0;
  }

  ioctl(rpch->fd, NASDPD_PUNBIND, &kpunbind);
  ret = ioctl(rpch->fd, NASDPD_PBIND, &kpbind);
  if (ret)
    return(NASD_RPC_LOCAL_FAIL);

  /*
   * The in-kernel part will do the retries
   */
  handle->skew_retries = 0;

  return(kpbind.bind_status);
}

nasd_status_t
nasd_cl_kpdev_unbind(
  nasd_drive_handle_t  handle)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_unbind_t kpunbind;
  int ret1, ret2;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  ret1 = ioctl(rpch->fd, NASDPD_PUNBIND, &kpunbind);
  ret2 = close(rpch->fd);
  if (ret1 || ret2)
    return(NASD_RPC_LOCAL_FAIL);

  return(kpunbind.unbind_status);
}

void
nasd_cl_kpdev_null_dr(
  nasd_drive_handle_t   handle,
  nasd_res_t           *res,
  nasd_rpc_status_t    *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_null_t null;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  ret = ioctl(rpch->fd, NASDPD_PNULL, &null);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  res->nasd_status = null.nasd_status;
  *status = null.op_status;
}

void
nasd_cl_kpdev_sync_dr(
  nasd_drive_handle_t   handle,
  nasd_res_t           *res,
  nasd_rpc_status_t    *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_sync_t sync;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  ret = ioctl(rpch->fd, NASDPD_PSYNC, &sync);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  res->nasd_status = sync.nasd_status;
  *status = sync.op_status;
}

void
nasd_cl_kpdev_part_creat_dr(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_part_creat_dr_args_t  *args,
  nasd_p_part_creat_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_crpart_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PCRPART, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_create_dr__otw_provided(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t    *sec_param,
  nasd_capability_t        *capability,
  nasd_p_create_dr_args_t  *args,
  nasd_p_create_dr_res_t   *res,
  nasd_cl_p_otw_buf_t      *otw,
  nasd_rpc_status_t        *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_create_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = args;
  ioccmd.res = res;
  ret = ioctl(rpch->fd, NASDPD_P_PCREATE, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_getattr_dr(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t         *capability,
  nasd_p_getattr_dr_args_t  *args,
  nasd_p_getattr_dr_res_t   *res,
  nasd_rpc_status_t         *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_getattr_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = args;
  ioccmd.res = res;
  ret = ioctl(rpch->fd, NASDPD_P_PGETATTR, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_read_simple_dr(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t         *capability,
  int                        is_read2,
  nasd_p_smpl_op_dr_args_t  *args,
  void                      *buf,
  nasd_p_fastread_dr_res_t  *res,
  nasd_rpc_status_t         *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_read_simple_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.is_read2 = is_read2;
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ioccmd.buf = buf;
  if (handle->type == NASD_BIND_KPDEV_DEFAULT_FAKE_RW)
    ret = ioctl(rpch->fd, NASDPD_P_PREAD_FAKE, &ioccmd);
  else
    ret = ioctl(rpch->fd, NASDPD_P_PREAD_SIMPLE, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_tread_simple_dr(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  int                         is_read2,
  nasd_p_thrtl_op_dr_args_t  *args,
  void                       *buf,
  nasd_p_fastread_dr_res_t   *res,
  nasd_rpc_status_t          *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_tread_simple_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;
  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.is_read2 = is_read2;
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ioccmd.buf = buf;
  if (handle->type == NASD_BIND_KPDEV_DEFAULT_FAKE_RW) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }
  else {
    ret = ioctl(rpch->fd, NASDPD_P_PTREAD_SIMPLE, &ioccmd);
    if (ret) {
      res->nasd_status = NASD_RPC_LOCAL_FAIL;
      *status = errno;
      return;
    }
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_write_simple_dr(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  nasd_p_smpl_op_dr_args_t   *args,
  void                       *buf,
  nasd_p_fastwrite_dr_res_t  *res,
  nasd_rpc_status_t          *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_write_simple_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ioccmd.buf = buf;
  if (handle->type == NASD_BIND_KPDEV_DEFAULT_FAKE_RW)
    ret = ioctl(rpch->fd, NASDPD_P_PWRITE_FAKE, &ioccmd);
  else
    ret = ioctl(rpch->fd, NASDPD_P_PWRITE_SIMPLE, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_setattr_dr__otw_provided(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t         *capability,
  nasd_p_setattr_dr_args_t  *args,
  nasd_p_setattr_dr_res_t   *res,
  nasd_cl_p_otw_buf_t       *otw,
  nasd_rpc_status_t         *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_setattr_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = args;
  ioccmd.res = res;
  ret = ioctl(rpch->fd, NASDPD_P_PSETATTR, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_flush_obj_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t       *sec_param,
  nasd_capability_t           *capability,
  nasd_p_flush_obj_dr_args_t  *args,
  nasd_p_flush_obj_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_flush_obj_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PFLUSH_OBJ, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_eject_obj_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t       *sec_param,
  nasd_capability_t           *capability,
  nasd_p_eject_obj_dr_args_t  *args,
  nasd_p_eject_obj_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_eject_obj_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PEJECT_OBJ, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_remove_dr(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t    *sec_param,
  nasd_capability_t        *capability,
  nasd_p_remove_dr_args_t  *args,
  nasd_p_remove_dr_res_t   *res,
  nasd_rpc_status_t        *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_remove_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PREMOVE, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_initialize_dr(
  nasd_drive_handle_t           handle,
  nasd_p_initialize_dr_args_t  *args,
  nasd_p_initialize_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_initialize_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PINITIALIZE, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_strt_iread_dr(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_strt_iread_dr_args_t  *args,
  nasd_p_strt_iread_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_strt_iread_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PSTRT_IREAD, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_stop_iread_dr(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_stop_iread_dr_args_t  *args,
  nasd_p_stop_iread_dr_res_t   *res,
  nasd_rpc_status_t            *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_stop_iread_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PSTOP_IREAD, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_rshutdown_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t            *capability,
  nasd_p_rshutdown_dr_args_t  *args,
  nasd_p_rshutdown_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_rshutdown_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  }
  else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid = 0;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PRSHUTDOWN, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void nasd_cl_kpdev_remote_attach_dr(
  nasd_drive_handle_t              handle,
  nasd_key_t                      in_key,
  nasd_security_param_t          *sec_param,
  nasd_capability_t              *capability,
  nasd_p_remote_attach_dr_args_t   *args,
  void                             *buf,
  nasd_p_remote_attach_dr_res_t    *res,
  nasd_rpc_status_t                *status)

{
  nasd_drive_handle_kpdev_t     *rpch;
  NASDPD_p_remote_attach_t      ioccmd;
  int                           ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;
  
  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  } else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid;
  }
  ioccmd.args = args;
  ioccmd.buf = buf;
  ret = ioctl(rpch->fd, NASDPD_P_PREMOTE_ATTACH,&ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *status = ioccmd.op_status;
}

 
void nasd_cl_kpdev_remote_detach_dr(
  nasd_drive_handle_t              handle,
  nasd_key_t                      in_key,
  nasd_security_param_t          *sec_param,
  nasd_capability_t              *capability,
  nasd_p_remote_detach_dr_args_t   *args,
  nasd_p_remote_detach_dr_res_t    *res,
  nasd_rpc_status_t                *status)
{
  nasd_drive_handle_kpdev_t     *rpch;
  NASDPD_p_remote_detach_t      ioccmd;
  int                           ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;
  
  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  } else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid;
  }
  ioccmd.args = *args;
  ret = ioctl(rpch->fd, NASDPD_P_PREMOTE_DETACH,&ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *status = ioccmd.op_status;
}


void nasd_cl_kpdev_remote_invoke_dr(
  nasd_drive_handle_t              handle,
  nasd_key_t                       in_key,
  nasd_security_param_t            *sec_param,
  nasd_capability_t                *capability,
  nasd_p_smpl_op_dr_args_t         *args,
  void                             *buf,
  nasd_p_fastread_dr_res_t         *res,
  nasd_rpc_status_t                *status)
{
  nasd_drive_handle_kpdev_t     *rpch;
  NASDPD_p_remote_invoke_t      ioccmd;
  int                           ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;
  
  KEYCPY(in_key, ioccmd.in_key);
  ioccmd.sec_param = *sec_param;
  if (capability) {
    ioccmd.capability = *capability;
    ioccmd.capability_valid = 1;
  } else {
    ioccmd.capability = nasd_cl_kpdev_bogus_cap;
    ioccmd.capability_valid;
  }
  ioccmd.args = *args;
  ioccmd.buf = buf;
  ret = ioctl(rpch->fd, NASDPD_P_PREMOTE_DETACH,&ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *status = ioccmd.op_status;
}


void
nasd_cl_kpdev_getinfo_dr(
  nasd_drive_handle_t       handle,
  nasd_p_getinfo_dr_res_t  *res,
  nasd_rpc_status_t        *status)
{
  nasd_drive_handle_kpdev_t *rpch;
  NASDPD_p_getinfo_t ioccmd;
  int ret;

  rpch = (nasd_drive_handle_kpdev_t *)handle->rpc_specific_handle;

  ret = ioctl(rpch->fd, NASDPD_P_PGETINFO, &ioccmd);
  if (ret) {
    res->nasd_status = NASD_RPC_LOCAL_FAIL;
    *status = errno;
    return;
  }
  *res = ioccmd.res;
  *status = ioccmd.op_status;
}

void
nasd_cl_kpdev_error_string(
  nasd_drive_handle_t   handle,
  nasd_rpc_status_t     status,
  nasd_error_string_t   str,
  char                 *file,
  int                   line)
{
  /*
   * XXX get the kpdev to fill this in
   */
  sprintf(str, "%d:%u", handle->type, status);
}

nasd_cl_p_rpcmod_tab_t nasd_cl_kpdev_mod = {
  NULL,
  NASD_RPCMOD_INIT_FAIL,

  nasd_cl_kpdev_bind,
  nasd_cl_kpdev_unbind,

  nasd_cl_kpdev_null_dr,
  nasd_cl_kpdev_sync_dr,
  nasd_cl_kpdev_part_creat_dr,
  nasd_cl_kpdev_getattr_dr,
  nasd_cl_kpdev_write_simple_dr,
  NULL,
  nasd_cl_kpdev_read_simple_dr,
  NULL,
  nasd_cl_kpdev_tread_simple_dr,
  NULL,
  nasd_cl_kpdev_flush_obj_dr,
  nasd_cl_kpdev_eject_obj_dr,
  nasd_cl_kpdev_remove_dr,
  nasd_cl_kpdev_initialize_dr,
  nasd_cl_kpdev_strt_iread_dr,
  nasd_cl_kpdev_stop_iread_dr,
  nasd_cl_kpdev_rshutdown_dr,
  nasd_cl_kpdev_getinfo_dr,

  nasd_cl_kpdev_remote_attach_dr,
  nasd_cl_kpdev_remote_detach_dr,
  nasd_cl_kpdev_remote_invoke_dr,


  nasd_cl_kpdev_create_dr__otw_provided,
  nasd_cl_kpdev_setattr_dr__otw_provided,

  nasd_cl_kpdev_error_string,

  NASD_BIND_KPDEV,
  "Kernel Pseudo-Device Redirect",
  0,

  NULL
};

nasd_cl_p_rpcmod_tab_t nasd_cl_kpdev_default_mod = {
  NULL,
  NASD_RPCMOD_INIT_FAIL,

  nasd_cl_kpdev_bind,
  nasd_cl_kpdev_unbind,

  nasd_cl_kpdev_null_dr,
  nasd_cl_kpdev_sync_dr,
  nasd_cl_kpdev_part_creat_dr,
  nasd_cl_kpdev_getattr_dr,
  nasd_cl_kpdev_write_simple_dr,
  NULL,
  nasd_cl_kpdev_read_simple_dr,
  NULL,
  nasd_cl_kpdev_tread_simple_dr,
  NULL,
  nasd_cl_kpdev_flush_obj_dr,
  nasd_cl_kpdev_eject_obj_dr,
  nasd_cl_kpdev_remove_dr,
  nasd_cl_kpdev_initialize_dr,
  nasd_cl_kpdev_strt_iread_dr,
  nasd_cl_kpdev_stop_iread_dr,
  nasd_cl_kpdev_rshutdown_dr,
  nasd_cl_kpdev_getinfo_dr,

  nasd_cl_kpdev_remote_attach_dr,
  nasd_cl_kpdev_remote_detach_dr,
  nasd_cl_kpdev_remote_invoke_dr,

  nasd_cl_kpdev_create_dr__otw_provided,
  nasd_cl_kpdev_setattr_dr__otw_provided,

  nasd_cl_kpdev_error_string,

  NASD_BIND_KPDEV_DEFAULT,
  "Kernel Pseudo-Device Redirect (Default)",
  0,

  NULL
};

nasd_cl_p_rpcmod_tab_t nasd_cl_kpdev_default_fake_rw_mod = {
  NULL,
  NASD_RPCMOD_INIT_FAIL,

  nasd_cl_kpdev_bind,
  nasd_cl_kpdev_unbind,

  nasd_cl_kpdev_null_dr,
  nasd_cl_kpdev_sync_dr,
  nasd_cl_kpdev_part_creat_dr,
  nasd_cl_kpdev_getattr_dr,
  nasd_cl_kpdev_write_simple_dr,
  NULL,
  nasd_cl_kpdev_read_simple_dr,
  NULL,
  nasd_cl_kpdev_tread_simple_dr,
  NULL,
  nasd_cl_kpdev_flush_obj_dr,
  nasd_cl_kpdev_eject_obj_dr,
  nasd_cl_kpdev_remove_dr,
  nasd_cl_kpdev_initialize_dr,
  nasd_cl_kpdev_strt_iread_dr,
  nasd_cl_kpdev_stop_iread_dr,
  nasd_cl_kpdev_rshutdown_dr,
  nasd_cl_kpdev_getinfo_dr,

  nasd_cl_kpdev_remote_attach_dr,
  nasd_cl_kpdev_remote_detach_dr,
  nasd_cl_kpdev_remote_invoke_dr,

  nasd_cl_kpdev_create_dr__otw_provided,
  nasd_cl_kpdev_setattr_dr__otw_provided,

  nasd_cl_kpdev_error_string,

  NASD_BIND_KPDEV_DEFAULT_FAKE_RW,
  "Kernel Pseudo-Device Redirect (Default, fake read/write)",
  0,

  NULL
};

nasd_cl_p_rpcmod_tab_t nasd_cl_kpdev_colocate_mod = {
  NULL,
  NASD_RPCMOD_INIT_FAIL,

  nasd_cl_kpdev_bind,
  nasd_cl_kpdev_unbind,

  nasd_cl_kpdev_null_dr,
  nasd_cl_kpdev_sync_dr,
  nasd_cl_kpdev_part_creat_dr,
  nasd_cl_kpdev_getattr_dr,
  nasd_cl_kpdev_write_simple_dr,
  NULL,
  nasd_cl_kpdev_read_simple_dr,
  NULL,
  nasd_cl_kpdev_tread_simple_dr,
  NULL,
  nasd_cl_kpdev_flush_obj_dr,
  nasd_cl_kpdev_eject_obj_dr,
  nasd_cl_kpdev_remove_dr,
  nasd_cl_kpdev_initialize_dr,
  nasd_cl_kpdev_strt_iread_dr,
  nasd_cl_kpdev_stop_iread_dr,
  nasd_cl_kpdev_rshutdown_dr,
  nasd_cl_kpdev_getinfo_dr,

  nasd_cl_kpdev_remote_attach_dr,
  nasd_cl_kpdev_remote_detach_dr,
  nasd_cl_kpdev_remote_invoke_dr,

  nasd_cl_kpdev_create_dr__otw_provided,
  nasd_cl_kpdev_setattr_dr__otw_provided,

  nasd_cl_kpdev_error_string,

  NASD_BIND_COLOCATE,
  "Kernel Pseudo-Device Colocate",
  0,

  NULL
};

void
nasd_cl_p_kpdev_register(
  int  counter_lock_held)
{
  nasd_cl_p_mod_register(&nasd_cl_kpdev_mod, counter_lock_held);
  nasd_cl_p_mod_register(&nasd_cl_kpdev_default_mod, counter_lock_held);
  nasd_cl_p_mod_register(&nasd_cl_kpdev_default_fake_rw_mod, counter_lock_held);
  nasd_cl_p_mod_register(&nasd_cl_kpdev_colocate_mod, counter_lock_held);
}

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