Exemplo n.º 1
0
static status_t
usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
{
	device_lun *lun = (device_lun *)cookie;
	disk_device *device = lun->device;
	mutex_lock(&device->lock);
	if (device->removed) {
		mutex_unlock(&device->lock);
		return B_DEV_NOT_READY;
	}

	status_t result = B_DEV_INVALID_IOCTL;
	switch (op) {
		case B_GET_MEDIA_STATUS: {
			*(status_t *)buffer = usb_disk_test_unit_ready(lun);
			TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer);
			result = B_OK;
			break;
		}

		case B_GET_GEOMETRY: {
			if (lun->media_changed) {
				result = usb_disk_update_capacity(lun);
				if (result != B_OK)
					break;
			}

			device_geometry *geometry = (device_geometry *)buffer;
			geometry->bytes_per_sector = lun->block_size;
			geometry->cylinder_count = lun->block_count;
			geometry->sectors_per_track = geometry->head_count = 1;
			geometry->device_type = lun->device_type;
			geometry->removable = lun->removable;
			geometry->read_only = lun->write_protected;
			geometry->write_once = (lun->device_type == B_WORM);
			TRACE("B_GET_GEOMETRY: %ld sectors at %ld bytes per sector\n",
				geometry->cylinder_count, geometry->bytes_per_sector);
			result = B_OK;
			break;
		}

		case B_FLUSH_DRIVE_CACHE:
			TRACE("B_FLUSH_DRIVE_CACHE\n");
			result = usb_disk_synchronize(lun, true);
			break;

		case B_EJECT_DEVICE:
			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 2,
				NULL, NULL, false);
			break;

		case B_LOAD_MEDIA:
			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 3,
				NULL, NULL, false);
			break;

#if HAIKU_TARGET_PLATFORM_HAIKU
		case B_GET_ICON:
			// We don't support this legacy ioctl anymore, but the two other
			// icon ioctls below instead.
			break;

		case B_GET_ICON_NAME:
			result = user_strlcpy((char *)buffer,
				"devices/drive-removable-media-usb", B_FILE_NAME_LENGTH);
			break;

		case B_GET_VECTOR_ICON:
		{
			if (length != sizeof(device_icon)) {
				result = B_BAD_VALUE;
				break;
			}

			device_icon iconData;
			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) {
				result = B_BAD_ADDRESS;
				break;
			}

			if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) {
				if (user_memcpy(iconData.icon_data, kDeviceIcon,
						sizeof(kDeviceIcon)) != B_OK) {
					result = B_BAD_ADDRESS;
					break;
				}
			}

			iconData.icon_size = sizeof(kDeviceIcon);
			result = user_memcpy(buffer, &iconData, sizeof(device_icon));
			break;
		}
#endif

		default:
			TRACE_ALWAYS("unhandled ioctl %ld\n", op);
			break;
	}

	mutex_unlock(&device->lock);
	return result;
}
Exemplo n.º 2
0
static status_t
usb_disk_device_added(usb_device newDevice, void **cookie)
{
	TRACE("device_added(0x%08lx)\n", newDevice);
	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
	device->device = newDevice;
	device->removed = false;
	device->open_count = 0;
	device->interface = 0xff;
	device->current_tag = 0;
	device->sync_support = SYNC_SUPPORT_RELOAD;
	device->tur_supported = true;
	device->luns = NULL;

	// scan through the interfaces to find our bulk-only data interface
	const usb_configuration_info *configuration = gUSBModule->get_configuration(newDevice);
	if (configuration == NULL) {
		free(device);
		return B_ERROR;
	}

	for (size_t i = 0; i < configuration->interface_count; i++) {
		usb_interface_info *interface = configuration->interface[i].active;
		if (interface == NULL)
			continue;

		if (interface->descr->interface_class == 0x08 /* mass storage */
			&& interface->descr->interface_subclass == 0x06 /* SCSI */
			&& interface->descr->interface_protocol == 0x50 /* bulk-only */) {

			bool hasIn = false;
			bool hasOut = false;
			for (size_t j = 0; j < interface->endpoint_count; j++) {
				usb_endpoint_info *endpoint = &interface->endpoint[j];
				if (endpoint == NULL
					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
					continue;

				if (!hasIn && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN)) {
					device->bulk_in = endpoint->handle;
					hasIn = true;
				} else if (!hasOut && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
					device->bulk_out = endpoint->handle;
					hasOut = true;
				}

				if (hasIn && hasOut)
					break;
			}

			if (!(hasIn && hasOut))
				continue;

			device->interface = interface->descr->interface_number;
			break;
		}
	}

	if (device->interface == 0xff) {
		TRACE_ALWAYS("no valid bulk-only interface found\n");
		free(device);
		return B_ERROR;
	}

	mutex_init(&device->lock, "usb_disk device lock");

	device->notify = create_sem(0, "usb_disk callback notify");
	if (device->notify < B_OK) {
		mutex_destroy(&device->lock);
		free(device);
		return device->notify;
	}

	device->lun_count = usb_disk_get_max_lun(device) + 1;
	device->luns = (device_lun **)malloc(device->lun_count
		* sizeof(device_lun *));
	for (uint8 i = 0; i < device->lun_count; i++)
		device->luns[i] = NULL;

	status_t result = B_OK;

	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
	for (uint8 i = 0; i < device->lun_count; i++) {
		// create the individual luns present on this device
		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
		if (lun == NULL) {
			result = B_NO_MEMORY;
			break;
		}

		device->luns[i] = lun;
		lun->device = device;
		lun->logical_unit_number = i;
		lun->should_sync = false;
		lun->media_present = true;
		lun->media_changed = true;
		usb_disk_reset_capacity(lun);

		// initialize this lun
		result = usb_disk_inquiry(lun);
		for (uint32 tries = 0; tries < 3; tries++) {
			status_t ready = usb_disk_test_unit_ready(lun);
			if (ready == B_OK || ready == B_DEV_NO_MEDIA) {
				if (ready == B_OK) {
					// TODO: check for write protection
					//if (usb_disk_mode_sense(lun) != B_OK)
						lun->write_protected = false;
				}

				break;
			}

			snooze(10000);
		}

		if (result != B_OK)
			break;
	}

	if (result != B_OK) {
		TRACE_ALWAYS("failed to initialize logical units\n");
		usb_disk_free_device_and_luns(device);
		return result;
	}

	mutex_lock(&gDeviceListLock);
	device->device_number = 0;
	disk_device *other = gDeviceList;
	while (other != NULL) {
		if (other->device_number >= device->device_number)
			device->device_number = other->device_number + 1;

		other = (disk_device *)other->link;
	}

	device->link = (void *)gDeviceList;
	gDeviceList = device;
	gLunCount += device->lun_count;
	for (uint8 i = 0; i < device->lun_count; i++)
		sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i);
	mutex_unlock(&gDeviceListLock);

	TRACE("new device: 0x%08lx\n", (uint32)device);
	*cookie = (void *)device;
	return B_OK;
}
Exemplo n.º 3
0
static status_t
usb_disk_device_added(usb_device newDevice, void **cookie)
{
	TRACE("device_added(0x%08lx)\n", newDevice);
	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
	device->device = newDevice;
	device->removed = false;
	device->open_count = 0;
	device->interface = 0xff;
	device->current_tag = 0;
	device->sync_support = SYNC_SUPPORT_RELOAD;
	device->is_atapi = false;
	device->luns = NULL;

	// scan through the interfaces to find our data interface
	const usb_configuration_info *configuration
		= gUSBModule->get_configuration(newDevice);
	if (configuration == NULL) {
		free(device);
		return B_ERROR;
	}

	for (size_t i = 0; i < configuration->interface_count; i++) {
		usb_interface_info *interface = configuration->interface[i].active;
		if (interface == NULL)
			continue;

		if (interface->descr->interface_class == 0x08 /* mass storage */
			&& interface->descr->interface_subclass == 0x04 /* UFI (floppy) */
			&& interface->descr->interface_protocol == 0x00) {

			bool hasIn = false;
			bool hasOut = false;
			bool hasInt = false;
			for (size_t j = 0; j < interface->endpoint_count; j++) {
				usb_endpoint_info *endpoint = &interface->endpoint[j];
				if (endpoint == NULL)
					continue;

				if (!hasIn && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN)
					&& endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
					device->bulk_in = endpoint->handle;
					hasIn = true;
				} else if (!hasOut && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN) == 0
					&& endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
					device->bulk_out = endpoint->handle;
					hasOut = true;
				} else if (!hasInt && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN)
					&& endpoint->descr->attributes
					== USB_ENDPOINT_ATTR_INTERRUPT) {
					// TODO:ensure there is only one interrupt endpoint and
					// stop enumerating
					device->interrupt = endpoint->handle;
					hasInt = true;
				}

				if (hasIn && hasOut && hasInt)
					break;
			}

			if (!(hasIn && hasOut && hasInt)) {
				TRACE("in: %d, out: %d, int: %d\n", hasIn, hasOut, hasInt);
				continue;
			}

			device->interface = interface->descr->interface_number;
			device->is_atapi = interface->descr->interface_subclass != 0x06;
			break;
		}
	}

	if (device->interface == 0xff) {
		TRACE_ALWAYS("no valid CBI interface found\n");
		free(device);
		return B_ERROR;
	}

	mutex_init(&device->lock, "usb_disk device lock");

	status_t result = device->notify =
		create_sem(0, "usb_disk callback notify");

	if (result < B_OK) {
		mutex_destroy(&device->lock);
		free(device);
		return result;
	}

	result = device->interruptLock = create_sem(0, "usb_disk interrupt lock");
	if (result < B_OK) {
		mutex_destroy(&device->lock);
		free(device);
		return result;
	}

	// TODO: handle more than 1 unit
	device->lun_count = 1;
	device->luns = (device_lun **)malloc(device->lun_count
		* sizeof(device_lun *));
	for (uint8 i = 0; i < device->lun_count; i++)
		device->luns[i] = NULL;

	result = B_OK;

	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
	for (uint8 i = 0; i < device->lun_count; i++) {
		// create the individual luns present on this device
		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
		if (lun == NULL) {
			result = B_NO_MEMORY;
			break;
		}

		device->luns[i] = lun;
		lun->device = device;
		lun->logical_unit_number = i;
		lun->should_sync = false;
		lun->media_present = true;
		lun->media_changed = true;
		usb_disk_reset_capacity(lun);

		// initialize this lun
		result = usb_disk_inquiry(lun);

		// Reset the device
		// If we don't do it all the other commands except inquiry and send
		// diagnostics will be stalled.
		result = usb_disk_send_diagnostic(lun);
		for (uint32 tries = 0; tries < 3; tries++) {
			status_t ready = usb_disk_test_unit_ready(lun);
			if (ready == B_OK || ready == B_DEV_NO_MEDIA) {
				if (ready == B_OK) {
					if (lun->device_type == B_CD)
						lun->write_protected = true;
					// TODO: check for write protection; disabled since
					// some devices lock up when getting the mode sense
					else if (/*usb_disk_mode_sense(lun) != B_OK*/true)
						lun->write_protected = false;
				}

				break;
			}

			snooze(10000);
		}

		if (result != B_OK)
			break;
	}

	if (result != B_OK) {
		TRACE_ALWAYS("failed to initialize logical units\n");
		usb_disk_free_device_and_luns(device);
		return result;
	}

	mutex_lock(&gDeviceListLock);
	device->device_number = 0;
	disk_device *other = gDeviceList;
	while (other != NULL) {
		if (other->device_number >= device->device_number)
			device->device_number = other->device_number + 1;

		other = (disk_device *)other->link;
	}

	device->link = (void *)gDeviceList;
	gDeviceList = device;
	gLunCount += device->lun_count;
	for (uint8 i = 0; i < device->lun_count; i++)
		sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i);
	mutex_unlock(&gDeviceListLock);

	TRACE("new device: 0x%08lx\n", (uint32)device);
	*cookie = (void *)device;
	return B_OK;
}
Exemplo n.º 4
0
static status_t
usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
{
	device_lun *lun = (device_lun *)cookie;
	disk_device *device = lun->device;
	mutex_lock(&device->lock);
	if (device->removed) {
		mutex_unlock(&device->lock);
		return B_DEV_NOT_READY;
	}

	status_t result = B_DEV_INVALID_IOCTL;
	switch (op) {
		case B_GET_DEVICE_SIZE: {
			if (lun->media_changed) {
				result = usb_disk_update_capacity(lun);
				if (result != B_OK)
					break;
			}

			size_t size = lun->block_size * lun->block_count;
			result = user_memcpy(buffer, &size, sizeof(size));

			break;
		}
		
		case B_GET_MEDIA_STATUS: {
			*(status_t *)buffer = usb_disk_test_unit_ready(lun);
			TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer);
			result = B_OK;
			break;
		}

		case B_GET_GEOMETRY: {
			if (lun->media_changed) {
				result = usb_disk_update_capacity(lun);
				if (result != B_OK)
					break;
			}

			device_geometry *geometry = (device_geometry *)buffer;
			geometry->bytes_per_sector = lun->block_size;
			geometry->cylinder_count = lun->block_count;
			geometry->sectors_per_track = geometry->head_count = 1;
			geometry->device_type = lun->device_type;
			geometry->removable = lun->removable;
			geometry->read_only = lun->write_protected;
			geometry->write_once = (lun->device_type == B_WORM);
			TRACE("B_GET_GEOMETRY: %ld sectors at %ld bytes per sector\n",
				geometry->cylinder_count, geometry->bytes_per_sector);
			result = B_OK;
			break;
		}

		case B_FLUSH_DRIVE_CACHE:
			TRACE("B_FLUSH_DRIVE_CACHE\n");
			result = usb_disk_synchronize(lun, true);
			break;

		case B_EJECT_DEVICE:
		{
			uint8 commandBlock[12];
			memset(commandBlock, 0, sizeof(commandBlock));

			commandBlock[0] = SCSI_START_STOP_UNIT_6;
			commandBlock[1] = lun->logical_unit_number << 5;
			commandBlock[4] = 2;

			result = usb_disk_operation(lun, commandBlock, NULL, NULL, false);
			break;
		}

		case B_LOAD_MEDIA:
		{
			uint8 commandBlock[12];
			memset(commandBlock, 0, sizeof(commandBlock));

			commandBlock[0] = SCSI_START_STOP_UNIT_6;
			commandBlock[1] = lun->logical_unit_number << 5;
			commandBlock[4] = 3;

			result = usb_disk_operation(lun, commandBlock, NULL, NULL, false);
			break;
		}

#if HAIKU_TARGET_PLATFORM_HAIKU
		case B_GET_ICON:
			// We don't support this legacy ioctl anymore, but the two other
			// icon ioctls below instead.
			break;

		case B_GET_ICON_NAME:
			result = user_strlcpy((char *)buffer,
				"devices/drive-removable-media-usb", B_FILE_NAME_LENGTH);
			break;

		case B_GET_VECTOR_ICON:
		{
			if (length != sizeof(device_icon)) {
				result = B_BAD_VALUE;
				break;
			}

			device_icon iconData;
			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) {
				result = B_BAD_ADDRESS;
				break;
			}

			if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) {
				if (user_memcpy(iconData.icon_data, kDeviceIcon,
						sizeof(kDeviceIcon)) != B_OK) {
					result = B_BAD_ADDRESS;
					break;
				}
			}

			iconData.icon_size = sizeof(kDeviceIcon);
			result = user_memcpy(buffer, &iconData, sizeof(device_icon));
			break;
		}

		case B_GET_DEVICE_NAME:
		{
			size_t nameLength = sizeof(lun->vendor_name)
				+ sizeof(lun->product_name) + sizeof(lun->product_revision) + 3;

			char name[nameLength];
			snprintf(name, nameLength, "%.8s %.16s %.4s", lun->vendor_name,
				lun->product_name, lun->product_revision);

			normalize_name(name, nameLength);

			result = user_strlcpy((char *)buffer, name, length);
			if (result > 0)
				result = B_OK;

			TRACE_ALWAYS("got device name: \"%s\" = %s\n", name, strerror(result));
			break;
		}
#endif

		default:
			TRACE_ALWAYS("unhandled ioctl %" B_PRIu32 "\n", op);
			break;
	}

	mutex_unlock(&device->lock);
	return result;
}
Exemplo n.º 5
0
static status_t
usb_disk_device_added(usb_device newDevice, void **cookie)
{
	TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice);
	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
	device->device = newDevice;
	device->removed = false;
	device->open_count = 0;
	device->interface = 0xff;
	device->current_tag = 0;
	device->sync_support = SYNC_SUPPORT_RELOAD;
	device->tur_supported = true;
	device->is_atapi = false;
	device->luns = NULL;

	// scan through the interfaces to find our bulk-only data interface
	const usb_configuration_info *configuration
		= gUSBModule->get_configuration(newDevice);
	if (configuration == NULL) {
		free(device);
		return B_ERROR;
	}

	for (size_t i = 0; i < configuration->interface_count; i++) {
		usb_interface_info *interface = configuration->interface[i].active;
		if (interface == NULL)
			continue;

		if (interface->descr->interface_class == 0x08 /* mass storage */
			&& (interface->descr->interface_subclass == 0x06 /* SCSI */
				|| interface->descr->interface_subclass == 0x02 /* ATAPI */
				|| interface->descr->interface_subclass == 0x05 /* ATAPI */)
			&& interface->descr->interface_protocol == 0x50 /* bulk-only */) {

			bool hasIn = false;
			bool hasOut = false;
			for (size_t j = 0; j < interface->endpoint_count; j++) {
				usb_endpoint_info *endpoint = &interface->endpoint[j];
				if (endpoint == NULL
					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
					continue;

				if (!hasIn && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN) != 0) {
					device->bulk_in = endpoint->handle;
					hasIn = true;
				} else if (!hasOut && (endpoint->descr->endpoint_address
					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
					device->bulk_out = endpoint->handle;
					hasOut = true;
				}

				if (hasIn && hasOut)
					break;
			}

			if (!(hasIn && hasOut))
				continue;

			device->interface = interface->descr->interface_number;
			device->is_atapi = interface->descr->interface_subclass != 0x06;
			break;
		}
	}

	if (device->interface == 0xff) {
		TRACE_ALWAYS("no valid bulk-only interface found\n");
		free(device);
		return B_ERROR;
	}

	mutex_init(&device->lock, "usb_disk device lock");

	device->notify = create_sem(0, "usb_disk callback notify");
	if (device->notify < B_OK) {
		mutex_destroy(&device->lock);
		status_t result = device->notify;
		free(device);
		return result;
	}

	device->lun_count = usb_disk_get_max_lun(device) + 1;
	device->luns = (device_lun **)malloc(device->lun_count
		* sizeof(device_lun *));
	for (uint8 i = 0; i < device->lun_count; i++)
		device->luns[i] = NULL;

	status_t result = B_OK;

	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
	for (uint8 i = 0; i < device->lun_count; i++) {
		// create the individual luns present on this device
		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
		if (lun == NULL) {
			result = B_NO_MEMORY;
			break;
		}

		device->luns[i] = lun;
		lun->device = device;
		lun->logical_unit_number = i;
		lun->should_sync = false;
		lun->media_present = true;
		lun->media_changed = true;

		memset(lun->vendor_name, 0, sizeof(lun->vendor_name));
		memset(lun->product_name, 0, sizeof(lun->product_name));
		memset(lun->product_revision, 0, sizeof(lun->product_revision));

		usb_disk_reset_capacity(lun);

		// initialize this lun
		result = usb_disk_inquiry(lun);
		err_act action = err_act_ok;
		for (uint32 tries = 0; tries < 8; tries++) {
			TRACE("usb lun %"B_PRIu8" inquiry attempt %"B_PRIu32" begin\n",
				i, tries);
			status_t ready = usb_disk_test_unit_ready(lun, &action);
			if (ready == B_OK || ready == B_DEV_NO_MEDIA
				|| ready == B_DEV_MEDIA_CHANGED) {
				if (lun->device_type == B_CD)
					lun->write_protected = true;
				// TODO: check for write protection; disabled since some
				// devices lock up when getting the mode sense
				else if (/*usb_disk_mode_sense(lun) != B_OK*/true)
					lun->write_protected = false;

				TRACE("usb lun %"B_PRIu8" ready. write protected = %c%s\n", i,
					lun->write_protected ? 'y' : 'n',
					ready == B_DEV_NO_MEDIA ? " (no media inserted)" : "");

				break;
			}
			TRACE("usb lun %"B_PRIu8" inquiry attempt %"B_PRIu32" failed\n",
				i, tries);
			if (action != err_act_retry && action != err_act_many_retries)
				break;
			bigtime_t snoozeTime = 1000000 * tries;
			TRACE("snoozing %"B_PRIu64" microseconds for usb lun\n",
				snoozeTime);
			snooze(snoozeTime);
		}

		if (result != B_OK)
			break;
	}

	if (result != B_OK) {
		TRACE_ALWAYS("failed to initialize logical units: %s\n",
			strerror(result));
		usb_disk_free_device_and_luns(device);
		return result;
	}

	mutex_lock(&gDeviceListLock);
	device->device_number = 0;
	disk_device *other = gDeviceList;
	while (other != NULL) {
		if (other->device_number >= device->device_number)
			device->device_number = other->device_number + 1;

		other = (disk_device *)other->link;
	}

	device->link = (void *)gDeviceList;
	gDeviceList = device;
	gLunCount += device->lun_count;
	for (uint8 i = 0; i < device->lun_count; i++)
		sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i);
	mutex_unlock(&gDeviceListLock);

	TRACE("new device: 0x%p\n", device);
	*cookie = (void *)device;
	return B_OK;
}
Exemplo n.º 6
0
static status_t
usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
{
	device_lun *lun = (device_lun *)cookie;
	disk_device *device = lun->device;
	mutex_lock(&device->lock);
	if (device->removed) {
		mutex_unlock(&device->lock);
		return B_DEV_NOT_READY;
	}

	status_t result = B_DEV_INVALID_IOCTL;
	switch (op) {
		case B_GET_MEDIA_STATUS:
		{
			err_act action = err_act_ok;
			for (uint32 tries = 0; tries < 3; tries++) {
				status_t ready = usb_disk_test_unit_ready(lun, &action);
				if (ready == B_OK || ready == B_DEV_NO_MEDIA
					|| (action != err_act_retry
						&& action != err_act_many_retries)) {
					*(status_t *)buffer = ready;
					break;
				}
				snooze(500000);
			}
			TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n",
				*(status_t *)buffer);
			result = B_OK;
			break;
		}

		case B_GET_GEOMETRY:
		{
			if (lun->media_changed) {
				result = usb_disk_update_capacity(lun);
				if (result != B_OK)
					break;
			}

			device_geometry geometry;
			devfs_compute_geometry_size(&geometry, lun->block_count,
				lun->block_size);

			geometry.device_type = lun->device_type;
			geometry.removable = lun->removable;
			geometry.read_only = lun->write_protected;
			geometry.write_once = lun->device_type == B_WORM;
			TRACE("B_GET_GEOMETRY: %" B_PRId32 " sectors at %" B_PRId32
				" bytes per sector\n", geometry.cylinder_count,
				geometry.bytes_per_sector);
			result = user_memcpy(buffer, &geometry, sizeof(device_geometry));
			break;
		}

		case B_FLUSH_DRIVE_CACHE:
			TRACE("B_FLUSH_DRIVE_CACHE\n");
			result = usb_disk_synchronize(lun, true);
			break;

		case B_EJECT_DEVICE:
			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 2,
				NULL, NULL, false);
			break;

		case B_LOAD_MEDIA:
			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 3,
				NULL, NULL, false);
			break;

#if HAIKU_TARGET_PLATFORM_HAIKU
		case B_GET_ICON:
			// We don't support this legacy ioctl anymore, but the two other
			// icon ioctls below instead.
			break;

		case B_GET_ICON_NAME:
			result = user_strlcpy((char *)buffer,
				"devices/drive-removable-media-usb", B_FILE_NAME_LENGTH);
			break;

		case B_GET_VECTOR_ICON:
		{
			if (length != sizeof(device_icon)) {
				result = B_BAD_VALUE;
				break;
			}

			device_icon iconData;
			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) {
				result = B_BAD_ADDRESS;
				break;
			}

			if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) {
				if (user_memcpy(iconData.icon_data, kDeviceIcon,
						sizeof(kDeviceIcon)) != B_OK) {
					result = B_BAD_ADDRESS;
					break;
				}
			}

			iconData.icon_size = sizeof(kDeviceIcon);
			result = user_memcpy(buffer, &iconData, sizeof(device_icon));
			break;
		}

		case B_GET_DEVICE_NAME:
		{
			size_t nameLength = sizeof(lun->vendor_name)
				+ sizeof(lun->product_name) + sizeof(lun->product_revision) + 3;

			char name[nameLength];
			snprintf(name, nameLength, "%.8s %.16s %.4s", lun->vendor_name,
				lun->product_name, lun->product_revision);

			normalize_name(name, nameLength);

			result = user_strlcpy((char *)buffer, name, length);
			if (result > 0)
				result = B_OK;

			TRACE_ALWAYS("got device name \"%s\": %s\n", name,
				strerror(result));
			break;
		}
#endif

		default:
			TRACE_ALWAYS("unhandled ioctl %" B_PRId32 "\n", op);
			break;
	}

	mutex_unlock(&device->lock);
	return result;
}