コード例 #1
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool  scsi_cmd_prevent_allow_medium_removal(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_6)
	{
		switch (scsi->cmd.len)
		{
		case 0:
		case 1:
			storage_enable_media_removal(scsi->storage);
			res = true;
			break;
		case 2:
		case 3:
			storage_disable_media_removal(scsi->storage);
			res = true;
			break;
		}
	}
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);

#if (SCSI_DEBUG_FLOW)
	printf("SCSI prevent/allow medium removal: %d\n\r", scsi->cmd.len);
#endif
	return res;
}
コード例 #2
0
int
do_rtpg(int fd, void* resp, long resplen, unsigned int timeout)
{
	struct rtpg_command	cmd;
	struct sg_io_hdr	hdr;
	unsigned char		sense[SENSE_BUFF_LEN];

	memset(&cmd, 0, sizeof(cmd));
	cmd.op			= OPERATION_CODE_RTPG;
	rtpg_command_set_service_action(&cmd);
	set_uint32(cmd.length, resplen);
	PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));

	memset(&hdr, 0, sizeof(hdr));
	hdr.interface_id	= 'S';
	hdr.cmdp		= (unsigned char *) &cmd;
	hdr.cmd_len		= sizeof(cmd);
	hdr.dxfer_direction	= SG_DXFER_FROM_DEV;
	hdr.dxferp		= resp;
	hdr.dxfer_len		= resplen;
	hdr.mx_sb_len		= sizeof(sense);
	hdr.sbp			= sense;
	hdr.timeout		= get_prio_timeout(timeout, SGIO_TIMEOUT);

	if (ioctl(fd, SG_IO, &hdr) < 0)
		return -RTPG_RTPG_FAILED;

	if (scsi_error(&hdr)) {
		PRINT_DEBUG("do_rtpg: SCSI error!\n");
		return -RTPG_RTPG_FAILED;
	}
	PRINT_HEX(resp, resplen);

	return 0;
}
コード例 #3
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_request_sense(SCSI* scsi)
{
	bool res = false;
	//f**ing microsoft suppose, that is SCSI 12 command. WTF???
	if (scsi->cmd.cmd_type == SCSI_CMD_6 || scsi->cmd.cmd_type == SCSI_CMD_12)
	{
		uint8_t code;
		uint16_t	asq;
		if (scsi->error_head != scsi->error_tail)
		{
			code = scsi->error_queue[scsi->error_tail].code;
			asq = scsi->error_queue[scsi->error_tail].asq;
			scsi->error_tail = (scsi->error_tail + 1) & SCSI_ERROR_BUF_MASK;
		}
		//empty queue
		else
		{
			code = SENSE_KEY_NO_SENSE;
			asq = ASQ_NO_ADDITIONAL_SENSE_INFORMATION;
		}
		scsi_fill_error_page(scsi, code, asq);
		res = true;
#if (SCSI_DEBUG_FLOW)
	printf("SCSI request sense 0x%02X, ASQ: 0x%04X\n\r", code, asq);
#endif
	}
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);
	return res;
}
コード例 #4
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_inquiry(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_6)
	{
		//vital data page request
		if (scsi->cmd.flags & SCSI_INQUIRY_EVPD)
		{
			switch (scsi->cmd.address)
			{
			case INQUIRY_VITAL_PAGE_SUPPORTED_PAGES:
				scsi_fill_evpd_page_00(scsi);
				res = true;
				break;
			case INQUIRY_VITAL_PAGE_DEVICE_INFO:
				scsi_fill_evpd_page_83(scsi);
				res = true;
				break;
			case INQUIRY_VITAL_PAGE_SERIAL_NUM:
				scsi_fill_evpd_page_80(scsi);
				res = true;
				break;
			default:
				break;
			}
	#if (SCSI_DEBUG_FLOW)
			printf("SCSI: inquiry vital data page %02X\n\r", scsi->cmd.address);
	#endif
		}
		//standart inquiry
		else
		{
			scsi_fill_standart_inquiry_page(scsi);
	#if (SCSI_DEBUG_FLOW)
			printf("SCSI: standart inquiry\n\r");
	#endif
			res = true;
		}
		if (!res)
			scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_INVALID_FIELD_IN_CDB);
	}
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);

	return res;
}
コード例 #5
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_read_capacity(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_10)
	{
		if (storage_check_media(scsi->storage))
		{
			res = true;
			scsi_fill_capacity_page(scsi);
#if (SCSI_DEBUG_FLOW)
	printf("SCSI: read capacity 0x%08X sectors, sector size: %d\n\r", storage_get_media_descriptor(scsi->storage)->num_sectors,
			 storage_get_device_descriptor(scsi->storage)->sector_size);
#endif
		}
		else
			scsi_error(scsi, SENSE_KEY_NOT_READY, ASQ_MEDIUM_NOT_PRESENT);
	}
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);
	return res;
}
コード例 #6
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_verify16(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_16)
		res = scsi_verify(scsi, scsi->cmd.additional_data, scsi->cmd.len);
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);

#if (SCSI_DEBUG_FLOW)
	printf("SCSI verify16 0x%08X, %x sector(s)\n\r", scsi->cmd.address, scsi->cmd.len);
#endif
	return res;
}
コード例 #7
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_write12(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_12)
		res = scsi_write(scsi, scsi->cmd.address, scsi->cmd.len);
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);

#if (SCSI_DEBUG_FLOW)
	printf("SCSI write12 0x%08X, %d sector(s)\n\r", scsi->cmd.address, scsi->cmd.len);
#endif //SCSI_DEBUG_FLOW
	return res;
}
コード例 #8
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_read6(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_6)
		res = scsi_read(scsi, scsi->cmd.address, scsi->cmd.len ? scsi->cmd.len : 256);
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);

#if (SCSI_DEBUG_FLOW)
	printf("SCSI read6 0x%08X, %x sector(s)\n\r", scsi->cmd.address, scsi->cmd.len ? scsi->cmd.len : 256);
#endif
	return res;
}
コード例 #9
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_synchronize_cache(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_10)
	{
		storage_write_cache(scsi->storage);
		res = true;
#if (SCSI_DEBUG_FLOW)
		printf("SCSI: synchronize cache\n\r");
#endif
	}
	else
		scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_CDB_DECRYPTION_ERROR);
	return res;
}
コード例 #10
0
ファイル: scsi.c プロジェクト: aarzho/mkernel
static inline bool scsi_cmd_mode_select6(SCSI* scsi)
{
	bool res = false;
	if (scsi->cmd.cmd_type == SCSI_CMD_6)
	{
		switch((scsi->cmd.address >> 8) & 0x3f)
		{
		case 0x1c:
			res = true;
			break;
		default:
			scsi_error(scsi, SENSE_KEY_ILLEGAL_REQUEST, ASQ_INVALID_FIELD_IN_CDB);
#if (SCSI_DEBUG_UNSUPPORTED)
			printf("SCSI: Mode select: unsupported page 0x%02X\n\r", (scsi->cmd.address >> 8) & 0x3f);
#endif
		}
	}
	else
コード例 #11
0
/*
 * Helper function to setup and run a SCSI inquiry command.
 */
int
do_inquiry(int fd, int evpd, unsigned int codepage,
	   void *resp, int resplen, unsigned int timeout)
{
	struct inquiry_command	cmd;
	struct sg_io_hdr	hdr;
	unsigned char		sense[SENSE_BUFF_LEN];

	memset(&cmd, 0, sizeof(cmd));
	cmd.op = OPERATION_CODE_INQUIRY;
	if (evpd) {
		inquiry_command_set_evpd(&cmd);
		cmd.page = codepage;
	}
	set_uint16(cmd.length, resplen);
	PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));

	memset(&hdr, 0, sizeof(hdr));
	hdr.interface_id	= 'S';
	hdr.cmdp		= (unsigned char *) &cmd;
	hdr.cmd_len		= sizeof(cmd);
	hdr.dxfer_direction	= SG_DXFER_FROM_DEV;
	hdr.dxferp		= resp;
	hdr.dxfer_len		= resplen;
	hdr.sbp			= sense;
	hdr.mx_sb_len		= sizeof(sense);
	hdr.timeout		= get_prio_timeout(timeout, SGIO_TIMEOUT);

	if (ioctl(fd, SG_IO, &hdr) < 0) {
		PRINT_DEBUG("do_inquiry: IOCTL failed!\n");
		return -RTPG_INQUIRY_FAILED;
	}

	if (scsi_error(&hdr)) {
		PRINT_DEBUG("do_inquiry: SCSI error!\n");
		return -RTPG_INQUIRY_FAILED;
	}
	PRINT_HEX((unsigned char *) resp, resplen);

	return 0;
}
コード例 #12
0
ファイル: sg-freebsd-port.c プロジェクト: svn2github/libburn
/** Sends a SCSI command to the drive, receives reply and evaluates wether
    the command succeeded or shall be retried or finally failed.
    Returned SCSI errors shall not lead to a return value indicating failure.
    The callers get notified by c->error. An SCSI failure which leads not to
    a retry shall be notified via scsi_notify_error().
    The Libburn_log_sg_commandS facility might be of help when problems with
    a drive have to be examined. It shall stay disabled for normal use.
    @return: 1 success , <=0 failure
*/
int sg_issue_command(struct burn_drive *d, struct command *c)
{
	int done = 0;
	int err;
	union ccb *ccb;

	if (d->cam == NULL) {
		c->error = 0;
		return 0;
	}

	c->error = 0;

	ccb = cam_getccb(d->cam);
	cam_fill_csio(&ccb->csio,
				  1,                              /* retries */
				  NULL,                           /* cbfncp */
				  CAM_DEV_QFRZDIS,                /* flags */
				  MSG_SIMPLE_Q_TAG,               /* tag_action */
				  NULL,                           /* data_ptr */
				  0,                              /* dxfer_len */
				  sizeof (ccb->csio.sense_data),  /* sense_len */
				  0,                              /* cdb_len */
				  30*1000);                       /* timeout */
	switch (c->dir) {
	case TO_DRIVE:
		ccb->csio.ccb_h.flags |= CAM_DIR_OUT;
		break;
	case FROM_DRIVE:
		ccb->csio.ccb_h.flags |= CAM_DIR_IN;
		break;
	case NO_TRANSFER:
		ccb->csio.ccb_h.flags |= CAM_DIR_NONE;
		break;
	}

	ccb->csio.cdb_len = c->oplen;
	memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
	
	if (c->page) {
		ccb->csio.data_ptr  = c->page->data;
		if (c->dir == FROM_DRIVE) {
			ccb->csio.dxfer_len = BUFFER_SIZE;
/* touch page so we can use valgrind */
			memset(c->page->data, 0, BUFFER_SIZE);
		} else {

			/* ts A61115: removed a ssert() */
			if(c->page->bytes <= 0)
				return 0;

			ccb->csio.dxfer_len = c->page->bytes;
		}
	} else {
		ccb->csio.data_ptr  = NULL;
		ccb->csio.dxfer_len = 0;
	}

	do {
		memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data));
		err = cam_send_ccb(d->cam, ccb);
		if (err == -1) {
			libdax_msgs_submit(libdax_messenger,
				d->global_index, 0x0002010c,
				LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
				"Failed to transfer command to drive",
				errno, 0);
			cam_freeccb(ccb);
			sg_close_drive(d);
			d->released = 1;
			d->busy = BURN_DRIVE_IDLE;
			c->error = 1;
			return -1;
		}
		/* XXX */
		memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
			if (!c->retry) {
				c->error = 1;
				cam_freeccb(ccb);
				return 1;
			}
			switch (scsi_error(d, c->sense, 0)) {
			case RETRY:
				done = 0;
				break;
			case FAIL:
				done = 1;
				c->error = 1;
				break;
			}
		} else {
			done = 1;
		}
	} while (!done);
	cam_freeccb(ccb);
	return 1;
}
コード例 #13
0
ファイル: rz_disk.c プロジェクト: rohsaini/mkunity
/*ARGSUSED*/
scsi_ret_t
scdisk_open(
	target_info_t		*tgt,
	io_req_t	        req)
{
	register int 	i;
	register scsi_ret_t	ret = SCSI_RET_SUCCESS;
	unsigned int		disk_size, secs_per_cyl, sector_size;
	unsigned int		nsectors, ntracks, ncylinders;
	scsi_rcap_data_t	*cap;
	struct disklabel	*label = 0;
	io_req_t		ior;
	void			(*readfun)(
					target_info_t	*tgt,
					unsigned int	secno,
					io_req_t	ior) = scdisk_read;
	char			*data, *rdata = 0;
	boolean_t		look_for_label;

	if (tgt->flags & TGT_ONLINE)
		return SCSI_RET_SUCCESS;

	tgt->lun = rzlun(req->io_unit);

	/*
	 * Dummy ior for proper sync purposes
	 */
	io_req_alloc(ior);
	bzero((char *)ior, sizeof(*ior));
	simple_lock_init(&(ior)->io_req_lock, ETAP_IO_REQ);

#if 0
	/*
	 * Set the LBN to tgt->block_size with a MODE SELECT.
	 * xxx do a MODE SENSE instead ?
	 */
	/*
	 * Ugh.  Can't use tgt->block_size here -- not set up
	 * yet.  Also can't use DEV_BSIZE.  So...since MK6
	 * dispenses with this command entirely, let's do
	 * so as well.
	 */
	for (i = 0; i < 5; i++) {
		ior->io_op = IO_INTERNAL;
		ior->io_error = 0;
		ret = scdisk_mode_select(tgt, tgt->block_size, ior,0,0,FALSE);
		if (ret == SCSI_RET_SUCCESS)
			break;
		if (ret == SCSI_RET_RETRY) {
			timeout((timeout_fcn_t)wakeup, tgt, 2*hz);
			await(tgt);
		}
		if (ret == SCSI_RET_DEVICE_DOWN)
			goto done;
	}
	if (ret != SCSI_RET_SUCCESS) {
		scsi_error( tgt, SCSI_ERR_MSEL, ret, 0);
		ret = D_INVALID_SIZE;
		goto done;
	}
#endif

#ifdef hp_pa
	if (tgt->flags & TGT_REMOVABLE_MEDIA) {
		scsi_mode_sense_page5_t	*page5;
		unsigned char           mode_save[0xff];
		scsi_mode_select_param_t *parm;
		int length;

		if((ret = scsi_mode_sense(tgt, 0x05, 0xff, 0))
		   != SCSI_RET_SUCCESS) 
			goto done;

		length = *tgt->cmd_ptr + 1;
		bcopy(tgt->cmd_ptr, mode_save, length);

		page5 = (scsi_mode_sense_page5_t *)(mode_save + 4 + mode_save[3]);
#if 1 
		/* force sector size to 512 */
		parm = (scsi_mode_select_param_t *)mode_save;

		parm->reserved1 = 0;
		parm->medium_type = 2;
		parm->device_spec &= ~0x90;
			
		parm->descs[0].density_code = 2; 
		parm->descs[0].nblocks1 = 0;
		parm->descs[0].nblocks2 = 0;
		parm->descs[0].nblocks3 = 0;
		parm->descs[0].reclen1 = 0x2;
		parm->descs[0].reclen2 = 0;
		parm->descs[0].reclen3 = 0;

		page5->ps = 0;
		page5->page_code &= ~0x80;
		
		page5->sectors_per_track = page5->sectors_per_track * 
			(page5->bytes_per_sector_msb << 8 |
			 page5->bytes_per_sector_lsb) /
				 512;
		
		page5->bytes_per_sector_msb = 2;
		page5->bytes_per_sector_lsb = 0;
			
		length -= parm->desc_len;
		parm->desc_len = 0;
		bcopy((const char*)page5, mode_save+4, sizeof(*page5));
		
		if((ret = scdisk_mode_select(tgt, 0, 0, mode_save, length, 0)) 
		   != SCSI_RET_SUCCESS) 
			goto done;

		if((ret = scsi_mode_sense(tgt, 0x05, 0xff, 0))
		   != SCSI_RET_SUCCESS) 
			goto done;

		length = *tgt->cmd_ptr + 1;
		bcopy(tgt->cmd_ptr, mode_save, length);
#endif

		ntracks      = page5->number_of_heads;
		nsectors     = page5->sectors_per_track;
		sector_size  = page5->bytes_per_sector_msb << 8 |
			page5->bytes_per_sector_lsb;
		ncylinders   = page5->number_of_cylinders_msb << 8 |
			page5->number_of_cylinders_lsb;
		secs_per_cyl = nsectors * ntracks;

		look_for_label = FALSE;
		geom_done = TRUE;
	}
#endif

	/*
	 * Do a READ CAPACITY to get max size. Check LBN too.
	 */
	for (i = 0; i < 5; i++) {
		ior->io_op = IO_INTERNAL;
		ior->io_error = 0;
		ret = scsi_read_capacity(tgt, 0, ior);
		if (ret == SCSI_RET_SUCCESS)
			break;
		if (ret == SCSI_RET_RETRY) {
			timeout((timeout_fcn_t)wakeup, tgt, 2*hz);
			await(tgt);
		}
		if (ret == SCSI_RET_DEVICE_DOWN)
			goto done;
	}
	if (ret != SCSI_RET_SUCCESS) {
		scsi_error( tgt, SCSI_ERR_MSEL, ret, 0);
		ret = D_INVALID_SIZE;
		goto done;
	}

	cap = (scsi_rcap_data_t*) tgt->cmd_ptr;
	disk_size = (cap->lba1<<24) |
	  	    (cap->lba2<<16) |
		    (cap->lba3<< 8) |
		     cap->lba4 + 1;
	if (scsi_debug)
		printf("rz%d holds %d blocks\n", tgt->unit_no, disk_size);

	tgt->block_size = (cap->blen1<<24) |
	  		  (cap->blen2<<16) |
	    		  (cap->blen3<<8 ) |
			   cap->blen4;

	if (scsi_debug) {
		printf("rz%d block size is %d bytes/block\n",
			       tgt->unit_no, tgt->block_size);
	}

	if (tgt->block_size > RZDISK_MAX_SECTOR || tgt->block_size <= 0) {
	 	 ret = D_INVALID_SIZE;
	  	goto done;
       	}

        rdata = (char *) kalloc(2*tgt->block_size);
	if (round_page(rdata) == round_page(rdata + tgt->block_size))
	    data = rdata;
	else
	    data = (char *)round_page(rdata);
#ifdef POWERMAC
	/* XXX TODO NMGS remove! must be cache aligned for now */
	if ((unsigned long)data & 0x1f)
		data = (char*)((unsigned long)(data + 0x1f) & ~0x1f);
	if (round_page(data) != round_page(data + tgt->block_size))
		data = (char *)round_page(data);
#endif /* POWERMAC */

	if (disk_size > SCSI_CMD_READ_MAX_LBA)
		tgt->flags |= TGT_BIG;

	/*
	 * Mandatory long-form commands ?
	 */
	if (BGET(scsi_use_long_form,(unsigned char)tgt->masterno,tgt->target_id))
		tgt->flags |= TGT_BIG;
	if (tgt->flags & TGT_BIG)
		readfun = scsi_long_read;

	ior->io_op = IO_INTERNAL;
	ior->io_error = 0;

#ifdef hp_pa
	if(geom_done)
		goto setup_label;
#endif

	/*
	 * Find out about the phys disk geometry
	 */

#if	PARAGON860

	/*
	 * The NCR RAID controller does not support a read capacity command
	 * with the Partial Medium Indicator (PMI) bit set.  Therefore we
	 * have to calculate the number of sectors per cylinder from data
	 * in the mode select pages.  This method should work for standalone
	 * disks as well.
	 */

	/*
	 * Read page 3 to find the number of sectors/track and bytes/sector
	 */
	ret = scsi_mode_sense(tgt, 0x03, 0xff, ior);
	/* scsi_error(...) */
	{
		scsi_mode_sense_page3_t	*page3;

		page3 = (scsi_mode_sense_page3_t *)
			(((scsi_mode_sense_data_t *)tgt->cmd_ptr) + 1);

		nsectors    = (page3->sectors_per_track_msb << 8) |
			       page3->sectors_per_track_lsb;

		sector_size = (page3->bytes_per_sector_msb  << 8) |
			       page3->bytes_per_sector_lsb;
	}

	ior->io_op = IO_INTERNAL;
	ior->io_error = 0;

	/*
	 * Read page 4 to find the number of cylinders and tracks/cylinder
	 */
	ret = scsi_mode_sense(tgt, 0x04, 0xff, ior);
	/* scsi_error(...) */
	{
		scsi_mode_sense_page4_t	*page4;

		page4 = (scsi_mode_sense_page4_t *)
			(((scsi_mode_sense_data_t *)tgt->cmd_ptr) + 1);

		ncylinders = (page4->number_of_cylinders_msb << 16) |
			     (page4->number_of_cylinders     <<  8) |
			      page4->number_of_cylinders_lsb;

		ntracks    = page4->number_of_heads;
	}

	/*
	 * Calculate the sectors per cylinder (sec/track * tracks/cyl)
	 */
	secs_per_cyl = nsectors * ntracks;

	if (scsi_debug) {
		printf("rz%d: %d bytes/sec %d sec/track\n", tgt->unit_no,
			sector_size, nsectors);
		printf("     %d tracks/cyl %d cyl/unit\n",
			ntracks, ncylinders);
	}

#else	/* PARAGON860 */
       /* Read page one to get read / write error recovery info */
       ret = scsi_mode_sense(tgt, 0x01, 0xff, ior);
       if(ret == SCSI_RET_SUCCESS) {
               scsi_mode_sense_page1_t *page1;
               unsigned char           mode_save[0xff];
               int length;

               length = *tgt->cmd_ptr + 1;
               bcopy(tgt->cmd_ptr, mode_save, length);

               page1 = (scsi_mode_sense_page1_t *)(mode_save + 4 + mode_save[3]);

               *mode_save = 0;         /* mode data length */
               page1->ps       = 0;
               page1->flags    = PAGE1_AWRE | PAGE1_ARRE | PAGE1_TB | PAGE1_PER;

               /*
                * Enable automatic reallocation of bad blocks,
                * Report any recovered errors.
                */
               ior->io_op = IO_INTERNAL;
               ior->io_error = 0;
               ret = scdisk_mode_select(tgt, 0, ior, mode_save, length, 0);
               if(ret != SCSI_RET_SUCCESS) {
		       if (scsi_debug)
                       printf("rz%d: Can't change error recovery parameters\n",
                                tgt->unit_no);
	       }
       }

       ior->io_op = IO_INTERNAL;
       ior->io_error = 0;

#ifdef	POWERMAC
	tgt->flags |= TGT_OPTIONAL_CMD;
#endif
		
	ret = scsi_read_capacity( tgt, 1, ior);
#ifdef POWERMAC
	tgt->flags &= ~TGT_OPTIONAL_CMD;
#endif
	/* scsi_error(...) */
	if (ret) {
		secs_per_cyl = 16;
		sector_size = tgt->block_size;
	} else {
		cap = (scsi_rcap_data_t*) tgt->cmd_ptr;
		secs_per_cyl =	(cap->lba1<<24) | (cap->lba2<<16) |
				(cap->lba3<< 8) |  cap->lba4;
		secs_per_cyl += 1;
		sector_size =	(cap->blen1<<24) | (cap->blen2<<16) |
			(cap->blen3<<8 ) |  cap->blen4;
	}
	if (scsi_debug)
	printf("rz%d: %d sect/cyl %d bytes/sec\n", tgt->unit_no,
		secs_per_cyl, sector_size);
#endif	/* PARAGON860 */

#if	NSCSI2 > 0
	/*
	 * ... and a bunch of other things for scsi2
	 */
#endif	/* NSCSI2 > 0 */

	/*
	 * Get partition table off pack
	 */

	if (tgt->dev_ops == &scsi_devsw[SCSI_CDROM]) {
		/* no label on a CD-ROM */
		look_for_label = FALSE;
	} else {
		look_for_label = TRUE;
	}

 setup_label:


	if (look_for_label) {
		/* first look for a BSD label */	
		ior->io_data = data;
		ior->io_count = tgt->block_size;
		ior->io_op = IO_READ;
		ior->io_error = 0;
		tgt->ior = ior;
		(*readfun)( tgt, LABELOFFSET/tgt->block_size, ior);
		iowait(ior);

		if (!ior->io_error) {
			/* Search for BSD label, might be a bit further along */
			register int	j;

			for (i = LABELOFFSET % tgt->block_size;
			     i < (tgt->block_size-sizeof(struct disklabel));
			     i += sizeof(int)) {
				label = (struct disklabel *) &data[i];
				if (label->d_magic  == DISKMAGIC &&
				    label->d_magic2 == DISKMAGIC) {
					break;
				} else
					label = (struct disklabel *) 0;
			}
		}
	} else {
		label = (struct disklabel *) 0;
	}

	if (label) {
		if (scsi_debug)
			printf("{Using BSD label}");
		tgt->dev_info.disk.l = *label;
	} else {
		/* then look for a vendor's label, but first
		   fill in defaults and what we found */

		label = &tgt->dev_info.disk.l;
		*label = scsi_default_label;
		label->d_secsize    = sector_size;
		label->d_nsectors   = nsectors;
		label->d_ntracks    = ntracks;
		label->d_ncylinders = ncylinders;
		label->d_secpercyl  = secs_per_cyl;
		label->d_secperunit = disk_size;

		ior->io_data = data;
		if (!look_for_label || !rz_vendor_label(tgt, label, ior)) {

			if (look_for_label) {
				printf("%s rz%d, %s\n",
				       "WARNING: No valid disk label on",
				       tgt->unit_no,
				       "using defaults");
			}

			/* Validate partitions a and c for initialization */
			tgt->dev_info.disk.l.d_partitions[0].p_offset = 0;
			tgt->dev_info.disk.l.d_partitions[0].p_size   = disk_size;
			tgt->dev_info.disk.l.d_partitions[2].p_offset = 0;
			tgt->dev_info.disk.l.d_partitions[2].p_size   = disk_size;
			tgt->dev_info.disk.l.d_partitions[MAXPARTITIONS].p_offset = 0;
			tgt->dev_info.disk.l.d_partitions[MAXPARTITIONS].p_size   = -1;
		}
		label->d_checksum = 0;
		label->d_checksum = dkcksum(label);
	}

	ret = SCSI_RET_SUCCESS;
done:
        if (rdata)
                kfree((vm_offset_t) rdata, 2 * tgt->block_size);
	io_req_free(ior);
	return ret;
}