OpenSolaris_b135/cmd/smserverd/smediad.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdio_ext.h>
#include <errno.h>
#include <ctype.h>
#include <syslog.h>
#include <signal.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <fcntl.h>
#include <sys/scsi/scsi.h>
#include <sys/scsi/generic/commands.h>
#include <string.h>
#include <door.h>
#include <pwd.h>
#include <thread.h>
#include <synch.h>
#include <pthread.h>
#include <locale.h>
#include <sys/resource.h>
#include <netconfig.h>
#include <sys/smedia.h>
#include "smserver.h"
#include <rpc/rpc.h>
#include "smed.h"
#include "myaudit.h"
#include <bsm/libbsm.h>
#include <bsm/audit_uevents.h>
#include <utmpx.h>


/*
 * The comments below would help in understanding what is being attempted
 * in the server.
 *
 * The server can be started either by inetd or by the client directly.
 * Normally the server is started by inetd when the client invokes the
 * appropriate libsmedia library call(smedia_get_handle).
 * However since the inetd runs only at init level 2 and above a mechanism
 * is provided for the server to be started if an attempt is made to use
 * the libsmedia calls in maintenence mode(init level 1).
 * The main() routine determines how the server was invoked and takes
 * the necessary action.
 * When started by inetd it registers itself as an RPC program.
 * The server also implements a mechanism by which it removes itself
 * after a period of inactivity. The period of inactivity is specified
 * by SVC_CLOSEDOWN which is set at 180 secs.
 * The logic of detecting inactivity is as follows:
 *
 * Two variables svcstate and svccount are used to determine if the server
 * is IDLE.
 * The svcstate is set to 1(_SERVED) when ever the server does any operation
 * on behalf of the client.
 * The svccount indicates the number of active clients who have established
 * a connection with the server. A connection is established when the
 * libsmedia call smedia_get_handle() succeeds.
 * The connection is broken when the client calls smedia_free_handle() OR
 * exits.
 * A thread called closedown is started up when server is started.
 * This thread runs periodically and monitors both svcstate and svccount.
 * If svcstate is IDLE and svccount is 0 then server exits.
 * The svcstate is set to IDLE by the closedown thread. It is set to _SERVED
 * by server. It is possible for the state to be _SERVED and the svccount
 * to be 0. The server could be kept busy by client calls of smedia_get_handle
 * that do not succeed. This is the reason for using both svcstate and svccount
 * to determine the true server state.
 *
 * The communication between client and server is thru door calls.
 * Below are the door descriptors available to communicate to the server.
 *
 * main_door_descriptor:
 * ---------------------
 * 	This is a predefined descriptor used by client to establish a
 * connection with the server. This descriptor is available to the client
 * as /var/adm/smedia_svc
 * The client uses the main_door_descriptor to obtain a dedicated
 * client_door_descriptor for itself. The smedia_get_handle call communicates
 * to the server using the main_door_descriptor and obtains the
 * client_door_descriptor which is stored in the handle structure.
 * All other libsmedia calls use the client_door_descriptor to communicate
 * with the server.
 *
 * client_door_descriptor:
 * -----------------------
 *	This is the door descriptor that is used by the clients to
 * request server to perform the necessary tasks. This door descriptor is
 * available only to the client for whom it was created.
 *
 * death_door_descriptor:
 * ----------------------
 * 	The sole function of this descriptor HAD been to inform the server of
 * the untimely death of the client. This descriptor is no longer used, though
 * it is still created, as libsmedia expects to use it.  This descriptor's
 * service procedure had used pthread cancellation(5) to terminate the thread of
 * the associated client_door_descriptor.  The client_door_descriptor now
 * handles the scenarios where a door_call/client are aborted/terminated.
 *
 * main_servproc()
 * -------------
 *	This is the routine associated with the main_door_descriptor.
 * This is the routine that handles the smedia_get_handle() call
 * of the client. If the door call to this routine succeeds it creates a
 * client_door_descriptor that is used by the client in subsequent library
 * calls.
 * This client_door_descriptor is passed to the client thru the door_return
 * call. This client_door_descriptor cannot be used by any other process other
 * than the client process that obtained it.
 * In addition to the client_door_descriptor a death_door_descriptor is also
 * created by the main server and passed on to the client. The client does not
 * use the death_door_descriptor.
 *
 * client_servproc()
 * ---------------
 *	This is the routine that handles the libsmedia calls of the
 * client. In the current implementation the server takes control of the
 * number of threads that handle the door calls. This is done by creating the
 * door descriptor as DOOR_PRIVATE.
 * The server runs only one thread per handle. This makes the implementation
 * simple as we do not have to use mutex to make the code MT safe.
 * The server thread has a data structure door_data_t associated with it.
 *
 * door_data_t
 * -----------
 * This is the data structure that is created by the main_servproc when it
 * creates the client_door_descriptor. The door mechanism has a way to associate
 * a cookie with the door descriptor. door_data_t is the cookie for the
 * client_door_descriptor. This cookie is passed to the server function that
 * handles the client_door_descriptor calls. In our case it is the
 * client_servproc routine.
 * The key elements of the door_data_t are the following:
 *
 *	dd_fd		file descriptor for the device.
 *	dd_buf		The shared memory buffer between client-server.
 *	dd_thread	The thread that handles the door_calls.
 *
 * signal handling:
 * ----------------
 *		The main purpose of trapping the signals is to exit gracefully
 * from the server after recording the appropriate message in the syslog.
 * This will help the administrator to determine the cause of failure of the
 * server by examining the log file.
 *
 * cleanup()
 * ---------
 *	This routine frees up all the resources allocated for the client.
 * Resources include the file descriptor, shared memory, threads.
 *
 * shared memory
 * -------------
 *	In order to reduce the overheads of moving large amounts of data
 * during raw read/write operations, the server uses the mmapped data of
 * client. The smedia_raw_read, smedia_raw_write library calls mmap the
 * memory and pass on the file descriptor that maps the memory to the server.
 * The server subsequently uses this mmapped memory during the IO.
 * If the mmapped memory changes in size, the server is informed and it
 * remaps the memory to the changed size.
 */
#ifdef DEBUG
#define	DEFAULT_VERBOSE		1
#define	DEFAULT_DEBUG		1
#else
#define	DEFAULT_VERBOSE		0
#define	DEFAULT_DEBUG		0
#endif

#define	N_BADSIGS		(sizeof (badsigs)/sizeof (badsigs[0]))
#define	MD_LEN			30
#define	MAXUGNAME		10
#define	SVC_CLOSEDOWN 		180

/*
 * We will NOT be permitting the following USCI cmd options.
 *
 * RESET of target
 * RESET of  Bus.
 * Tagged commands to device
 * Explicitly setting SYNC/ASYNC mode of operations.
 * POLLED MODE of operation.
 * Explicitly setting NO DISCONNECT features.
 * use of RESERVED flags.
 */
#define	FORBIDDEN_FLAGS		(USCSI_RESET | USCSI_RESET_ALL | USCSI_RENEGOT \
				| USCSI_ASYNC  | USCSI_SYNC | USCSI_NOINTR | \
				USCSI_NOTAG | USCSI_NOPARITY | USCSI_NODISCON \
				| USCSI_RESERVED)

/* States a server can be in wrt request */

#define	_IDLE 0
#define	_SERVED 1

static char		*prog_name;
static int svcstate = _IDLE;	/* Set when a request is serviced */
static int svccount = 0;	/* Number of requests being serviced */
static int svcstart_level = 0;	/* init level when server was started */
static mutex_t svcstate_lock;	/* lock for svcstate, svccount */

extern	void smserverprog_1(struct svc_req *, SVCXPRT *);

/*
 * Log messages
 */
#define	SIGACT_FAILED	"Failed to install signal handler for %s: %s"
#define	BADSIG_MSG	"Thread %d Caught signal %d addr=%p trapno=%d pc=%p"

static int	badsigs[] = {SIGSEGV, SIGBUS, SIGFPE, SIGILL};

/* global variables */
int		verbose		= DEFAULT_VERBOSE;
int		debug_level	= DEFAULT_DEBUG;
char		*smediad_devdir = DEFAULT_SMEDIAD_DEVDIR;

thread_key_t	door_key;

server_data_t	server_data;

static int	server_door, server_fd;

static int32_t do_uscsi_cmd(int32_t file, struct uscsi_cmd *uscsi_cmd,
		int32_t flag);
static void client_servproc(void *cookie, char *argp, size_t arg_size,
		door_desc_t *dp, uint_t ndesc);
static void cleanup(door_data_t *);
static void *init_server(void *);
static int32_t scsi_reassign_block(int32_t fd, diskaddr_t);
static int32_t get_mode_page(int32_t fd, uchar_t pc, uchar_t page_code,
	uchar_t *md_data, uchar_t data_len);
static int32_t get_device_type(char *v_name);
static int32_t get_device_type_scsi(int32_t fd, struct scsi_inquiry *inq);

static int32_t scsi_format(int32_t fd, uint_t flavor, uint_t mode);
static int32_t scsi_media_status(int32_t fd);
static int32_t scsi_write_protect(int32_t fd, smwp_state_t *wp);
static int32_t scsi_floppy_media_status(int32_t fd);
static int32_t scsi_floppy_write_protect(int32_t fd, smwp_state_t *wp);
static int32_t scsi_floppy_format(int32_t, uint_t, uint_t);
static int32_t get_floppy_geom(int32_t fd, uint32_t capacity,
			struct dk_geom *dkgeom);
static int32_t get_media_capacity(int32_t fd, uint32_t *capacity,
			uint32_t *blocksize);

static int32_t scsi_ls120_format(uint_t fd, uint_t flavor, uint32_t capacity,
			uint32_t blocksize);

static void *sm_server_thread(void *arg);
static void sm_door_server_create(door_info_t *dip);
static void term_handler(int sig, siginfo_t *siginfo, void *sigctx);
static void hup_handler(int sig, siginfo_t *siginfo, void *sigctx);
static void sig_handler(int sig, siginfo_t *siginfo, void *sigctx);
static void badsig_handler(int sig, siginfo_t *siginfo, void *sigctx);
static void server_badsig_handler(int sig, siginfo_t *siginfo, void *sigctx);
static char *xlate_state(int32_t);
static uint32_t	get_sector_size(int fd);
static int32_t raw_read(door_data_t *door_dp, smedia_services_t *req);
static int32_t raw_write(door_data_t *door_dp, smedia_services_t *req);
static int32_t reassign_block(door_data_t *door_dp, smedia_services_t *req);
static int32_t set_protection_status(door_data_t *door_dp,
			smedia_services_t *req);
static int32_t set_shfd(door_data_t *door_dp, int32_t fd,
			smedia_services_t *req);

static void door_ret_err(smedia_reterror_t *reterror, int32_t err);
static void my_door_return(char *data_ptr, size_t data_size,
			door_desc_t *desc_ptr, uint_t num_desc);
static int32_t invalid_uscsi_operation(door_data_t *, struct uscsi_cmd *);

#define	W_E_MASK	0x80

static smserver_info server_info;

static int32_t
invalid_uscsi_operation(door_data_t *door_dp, struct uscsi_cmd *ucmd)
{

	if (door_dp->dd_dkinfo.dki_ctype != DKC_CDROM) {
		debug(5,
		"Invalid device type(0x%x) found for uscsi cmd.\n",
			door_dp->dd_dkinfo.dki_ctype);
		errno = EINVAL;
		return (EINVAL);
	}
	if (ucmd->uscsi_flags & FORBIDDEN_FLAGS) {
		debug(5,
		"Invalid flags(0x%x) set in uscsi cmd. cdb[0]=0x%x\n",
		ucmd->uscsi_flags,  ucmd->uscsi_cdb[0]);
		errno = EINVAL;
		return (EINVAL);
	}
	if (ucmd->uscsi_cdb[0] == SCMD_COPY ||
	    ucmd->uscsi_cdb[0] == SCMD_COPY_VERIFY ||
	    ucmd->uscsi_cdb[0] == SCMD_COMPARE ||
	    ucmd->uscsi_cdb[0] == SCMD_WRITE_BUFFER) {
		debug(5,
		"Invalid command(0x%x) found in cdb.\n",
		ucmd->uscsi_cdb[0]);
		errno = EINVAL;
		return (EINVAL);
	}
	return (0);
}

static uint32_t
get_sector_size(int fd)
{
	uint32_t	sector_size;
	struct uscsi_cmd	ucmd;
	union scsi_cdb		cdb;
	int32_t		ret_val;
	uint32_t rc_data[2];
	char rq_data[RQ_LEN];

	cdb.scc_cmd = SCMD_READ_CAPACITY;
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)&rc_data;
	ucmd.uscsi_buflen = sizeof (rc_data);
	ucmd.uscsi_timeout = 120; /* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;

	ret_val = do_uscsi_cmd(fd,
		&ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Read capacity : %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		sector_size = 512;
	} else {
		sector_size = ntohl(rc_data[1]);
	}
	debug(5, "sector size = 0x%x(%d)\n",
		sector_size, sector_size);
	return (sector_size);
}

static char *
xlate_state(int32_t state)
{
	switch (state) {

	case SM_WRITE_PROTECT_DISABLE:
		return ("PROTECTION_DISABLED");
	case SM_WRITE_PROTECT_PASSWD:
		return ("WRITE_PROTECT_PASSWD");
	case SM_WRITE_PROTECT_NOPASSWD:
		return ("WRITE_PROTECT_NOPASSWD");
	case SM_READ_WRITE_PROTECT:
		return ("READ_WRITE_PROTECT");
	case SM_TEMP_UNLOCK_MODE:
		return ("PROTECTION DISABLED");
	default:
		return ("UNKNOWN_STATE");
	}
}

static char *
xlate_cnum(smedia_callnumber_t cnum)
{
	switch (cnum) {

	case SMEDIA_CNUM_OPEN_FD:
		return ("SMEDIA_CNUM_OPEN_FD");
	case SMEDIA_CNUM_GET_DEVICE_INFO:
		return ("SMEDIA_CNUM_GET_DEVICE_INFO");
	case SMEDIA_CNUM_GET_MEDIUM_PROPERTY:
		return ("SMEDIA_CNUM_GET_MEDIUM_PROPERTY");
	case SMEDIA_CNUM_GET_PROTECTION_STATUS:
		return ("SMEDIA_CNUM_GET_PROTECTION_STATUS");
	case SMEDIA_CNUM_SET_PROTECTION_STATUS:
		return ("SMEDIA_CNUM_SET_PROTECTION_STATUS");
	case SMEDIA_CNUM_RAW_READ:
		return ("SMEDIA_CNUM_RAW_READ");
	case SMEDIA_CNUM_RAW_WRITE:
		return (" SMEDIA_CNUM_RAW_WRITE");
	case SMEDIA_CNUM_FORMAT:
		return ("SMEDIA_CNUM_FORMAT");
	case SMEDIA_CNUM_CHECK_FORMAT_STATUS:
		return ("SMEDIA_CNUM_CHECK_FORMAT_STATUS");
	case SMEDIA_CNUM_EJECT:
		return ("SMEDIA_CNUM_EJECT");
	case SMEDIA_CNUM_REASSIGN_BLOCK:
		return ("SMEDIA_CNUM_REASSIGN_BLOCK");
	case SMEDIA_CNUM_SET_SHFD:
		return ("SMEDIA_CNUM_SET_SHFD");
	case SMEDIA_CNUM_PING:
		return ("SMEDIA_CNUM_PING");
	case SMEDIA_CNUM_USCSI_CMD:
		return ("SMEDIA_CNUM_USCSI_CMD");
	default:
		return ("UNKNOWN_CNUM");
	}
}

/*ARGSUSED*/
smserver_info *
smserverproc_get_serverinfo_1(void *argp, CLIENT *clnt)
{
	(void) mutex_lock(&svcstate_lock);
	svcstate = _SERVED;
	(void) mutex_unlock(&svcstate_lock);
	server_info.vernum = SMSERVERVERS;
	server_info.status = 0;
	(void) mutex_lock(&server_data.sd_init_lock);
	if (server_data.sd_init_state == INIT_NOT_DONE) {
		server_data.sd_init_state = INIT_IN_PROGRESS;
		debug(5, "Initialising server\n");
		(void) init_server(NULL);
	}
	if (server_data.sd_init_state != INIT_DONE) {
		debug(1, "init_server did not do the job. "
		    "init_state=%d\n", server_data.sd_init_state);
		server_data.sd_init_state = INIT_NOT_DONE;
		(void) mutex_unlock(&server_data.sd_init_lock);
		server_info.status = -1;
		return (&server_info);
	}
	(void) mutex_unlock(&server_data.sd_init_lock);

	debug(5, "smserverproc thread %d running....\n", pthread_self());
	return (&server_info);
}

/*ARGSUSED*/
static void
server_badsig_handler(int sig, siginfo_t *siginfo, void *sigctx)
{

	fatal(gettext(BADSIG_MSG), pthread_self(), sig, siginfo->si_addr,
		siginfo->si_trapno,
		siginfo->si_pc);
}

static int32_t
do_uscsi_cmd(int32_t file, struct uscsi_cmd *uscsi_cmd, int32_t	flag)
{
	int32_t	ret_val;

	/*
	 * Set function flags for driver.
	 */
	uscsi_cmd->uscsi_flags = USCSI_ISOLATE;

#ifdef DEBUG
	uscsi_cmd->uscsi_flags |= USCSI_DIAGNOSE;
#else
	uscsi_cmd->uscsi_flags |= USCSI_SILENT;
#endif /* DEBUG */

	uscsi_cmd->uscsi_flags |= flag;

	errno = 0;
	ret_val = ioctl(file, USCSICMD, uscsi_cmd);
	if (ret_val == 0 && uscsi_cmd->uscsi_status == 0) {
		return (ret_val);
	}
	if (!errno)
		errno = EIO;
	return (-1);
}

static int32_t
get_device_type(char *v_name)
{
	int32_t i;

	for (i = 0; i < 8; i++) {
		v_name[i] = toupper(v_name[i]);
	}
	if (strstr(v_name, "IOMEGA")) {
		return (SCSI_IOMEGA);
	}
	if (strstr(v_name, "FD") ||
	    strstr(v_name, "LS-120")) {
		return (SCSI_FLOPPY);
	}
	return (SCSI_GENERIC);

}

static int32_t
get_device_type_scsi(int32_t fd, struct scsi_inquiry *inq)
{
	int32_t dev_type;
	struct uscsi_cmd ucmd;
	union scsi_cdb  cdb;
	int32_t	ret_val;
	char rq_data[RQ_LEN];

	(void) memset((void *) inq, 0, sizeof (struct scsi_inquiry));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
	cdb.scc_cmd = SCMD_INQUIRY;
	FORMG0COUNT(&cdb, sizeof (struct scsi_inquiry));
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)inq;
	ucmd.uscsi_buflen = sizeof (struct scsi_inquiry);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "INQUIRY failed: rv = %d  uscsi_status = "
		    "%d  errno = %d\n", ret_val, ucmd.uscsi_status, errno);
		return (-1);
	}

	dev_type = get_device_type(inq->inq_vid);

	debug(5, "dev_type %d\n", dev_type);
	return (dev_type);

}

static int32_t
get_media_capacity(int32_t fd, uint32_t *capacity, uint32_t *blocksize)
{
	struct uscsi_cmd ucmd;
	uchar_t cdb[12];
	int32_t ret_val;
	uchar_t data[20];
	char rq_data[RQ_LEN];

	debug(5, "get_media_capacity:\n");

	(void) memset((void *)&data, 0, sizeof (data));
	(void) memset((void *)&ucmd, 0, sizeof (ucmd));
	(void) memset((void *)&cdb, 0, sizeof (cdb));

	/* retrieve size discriptor of inserted media */
	cdb[0] = SCMD_READ_FORMAT_CAP;
	cdb[8] = 0x14;  /* data size */

	/* Fill in the USCSI fields */
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP5;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = sizeof (data);
	ucmd.uscsi_timeout = 120;
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);

	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Retrieving media info failed: %d - %d\n", ret_val,
		    ucmd.uscsi_status);

		if ((rq_data[2] == KEY_DATA_PROTECT) && (rq_data[12] == 0x30) &&
		    (rq_data[13] == 0)) {
			(void) debug(1, "Invalid command for media\n");
			errno = EINVAL;
		}
		return (-1);
	}

	/* No media, bail out */
	if (data[8] == 0x3) {
		(void) debug(5, "no media in drive\n");
		return (-1);
	}

	/*
	 * Generate capacity and blocksize information
	 */

	*capacity =  (uint32_t)((data[4] << 24) + (data[5] << 16) +
	    (data[6] << 8) + data[7]);

	debug(1, "capacity is %x %x %x %x = %x", data[4], data[5], data[6],
	    data[7], *capacity);

	*blocksize = (uint32_t)((data[9] << 16) + (data[10] << 8) + data[11]);

	return (0);
}

static int32_t
scsi_zip_format(int32_t fd, uint_t flavor, uint_t mode)
{
	struct uscsi_cmd ucmd;
	struct scsi_inquiry inq;
	uchar_t cdb[12];
	int32_t   ret_val;
	uchar_t data[4];
	uint32_t rc_data[2];
	char rq_data[RQ_LEN];
	uint32_t capacity;


	if ((mode != SM_FORMAT_IMMEDIATE) &&
		(mode != SM_FORMAT_BLOCKED)) {
		errno = ENOTSUP;
		return (ENOTSUP);
	}
	/*
	 * Do an inquiry and try to figure out if it an
	 * IOMEGA JAZ 2GB device.
	 */

	(void) memset((void *) &inq, 0, sizeof (inq));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	(void) memset((void *) &rq_data, 0, sizeof (rq_data));
	cdb[0] = SCMD_INQUIRY;
	cdb[4] = sizeof (inq);
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)&inq;
	ucmd.uscsi_buflen = sizeof (inq);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "inquiry failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (ucmd.uscsi_status);
	}

	(void) memset((void *) &rc_data, 0, sizeof (rc_data));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	cdb[0] = SCMD_READ_CAPACITY;
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)&rc_data;
	ucmd.uscsi_buflen = sizeof (rc_data);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */

	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Read capacity : %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (ucmd.uscsi_status);
	}

	capacity = ntohl(rc_data[0]);

	(void) memset((void *)&data, 0, sizeof (data));
	(void) memset((void *)&ucmd, 0, sizeof (ucmd));
	(void) memset((void *)&cdb, 0, sizeof (cdb));
	cdb[0] =  SCMD_FORMAT;
	/*
	 * Defect list sent by initiator is a complete list of defects.
	 */
	cdb[1] = (FMTDATA | CMPLIST);
	/*
	 * Target should examine the setting of the DPRY, DCRT, STPF, IP
	 * and DSP bits.
	 */
	data[1] = FOV;

	switch (flavor) {
		case SM_FORMAT_QUICK :
			/*
			 * Target should not perform any vendor specific
			 * medium certification process or format verification
			 */
			data[1] = (FOV | DCRT);
			/*
			 * Defect list sent is an addition to the existing
			 * list of defects.
			 */
			cdb[1] =  FMTDATA;
			break;
		case SM_FORMAT_FORCE :
			if (strstr(inq.inq_pid, "jaz")) {
				debug(1,
				"LONG Format of JAZ media not supported\n");
				errno = ENOTSUP;
				return (ENOTSUP);
			}
			/*
			 * Formatting a write-protected or read/write
			 * protected cartridge is allowed.
			 * This is a vendor specific Format Option.
			 */
			cdb[2] = 0x20;
			break;
		case SM_FORMAT_LONG :
			if (strstr(inq.inq_pid, "jaz")) {
				debug(1,
				"LONG Format of JAZ media not supported\n");
				errno = ENOTSUP;
				return (ENOTSUP);
			}
			/*
			 * Defect list sent is an addition to the existing
			 * list of defects.
			 */
			cdb[1] = FMTDATA;
			break;
		default :
			debug(1, "Format option %d not supported!!\n",
			flavor);
			errno = ENOTSUP;
			return (ENOTSUP);
	}

	if (mode == SM_FORMAT_IMMEDIATE) {
		data[1] |= IMMED;
		debug(5, "immediate_flag set\n");
	}

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	debug(5, "cdb: %x ", cdb[0]);
	debug(5, "%x %x ", cdb[1], cdb[2]);
	debug(5, "%x %x %x\n", cdb[3], cdb[4], cdb[5]);
	debug(5, "data: %x %x %x %x\n", data[0], data[1], data[2], data[3]);

	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = sizeof (data);
	ucmd.uscsi_timeout = FORMAT_TIMEOUT(capacity);
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Format failed : %d - uscsi_status = %d errno = %d\n",
			ret_val,
			ucmd.uscsi_status, errno);
		if ((rq_data[2] == KEY_DATA_PROTECT) ||
			(rq_data[2] == KEY_ILLEGAL_REQUEST))
			errno = EINVAL;
		if ((rq_data[2] == KEY_MEDIUM_ERROR) ||
			(rq_data[2] == KEY_HARDWARE_ERROR))
			errno = EIO;
		return (errno);
	}

	return (0);
}

static int32_t
scsi_ls120_format(uint_t fd, uint_t flavor, uint32_t capacity,
    uint32_t blocksize)
{
	struct uscsi_cmd ucmd;
	uchar_t cdb[12];
	int32_t ret_val;
	uchar_t data[12];
	char	rq_data[RQ_LEN];

	debug(5, "scsi_ls120_format:\n");

	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	(void) memset((void *) &rq_data, 0, sizeof (rq_data));

	cdb[0] = SCMD_FORMAT;
	cdb[1] = (FMTDATA | 0x7);
	cdb[8] = 0x0C; /* parameter list length */

	data[1] = 0x80;
	data[3] = 0x08;


	data[4] = (capacity >> 24) & 0xff;
	data[5] = (capacity >> 16) & 0xff;
	data[6] = (capacity >> 8) & 0xff;
	data[7] = capacity & 0xff;


	data[9] =  (blocksize >> 16) & 0xff;
	data[10] = (blocksize >> 8) & 0xff;
	data[11] = blocksize & 0xff;

	debug(5, "cdb: %x %x %x ... %x", cdb[0], cdb[1], cdb[2], cdb[8]);
	debug(5, "data: %x %x %x %x\n", data[0], data[1], data[2], data[3]);
	debug(5, "    : %x %x %x %x\n", data[4], data[5], data[6], data[7]);
	debug(5, "    : %x %x %x %x\n", data[8], data[9], data[10], data[11]);

	switch (flavor) {
		case SM_FORMAT_QUICK :
			debug(1, "Format not supported\n");
			errno = ENOTSUP;
			return (-1);
		case SM_FORMAT_FORCE :
			break;
		case SM_FORMAT_LONG :
			break;
		default :
			debug(1, "Format option not specified!!\n");
			errno = ENOTSUP;
			return (-1);
	}

	ucmd.uscsi_cdb = (caddr_t)&cdb;


	ucmd.uscsi_cdblen = CDB_GROUP5;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = sizeof (data);
	ucmd.uscsi_timeout = 0x12c0;
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	(void) fflush(stdout);

	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(1, "Format failed failed: %d - %d\n", ret_val,
		    ucmd.uscsi_status);

		if ((rq_data[2] == KEY_DATA_PROTECT) &&
		    (rq_data[12] == 0x30) && (rq_data[13] == 0)) {

			debug(1, "Invalid command for media\n");
			errno = EINVAL;
		}

		if ((rq_data[2] == KEY_NOT_READY) && (rq_data[12] == 0x30)) {
			debug(1, "Incompatible media.\n");
			errno = EINVAL;
		}

		return (-1);
	}

	return (0);
}

static int32_t
scsi_format(int32_t fd, uint_t flavor, uint_t mode)
{
	struct uscsi_cmd ucmd;
	struct scsi_inquiry inq;
	uchar_t cdb[12];
	int32_t   ret_val;
	uchar_t data[4];
	char rq_data[RQ_LEN];
	uint32_t rc_data[2];
	uint32_t capacity;



	if ((mode != SM_FORMAT_IMMEDIATE) &&
		(mode != SM_FORMAT_BLOCKED)) {
		errno = ENOTSUP;
		return (-1);
	}

	/*
	 * Do an inquiry and try to figure out if it an
	 * IOMEGA JAZ 2GB device.
	 */

	(void) memset((void *) &inq, 0, sizeof (inq));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	(void) memset((void *) &rq_data, 0, sizeof (rq_data));
	cdb[0] = SCMD_INQUIRY;
	cdb[4] = sizeof (inq);
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)&inq;
	ucmd.uscsi_buflen = sizeof (inq);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "inquiry failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (ucmd.uscsi_status);
	}

	(void) memset((void *) &rc_data, 0, sizeof (rc_data));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	cdb[0] = SCMD_READ_CAPACITY;
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)&rc_data;
	ucmd.uscsi_buflen = sizeof (rc_data);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */

	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Read capacity : %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (ucmd.uscsi_status);
	}

	capacity = ntohl(rc_data[0]);

	(void) memset((void *)&data, 0, sizeof (data));
	(void) memset((void *)&ucmd, 0, sizeof (ucmd));
	(void) memset((void *)&cdb, 0, sizeof (cdb));
	cdb[0] =  SCMD_FORMAT;
	/*
	 * Defect list sent is an addition to the existing
	 * list of defects.
	 */
	cdb[1] =  FMTDATA;
	/*
	 * Target should examine the setting of the DPRY, DCRT, STPF, IP
	 * and DSP bits.
	 */
	data[1] = FOV;

	if (mode == SM_FORMAT_IMMEDIATE) {
		debug(5,
	"SM_FORMAT_IMMEDIATE specified ignored. Performing a long format!\n");
	}

	switch (flavor) {
		case SM_FORMAT_LONG :
			if (strstr(inq.inq_pid, "jaz")) {
				debug(1,
				"LONG Format of JAZ media not supported\n");
				errno = ENOTSUP;
				return (ENOTSUP);
			}
			/*
			 * Defect list sent is an addition to the existing
			 * list of defects.
			 */
			cdb[1] = FMTDATA;
			break;
		default :
			debug(1, "Format option %d  not supported!!\n",
			flavor);
			errno = ENOTSUP;
			return (ENOTSUP);
	}


	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = sizeof (data);
	ucmd.uscsi_timeout = FORMAT_TIMEOUT(capacity);
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Format failed failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (ucmd.uscsi_status);
	}

	return (0);
}

static int32_t
scsi_media_status(int32_t fd)
{
	struct mode_header modeh;
	struct uscsi_cmd ucmd;
	union scsi_cdb  cdb;
	int32_t ret_val;
	int32_t cur_status;
	char rq_data[RQ_LEN];

	debug(10, "SCSI MEDIA STATUS CALLED \n");

	(void) memset((void *) &modeh, 0, sizeof (modeh));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
	cdb.scc_cmd = SCMD_MODE_SENSE;
	cdb.cdb_opaque[2] = MODEPAGE_ALLPAGES;
	FORMG0COUNT(&cdb, sizeof (modeh));

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)&modeh;
	ucmd.uscsi_buflen = sizeof (modeh);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Modesense for 0x3f pages failed: %d-%d errno=%d\n",
			ret_val, ucmd.uscsi_status, errno);
		cdb.cdb_opaque[2] = 0;
		ucmd.uscsi_rqlen = RQ_LEN;
		FORMG0COUNT(&cdb, sizeof (modeh));
		ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
		if (ret_val || ucmd.uscsi_status) {
			debug(5, "Modesense failed: %d - %d errno = %d\n",
				ret_val, ucmd.uscsi_status, errno);
			return (-1);
		}
	}

	if (modeh.device_specific & W_E_MASK) {
		cur_status = SM_WRITE_PROTECT_NOPASSWD;
	} else {
		cur_status = SM_WRITE_PROTECT_DISABLE;
	}
	debug(5, "cur status %d\n", cur_status);

	return (cur_status);
}

static int32_t
scsi_zip_media_status(int32_t fd)
{
	struct uscsi_cmd ucmd;
	uchar_t cdb[12];
	int32_t	status;
	int32_t mode;
	uchar_t data[64];
	char rq_data[RQ_LEN];

	debug(10, "Getting media status\n");

	(void) memset((void *)&ucmd, 0, sizeof (ucmd));
	(void) memset((void *)&cdb, 0, sizeof (cdb));

	cdb[0] = IOMEGA_NONSENSE_CMD;
	cdb[2] = CARTRIDGE_STATUS_PAGE;
	cdb[4] = ND_LENGTH;
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = 64;
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	status = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (status || ucmd.uscsi_status) {
		debug(5, "Cartridge protect operation failed: "
		    "rv = %d  uscsi_status = %d  errno = %d\n",
		    status, ucmd.uscsi_status, errno);
		return (-1);
	}

	if (data[DISK_STATUS_OFFSET + NON_SENSE_HDR_LEN] == 4) {
		debug(1, "Disk not present. \n");
		return (-1);
	}
	mode = data[PROTECT_MODE_OFFSET + NON_SENSE_HDR_LEN] & 0xF;

	debug(5, "MODE 0x%x / %d.\n", mode, mode);

	switch (mode) {
		case UNLOCK_MODE:
			status = SM_WRITE_PROTECT_DISABLE;
			break;
		case WRITE_PROTECT_MODE:
			status = SM_WRITE_PROTECT_NOPASSWD;
			break;
		case PASSWD_WRITE_PROTECT_MODE:
			status = SM_WRITE_PROTECT_PASSWD;
			break;
		case READ_WRITE_PROTECT_MODE:
			status = SM_READ_WRITE_PROTECT;
			break;
		default :
			if (mode & TEMP_UNLOCK_MODE)
				status = SM_TEMP_UNLOCK_MODE;
			else
				status = SM_STATUS_UNKNOWN;
			break;
	}

	debug(5, "status %d \n", status);
	return (status);
}

static int32_t
scsi_reassign_block(int32_t fd, diskaddr_t block)
{
	uchar_t data[8];
	struct uscsi_cmd ucmd;
	char cdb[12];
	int32_t	ret_val;
	char rq_data[RQ_LEN];

	debug(5, "SCSI REASSIGN CALLED block = %lld\n", block);

	(void) memset((void *) &data, 0, sizeof (data));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	cdb[0] = SCMD_REASSIGN_BLOCK;
	data[3] = 4;
	data[4] = ((block & 0xFF000000) >> 24);
	data[5] = ((block & 0xFF0000) >> 16);
	data[6] = ((block & 0xFF00) >> 8);
	data[7] = block & 0xFF;

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = sizeof (data);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Reassign block failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (-1);
	}

	return (0);
}

static int32_t
get_mode_page(int32_t fd, uchar_t pc, uchar_t page_code,
    uchar_t *md_data, uchar_t data_len)
{
	struct uscsi_cmd ucmd;
	uchar_t cdb[12];
	int32_t	ret_val;
	char rq_data[RQ_LEN];

	debug(10, "MODE SENSE(6) - page_code = 0x%x\n", page_code);

	(void) memset((void *) md_data, 0, sizeof (data_len));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	cdb[0] = SCMD_MODE_SENSE;
	cdb[2] = (pc << 6) | page_code;
	cdb[4] = data_len;

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)md_data;
	ucmd.uscsi_buflen = data_len;
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Modesense failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		return (-2);
	}

	return (0);
}

static int32_t
scsi_zip_write_protect(int32_t fd, smwp_state_t *wp)
{
	struct uscsi_cmd ucmd;
	struct scsi_inquiry inq;
	uchar_t cdb[12];
	int32_t	status;
	int32_t new_mode;
	char rq_data[RQ_LEN];
	int32_t wa_bit;
	char *tmp_passwd = NULL;

	debug(10, "SCSI ZIP WRITE PROTECT CALLED \n");

	/*
	 * Do an inquiry and try to figure out if it an
	 * ATAPI or SCSI device.
	 */

	(void) memset((void *) &inq, 0, sizeof (inq));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));
	(void) memset((void *) &rq_data, 0, sizeof (rq_data));
	cdb[0] = SCMD_INQUIRY;
	cdb[4] = sizeof (inq);
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_bufaddr = (caddr_t)&inq;
	ucmd.uscsi_buflen = sizeof (inq);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	status = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (status || ucmd.uscsi_status) {
		debug(5, "inquiry failed: %d - %d errno = %d\n",
			status, ucmd.uscsi_status, errno);
		return (-1);
	}

	if (inq.inq_ansi > 0) {
		wa_bit = 0;
		debug(5, "SCSI device\n");
	} else {
		wa_bit = 1;
		debug(5, "ATAPI device\n");
	}

	switch (wp->sm_new_state) {
		case SM_WRITE_PROTECT_DISABLE :
			new_mode = 0x0;
			break;
		case SM_WRITE_PROTECT_NOPASSWD :
			new_mode = 0x2;
			break;
		case SM_WRITE_PROTECT_PASSWD :
			new_mode = 0x3;
			break;
		case SM_READ_WRITE_PROTECT :
			new_mode = 0x5;
			break;
		case SM_TEMP_UNLOCK_MODE :
			new_mode = 0x8;
			break;
		default :
			debug(1, "Invalid mode 0x%x specified\n",
			wp->sm_new_state);
			errno = ENOTSUP;
			return (-1);
	}


	(void) memset((void *)&ucmd, 0, sizeof (ucmd));
	(void) memset((void *)&cdb, 0, sizeof (cdb));
	(void) memset((void *) &rq_data, 0, sizeof (rq_data));
	cdb[0] = IOMEGA_CATRIDGE_PROTECT;
	cdb[1] |= new_mode;
	if (wa_bit)
		cdb[1] |= WA_BIT;
	cdb[4] = wp->sm_passwd_len;
	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP0;
	if (wa_bit && (wp->sm_passwd_len & 1)) {
		/*
		 * Oops, ATAPI device with an odd length passwd!
		 * Allocate a buffer to hold one extra byte.
		 */
		debug(5, "Odd len passwd for ATAPI device!\n");
		errno = 0;
		tmp_passwd = (char *)malloc(wp->sm_passwd_len+1);
		if (tmp_passwd == NULL) {
			if (errno == 0)
				errno = ENOMEM;
			return (-1);
		}
		(void) memset(tmp_passwd, 0, wp->sm_passwd_len+1);
		(void) memcpy(tmp_passwd, wp->sm_passwd, wp->sm_passwd_len);
		ucmd.uscsi_bufaddr = (caddr_t)tmp_passwd;
		ucmd.uscsi_buflen = wp->sm_passwd_len+1;
	} else {
		ucmd.uscsi_bufaddr = (caddr_t)wp->sm_passwd;
		ucmd.uscsi_buflen = wp->sm_passwd_len;
	}
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	status = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
	if (tmp_passwd != NULL) {
		free(tmp_passwd);
	}
	if (status || ucmd.uscsi_status) {
		debug(5, "Cartridge-protect operation failed: rv "
		    "= %d  uscsi_status = %d  errno = %d\n", status,
		    ucmd.uscsi_status, errno);
		if ((rq_data[2] & 0xF) == KEY_ILLEGAL_REQUEST) {
			if (rq_data[12] == 0x26) {
				/* Wrong passwd */
				debug(5, "Protection Request with wrong "
				    "passwd. errno is being set to EACCES.\n");
				errno = EACCES;
			}
		}
		return (-1);
	}

	return (0);
}

/*ARGSUSED*/
static int32_t
scsi_write_protect(int32_t fd, smwp_state_t *wp)
{
	errno = ENOTSUP;
	return (-1);
}

/*
 * This thread becomes the server-side thread used in
 * the implementation of a door_call between a client
 * and the Client Door.
 *
 * This thread is customized both by the door_server_create(3c)
 * function sm_door_server_create, as well as by itself.
 *
 * This thread needs to synchronize with the
 * main_servproc[SMEDIA_CNUM_OPEN_FD] door_call in terms of
 * both successful and failure scenarios.  main_servproc
 * locks dd_lock before calling door_create.  This thread
 * then attempts to lock, but will block until main_servproc
 * has either created all doors it requires, or until a
 * door_create has failed (door_create's return and the
 * creation of an associated thread are asynchronous).
 *
 * If door_create failed, this thread will be able to obtain
 * dd_lock and call pthread_exit.  If all door_create's succeed,
 * this thread will obtain dd_lock and commence with
 * customizing the thread's attributes.  door_bind is called to
 * bind this thread to the per-door private thread pool, and
 * main_servproc is cond_signal'd to avail it of this fact.
 *
 * Finally, this thread calls door_return, which causes it to
 * commence its lifetime as a server-side thread in implementation
 * of a Client Door door_call.
 */
static void *
sm_server_thread(void *arg)
{
	door_data_t	*door_dp;
	struct		sigaction act;
	int		i;
	int		err;

	door_dp = (door_data_t *)arg;

	if (door_dp == NULL) {
		fatal("sm_server_thread[%d]: argument is NULL!!\n",
		    pthread_self());
		exit(-1);
	}

	/* Wait for Client Door to be created */
	(void) mutex_lock(&door_dp->dd_lock);
	if (door_dp->dd_cdoor_descriptor < 0) {
		debug(5, "sm_server_thread[%d]: door_create() failed",
		    pthread_self());
		(void) mutex_unlock(&door_dp->dd_lock);
		pthread_exit((void *)-2);
	}
	(void) mutex_unlock(&door_dp->dd_lock);

	for (i = 0; i < N_BADSIGS; i++) {
		act.sa_sigaction = server_badsig_handler;
		(void) sigemptyset(&act.sa_mask);
		act.sa_flags = SA_SIGINFO;
		if (sigaction(badsigs[i], &act, NULL) == -1)
			warning(gettext(SIGACT_FAILED), strsignal(badsigs[i]),
			    strerror(errno));
	}
	if (sigemptyset(&door_dp->dd_newset) != 0)
		warning(gettext("sigemptyset failed. errno = %d\n"),
		    errno);
	if ((err = pthread_sigmask(SIG_BLOCK, &door_dp->dd_newset, NULL)) != 0)
		warning(gettext("pthread_sigmask failed = %d\n"), err);

	/* Bind thread with pool associated with Client Door */

	if (door_bind(door_dp->dd_cdoor_descriptor) < 0) {
		fatal("door_bind");
		exit(-1);
	}
	debug(5, "thr[%d] bound to Client Door[%d]", pthread_self(),
	    door_dp->dd_cdoor_descriptor);

	/*
	 * Set these two cancellation(5) attributes.  Ensure that the
	 * pthread we create has cancellation(5) DISABLED and DEFERRED,
	 * as our implementation is based on this.  DEFERRED is the
	 * default, but set it anyways, in case the defaults change in
	 * the future.
	 */
	if ((err = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)) != 0)
		warning(gettext("pthread_setcancelstate(PTHREAD_CANCEL_DISABLE)"
		    " failed = %d\n"), err);
	if ((err = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,
	    NULL)) != 0)
		warning(gettext("pthread_setcanceltype(DEFERRED) "
		    "failed = %d\n"), err);

	/* Inform main_servproc that door_bind() is complete. */
	(void) cond_signal(&door_dp->dd_cv_bind);

	/*
	 * Per doors protocol, transfer control to the doors-runtime in
	 * order to make this thread available to answer future door_call()'s.
	 */
	(void) door_return(NULL, 0, NULL, 0);
	return (NULL);
}

/*
 * This function cleans up all per-connection resources.
 *
 * This function is called when the Client Door's service procedure
 * (client_servproc) is called w/ DOOR_UNREF_DATA, which is the
 * doors protocol convention stating that the number of file
 * descriptors referring to this door has dropped to one.
 * client_servproc is passed DOOR_UNREF_DATA because the Client Door
 * was door_create'd with the DOOR_UNREF bitflag.
 */
static void
cleanup(door_data_t *door_dp)
{
	/* do door_revoke() of Death Door */
	if (door_dp->dd_ddoor_descriptor >= 0) {
		debug(1, "cleanup[%d]: door_revoke() Death Door[%d]",
		    pthread_self(), door_dp->dd_ddoor_descriptor);

		if (door_revoke(door_dp->dd_ddoor_descriptor) < 0) {
			warning(gettext("cleanup[%d]: door_revoke() of Death "
			    "Door(%d) failed = %d"), pthread_self(),
			    door_dp->dd_ddoor_descriptor, errno);
		} else {
			door_dp->dd_ddoor_descriptor = -1;
		}
	}

	/* release memory that is shared between client and (our) server */
	if (door_dp->dd_buffd >= 0) {
		debug(1, "cleanup[%d]: release shared memory", pthread_self());
		(void) munmap(door_dp->dd_buf, door_dp->dd_buf_len);
		(void) close(door_dp->dd_buffd);

		door_dp->dd_buffd = -1;
		door_dp->dd_buf = NULL;
		door_dp->dd_buf_len = 0;
	}

	/* close the (target) device that the Client is operating on */
	if (door_dp->dd_fd >= 0) {
		debug(1, "cleanup[%d]: close(%d) target device", pthread_self(),
		    door_dp->dd_fd);
		if (close(door_dp->dd_fd) < 0) {
			warning(gettext("cleanup[%d]: close() of target device"
			    "failed = %d\n"), pthread_self(), errno);
		}
	}

	/*
	 * Unbind the current thread from the Client Door's private
	 * thread pool.
	 */
	debug(1, "cleanup[%d]: door_unbind() of Client Door[%d]",
	    pthread_self(), door_dp->dd_cdoor_descriptor);
	if (door_unbind() < 0)
		warning("door_unbind() of Client Door[%d] failed = "
		    "%d", door_dp->dd_cdoor_descriptor, errno);

	/* Disallow any future requests to the Client Door */
	if (door_dp->dd_cdoor_descriptor >= 0) {
		debug(1, "cleanup[%d]: door_revoke() Client Door[%d]",
		    pthread_self(), door_dp->dd_cdoor_descriptor);

		if (door_revoke(door_dp->dd_cdoor_descriptor) < 0) {
			warning(gettext("cleanup[%d]: door_revoke() of "
			    "Client Door[%d] failed = %d"), pthread_self(),
			    door_dp->dd_cdoor_descriptor, errno);
		}
	}

	free(door_dp);
	debug(5, "cleanup[%d] ...exiting\n", pthread_self());
}

/*
 * This is the door_server_create(3c) function used to customize
 * creation of the threads used in the handling of our daemon's
 * door_call(3c)'s.
 *
 * This function is called synchronously as part of door_create(3c).
 * Note that door_create(), however, is not synchronous; it can return
 * with the created door file descriptor before any associated
 * thread has been created.  As a result, synchronization is needed
 * between door_create() caller and the created pthread.  This is
 * needed both when each activity succeeds or when either activity
 * fails.
 *
 * Specifically, this function ensures that each "connection"
 * with the client creates only one thread in the per-door,
 * private thread pool.  This function locks dd_threadlock and
 * then calls pthread_create().  If that succeeds, dd_thread
 * is assigned the thread id, and dd_threadlock is unlocked.
 * Any per-connection door_create that causes control to flow
 * to this function will eventually find that dd_thread is
 * non-zero, and control will exit this function.
 *
 * In the current implementation, the door_create for the Client Door
 * is called first, and the Death Door is door_create'd second.
 * As a result, the following function can safely make the static
 * assumption that the first door (within a connection) is the
 * Client Door.  A connection's Client Door and Death Door share
 * the same thread as well as the same door_data_t instance.
 */
static void
sm_door_server_create(door_info_t *dip)
{
	door_data_t	*door_dp;
	pthread_t	tid;
	pthread_attr_t	attr;
	int		ret_val;
	int		err;

	if (dip == NULL) {
		return;
	}
	door_dp = (door_data_t *)(uintptr_t)dip->di_data;

	debug(10, "sm_door_server_create[%d]: entering...\n", pthread_self());

	/* create one thread for this door */

	(void) mutex_lock(&door_dp->dd_threadlock);

	if (door_dp->dd_thread != 0) {
		debug(8, "sm_door_server_create[%d]: Exiting without creating "
		    "thread.\n", pthread_self());
		(void) mutex_unlock(&door_dp->dd_threadlock);
		return;
	}

	(void) pthread_attr_init(&attr);

	if ((err = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) != 0)
		warning(gettext("pthread_attr_setscope failed = %d\n"), err);
	if ((err = pthread_attr_setdetachstate(&attr,
	    PTHREAD_CREATE_DETACHED)) != 0)
		warning(gettext("pthread_attr_setdetachstate failed = %d\n"),
		    err);

	ret_val = pthread_create(&tid, &attr, sm_server_thread,
	    (void *)(uintptr_t)(dip->di_data));
	if (ret_val != 0) {
		warning(gettext("sm_door_server_create[%d]: pthread_create "
		    "failed = %d\n"), pthread_self(), ret_val);
		(void) mutex_unlock(&door_dp->dd_threadlock);
		(void) pthread_attr_destroy(&attr);
		return;
	}
	(void) pthread_attr_destroy(&attr);
	door_dp->dd_thread = tid;

	(void) mutex_unlock(&door_dp->dd_threadlock);
	debug(5, "Exiting sm_door_server_create[%d] after creating thr[%d].\n",
	    pthread_self(), tid);
}

static void
door_ret_err(smedia_reterror_t *reterror, int32_t err)
{
	reterror->cnum = SMEDIA_CNUM_ERROR;
	reterror->errnum = err;
	(void) door_return((char *)reterror, sizeof (smedia_reterror_t), 0, 0);
}

static void
my_door_return(char *data_ptr, size_t data_size,
	door_desc_t *desc_ptr, uint_t num_desc)
{
	(void) door_return(data_ptr, data_size, desc_ptr, num_desc);
}

static int32_t
raw_read(door_data_t *door_dp, smedia_services_t *req)
{
	struct uscsi_cmd	ucmd;
	union scsi_cdb		cdb;
	int32_t			ret_val;
	int32_t			num_sectors, sector_size;
	int32_t			rc_data[2];
	char			rq_data[RQ_LEN];

	(void) memset((void *) &rc_data, 0, sizeof (rc_data));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));

	if (door_dp->dd_sector_size == 0) {
		sector_size = get_sector_size(door_dp->dd_fd);
		door_dp->dd_sector_size = sector_size;
	} else sector_size = door_dp->dd_sector_size;

	if ((req->reqraw_read.nbytes > door_dp->dd_buf_len) ||
		(door_dp->dd_buf == NULL)) {
		errno = EINVAL;
		return (-1);
	}
	if ((!req->reqraw_read.nbytes) ||
		(req->reqraw_read.nbytes % sector_size)) {
		errno = EINVAL;
		return (-1);
	}

	(void) memset((void *) &cdb, 0, sizeof (cdb));
	num_sectors = (uint32_t)req->reqraw_read.nbytes/sector_size;

	cdb.scc_cmd = SCMD_READ_G1;
	FORMG1ADDR(&cdb, (uint32_t)req->reqraw_read.blockno);
	FORMG1COUNT(&cdb, num_sectors);

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)door_dp->dd_buf;
	ucmd.uscsi_buflen = (uint32_t)req->reqraw_read.nbytes;
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(door_dp->dd_fd,
		&ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "read failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		debug(5, "buflen = 0x%x resid = 0x%x sector size = %d\n",
			ucmd.uscsi_buflen, ucmd.uscsi_resid, sector_size);
		debug(5, "cdb addr: %x %x %x %x \n", cdb.g1_addr3,
			cdb.g1_addr2, cdb.g1_addr1, cdb.g1_addr0);
		debug(5, "cdb count: %x %x\n", cdb.g1_count1,
			cdb.g1_count0);
		return (-1);
	}
	ret_val = ucmd.uscsi_buflen - ucmd.uscsi_resid;
	return (ret_val);
}

static int32_t
raw_write(door_data_t *door_dp, smedia_services_t *req)
{
	struct uscsi_cmd	ucmd;
	union scsi_cdb		cdb;
	int32_t			ret_val;
	int32_t			num_sectors, sector_size;
	int32_t			rc_data[2];
	char			rq_data[RQ_LEN];

	(void) memset((void *) &rc_data, 0, sizeof (rc_data));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));

	if (door_dp->dd_sector_size == 0) {
		sector_size = get_sector_size(door_dp->dd_fd);
		door_dp->dd_sector_size = sector_size;
	} else sector_size = door_dp->dd_sector_size;


	if ((req->reqraw_write.nbytes > door_dp->dd_buf_len) ||
		(door_dp->dd_buf == NULL)) {
		errno = EINVAL;
		return (-1);
	}
	if ((req->reqraw_write.nbytes % sector_size)) {
		errno = EINVAL;
		return (-1);
	}

	(void) memset((void *) &cdb, 0, sizeof (cdb));
	num_sectors = (uint32_t)req->reqraw_write.nbytes/sector_size;

	cdb.scc_cmd = SCMD_WRITE_G1;
	FORMG1ADDR(&cdb, (uint32_t)req->reqraw_write.blockno);
	FORMG1COUNT(&cdb, num_sectors);

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)door_dp->dd_buf;
	ucmd.uscsi_buflen = (uint32_t)req->reqraw_write.nbytes;
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(door_dp->dd_fd,
		&ucmd, USCSI_WRITE|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "write failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		debug(5, "buflen = 0x%x resid = 0x%x sector size = %d\n",
			ucmd.uscsi_buflen, ucmd.uscsi_resid, sector_size);
		debug(5, "cdb addr: %x %x %x %x \n", cdb.g1_addr3,
			cdb.g1_addr2, cdb.g1_addr1, cdb.g1_addr0);
		debug(5, "cdb count: %x %x\n", cdb.g1_count1,
			cdb.g1_count0);
		return (-1);
	}
	ret_val = ucmd.uscsi_buflen - ucmd.uscsi_resid;
	return (ret_val);
}

static int32_t
set_protection_status(door_data_t *door_dp, smedia_services_t *req)
{
	int32_t			ret_val, saved_errno, status;
	struct scsi_inquiry	inq;
	char			vid[9];
	char			pid[17];
	struct passwd		*pwd;
	char			uname[MAXUGNAME + 1];
	char			*new_state, *old_state;

	/*
	 * Read the current protection state before modifiying.
	 * Needed for audit purposes.
	 */
	switch (get_device_type_scsi(door_dp->dd_fd, &inq)) {
	case SCSI_IOMEGA:
		status = scsi_zip_media_status(door_dp->dd_fd);
		ret_val = scsi_zip_write_protect(door_dp->dd_fd,
			&req->reqset_protection_status.prot_state);
		break;
	case SCSI_FLOPPY:
		info("Formatting floppy");
		status = scsi_floppy_media_status(door_dp->dd_fd);
		ret_val = scsi_floppy_write_protect(door_dp->dd_fd,
			&req->reqset_protection_status.prot_state);
		break;
	case SCSI_GENERIC:
		status = scsi_media_status(door_dp->dd_fd);
		ret_val = scsi_write_protect(door_dp->dd_fd,
			&req->reqset_protection_status.prot_state);
		break;
	}

	saved_errno = errno;
	new_state = xlate_state(
	    req->reqset_protection_status.prot_state.sm_new_state);
	old_state = xlate_state(status);

	if (can_audit()) {
		(void) audit_save_me(door_dp);
		door_dp->audit_text[0] = 0;
		door_dp->audit_text1[0] = 0;
		door_dp->audit_event = AUE_smserverd;
	}
	(void) strlcpy(vid, inq.inq_vid, sizeof (vid));
	(void) strlcpy(pid, inq.inq_pid, sizeof (pid));
	if (ret_val < 0) {
	    if (errno == EACCES) {
		pwd = getpwuid(door_dp->dd_cred.dc_ruid);
		if (pwd != NULL) {
			(void) strlcpy(uname,
				pwd->pw_name, MAXUGNAME);
		} else uname[0] = 0;

		if (can_audit()) {
			(void) snprintf(door_dp->audit_text,
				sizeof (door_dp->audit_text),
				dgettext(TEXT_DOMAIN, "from %s to %s"),
				old_state, new_state);

			(void) snprintf(door_dp->audit_text1,
				sizeof (door_dp->audit_text1),
				"%s %s (%d,%d)", vid, pid,
				(int)major(door_dp->dd_stat.st_rdev),
				(int)minor(door_dp->dd_stat.st_rdev));

			door_dp->audit_sorf = 1;
			if (audit_audit(door_dp) == -1)
			    warning("Error in writing audit info\n");
		}
	    } /* errno == EACCES */
	    errno = saved_errno;
	    return (-1);
	}
	if (can_audit()) {
		(void) snprintf(door_dp->audit_text,
			sizeof (door_dp->audit_text),
			dgettext(TEXT_DOMAIN, "from %s to %s"),
			old_state, new_state);

		(void) snprintf(door_dp->audit_text1,
			sizeof (door_dp->audit_text1),
			"%s %s (%d,%d)", vid, pid,
			(int)major(door_dp->dd_stat.st_rdev),
			(int)minor(door_dp->dd_stat.st_rdev));

		door_dp->audit_sorf = 0;
		if (audit_audit(door_dp) == -1)
		    warning("Error in writing audit info\n");
	}
	errno = saved_errno;
	return (0);
}

static int32_t
set_shfd(door_data_t *door_dp, int32_t fd, smedia_services_t *req)
{
	void	*fbuf;
	int32_t ret_val = 0;

	if ((door_dp->dd_buffd != -1) && (door_dp->dd_buf != NULL)) {
		ret_val = munmap(door_dp->dd_buf, door_dp->dd_buf_len);
		if (ret_val == -1)
			warning(gettext("munmap failed. errno=%d\n"),
			    errno);
		(void) close(door_dp->dd_buffd);

		door_dp->dd_buffd = -1;
		door_dp->dd_buf = 0;
		door_dp->dd_buf_len = 0;
	}

	fbuf = mmap(0, req->reqset_shfd.fdbuf_len,
	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (fbuf == MAP_FAILED) {
		ret_val = errno;
		debug(5, "mmap failed. errno=%d\n", errno);
		return (ret_val);
	}
	door_dp->dd_buffd = fd;
	door_dp->dd_buf = fbuf;
	door_dp->dd_buf_len = req->reqset_shfd.fdbuf_len;

	return (0);
}

static int32_t
reassign_block(door_data_t *door_dp, smedia_services_t *req)
{
	struct uscsi_cmd	ucmd;
	union scsi_cdb		cdb;
	int32_t			ret_val;
	int32_t			sector_size;
	char			*read_buf;
	uchar_t			mode_data[MD_LEN];

	if (get_mode_page(door_dp->dd_fd, 0, 1,
	    mode_data, MD_LEN) < 0) {
		debug(5, "Mode sense failed\n");
		ret_val =  scsi_reassign_block(door_dp->dd_fd,
		    req->reqreassign_block.blockno);
		if (ret_val != 0)
			return (-1);
		return (0);
	}

	/*
	 * No need to check if enough data is returned for
	 * AWRE bit or not.
	 * It will be 0 otherwise which needs to reassign the block.
	 */
	if (!(mode_data[AWRE_OFFSET] & AWRE)) {
		debug(5, "AWRE bit not set\n");
		ret_val =  scsi_reassign_block(door_dp->dd_fd,
			req->reqreassign_block.blockno);
		if (ret_val != 0)
			return (-1);
		return (0);
	}
	sector_size = (mode_data[BLOCK_LEN_OFFSET] << 16) |
		(mode_data[BLOCK_LEN_OFFSET + 1] << 8) |
		mode_data[BLOCK_LEN_OFFSET + 2];

	debug(5, "REASSIGN BLOCK: sec size = 0x%x\n", sector_size);
	read_buf = (char *)malloc(sector_size);
	if (read_buf == NULL) {
		/* Alloc failed. Atleast reassign the block */
		ret_val =  scsi_reassign_block(door_dp->dd_fd,
			req->reqreassign_block.blockno);
		if (ret_val != 0)
			return (-1);
		return (0);
	}

	(void) memset(read_buf, 0, sector_size);
	/* Read the sector */
	debug(5, "Reading the block %d\n",
		(uint32_t)req->reqreassign_block.blockno);

	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));

	cdb.scc_cmd = SCMD_READ_G1;
	FORMG1ADDR(&cdb, req->reqreassign_block.blockno);
	FORMG1COUNT(&cdb, 1);	/* One block */

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)read_buf;
	ucmd.uscsi_buflen = sector_size;
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	(void) do_uscsi_cmd(door_dp->dd_fd, &ucmd, USCSI_READ);

	/* Write the data back */

	debug(5, "Writing the block %d\n",
		(uint32_t)req->reqreassign_block.blockno);
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset((void *) &cdb, 0, sizeof (cdb));

	cdb.scc_cmd = SCMD_WRITE_G1;
	FORMG1ADDR(&cdb, req->reqreassign_block.blockno);
	FORMG1COUNT(&cdb, 1);	/* One block */

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)read_buf;
	ucmd.uscsi_buflen = sector_size;
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ret_val = do_uscsi_cmd(door_dp->dd_fd, &ucmd, USCSI_WRITE);
	free(read_buf);
	if (ret_val || ucmd.uscsi_status) {
		debug(5, "Reassign failed: %d - %d errno = %d\n",
			ret_val, ucmd.uscsi_status, errno);
		ret_val = scsi_reassign_block(door_dp->dd_fd,
			req->reqreassign_block.blockno);
		if (ret_val != 0)
			return (-1);
		return (0);
	}

	return (0);
}

static void
close_door_descs(door_desc_t *dp, uint_t ndesc)
{
	while (ndesc > 0) {
		int fd = dp->d_data.d_desc.d_descriptor;
		if (dp->d_attributes & DOOR_DESCRIPTOR)
			(void) close(fd);
		dp++;
		ndesc--;
	}
}

/*
 * This is a Death Door's service procedure.
 *
 * This procedure is a NOP because the Death Door functionality
 * is no longer used and will be removed in the future.
 */
/*ARGSUSED*/
static void
death_servproc(void *cookie, char *argp, size_t arg_size,
    door_desc_t *dp, uint_t ndesc)
{
	debug(1, "death_servproc[%d]: argp = 0x%p  "
	    "Death Door[%d]\n", pthread_self(), (void *)argp,
	    ((door_data_t *)cookie)->dd_ddoor_descriptor);

	(void) door_return(NULL, 0, NULL, 0);
}

/*
 * This is a Client Door's service procedure.
 *
 * This procedure is specified in the door_create() of a Client Door,
 * and its functionality represents the bulk of services that the
 * rpc.smserverd daemon offers.
 */
static void
client_servproc(void *cookie, char *argp, size_t arg_size,
    door_desc_t *dp, uint_t ndesc)
{
	smedia_services_t	*req;
	smedia_services_t	rmsvc;
	smedia_reterror_t	reterror;
	smedia_retraw_read_t	retraw_read;
	struct scsi_inquiry	inq;
	struct dk_minfo		media_info;
	struct dk_geom		dkgeom;
	int32_t			status;
	uchar_t			data[18];
	int32_t			completed = 0;
	door_data_t		*door_dp;
	size_t			retbuf_size;
	struct uscsi_cmd	ucmd;
	union scsi_cdb		cdb;
	int32_t			ret_val, err;
	char			rq_data[RQ_LEN];
	uint_t			nexpected_desc;
	struct vtoc		vtoc;
	struct extvtoc		extvtoc;

	door_dp = (door_data_t *)cookie;
	req = (smedia_services_t *)((void *)argp);

	debug(10, "client_servproc[%d]...\n", pthread_self());

	if (argp == DOOR_UNREF_DATA) {
		debug(5, "client_servproc[%d]: req = DOOR_UNREF_DATA\n",
		    pthread_self());
		debug(5, "Client has exited. Cleaning up resources\n");

		(void) mutex_lock(&svcstate_lock);
		svccount--;
		(void) mutex_unlock(&svcstate_lock);

		cleanup(door_dp);
		return;
	}

	(void) mutex_lock(&svcstate_lock);
	svcstate = _SERVED;
	(void) mutex_unlock(&svcstate_lock);

	rmsvc.in.cnum = req->in.cnum;
	debug(5, "client_servproc[%d]: req = %s\n", pthread_self(),
	    xlate_cnum(req->in.cnum));

	/*
	 * Our caller may have passed more descriptors than we expected.
	 * If so, we silently close (and ignore) them.
	 */
	nexpected_desc = (req->in.cnum == SMEDIA_CNUM_SET_SHFD) ? 1 : 0;
	if (ndesc > nexpected_desc) {
		close_door_descs(dp + nexpected_desc, ndesc - nexpected_desc);
	}

	switch (req->in.cnum) {
	default:
		debug(5, "client_servproc: unknown command %d\n", req->in.cnum);
		door_ret_err(&reterror, ENOTSUP);
		break;

	case SMEDIA_CNUM_SET_SHFD:
		if (ndesc == 0)
			door_ret_err(&reterror, EINVAL);
		/*
		 * Allocate shared memory for this connection.
		 * If this connection already has shared memory,
		 * deallocate before doing the allocation.
		 */
		ret_val = set_shfd(door_dp, dp->d_data.d_desc.d_descriptor,
		    req);
		if (ret_val == 0) {
			reterror.cnum = SMEDIA_CNUM_SET_SHFD;
			reterror.errnum = 0;

			my_door_return((char *)&reterror,
				sizeof (smedia_reterror_t), 0, 0);
		} else {
			(void) close(dp->d_data.d_desc.d_descriptor);
			door_ret_err(&reterror, ret_val);
		}
		break;

	case SMEDIA_CNUM_RAW_READ:
		debug(10, " arg size = %d blk num=0x%x nbytes = 0x%x \n",
			(int)arg_size,
			(uint32_t)req->reqraw_read.blockno,
			req->reqraw_read.nbytes);
		retbuf_size = sizeof (smedia_retraw_read_t);
		if (req->reqraw_read.nbytes == 0) {
			/* Nothing to write */
			rmsvc.retraw_write.nbytes = 0;
			my_door_return((char *)&rmsvc,
				sizeof (smedia_retraw_write_t), 0, 0);
		}
		retraw_read.cnum = SMEDIA_CNUM_RAW_READ;
		ret_val = raw_read(door_dp, req);
		if (ret_val == -1) {
			door_ret_err(&reterror, errno);
		}
		retraw_read.nbytes = ret_val;
		my_door_return((char *)&retraw_read, retbuf_size, 0, 0);
		break;

	case	SMEDIA_CNUM_USCSI_CMD:
		retbuf_size = sizeof (smedia_retuscsi_cmd_t);
		rmsvc.retuscsi_cmd.cnum = SMEDIA_CNUM_USCSI_CMD;
		ucmd.uscsi_flags = req->requscsi_cmd.uscsi_flags;
		ucmd.uscsi_cdb = (caddr_t)&req->requscsi_cmd.uscsi_cdb;
		ucmd.uscsi_cdblen = req->requscsi_cmd.uscsi_cdblen;
		ucmd.uscsi_bufaddr = (caddr_t)door_dp->dd_buf;
		ucmd.uscsi_buflen = req->requscsi_cmd.uscsi_buflen;
		ucmd.uscsi_timeout = req->requscsi_cmd.uscsi_timeout;
		ucmd.uscsi_rqlen = req->requscsi_cmd.uscsi_rqlen;
		ucmd.uscsi_rqbuf = (caddr_t)&rmsvc.retuscsi_cmd.uscsi_rqbuf;
		debug(5, "USCSI CMD 0x%x requested.\n",
		    req->requscsi_cmd.uscsi_cdb[0]);
		/*
		 * Check the device type and invalid flags specified.
		 * We permit operations only on CDROM devices types.
		 */
		errno = invalid_uscsi_operation(door_dp, &ucmd);
		if (errno) {
			door_ret_err(&reterror, errno);
		}

		if ((req->requscsi_cmd.uscsi_buflen) &&
		    ((req->requscsi_cmd.uscsi_buflen > door_dp->dd_buf_len) ||
		    (door_dp->dd_buf == NULL))) {
			debug(5, "uscsi_cmd failed: uscsi_buflen=0x%x "
			    "dd_buf_len=0x%x dd_buf=0x%p\n",
			    req->requscsi_cmd.uscsi_buflen,
			    door_dp->dd_buf_len,
			    door_dp->dd_buf);
			errno = EINVAL;
			door_ret_err(&reterror, errno);
		}
		ret_val = do_uscsi_cmd(door_dp->dd_fd,
			&ucmd, req->requscsi_cmd.uscsi_flags);
		rmsvc.retuscsi_cmd.uscsi_status = ucmd.uscsi_status;
		rmsvc.retuscsi_cmd.uscsi_resid = ucmd.uscsi_resid;
		rmsvc.retuscsi_cmd.uscsi_rqstatus = ucmd.uscsi_rqstatus;
		rmsvc.retuscsi_cmd.uscsi_rqresid = ucmd.uscsi_rqresid;
		rmsvc.retuscsi_cmd.uscsi_retval = ret_val;
		rmsvc.retuscsi_cmd.uscsi_errno = errno;
		if (ret_val || ucmd.uscsi_status) {
			debug(5, "uscsi_cmd failed: %d - %d errno = %d\n",
				ret_val, ucmd.uscsi_status, errno);
		}
		my_door_return((char *)&rmsvc, retbuf_size, 0, 0);
		break;

	case SMEDIA_CNUM_RAW_WRITE:
		if (req->reqraw_write.nbytes == 0) {
			/* Nothing to write */
			rmsvc.retraw_write.nbytes = 0;
			my_door_return((char *)&rmsvc,
				sizeof (smedia_retraw_write_t), 0, 0);
		}
		ret_val = raw_write(door_dp, req);
		if (ret_val == -1)
			door_ret_err(&reterror, errno);
		rmsvc.retraw_write.nbytes = ret_val;
		my_door_return((char *)&rmsvc, sizeof (smedia_retraw_write_t),
			0, 0);
		break;

	case SMEDIA_CNUM_GET_DEVICE_INFO:

		(void) memset((void *) &inq, 0, sizeof (inq));
		(void) memset((void *) &ucmd, 0, sizeof (ucmd));
		(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
		cdb.scc_cmd = SCMD_INQUIRY;
		FORMG0COUNT(&cdb, sizeof (inq));
		ucmd.uscsi_cdb = (caddr_t)&cdb;
		ucmd.uscsi_cdblen = CDB_GROUP0;
		ucmd.uscsi_bufaddr = (caddr_t)&inq;
		ucmd.uscsi_buflen = sizeof (inq);
		ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
		ucmd.uscsi_rqlen = RQ_LEN;
		ucmd.uscsi_rqbuf = rq_data;
		ret_val = do_uscsi_cmd(door_dp->dd_fd,
			&ucmd, USCSI_READ|USCSI_RQENABLE);
		if (ret_val || ucmd.uscsi_status) {
			debug(5, "inquiry failed: %d - %d errno = %d\n",
				ret_val, ucmd.uscsi_status, errno);
			door_ret_err(&reterror, errno);
		}

		debug(5, "%s\n", inq.inq_vid);
		debug(5, "%s\n", rmsvc.retget_device_info.sm_vendor_name);

		(void) strlcpy(rmsvc.retget_device_info.sm_vendor_name,
			inq.inq_vid, 8);
		rmsvc.retget_device_info.sm_vendor_name[8] = 0;
		(void) strlcpy(rmsvc.retget_device_info.sm_product_name,
			inq.inq_pid, 16);
		rmsvc.retget_device_info.sm_product_name[16] = 0;
		(void) strlcpy(rmsvc.retget_device_info.sm_firmware_version,
			inq.inq_revision, 4);
		rmsvc.retget_device_info.sm_firmware_version[4] = ' ';
		(void) strlcpy(
			&rmsvc.retget_device_info.sm_firmware_version[5],
				inq.inq_serial, 12);
		rmsvc.retget_device_info.sm_product_name[17] = 0;

		rmsvc.retget_device_info.sm_interface_type = IF_SCSI;

		debug(5, "Vendor name = %s\n",
		    rmsvc.retget_device_info.sm_vendor_name);
		debug(5, "product name = %s\n",
		    rmsvc.retget_device_info.sm_product_name);
		debug(5, "Firmware revision = %s\n",
		    rmsvc.retget_device_info.sm_firmware_version);

		my_door_return((char *)&rmsvc.retget_device_info,
			sizeof (smedia_retget_device_info_t), 0, 0);
		break;

	case	SMEDIA_CNUM_GET_MEDIUM_PROPERTY:

		(void) memset((void *)&rmsvc.retget_medium_property.smprop,
			0, sizeof (smmedium_prop_t));

		ret_val = ioctl(door_dp->dd_fd, DKIOCGMEDIAINFO, &media_info);

		if (ret_val < 0) {
			uint32_t capacity;
			uint32_t blocksize;
			/*
			 * Devices may fail DKIOCGMEDIAINFO if an unformed
			 * media is inserted. We can get the capacity
			 * information from the SCMD_READ_FORMAT_CAP command.
			 */

			debug(5, "DKIOCGMEDIAINFO failed; using "
			    "SCMD_READ_FORMAT_CAP");
			ret_val = get_media_capacity(door_dp->dd_fd,
			    &capacity, &blocksize);

			if (ret_val >= 0) {
				media_info.dki_lbsize =	blocksize;
				media_info.dki_capacity = capacity;
			} else {
				debug(5, "SCMD_READ_FORMAT_CAP failed");
				door_ret_err(&reterror, errno);
			}
		}
		rmsvc.retget_medium_property.smprop.sm_blocksize =
		    media_info.dki_lbsize;
		rmsvc.retget_medium_property.smprop.sm_capacity =
		    media_info.dki_capacity;

		rmsvc.retget_medium_property.smprop.sm_media_type =
		    media_info.dki_media_type;
		/*
		 * These devices show as SCSI devices but we need to treat it
		 * differently. so we need a seperate class.
		 */
		if (get_device_type_scsi(door_dp->dd_fd, &inq) == SCSI_FLOPPY) {
			rmsvc.retget_medium_property.smprop.sm_media_type =
			    SM_SCSI_FLOPPY;
		}

		/* Check for EFI type because DKIOCGGEOM does not support EFI */
		ret_val = ioctl(door_dp->dd_fd, DKIOCGEXTVTOC, &extvtoc);
		if (ret_val < 0 && errno == ENOTTY)
			ret_val = ioctl(door_dp->dd_fd, DKIOCGVTOC, &vtoc);

		if (!((ret_val < 0) && (errno == ENOTSUP))) {
			ret_val = ioctl(door_dp->dd_fd, DKIOCGGEOM, &dkgeom);
			if (ret_val < 0)  {
				/*
				 * DKIOCGGEOM may fail for unformed floppies.
				 * We need to generate the appropriate geometry
				 * information.
				 */
				if (rmsvc.retget_medium_property.smprop.
				    sm_media_type == SM_SCSI_FLOPPY) {
					ret_val = get_floppy_geom(
					    door_dp->dd_fd,
					    media_info.dki_capacity, &dkgeom);

					if (ret_val < 0) {
						debug(5, "Cannot determine "
						    "media size");
						door_ret_err(&reterror, errno);
					}
				} else {
#ifdef sparc
					debug(5, "DKIOCGGEOM ioctl failed");
					door_ret_err(&reterror, errno);
#else /* !sparc */
					/*
					 * Try getting Physical geometry on x86.
					 */
					ret_val = ioctl(door_dp->dd_fd,
					    DKIOCG_PHYGEOM, &dkgeom);
					if (ret_val < 0) {
						debug(5, "DKIOCG_PHYGEOM "
						    "ioctl failed");
						door_ret_err(&reterror, errno);
					}
#endif /* sparc */
				}
			}


			/*
			 * Some faked geometry may not have pcyl filled in so
			 * later calculations using this field will be
			 * incorrect.  We will substitute it with the number of
			 * available cylinders.
			 */
			if (dkgeom.dkg_pcyl == 0)
				rmsvc.retget_medium_property.smprop.sm_pcyl =
				    dkgeom.dkg_ncyl;
			else
				rmsvc.retget_medium_property.smprop.sm_pcyl =
				    dkgeom.dkg_pcyl;

			rmsvc.retget_medium_property.smprop.sm_nhead =
			    dkgeom.dkg_nhead;
			rmsvc.retget_medium_property.smprop.sm_nsect =
			    dkgeom.dkg_nsect;
		}

		debug(1, "properties are: lbasize = %d, cap = %llu",
		    media_info.dki_lbsize, media_info.dki_capacity);

		my_door_return((char *)&rmsvc.retget_medium_property,
			sizeof (smedia_retget_medium_property_t), 0, 0);
		break;

	case	SMEDIA_CNUM_GET_PROTECTION_STATUS:
		switch (get_device_type_scsi(door_dp->dd_fd, &inq)) {
		case SCSI_FLOPPY:
			status = scsi_floppy_media_status(door_dp->dd_fd);
			break;
		case SCSI_IOMEGA:
			status = scsi_zip_media_status(door_dp->dd_fd);
			break;
		case SCSI_GENERIC:
			status = scsi_media_status(door_dp->dd_fd);
			break;
		default:
			door_ret_err(&reterror, errno);
		}
		if (status < 0)
			door_ret_err(&reterror, errno);

		rmsvc.retget_protection_status.prot_state.sm_new_state  =
			status;

		my_door_return((char *)&rmsvc.retget_protection_status,
			sizeof (smedia_retget_protection_status_t), 0, 0);
		break;

	case	SMEDIA_CNUM_SET_PROTECTION_STATUS:

		ret_val = set_protection_status(door_dp, req);
		if (ret_val == -1)
			door_ret_err(&reterror, errno);
		else
			my_door_return((char *)&rmsvc.retset_protection_status,
				sizeof (smedia_retset_protection_status_t),
				0, 0);
		break;

	case SMEDIA_CNUM_FORMAT:
		switch (get_device_type_scsi(door_dp->dd_fd, &inq)) {
		case SCSI_FLOPPY:
			info("formatting floppy");
			err = scsi_floppy_format(door_dp->dd_fd,
				req->reqformat.flavor, req->reqformat.mode);

			break;
		case SCSI_IOMEGA:
			err = scsi_zip_format(door_dp->dd_fd,
				req->reqformat.flavor, req->reqformat.mode);
			break;
		case SCSI_GENERIC:
			err = scsi_format(door_dp->dd_fd,
				req->reqformat.flavor, req->reqformat.mode);
			break;
		default:
			door_ret_err(&reterror, ENOTSUP);
		}

		if (err)
			door_ret_err(&reterror, errno);
		my_door_return((char *)&rmsvc.retformat,
			sizeof (smedia_retformat_t), 0, 0);

		break;

	case SMEDIA_CNUM_CHECK_FORMAT_STATUS:

		(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
		(void) memset((void *) &ucmd, 0, sizeof (ucmd));
		(void) memset((void *) &data, 0, sizeof (data));
		cdb.scc_cmd = SCMD_REQUEST_SENSE;
		cdb.g0_count0 = sizeof (data);
		ucmd.uscsi_cdb = (caddr_t)&cdb;
		ucmd.uscsi_cdblen = CDB_GROUP0;
		ucmd.uscsi_bufaddr = (caddr_t)&data;
		ucmd.uscsi_buflen = sizeof (data);
		ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
		ucmd.uscsi_rqlen = RQ_LEN;
		ucmd.uscsi_rqbuf = rq_data;
		ret_val = do_uscsi_cmd(door_dp->dd_fd,
			&ucmd, USCSI_READ|USCSI_RQENABLE);
		if (ret_val || ucmd.uscsi_status) {
			debug(5, "Request sense failed: %d - %d errno = %d\n",
				ret_val, ucmd.uscsi_status, errno);
			door_ret_err(&reterror, errno);
		}

		if ((data[0] & 0x7F) == DEFERRED_ERROR) {
		/* Deffered error. The format must have failed */
			debug(5, "format failed!\n");
			door_ret_err(&reterror, EIO);
		}

		if (data[SKSV_OFFSET] & SKSV_FIELD) {
			completed =
				(data[FORMAT_PROGRESS_INDICATOR_OFFSET_0] << 8)
				| data[FORMAT_PROGRESS_INDICATOR_OFFSET_1];
			completed = (completed*100/65536);
		} else {
			completed = (100);
		}
		rmsvc.retcheck_format_status.percent_complete = completed;
		my_door_return((char *)&rmsvc.retcheck_format_status,
			sizeof (smedia_retcheck_format_status_t), 0, 0);
		break;

	case SMEDIA_CNUM_REASSIGN_BLOCK:

		ret_val = reassign_block(door_dp, req);
		if (ret_val == -1)
			door_ret_err(&reterror, errno);
		my_door_return((char *)&rmsvc.retreassign_block,
			sizeof (smedia_retreassign_block_t), 0, 0);
		break;

	}	/* end of switch */

	debug(10, "Exiting client server...\n");
	my_door_return((char *)&reterror, sizeof (smedia_reterror_t), 0, 0);
}

/*
 * This is the service procedure for the door that is associated with
 * the (doorfs) filesystem Door that is created at 'smedia_service'.
 */
/*ARGSUSED*/
static void
main_servproc(void *server_data, char *argp, size_t arg_size,
    door_desc_t *dp, uint_t ndesc)
{
	smedia_services_t	*req;
	door_cred_t	door_credentials;
	int		ret_val;
	door_data_t	*ddata;
	smedia_reterror_t	reterror;
	smedia_reterror_t	retok;
	struct	stat	stat;
	door_desc_t	*didpp;
	struct dk_cinfo dkinfo;
	uint_t		nexpected_desc;

	debug(10, "Entering main_servproc[%d].\n", pthread_self());

	didpp = dp;
	(void) mutex_lock(&svcstate_lock);
	svcstate = _SERVED;
	(void) mutex_unlock(&svcstate_lock);

	reterror.cnum = SMEDIA_CNUM_ERROR;
	reterror.errnum = SMEDIA_FAILURE;

	if (argp == NULL) {
		debug(5, "argp is NULL\n");
		if (ndesc > 0)
			close_door_descs(dp, ndesc);
		my_door_return((char *)&reterror,
		    sizeof (smedia_reterror_t), 0, 0);
	}

	req = (smedia_services_t *)((void *)argp);

	retok.cnum = req->in.cnum;
	retok.errnum = 0;

	debug(5, "req = %s arg_size = 0x%x \n",
	    xlate_cnum(req->reqopen.cnum), arg_size);

	/*
	 * Our caller may have passed more descriptors than we expected.
	 * If so, we silently close (and ignore) them.
	 */
	nexpected_desc = (req->in.cnum == SMEDIA_CNUM_OPEN_FD) ? 1 : 0;
	if (ndesc > nexpected_desc) {
		close_door_descs(dp + nexpected_desc, ndesc - nexpected_desc);
	}

	switch (req->in.cnum) {
	default:
		debug(5, "main_servproc: unknown command 0x%x\n",
		    req->reqopen.cnum);
		break;

	case SMEDIA_CNUM_PING:
		/*
		 * This service is to indicate that server is up and
		 * running. It is usually called from another instance of
		 * server that is started.
		 */
		reterror.cnum = SMEDIA_CNUM_PING;
		reterror.errnum = 0;
		my_door_return((char *)&reterror,
		    sizeof (smedia_reterror_t), 0, 0);
		break;


	case SMEDIA_CNUM_OPEN_FD:

		debug(5, "ndesc = %d\n", ndesc);
		if (ndesc == 0) {
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), 0, 0);
		}
		debug(5, "Checking file descriptor of target device\n");
		if (fstat(didpp->d_data.d_desc.d_descriptor, &stat) < 0) {
			warning(gettext("main_servproc:fstat failed. "
			    "errno = %d\n"), errno);
			(void) close(didpp->d_data.d_desc.d_descriptor);
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), 0, 0);
		}
		debug(5, "descriptor = %d st_mode = 0x%lx\n",
		    didpp->d_data.d_desc.d_descriptor,
		    stat.st_mode);

		/* Obtain the credentials of the user */
		ret_val = door_cred(&door_credentials);
		if (ret_val < 0) {
			warning(gettext("main_servproc:door_cred "
			    "failed. errno = %d\n"), errno);
			(void) close(didpp->d_data.d_desc.d_descriptor);
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), 0, 0);
		}
		if (ioctl(didpp->d_data.d_desc.d_descriptor, DKIOCINFO,
			&dkinfo) == -1) {
			warning(gettext("main_servproc:DKIOCINFO failed. "
			    "errno = %d\n"), errno);
			(void) close(didpp->d_data.d_desc.d_descriptor);
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), 0, 0);
		}

		ddata = (door_data_t *)calloc(1, sizeof (door_data_t));
		if (ddata == NULL) {
			warning(gettext("main_servproc:calloc failed. "
			    "errno = %d\n"), errno);
			(void) close(didpp->d_data.d_desc.d_descriptor);
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), 0, 0);
		}
		ddata->dd_stat = stat;
		ddata->dd_cred = door_credentials;
		ddata->dd_fd = didpp->d_data.d_desc.d_descriptor;
		ddata->dd_buf = NULL;
		ddata->dd_buf_len = 0;
		ddata->dd_buffd = -1;
		ddata->dd_sector_size = 0;
		ddata->dd_dkinfo = dkinfo;
		debug(5, "ddata = 0x%p \n", (void *)ddata);

		/* specify a function that'll customize our door threads */
		(void) door_server_create(sm_door_server_create);
		debug(5, "door_server_create called.\n");

		(void) mutex_lock(&ddata->dd_lock);

		/* create Client Door */
		ddata->dd_cdoor_descriptor =
		    door_create(client_servproc,
		    (void *)ddata, DOOR_PRIVATE | DOOR_NO_CANCEL | DOOR_UNREF);

		if (ddata->dd_cdoor_descriptor < 0) {
			/* then door_create() failed */
			int err = errno;

			(void) mutex_unlock(&ddata->dd_lock);

			warning(gettext("main_servproc: door_create of Client "
			    "Door failed = %d\n"), err);
			free(ddata);

			/* close target device */
			(void) close(didpp->d_data.d_desc.d_descriptor);
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), 0, 0);
		}

		/* create Death Door */
		ddata->dd_ddoor_descriptor =
		    door_create(death_servproc, (void *)ddata,
		    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
		if (ddata->dd_ddoor_descriptor < 0) {
			warning(gettext("main_servproc: door_create of Death "
			    "Door failed = %d\n"), errno);
		} else {
			(void) door_setparam(ddata->dd_ddoor_descriptor,
			    DOOR_PARAM_DATA_MAX, 0);
		}

		debug(5, "main_servproc[%d]: Client Door = %d, "
		    "Death Door = %d", pthread_self(),
		    ddata->dd_cdoor_descriptor, ddata->dd_ddoor_descriptor);

		audit_init(ddata);

		/* wait until sm_server_thread does door_bind() */
		(void) cond_wait(&ddata->dd_cv_bind, &ddata->dd_lock);

		(void) mutex_unlock(&ddata->dd_lock);

		(void) mutex_lock(&svcstate_lock);
		svccount++;
		(void) mutex_unlock(&svcstate_lock);

		if (ddata->dd_ddoor_descriptor < 0) {
			/* Return only the Client Door to the client. */
			ddata->dd_cdoor.d_attributes = (DOOR_DESCRIPTOR);
			my_door_return((char *)&reterror,
			    sizeof (smedia_reterror_t), &ddata->dd_desc[0], 1);
		} else {
			/*
			 * Return the Client Door and Death Door
			 * to the client.
			 */
			debug(5, "retok.cnum = 0x%x\n", retok.cnum);
			ddata->dd_cdoor.d_attributes = (DOOR_DESCRIPTOR);
			ddata->dd_ddoor.d_attributes = (DOOR_DESCRIPTOR);
			my_door_return((char *)&retok,
			    sizeof (smedia_reterror_t), &ddata->dd_desc[0], 2);
		}
		break;
	}

	debug(10, "exiting main_servproc. \n");
	my_door_return((char *)&reterror, sizeof (smedia_reterror_t), 0, 0);
}

/* ARGSUSED */
static void
term_handler(int sig, siginfo_t *siginfo, void *sigctx)
{
	warning(gettext("thread[%d]: Received signal %d. Ignoring it.\n"),
	    pthread_self(),
	    sig);
}

/* ARGSUSED */
static void
hup_handler(int sig, siginfo_t *siginfo, void *sigctx)
{
	warning(gettext("thread[%d]: Received signal %d. Ignoring it.\n"),
	    pthread_self(),
	    sig);
}

/*ARGSUSED*/
static void
sig_handler(int sig, siginfo_t *siginfo, void *sigctx)
{
	warning(gettext("thread[%d]: Received signal %d. Ignoring it.\n"),
	    pthread_self(),
	    sig);
}

/*ARGSUSED*/
static void
badsig_handler(int sig, siginfo_t *siginfo, void *sigctx)
{
	fatal(BADSIG_MSG, pthread_self(), sig, siginfo->si_addr,
	    siginfo->si_trapno,
	    siginfo->si_pc);
}

/*ARGSUSED*/
static void *
init_server(void *argp)
{
	int	i, fd;
	struct	sigaction	act;
	struct	rlimit		rlim;

	debug(10, "init_server  running\n");

	(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);


	if (geteuid() != 0) fatal("Must be root to execute smserverd\n");


	/*
	 * setup signal handlers.
	 */

	for (i = 0; i < N_BADSIGS; i++) {
		act.sa_sigaction = badsig_handler;
		(void) sigemptyset(&act.sa_mask);
		act.sa_flags = SA_SIGINFO;
		if (sigaction(badsigs[i], &act, NULL) == -1)
			warning(gettext(SIGACT_FAILED), strsignal(badsigs[i]),
				strerror(errno));
	}

	/*
	 * Ignore SIGHUP until all the initialization is done.
	 */
	act.sa_handler = SIG_IGN;
	(void) sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (sigaction(SIGHUP, &act, NULL) == -1)
		warning(gettext(SIGACT_FAILED), strsignal(SIGHUP),
			strerror(errno));
	/*
	 * Increase file descriptor limit to the most it can possibly
	 * be.
	 */
	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
		warning(gettext("getrlimit for fd's failed; %m\n"));
	}

	rlim.rlim_cur = rlim.rlim_max;

	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
		warning(gettext("setrlimit for fd's failed; %m\n"));
	}
	(void) enable_extended_FILE_stdio(-1, -1);

	server_door = door_create(main_servproc, (void *)&server_data, 0);
	if (server_door == -1) {
		debug(1, "main door_create");
		exit(1);
	}

	(void) unlink(smedia_service);
	fd = open(smedia_service, O_RDWR|O_CREAT|O_EXCL, 0644);
	if (fd < 0) {
		debug(5, "could not open %s.\n", smedia_service);
		exit(1);
	}
	(void) close(fd);
	server_fd = fattach(server_door, smedia_service);
	if (server_fd == -1) {
		debug(1, "main fattach");
		exit(1);
	}
	server_data.sd_door = server_door;
	server_data.sd_fd = server_fd;

	/*
	 * setup signal handlers for post-init
	 */

	act.sa_sigaction = hup_handler;
	(void) sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	if (sigaction(SIGHUP, &act, NULL) == -1)
		warning(gettext(SIGACT_FAILED), strsignal(SIGHUP),
		    strerror(errno));

	act.sa_sigaction = term_handler;
	(void) sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	if (sigaction(SIGTERM, &act, NULL) == -1)
		warning(gettext(SIGACT_FAILED), strsignal(SIGTERM),
		    strerror(errno));

	act.sa_sigaction = sig_handler;
	(void) sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	if (sigaction(SIGINT, &act, NULL) == -1)
		warning(gettext(SIGACT_FAILED), strsignal(SIGHUP),
		    strerror(errno));

	act.sa_sigaction = sig_handler;
	(void) sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	if (sigaction(SIGQUIT, &act, NULL) == -1)
		warning(gettext(SIGACT_FAILED), strsignal(SIGHUP),
		    strerror(errno));
	debug(10, "init_server completed successfully\n");

	server_data.sd_init_state = INIT_DONE;
	return (NULL);
}

static int
server_exists()
{
	door_arg_t		darg;
	smedia_reqping_t	req_ping;
	smedia_retping_t	*ret_ping;
	int			doorh;
	door_info_t		dinfo;
	char    rbuf[sizeof (smedia_services_t) + sizeof (door_desc_t)];

	doorh = open(smedia_service, O_RDONLY);
	if (doorh < 0)
		return (0);
	if (door_info(doorh, &dinfo) < 0) {
		(void) close(doorh);
		return (0);
	}
	if (dinfo.di_attributes & DOOR_REVOKED) {
		(void) close(doorh);
		return (0);
	}

	req_ping.cnum = SMEDIA_CNUM_PING;

	darg.data_ptr = (char *)&req_ping;
	darg.data_size = sizeof (smedia_reqping_t);
	darg.desc_ptr = NULL;
	darg.desc_num = 0;
	darg.rbuf = rbuf;
	darg.rsize = sizeof (rbuf);

	if (door_call(doorh, &darg) < 0) {
		(void) close(doorh);
		return (0);
	}
	ret_ping = (smedia_retping_t *)((void *)darg.data_ptr);
	if (ret_ping->cnum != SMEDIA_CNUM_PING) {
		(void) close(doorh);
		return (0);
	}

	(void) close(doorh);
	return (1);
}

static int
get_run_level()
{
	int	run_level;
	struct utmpx	*utmpp;

	setutxent();
	while ((utmpp = getutxent()) != NULL) {
		if (utmpp->ut_type == RUN_LVL) {
			run_level = atoi(
				&utmpp->ut_line[strlen("run-level ")]);
		}
	}
	return (run_level);
}

/*ARGSUSED*/
static void *
closedown(void *arg)
{

	int	current_run_level;

	/*CONSTCOND*/
#ifndef lint
	while (1) {
#endif
		(void) sleep(SVC_CLOSEDOWN/2);

		/*
		 * If the server was started at init level 1
		 * and the current init level is 1 then
		 * do not exit from server. This server will run
		 * until it is explicitly stopped by the user.
		 */
		if (svcstart_level == 1) {
			current_run_level = get_run_level();
			if (current_run_level == 1)
#ifndef lint
				continue;
#else
				return (NULL);
#endif
			/*
			 * who ever started the server at level 1 has
			 * forgotten to stop the server. we will kill ourself.
			 */
			debug(5,
			"Terminating the server started at init level 1\n");
			exit(0);
		}

		if (mutex_trylock(&svcstate_lock) != 0)
#ifndef lint
			continue;
#else
			return (NULL);
#endif
		if (svcstate == _IDLE && svccount == 0) {
			int size;
			int i, openfd = 0;

			size = svc_max_pollfd;
			for (i = 0; i < size && openfd < 2; i++)
				if (svc_pollfd[i].fd >= 0)
					openfd++;
			if (openfd <= 1) {
				debug(5,
				"Exiting the server from closedown routine.\n");
				exit(0);
			}
		} else
			svcstate = _IDLE;

		(void) mutex_unlock(&svcstate_lock);
#ifndef lint
	}
#else
	return (NULL);
#endif

}

static void
usage()
{
	warning(gettext("usage: %s [-L loglevel] level of debug information\n"),
		prog_name);
}


/*ARGSUSED*/
int
main(int argc, char **argv)
{
	int c;
	pthread_attr_t	attr;

	(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);

	prog_name = argv[0];

	(void) sigset(SIGPIPE, SIG_IGN);

	while ((c = getopt(argc, argv, "L:")) != -1) {
		switch (c) {
		case 'L':
			debug_level = atoi((char *)optarg);
			break;
		default:
			usage();
			break;
		}
	}

	/*
	 * If stdin looks like a TLI endpoint, we assume
	 * that we were started by a port monitor. If
	 * t_getstate fails with TBADF, this is not a
	 * TLI endpoint.
	 */
	if (t_getstate(0) != -1 || t_errno != TBADF) {
		char *netid;
		struct netconfig *nconf = NULL;
		SVCXPRT *transp;
		int pmclose;

		openlog(prog_name, LOG_PID, LOG_DAEMON);

		debug(1, gettext("server started by port monitor.\n"));
		if ((netid = getenv("NLSPROVIDER")) == NULL) {
		/* started from inetd */
			pmclose = 1;
		} else {
			if ((nconf = getnetconfigent(netid)) == NULL)
				syslog(LOG_ERR, gettext(
					"cannot get transport info"));

			pmclose = (t_getstate(0) != T_DATAXFER);
		}
		if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
			syslog(LOG_ERR, gettext("cannot create server handle"));
			exit(1);
		}
		if (nconf)
			freenetconfigent(nconf);
		if (!svc_reg(transp, SMSERVERPROG, SMSERVERVERS,
			smserverprog_1, 0)) {
			syslog(LOG_ERR, gettext(
			"unable to register (SMSERVERPROG, SMSERVERVERS)."));
			exit(1);
		}
		svcstart_level = get_run_level();
		if (pmclose) {
			(void) pthread_attr_init(&attr);
			(void) pthread_attr_setscope(&attr,
			    PTHREAD_SCOPE_SYSTEM);
			(void) pthread_attr_setdetachstate(&attr,
			    PTHREAD_CREATE_DETACHED);
			if (pthread_create(NULL, &attr, closedown, NULL) != 0) {
				syslog(LOG_ERR, gettext(
					"cannot create closedown thread"));
				exit(1);
			}
			(void) pthread_attr_destroy(&attr);
		}
		svc_run();
		exit(1);
		/* NOTREACHED */
	} else {
		/*
		 * Started by library or manually.
		 */
		/*
		 * Check to see if the server is already running.
		 * There is no need to log messages in the syslog file
		 * because server will get launched each time libsmedia
		 * library calls are made at init 1 level.
		 * We ensure that only one copy will run.
		 */
		debug(1, gettext("server started manually.\n"));
		if (server_exists()) {
			exit(0);
		}
		svcstart_level = get_run_level();
		(void) pthread_attr_init(&attr);
		(void) pthread_attr_setscope(&attr,
		    PTHREAD_SCOPE_SYSTEM);
		(void) pthread_attr_setdetachstate(&attr,
		    PTHREAD_CREATE_DETACHED);
		if (pthread_create(NULL, &attr, closedown, NULL) != 0) {
			syslog(LOG_ERR, gettext(
				"cannot create closedown thread"));
			exit(1);
		}
		(void) pthread_attr_destroy(&attr);
		(void) init_server(NULL);
		for (;;) (void) pause();
	}
	return (0);
}


/*ARGSUSED*/
static int32_t
scsi_floppy_write_protect(int32_t fd, smwp_state_t *wp)
{
	debug(5, "Invalid mode\n");
	errno = ENOTSUP;

	return (-1);
}

/*
 * Generate standard geometry information for SCSI floppy devices. And
 * register the geometry with the SCSI driver. This will expand as more
 * formats are added.
 */

/*ARGSUSED*/
static int32_t
get_floppy_geom(int32_t fd, uint32_t capacity, struct dk_geom *dkgeom)
{


	debug(5, "get_floppy_geom: capacity = 0x%x\n", capacity);

	switch (capacity) {

		case 0x5A0:
			/* Double Density 720K */
			dkgeom->dkg_pcyl = 80;
			dkgeom->dkg_ncyl = 80;
			dkgeom->dkg_nhead = 2;
			dkgeom->dkg_nsect = 9;
			break;
		case 0x4D0:
			/* High Density 1.25MB */
			dkgeom->dkg_pcyl = 77;
			dkgeom->dkg_ncyl = 77;
			dkgeom->dkg_nhead = 2;
			dkgeom->dkg_nsect = 9;
			break;
		case 0xB40:
			/* High Density 1.44MB */

			dkgeom->dkg_pcyl = 80;
			dkgeom->dkg_ncyl = 80;
			dkgeom->dkg_nhead = 2;
			dkgeom->dkg_nsect = 18;
			break;
		case 0x3C300:
			/* Ultra High density ls-120 120MB */
			dkgeom->dkg_pcyl = 963;
			dkgeom->dkg_ncyl = 963;
			dkgeom->dkg_nhead = 8;
			dkgeom->dkg_nsect = 32;
			break;
		default:
			debug(5, "unknown capacity type %d\n", capacity);
			return (-1);

	}
	debug(5, "get_floppy_geom: setting cyl = %d, nsect = %d, head = %d",
		dkgeom->dkg_pcyl, dkgeom->dkg_nhead, dkgeom->dkg_nsect);
	return (0);

}
/* ARGSUSED */
static int32_t
scsi_floppy_format(int32_t fd, uint_t flavor, uint_t mode)
{
	struct uscsi_cmd ucmd;
	uchar_t		cdb[12];
	int32_t		ret_val;
	uint32_t	capacity, blocksize;
	uchar_t		data[12];
	char 		rq_data[RQ_LEN];
	int		i;
	struct dk_geom	dkgeom;

	debug(5, "scsi_floppy_format:\n");

	if ((mode != SM_FORMAT_IMMEDIATE) && (mode != SM_FORMAT_BLOCKED)) {
		errno = ENOTSUP;

		return (-1);
	}

	switch (flavor) {
		case SM_FORMAT_QUICK :
			debug(1, "Format not supported\n");
			errno = ENOTSUP;
			return (-1);
		case SM_FORMAT_FORCE :
			break;
		case SM_FORMAT_LONG :
			break;

		default :
			debug(1, "Format option not specified!!\n");
			errno = ENOTSUP;
			return (-1);
	}

	ret_val = get_media_capacity(fd, &capacity, &blocksize);

	if (capacity >= 0x3C300) {
		/*
		 * It's an LS-120 media, it does not support track
		 * formatting.
		 */
		return (scsi_ls120_format(fd, flavor, capacity, blocksize));
	}

	ret_val = get_floppy_geom(fd, capacity, &dkgeom);
		if (ret_val) {
			errno = ENOTSUP;
			return (-1);
		}

	(void) memset((void *)&data, 0, sizeof (data));
	(void) memset((void *)&ucmd, 0, sizeof (ucmd));
	(void) memset((void *)&cdb, 0, sizeof (cdb));

	/* retrieve size discriptor of inserted media */
	cdb[0] = SCMD_FORMAT;	/* format */

	/*
	 * Defect list sent by initiator is a complete list of defects.
	 */

	cdb[1] = (FMTDATA | 0x7);

	cdb[8] = 0xC;   /* parameter list length */
	data[3] = 0x8;	/* should be always 8 */

	data[4] = (uchar_t)(capacity >> 24);
	data[5] = (uchar_t)(capacity >> 16);
	data[6] = (uchar_t)(capacity >> 8);
	data[7] = (uchar_t)capacity;

	data[9] = (uchar_t)(blocksize >> 16);
	data[10] = (uchar_t)(blocksize >> 8);
	data[11] = (uchar_t)blocksize;

	ucmd.uscsi_cdb = (caddr_t)&cdb;
	ucmd.uscsi_cdblen = CDB_GROUP5;
	ucmd.uscsi_bufaddr = (caddr_t)data;
	ucmd.uscsi_buflen = sizeof (data);
	ucmd.uscsi_timeout = 0x15;
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;

	debug(5, "cdb: %x %x %x ... %x", cdb[0], cdb[1], cdb[2], cdb[8]);
	debug(5, "data: %x %x %x %x\n", data[0], data[1], data[2], data[3]);
	debug(5, "    : %x %x %x %x\n", data[4], data[5], data[6], data[7]);
	debug(5, "    : %x %x %x %x\n", data[8], data[9], data[10], data[11]);

	for (i = 0; i < dkgeom.dkg_pcyl; i++) {	/* number of tracks */
		data[1] = (0xb0 | FOV);
		cdb[2] = i;

		(void) fflush(stdout);
		ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
		info("format side 0 returned : 0x%x\n", ret_val);

		if (ret_val || ucmd.uscsi_status) {
			debug(5, "Retrieving media info failed: %d - %d\n",
			    ret_val, ucmd.uscsi_status);
			if ((rq_data[2] == KEY_DATA_PROTECT) &&
			    (rq_data[12] == 0x30) && (rq_data[13] == 0)) {
				debug(5, "Invalid command for media\n");
				errno = EINVAL;
			}

			if ((rq_data[2] == KEY_NOT_READY) &&
			    (rq_data[12] == 0x30)) {
				debug(5, "Incompatible media.\n");
				errno = EINVAL;
			}

			return (-1);
		}
		data[1] = (0xb0 | FOV) + 1;
		ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_WRITE|USCSI_RQENABLE);
		info("format side 1 returned : 0x%x\n", ret_val);

		if (ret_val || ucmd.uscsi_status) {
			debug(5, "Retrieving media info failed: %d - %d\n",
			    ret_val, ucmd.uscsi_status);
			if ((rq_data[2] == KEY_DATA_PROTECT) &&
			    (rq_data[12] == 0x30) && (rq_data[13] == 0)) {
				(void) info("Invalid command for media\n");
				errno = EINVAL;
			}

			if ((rq_data[2] == KEY_NOT_READY) &&
			    (rq_data[12] == 0x30)) {
				debug(5, "Incompatible media.\n");
				errno = EINVAL;
			}

			return (-1);
		}
	}

	debug(5, "formatting done!");
	return (0);
}


/* ARGSUSED */
static int32_t
scsi_floppy_media_status(int32_t fd)
{
	struct mode_header_g1 modeh;
	struct uscsi_cmd ucmd;
	uchar_t cdb[10];
	int32_t ret_val;
	int32_t cur_status;
	char rq_data[RQ_LEN];

	debug(5, "SCSI MEDIA STATUS CALLED \n");

	(void) memset((void *) &modeh, 0, sizeof (modeh));
	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
	(void) memset(cdb, 0, sizeof (cdb));
	/*
	 * issue 10 byte mode sense (0x5A)
	 */
	cdb[0] = SCMD_MODE_SENSE_G1;
	cdb[7] = sizeof (modeh) >> 8;
	cdb[8] = sizeof (modeh) & 0xff;

	ucmd.uscsi_cdb = (caddr_t)cdb;
	ucmd.uscsi_cdblen = CDB_GROUP1;
	ucmd.uscsi_bufaddr = (caddr_t)&modeh;
	ucmd.uscsi_buflen = sizeof (modeh);
	ucmd.uscsi_timeout = 120;	/* If 0, HBA hangs forever */
	ucmd.uscsi_rqlen = RQ_LEN;
	ucmd.uscsi_rqbuf = rq_data;
	ret_val = do_uscsi_cmd(fd, &ucmd, USCSI_READ|USCSI_RQENABLE);
	if (ret_val || ucmd.uscsi_status) {
		/*
		 * UFI devices may not respond to the 0 mode page.
		 * retry with the error recovery page(0x01)
		 */
		if (ucmd.uscsi_status & STATUS_CHECK) {
			cdb[2] = 0x1;	/* page code */
			ret_val = do_uscsi_cmd(fd, &ucmd,
					USCSI_READ|USCSI_RQENABLE);
		}
		if (ret_val || ucmd.uscsi_status) {
			debug(1, "Modesense failed: %d - %d\n",
				ret_val, ucmd.uscsi_status);
			return (-1);
		}
	}
	debug(5, "Modesense succeeded: 0x%x\n", modeh.device_specific);

	if (modeh.device_specific & 0x80) {
		cur_status = SM_WRITE_PROTECT_NOPASSWD;
	} else {
		cur_status = SM_WRITE_PROTECT_DISABLE;
	}
	return (cur_status);
}