TinWoo/include/libusbhsfs/source/usbhsfs_drive.c
2022-04-26 01:22:43 +01:00

692 lines
28 KiB
C

/*
* usbhsfs_drive.c
*
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
* Copyright (c) 2020-2021, XorTroll.
*
* This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs).
*/
#include "usbhsfs_utils.h"
#include "usbhsfs_request.h"
#include "usbhsfs_scsi.h"
#include "usbhsfs_mount.h"
/* Function prototypes. */
static bool usbHsFsDriveSetupInterfaceAndEndpointDescriptors(UsbHsFsDriveContext *drive_ctx);
static bool usbHsFsDriveChangeInterfaceDescriptor(UsbHsClientIfSession *usb_if_session, struct usb_interface_descriptor *interface_desc);
static bool usbHsFsDriveSetupEndpointDescriptors(UsbHsFsDriveContext *drive_ctx, u8 *config_desc_start, u8 *config_desc_end, u8 **config_desc_ptr);
static bool usbHsFsDriveGetEndpointSession(UsbHsClientIfSession *usb_if_session, UsbHsClientEpSession *usb_ep_session, bool input, u8 ep_addr);
static void usbHsFsDriveGetDeviceStrings(UsbHsFsDriveContext *drive_ctx);
static void usbHsFsDriveGetUtf8StringFromStringDescriptor(UsbHsClientIfSession *usb_if_session, u8 idx, u16 lang_id, char **out_buf);
static void usbHsFsDriveDestroyLogicalUnitContext(UsbHsFsDriveLogicalUnitContext *lun_ctx, bool stop_lun);
bool usbHsFsDriveInitializeContext(UsbHsFsDriveContext *drive_ctx, UsbHsInterface *usb_if)
{
if (!drive_ctx || !usb_if)
{
USBHSFS_LOG_MSG("Invalid parameters!");
return false;
}
Result rc = 0;
UsbHsClientIfSession *usb_if_session = &(drive_ctx->usb_if_session);
UsbHsFsDriveLogicalUnitContext **tmp_lun_ctx = NULL;
bool ret = false;
/* Allocate memory for the USB transfer buffer. */
drive_ctx->xfer_buf = usbHsFsRequestAllocateXferBuffer();
if (!drive_ctx->xfer_buf)
{
USBHSFS_LOG_MSG("Failed to allocate USB transfer buffer! (interface %d).", usb_if->inf.ID);
goto end;
}
/* Open current interface. */
rc = usbHsAcquireUsbIf(usb_if_session, usb_if);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("usbHsAcquireUsbIf failed! (0x%08X) (interface %d).", rc, usb_if->inf.ID);
goto end;
}
/* Setup interface and endpoint descriptors. */
if (!usbHsFsDriveSetupInterfaceAndEndpointDescriptors(drive_ctx))
{
USBHSFS_LOG_MSG("Failed to setup interface and endpoint descriptors! (interface %d).", usb_if->inf.ID);
goto end;
}
/* Fill extra device data. */
drive_ctx->usb_if_id = usb_if_session->ID;
drive_ctx->uasp = (usb_if_session->inf.inf.interface_desc.bInterfaceProtocol == USB_PROTOCOL_USB_ATTACHED_SCSI);
drive_ctx->vid = usb_if_session->inf.device_desc.idVendor;
drive_ctx->pid = usb_if_session->inf.device_desc.idProduct;
usbHsFsDriveGetDeviceStrings(drive_ctx);
/* Retrieve max supported logical units from this storage device. */
rc = usbHsFsRequestGetMaxLogicalUnits(usb_if_session, &(drive_ctx->max_lun));
if (R_FAILED(rc))
{
/* Fallback to a single logical unit. */
drive_ctx->max_lun = 1;
/* If the request fails (e.g. unsupported by the device), we'll attempt to clear a possible STALL status from the endpoints. */
if (R_MODULE(rc) != Module_Libnx) usbHsFsDriveClearStallStatus(drive_ctx);
}
USBHSFS_LOG_MSG("Max LUN count (interface %d): %u.", drive_ctx->usb_if_id, drive_ctx->max_lun);
/* Bail out if we're dealing with a UASP interface (for now). */
if (drive_ctx->uasp) goto end;
/* Allocate memory for LUN context pointer array. */
drive_ctx->lun_ctx = calloc(drive_ctx->max_lun, sizeof(UsbHsFsDriveLogicalUnitContext*));
if (!drive_ctx->lun_ctx)
{
USBHSFS_LOG_MSG("Failed to allocate memory for LUN context pointer array! (interface %d).", drive_ctx->usb_if_id);
goto end;
}
/* Prepare LUNs using SCSI commands. */
for(u8 i = 0; i < drive_ctx->max_lun; i++)
{
/* Retrieve pointer to the current LUN context. */
UsbHsFsDriveLogicalUnitContext *lun_ctx = drive_ctx->lun_ctx[drive_ctx->lun_count];
if (!lun_ctx)
{
/* Allocate memory for a new LUN context. */
lun_ctx = calloc(1, sizeof(UsbHsFsDriveLogicalUnitContext));
if (!lun_ctx)
{
USBHSFS_LOG_MSG("Failed to allocate memory for LUN context entry #%u! (%u / %u).", drive_ctx->lun_count, i + 1, drive_ctx->max_lun);
goto end;
}
/* Set LUN context entry pointer. */
drive_ctx->lun_ctx[drive_ctx->lun_count] = lun_ctx;
}
/* Increase LUN context count. */
(drive_ctx->lun_count)++;
/* Set USB interface ID and LUN index. */
lun_ctx->drive_ctx = drive_ctx;
lun_ctx->usb_if_id = drive_ctx->usb_if_id;
lun_ctx->uasp = drive_ctx->uasp;
lun_ctx->lun = i;
/* Start LUN. */
if (!usbHsFsScsiStartDriveLogicalUnit(lun_ctx))
{
USBHSFS_LOG_MSG("Failed to initialize context for LUN #%u! (interface %d).", i, drive_ctx->usb_if_id);
(drive_ctx->lun_count)--; /* Decrease LUN context count. */
continue;
}
/* Initialize filesystem contexts for this LUN. */
if (!usbHsFsMountInitializeLogicalUnitFileSystemContexts(lun_ctx))
{
USBHSFS_LOG_MSG("Failed to initialize filesystem contexts for LUN #%u! (interface %d).", i, drive_ctx->usb_if_id);
usbHsFsDriveDestroyLogicalUnitContext(lun_ctx, true);
(drive_ctx->lun_count)--; /* Decrease LUN context count. */
}
}
if (!drive_ctx->lun_count)
{
if (drive_ctx->lun_ctx[0]) free(drive_ctx->lun_ctx[0]);
USBHSFS_LOG_MSG("Failed to initialize any LUN/filesystem contexts! (interface %d).", drive_ctx->usb_if_id);
goto end;
}
/* Reallocate LUN context pointer array if we're not using all allocated entries. */
if (drive_ctx->lun_count < drive_ctx->max_lun)
{
tmp_lun_ctx = realloc(drive_ctx->lun_ctx, drive_ctx->lun_count * sizeof(UsbHsFsDriveLogicalUnitContext*));
if (tmp_lun_ctx)
{
drive_ctx->lun_ctx = tmp_lun_ctx;
tmp_lun_ctx = NULL;
}
}
/* Update return value. */
ret = true;
end:
/* Destroy drive context if something went wrong. */
if (!ret) usbHsFsDriveDestroyContext(drive_ctx, true);
return ret;
}
void usbHsFsDriveDestroyContext(UsbHsFsDriveContext *drive_ctx, bool stop_lun)
{
if (!drive_ctx) return;
UsbHsClientIfSession *usb_if_session = &(drive_ctx->usb_if_session);
UsbHsClientEpSession *usb_in_ep_session_1 = &(drive_ctx->usb_in_ep_session[0]);
UsbHsClientEpSession *usb_out_ep_session_1 = &(drive_ctx->usb_out_ep_session[0]);
UsbHsClientEpSession *usb_in_ep_session_2 = &(drive_ctx->usb_in_ep_session[1]);
UsbHsClientEpSession *usb_out_ep_session_2 = &(drive_ctx->usb_out_ep_session[1]);
if (drive_ctx->lun_ctx)
{
/* Destroy LUN contexts. */
for(u8 i = 0; i < drive_ctx->lun_count; i++)
{
UsbHsFsDriveLogicalUnitContext *lun_ctx = drive_ctx->lun_ctx[i];
if (!lun_ctx) continue;
usbHsFsDriveDestroyLogicalUnitContext(lun_ctx, stop_lun);
free(lun_ctx);
}
/* Free LUN context pointer array. */
free(drive_ctx->lun_ctx);
drive_ctx->lun_ctx = NULL;
}
/* Free device strings. */
if (drive_ctx->manufacturer)
{
free(drive_ctx->manufacturer);
drive_ctx->manufacturer = NULL;
}
if (drive_ctx->product_name)
{
free(drive_ctx->product_name);
drive_ctx->product_name = NULL;
}
if (drive_ctx->serial_number)
{
free(drive_ctx->serial_number);
drive_ctx->serial_number = NULL;
}
/* Close USB interface and endpoint sessions. */
if (serviceIsActive(&(usb_out_ep_session_2->s))) usbHsEpClose(usb_out_ep_session_2);
if (serviceIsActive(&(usb_in_ep_session_2->s))) usbHsEpClose(usb_in_ep_session_2);
if (serviceIsActive(&(usb_out_ep_session_1->s))) usbHsEpClose(usb_out_ep_session_1);
if (serviceIsActive(&(usb_in_ep_session_1->s))) usbHsEpClose(usb_in_ep_session_1);
if (usbHsIfIsActive(usb_if_session)) usbHsIfClose(usb_if_session);
/* Free dedicated USB transfer buffer. */
if (drive_ctx->xfer_buf)
{
free(drive_ctx->xfer_buf);
drive_ctx->xfer_buf = NULL;
}
}
void usbHsFsDriveClearStallStatus(UsbHsFsDriveContext *drive_ctx)
{
if (!usbHsFsDriveIsValidContext(drive_ctx)) return;
usbHsFsRequestClearEndpointHaltFeature(&(drive_ctx->usb_if_session), &(drive_ctx->usb_in_ep_session[0]));
usbHsFsRequestClearEndpointHaltFeature(&(drive_ctx->usb_if_session), &(drive_ctx->usb_out_ep_session[0]));
if (drive_ctx->uasp)
{
usbHsFsRequestClearEndpointHaltFeature(&(drive_ctx->usb_if_session), &(drive_ctx->usb_in_ep_session[1]));
usbHsFsRequestClearEndpointHaltFeature(&(drive_ctx->usb_if_session), &(drive_ctx->usb_out_ep_session[1]));
}
}
static bool usbHsFsDriveSetupInterfaceAndEndpointDescriptors(UsbHsFsDriveContext *drive_ctx)
{
if (!drive_ctx || !usbHsIfIsActive(&(drive_ctx->usb_if_session))) return false;
Result rc = 0;
UsbHsClientIfSession *usb_if_session = &(drive_ctx->usb_if_session);
struct usb_interface_descriptor orig_interface_desc = {0}, *new_interface_desc = NULL;
u32 config_desc_size = 0;
u8 *config_desc_start = NULL, *config_desc_end = NULL, *config_desc_ptr = NULL;
bool success = false;
/* Copy interface descriptor provided by UsbHsInterface. */
/* We'll use this to skip this descriptor when we find it within the full configuration descriptor. */
/* Furthermore, we'll use it as a fallback method if something goes wrong while setting up USB Attached SCSI interface and endpoint descriptors. */
memcpy(&orig_interface_desc, &(usb_if_session->inf.inf.interface_desc), sizeof(struct usb_interface_descriptor));
/* Get full configuration descriptor. The one provided by UsbHsInterface is truncated. */
/* To simplify things, we won't go beyond index #0. */
rc = usbHsFsRequestGetConfigurationDescriptor(usb_if_session, 0, &config_desc_start, &config_desc_size);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("usbHsFsRequestGetConfigurationDescriptor failed! (0x%08X) (interface %d).", rc, usb_if_session->ID);
goto end;
}
/* Do not proceed if the configuration descriptor is too small to hold a USB Attached SCSI interface. */
if (config_desc_size < (sizeof(struct usb_config_descriptor) + sizeof(struct usb_interface_descriptor) + ((sizeof(struct usb_endpoint_descriptor) + sizeof(struct usb_pipe_usage_descriptor)) * 4)))
{
USBHSFS_LOG_MSG("Configuration descriptor is too small to hold a UASP interface (interface %d).", usb_if_session->ID);
goto end;
}
/* Update configuration descriptor pointers. */
config_desc_end = (config_desc_start + config_desc_size);
config_desc_ptr = config_desc_start;
/* Parse configuration descriptor. */
while(config_desc_ptr < config_desc_end)
{
u32 config_desc_offset = (u32)(config_desc_ptr - config_desc_start);
/* If somehow we're only a byte away from the end of the configuration descriptor, bail out. */
if ((config_desc_ptr + 1) >= config_desc_end) break;
/* Get descriptor size and type. */
u8 cur_desc_size = *config_desc_ptr;
u8 cur_desc_type = *(config_desc_ptr + 1);
/* Check descriptor size. */
if (!cur_desc_size)
{
USBHSFS_LOG_MSG("Size for descriptor 0x%02X at offset 0x%X is zero! (interface %d).", cur_desc_type, config_desc_offset, usb_if_session->ID);
goto end;
}
if ((config_desc_offset + cur_desc_size) > config_desc_size)
{
USBHSFS_LOG_MSG("Descriptor 0x%02X at offset 0x%X exceeds configuration descriptor size! (interface %d).", cur_desc_type, config_desc_offset, usb_if_session->ID);
goto end;
}
/* Check if we're dealing with a valid USB Attached SCSI interface descriptor. */
if (cur_desc_type != USB_DT_INTERFACE || cur_desc_size != sizeof(struct usb_interface_descriptor) || \
(orig_interface_desc.bInterfaceProtocol != USB_PROTOCOL_USB_ATTACHED_SCSI && !memcmp(config_desc_ptr, &orig_interface_desc, sizeof(struct usb_interface_descriptor))) || \
*(config_desc_ptr + 7) != USB_PROTOCOL_USB_ATTACHED_SCSI || *(config_desc_ptr + 4) != 4)
{
config_desc_ptr += cur_desc_size;
continue;
}
/* Found a USB Attached SCSI descriptor. */
new_interface_desc = (struct usb_interface_descriptor*)config_desc_ptr;
USBHSFS_LOG_DATA(new_interface_desc, sizeof(struct usb_interface_descriptor), "Found UASP interface descriptor at offset 0x%X (interface %d):", config_desc_offset, usb_if_session->ID);
/* Update configuration descriptor pointer. */
config_desc_ptr += cur_desc_size;
/* Switch to the new interface descriptor. */
if (!usbHsFsDriveChangeInterfaceDescriptor(usb_if_session, new_interface_desc)) continue;
/* Setup endpoint descriptors. */
success = usbHsFsDriveSetupEndpointDescriptors(drive_ctx, config_desc_start, config_desc_end, &config_desc_ptr);
if (success) break;
}
if (!success) USBHSFS_LOG_MSG("Unable to find and/or set a UASP interface descriptor (interface %d).", usb_if_session->ID);
end:
if (config_desc_start) free(config_desc_start);
/* Fallback to the Bulk-Only Transport interface if we must. */
if (!success && orig_interface_desc.bInterfaceProtocol == USB_PROTOCOL_BULK_ONLY_TRANSPORT && usbHsFsDriveChangeInterfaceDescriptor(usb_if_session, &orig_interface_desc))
{
USBHSFS_LOG_MSG("Proceeding with BOT interface descriptor (interface %d).", usb_if_session->ID);
success = usbHsFsDriveSetupEndpointDescriptors(drive_ctx, NULL, NULL, NULL);
}
return success;
}
static bool usbHsFsDriveChangeInterfaceDescriptor(UsbHsClientIfSession *usb_if_session, struct usb_interface_descriptor *interface_desc)
{
if (!usb_if_session || !usbHsIfIsActive(usb_if_session) || !interface_desc) return false;
Result rc = 0;
u8 cur_if_num = usb_if_session->inf.inf.interface_desc.bInterfaceNumber;
u8 cur_if_alt_setting = usb_if_session->inf.inf.interface_desc.bAlternateSetting;
u8 new_if_num = interface_desc->bInterfaceNumber;
u8 new_if_alt_setting = interface_desc->bAlternateSetting;
/* Check if we're trying to set the same interface. */
if (new_if_num == cur_if_num && new_if_alt_setting == cur_if_alt_setting)
{
USBHSFS_LOG_MSG("Trying to set an interface descriptor that matches the current one (interface %d).", usb_if_session->ID);
return true;
}
/* Check if we must set a new interface. */
if (new_if_num != cur_if_num)
{
rc = usbHsIfSetInterface(usb_if_session, NULL, new_if_num);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("usbHsIfSetInterface failed! (0x%08X) (interface %d).", rc, usb_if_session->ID);
return false;
}
}
/* Check if we must set an alternate interface setting. */
if (new_if_alt_setting != cur_if_alt_setting)
{
rc = usbHsIfGetAlternateInterface(usb_if_session, NULL, new_if_alt_setting);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("usbHsIfGetAlternateInterface failed! (0x%08X) (interface %d).", rc, usb_if_session->ID);
return false;
}
}
USBHSFS_LOG_DATA(&(usb_if_session->inf), sizeof(UsbHsInterface), "New interface data (interface %d):", usb_if_session->ID);
/*rc = usbHsFsRequestSetInterface(usb_if_session);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("usbHsFsRequestSetInterface failed! (0x%08X) (interface %d).", rc, usb_if_session->ID);
return false;
}*/
return true;
}
static bool usbHsFsDriveSetupEndpointDescriptors(UsbHsFsDriveContext *drive_ctx, u8 *config_desc_start, u8 *config_desc_end, u8 **config_desc_ptr)
{
UsbHsClientIfSession *usb_if_session = NULL;
UsbHsClientEpSession *usb_in_ep_session_1 = NULL, *usb_out_ep_session_1 = NULL, *usb_in_ep_session_2 = NULL, *usb_out_ep_session_2 = NULL;
u8 *config_desc_ptr_tmp = NULL;
u32 config_desc_size = 0;
struct usb_endpoint_descriptor *ep_desc = NULL;
struct usb_pipe_usage_descriptor *pipe_usage_desc = NULL;
bool success = false, uasp = false;
if (!drive_ctx || !usbHsIfIsActive(&(drive_ctx->usb_if_session)) || ((uasp = (drive_ctx->usb_if_session.inf.inf.interface_desc.bInterfaceProtocol == USB_PROTOCOL_USB_ATTACHED_SCSI)) && \
(!config_desc_start || !config_desc_end || !config_desc_ptr || !*config_desc_ptr || config_desc_end <= config_desc_start || *config_desc_ptr >= config_desc_end))) return false;
usb_if_session = &(drive_ctx->usb_if_session);
usb_in_ep_session_1 = &(drive_ctx->usb_in_ep_session[0]);
usb_out_ep_session_1 = &(drive_ctx->usb_out_ep_session[0]);
usb_in_ep_session_2 = &(drive_ctx->usb_in_ep_session[1]);
usb_out_ep_session_2 = &(drive_ctx->usb_out_ep_session[1]);
if (!uasp)
{
/* If we're dealing with a Bulk-Only Transport interface, just setup the endpoints from the interface session right away. */
success = (usbHsFsDriveGetEndpointSession(usb_if_session, usb_in_ep_session_1, true, 0) && usbHsFsDriveGetEndpointSession(usb_if_session, usb_out_ep_session_1, false, 0));
goto end;
}
/* Parse endpoint and pipe usage descriptors from the configuration descriptor. */
config_desc_ptr_tmp = *config_desc_ptr;
config_desc_size = (u32)(config_desc_end - config_desc_start);
while(config_desc_ptr_tmp < config_desc_end)
{
u32 config_desc_offset = (u32)(config_desc_ptr_tmp - config_desc_start);
/* If somehow we're only a byte away from the end of the configuration descriptor, bail out. */
if ((config_desc_ptr_tmp + 1) >= config_desc_end) break;
/* Get descriptor size and type. */
u8 cur_desc_size = *config_desc_ptr_tmp;
u8 cur_desc_type = *(config_desc_ptr_tmp + 1);
/* Check descriptor size. */
if (!cur_desc_size)
{
USBHSFS_LOG_MSG("Size for descriptor 0x%02X at offset 0x%X is zero! (interface %d).", cur_desc_type, config_desc_offset, usb_if_session->ID);
goto end;
}
if ((config_desc_offset + cur_desc_size) > config_desc_size)
{
USBHSFS_LOG_MSG("Descriptor 0x%02X at offset 0x%X exceeds configuration descriptor size! (interface %d).", cur_desc_type, config_desc_offset, usb_if_session->ID);
goto end;
}
/* Check if we're dealing with an endpoint descriptor or a pipe usage descriptor. */
if ((!ep_desc && (cur_desc_type != USB_DT_ENDPOINT || cur_desc_size != sizeof(struct usb_endpoint_descriptor))) || (ep_desc && (cur_desc_type != USB_DT_PIPE_USAGE || \
cur_desc_size != sizeof(struct usb_pipe_usage_descriptor))))
{
config_desc_ptr_tmp += cur_desc_size;
continue;
}
if (cur_desc_type == USB_DT_ENDPOINT)
{
/* Found an endpoint descriptor. */
/* Update our current endpoint pointer, then look for its pipe usage descriptor. */
ep_desc = (struct usb_endpoint_descriptor*)config_desc_ptr_tmp;
USBHSFS_LOG_DATA(ep_desc, sizeof(struct usb_endpoint_descriptor), "Found endpoint descriptor at offset 0x%X (interface %d):", config_desc_offset, usb_if_session->ID);
config_desc_ptr_tmp += cur_desc_size;
continue;
}
/* Found a pipe usage descriptor. */
pipe_usage_desc = (struct usb_pipe_usage_descriptor*)config_desc_ptr_tmp;
USBHSFS_LOG_DATA(pipe_usage_desc, sizeof(struct usb_pipe_usage_descriptor), "Found pipe usage descriptor at offset 0x%X (interface %d):", config_desc_offset, usb_if_session->ID);
/* Update configuration descriptor pointer. */
config_desc_ptr_tmp += cur_desc_size;
/* Check if the pipe ID matches the endpoint direction. */
u8 ep_addr = ep_desc->bEndpointAddress, pipe_id = pipe_usage_desc->bPipeID;
bool input = ((ep_addr & USB_ENDPOINT_IN) != 0);
if ((input && pipe_id != USB_PIPE_USAGE_ID_STS && pipe_id != USB_PIPE_USAGE_ID_DATA_IN) || (!input && pipe_id != USB_PIPE_USAGE_ID_CMD && pipe_id != USB_PIPE_USAGE_ID_DATA_OUT))
{
USBHSFS_LOG_MSG("Pipe ID 0x%02X doesn't match direction from endpoint 0x%02X! (interface %d).", pipe_id, ep_addr, usb_if_session->ID);
goto end;
}
/* Finally, setup this endpoint. */
UsbHsClientEpSession *usb_ep_session = (pipe_id == USB_PIPE_USAGE_ID_CMD ? usb_out_ep_session_1 : (pipe_id == USB_PIPE_USAGE_ID_STS ? usb_in_ep_session_1 : \
(pipe_id == USB_PIPE_USAGE_ID_DATA_IN ? usb_in_ep_session_2 : usb_out_ep_session_2)));
if (!usbHsFsDriveGetEndpointSession(usb_if_session, usb_ep_session, input, ep_addr))
{
USBHSFS_LOG_MSG("Failed to retrieve endpoint session! (interface %d, endpoint 0x%02X, pipe ID 0x%02X).", usb_if_session->ID, ep_addr, pipe_id);
goto end;
}
/* Clear endpoint and pipe usage descriptor pointers. */
ep_desc = NULL;
pipe_usage_desc = NULL;
/* Check if we're done here. */
if (serviceIsActive(&(usb_in_ep_session_1->s)) && serviceIsActive(&(usb_out_ep_session_1->s)) && serviceIsActive(&(usb_in_ep_session_2->s)) && serviceIsActive(&(usb_out_ep_session_2->s)))
{
success = true;
break;
}
}
end:
/* Close opened endpoints if something went wrong. */
if (!success)
{
if (serviceIsActive(&(usb_out_ep_session_2->s))) usbHsEpClose(usb_out_ep_session_2);
if (serviceIsActive(&(usb_in_ep_session_2->s))) usbHsEpClose(usb_in_ep_session_2);
if (serviceIsActive(&(usb_out_ep_session_1->s))) usbHsEpClose(usb_out_ep_session_1);
if (serviceIsActive(&(usb_in_ep_session_1->s))) usbHsEpClose(usb_in_ep_session_1);
}
/* Update configuration descriptor pointer (if needed). */
if (config_desc_ptr_tmp) *config_desc_ptr = config_desc_ptr_tmp;
return success;
}
static bool usbHsFsDriveGetEndpointSession(UsbHsClientIfSession *usb_if_session, UsbHsClientEpSession *usb_ep_session, bool input, u8 ep_addr)
{
if (!usb_if_session || !usbHsIfIsActive(usb_if_session) || !usb_ep_session) return false;
for(u8 i = 0; i < 15; i++)
{
struct usb_endpoint_descriptor *ep_desc = (input ? &(usb_if_session->inf.inf.input_endpoint_descs[i]) : &(usb_if_session->inf.inf.output_endpoint_descs[i]));
u8 max_burst = (input ? usb_if_session->inf.inf.input_ss_endpoint_companion_descs[i].bMaxBurst : usb_if_session->inf.inf.output_ss_endpoint_companion_descs[i].bMaxBurst);
max_burst++;
if (ep_desc->bLength && ((!ep_addr && ((input && (ep_desc->bEndpointAddress & USB_ENDPOINT_IN)) || (!input && !(ep_desc->bEndpointAddress & USB_ENDPOINT_IN)))) || \
(ep_addr && ep_desc->bEndpointAddress == ep_addr)) && (ep_desc->bmAttributes & 0x3F) == USB_TRANSFER_TYPE_BULK)
{
Result rc = usbHsIfOpenUsbEp(usb_if_session, usb_ep_session, 1, ep_desc->wMaxPacketSize, ep_desc);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("usbHsIfOpenUsbEp failed! (0x%08X) (interface %d, endpoint 0x%02X, %u URB(s)).", rc, usb_if_session->ID, ep_desc->bEndpointAddress, max_burst);
break;
}
return true;
}
}
return false;
}
static void usbHsFsDriveGetDeviceStrings(UsbHsFsDriveContext *drive_ctx)
{
if (!drive_ctx || !usbHsIfIsActive(&(drive_ctx->usb_if_session))) return;
Result rc = 0;
UsbHsClientIfSession *usb_if_session = &(drive_ctx->usb_if_session);
u16 *lang_ids = NULL, cur_lang_id = 0, sel_lang_id = 0;
u32 lang_ids_count = 0;
/* Set default string language ID to English (US). */
sel_lang_id = USB_LANGID_ENUS;
/* Retrieve string descriptor indexes. Bail out if none of them are valid. */
u8 manufacturer = usb_if_session->inf.device_desc.iManufacturer;
u8 product_name = usb_if_session->inf.device_desc.iProduct;
u8 serial_number = usb_if_session->inf.device_desc.iSerialNumber;
if (!manufacturer && !product_name && !serial_number) return;
/* Get supported language IDs. */
rc = usbHsFsRequestGetStringDescriptor(usb_if_session, 0, 0, &lang_ids, &lang_ids_count);
if (R_FAILED(rc))
{
USBHSFS_LOG_MSG("Unable to retrieve supported language IDs! (0x%08X) (interface %d).", rc, usb_if_session->ID);
return;
}
/* Check if English (US) is supported. Otherwise, just default to the last valid language ID. */
lang_ids_count /= sizeof(u16);
for(u32 i = 0; i < lang_ids_count; i++)
{
if (!(cur_lang_id = lang_ids[i])) continue;
if (cur_lang_id == sel_lang_id) break;
if ((i + 1) == lang_ids_count)
{
sel_lang_id = cur_lang_id;
break;
}
}
free(lang_ids);
/* Retrieve string descriptors. */
usbHsFsDriveGetUtf8StringFromStringDescriptor(usb_if_session, manufacturer, sel_lang_id, &(drive_ctx->manufacturer));
usbHsFsDriveGetUtf8StringFromStringDescriptor(usb_if_session, product_name, sel_lang_id, &(drive_ctx->product_name));
usbHsFsDriveGetUtf8StringFromStringDescriptor(usb_if_session, serial_number, sel_lang_id, &(drive_ctx->serial_number));
}
static void usbHsFsDriveGetUtf8StringFromStringDescriptor(UsbHsClientIfSession *usb_if_session, u8 idx, u16 lang_id, char **out_buf)
{
if (!usb_if_session || !usbHsIfIsActive(usb_if_session) || !idx || !lang_id || !out_buf) return;
Result rc = 0;
u16 *string_data = NULL;
u32 string_data_size = 0;
ssize_t units = 0;
char *utf8_str = NULL;
/* Get string descriptor. */
rc = usbHsFsRequestGetStringDescriptor(usb_if_session, idx, lang_id, &string_data, &string_data_size);
if (R_FAILED(rc)) goto end;
/* Get UTF-8 string size. */
units = utf16_to_utf8(NULL, string_data, 0);
if (units <= 0)
{
USBHSFS_LOG_MSG("Failed to get UTF-8 string size for string descriptor! (interface %d, language ID 0x%04X, index %u).", usb_if_session->ID, lang_id, idx);
goto end;
}
/* Allocate memory for the UTF-8 string. */
utf8_str = calloc(units + 1, sizeof(char));
if (!utf8_str)
{
USBHSFS_LOG_MSG("Failed to allocate 0x%lX byte-long UTF-8 buffer for string descriptor! (interface %d, language ID 0x%04X, index %u).", units + 1, usb_if_session->ID, lang_id, idx);
goto end;
}
/* Perform UTF-16 to UTF-8 conversion. */
units = utf16_to_utf8((u8*)utf8_str, string_data, (size_t)units);
if (units <= 0)
{
USBHSFS_LOG_MSG("UTF-16 to UTF-8 conversion failed for string descriptor! (interface %d, language ID 0x%04X, index %u).", usb_if_session->ID, lang_id, idx);
goto end;
}
USBHSFS_LOG_MSG("Converted string (interface %d, language ID 0x%04X, index %u): \"%s\".", usb_if_session->ID, lang_id, idx, utf8_str);
/* Update output. */
*out_buf = utf8_str;
end:
if ((R_FAILED(rc) || units <= 0) && utf8_str) free(utf8_str);
if (string_data) free(string_data);
}
static void usbHsFsDriveDestroyLogicalUnitContext(UsbHsFsDriveLogicalUnitContext *lun_ctx, bool stop_lun)
{
if (!lun_ctx || !usbHsFsDriveIsValidContext((UsbHsFsDriveContext*)lun_ctx->drive_ctx) || lun_ctx->lun >= UMS_MAX_LUN) return;
if (lun_ctx->fs_ctx)
{
/* Destroy filesystem contexts. */
for(u32 i = 0; i < lun_ctx->fs_count; i++)
{
UsbHsFsDriveLogicalUnitFileSystemContext *fs_ctx = lun_ctx->fs_ctx[i];
if (!fs_ctx) continue;
usbHsFsMountDestroyLogicalUnitFileSystemContext(fs_ctx);
free(fs_ctx);
}
/* Free filesystem context pointer array. */
free(lun_ctx->fs_ctx);
lun_ctx->fs_ctx = NULL;
}
/* Stop current LUN. */
if (stop_lun) usbHsFsScsiStopDriveLogicalUnit(lun_ctx);
}