/*
 * nasd_pdrive_client_switch.c
 *
 * Master dispatch for client RPCs. RPC-specific modules
 * plug-in their individual dispatch routines.
 *
 * 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>

#define NASD_CL_P_SWITCH_STANDALONE 0

#include <nasd/nasd_mem.h>

#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_nonce_mgmt.h>
#include <nasd/nasd_control_marshall.h>
#include <nasd/nasd_types_marshall.h>

#if NASD_CL_P_SWITCH_STANDALONE == 0
#include <nasd/nasd_pipe.h>
#include <nasd/nasd_pdev.h>
#include <nasd/nasd_udppipe.h>
#include <nasd/nasd_freelist.h>
#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */

#define NASD_CL_P_NONCE_TABLE_SIZE 256

nasd_cl_p_rpcmod_tab_t *nasd_cl_p_rpcmod_tabs = NULL;
int nasd_cl_p_rpcmod_max_specsize = 1;
struct nasd_drive_handle_s nasd_cl_p_handles;

nasd_drive_default_bindings_t *nasd_drive_default_bindings = NULL;

typedef union nasd_cl_ctrl_pagebuf_u nasd_cl_ctrl_pagebuf_t;
union nasd_cl_ctrl_pagebuf_u {
  nasd_info_page_otw_t     page;
  nasd_cl_ctrl_pagebuf_t  *next;
};

#define NASD_CL_P_NONCE_RETRY_DECL \
  nasd_status_t skew_rc; \
  int skew_try_count;

#define NASD_CL_P_NONCE_RETRY_BEGIN() \
skew_try_count = 0; \
do { \

#define NASD_CL_P_NONCE_RETRY_END() \
  if ((*status) == 0) { \
    if (res->nasd_status == NASD_BAD_NONCE) { \
      skew_rc = nasd_cl_p_update_skew(handle); \
      if (skew_rc) { \
        res->nasd_status = skew_rc; \
        break; \
      } \
    } \
  } \
\
} while((res->nasd_status == NASD_BAD_NONCE) \
    && ((*status) == 0) \
    && (skew_try_count++ <= handle->skew_retries)); \

#define NASD_CL_P_NONCE_RETRY_BEGIN_OTW(_otw_) \
(_otw_)->skew_try_count = 0; \
do { \

#define NASD_CL_P_NONCE_RETRY_END_OTW(_otw_) \
  if ((*status) == 0) { \
    if (res->nasd_status == NASD_BAD_NONCE) { \
      (_otw_)->skew_rc = nasd_cl_p_update_skew(handle); \
      if ((_otw_)->skew_rc) { \
        res->nasd_status = (_otw_)->skew_rc; \
        break; \
      } \
    } \
  } \
\
} while((res->nasd_status == NASD_BAD_NONCE) \
    && ((*status) == 0) \
    && ((_otw_)->skew_try_count++ <= handle->skew_retries)); \

#if NASD_CL_P_SWITCH_STANDALONE == 0

NASD_DECLARE_ONCE(nasd_cl_p_init_once)
NASD_DECLARE_MUTEX(nasd_cl_p_use_counter_lock)
int nasd_cl_p_use_counter = 0;
nasd_shutdown_list_t *nasd_cl_p_shutdown_list = NULL;

#define INIT_COUNTER_LOCK_ONCE() \
  nasd_once(&nasd_cl_p_init_once, nasd_cl_p_sys_init)
#define LOCK_COUNTER_LOCK()   NASD_LOCK_MUTEX(nasd_cl_p_use_counter_lock)
#define UNLOCK_COUNTER_LOCK() NASD_UNLOCK_MUTEX(nasd_cl_p_use_counter_lock)

nasd_freelist_t *nasd_cl_free_pagebuf = NULL;
#define NASD_MAX_FREE_PAGEBUF  2
#define NASD_PAGEBUF_INC       1
#define NASD_PAGEBUF_INITIAL   1

#define GET_CTRL_PAGEBUF(_pb_) \
  NASD_FREELIST_GET(nasd_cl_free_pagebuf,_pb_,next,\
    (nasd_cl_ctrl_pagebuf_t *));

#define PUT_CTRL_PAGEBUF(_pb_) \
  NASD_FREELIST_FREE(nasd_cl_free_pagebuf,_pb_,next);

#define NASD_CL_DRIVE_HANDLE_DELTA_LOCK(_handle_) \
  NASD_LOCK_MUTEX((_handle_)->drive_delta_lock)

#define NASD_CL_DRIVE_HANDLE_DELTA_UNLOCK(_handle_) \
  NASD_UNLOCK_MUTEX((_handle_)->drive_delta_lock)

nasd_sec_nonce_mgr_t *nasd_cl_p_nonce_mgr;

void
nasd_cl_pagebuf_shutdown(
  void  *ignored)
{
  NASD_FREELIST_DESTROY(nasd_cl_free_pagebuf,next,(nasd_cl_ctrl_pagebuf_t *));
}

/*
 * nasd_cl_p_real_init
 *
 * Called when the system comes into use after not being
 * used (start-of-day call to nasd_cl_p_init(), or first
 * such call after the last call to nasd_cl_p_shutdown()
 * which actually deactivated the system).
 */
nasd_status_t
nasd_cl_p_real_init()
{
  nasd_status_t rc;

  /* already did threads_init() */

  rc = nasd_mem_init();
  if (rc) {
    nasd_threads_shutdown();
    return(rc);
  }

  rc = nasd_shutdown_sys_init();
  if (rc) {
    nasd_mem_shutdown();
    nasd_threads_shutdown();
    return(rc);
  }

  rc = nasd_shutdown_list_init(&nasd_cl_p_shutdown_list);
  if (rc) {
    nasd_shutdown_cleanup();
    nasd_mem_shutdown();
    nasd_threads_shutdown();
    return(rc);
  }

  NASD_FREELIST_CREATE(nasd_cl_free_pagebuf, NASD_MAX_FREE_PAGEBUF,
    NASD_PAGEBUF_INC, sizeof(nasd_cl_ctrl_pagebuf_t));
  if (nasd_cl_free_pagebuf == NULL) {
    rc = NASD_NO_MEM;
    goto bad;
  }
  NASD_FREELIST_PRIME(nasd_cl_free_pagebuf, NASD_PAGEBUF_INITIAL,next,
    (nasd_cl_ctrl_pagebuf_t *));
  rc = nasd_shutdown_proc(nasd_cl_p_shutdown_list,
    nasd_cl_pagebuf_shutdown, NULL);
  if (rc) {
    nasd_cl_pagebuf_shutdown(NULL);
    goto bad;
  }

#if NASD_SECURITY_KEEP_STATS > 0 


  rc = nasd_SHA1_init_stats();
  if (rc)
    goto bad;

  rc = nasd_HMAC_init_stats();
  if (rc)
    goto bad;

#endif /* NASD_SECURITY_KEEP_STATS > 0 */

#if NASD_CL_P_SWITCH_STANDALONE == 0
  /* Initialize the nonce subsystem */
  rc = nasd_sec_init_nonce();
  if (rc)
    goto bad;
  rc = nasd_shutdown_proc(nasd_cl_p_shutdown_list, nasd_sec_shutdown_nonce,
                          NULL);
  if(rc) {
    nasd_sec_shutdown_nonce(NULL);
    goto bad;
  }

  rc = nasd_sec_nonce_mgr_init(&nasd_cl_p_nonce_mgr,
    NASD_CL_P_NONCE_TABLE_SIZE);
  if (rc) {
    goto bad;
  }
  rc = nasd_shutdown_proc(nasd_cl_p_shutdown_list,
    nasd_sec_nonce_mgr_shutdown, nasd_cl_p_nonce_mgr);
  if (rc) {
    nasd_sec_nonce_mgr_shutdown(nasd_cl_p_nonce_mgr);
    goto bad;
  }
#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */

  nasd_cl_p_handles.prev = &nasd_cl_p_handles;
  nasd_cl_p_handles.next = &nasd_cl_p_handles;

  rc = nasd_cl_p_mods_init(nasd_cl_p_shutdown_list);
  if (rc)
    goto bad;

  return(NASD_SUCCESS);

bad:
  rc = nasd_shutdown_list_shutdown(nasd_cl_p_shutdown_list,
    NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    NASD_PANIC();
  }
  nasd_shutdown_cleanup();
  nasd_mem_shutdown();
  nasd_threads_shutdown();
  return(rc);
}

/*
 * nasd_cl_p_real_shutdown
 *
 * Called when last user of the cl_p system calls nasd_cl_p_shutdown().
 * Clean up and deallocate resources.
 */
void
nasd_cl_p_real_shutdown()
{
  nasd_status_t rc;

  rc = nasd_shutdown_list_shutdown(nasd_cl_p_shutdown_list,
    NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    NASD_PANIC();
  }
  nasd_cl_p_shutdown_list = NULL;
  nasd_shutdown_cleanup();
  nasd_mem_shutdown();
  nasd_threads_shutdown();
}

/*
 * nasd_cl_p_sys_init
 *
 * Executed exactly once, the first time nasd_cl_p_init() is
 * called. Initialize counter tracking number of times system
 * is initted.
 */
void
nasd_cl_p_sys_init()
{
  nasd_status_t rc;

  rc = nasd_mutex_init(&nasd_cl_p_use_counter_lock);
  if (rc) {
    NASD_PANIC();
  }

  nasd_cl_p_use_counter = 0;

  LOCK_COUNTER_LOCK();
  nasd_drive_default_bindings = NULL;
  nasd_cl_p_mods_load();
  UNLOCK_COUNTER_LOCK();
}

/*
 * nasd_cl_p_init
 *
 * Keep a counter of the number of times we're initted and
 * shutdown. When the last shutdown arrives, really deallocate.
 * This lets multiple subsystems use us without knowing about
 * one another.
 */
nasd_status_t
nasd_cl_p_init()
{
  nasd_status_t rc;

  rc = nasd_threads_init();
  if (rc)
    return(rc);

  INIT_COUNTER_LOCK_ONCE();

  LOCK_COUNTER_LOCK();
  nasd_cl_p_use_counter++;
  if (nasd_cl_p_use_counter == 1) {
    rc = nasd_cl_p_real_init();
    if (rc) {
      nasd_cl_p_use_counter = 0;
    }
  }
  else {
    /* get rid of extra threads init above */
    nasd_threads_shutdown();
    rc = NASD_SUCCESS;
  }
  UNLOCK_COUNTER_LOCK();

  return(rc);
}

/*
 * nasd_cl_p_shutdown
 *
 * Previous caller of nasd_cl_p_init() not using the cl_p
 * subsystem any more. Deallocate and cleanup iff necessary.
 */
void
nasd_cl_p_shutdown()
{
  NASD_ASSERT(nasd_cl_p_use_counter != 0);

  LOCK_COUNTER_LOCK();
  nasd_cl_p_use_counter--;
  if (nasd_cl_p_use_counter == 0) {
    nasd_cl_p_real_shutdown();
  }
  UNLOCK_COUNTER_LOCK();
}

#else /* NASD_CL_P_SWITCH_STANDALONE == 0 */

#undef NASD_Malloc
#undef NASD_Free

#define NASD_Malloc(_p_, _size_, _cast_) { \
  _p_ = _cast_ malloc(_size_); \
}

#define NASD_Free(_p_, _sz_) { \
  free(_p_); \
}

#define INIT_COUNTER_LOCK_ONCE() { /* noop */ }
#define LOCK_COUNTER_LOCK()      { /* noop */ }
#define UNLOCK_COUNTER_LOCK()    { /* noop */ }

#define GET_CTRL_PAGEBUF(_pb_) \
  NASD_Malloc(_pb_,sizeof(nasd_cl_ctrl_pagebuf_t),\
    (nasd_cl_ctrl_pagebuf_t *));

#define PUT_CTRL_PAGEBUF(_pb_) \
  NASD_Free(_pb_,sizeof(nasd_cl_ctrl_pagebuf_t)); \

#define NASD_CL_DRIVE_HANDLE_DELTA_LOCK(_handle_)   { /* noop */ }

#define NASD_CL_DRIVE_HANDLE_DELTA_UNLOCK(_handle_) { /* noop */ }

int nasd_cl_p_use_counter = 0;
int nasd_cl_p_mods_loaded = 0;

nasd_status_t
nasd_cl_p_init()
{
  nasd_status_t rc;

  if (nasd_cl_p_mods_loaded == 0) {
    nasd_drive_default_bindings = NULL;
    nasd_cl_p_mods_load();
    nasd_cl_p_mods_loaded++;
  }

  if (nasd_cl_p_use_counter == 0) {
    nasd_cl_p_handles.prev = &nasd_cl_p_handles;
    nasd_cl_p_handles.next = &nasd_cl_p_handles;

    rc = nasd_cl_p_mods_init(NULL);
    if (rc)
      return(rc);
  }

  nasd_cl_p_use_counter++;

  return(NASD_SUCCESS);
}

void
nasd_cl_p_shutdown()
{
  nasd_cl_p_use_counter--;
}

#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */

void
nasd_cl_p_mod_register(
  nasd_cl_p_rpcmod_tab_t  *tab,
  int                      counter_lock_held)
{
  nasd_cl_p_rpcmod_tab_t *r;

  if (counter_lock_held == 0) {
    INIT_COUNTER_LOCK_ONCE();
    LOCK_COUNTER_LOCK();
  }

  for(r=nasd_cl_p_rpcmod_tabs;r;r=r->next) {
    if (r == tab) {
      nasd_printf("NASD CLIENT: module 0x%lx (%s) already registered!\n",
        (unsigned long)tab, tab->name);
      if (counter_lock_held == 0) {
        UNLOCK_COUNTER_LOCK();
      }
      return;
    }
    if (!strcmp(r->name, tab->name)) {
      nasd_printf("NASD CLIENT: new module 0x%lx has same name as module "
        "0x%lx (%s), rejecting registration\n",
        (unsigned long)tab, (unsigned long)r, r->name);
      if (counter_lock_held == 0) {
        UNLOCK_COUNTER_LOCK();
      }
      return;
    }
    if (r->binding_type == tab->binding_type) {
      nasd_printf("NASD CLIENT: new module 0x%lx (%s) has same binding type"
        " as module 0x%lx (%s) (type %d), rejecting registration\n",
        (unsigned long)tab, tab->name,
        (unsigned long)r, r->name, r->binding_type);
      if (counter_lock_held == 0) {
        UNLOCK_COUNTER_LOCK();
      }
      return;
    }
  }

  tab->next = nasd_cl_p_rpcmod_tabs;
  nasd_cl_p_rpcmod_tabs = tab;

  if (tab->spec_size > nasd_cl_p_rpcmod_max_specsize)
    nasd_cl_p_rpcmod_max_specsize = tab->spec_size;

  if (nasd_cl_p_use_counter) {
    if (r->init) {
      r->init_status = r->init(NULL);
    }
    else {
      r->init_status = NASD_SUCCESS;
    }
  }

  if (counter_lock_held == 0) {
    UNLOCK_COUNTER_LOCK();
  }
}

/*
 * XXX deal with outstanding handles using the
 * mod being unloaded
 */
void
nasd_cl_p_mod_unregister(
  nasd_cl_p_rpcmod_tab_t  *tab,
  int                      counter_lock_held)
{
  nasd_cl_p_rpcmod_tab_t *r, *p;

  if (counter_lock_held == 0) {
    INIT_COUNTER_LOCK_ONCE();
    LOCK_COUNTER_LOCK();
  }

  for(p=NULL,r=nasd_cl_p_rpcmod_tabs;r;p=r,r=r->next) {
    if (r == tab) {
      if (p) {
        p->next = r->next;
      }
      else {
        nasd_cl_p_rpcmod_tabs = r->next;
      }
      r->next = NULL;
      break;
    }
  }

  if (r == NULL) {
    nasd_printf("NASD CLIENT: failed unregistry for module 0x%lx (%s),"
      " could not locate it!\n", (unsigned long)tab, tab->name);
  }

  if (counter_lock_held == 0) {
    UNLOCK_COUNTER_LOCK();
  }
}

/*
 * Caller holds counter lock
 */
nasd_status_t
nasd_cl_p_mods_init(
  nasd_shutdown_list_t  *sl)
{
  nasd_cl_p_rpcmod_tab_t *r;
  nasd_status_t rc;

  rc = NASD_SUCCESS;
  for(r=nasd_cl_p_rpcmod_tabs;r;r=r->next) {
    if (r->init) {
      rc = r->init_status = r->init(sl);
      if (rc) {
        break;
      }
    }
    else {
      rc = r->init_status = NASD_SUCCESS;
    }
  }

  return(rc);
}

static void
nasd_cl_p_handle_free(
  nasd_drive_handle_t  handle)
{
#if NASD_CL_P_SWITCH_STANDALONE == 0
  nasd_mutex_destroy(&handle->drive_delta_lock);
#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */
  NASD_Free(handle->rpc_specific_handle, handle->rpc_specific_handle_size);
  NASD_Free(handle, sizeof(struct nasd_drive_handle_s));
}

nasd_status_t
nasd_bind_to_drive(
  char                 *drive_name,
  char                 *portnum,
  int                   in_binding_type,
  void                 *binding_param,
  int                   binding_param_len,
  nasd_drive_handle_t  *handlep)
{
  nasd_drive_handle_t handle;
  nasd_cl_p_rpcmod_tab_t *r;
  int binding_type, nd;
  nasd_status_t rc;

  INIT_COUNTER_LOCK_ONCE();
  nd = 0;

  if ((in_binding_type == NASD_BIND_DEFAULT) && binding_param_len) {
    return(NASD_BAD_BINDING_PARAM_LEN);
  }

  LOCK_COUNTER_LOCK();
  if (nasd_cl_p_use_counter == 0) {
    return(NASD_SYS_NOT_INITIALIZED);
  }
  nasd_cl_p_use_counter++;
  UNLOCK_COUNTER_LOCK();

#ifndef KERNEL
{
  extern char *getenv();
  char *env;

  /* set this to something like "/dev/nasdkp0" */
  env = getenv("NASD_JIMZ_MONKEY_COLOCATE");
  /*
   * If we've already got a nasd_drive_param_kpdev_t
   * argument, we use it anyway.
   */
  if (env) {
    if (in_binding_type != NASD_BIND_COLOCATE) {
      if (in_binding_type != NASD_BIND_KPDEV)
      {
        static nasd_drive_param_kpdev_t kpdev_args;
        strcpy(kpdev_args.devname, env);
        binding_param = &kpdev_args;
        binding_param_len = sizeof(kpdev_args);
      }
      in_binding_type = NASD_BIND_COLOCATE;
    }
  }
}
#endif /* !KERNEL */

  if (in_binding_type == NASD_BIND_DEFAULT) {
    if ((nasd_drive_default_bindings == NULL) ||
      (nasd_drive_default_bindings->nbindings == 0))
    {
      nasd_cl_p_shutdown();
      return(NASD_NO_DEFAULT_BINDING);
    }
    nd = 0;
  }

  *handlep = NULL;

  NASD_Malloc(handle, sizeof(struct nasd_drive_handle_s),
    (nasd_drive_handle_t));
  if (handle == NULL) {
    nasd_cl_p_shutdown();
    return(NASD_NO_MEM);
  }
  bzero((char *)handle, sizeof(struct nasd_drive_handle_s));

#if NASD_CL_P_SWITCH_STANDALONE == 0
  rc = nasd_mutex_init(&handle->drive_delta_lock);
  if (rc) {
    NASD_Free(handle, sizeof(struct nasd_drive_handle_s));
    UNLOCK_COUNTER_LOCK();
    nasd_cl_p_shutdown();
    return(rc);
  }
#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */

  LOCK_COUNTER_LOCK();

  NASD_Malloc(handle->rpc_specific_handle, nasd_cl_p_rpcmod_max_specsize,
    (void *));
  if (handle->rpc_specific_handle == NULL) {
#if NASD_CL_P_SWITCH_STANDALONE == 0
    nasd_mutex_destroy(&handle->drive_delta_lock);
#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */
    NASD_Free(handle, sizeof(struct nasd_drive_handle_s));
    UNLOCK_COUNTER_LOCK();
    nasd_cl_p_shutdown();
    return(NASD_NO_MEM);
  }
  handle->skew_retries = 1;
  handle->rpc_specific_handle_size = nasd_cl_p_rpcmod_max_specsize;
  bzero(handle->rpc_specific_handle, handle->rpc_specific_handle_size);

begin_binding_try:

  if (in_binding_type == NASD_BIND_DEFAULT) {
    if (nd >= nasd_drive_default_bindings->nbindings) {
      /* no more defaults to try */
      UNLOCK_COUNTER_LOCK();
      nasd_cl_p_handle_free(handle);
      nasd_cl_p_shutdown();
      /*
       * Return error from last binding try- more informative than
       * NASD_FAIL or somesuch, at least.
       */
      return(rc);
    }
    binding_type = nasd_drive_default_bindings->bindings[nd];
  }
  else {
    binding_type = in_binding_type;
  }

  for(r=nasd_cl_p_rpcmod_tabs;r;r=r->next) {
    if (r->binding_type == binding_type)
      break;
  }

  handle->rpc_tab = r;
  handle->type = binding_type;

  if (handle->rpc_tab) {
    if (handle->rpc_tab->init_status != NASD_SUCCESS) {
      rc = NASD_RPCMOD_INIT_FAIL;
    }
    else if (handle->rpc_tab->getinfo == NULL) {
      /* getinfo support mandatory for clock sync */
      rc = NASD_RPCMOD_NO_GETINFO;
    }
    else {
      rc = handle->rpc_tab->bind(handle, drive_name, portnum, binding_type,
        binding_param, binding_param_len);
    }
    if (rc == NASD_SUCCESS) {
      handle->next = nasd_cl_p_handles.next;
      handle->prev = &nasd_cl_p_handles;
      handle->prev->next = handle;
      handle->next->prev = handle;
    }
  }
  else {
    rc = NASD_BAD_HANDLE_TYPE;
  }

  if (rc && (in_binding_type == NASD_BIND_DEFAULT)) {
    /* retry next default type */
    nd++;
    goto begin_binding_try;
  }

  UNLOCK_COUNTER_LOCK();

  if (rc == NASD_SUCCESS) {
    /*
     * Go ahead and get clock synchronization info
     */
    rc = nasd_cl_p_update_skew(handle);
  }

  if (rc) {
    nasd_cl_p_handle_free(handle);
    nasd_cl_p_shutdown();
    return(rc);
  }

#if NASD_CL_P_SWITCH_STANDALONE == 0
  handle->nonce_mgr = nasd_cl_p_nonce_mgr;
#endif /* NASD_CL_P_SWITCH_STANDALONE == 0 */
  *handlep = handle;

  return(NASD_SUCCESS);
}

nasd_status_t
nasd_unbind_drive(
  nasd_drive_handle_t  *handlep)
{
  nasd_drive_handle_t handle;
  nasd_status_t rc;

  INIT_COUNTER_LOCK_ONCE();

  handle = *handlep;

  *handlep = NULL;

  if (handle == NULL) {
    return(NASD_BAD_HANDLE);
  }

  if (handle->rpc_tab) {
    rc = handle->rpc_tab->unbind(handle);
  }
  else {
    rc = NASD_SUCCESS;
  }

  LOCK_COUNTER_LOCK();
  handle->prev->next = handle->next;
  handle->next->prev = handle->prev;
  UNLOCK_COUNTER_LOCK();

  nasd_cl_p_handle_free(handle);

  nasd_cl_p_shutdown();

  return(rc);
}

/*
 * RPC WRAPPERS
 */

void
nasd_cl_p_null_dr(
  nasd_drive_handle_t   handle,
  nasd_status_t        *nasd_status,
  nasd_rpc_status_t    *status)
{
  nasd_res_t real_res, *res;

  res = &real_res;
  if (handle == NULL) {
    *nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    *nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->null == NULL) {
    *nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  handle->rpc_tab->null(handle, res, status);
  *nasd_status = res->nasd_status;
}

void
nasd_cl_p_sync_dr(
  nasd_drive_handle_t   handle,
  nasd_status_t        *nasd_status,
  nasd_rpc_status_t    *status)
{
  nasd_res_t real_res, *res;

  res = &real_res;

  if (handle == NULL) {
    *nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    *nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->sync == NULL) {
    *nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  handle->rpc_tab->sync(handle, res, status);
  *nasd_status = res->nasd_status;
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->part_creat == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->part_creat(handle, in_key, sec_param, capability,
                              args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_create_dr(
  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_rpc_status_t        *status)
{
  nasd_cl_p_otw_buf_t otw;
  nasd_p_create_dr_args_otw_t args_otw;
  nasd_p_create_dr_res_otw_t res_otw;

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->create_otw_provided == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  otw.args_otw = args_otw;
  otw.res_otw = res_otw;

  NASD_CL_P_NONCE_RETRY_BEGIN_OTW(&otw);

  handle->rpc_tab->create_otw_provided(handle, in_key, sec_param, capability,
                                       args, res, &otw, status);

  NASD_CL_P_NONCE_RETRY_END_OTW(&otw);
}

void
nasd_cl_p_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)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->create_otw_provided == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN_OTW(otw);

  handle->rpc_tab->create_otw_provided(handle, in_key, sec_param, capability,
    args, res, otw, status);

  NASD_CL_P_NONCE_RETRY_END_OTW(otw);
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->getattr == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->getattr(handle, in_key, sec_param, capability,
    args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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_mem_list_t list_elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->write_simple == NULL) {
    if (handle->rpc_tab->range_write) {
      list_elem.addr = buf;
      list_elem.len = args->in_len;
      list_elem.stride = 0;
      list_elem.nelem = 1;
      list_elem.next = NULL;

      NASD_CL_P_NONCE_RETRY_BEGIN();

      handle->rpc_tab->range_write(handle, in_key, sec_param, capability,
        args, &list_elem, res, status);

      NASD_CL_P_NONCE_RETRY_END();

      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->write_simple(handle, in_key, sec_param, capability, args,
    buf, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_read_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_fastread_dr_res_t   *res,
  nasd_rpc_status_t          *status)
{
  nasd_mem_list_t list_elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->read_simple == NULL) {
    if (handle->rpc_tab->range_read) {
      list_elem.addr = buf;
      list_elem.len = args->in_len;
      list_elem.stride = 0;
      list_elem.nelem = 1;
      list_elem.next = NULL;

      NASD_CL_P_NONCE_RETRY_BEGIN();

      handle->rpc_tab->range_read(handle, in_key, sec_param, capability,
        0, args, &list_elem, res, status);

      NASD_CL_P_NONCE_RETRY_END();

      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->read_simple(handle, in_key, sec_param, capability, 0, args,
    buf, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_read2_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_fastread_dr_res_t   *res,
  nasd_rpc_status_t          *status)
{
  nasd_mem_list_t list_elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->read_simple == NULL) {
    if (handle->rpc_tab->range_read) {
      list_elem.addr = buf;
      list_elem.len = args->in_len;
      list_elem.stride = 0;
      list_elem.nelem = 1;
      list_elem.next = NULL;

      NASD_CL_P_NONCE_RETRY_BEGIN();

      handle->rpc_tab->range_read(handle, in_key, sec_param, capability,
        1, args, &list_elem, res, status);

      NASD_CL_P_NONCE_RETRY_END();

      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->read_simple(handle, in_key, sec_param, capability, 1, args,
    buf, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_tread_simple_dr(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  nasd_p_thrtl_op_dr_args_t  *args,
  void                       *buf,
  nasd_p_fastread_dr_res_t   *res,
  nasd_rpc_status_t          *status)
{
  nasd_mem_list_t list_elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->read_simple == NULL) {
    if (handle->rpc_tab->range_read) {
      list_elem.addr = buf;
      list_elem.len = args->in_len;
      list_elem.stride = 0;
      list_elem.nelem = 1;
      list_elem.next = NULL;

      NASD_CL_P_NONCE_RETRY_BEGIN();

      handle->rpc_tab->range_tread(handle, in_key, sec_param, capability,
        0, args, &list_elem, res, status);

      NASD_CL_P_NONCE_RETRY_END();

      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->tread_simple(handle, in_key, sec_param, capability, 1, args,
    buf, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_range_read_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,
  nasd_mem_list_t            *memlist,
  nasd_p_fastread_dr_res_t   *res,
  nasd_rpc_status_t          *status)
{
  nasd_p_smpl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_len_t len, total_len;
  nasd_offset_t offset;
  nasd_mem_list_t *m;
  nasd_byte_t *addr;
  int elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->range_read == NULL) {
    if (handle->rpc_tab->read_simple) {
      offset = args->in_offset;
      total_len = 0;
      res->out_datalen = 0;
      for(m=memlist;m;m=m->next) {
        addr = m->addr;
        for(elem=0;elem<m->nelem;elem++) {
          bcopy((char *)args, (char *)&rd_args, sizeof(rd_args));
          rd_args.in_offset = offset;
          rd_args.in_len = m->len;
          NASD_CL_P_NONCE_RETRY_BEGIN();
          handle->rpc_tab->read_simple(handle, in_key, sec_param, capability,
            0, &rd_args, addr, &rd_res, status);
          NASD_CL_P_NONCE_RETRY_END();
          len = rd_res.out_datalen;
          res->nasd_status = rd_res.nasd_status;
          if (res->nasd_status || *status)
            return;
          total_len += len;
          res->out_datalen = total_len;
          if (len < m->len)
            return;
          addr += m->stride;
          offset += len;
        }
      }
      res->nasd_status = NASD_SUCCESS;
      *status = 0;
      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->range_read(handle, in_key, sec_param, capability, 0, args,
    memlist, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_range_tread_dr(
  nasd_drive_handle_t          handle,
  nasd_key_t                   in_key,
  nasd_security_param_t       *sec_param,
  nasd_capability_t           *capability,
  nasd_p_thrtl_op_dr_args_t   *args,
  nasd_mem_list_t             *memlist,
  nasd_p_fastread_dr_res_t    *res,
  nasd_rpc_status_t           *status)
{
  nasd_p_thrtl_op_dr_args_t rd_args;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_len_t len, total_len;
  nasd_offset_t offset;
  nasd_mem_list_t *m;
  nasd_byte_t *addr;
  int elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->range_tread == NULL) {
    if (handle->rpc_tab->tread_simple) {
      offset = args->in_offset;
      total_len = 0;
      res->out_datalen = 0;
      for(m=memlist;m;m=m->next) {
        addr = m->addr;
        for(elem=0;elem<m->nelem;elem++) {
          bcopy((char *)args, (char *)&rd_args, sizeof(rd_args));
          rd_args.in_offset = offset;
          rd_args.in_len = m->len;
          NASD_CL_P_NONCE_RETRY_BEGIN();
          handle->rpc_tab->tread_simple(handle, in_key, sec_param, capability,
            0, &rd_args, addr, &rd_res, status);
          NASD_CL_P_NONCE_RETRY_END();
          len = rd_res.out_datalen;
          res->nasd_status = rd_res.nasd_status;
          if (res->nasd_status || *status)
            return;
          total_len += len;
          res->out_datalen = total_len;
          if (len < m->len)
            return;
          addr += m->stride;
          offset += len;
        }
      }
      res->nasd_status = NASD_SUCCESS;
      *status = 0;
      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->range_tread(handle, in_key, sec_param, capability, 0, args,
    memlist, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_range_write_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,
  nasd_mem_list_t             *memlist,
  nasd_p_fastwrite_dr_res_t   *res,
  nasd_rpc_status_t           *status)
{
  nasd_p_smpl_op_dr_args_t wr_args;
  nasd_p_fastwrite_dr_res_t wr_res;
  nasd_len_t len, total_len;
  nasd_offset_t offset;
  nasd_mem_list_t *m;
  nasd_byte_t *addr;
  int elem;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->range_write == NULL) {
    if (handle->rpc_tab->write_simple) {
      offset = args->in_offset;
      total_len = 0;
      res->out_datalen = 0;
      for(m=memlist;m;m=m->next) {
        addr = m->addr;
        for(elem=0;elem<m->nelem;elem++) {
          bcopy((char *)args, (char *)&wr_args, sizeof(wr_args));
          wr_args.in_offset = offset;
          wr_args.in_len = m->len;
          NASD_CL_P_NONCE_RETRY_BEGIN();
          handle->rpc_tab->write_simple(handle, in_key, sec_param,
            capability, &wr_args, addr, &wr_res, status);
          NASD_CL_P_NONCE_RETRY_END();
          len = wr_res.out_datalen;
          res->nasd_status = wr_res.nasd_status;
          if (res->nasd_status || *status)
            return;
          total_len += len;
          res->out_datalen = total_len;
          if (len < m->len)
            return;
          addr += m->stride;
          offset += len;
        }
      }
      res->nasd_status = NASD_SUCCESS;
      *status = 0;
      return;
    }
    else {
      res->nasd_status = NASD_BINDING_NOIMPL;
      *status = 0;
      return;
    }
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->range_write(handle, in_key, sec_param, capability, args,
    memlist, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_setattr_dr(
  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_rpc_status_t         *status)
{
  nasd_cl_p_otw_buf_t otw;
  nasd_p_setattr_dr_args_otw_t args_otw;
  nasd_p_setattr_dr_res_otw_t res_otw;
  NASD_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->setattr_otw_provided == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  otw.args_otw = args_otw;
  otw.res_otw = res_otw;

  NASD_CL_P_NONCE_RETRY_BEGIN_OTW(&otw);

  handle->rpc_tab->setattr_otw_provided(handle, in_key, sec_param,
    capability, args, res, &otw, status);

  NASD_CL_P_NONCE_RETRY_END_OTW(&otw);
}

void
nasd_cl_p_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)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->setattr_otw_provided == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN_OTW(otw);

  handle->rpc_tab->setattr_otw_provided(handle, in_key, sec_param,
    capability, args, res, otw, status);

  NASD_CL_P_NONCE_RETRY_END_OTW(otw);
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->flush_obj == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->flush_obj(handle, in_key, sec_param, capability,
                             args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->eject_obj == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->eject_obj(handle, in_key, sec_param, capability,
                             args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->remove == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->remove(handle, in_key, sec_param, capability,
                          args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->initialize == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  handle->rpc_tab->initialize(handle, args, res, status);
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->strt_iread == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->strt_iread(handle, in_key, sec_param, capability,
                              args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->stop_iread == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->stop_iread(handle, in_key, sec_param, capability,
                              args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL

  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->rshutdown == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  NASD_CL_P_NONCE_RETRY_BEGIN();

  handle->rpc_tab->rshutdown(handle, in_key, sec_param, capability,
                             args, res, status);

  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_getinfo_dr(
  nasd_drive_handle_t       handle, 
  nasd_p_getinfo_dr_res_t  *res, 
  nasd_rpc_status_t        *status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->getinfo == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }

  handle->rpc_tab->getinfo(handle, res, status);
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL
    
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->getinfo == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }
  NASD_CL_P_NONCE_RETRY_BEGIN();
  handle->rpc_tab->remote_attach(handle, in_key, sec_param, capability, args, buf, res, status);
  NASD_CL_P_NONCE_RETRY_END();
}


void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL
    
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->getinfo == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }
  NASD_CL_P_NONCE_RETRY_BEGIN();
  handle->rpc_tab->remote_detach(handle, in_key, sec_param, capability, args, res, status);
  NASD_CL_P_NONCE_RETRY_END();
}

void
nasd_cl_p_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_CL_P_NONCE_RETRY_DECL
    
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *status = 0;
    return;
  }
  if (handle->rpc_tab->getinfo == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *status = 0;
    return;
  }
  NASD_CL_P_NONCE_RETRY_BEGIN();
  handle->rpc_tab->remote_invoke(handle, in_key, sec_param, capability, args, buf, res, status);
  NASD_CL_P_NONCE_RETRY_END();
}





/*
 * Not properly an RPC wrapper. This gets called internally by the
 * client library to update its notion of the delta between its clock
 * and the drive's clock.
 */
nasd_status_t
nasd_cl_p_update_skew(
  nasd_drive_handle_t  handle)
{
  nasd_p_getinfo_dr_res_t res;
  nasd_rpc_status_t status;
  nasd_timespec_t cur_time;

  nasd_gettime(&cur_time);
  nasd_cl_p_getinfo_dr(handle, &res, &status);
  if (status == 999)
    return(NASD_OFFLINE);
  if (status)
    return(NASD_RPC_FAILURE);
  if (res.nasd_status)
    return(res.nasd_status);

  NASD_CL_DRIVE_HANDLE_DELTA_LOCK(handle);
  if (NASD_TIMESPEC_GE(res.info.cur_time, cur_time)) {
    handle->drive_is_ahead = 1;
    handle->drive_delta = res.info.cur_time;
    NASD_TIMESPEC_SUB(handle->drive_delta, cur_time);
  }
  else {
    handle->drive_is_ahead = 0;
    handle->drive_delta = cur_time;
    NASD_TIMESPEC_SUB(handle->drive_delta, res.info.cur_time);
  }
  NASD_CL_DRIVE_HANDLE_DELTA_UNLOCK(handle);

  return(NASD_SUCCESS);
}

/*
 * Given a drive handle, get the anticipated current time
 * on the drive.
 */
void
nasd_drive_handle_get_time(
  nasd_drive_handle_t   handle,
  nasd_timespec_t      *tsp)
{
  NASD_CL_DRIVE_HANDLE_DELTA_LOCK(handle);

  nasd_gettime(tsp);

  if (handle->drive_is_ahead) {
    NASD_TIMESPEC_ADD(*tsp, handle->drive_delta);
  }
  else {
    NASD_TIMESPEC_SUB(*tsp, handle->drive_delta);
  }

  NASD_CL_DRIVE_HANDLE_DELTA_UNLOCK(handle);
}


/*
 * END RPC WRAPPERS
 *
 * BEGIN CONTROL WRAPPERS
 */

/*
 * Read or write an info page
 */
nasd_status_t
nasd_cl_p_ctrl_rdwr_stats(
  nasd_drive_handle_t      handle,
  int                      partnum,
  nasd_identifier_t        nasdid,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  void                    *info,
  nasd_offset_t            offset,
  int                      read)
{
  nasd_p_smpl_op_dr_args_t op_args;
  nasd_p_fastwrite_dr_res_t wr_res;
  nasd_p_fastread_dr_res_t rd_res;
  nasd_rpc_status_t status;
  nasd_identifier_t nid;
  nasd_len_t out_len;
  nasd_status_t rc;

  if (read) {
    op_args.in_partnum = partnum;
    op_args.in_identifier = nasdid;
    op_args.in_offset = offset;
    op_args.in_len = NASD_INFO_PAGESIZE;
    nasd_cl_p_read2_simple_dr(handle, in_key, sec_param, capability,
                              &op_args, (void *)info, &rd_res, &status);
    out_len = rd_res.out_datalen;
    rc = rd_res.nasd_status;
  }
  else {
    op_args.in_partnum = partnum;
    op_args.in_identifier = nasdid;
    op_args.in_offset = offset;
    op_args.in_len = NASD_INFO_PAGESIZE;
    nasd_cl_p_write_simple_dr(handle, in_key, sec_param, capability,
                              &op_args, (void *)info, &wr_res, &status);
    out_len = wr_res.out_datalen;
    rc = wr_res.nasd_status;
  }
  if (status)
    rc = NASD_RPC_TRAP;
  else {
    if (rc == NASD_SUCCESS) {
      /* The ctrl_id field */
      nasd_identifier_t_unmarshall(info, &nid);
      if (out_len != NASD_INFO_PAGESIZE)
        rc = NASD_CTRL_IO_BAD_LEN;
      else if (read && (nid != nasdid))
        rc = NASD_CTRL_ID_CHECK_FAILED;
    }
  }
  return(rc);
}

/*
 * Retrieve drive info struct
 */
nasd_status_t
nasd_cl_p_ctrl_get_drive_info(
  nasd_drive_handle_t      handle,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_drive_info_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 0, 1);
  nasd_ctrl_drive_info_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

/*
 * Retrieve partition info struct
 */
nasd_status_t
nasd_cl_p_ctrl_get_part_info(
  nasd_drive_handle_t      handle,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  int                      partnum,
  nasd_ctrl_part_info_t   *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, partnum, NASD_CTRL_PART_INFO,
    in_key, sec_param, capability, pb->page, 0, 1);
  nasd_ctrl_part_info_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_op_stats(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  nasd_ctrl_drive_opstats_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 8192, 1);
  nasd_ctrl_drive_opstats_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}


nasd_status_t
nasd_cl_p_ctrl_put_drive_op_stats(
  nasd_drive_handle_t         handle,
  nasd_key_t                  in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t          *capability,
  nasd_ctrl_drive_opstats_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_drive_opstats_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 8192, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}


nasd_status_t
nasd_cl_p_ctrl_get_drive_cache_stats(
  nasd_drive_handle_t      handle,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_cache_stat_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 16384, 1);
  nasd_ctrl_cache_stat_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_put_drive_cache_stats(
  nasd_drive_handle_t      handle,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_cache_stat_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_cache_stat_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 16384, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_io_stats(
  nasd_drive_handle_t      handle,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_io_stat_t     *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 24576, 1);
  nasd_ctrl_io_stat_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_put_drive_io_stats(
  nasd_drive_handle_t      handle,
  nasd_key_t               in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_io_stat_t     *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_io_stat_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 24576, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_io_read_size_stats(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_io_size_stat_t     *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 32768, 1);
  nasd_ctrl_io_size_stat_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_put_drive_io_read_size_stats(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_io_size_stat_t     *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_io_size_stat_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 32768, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_io_write_size_stats(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_io_size_stat_t     *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 40960, 1);
  nasd_ctrl_io_size_stat_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_put_drive_io_write_size_stats(
  nasd_drive_handle_t           handle,
  nasd_key_t                    in_key,
  nasd_security_param_t        *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_io_size_stat_t     *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_io_size_stat_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 40960, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_layout_stats(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_layout_stat_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 49152, 1);
  nasd_ctrl_layout_stat_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}


nasd_status_t
nasd_cl_p_ctrl_put_drive_layout_stats(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
    nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_layout_stat_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_layout_stat_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 49152, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}


nasd_status_t
nasd_cl_p_ctrl_get_drive_ioqueue_stats(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_ioqueue_stat_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 57344, 1);
  nasd_ctrl_ioqueue_stat_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}


nasd_status_t
nasd_cl_p_ctrl_put_drive_ioqueue_stats(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t     *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_ioqueue_stat_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_ioqueue_stat_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 57344, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_iread_cfg(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_index_read_cfg_t *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 65536, 1);
  nasd_ctrl_index_read_cfg_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}


nasd_status_t
nasd_cl_p_ctrl_put_drive_iread_cfg(
  nasd_drive_handle_t        handle,
  nasd_key_t                 in_key,
  nasd_security_param_t      *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_index_read_cfg_t *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_index_read_cfg_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_DRIVE_INFO,
    in_key, sec_param, capability, pb->page, 65536, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_get_drive_trace_basic(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_trace_basic_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_TRACE_INFO,
    in_key, sec_param, capability, pb->page, 0, 1);
  nasd_ctrl_trace_basic_t_unmarshall(pb->page, info);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

nasd_status_t
nasd_cl_p_ctrl_put_drive_trace_basic(
  nasd_drive_handle_t       handle,
  nasd_key_t                in_key,
  nasd_security_param_t   *sec_param,
  nasd_capability_t       *capability,
  nasd_ctrl_trace_basic_t  *info)
{
  nasd_cl_ctrl_pagebuf_t *pb;
  nasd_status_t rc;

  GET_CTRL_PAGEBUF(pb);
  if (pb == NULL)
    return(NASD_NO_MEM);

  nasd_ctrl_trace_basic_t_marshall(info, pb->page);
  rc = nasd_cl_p_ctrl_rdwr_stats(handle, 0, NASD_CTRL_TRACE_INFO,
    in_key, sec_param, capability, pb->page, 0, 0);

  PUT_CTRL_PAGEBUF(pb);

  return(rc);
}

/*
 * END CONTROL WRAPPERS
 *
 * BEGIN ERROR WRAPPERS
 */

char *
_nasd_cl_error_string(
  nasd_drive_handle_t   handle,
  nasd_rpc_status_t     status,
  nasd_error_string_t   str,
  char                 *in_file,
  int                   line)
{
  char *file;
  int len;

  /*
   * Be paranoid about fitting filename in string. Truncate to 80
   * characters. Use the last 80 characters instead of the first
   * to try to be maximally informitive.
   */
  len = strlen(in_file);
  if (len > 80) {
    file = &in_file[len-80];
  }
  else {
    file = in_file;
  }

  if (handle == NULL) {
    sprintf(str,
      "NULL handle passed to nasd_cl_error_string file [%s] line %d",
      file, line);
  }
  else if (status == 999) {
    /*
     * !!! Nasty hack. See nasd_od_rpc.h for this
     * in action.
     */
    strcpy(str, "Drive offline");
  }
  else {
    if (handle->rpc_tab == NULL) {
      sprintf(str,
        "Invalidated handle passed to nasd_cl_error_string file [%s] line %d",
        file, line);
    }
    else {
      handle->rpc_tab->error_string(handle, status, str, file, line);
    }
  }

  return(str);
}

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