mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 04:22:50 +02:00
usbhs: Added the remaining cmds and expose more functionality, etc.
This commit is contained in:
parent
6ee5d8d148
commit
e768ac74f9
@ -9,6 +9,7 @@
|
||||
#include "../sf/service.h"
|
||||
#include "../services/usb.h"
|
||||
#include "../kernel/event.h"
|
||||
#include "../kernel/tmem.h"
|
||||
|
||||
typedef enum {
|
||||
///< These use \ref usb_device_descriptor. Bit2..6 require [6.0.0+], these are ignored on eariler versions.
|
||||
@ -84,6 +85,11 @@ typedef struct {
|
||||
u64 unk_x10;
|
||||
} UsbHsXferReport;
|
||||
|
||||
typedef struct {
|
||||
vu64 write_index;
|
||||
vu64 read_index;
|
||||
} UsbHsRingHeader;
|
||||
|
||||
/// The interface service object. These Events have autoclear=false.
|
||||
typedef struct {
|
||||
Service s;
|
||||
@ -97,6 +103,10 @@ typedef struct {
|
||||
typedef struct {
|
||||
Service s;
|
||||
Event eventXfer; ///< [2.0.0+] Signaled when PostBufferAsync finishes.
|
||||
TransferMemory tmem;
|
||||
u32 maxUrbCount;
|
||||
u64 max_reports;
|
||||
bool tmem_initialized;
|
||||
|
||||
struct usb_endpoint_descriptor desc;
|
||||
} UsbHsClientEpSession;
|
||||
@ -226,6 +236,67 @@ Result usbHsIfResetDevice(UsbHsClientIfSession* s);
|
||||
/// Closes the specified endpoint session.
|
||||
void usbHsEpClose(UsbHsClientEpSession* s);
|
||||
|
||||
/// Uses a data transfer with the specified endpoint, this will block until the transfer finishes. The buffer address and size should be aligned to 0x1000-bytes, where the input size is the original size.
|
||||
/// Gets the Xfer Event which is signaled when PostBufferAsync finishes. This is only valid for [2.0.0+]. If using \ref eventWait with this, then \ref eventClear should be used if the event was signaled (since the autoclear is false).
|
||||
static inline Event* usbHsEpGetXferEvent(UsbHsClientEpSession* s) {
|
||||
return &s->eventXfer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts an async data transfer with the specified endpoint. The Event from \ref usbHsEpGetXferEvent can be used to determine when the transfer finished. If you don't need async, \ref usbHsEpPostBuffer can be used instead.
|
||||
* @note Only available on [2.0.0+].
|
||||
* @param[in] s The endpoint object.
|
||||
* @param buffer Data buffer. The buffer address and size should be aligned to 0x1000-bytes.
|
||||
* @param[in] size The actual data size.
|
||||
* @param[out] xferId Output xferId.
|
||||
*/
|
||||
Result usbHsEpPostBufferAsync(UsbHsClientEpSession* s, void* buffer, u32 size, u32* xferId);
|
||||
|
||||
/**
|
||||
* @brief Gets an array of \ref UsbHsXferReport for the specified endpoint. This should be used after waiting on the Event from \ref usbHsEpGetXferEvent.
|
||||
* @note Only available on [2.0.0+].
|
||||
* @param[in] s The endpoint object.
|
||||
* @param[out] reports Output array of \ref UsbHsXferReport.
|
||||
* @param[in] max_reports Size of the reports array in entries.
|
||||
* @param[out] count Number of entries written to the array.
|
||||
*/
|
||||
Result usbHsEpGetXferReport(UsbHsClientEpSession* s, UsbHsXferReport* reports, u32 max_reports, u32* count);
|
||||
|
||||
/**
|
||||
* @brief Uses a data transfer with the specified endpoint, this will block until the transfer finishes. This wraps \ref usbHsEpPostBufferAsync and \ref usbHsEpGetXferReport, and also handles the Event (on pre-2.0.0 this handles using the relevant cmds instead). If async is needed, use \ref usbHsEpPostBufferAsync instead.
|
||||
* @param[in] s The endpoint object.
|
||||
* @param buffer Data buffer. The buffer address and size should be aligned to 0x1000-bytes.
|
||||
* @param[in] size The actual data size.
|
||||
* @param[out] transferredSize Output transferred size.
|
||||
*/
|
||||
Result usbHsEpPostBuffer(UsbHsClientEpSession* s, void* buffer, u32 size, u32* transferredSize);
|
||||
|
||||
/**
|
||||
* @brief This uses the same functionality internally as \ref usbHsEpPostBufferAsync except the urbs array and unk1/unk2 are specified by the user instead.
|
||||
* @note Only available on [2.0.0+].
|
||||
* @param[in] s The endpoint object.
|
||||
* @param buffer Data buffer. The buffer address and size should be aligned to 0x1000-bytes.
|
||||
* @param[in] urbs Input array of u32s for the size of each urb.
|
||||
* @param[in] urbCount Total entries in the urbs array.
|
||||
* @param[in] unk1 \ref usbHsEpPostBufferAsync would internally pass value 0 here.
|
||||
* @param[in] unk2 \ref usbHsEpPostBufferAsync would internally pass value 0 here.
|
||||
* @param[out] xferId Output xferId.
|
||||
*/
|
||||
Result usbHsEpBatchBufferAsync(UsbHsClientEpSession* s, void* buffer, u32* urbs, u32 urbCount, u32 unk1, u32 unk2, u32* xferId);
|
||||
|
||||
/**
|
||||
* @brief This can be used to map the specified buffer as devicemem, which can then be used with \ref usbHsEpPostBufferAsync / \ref usbHsEpPostBuffer / \ref usbHsEpBatchBufferAsync. If the buffer address passed to those funcs is within this SmmuSpace, the specified buffer must be within the bounds of the SmmuSpace buffer.
|
||||
* @note Only available on [4.0.0+].
|
||||
* @note A buffer from usbHsEpCreateSmmuSpace can't be reused by another endpoint with the aforementioned funcs.
|
||||
* @note This can only be used once per UsbHsClientEpSession object.
|
||||
* @param[in] s The endpoint object.
|
||||
* @param buffer Buffer address, this must be aligned to 0x1000-bytes.
|
||||
* @param[in] size Buffer size, this must be aligned to 0x1000-bytes.
|
||||
*/
|
||||
Result usbHsEpCreateSmmuSpace(UsbHsClientEpSession* s, void* buffer, u32 size);
|
||||
|
||||
/**
|
||||
* @brief This creates TransferMemory which is used to read \ref UsbHsXferReport when \ref usbHsEpGetXferReport is used, instead of using the service cmd.
|
||||
* @note Only available on [4.0.0+].
|
||||
*/
|
||||
Result usbHsEpShareReportRing(UsbHsClientEpSession* s);
|
||||
|
||||
|
@ -354,8 +354,10 @@ Result usbHsIfOpenUsbEp(UsbHsClientIfSession* s, UsbHsClientEpSession* ep, u16 m
|
||||
if (R_SUCCEEDED(rc)) rc = _usbHsGetEvent(&ep->s, &ep->eventXfer, false, 2);
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) ep->maxUrbCount = maxUrbCount;
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
serviceAssumeDomain(&s->s);
|
||||
serviceAssumeDomain(&ep->s);
|
||||
serviceClose(&ep->s);
|
||||
eventClose(&ep->eventXfer);
|
||||
}
|
||||
@ -375,6 +377,7 @@ void usbHsEpClose(UsbHsClientEpSession* s) {
|
||||
serviceAssumeDomain(&s->s);
|
||||
serviceClose(&s->s);
|
||||
eventClose(&s->eventXfer);
|
||||
tmemClose(&s->tmem);
|
||||
memset(s, 0, sizeof(UsbHsClientEpSession));
|
||||
}
|
||||
|
||||
@ -398,24 +401,57 @@ static Result _usbHsEpSubmitRequest(UsbHsClientEpSession* s, void* buffer, u32 s
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _usbHsEpPostBufferAsync(UsbHsClientEpSession* s, void* buffer, u32 size, u64 unk, u32* xferId) {
|
||||
Result usbHsEpPostBufferAsync(UsbHsClientEpSession* s, void* buffer, u32 size, u32* xferId) {
|
||||
if (hosversionBefore(2,0,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
|
||||
const struct {
|
||||
u32 size;
|
||||
u32 pad;
|
||||
u64 buffer;
|
||||
u64 unk;
|
||||
} in = { size, 0, (u64)buffer, unk };
|
||||
} in = { size, 0, (u64)buffer, 0 };
|
||||
|
||||
serviceAssumeDomain(&s->s);
|
||||
return serviceDispatchInOut(&s->s, 4, in, *xferId);
|
||||
}
|
||||
|
||||
static Result _usbHsEpGetXferReport(UsbHsClientEpSession* s, UsbHsXferReport* reports, u32 max_reports, u32* count) {
|
||||
serviceAssumeDomain(&s->s);
|
||||
return serviceDispatchInOut(&s->s, 5, max_reports, *count,
|
||||
Result usbHsEpGetXferReport(UsbHsClientEpSession* s, UsbHsXferReport* reports, u32 max_reports, u32* count) {
|
||||
if (hosversionBefore(2,0,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
|
||||
if (!s->tmem_initialized) {
|
||||
serviceAssumeDomain(&s->s);
|
||||
return serviceDispatchInOut(&s->s, 5, max_reports, *count,
|
||||
.buffer_attrs = { (hosversionBefore(3,0,0) ? SfBufferAttr_HipcMapAlias : SfBufferAttr_HipcAutoSelect) | SfBufferAttr_Out },
|
||||
.buffers = { { reports, max_reports*sizeof(UsbHsXferReport) } },
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
u32 total=0;
|
||||
|
||||
UsbHsRingHeader *hdr = (UsbHsRingHeader*)s->tmem.src_addr;
|
||||
UsbHsXferReport *ring_reports = (UsbHsXferReport*)(hdr+1);
|
||||
|
||||
memset(reports, 0, max_reports*sizeof(UsbHsXferReport));
|
||||
|
||||
for (u32 i=0; i<max_reports; i++) {
|
||||
u64 write_index = hdr->write_index;
|
||||
u64 read_index = hdr->read_index;
|
||||
|
||||
if (write_index == read_index) break;
|
||||
|
||||
if (read_index >= s->max_reports) return MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen); // Official sw would Abort here.
|
||||
reports[i] = ring_reports[read_index];
|
||||
__asm__ __volatile__ ("dmb sy" ::: "memory");
|
||||
read_index = hdr->read_index;
|
||||
hdr->read_index = (read_index+1) % s->max_reports;
|
||||
total++;
|
||||
}
|
||||
|
||||
*count = total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result usbHsEpPostBuffer(UsbHsClientEpSession* s, void* buffer, u32 size, u32* transferredSize) {
|
||||
@ -426,7 +462,7 @@ Result usbHsEpPostBuffer(UsbHsClientEpSession* s, void* buffer, u32 size, u32* t
|
||||
|
||||
if (hosversionBefore(2,0,0)) return _usbHsEpSubmitRequest(s, buffer, size, 0, transferredSize);
|
||||
|
||||
rc = _usbHsEpPostBufferAsync(s, buffer, size, 0, &xferId);
|
||||
rc = usbHsEpPostBufferAsync(s, buffer, size, &xferId);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = eventWait(&s->eventXfer, UINT64_MAX);
|
||||
@ -434,7 +470,7 @@ Result usbHsEpPostBuffer(UsbHsClientEpSession* s, void* buffer, u32 size, u32* t
|
||||
eventClear(&s->eventXfer);
|
||||
|
||||
memset(&report, 0, sizeof(report));
|
||||
rc = _usbHsEpGetXferReport(s, &report, 1, &count);
|
||||
rc = usbHsEpGetXferReport(s, &report, 1, &count);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
if (count<1) return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
@ -445,3 +481,60 @@ Result usbHsEpPostBuffer(UsbHsClientEpSession* s, void* buffer, u32 size, u32* t
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result usbHsEpBatchBufferAsync(UsbHsClientEpSession* s, void* buffer, u32* urbs, u32 urbCount, u32 unk1, u32 unk2, u32* xferId) {
|
||||
if (hosversionBefore(2,0,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
|
||||
const struct {
|
||||
u32 urbCount;
|
||||
u32 unk1;
|
||||
u32 unk2;
|
||||
u32 pad;
|
||||
u64 buffer;
|
||||
u64 unk;
|
||||
} in = { urbCount, unk1, unk2, 0, (u64)buffer, 0 };
|
||||
|
||||
serviceAssumeDomain(&s->s);
|
||||
return serviceDispatchInOut(&s->s, 6, in, *xferId,
|
||||
.buffer_attrs = { (hosversionBefore(3,0,0) ? SfBufferAttr_HipcMapAlias : SfBufferAttr_HipcAutoSelect) | SfBufferAttr_In },
|
||||
.buffers = { { urbs, urbCount*sizeof(u32) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result usbHsEpCreateSmmuSpace(UsbHsClientEpSession* s, void* buffer, u32 size) {
|
||||
if (hosversionBefore(4,0,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
|
||||
const struct {
|
||||
u32 size;
|
||||
u32 pad;
|
||||
u64 buffer;
|
||||
} in = { size, 0, (u64)buffer };
|
||||
|
||||
serviceAssumeDomain(&s->s);
|
||||
return serviceDispatchIn(&s->s, 7, in);
|
||||
}
|
||||
|
||||
Result usbHsEpShareReportRing(UsbHsClientEpSession* s) {
|
||||
if (hosversionBefore(4,0,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
if (s->tmem_initialized)
|
||||
return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
|
||||
|
||||
s->max_reports = s->maxUrbCount * 0x21;
|
||||
u32 size = sizeof(UsbHsRingHeader) + s->max_reports*sizeof(UsbHsXferReport);
|
||||
size = (size+0xFFF) & ~0xFFF;
|
||||
|
||||
Result rc = tmemCreate(&s->tmem, size, Perm_Rw);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
serviceAssumeDomain(&s->s);
|
||||
rc = serviceDispatchIn(&s->s, 8, size,
|
||||
.in_num_handles = 1,
|
||||
.in_handles = { s->tmem.handle },
|
||||
);
|
||||
if (R_FAILED(rc)) tmemClose(&s->tmem);
|
||||
else s->tmem_initialized = true;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user