예제 #1
0
/*
 *	move - move from one element to another
 *
 *	entry -
 *	   library - library_t *
 *	   event - robo_event_t *
 */
int
move(
    library_t *library,
    robo_event_t *event)
{
    dev_ent_t 	*un;
    int 		err = -1, retry, timeout;
    char 		*l_mess = library->un->dis_mes[DIS_MES_NORM];
    char 		*MES_9079 =
        catgets(catfd, SET, 9079, "move from %s to %s %s");
    char 		*mess, *src_mess, *des_mess, *i_mess;
    robot_internal_t 	*cmd = &event->request.internal;
    int 		added_more_time = FALSE;
    sam_extended_sense_t *sense = (sam_extended_sense_t *)
                                  SHM_REF_ADDR(library->un->sense);
    int		movmed_err;

    un = library->un;
    src_mess = element_string(element_type(library, cmd->source));
    des_mess = element_string(element_type(library, cmd->destination1));
    if (cmd->flags.b.invert1)
        i_mess = catgets(catfd, SET, 9084, "invert");
    else
        i_mess = "";

    mess = (char *)malloc_wait(strlen(MES_9079) + strlen(src_mess) +
                               strlen(des_mess) + strlen(i_mess) + 10, 4, 0);
    sprintf(mess, MES_9079, src_mess, des_mess, i_mess);
    memccpy(l_mess, mess, '\0', DIS_MES_LEN);
    free(mess);
    DevLog(DL_DETAIL(5057),
           cmd->source, cmd->flags.b.invert1 ? "invert" : "asis",
           cmd->destination1);

    /*
     * A programming note from DocStore states that you should
     * allow up to 4 minutes for a move.  This is to allow
     * for recovery and retry by the robot.	 If other robots need
     * different time outs, this is the place to put um.
     */
    switch (library->un->type) {
    case DT_DLT2700:
        timeout = 300;
        break;

    case DT_DOCSTOR:
        timeout = 4 * 60;
        break;

    case DT_METD28:
    /* FALLTHROUGH */
    case DT_METD360:
    /* FALLTHROUGH */
    case DT_SPECLOG:
    /* FALLTHROUGH */
    case DT_ATL1500:
    /* FALLTHROUGH */
    case DT_ODI_NEO:
    /* FALLTHROUGH */
    case DT_QUANTUMC4:
    /* FALLTHROUGH */
    case DT_STK97XX:
    /* FALLTHROUGH */
    case DT_FJNMXX:
    /* FALLTHROUGH */
    case DT_SL3000:
    /* FALLTHROUGH */
    case DT_SLPYTHON:
        timeout = 10 * 60;
        break;

    default:
        timeout = 5 * 60;
        break;
    }

    mutex_lock(&library->un->io_mutex);
    retry = 2;
    do {
        time_t start;

        TAPEALERT(library->open_fd, library->un);
        memset(sense, 0, sizeof (sam_extended_sense_t));
        start = time(NULL);
        movmed_err = scsi_cmd(library->open_fd, library->un,
                              SCMD_MOVE_MEDIUM, timeout,
                              cmd->transport, cmd->source, cmd->destination1,
                              (cmd->flags.b.invert1 ? 1 : 0));
        TAPEALERT(library->open_fd, library->un);
        if (movmed_err < 0) {
            DevLog(DL_TIME(5177), cmd->source,
                   cmd->flags.b.invert1 ? "invert" : "asis",
                   cmd->destination1, time(NULL) - start);

            GENERIC_SCSI_ERROR_PROCESSING(library->un,
                                          library->scsi_err_tab, 0,
                                          err, added_more_time, retry,
                                          /* code for DOWN_EQU */
            if (!cmd->flags.b.noerror) {
            err = DOWN_EQU;
            if (sense->es_add_code == 0x40 &&
                        sense->es_qual_code == 0x02) {
                    move_drive_error(library, cmd->source,
                                     cmd->destination1, &err);
                } else {
                    down_library(library, SAM_STATE_CHANGE);
                }
                retry = 1;
                /* MACRO for cstyle */
            }
            break;
            /* MACRO for cstyle */,
예제 #2
0
파일: usb_msc_io.c 프로젝트: aarzho/mkernel
void usb_msc_thread(void* param)
{
	USB_MSC* msc = (USB_MSC*)param;
	for (;;)
	{
		event_wait_ms(msc->event, INFINITE);
		msc->state = MSC_STATE_DATA;
		//process received CBW
		if (msc->cbw.dCBWSignature == CBW_MAGIC && msc->cbw.bCBWCBLength <= MAX_CB_SIZE)
		{
			msc->csw_status = CSW_STATUS_OK;
#if (USB_MSC_DEBUG_FLOW)
			printf("USB_MSC: dCBWTag: %08x\n\r", msc->cbw.dCBWTag);
			printf("USB_MSC: dCBWDataTransferLength: %08x\n\r", msc->cbw.dCBWDataTransferLength);
			printf("USB_MSC: dCBWDataFlags: %02x\n\r", msc->cbw.bmCBWFlags);
			printf("USB_MSC: dCBWLUN: %02x\n\r", msc->cbw.bCBWLUN);
			printf("USB_MSC: dCBWCB:");
			int i;
			for (i = 0; i < msc->cbw.bCBWCBLength; ++i)
				printf(" %02x", msc->cbw.CBWCB[i]);
			printf(" (%d)\n\r", msc->cbw.bCBWCBLength);
#endif
			if (!scsi_cmd(msc->scsi, (char*)msc->cbw.CBWCB, msc->cbw.bCBWCBLength))
				msc->csw_status = CSW_STATUS_FAILED;
			//wait for transfer completed in any case
			event_wait_ms(msc->event, INFINITE);

		}
		//CBW invalid, phase ERROR
		else
		{
#if (USB_DEBUG_ERRORS)
			printf("Invalid CBW\n\r");
#endif
			msc->csw_status = CSW_STATUS_ERROR;
		}
		event_clear(msc->event);
		msc->state = MSC_STATE_CSW;
		//need zlp?
		if ((msc->scsi_transferred % msc->ep_size) == 0 && msc->cbw.dCBWDataTransferLength != 0 &&
			 msc->scsi_transferred < msc->cbw.dCBWDataTransferLength)
		{
			if (msc->cbw.bmCBWFlags & 0x80)
				usb_write(usbd_get_usb(msc->usbd), EP_IN(msc->ep_num), NULL, 0);
			else
				usb_read(usbd_get_usb(msc->usbd), EP_OUT(msc->ep_num), NULL, 0);
	#if (USB_MSC_DEBUG_FLOW)
		if (msc->cbw.bmCBWFlags & 0x80)
			printf("USB_MSC: TX ZLP\n\r");
		else
			printf("USB_MSC: RX ZLP\n\r");
	#endif
		}
		//send csw directly
		else
		{
			if (msc->cbw.bmCBWFlags & 0x80)
				on_msc_sent(EP_IN(msc->ep_num), msc);
			else
				on_msc_received(EP_OUT(msc->ep_num), msc);
		}
	}
}
예제 #3
0
static int
inquiry( instance_data_t *sd )
{
    char inquiry_cmd[6] = { 0x12, 0, 0, 0, 32, 0 };
    char start_stop_unit_cmd[6] = { 0x1b, 0, 0, 0, 1, 0 };
    char test_unit_ready_cmd[6] = { 0x00, 0, 0, 0, 0, 0 };
    char prev_allow_medium_removal[6] = { 0x1e, 0, 0, 0, 1, 0 };
    char set_cd_speed_cmd[12] = { 0xbb, 0, 0xff, 0xff, 0xff, 0xff,
                                  0, 0, 0, 0, 0, 0
                                };
    target_info_t *info = &scsi_devs[sd->target];
    char ret[32];
    int i, sense;

    if( sd->target >= MAX_TARGETS )
        return -1;
    sd->info = info;

    if( info->probed )
        return info->valid ? 0:-1;
    info->probed = 1;

    if( (sense=scsi_cmd_(sd, inquiry_cmd, 6, ret, 2, 0, 0)) ) {
        if( sense < 0 )
            return -1;
        printk("INQUIRY failed\n");
        return -1;
    }

    /* medium present? */
    if( (scsi_cmd(sd, test_unit_ready_cmd, 6) >> 8) == 0x23a ) {
        printk("no media\n");
        return -1;
    }

    info->is_cd = 0;
    info->blocksize = 512;

    if( ret[0] == 5 /* CD/DVD */ ) {
        info->blocksize = 2048;
        info->is_cd = 1;

        scsi_cmd( sd, prev_allow_medium_removal, 6 );
        scsi_cmd( sd, set_cd_speed_cmd, 12 );
        scsi_cmd( sd, start_stop_unit_cmd, 6 );

    } else if( ret[0] == 0 /* DISK */ ) {
        scsi_cmd( sd, test_unit_ready_cmd, 6 );
        scsi_cmd( sd, start_stop_unit_cmd, 6 );
    } else {
        /* don't boot from this device (could be a scanner :-)) */
        return -1;
    }

    /* wait for spin-up (or whatever) to complete */
    for( i=0; ; i++ ) {
        if( i > 300 ) {
            printk("SCSI timeout (sense %x)\n", sense );
            return -1;
        }
        sense = scsi_cmd( sd, test_unit_ready_cmd, 6 );
        if( (sense & 0xf0000) == 0x20000 ) {
            OSI_USleep( 10000 );
            continue;
        }
        break;
    }

    info->valid = 1;
    return 0;
}
예제 #4
0
/*
 * -- tapealert - tapealert log sense page 0x2e processing
 * Process media changer or tape drive request for tapealert log sense
 * page 0x2e.  An active tapealert never interferes with data
 * transfers.  Active tapealert flags are written to the device log and
 * real-time notification is by a posted tapealert sysevent.  See
 * www.t10.org SSC-2 and SMC-2 for additional tapealert information.
 */
int			/* 0 successful */
tapealert(
	char *src_fn,		/* source filename */
	int src_ln,		/* source file line number */
	int fd,			/* device file descriptor */
	dev_ent_t *un,		/* device */
	uchar_t *logpage,	/* existing tapealert log sense page */
	int logpage_len)	/* existing tapealert log sense page length */
{
#define	CLEAN_NOW		0x80000
#define	CLEAN_PERIODIC		0x100000
#define	EXPIRED_CLEANING_MEDIA	0x200000
#define	INVALID_CLEANING_MEDIA	0x400000
#define	STK_CLEAN_REQUESTED	0x800000
	int			rtn = 1;
	uchar_t			*page;
	uchar_t			*log_param;
#define		PAGE_LEN 256
	uchar_t			tmp_page [PAGE_LEN];
	int 			resid;
	int 			i;
	int			j;
	int			page_len;
	int			param_len;
	int			param_code;
	int 			add_page_len;
	sam_extended_sense_t	*sense;
	sam_extended_sense_t	saved_sense;
	uchar_t			saved_cdb [16];
#define		FLAGS_LEN 64
	int			flags_len;
	uint64_t		flags;
	uchar_t			val;
	int			save_errno = errno;
	uint64_t		seq_no = 0;
	int			supported;
	int			enabled;
	int			init_query;
	int			required;
	int 			requested;
	int			expired;
	int			invalid;


	supported = un->tapealert & TAPEALERT_SUPPORTED;
	enabled = un->tapealert & TAPEALERT_ENABLED;
	init_query = un->tapealert & TAPEALERT_INIT_QUERY;
	if (!(supported && (enabled || init_query))) {
		return (0);
	}

	/*
	 * get device sense pointer.
	 */
	sense = (sam_extended_sense_t *)SHM_REF_ADDR(un->sense);

	/*
	 * Second, process tapealert log sense page.
	 */
	if (logpage == NULL || logpage_len < 4) {
		/* save callers previous cdb and sense */
		memcpy(saved_cdb, un->cdb, SAM_CDB_LENGTH);
		memcpy(&saved_sense, sense, sizeof (sam_extended_sense_t));
		if (scsi_cmd(fd, un, SCMD_LOG_SENSE, 0, tmp_page, 0,
		    0x2e, 0, PAGE_LEN, &resid) < 0) {
			DevLog(DL_DEBUG(12002), sense->es_key,
			    sense->es_add_code, sense->es_qual_code);
			memcpy(un->cdb, saved_cdb, SAM_CDB_LENGTH);
			memcpy(sense, &saved_sense,
			    sizeof (sam_extended_sense_t));
			goto cleanup;
		}
		/* restore callers previous cdb and sense */
		memcpy(un->cdb, saved_cdb, SAM_CDB_LENGTH);
		memcpy(sense, &saved_sense, sizeof (sam_extended_sense_t));

		page = tmp_page;
		page_len = PAGE_LEN - resid;
	} else {
		page = logpage;
		page_len = logpage_len;
	}

	if (page_len < 4) {
		DevLog(DL_DEBUG(12003), page_len);
		goto cleanup;
	}

	if (page [0] != 0x2e) {
		DevLog(DL_DEBUG(12004), page [0]);
		goto cleanup;
	}

	add_page_len = (page [2] << 8) | page [3];
	add_page_len &= 0xffff;
	log_param = page + 4;

	flags = 0;
	flags_len = 0;
	for (i = 0, j = 0; i < FLAGS_LEN && j < add_page_len; i++, j += 5) {

		param_code = (log_param [j] << 8) | log_param [j + 1];
		if (param_code != (i + 1)) {
			if (flags != 0) break;
			goto cleanup;
		}

		param_len = log_param [j + 3];
		param_len &= 0xff;
		if (param_len == 1) {
			val = log_param [j + 4];
			if (i < 64 && val != 0) {
				flags |= ((uint64_t)1 << i);
			}
		} else {
			/*
			 * vendor unique flag length value, quit
			 * processing flags
			 */
			break;
		}

		/*
		 * increment number of valid TapeAlert flags
		 * contained in the 64 bit wide flags variable
		 */
		flags_len++;
	}

	/* check for in-active or already seen */
	if (flags == 0 || (flags == un->tapealert_flags &&
	    strcmp(un->tapealert_vsn, un->vsn) == 0)) {
		rtn = 0;
		goto cleanup;
	}

	/* build active flags filter by vsn */
	if (strcmp(un->tapealert_vsn, un->vsn) == 0) {
		/* check for already seen */
		if ((flags & ~un->tapealert_flags) == 0) {
			rtn = 0;
			goto cleanup;
		}
		/* current vsn, add active flag(s) to filter */
		un->tapealert_flags |= flags;
	} else {
		/* new vsn, initialize flags filter */
		un->tapealert_flags = flags;
	}
	strcpy(un->tapealert_vsn, un->vsn);

	/*
	 * send tapealert sysevent
	 */
	rtn = tapealert_sysevent(src_fn, src_ln, un, flags_len, flags, &seq_no);

	/*
	 * log active flags
	 *
	 * seq_no is zero when sysevent not sent.
	 */
	if (strlen(un->vsn) > 0) {
		DevLog(DL_ALL(12007), un->version, un->eq, un->scsi_type,
		    seq_no, flags_len, flags, un->vsn);
	} else {
		DevLog(DL_ALL(12006), un->version, un->eq, un->scsi_type,
		    seq_no, flags_len, flags);
	}

	if ((un->tapeclean & TAPECLEAN_AUTOCLEAN) &&
	    (un->tapeclean & TAPECLEAN_LOGSENSE)) {
		required = (flags & CLEAN_NOW) ? 1 : 0;
		requested = (flags & CLEAN_PERIODIC) ? 1 : 0;
		expired = (flags & EXPIRED_CLEANING_MEDIA) ? 1 : 0;
		invalid = (flags & INVALID_CLEANING_MEDIA) ? 1 : 0;
		if (un->equ_type == DT_9840 ||
		    un->equ_type == DT_9940 || un->equ_type == DT_TITAN) {
			requested = (flags & STK_CLEAN_REQUESTED) ? 1 : 0;
		}

		/* Map active TapeAlert flags onto SAM-FS status. */
		tapeclean_active(un, required, requested, expired, invalid);
	}

cleanup:

	/*
	 * restore callers errno state
	 */
	errno = save_errno;
	return (rtn);
}
예제 #5
0
/*
 * --- get_supports_tapealert - determine if the robot or tape drive supports
 * TapeAlert.
 */
void
get_supports_tapealert(dev_ent_t *un, int fd)
{
	int			local_open = 0;
	int			open_fd;
	sam_extended_sense_t    *sense;
#define	PAGE_LEN 256
	char			page[PAGE_LEN], *page_ptr;
	int			i, page_len, resid;
	boolean_t		unlock_needed = B_FALSE;


	/* Feature check. */
	if ((un->tapealert & TAPEALERT_ENABLED) == 0) {
		return;
	}

	/* Clear previous success flag. */
	un->tapealert &= ~TAPEALERT_SUPPORTED;

	/* Only tape drives and robots. */
	if (un->scsi_type != 1 && un->scsi_type != 8) {
		return;
	}

	/* Local file descriptor open used by fifo command message. */
	if (fd < 0) {
		if ((open_fd = open(un->name, O_RDONLY | O_NONBLOCK)) < 0) {
			if (IS_TAPE(un)) {
					char	*open_name;
					if ((open_fd = open((open_name =
					    samst_devname(un)),
					    O_RDONLY | O_NONBLOCK)) < 0) {
						if (open_name != (char *)
						    un->dt.tp.samst_name)
							free(open_name);
						return;
					} else {
						INC_OPEN(un);
						if (open_name != (char *)
						    un->dt.tp.samst_name)
							free(open_name);
						local_open = TRUE;
					}
			} else {
				DevLog(DL_DEBUG(12014));
				return;
			}
		} else {
			INC_OPEN(un);
			local_open = TRUE;
		}
	} else {
		open_fd = fd;
	}

	/* Look for log sense tapealert page 0x2e */
	sense = (sam_extended_sense_t *)SHM_REF_ADDR(un->sense);
	(void) memset(sense, 0, sizeof (sam_extended_sense_t));

	if (mutex_trylock(&un->io_mutex) == 0) {
		unlock_needed = B_TRUE;
	}

	memset(page, 0, PAGE_LEN);
	if (scsi_cmd(open_fd, un, SCMD_LOG_SENSE, 0, page, 0, 0, 0,
	    PAGE_LEN, &resid) >= 0) {

		page_len = ((PAGE_LEN - resid) >= 4 ? (PAGE_LEN - resid) : 0);
		if (page_len >= 4) {
			page_len = (page[2] << 8) | page[3];
			page_ptr = page + 4;
		}

		for (i = 0; i < page_len; i++) {
			if (page_ptr[i] == 0x2e) {
				/* initialize tapealert data */
				un->tapealert |= TAPEALERT_SUPPORTED;
				un->tapealert_flags = 0;
				un->tapealert_vsn[0] = '\0';
				DevLog(DL_ALL(12001));
				setup_tapealert(un, open_fd);
				break;
			}
		}
	}

	/* query, clear, and report active flags at setup */
	un->tapealert |= TAPEALERT_INIT_QUERY;
	TAPEALERT(open_fd, un);
	un->tapealert &= ~TAPEALERT_INIT_QUERY;

	if (unlock_needed == B_TRUE) {
		mutex_unlock(&un->io_mutex);
	}
	if (local_open) {
		(void) close(open_fd);
		DEC_OPEN(un);
	}
}
예제 #6
0
/*
 * -- setup_tapealert - Setup TapeAlert in polling mode.  Disable
 * recovered error check condition mode.  Disable test mode. Set
 * all other flags to the default values listed in the TapeAlert
 * Specification v3.
 */
static void
setup_tapealert(dev_ent_t *un, int fd)
{
	int		resid;		/* bytes not xfered */
#define	LEN 255				/* max page len */
	uchar_t		page[LEN];	/* current page */
	int		i, len, offset; /* page variables */
#define	PERF 0x80			/* performance */
#define	EBF 0x20			/* enable background funcs */
#define	EWASC 0x10			/* excpt warn chk cond */
#define	DEXCPT 0x08			/* polling mode */
#define	TEST 0x04			/* test mode */
#define	LOGERR 0x01			/* logging of errors */
#define	MIRE 0x03			/* tapealert mode */
#define	MIRE_MASK 0x0f			/* mire field */
	sam_extended_sense_t    *sense; /* device sense data ptr */
	uchar_t		 pagechg[LEN];	/* changable page */
	int		 offsetchg;	/* chg page variables */
	uchar_t		 cdb[SAM_CDB_LENGTH];    /* custom cdb */


	/*
	 * Setup TapeAlert mode page in polling mode.
	 */


	memset(page, 0, LEN);
	memset(pagechg, 0, LEN);
	sense = (sam_extended_sense_t *)SHM_REF_ADDR(un->sense);

	/* get current page */
	if (scsi_cmd(fd, un, SCMD_MODE_SENSE, 30, page, LEN, 0x1c,
	    &resid) < 0) {
		DevLog(DL_DEBUG(12009), sense->es_key, sense->es_add_code,
		    sense->es_qual_code);
		goto done;
	}
	len = page[0]+1;
	page[0] = 0;
	offset = page[3];
	page[4+offset] &= 0x7f;


	/* get changeable page */
	memset(cdb, 0, SAM_CDB_LENGTH);
	cdb[0] = SCMD_MODE_SENSE;
	if (IS_ROBOT(un)) {
		cdb[1] = 0x08;
	}
	cdb[2] = 0x40 | 0x1c;
	cdb[4] = LEN;
	if (scsi_cmd(fd, un, SCMD_ISSUE_CDB, 30, cdb, 6, pagechg, LEN,
	    USCSI_READ, &resid) < 0) {
		DevLog(DL_DEBUG(12010), sense->es_key, sense->es_add_code,
		    sense->es_qual_code);
		goto done;
	}
	offsetchg = pagechg[3];


	/*
	 * Change current TapeAlert page settings to polling mode.
	 */


	/* performance */
	if ((pagechg[6+offsetchg] & PERF) == PERF) {
		page[6+offset] &= ~PERF;
	}

	/* enable background functions */
	if ((pagechg[6+offsetchg] & EBF) == EBF) {
		page[6+offset] &= ~EBF;
	}

	/* exception warning reporting */
	if ((pagechg[6+offsetchg] & EWASC) == EWASC) {
		page[6+offset] &= ~EWASC;
	}

	/* disable exception control */
	if ((pagechg[6+offsetchg] & DEXCPT) == DEXCPT) {
		page[6+offset] |= DEXCPT;
	}

	/* test mode */
	if ((pagechg[6+offsetchg] & TEST) == TEST) {
		page[6+offset] &= ~TEST;
	}

	/* log err */
	if ((pagechg[6+offsetchg] & LOGERR) == LOGERR) {
		page[6+offset] &= ~LOGERR;
	}

	/* MIRE - field ignored if DEXCPT is set */
	if ((pagechg[7+offsetchg] & MIRE_MASK) != 0) {
		page[7+offset] &= ~(pagechg[7+offsetchg] & MIRE_MASK);
		page[7+offset] |= (MIRE & pagechg[7+offsetchg]);
		if ((page[7+offset] & MIRE_MASK) != MIRE) {
			page[7+offset] &= ~MIRE_MASK;
		}
	}

	/* interval timer */
	for (i = 0; i < 4; i++) {
		if (pagechg[i+8+offset] != 0) {
			page[i+8+offset] &= ~(pagechg[i+8+offset]);
		}
	}

	/* report count / test flag number */
	for (i = 0; i < 4; i++) {
		if (pagechg[i+12+offset] != 0) {
			page[i+12+offset] &= ~(pagechg[i+12+offset]);
		}
	}

	/* set polling mode and defaults */
	memset(cdb, 0, SAM_CDB_LENGTH);
	cdb[0] = SCMD_MODE_SELECT;
	cdb[1] = 0x10; /* pf bit */
	cdb[4] = len;
	if (scsi_cmd(fd, un, SCMD_ISSUE_CDB, 30, cdb, 6, page, len,
	    USCSI_WRITE, &resid) < 0) {
		DevLog(DL_DEBUG(12011), sense->es_key, sense->es_add_code,
		    sense->es_qual_code);
		goto done;
	}

done:
	/* log run-time settings from current mode page query */
	memset(page, 0, LEN);
	if (scsi_cmd(fd, un, SCMD_MODE_SENSE, 30, page, LEN, 0x1c,
	    &resid) < 0) {
		DevLog(DL_DEBUG(12012), sense->es_key, sense->es_add_code,
		    sense->es_qual_code);
		return;
	}
	offset = page[3];
	if (page[6+offset] != 8 || page[7+offset] != 3) {
		DevLog(DL_DEBUG(12013), page[6+offset], page[7+offset]);
	}
}
예제 #7
0
/*
 *	init_elements - get status for all elements in the library.
 *
 * exit -
 */
int				/* 0 = all ok !0 = failure */
init_elements(
		library_t *library)
{
	uint16_t	count, start_element;
	uint16_t	avail_drives;
	int		i, err, conlevel = 5;
	size_t	  retry;
	dev_ent_t	*un;
	char	   *drv_tbl;
	mode_sense_t   *mode_sense;
	drive_state_t  *drive;
	xport_state_t  *xport;
	iport_state_t  *import;
	robot_ms_page1d_t *pg1d = NULL;
	robot_ms_page1e_t *pg1e = NULL;
	robot_ms_page1f_t *pg1f = NULL;
	sam_extended_sense_t *sense;

	SANITY_CHECK(library != (library_t *)0);
	un = library->un;
	SANITY_CHECK(un != (dev_ent_t *)0);

	/* Put mode sense data into shared memory. */

	/* LINTED pointer cast may result in improper alignment */
	mode_sense = (mode_sense_t *)SHM_REF_ADDR(un->mode_sense);
	sense = (sam_extended_sense_t *)SHM_REF_ADDR(un->sense);
	SANITY_CHECK(mode_sense != (mode_sense_t *)0);
	SANITY_CHECK(sense != (sam_extended_sense_t *)0);
	(void) memset(mode_sense, 0, sizeof (mode_sense_t));

	mutex_lock(&un->io_mutex);
	pg1d = (robot_ms_page1d_t *)lib_mode_sense(library, 0x1d,
	    (uchar_t *)& mode_sense->u.robot_ms.pg1d,
	    sizeof (robot_ms_page1d_t));
	pg1f = (robot_ms_page1f_t *)lib_mode_sense(library, 0x1f,
	    (uchar_t *)& mode_sense->u.robot_ms.pg1f,
	    sizeof (robot_ms_page1f_t));
	pg1e = (robot_ms_page1e_t *)lib_mode_sense(library, 0x1e,
	    (uchar_t *)& mode_sense->u.robot_ms.pg1e,
	    sizeof (robot_ms_page1e_t));
	mutex_unlock(&un->io_mutex);

	if (pg1d == NULL || pg1f == NULL || pg1e == NULL) {
		DevLog(DL_ERR(5115));
		return (1);
	}
	library->status.b.two_sided = pg1e->transport_sets[0].rotate;
	if (un->type == DT_CYGNET)
		library->status.b.two_sided = 0;

	/* Allocate the drive tables. */
	BE16toH(&pg1d->first_drive, &start_element);
	BE16toH(&pg1d->num_drive, &count);
	library->range.drives_lower = start_element;
	library->range.drives_count = count;
	library->range.drives_upper = start_element + count - 1;

	/*
	 * This code is currently applied to IBM3584 only since the IBM3584
	 * returns a valid status if drive unit is not installed in a
	 * library. ASC/ASCQ:0x82/0x00. May need to add other library types
	 * to this check, check scsi docs.
	 *
	 * If drive is not fully populated and there is an empty slot for the
	 * drive, we don't need to create a redundant drive_thread.
	 */
	avail_drives = count;
	drv_tbl = malloc_wait(count, 2, 0);
	(void) memset(drv_tbl, TRUE, count);
	if (DT_IBM3584 == un->type)
		if ((avail_drives =
		    (uint16_t)populate_drives(library, drv_tbl)) == 0) {
			/*
			 * No drives installed, assum fully populated.
			 */
			DevLog(DL_ERR(5361));
			avail_drives = count;
			(void) memset(drv_tbl, TRUE, count);
		} else if (avail_drives > count) {
			avail_drives = count;
		}
	DevLog(DL_DETAIL(5362), avail_drives);

	/* one for the drive, one for stage and one for the stage helper */
	conlevel += (avail_drives * 3);

	library->drive = (drive_state_t *)malloc_wait(
	    sizeof (drive_state_t), 5, 0);
	library->index = library->drive;
	(void) memset(library->drive, 0, sizeof (drive_state_t));

	/*
	 * For each drive, build the drive state structure, put the init
	 * request on the list and start a thread with a new lwp.
	 */
	for (drive = library->drive, i = 0;
	    i < (int)count && avail_drives > 0; i++) {

		if (drv_tbl[i] == FALSE) {
			continue;
		}
		/* assign element number */
		drive->element = start_element + i;
		drive->library = library;
		/* hold the lock until ready */
		mutex_lock(&drive->mutex);
		drive->new_slot = ROBOT_NO_SLOT;
		drive->open_fd = -1;
		drive->active_count = 1;
		drive->first = (robo_event_t *)malloc_wait(
		    sizeof (robo_event_t), 5, 0);
		(void) memset(drive->first, 0, sizeof (robo_event_t));
		drive->first->type = EVENT_TYPE_INTERNAL;
		drive->first->status.bits = REST_FREEMEM;
		drive->first->request.internal.command = ROBOT_INTRL_INIT;
		if (thr_create(NULL, MD_THR_STK, &drive_thread, (void *) drive,
		    (THR_NEW_LWP | THR_BOUND | THR_DETACHED),
		    &drive->thread)) {
			DevLog(DL_SYSERR(5116));
			drive->status.b.offline = TRUE;
			drive->thread = (thread_t)- 1;
		}
		if (--avail_drives <= 0) {
			break;
		} else {
			/* Allocate next entry */
			drive->next = (drive_state_t *)malloc_wait(
			    sizeof (drive_state_t), 5, 0);
			(void) memset(drive->next, 0, sizeof (drive_state_t));
			drive->next->previous = drive;	/* set back link */
			drive = drive->next;
		}
	}

	drive->next = NULL;	/* no next drive */
	library->drive->previous = NULL;	/* no previous drive */
	free(drv_tbl);

	/* Allocate transport tables */

	BE16toH(&pg1d->first_tport, &start_element);
	BE16toH(&pg1d->num_tport, &count);
	library->range.transport_lower = start_element;
	library->range.transport_count = count;
	library->range.transport_upper = start_element + count - 1;
	library->range.default_transport = 0;
	library->page1f = pg1f;
	conlevel += count;
	library->transports =
	    (xport_state_t *)malloc_wait(sizeof (xport_state_t), 5, 0);
	(void) memset(library->transports, 0, sizeof (xport_state_t));

	for (xport = library->transports, i = 0; i < (int)count; i++) {
		/* assign element number */
		xport->element = start_element + i;
		xport->library = library;

		mutex_lock(&xport->mutex);
		/* start only one transport thread */
		if (i == 0) {
			xport->first =
			    (robo_event_t *)malloc_wait(
			    sizeof (robo_event_t), 5, 0);
			(void) memset(xport->first, 0, sizeof (robo_event_t));
			xport->first->type = EVENT_TYPE_INTERNAL;
			xport->first->status.bits = REST_FREEMEM;
			xport->first->request.internal.command =
			    ROBOT_INTRL_INIT;
			xport->active_count = 1;

			if (thr_create(NULL, SM_THR_STK,
			    &transport_thread, (void *) xport,
			    (THR_NEW_LWP | THR_BOUND | THR_DETACHED),
			    &xport->thread)) {
				DevLog(DL_SYSERR(5117));
				xport->thread = (thread_t)- 1;
			}
		}
		/* Allocate next entry */
		if (i != (count - 1)) {
			xport->next = (xport_state_t *)malloc_wait(
			    sizeof (xport_state_t), 5, 0);
			(void) memset(xport->next, 0, sizeof (xport_state_t));
			xport->next->previous = xport;	/* set back link */
			xport = xport->next;
		}
	}

	/* for the metrum d-360 the last transport is used with import export */
	xport->next = NULL;	/* no next transport */
	library->transports->previous = NULL;

	/* Allocate mailbox (import/export) tables */

	BE16toH(&pg1d->first_mail, &start_element);
	BE16toH(&pg1d->num_mail, &count);
	library->range.ie_lower = start_element;
	library->range.ie_count = count;
	if (count != 0)
		library->range.ie_upper = start_element + count - 1;
	else
		library->range.ie_upper = 0;

	conlevel += 1;		/* only one import/export thread */
	library->import = (iport_state_t *)malloc_wait(
	    sizeof (iport_state_t), 5, 0);
	(void) memset(library->import, 0, sizeof (iport_state_t));

	/* store the transport used in import/export for the metrum D-360 */
	if (un->type == DT_METD28)
		library->import->xport = xport;

	for (import = library->import, i = 0; i < (int)count; i++) {
		SANITY_CHECK(import != (iport_state_t *)0);
		/* assign element number */
		import->element = start_element + i;
		import->library = library;

		mutex_lock(&import->mutex);
		/* Create only one mailbox thread */
		if (i == 0) {
			import->active_count = 1;
			import->first = (robo_event_t *)malloc_wait(
			    sizeof (robo_event_t), 5, 0);
			(void) memset(import->first, 0, sizeof (robo_event_t));
			import->first->type = EVENT_TYPE_INTERNAL;
			import->first->status.bits = REST_FREEMEM;
			import->first->request.internal.command =
			    ROBOT_INTRL_INIT;
			if (thr_create(NULL, SM_THR_STK,
			    &import_thread, (void *) import,
			    (THR_DETACHED | THR_BOUND | THR_NEW_LWP),
			    &import->thread)) {
				DevLog(DL_SYSERR(5118));
				import->thread = (thread_t)- 1;
			}
		}
		if (i != (count - 1)) {	/* Allocate next entry */
			import->next = (iport_state_t *)malloc_wait(
			    sizeof (iport_state_t), 5, 0);
			(void) memset(import->next, 0, sizeof (iport_state_t));
			/* set back link */
			import->next->previous = import;
			import = import->next;
		}
	}

	import->next = NULL;	/* no next mailbox */
	SANITY_CHECK(library->import != (iport_state_t *)0);
	library->import->previous = NULL;

	/* allocate the audit table if needed */

	BE16toH(&pg1d->first_stor, &start_element);
	BE16toH(&pg1d->num_stor, &count);
	library->range.storage_lower = start_element;
	library->range.storage_count = count;
	library->range.storage_upper = start_element + count - 1;

	/* add for the import/export door slots */
	if (un->type == DT_ACL452)
		count += library->range.ie_count;

	DevLog(DL_DETAIL(5220), library->range.drives_count,
	    library->range.transport_count, library->range.storage_count,
	    library->range.ie_count);

	if (thr_setconcurrency(conlevel)) {
		DevLog(DL_SYSERR(5058));
	}
	/*
	 * If the audit table is the wrong length (based on the number of
	 * storage elements returned by mode-sense) or the audit bit is set,
	 * the set up for an audit.
	 */
	if ((library->audit_tab_len == 0) || un->status.b.audit) {
		int		added_more_time = FALSE;
		char	   *l_mess = un->dis_mes[DIS_MES_NORM];

		/*
		 * Audit table does not exist or is the wrong length.  This
		 * is generally a bad thing and  will force an initialize
		 * element scsi command and an audit. Both of these take a
		 * long time.
		 */
		/* tell the outside world */
		un->status.b.audit = TRUE;
		memccpy(l_mess, catgets(catfd, SET, 9022,
		    "initializing elements"),
		    '\0', DIS_MES_LEN);

		mutex_lock(&un->io_mutex);
		retry = 2;
		do {
			/*
			 * Allow 16 seconds for each storage element and 30
			 * seconds of slop.
			 */
			(void) memset(sense, 0, sizeof (sam_extended_sense_t));
			if ((err = scsi_cmd(library->open_fd, un,
			    SCMD_INIT_ELEMENT_STATUS,
			    (count << 4) + 30)) < 0) {
			TAPEALERT_SKEY(library->open_fd, un);
			GENERIC_SCSI_ERROR_PROCESSING(un,
			    library->scsi_err_tab, 0,
			    err, added_more_time, retry,
				/* code for DOWN_EQU */
			    down_library(library, SAM_STATE_CHANGE);
				mutex_unlock(&un->io_mutex);
				return (-1);
				/* MACRO for cstyle */,
				/* code for ILLREQ */
				    mutex_unlock(&un->io_mutex);
				return (-1);
				/* MACRO for cstyle */,
예제 #8
0
cdrom_ioctl(int fd, u_long cmd, void *arg)
	{
	int	ret;
	cgc_t	cgc;

	switch	(cmd)
		{
		case	CDROMREADRAW:
		case	CDROMREADMODE1:
		case	CDROMREADMODE2:
			{
			struct cdrom_msf *msf;
			int blocksize = 0, format = 0, lba;
		
			switch	(cmd)
				{
				case	CDROMREADRAW:
					blocksize = CD_FRAMESIZE_RAW;
					break;
				case	CDROMREADMODE1:
					blocksize = CD_FRAMESIZE;
					format = 2;
					break;
				case	CDROMREADMODE2:
					blocksize = CD_FRAMESIZE_RAW0;
					break;
				}
			msf = (struct cdrom_msf *)arg;
			lba = msf_to_lba(msf->cdmsf_min0,msf->cdmsf_sec0,
				msf->cdmsf_frame0);
			ret = EINVAL;
			if	(lba < 0)
				break;

			cgc_init(&cgc, arg, blocksize, SUC_READ);
			ret = cdrom_read_block(fd, &cgc, lba, 1, format,							blocksize);
			if	(ret)
				{
/*
 * SCSI-II devices are not required to support CMD_READ_CD (which specifies
 * the blocksize to read) so try switching the block size with a mode select,
 * doing the normal read sector command and then changing the sector size back
 * to 2048.
 *
 * If the program dies before changing the blocksize back sdopen()
 * in the kernel will fail opens with a message that looks something like:
 *
 * "sr1: blksize 2336 not multiple of 512: cannot use"
 *
 * At that point the drive has to be power cycled (or reset in some other way).
*/
				if	(ret = cdrom_blocksize(fd, blocksize))
					break;
				ret = cdrom_read_cd(fd, &cgc, lba, blocksize, 1);
				ret |= cdrom_blocksize(fd, 2048);
				}
			break;
			}
		case	CDROMREADTOCHDR:
			{
			struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
			u_char buffer[12];
			
			cgc_init(&cgc, buffer, sizeof (buffer), SUC_READ);
			cgc.cdb[0] = CMD_READ_TOC_PMA_ATIP;
			cgc.cdb[1] = 0x2;	/* MSF */
			cgc.cdb[8] = 12;	/* LSB of length */

			ret = scsi_cmd(fd, &cgc);
			if	(!ret)
				{
				tochdr->cdth_trk0 = buffer[2];
				tochdr->cdth_trk1 = buffer[3];
				}
			break;
			}
		case	CDROMREADTOCENTRY:
			{
			struct cdrom_tocentry *tocentry = (struct cdrom_tocentry *) arg;
			u_char	buffer[12];

			cgc_init(&cgc, buffer, sizeof (buffer), SUC_READ);
			cgc.cdb[0] = CMD_READ_TOC_PMA_ATIP;
			cgc.cdb[1] = (tocentry->cdte_format == CDROM_MSF) ? 0x02 : 0;
			cgc.cdb[6] = tocentry->cdte_track;
			cgc.cdb[8] = 12;		/* LSB of length */

			ret = scsi_cmd(fd, &cgc);
			if	(ret)
				break;

			tocentry->cdte_ctrl = buffer[5] & 0xf;
			tocentry->cdte_adr = buffer[5] >> 4;
			tocentry->cdte_datamode = (tocentry->cdte_ctrl & 0x04) ? 1 : 0;
			if	(tocentry->cdte_format == CDROM_MSF)
				{
				tocentry->cdte_addr.msf.minute = buffer[9];
				tocentry->cdte_addr.msf.second = buffer[10];
				tocentry->cdte_addr.msf.frame = buffer[11];
				}
			else
				tocentry->cdte_addr.lba = (((((buffer[8] << 8)
						+ buffer[9]) << 8)
						+ buffer[10]) << 8)
						+ buffer[11];
			break;
			}
		case	CDROMEJECT:		/* NO-OP for now */
			ret = cdrom_tray_move(fd, 1);
			break;
		case	CDROMCLOSETRAY:
			ret = cdrom_tray_move(fd, 0);
			break;
/*
 * This sucks but emulates the expected behaviour.  Instead of the return
 * value being the actual status a success/fail indicator should have been
 * returned and the 3rd arg to the ioctl should have been an 'int *' to update
 * with the actual status.   Both the drive and disc status ioctl calls are
 * similarily braindamaged.
*/
		case	CDROM_DRIVE_STATUS:
			return(CDS_NO_INFO);	/* XXX */
		case	CDROM_DISC_STATUS:
			{
			tracktype tracks;
			int	cnt;

			cdrom_count_tracks(fd, &tracks);
			if	(tracks.error)
				return(tracks.error);
			if	(tracks.audio > 0)
				{
				cnt = tracks.data + tracks.cdi + tracks.xa;
				if	(cnt == 0)
					return(CDS_AUDIO);
				else
					return(CDS_MIXED);
				}
			if	(tracks.cdi)
				return(CDS_XA_2_2);
			if	(tracks.xa)
				return(CDS_XA_2_1);
			if	(tracks.data)
				return(CDS_DATA_1);
			return(CDS_NO_INFO);
			}
		}
	errno = ret;
	return(ret ? -1 : 0);
	}
예제 #9
0
static int dvd_do_auth(int fd, dvd_authinfo *ai)
	{
	int	ret;
	u_char	buf[20];
	cgc_t	cgc;
	rpc_state_t rpc_state;

	memset(buf, 0, sizeof(buf));
	cgc_init(&cgc, buf, 0, SUC_READ);

	switch	(ai->type)
		{
		case	DVD_LU_SEND_AGID:	/* LU data send */
			setup_report_key(&cgc, ai->lsa.agid, 0);
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			ai->lsa.agid = buf[7] >> 6;
			break;
		case	DVD_LU_SEND_KEY1:
			setup_report_key(&cgc, ai->lsk.agid, 2);
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			copy_key(ai->lsk.key, &buf[4]);
			break;
		case	DVD_LU_SEND_CHALLENGE:
			setup_report_key(&cgc, ai->lsc.agid, 1);
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			copy_chal(ai->lsc.chal, &buf[4]);
			break;
		case	DVD_LU_SEND_TITLE_KEY:	/* Post-auth key */
			setup_report_key(&cgc, ai->lstk.agid, 4);
			cgc.cdb[5] = ai->lstk.lba;
			cgc.cdb[4] = ai->lstk.lba >> 8;
			cgc.cdb[3] = ai->lstk.lba >> 16;
			cgc.cdb[2] = ai->lstk.lba >> 24;
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			ai->lstk.cpm = (buf[4] >> 7) & 1;
			ai->lstk.cp_sec = (buf[4] >> 6) & 1;
			ai->lstk.cgms = (buf[4] >> 4) & 3;
			copy_key(ai->lstk.title_key, &buf[5]);
			break;
		case	DVD_LU_SEND_ASF:
			setup_report_key(&cgc, ai->lsasf.agid, 5);
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			ai->lsasf.asf = buf[7] & 1;
			break;
		case	DVD_HOST_SEND_CHALLENGE: /* LU data receive (LU changes state) */
			setup_send_key(&cgc, ai->hsc.agid, 1);
			buf[1] = 0xe;
			copy_chal(&buf[4], ai->hsc.chal);
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			ai->type = DVD_LU_SEND_KEY1;
			break;
		case	DVD_HOST_SEND_KEY2:
			setup_send_key(&cgc, ai->hsk.agid, 3);
			buf[1] = 0xa;
			copy_key(&buf[4], ai->hsk.key);
			if	(ret = scsi_cmd(fd, &cgc))
				{
				ai->type = DVD_AUTH_FAILURE;
				return ret;
				}
			ai->type = DVD_AUTH_ESTABLISHED;
			break;
		case	DVD_INVALIDATE_AGID:
			setup_report_key(&cgc, ai->lsa.agid, 0x3f);
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			break;
		case	DVD_LU_SEND_RPC_STATE:	/* Get region settings */
			setup_report_key(&cgc, 0, 8);
			memset(&rpc_state, 0, sizeof(rpc_state_t));
			cgc.buf = (char *) &rpc_state;
			if	(ret = scsi_cmd(fd, &cgc))
				{
				ai->lrpcs.type = 0;
				ai->lrpcs.rpc_scheme = 0;
				}
			else
				{
				ai->lrpcs.type = rpc_state.type_code;
				ai->lrpcs.vra = rpc_state.vra;
				ai->lrpcs.ucca = rpc_state.ucca;
				ai->lrpcs.region_mask = rpc_state.region_mask;
				ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme;
				}
			break;
		case	DVD_HOST_SEND_RPC_STATE:  /* Set region settings */
			setup_send_key(&cgc, 0, 6);
			buf[1] = 6;
			buf[4] = ai->hrpcs.pdrc;
			if	(ret = scsi_cmd(fd, &cgc))
				return ret;
			break;
		default:
			return EINVAL;
		}
	return 0;
	}
예제 #10
0
/*
 * clean_3570 - attempt to load cleaning tape into 3570.
 */
void
clean_3570(
	drive_state_t *drive,
	robo_event_t *event,
	struct CatalogEntry *ce)
{
	int		retry;
	char	   *dev_name;
	char	   *d_mess = drive->un->dis_mes[DIS_MES_NORM];
	dev_ent_t	*un = drive->un;
	library_t	*library = drive->library;
	move_flags_t    move_flags;

	mutex_lock(&drive->mutex);
	move_flags.bits = 0;
	/*
	 * The 3570 does not return from the move until the cleaning cycle
	 * has completed.
	 */
	memccpy(d_mess, catgets(catfd, SET, 9030, "wait for cleaning cycle"),
	    '\0', DIS_MES_LEN);
	if (generic_get_media(library, drive, event, ce)) {
		memccpy(drive->un->dis_mes[DIS_MES_CRIT],
		    catgets(catfd, SET, 9029,
		    "unable to load cleaning cartridge, move failed"),
		    '\0', DIS_MES_LEN);

		DevLog(DL_ERR(5145), ce->CeSlot);
		down_drive(drive, SAM_STATE_CHANGE);
		drive->status.b.cln_inprog = FALSE;
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EIO);
		return;
	}
	mutex_unlock(&drive->mutex);
	sleep(4);
	dev_name = samst_devname(un);
	mutex_lock(&un->mutex);
	drive->open_fd = open_unit(un, dev_name, 10);
	mutex_unlock(&un->mutex);
	free(dev_name);

	un->i.ViEq = un->fseq;
	un->i.ViSlot = un->slot;
	un->i.ViPart = 0;
	un->i.ViFlags = VI_cart;
	UpdateCatalog(drive->un, 0, CatalogVolumeLoaded);

	/* Wait for cleaning to finish */

	retry = 60;
	while (retry--) {
		sam_extended_sense_t *sense = (sam_extended_sense_t *)
		    SHM_REF_ADDR(un->sense);

		mutex_lock(&un->io_mutex);
		memset(sense, 0, sizeof (sam_extended_sense_t));
		if (scsi_cmd(drive->open_fd, un, SCMD_TEST_UNIT_READY, 20) ||
		    sense->es_key != 0) {
			/* If cleaning in progress */
			if (sense->es_key == 0x02 &&
			    sense->es_add_code == 0x30 &&
			    sense->es_qual_code == 0x03) {
				mutex_unlock(&un->io_mutex);
				sleep(30);
				continue;
			}
			if (sense->es_key == 0x06 &&
			    sense->es_add_code == 0x82 &&
			    sense->es_qual_code == 0x83)
				break;

			mutex_unlock(&un->io_mutex);
			sprintf(d_mess, "sense %x, %x, %x", sense->es_key,
			    sense->es_add_code, sense->es_qual_code);
			sleep(10);
		}
	}
	if (retry <= 0)
		DevLog(DL_ERR(5216));

	memccpy(d_mess, catgets(catfd, SET, 9034, "drive has been cleaned"),
	    '\0', DIS_MES_LEN);
	mutex_unlock(&un->io_mutex);
	mutex_lock(&un->mutex);
	close_unit(un, &drive->open_fd);
	mutex_unlock(&un->mutex);
	mutex_lock(&drive->mutex);
	move_flags.bits = 0;
	memccpy(d_mess,
	    catgets(catfd, SET, 9009, "waiting for media changer"),
	    '\0', DIS_MES_LEN);
	if (move_media(library, 0, drive->element, 0xff, 1, move_flags)) {
		memccpy(drive->un->dis_mes[DIS_MES_CRIT],
		    catgets(catfd, SET, 9032,
		    "unable to unload cleaning cartridge"),
		    '\0', DIS_MES_LEN);
		DevLog(DL_ERR(5147));
		drive->status.b.cln_inprog = FALSE;
		down_drive(drive, SAM_STATE_CHANGE);
		mutex_unlock(&drive->mutex);
		disp_of_event(library, event, EIO);
		return;
	}
	if (CatalogVolumeUnloaded(&un->i, "") == -1) {
		DevLog(DL_SYSERR(5336), ce->CeSlot);
	}
	drive->status.b.cln_inprog = FALSE;
	mutex_lock(&drive->un->mutex);
	drive->un->status.bits &= ~(DVST_CLEANING | DVST_REQUESTED);
	un->label_time = 0;
	mutex_unlock(&drive->un->mutex);
	mutex_unlock(&drive->mutex);
	disp_of_event(library, event, 0);
}