www.pudn.com > libscsi.rar > libscsi.c



#include "libscsi.h"

/* ioctl */

struct megascsi_adapter *mega_scsi_adapter;
unsigned char	megascsi_adapt_no;
int	megascsi_ioctl_initialized = 0;
char dv_xname[] = "PERCSCSI"; 
 
unsigned int	USCSICMD = 0x80; 

void
megascsi_copyhds(scsi_adapter, sizes, props, stats)
	struct megascsi_adapter *scsi_adapter;
	const unsigned int *sizes;
	const unsigned char *props, *stats;
{
	int i;

	for (i = 0; i < scsi_adapter->adapter_nunits; i++) {
		scsi_adapter->adapter_hdr[i].hd_present = 1;
		scsi_adapter->adapter_hdr[i].hd_is_logdrv = 1;
		scsi_adapter->adapter_hdr[i].hd_size = letoh32(sizes[i]);
		scsi_adapter->adapter_hdr[i].hd_prop = props[i];
		scsi_adapter->adapter_hdr[i].hd_stat = stats[i];
		if (scsi_adapter->adapter_hdr[i].hd_size > 0x200000) {
			scsi_adapter->adapter_hdr[i].hd_heads = 255;
			scsi_adapter->adapter_hdr[i].hd_secs = 63;
		} else {
			scsi_adapter->adapter_hdr[i].hd_heads = 64;
			scsi_adapter->adapter_hdr[i].hd_secs = 32;
		}
	}
}


int
megascsi_ioctl_init()
{
	int error = 0;
	struct utsname uname_buf;

	char delimiters[] = ".-";
	char *token, cp[128], *tail;

    if ( uname(&uname_buf) )return EINVAL;
	strncpy(cp, uname_buf.release, 128);
	token = strtok(cp, delimiters);
	if ( strtol(token, &tail, 0) != 2 ) return EINVAL;
	token = strtok(NULL, delimiters);
	if ( strtol(token, &tail, 0) == 4 ) 
		USCSICMD = 0xe0;	/* kernel 2.4.x */
	else if (strtol(token, &tail, 0) == 6 ) 
			USCSICMD = 0x80;	/* kernel 2.6.x */
		else {
			printf("This program not supported on %s\n", uname_buf.release);
			return EINVAL;
		}	
			
	if (!megascsi_ioctl_initialized) {
	
		/* Initialize mega_scsi_adapter structure */
		mega_scsi_adapter = malloc(sizeof(struct megascsi_adapter));
		if ( mega_scsi_adapter == NULL ) {
			printf("MEGA Init Failture: No memory allocation\n");
			return EINVAL;
		}
		memset(mega_scsi_adapter, sizeof(struct megascsi_adapter), 0); 
		mega_scsi_adapter->adapter_handle = open("/dev/megadev0",  O_RDONLY); 
		if (mega_scsi_adapter->adapter_handle == (uint)-1) {
			printf("MEGA Init failture: Can not open device file /dev/megadev0\n");
			return EINVAL;
		}	
		megascsi_adapt_no = 0;
		mega_scsi_adapter->adapter_adapt_no = megascsi_adapt_no;
		megascsi_ioctl_initialized = 1;
	}
	megascsi_query_adapt(mega_scsi_adapter);
	return (error);
	
}

int
megascsi_query_adapt(scsi_adapter)
struct megascsi_adapter *scsi_adapter;
{
	int error = 0, i;
	char *p=NULL;
	struct megascsi_fc_einquiry *einq;
	struct megascsi_fc_prodinfo *pi;
	if ( !scsi_adapter ) return EINVAL;
	
	/* try FC inquiry first */

	

	
	p = malloc(sizeof(struct megascsi_fc_einquiry)+sizeof(struct megascsi_fc_prodinfo));
	if (!p) {
		printf("%s: No memory for adapter information\n",scsi_adapter->adapter_dev.dv_xname);
		return EINVAL;
	}
	einq = (struct megascsi_fc_einquiry *)p;
	pi = (struct megascsi_fc_prodinfo *)(p + sizeof(struct megascsi_fc_einquiry));

	memset(einq, 0, sizeof(struct megascsi_fc_einquiry));
	memset(pi, 0, sizeof(struct megascsi_fc_prodinfo));

	if (megascsi_mgmt(scsi_adapter, MEGASCSI_FCOP, MEGASCSI_FC_EINQ3_SOLICITED_FULL, 0, 0, sizeof 			(struct megascsi_fc_einquiry), einq)==0) {
		scsi_adapter->adapter_nunits = einq->ain_nlogdrv;
		megascsi_copyhds(scsi_adapter, einq->ain_ldsize, einq->ain_ldprop,
				einq->ain_ldstat);
		if (megascsi_mgmt(scsi_adapter, MEGASCSI_FCOP, MEGASCSI_FC_PRODINF, 0, 0, sizeof (struct 				megascsi_fc_prodinfo), pi)==0) 			{
				scsi_adapter->adapter_maxunits = MEGASCSI_BIG_MAX_LDRIVES;
				memcpy(scsi_adapter->adapter_fwver, pi->api_fwver, 16);
				scsi_adapter->adapter_fwver[15] = '\0';
				memcpy(scsi_adapter->adapter_biosver, pi->api_biosver, 16);
                                                                                                                                                                                                                                 				scsi_adapter->adapter_biosver[15] = '\0';
				scsi_adapter->adapter_channels = pi->api_channels;
				scsi_adapter->adapter_targets = pi->api_fcloops;
				scsi_adapter->adapter_memory = letoh16(pi->api_ramsize);
				scsi_adapter->adapter_maxcmds = pi->api_maxcmd;
//				p = "FC loop";
		
		}
		else
		{
			error = EINVAL;
			goto bail2;
		}
		if (scsi_adapter->adapter_maxunits == 0) {
			struct megascsi_inquiry *inq = (struct megascsi_inquiry *)einq;
			if (megascsi_mgmt(scsi_adapter, MEGASCSI_EINQUIRY, 0, 0, 0, sizeof(struct megascsi_inquiry) , inq)!=0){
				if (megascsi_mgmt(scsi_adapter, MEGASCSI_INQUIRY, 0, 0, 0, sizeof(struct megascsi_inquiry), inq)!=0){
					error = EINVAL;
					goto bail2;
				}	
			}
			scsi_adapter->adapter_maxunits = MEGASCSI_MAX_LDRIVES;
			scsi_adapter->adapter_nunits = inq->ain_nlogdrv;
			megascsi_copyhds(scsi_adapter, inq->ain_ldsize, inq->ain_ldprop,
			    inq->ain_ldstat);

			memcpy (scsi_adapter->adapter_fwver, inq->ain_fwver, 4);
			scsi_adapter->adapter_fwver[4] = '\0';
			memcpy (scsi_adapter->adapter_biosver, inq->ain_biosver, 4);
			scsi_adapter->adapter_biosver[4] = '\0';
			scsi_adapter->adapter_channels = inq->ain_channels;
			scsi_adapter->adapter_targets = inq->ain_targets;
			scsi_adapter->adapter_memory = inq->ain_ramsize;
			scsi_adapter->adapter_maxcmds = inq->ain_maxcmd;
//			p = "target";					
		}
	}
	else
	{
		error = EINVAL;
		goto bail2;
	}
/*	
#ifdef MEGASCSI_DEBUG
	printf(": FW %s, BIOS v%s, %dMB RAM\n"
	    "%s: %d channels, %d %ss, %d logical drives, "
	    "openings %d, max commands %d, quirks: %04x\n",
	    scsi_adapter->adapter_fwver, scsi_adapter->adapter_biosver, scsi_adapter->adapter_memory,
	    scsi_adapter->adapter_dev.dv_xname,
	    scsi_adapter->adapter_channels, scsi_adapter->adapter_targets, p, scsi_adapter->adapter_nunits,
	    scsi_adapter->adapter_link.openings, scsi_adapter->adapter_maxcmds, scsi_adapter->adapter_flags);
#else
	printf(": FW %s, BIOS v%s, %dMB RAM\n"
	    "%s: %d channels, %d %ss, %d logical drives\n",
	    scsi_adapter->adapter_fwver, scsi_adapter->adapter_biosver, scsi_adapter->adapter_memory,
	    scsi_adapter->adapter_dev.dv_xname,
	    scsi_adapter->adapter_channels, scsi_adapter->adapter_targets, p, scsi_adapter->adapter_nunits);

#endif  MEGASCSI_DEBUG */

	memcpy(scsi_adapter->adapter_dev.dv_xname, dv_xname, 16); 
	scsi_adapter->adapter_flags |= MEGASCSI_QUARTZ;
	for(i = 0; i < ((scsi_adapter->adapter_flags & MEGASCSI_QUARTZ) ?
	    MEGASCSI_BIG_MAX_PDRIVES : MEGASCSI_MAX_PDRIVES); i++) {
		memcpy(scsi_adapter->adapter_hdr[i].dev, scsi_adapter->adapter_dev.dv_xname, 16);
	}
	for(i = 0; i < MEGASCSI_MAX_CHANNEL; i++) {
		memcpy(scsi_adapter->adapter_rawadapters[i].adapter_procdev, scsi_adapter->adapter_dev.dv_xname, 16);
	}

bail2:
	if (p) free(p);
	return (error);
}

int
megascsi_ioctl_end()
{
	int error = 0;

	if (mega_scsi_adapter) {
		if (mega_scsi_adapter->adapter_handle)
			close(mega_scsi_adapter->adapter_handle);
		free(mega_scsi_adapter);
		megascsi_ioctl_initialized = 0;
	}
	else 
		error = EINVAL;
	return error;
}

int megascsi_ioctl_sel_adapt(new_adapt_no)
	unsigned char new_adapt_no;
{
	int old_adapt_no = 0;

	if (!mega_scsi_adapter) return EINVAL;
	old_adapt_no = mega_scsi_adapter->adapter_adapt_no;
	if ( old_adapt_no != new_adapt_no) { 
		mega_scsi_adapter->adapter_adapt_no = new_adapt_no;
		megascsi_query_adapt(mega_scsi_adapter);
	}
	megascsi_adapt_no = mega_scsi_adapter->adapter_adapt_no;		
	return old_adapt_no;
}

int
megascsi_ioctl(cmd, addr)
/*	struct megascsi_adapter *scsi_adapter;*/
	unsigned int cmd;
	char * addr;
{
//	struct megascsi_adapter *scsi_adapter = (struct megascsi_adapter *)dev;
	int error = 0;

	switch (cmd) {
	case MIOC_INQ:
		error = megascsi_ioctl_inq(mega_scsi_adapter, (struct mioc_inq *)addr);
		break;

	case MIOC_VOL:
		error = megascsi_ioctl_vol(mega_scsi_adapter, (struct mioc_vol *)addr);
		break;

	case MIOC_DISK:
		error = megascsi_ioctl_disk(mega_scsi_adapter, (struct mioc_disk *)addr);
		break;

	default:
		error = EINVAL;
	}

	return (error);
}

int
megascsi_drv_inq(scsi_adapter, ch, tg, page, inqbuf)
	struct megascsi_adapter *scsi_adapter;
	unsigned char ch;
	unsigned char tg;
	unsigned char page;
	void *inqbuf;
{
	mimd_t *mimd;
	struct megascsi_iocmd *cmd;
	struct megascsi_passthrough *ps;
	struct scsi_inquiry_data *pp;
	void *idata;
	int error = 0;

	if (!(idata = malloc(sizeof(mimd_t) + sizeof(struct scsi_inquiry_data)))) {
	    	return (ENOMEM);
	}

	mimd = (mimd_t *) idata;
	memset(mimd, 0, sizeof *mimd);

	ps = (struct megascsi_passthrough *)&(mimd->pthru);
	pp = idata + sizeof *mimd;
	memset(pp, 0, sizeof *pp);
	
	mimd->ui.fcs.opcode = 0x80;
	mimd->ui.fcs.adapno = 0x6d00 + scsi_adapter->adapter_adapt_no; 
	mimd->outlen = sizeof(struct scsi_inquiry_data);		
	mimd->data = (char *)htole(pp);

	cmd = (struct megascsi_iocmd *)&(mimd->mbox);
	cmd->acc_cmd = MEGASCSI_PASSTHRU;
	cmd->acc_passthru.apt_data = (unsigned int)htole(pp);
	ps->apt_channel = ch;
	ps->apt_target = tg;
	ps->apt_ncdb = sizeof(struct scsi_inquiry);
	ps->apt_nsense = sizeof(struct scsi_sense_data);

	ps->apt_cdb[0] = INQUIRY;
	ps->apt_cdb[1] = 0;
	ps->apt_cdb[2] = 0;
	ps->apt_cdb[3] = 0;
	ps->apt_cdb[4] = sizeof(struct scsi_inquiry_data); /* INQUIRY length */
	ps->apt_cdb[5] = 0;

	if (page != 0) {
		ps->apt_cdb[1] = SI_EVPD;
		ps->apt_cdb[2] = page;
	}
	ps->apt_data = (unsigned int)htole(pp);
	ps->apt_datalen = sizeof(struct scsi_inquiry_data);

	if (ioctl(scsi_adapter->adapter_handle, USCSICMD, mimd) == 0) {
		if (ps->apt_scsistat == 0x00) {
			memcpy(inqbuf, pp, sizeof(struct scsi_inquiry_data));

			if (pp->device != T_DIRECT)
				error = EINVAL;

			goto bail;
		}

		error = EINVAL;
	}
	else 
		error = EINVAL;

bail:
	free(idata);

	return (error);
}

int
megascsi_mgmt(scsi_adapter, opcode, par1, par2, par3, size, buffer)
	struct megascsi_adapter *scsi_adapter;
	unsigned char opcode;
	unsigned char par1;
	unsigned char par2;
	unsigned char par3;
	size_t size;
	void *buffer;
{
	mimd_t *mimd;
	struct megascsi_iocmd *cmd;
	void *idata;

	void *pa;
	int error = 0;


	if (!buffer) return (ENOMEM);
	if (!(idata = malloc(sizeof(mimd_t)))) {
	    	return (ENOMEM);
	}
	
	mimd = (mimd_t *)idata;
	memset(mimd, 0, sizeof(mimd_t));

	pa = buffer;

	mimd->ui.fcs.opcode = 0x80;
	mimd->ui.fcs.adapno = 0x6d00 + scsi_adapter->adapter_adapt_no; 
	mimd->outlen = size;		
	mimd->data = htole(pa);

	cmd = (struct megascsi_iocmd *)&(mimd->mbox);
	cmd->acc_cmd = opcode;

	/*
	 * some commands require data to be written to idata before sending
	 * command to fw
	 */
	switch (opcode) {
	case MEGASCSI_SPEAKER:
		*((char *)pa) = par1;
		break;
	default:
		cmd->acc_io.aio_channel = par1;
		cmd->acc_io.aio_param = par2;
		cmd->acc_io.aio_pad[0] = par3;
	};

	cmd->acc_io.aio_data = (unsigned int)htole(pa);

	if (ioctl(scsi_adapter->adapter_handle, USCSICMD, mimd) == 0) {

		/* XXX how do commands fail? */
		/* buffer = pa */		
/*		if (buffer)
			memcpy(buffer, pa, size);
*/
	}
	else
		error = EINVAL;

	if (idata) free(idata);

	return (error);
}

int
megascsi_ioctl_inq(scsi_adapter, bi)
	struct megascsi_adapter *scsi_adapter;
	struct mioc_inq *bi;
{
	struct megascsi_big_diskarray *p; /* struct too large for stack */
	char *plist;
	int i, s, t;
	int off;
	int error = 0;
	struct scsi_inquiry_data inqbuf;
	unsigned char ch, tg;

	p = malloc(sizeof(struct megascsi_big_diskarray));

	if (!p) {
		printf("%s: no memory for disk array\n",scsi_adapter->adapter_dev.dv_xname);
		return (ENOMEM);
	}
	memset(p, 0, sizeof(struct megascsi_big_diskarray));

	plist = malloc(MEGASCSI_BIG_MAX_PDRIVES);
	if (!plist) {
		printf("%s: no memory for disk list\n", scsi_adapter->adapter_dev.dv_xname);
		error = ENOMEM;
		goto bail;
	}

	if (megascsi_mgmt(scsi_adapter, MEGASCSI_FCOP, MEGASCSI_FC_RDCONF, 0, 0, sizeof *p, p)) {
		error = EINVAL;
		goto bail2;
	}

	memset(plist, 0, MEGASCSI_BIG_MAX_PDRIVES);

	bi->bi_novol = p->ada_nld;
	bi->bi_nodisk = 0;

	strncpy(bi->bi_dev, scsi_adapter->adapter_dev.dv_xname, sizeof(bi->bi_dev));

	/* do we actually care how many disks we have at this point? */
	for (i = 0; i < p->ada_nld; i++)
		for (s = 0; s < p->ald[i].adl_spandepth; s++)
			for (t = 0; t < p->ald[i].adl_nstripes; t++) {
				off = p->ald[i].asp[s].adv[t].add_channel *
				    MEGASCSI_MAX_TARGET +
				    p->ald[i].asp[s].adv[t].add_target;

				if (!plist[off]) {
					plist[off] = 1;
					bi->bi_nodisk++;
				}
			}

	/*
	 * hack warning!
	 * Megaraid cards sometimes return a size in the PD structure
	 * even though there is no disk in that slot.  Work around
	 * that by issuing an INQUIRY to determine if there is
	 * an actual disk in the slot.
	 */
	for(i = 0; i < ((scsi_adapter->adapter_flags & MEGASCSI_QUARTZ) ?
	    MEGASCSI_BIG_MAX_PDRIVES : MEGASCSI_MAX_PDRIVES); i++) {
	    	/* skip claimed drives */
	    	if (plist[i])
			continue;

	    	/*
		 * poke drive to make sure its there.  If it is it is either
		 * unused or a hot spare; at this point we dont care which it is
		 */
		if (p->apd[i].adp_size) {
			ch = (i & 0xf0) >> 4;
			tg = i & 0x0f;

			if (!megascsi_drv_inq(scsi_adapter, ch, tg, 0, &inqbuf)) {
				bi->bi_novol++;
				bi->bi_nodisk++;
				plist[i] = 1;
			}
		}
	}

bail2:
	free(plist);
bail:
	free(p);

	return (error);
}


int
megascsi_vol(scsi_adapter, bv, p)
	struct megascsi_adapter *scsi_adapter;
	struct mioc_vol *bv;
	struct megascsi_big_diskarray *p;
{
	struct scsi_inquiry_data inqbuf;
	char *plist;
	int i, s, t, off;
	int ld = p->ada_nld, error = EINVAL;
	unsigned char ch, tg;

	plist = malloc(MEGASCSI_BIG_MAX_PDRIVES);
	if (!plist) {
		printf("%s: no memory for disk list\n",scsi_adapter->adapter_dev.dv_xname);
		return (ENOMEM);
	}

	memset(plist, 0, MEGASCSI_BIG_MAX_PDRIVES);

	/* setup plist */
	for (i = 0; i < p->ada_nld; i++)
		for (s = 0; s < p->ald[i].adl_spandepth; s++)
			for (t = 0; t < p->ald[i].adl_nstripes; t++) {
				off = p->ald[i].asp[s].adv[t].add_channel *
				    MEGASCSI_MAX_TARGET +
				    p->ald[i].asp[s].adv[t].add_target;

				if (!plist[off])
					plist[off] = 1;
			}

	for(i = 0; i < ((scsi_adapter->adapter_flags & MEGASCSI_QUARTZ) ?
	    MEGASCSI_BIG_MAX_PDRIVES : MEGASCSI_MAX_PDRIVES); i++) {
	    	/* skip claimed drives */
	    	if (plist[i])
			continue;

	    	/*
		 * poke drive to make sure its there.  If it is it is either
		 * unused or a hot spare; at this point we dont care which it is
		 */
		if (p->apd[i].adp_size) {
			ch = (i & 0xf0) >> 4;
			tg = i & 0x0f;

			if (!megascsi_drv_inq(scsi_adapter, ch, tg, 0, &inqbuf)) {
				if (ld != bv->bv_volid) {
					ld++;
					continue;
				}

				bv->bv_status = MIOC_SVONLINE;
				bv->bv_size = (unsigned long long)p->apd[i].adp_size *
				    (unsigned long long)512;
				bv->bv_nodisk = 1;
				strncpy(bv->bv_dev,
				    scsi_adapter->adapter_hdr[bv->bv_volid].dev,
				    sizeof(bv->bv_dev));

				if (p->apd[i].adp_ostatus == MEGASCSI_PD_HOTSPARE
				    && p->apd[i].adp_type == 0)
					bv->bv_level = -1;
				else
					bv->bv_level = -2;

				error = 0;
				goto bail;
			}
		}
	}

bail:
	free(plist);

	return (error);
}

int
megascsi_disk(scsi_adapter, bd, p)
	struct megascsi_adapter *scsi_adapter;
	struct mioc_disk *bd;
	struct megascsi_big_diskarray *p;
{
	struct scsi_inquiry_data inqbuf;
	struct scsi_inquiry_vpd vpdbuf;
	char *plist;
	int i, s, t, off;
	int ld = p->ada_nld, error = EINVAL;
	unsigned char ch, tg;

	plist = malloc(MEGASCSI_BIG_MAX_PDRIVES);
	if (!plist) {
		printf("%s: no memory for disk list\n",scsi_adapter->adapter_dev.dv_xname);
		return (ENOMEM);
	}

	memset(plist, 0, MEGASCSI_BIG_MAX_PDRIVES);

	/* setup plist */
	for (i = 0; i < p->ada_nld; i++)
		for (s = 0; s < p->ald[i].adl_spandepth; s++)
			for (t = 0; t < p->ald[i].adl_nstripes; t++) {
				off = p->ald[i].asp[s].adv[t].add_channel *
				    MEGASCSI_MAX_TARGET +
				    p->ald[i].asp[s].adv[t].add_target;

				if (!plist[off])
					plist[off] = 1;
			}

	for(i = 0; i < ((scsi_adapter->adapter_flags & MEGASCSI_QUARTZ) ?
	    MEGASCSI_BIG_MAX_PDRIVES : MEGASCSI_MAX_PDRIVES); i++) {
	    	/* skip claimed drives */
	    	if (plist[i])
			continue;

	    	/*
		 * poke drive to make sure its there.  If it is it is either
		 * unused or a hot spare; at this point we dont care which it is
		 */
		if (p->apd[i].adp_size) {
			ch = (i & 0xf0) >> 4;
			tg = i & 0x0f;

			if (!megascsi_drv_inq(scsi_adapter, ch, tg, 0, &inqbuf)) {
				char vend[8+16+4+1];

				if (ld != bd->bd_volid) {
					ld++;
					continue;
				}

				memcpy(vend, inqbuf.vendor,
				    sizeof vend - 1);

				vend[sizeof vend - 1] = '\0';
				strncpy(bd->bd_vendor, vend,
				    sizeof(bd->bd_vendor));

				if (!megascsi_drv_inq(scsi_adapter, ch, tg, 0x80, &vpdbuf)) {
					char ser[32 + 1];

					memcpy(ser, vpdbuf.serial,
					    sizeof ser - 1);

					ser[sizeof ser - 1] = '\0';
					if (vpdbuf.page_length < sizeof ser)
						ser[vpdbuf.page_length] = '\0';
					strncpy(bd->bd_serial, ser,
					    sizeof(bd->bd_serial));
				}

				bd->bd_size = (unsigned long long)p->apd[i].adp_size *
				    (unsigned long long)512;

				bd->bd_channel = ch;
				bd->bd_target = tg;

				strncpy(bd->bd_procdev,
				    scsi_adapter->adapter_rawadapters[ch].adapter_procdev,
				    sizeof(bd->bd_procdev));

				if (p->apd[i].adp_ostatus == MEGASCSI_PD_HOTSPARE
				    && p->apd[i].adp_type == 0)
					bd->bd_status = MIOC_SDHOTSPARE;
				else
					bd->bd_status = MIOC_SDUNUSED;

				error = 0;
				goto bail;
			}
		}
	}

bail:
	free(plist);

	return (error);
}

int
megascsi_ioctl_vol(scsi_adapter, bv)
	struct megascsi_adapter *scsi_adapter;
	struct mioc_vol *bv;
{
	struct megascsi_big_diskarray *p; /* struct too large for stack */
	int i, s, t;
	int error = 0;

	p = malloc(sizeof(struct megascsi_big_diskarray));
	if (!p) {
		printf("%s: no memory for raw interface\n",scsi_adapter->adapter_dev.dv_xname);
		return (ENOMEM);
	}

	memset(p, 0, sizeof(struct megascsi_big_diskarray));
	if (megascsi_mgmt(scsi_adapter, MEGASCSI_FCOP, MEGASCSI_FC_RDCONF, 0, 0, sizeof *p, p)) {
		error = EINVAL;
		goto bail;
	}

	if (bv->bv_volid >= p->ada_nld) {
		error = megascsi_vol(scsi_adapter, bv, p);
		goto bail;
	}

	i = bv->bv_volid;

	switch (p->ald[i].adl_status) {
	case MEGASCSI_RDRV_OFFLINE:
		bv->bv_status = MIOC_SVOFFLINE;
		break;

	case MEGASCSI_RDRV_DEGRADED:
		bv->bv_status = MIOC_SVDEGRADED;
		break;

	case MEGASCSI_RDRV_OPTIMAL:
		bv->bv_status = MIOC_SVONLINE;
		break;

	default:
		bv->bv_status = MIOC_SVINVALID;
	}

	bv->bv_size = 0;
	bv->bv_level = p->ald[i].adl_raidlvl;
	bv->bv_nodisk = 0;

	for (s = 0; s < p->ald[i].adl_spandepth; s++) {
		for (t = 0; t < p->ald[i].adl_nstripes; t++)
			bv->bv_nodisk++;

		switch (bv->bv_level) {
		case 0:
			bv->bv_size += p->ald[i].asp[s].ads_length *
			    p->ald[i].adl_nstripes;
			break;

		case 1:
			bv->bv_size += p->ald[i].asp[s].ads_length;
			break;

		case 5:
			bv->bv_size += p->ald[i].asp[s].ads_length *
			    (p->ald[i].adl_nstripes - 1);
			break;
		}
	}

	if (p->ald[i].adl_spandepth > 1)
		bv->bv_level *= 10;

	bv->bv_size *= (unsigned long long)512;

	strncpy(bv->bv_dev, scsi_adapter->adapter_hdr[i].dev, sizeof(bv->bv_dev));
	
bail:
	free(p);

	return (error);
}

int
megascsi_ioctl_disk(scsi_adapter, bd)
	struct megascsi_adapter *scsi_adapter;
	struct mioc_disk *bd;
{
	struct scsi_inquiry_data inqbuf;
	struct scsi_inquiry_vpd vpdbuf;
	struct megascsi_big_diskarray *p; /* struct too large for stack */
	int i, s, t, d;
	int off;
	int error = 0;
	unsigned short ch, tg;

	p = malloc(sizeof(struct megascsi_big_diskarray));
	if (!p) {
		printf("%s: no memory for raw interface\n",scsi_adapter->adapter_dev.dv_xname);
		return (ENOMEM);
	}

	memset(p, 0, sizeof(struct megascsi_big_diskarray));
	if (megascsi_mgmt(scsi_adapter, MEGASCSI_FCOP, MEGASCSI_FC_RDCONF, 0, 0, sizeof *p, p)) {
		error = EINVAL;
		goto bail;
	}

	if (bd->bd_volid >= p->ada_nld) {
		error = megascsi_disk(scsi_adapter, bd, p);
		goto bail;
	}

	i = bd->bd_volid;
	error = EINVAL;

	for (s = 0, d = 0; s < p->ald[i].adl_spandepth; s++)
		for (t = 0; t < p->ald[i].adl_nstripes; t++) {
			if (d != bd->bd_diskid) {
				d++;
				continue;
			}

			off = p->ald[i].asp[s].adv[t].add_channel *
			    MEGASCSI_MAX_TARGET +
			    p->ald[i].asp[s].adv[t].add_target;

			switch (p->apd[off].adp_ostatus) {
			case MEGASCSI_PD_UNCNF:
				bd->bd_status = MIOC_SDUNUSED;
				break;

			case MEGASCSI_PD_ONLINE:
				bd->bd_status = MIOC_SDONLINE;
				break;

			case MEGASCSI_PD_FAILED:
				bd->bd_status = MIOC_SDFAILED;
				break;

			case MEGASCSI_PD_RBLD:
				bd->bd_status = MIOC_SDREBUILD;
				break;

			case MEGASCSI_PD_HOTSPARE:
				bd->bd_status = MIOC_SDHOTSPARE;
				break;

			default:
				bd->bd_status = MIOC_SDINVALID;
			}

			bd->bd_size = (unsigned long long)p->apd[off].adp_size *
			    (unsigned long long)512;

			ch = p->ald[i].asp[s].adv[t].add_target >> 4;
			tg = p->ald[i].asp[s].adv[t].add_target & 0x0f;

			if (!megascsi_drv_inq(scsi_adapter, ch, tg, 0, &inqbuf)) {
				char vend[8+16+4+1];

				memcpy(vend, inqbuf.vendor, sizeof vend - 1);

				vend[sizeof vend - 1] = '\0';
				strncpy(bd->bd_vendor, vend,
				    sizeof(bd->bd_vendor));
			}

			if (!megascsi_drv_inq(scsi_adapter, ch, tg, 0x80, &vpdbuf)) {
				char ser[32 + 1];

				memcpy(ser, vpdbuf.serial, sizeof ser - 1);

				ser[sizeof ser - 1] = '\0';
				if (vpdbuf.page_length < sizeof ser)
					ser[vpdbuf.page_length] = '\0';
				strncpy(bd->bd_serial, ser,
				    sizeof(bd->bd_serial));
			}

			bd->bd_channel = ch;
			bd->bd_target = tg;

			strncpy(bd->bd_procdev, scsi_adapter->adapter_rawadapters[ch].adapter_procdev,
			    sizeof(bd->bd_procdev));

			error = 0;
			goto bail;
		}

	/* XXX if we reach this do dedicated hotspare magic*/
bail:
	free(p);

	return (error);
}