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; }
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; }
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; }
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; }
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; }
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; }