Пример #1
0
int
kvss905c_set_windows(int fd,
            const struct kvss905c_window *window,
            char duplex,
            u8 *requestsense) {
  // see page 35
  u8 windowbytes[6 + 2 + WINDOW_SIZE];
  memset(windowbytes, 0, sizeof(windowbytes));

  const int bytes_written = kvss905c_window_serialise(windowbytes + 8, window);
  if (bytes_written != WINDOW_SIZE) abort();

  const u16 length = htons(WINDOW_SIZE);
  memcpy(windowbytes + 6, &length, sizeof(length));
  static const int transfer_length = sizeof(windowbytes);

  const u8 command[] = {0x24, 0, 0, 0, 0, 0, transfer_length >> 16,
                        transfer_length >> 8, transfer_length, 0};
  const int r = scsi_command(fd, SG_DXFER_TO_DEV, command, sizeof(command),
                             windowbytes, transfer_length, requestsense, 0);

  if (r) return r;

  if (duplex) {
    windowbytes[8] = 0x80;
    return scsi_command(fd, SG_DXFER_TO_DEV, command, sizeof(command),
                        windowbytes, transfer_length, requestsense, 0);
  }

  return 0;
}
Пример #2
0
/*
 * sd_read_capacity:
 *
 *	Find out from the device what its capacity is.
 */
static uint64_t
sd_read_capacity(struct sd_softc *sd, int *blksize)
{
	union {
		struct scsipi_read_capacity_10 cmd;
		struct scsipi_read_capacity_16 cmd16;
	} cmd;
	union {
		struct scsipi_read_capacity_10_data data;
		struct scsipi_read_capacity_16_data data16;
	} data;
	uint64_t rv;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cmd.opcode = READ_CAPACITY_10;

	/*
	 * If the command works, interpret the result as a 4 byte
	 * number of blocks
	 */
	rv = 0;
	memset(&data, 0, sizeof(data.data));
	if (scsi_command(sd, (void *)&cmd.cmd, sizeof(cmd.cmd),
	    (void *)&data, sizeof(data.data)) != 0)
		goto out;

	if (_4btol(data.data.addr) != 0xffffffff) {
		*blksize = _4btol(data.data.length);
		rv = _4btol(data.data.addr) + 1;
		goto out;
	}

	/*
	 * Device is larger than can be reflected by READ CAPACITY (10).
	 * Try READ CAPACITY (16).
	 */

	memset(&cmd, 0, sizeof(cmd));
	cmd.cmd16.opcode = READ_CAPACITY_16;
	cmd.cmd16.byte2 = SRC16_SERVICE_ACTION;
	_lto4b(sizeof(data.data16), cmd.cmd16.len);

	memset(&data, 0, sizeof(data.data16));
	if (scsi_command(sd, (void *)&cmd.cmd16, sizeof(cmd.cmd16),
	    (void *)&data, sizeof(data.data16)) != 0)
		goto out;

	*blksize = _4btol(data.data16.length);
	rv = _8btol(data.data16.addr) + 1;

 out:
	return rv;
}
Пример #3
0
int
kvss905c_stop(int fd, u8 *requestsense) {
  // see page 89
  static const u8 command[] = {0xe1, 0, 0x8b, 0, 0, 0, 0, 0, 0, 0};
  return scsi_command(fd, SG_DXFER_TO_DEV, command, sizeof(command),
                      NULL, 0, requestsense, 0);
}
Пример #4
0
int
kvss905c_read(int fd, u8 type, u8 q1, u8 q2, u8 *buffer, u32 length, u8 *requestsense) {
  // see page 50
  const u8 command[] = {0x28, 0, type, 0, q1, q2, length >> 16, length >> 8, length, 0};

  return scsi_command(fd, SG_DXFER_FROM_DEV, command, sizeof(command),
                             buffer, length, requestsense, 0);
}
Пример #5
0
int
scsi_mode_sense(struct sd_softc *sd, int byte2, int page,
		  struct scsi_mode_parameter_header_6 *data, int len)
{
	struct scsi_mode_sense_6 cmd;

	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = SCSI_MODE_SENSE_6;
	cmd.byte2 = byte2;
	cmd.page = page;
	cmd.length = len & 0xff;

	return scsi_command(sd, (void *)&cmd, sizeof(cmd), (void *)data, len);
}
Пример #6
0
int
kvss905c_detect(int fd) {
  // see page 28
  u8 requestsense[REQUEST_SENSE_SIZE];

  u8 inquirydata[96];
  memset(inquirydata, 0, sizeof(inquirydata));
  static const u8 command[] = {0x12, 0, 0, 0, 0x60, 0};

  const int r = scsi_command(fd, SG_DXFER_FROM_DEV, command, sizeof(command),
                             inquirydata, sizeof(inquirydata), requestsense, 0);
  if (r) return r;

  // We check that the model string begins with "KV-"
  return memcmp(inquirydata + 16, "KV-", 3);
}
Пример #7
0
int
get_data_buffer_status(int fd, u8 *window_id, u32 *length, u8 *requestsense) {
  // see page 71
  u8 buffer[12];
  static const u8 command[] = {0x34, 0, 0, 0, 0, 0, 0, 0, sizeof(buffer), 0};
  const int r = scsi_command(fd, SG_DXFER_FROM_DEV, command, sizeof(command),
                             buffer, sizeof(buffer), requestsense, 0);
  if (r) return r;

  *window_id = buffer[4];
  const u32 length_be = ((u32) buffer[9]) << 16 |
                        ((u32) buffer[10]) << 8 |
                        ((u32) buffer[11]);
  *length = ntohl(length_be);

  return 0;
}
Пример #8
0
int
test_unit_ready(int fd, u8 *requestsense) {
  static const u8 command[] = {0, 0, 0, 0, 0, 0};
  return scsi_command(fd, SG_DXFER_FROM_DEV, command, sizeof(command), NULL, 0,
                      requestsense, 0);
}
Пример #9
0
void nscsi_full_device::step(bool timeout)
{
	UINT32 ctrl = scsi_bus->ctrl_r();
	UINT32 data = scsi_bus->data_r();
	if(ctrl & S_RST) {
		scsi_bus->data_w(scsi_refid, 0);
		scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
		scsi_state = IDLE;
		logerror("%s: scsi bus reset\n", tag());
		return;
	}

	if(0)
		logerror("%s: state=%d.%d %s\n",
					tag(), scsi_state & STATE_MASK, (scsi_state & SUB_MASK) >> SUB_SHIFT,
					timeout ? "timeout" : "change");

	switch(scsi_state & SUB_MASK ? scsi_state & SUB_MASK : scsi_state & STATE_MASK) {
	case IDLE:
		if(((ctrl & (S_SEL|S_BSY)) == S_SEL) && (scsi_id != -1) && ((data & (1 << scsi_id)) != 0)) {
			for(scsi_initiator_id = 0; scsi_initiator_id != 16 && (scsi_initiator_id == scsi_id || (data & (1 << scsi_initiator_id))); scsi_initiator_id++);
			if(scsi_initiator_id == 16)
				scsi_initiator_id = -1;
			scsi_state = TARGET_SELECT_WAIT_BUS_SETTLE;
			scsi_timer->adjust(scsi_bus_settle_delay());
		}
		break;

	case TARGET_SELECT_WAIT_BUS_SETTLE:
		if((ctrl & (S_SEL|S_BSY)) == S_SEL) {
			scsi_state = TARGET_SELECT_WAIT_SEL_0;
			scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
		} else
			scsi_state = IDLE;
		break;

	case TARGET_SELECT_WAIT_SEL_0:
		if(ctrl & S_SEL)
			break;
		buf_control_push()->action = BC_MSG_OR_COMMAND;
		scsi_state = TARGET_NEXT_CONTROL;
		step(false);
		break;

	case RECV_BYTE_T_WAIT_ACK_1 << SUB_SHIFT:
		if(ctrl & S_ACK) {
			scsi_put_data(data_buffer_id, data_buffer_pos++, scsi_bus->data_r());
			scsi_state = (scsi_state & STATE_MASK) | (RECV_BYTE_T_WAIT_ACK_0 << SUB_SHIFT);
			scsi_bus->ctrl_w(scsi_refid, 0, S_REQ);
		}
		break;

	case RECV_BYTE_T_WAIT_ACK_0 << SUB_SHIFT:
		if(!(ctrl & S_ACK)) {
			scsi_state &= STATE_MASK;
			scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
			step(false);
		}
		break;

	case SEND_BYTE_T_WAIT_ACK_1 << SUB_SHIFT:
		if(ctrl & S_ACK) {
			scsi_state = (scsi_state & STATE_MASK) | (SEND_BYTE_T_WAIT_ACK_0 << SUB_SHIFT);
			scsi_bus->data_w(scsi_refid, 0);
			scsi_bus->ctrl_w(scsi_refid, 0, S_REQ);
		}
		break;

	case SEND_BYTE_T_WAIT_ACK_0 << SUB_SHIFT:
		if(!(ctrl & S_ACK)) {
			scsi_state &= STATE_MASK;
			scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
			step(false);
		}
		break;

	case TARGET_NEXT_CONTROL: {
		control *ctl = buf_control_pop();
		switch(ctl->action) {
		case BC_MSG_OR_COMMAND:
			data_buffer_id = SBUF_MAIN;
			data_buffer_pos = 0;
			if(ctrl & S_ATN) {
				scsi_state = TARGET_WAIT_MSG_BYTE;
				scsi_bus->ctrl_w(scsi_refid, S_PHASE_MSG_OUT, S_PHASE_MASK);
			} else {
				scsi_state = TARGET_WAIT_CMD_BYTE;
				scsi_bus->ctrl_w(scsi_refid, S_PHASE_COMMAND, S_PHASE_MASK);
			}
			target_recv_byte();
			break;

		case BC_STATUS:
			scsi_bus->ctrl_w(scsi_refid, S_PHASE_STATUS, S_PHASE_MASK);
			target_send_byte(ctl->param1);
			break;

		case BC_DATA_IN:
			scsi_bus->ctrl_w(scsi_refid, S_PHASE_DATA_IN, S_PHASE_MASK);
			data_buffer_id = ctl->param1;
			data_buffer_size = ctl->param2;
			data_buffer_pos = 0;
			scsi_state = TARGET_WAIT_DATA_IN_BYTE;
			target_send_buffer_byte();
			break;

		case BC_DATA_OUT:
			scsi_bus->ctrl_w(scsi_refid, S_PHASE_DATA_OUT, S_PHASE_MASK);
			data_buffer_id = ctl->param1;
			data_buffer_size = ctl->param2;
			data_buffer_pos = 0;
			scsi_state = TARGET_WAIT_DATA_OUT_BYTE;
			target_recv_byte();
			break;

		case BC_MESSAGE_1:
			scsi_bus->ctrl_w(scsi_refid, S_PHASE_MSG_IN, S_PHASE_MASK);
			target_send_byte(ctl->param1);
			break;

		case BC_BUS_FREE:
			scsi_bus->data_w(scsi_refid, 0);
			scsi_bus->ctrl_wait(scsi_refid, S_BSY|S_SEL|S_RST, S_ALL);
			scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
			scsi_state = IDLE;
			break;
		};
		break;
	}

	case TARGET_WAIT_DATA_IN_BYTE:
		if(data_buffer_pos == data_buffer_size-1)
			scsi_state = TARGET_NEXT_CONTROL;
		target_send_buffer_byte();
		break;

	case TARGET_WAIT_DATA_OUT_BYTE:
		if(data_buffer_pos == data_buffer_size-1)
			scsi_state = TARGET_NEXT_CONTROL;
		target_recv_byte();
		break;

	case TARGET_WAIT_MSG_BYTE:
		if(ctrl & S_SEL)
			return;
		if(!(ctrl & S_ATN)) {
			scsi_cmdsize = data_buffer_pos;
			scsi_message();
			data_buffer_id = SBUF_MAIN;
			data_buffer_pos = 0;
			scsi_state = TARGET_WAIT_CMD_BYTE;
			scsi_bus->ctrl_w(scsi_refid, S_PHASE_COMMAND, S_PHASE_MASK);
		}
		target_recv_byte();
		break;

	case TARGET_WAIT_CMD_BYTE:
		if(ctrl & S_SEL)
			return;
		if(ctrl & S_ATN) {
			logerror("%s: Parity error? Say what?\n", tag());
			scsi_state = IDLE;
			break;
		}

		if(command_done()) {
			scsi_cmdsize = data_buffer_pos;
			scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
			scsi_command();
			scsi_state = TARGET_NEXT_CONTROL;
			step(false);
		} else
			target_recv_byte();
		break;

	default:
		logerror("%s: step() unexpected state %d.%d\n",
					tag(),
					scsi_state & STATE_MASK, (scsi_state & SUB_MASK) >> SUB_SHIFT);
		exit(0);
	}
}
Пример #10
0
//
// USB Callbacks
//
bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* const device)
{
    return scsi_command(sd, device);
}
Пример #11
0
/*
 * Read some data.
 */
int
sdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize)
{
	struct sd_softc *sd;
	struct disklabel *lp;
	struct partition *pp;
	struct scsipi_generic *cmdp;
	struct scsipi_rw_16 cmd16;
	struct scsipi_rw_10 cmd_big;
	struct scsi_rw_6 cmd_small;
	daddr_t blkno;
	int cmdlen, nsect, i;
	uint8_t *buf;

	if (size == 0)
		return 0;
    
	if (rw != F_READ)
		return EOPNOTSUPP;

	buf = p;
	sd = f;
	lp = &sd->sc_label;
	pp = &lp->d_partitions[sd->sc_part];

	if (!(sd->sc_flags & FLAGS_MEDIA_LOADED))
		return EIO;

	nsect = howmany(size, lp->d_secsize);
	blkno = dblk + pp->p_offset;

	for (i = 0; i < nsect; i++, blkno++) {
		int error;

		/*
		 * Fill out the scsi command.  Use the smallest CDB possible
		 * (6-byte, 10-byte, or 16-byte).
		 */
		if ((blkno & 0x1fffff) == blkno) {
			/* 6-byte CDB */
			memset(&cmd_small, 0, sizeof(cmd_small));
			cmd_small.opcode = SCSI_READ_6_COMMAND;
			_lto3b(blkno, cmd_small.addr);
			cmd_small.length = 1;
			cmdlen = sizeof(cmd_small);
			cmdp = (struct scsipi_generic *)&cmd_small;
		} else if ((blkno & 0xffffffff) == blkno) {
			/* 10-byte CDB */
			memset(&cmd_big, 0, sizeof(cmd_big));
			cmd_small.opcode = READ_10;
			_lto4b(blkno, cmd_big.addr);
			_lto2b(1, cmd_big.length);
			cmdlen = sizeof(cmd_big);
			cmdp = (struct scsipi_generic *)&cmd_big;
		} else {
			/* 16-byte CDB */
			memset(&cmd16, 0, sizeof(cmd16));
			cmd_small.opcode = READ_16;
			_lto8b(blkno, cmd16.addr);
			_lto4b(1, cmd16.length);
			cmdlen = sizeof(cmd16);
			cmdp = (struct scsipi_generic *)&cmd16;
		}

		error = scsi_command(sd, cmdp, cmdlen, buf, lp->d_secsize);
		if (error)
			return error;

		buf += lp->d_secsize;
	}

	*rsize = size;
	return 0;
}
Пример #12
0
/*
 * Open device (read drive parameters and disklabel)
 */
int
sdopen(struct open_file *f, ...)
{
	struct sd_softc *sd;
	struct scsi_test_unit_ready cmd;
	struct scsipi_inquiry_data *inqbuf;
	u_int bus, target, lun, part;
	int error;
	char buf[SCSIPI_INQUIRY_LENGTH_SCSI2];
	va_list ap;

	va_start(ap, f);
	bus = 0;
	target = va_arg(ap, u_int);
	lun = va_arg(ap, u_int);
	part = va_arg(ap, u_int);
	va_end(ap);

	DPRINTF(("sdopen: sd(%d,%d,%d)\n", target, lun, part));

	sd = alloc(sizeof(struct sd_softc));
	if (sd == NULL)
		return ENOMEM;

	memset(sd, 0, sizeof(struct sd_softc));

	sd->sc_part = part;
	sd->sc_lun = lun;
	sd->sc_target = target;
	sd->sc_bus = bus;

	if ((error = scsi_inquire(sd, sizeof(buf), buf)) != 0)
		return error;

	inqbuf = (struct scsipi_inquiry_data *)buf;

	sd->sc_type = inqbuf->device & SID_TYPE;

	/*
	 * Determine the operating mode capabilities of the device.
	 */
	if ((inqbuf->version & SID_ANSII) >= 2) {
//		if ((inqbuf->flags3 & SID_CmdQue) != 0)
//			sd->sc_cap |= PERIPH_CAP_TQING;
		if ((inqbuf->flags3 & SID_Sync) != 0)
			sd->sc_cap |= PERIPH_CAP_SYNC;

		/* SPC-2 */
		if ((inqbuf->version & SID_ANSII) >= 3) {
			/*
			 * Report ST clocking though CAP_WIDExx/CAP_SYNC.
			 * If the device only supports DT, clear these
			 * flags (DT implies SYNC and WIDE)
			 */
			switch (inqbuf->flags4 & SID_Clocking) {
			case SID_CLOCKING_DT_ONLY:
				sd->sc_cap &= ~PERIPH_CAP_SYNC;
				break;
			}
		}
	}
	sd->sc_flags =
	    (inqbuf->dev_qual2 & SID_REMOVABLE) ? FLAGS_REMOVABLE : 0;

	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = SCSI_TEST_UNIT_READY;
	if ((error = scsi_command(sd, (void *)&cmd, sizeof(cmd), NULL, 0)) != 0)
		return error;

	if (sd->sc_flags & FLAGS_REMOVABLE) {
		printf("XXXXX: removable device found. will not support\n");
	}
	if (!(sd->sc_flags & FLAGS_MEDIA_LOADED))
		sd->sc_flags |= FLAGS_MEDIA_LOADED;

	if ((error = sd_get_parms(sd)) != 0)
		return error;

	strncpy(sd->sc_label.d_typename, inqbuf->product, 16);
	if ((error = sdgetdisklabel(sd)) != 0)
		return error;

	f->f_devdata = sd;
	return 0;
}
Пример #13
0
/*
 * Get the scsi driver to send a full inquiry to the * device and use the
 * results to fill out the disk parameter structure.
 */
static int
sd_get_capacity(struct sd_softc *sd)
{
	struct disk_parms *dp = &sd->sc_params;
	uint64_t blocks;
	int error, blksize;

	dp->disksize = blocks = sd_read_capacity(sd, &blksize);
	if (blocks == 0) {
		struct scsipi_read_format_capacities cmd;
		struct {
			struct scsipi_capacity_list_header header;
			struct scsipi_capacity_descriptor desc;
		} __packed data;

		memset(&cmd, 0, sizeof(cmd));
		memset(&data, 0, sizeof(data));
		cmd.opcode = READ_FORMAT_CAPACITIES;
		_lto2b(sizeof(data), cmd.length);

		error = scsi_command(sd,
		    (void *)&cmd, sizeof(cmd), (void *)&data, sizeof(data));
		if (error == EFTYPE)
			/* Medium Format Corrupted, handle as not formatted */
			return SDGP_RESULT_UNFORMATTED;
		if (error || data.header.length == 0)
			return SDGP_RESULT_OFFLINE;

		switch (data.desc.byte5 & SCSIPI_CAP_DESC_CODE_MASK) {
		case SCSIPI_CAP_DESC_CODE_RESERVED:
		case SCSIPI_CAP_DESC_CODE_FORMATTED:
			break;

		case SCSIPI_CAP_DESC_CODE_UNFORMATTED:
			return SDGP_RESULT_UNFORMATTED;

		case SCSIPI_CAP_DESC_CODE_NONE:
			return SDGP_RESULT_OFFLINE;
		}

		dp->disksize = blocks = _4btol(data.desc.nblks);
		if (blocks == 0)
			return SDGP_RESULT_OFFLINE;		/* XXX? */

		blksize = _3btol(data.desc.blklen);

	} else if (!sd_validate_blksize(blksize)) {
		struct sd_mode_sense_data scsipi_sense;
		int bsize;

		memset(&scsipi_sense, 0, sizeof(scsipi_sense));
		error = scsi_mode_sense(sd, 0, 0, &scsipi_sense.header,
		    sizeof(struct scsi_mode_parameter_header_6) +
						sizeof(scsipi_sense.blk_desc));
		if (!error) {
			bsize = scsipi_sense.header.blk_desc_len;

			if (bsize >= 8)
				blksize = _3btol(scsipi_sense.blk_desc.blklen);
		}
	}

	if (!sd_validate_blksize(blksize))
		blksize = SD_DEFAULT_BLKSIZE;

	dp->blksize = blksize;
	dp->disksize512 = (blocks * dp->blksize) / DEV_BSIZE;
	return 0;
}
Пример #14
0
HalDevice *
devinfo_mass_add(HalDevice *parent, const char *devnode, char *devfs_path, char *device_type)
{
	HalDevice *d = NULL, *gparent = NULL;
	prop_dictionary_t dict;
	struct disklabel label;
	struct stat st;
	const char *driver;
	char *rdevpath, *devpath;
	char *childnode;
	char *parent_devnode, *gparent_devnode = NULL;
	char *gparent_udi;
	int16_t unit;
	int i, fd;
	struct scsipi_inquiry_data inqbuf;
	struct scsipi_inquiry cmd;
	bool scsiinq_status;
	char *storage_model = NULL, *storage_vendor = NULL;

	if (drvctl_find_device (devnode, &dict) == FALSE || dict == NULL)
		return NULL;

	if (prop_dictionary_get_int16 (dict, "device-unit", &unit) == false ||
	    prop_dictionary_get_cstring_nocopy (dict, "device-driver", &driver) == false) {
		prop_object_release (dict);
		return NULL;
	}

	if (strcmp (driver, "wd") != 0 && strcmp (driver, "sd") != 0 &&
	    strcmp (driver, "ld") != 0) {
		prop_object_release (dict);
		return NULL;
	}

	sleep (1);

	devpath = g_strdup_printf ("/dev/%s%c", devnode, RAW_PART + 'a');
	rdevpath = g_strdup_printf ("/dev/r%s%c", devnode, RAW_PART + 'a');
	HAL_INFO (("  going to open %s", rdevpath));
	fd = open (rdevpath, O_RDONLY);
	if (fd < 0) {
		HAL_WARNING (("couldn't open %s: %s", rdevpath, strerror (errno)));
		g_free (rdevpath);
		g_free (devpath);
		return NULL;
	}

	HAL_INFO (("  going to DIOCGDINFO %s", rdevpath));
	if (ioctl (fd, DIOCGDINFO, &label) == -1) {
		HAL_WARNING (("DIOCGDINFO failed on %s: %s", rdevpath, strerror (errno)));
		g_free (rdevpath);
		g_free (devpath);
		close (fd);
		return NULL;
	}

	if (strcmp (driver, "sd") == 0) {
		memset(&cmd, 0, sizeof (cmd));
		memset(&inqbuf, 0, sizeof (inqbuf));
		cmd.opcode = INQUIRY;
		cmd.length = sizeof (inqbuf);

		scsiinq_status = scsi_command (fd, &cmd, sizeof (cmd), &inqbuf, sizeof (inqbuf), 10000, SCCMD_READ);
	} else
		scsiinq_status = false;

	close (fd);

	d = hal_device_new ();

	devinfo_set_default_properties (d, parent, devnode, devfs_path);

	hal_device_add_capability (d, "block");
	hal_device_property_set_string (d, "info.subsystem", "block");
	hal_device_property_set_string (d, "block.device", devpath);
	if (stat (devpath, &st) == 0) {
		hal_device_property_set_int (d, "block.major", major (st.st_rdev));
		hal_device_property_set_int (d, "block.minor", minor (st.st_rdev));
	}
	hal_device_property_set_bool (d, "block.is_volume", FALSE);
	hal_device_property_set_bool (d, "block.no_partitions", FALSE);
	hal_device_property_set_bool (d, "block.have_scanned", TRUE);

	hal_device_add_capability (d, "storage");
	hal_device_property_set_string (d, "info.category", "storage");
	parent_devnode = hal_device_property_get_string (parent, "netbsd.device");
	gparent_udi = hal_device_property_get_string (parent, "info.parent");
	if (gparent_udi) {
		gparent = hal_device_store_find (hald_get_gdl (), gparent_udi);
		if (gparent)
			gparent_devnode = hal_device_property_get_string (gparent, "netbsd.device");
	}
	if (gparent_devnode && strstr (gparent_devnode, "umass") == gparent_devnode)
		hal_device_property_set_string (d, "storage.bus", "usb");
	else if (parent_devnode && strstr (parent_devnode, "atabus") == parent_devnode)
		hal_device_property_set_string (d, "storage.bus", "ide");
	else
		hal_device_property_set_string (d, "storage.bus", "scsi");
	hal_device_property_set_string (d, "storage.device_type", "disk");
	hal_device_property_set_bool (d, "storage.removable", label.d_flags & D_REMOVABLE ? TRUE : FALSE);
	if (label.d_flags & D_REMOVABLE) {
		hal_device_property_set_bool (d, "storage.removable.media_available", TRUE);
		hal_device_property_set_uint64 (d, "storage.removable.media_size",
		    (uint64_t)label.d_secsize * (uint64_t)label.d_secperunit);
		hal_device_property_set_uint64 (d, "storage.size", 0);
		hal_device_property_set_bool (d, "storage.hotpluggable", TRUE);
		hal_device_property_set_bool (d, "storage.automount_enabled_hint", TRUE);
	} else {
		hal_device_property_set_bool (d, "storage.removable.media_available", FALSE);
		hal_device_property_set_uint64 (d, "storage.removable.media_size", 0);
		hal_device_property_set_uint64 (d, "storage.size",
		    (uint64_t)label.d_secsize * (uint64_t)label.d_secperunit);
		hal_device_property_set_bool (d, "storage.hotpluggable", FALSE);
		hal_device_property_set_bool (d, "storage.automount_enabled_hint", FALSE);
	}
	hal_device_property_set_bool (d, "storage.no_partitions_hint", FALSE);
	hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
	hal_device_property_set_bool (d, "storage.removable.support_async_notification", FALSE);
	hal_device_property_set_string (d, "storage.partitioning_scheme", "mbr");
	hal_device_property_set_string (d, "storage.originating_device",
	    hal_device_property_get_string (d, "info.udi"));

	if (scsiinq_status == true) {
		storage_model = rtrim_copy(inqbuf.product, sizeof (inqbuf.product));
		storage_vendor = rtrim_copy(inqbuf.vendor, sizeof (inqbuf.vendor));
	}

	if (storage_model) {
		hal_device_property_set_string (d, "storage.model", storage_model);
		free (storage_model);
	} else
		hal_device_property_set_string (d, "storage.model", label.d_packname);

	if (storage_vendor) {
		hal_device_property_set_string (d, "storage.vendor", storage_vendor);
		free (storage_vendor);
	} else
		hal_device_property_set_string (d, "storage.vendor", label.d_typename);

	devinfo_add_enqueue (d, devfs_path, &devinfo_mass_handler);

	for (i = 0; i < MAXPARTITIONS; i++) {
		const char *fstype;

		fstype = devinfo_mass_get_fstype(label.d_partitions[i].p_fstype);
		if (fstype == NULL)
			continue;

		childnode = g_strdup_printf ("%s%c", devnode, 'a' + i);
		HAL_INFO (("  adding %s on %s", childnode, rdevpath));
		devinfo_mass_disklabel_add (d, childnode, childnode, childnode);
		g_free (childnode);
	}

	HAL_INFO (("  returning"));
	g_free (rdevpath);
	g_free (devpath);

done:
	prop_object_release (dict);

	return d;
}