From 0a015032275ed7b595fd78ddba0baf0ac1dd5f10 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 19 Mar 2021 12:31:26 +0100 Subject: [PATCH] capnhook/usbhook: Add invoce next function that resets next handlers Required for piuio-khack which needs to convert a single libusb call into 8 separate calls. In that case, we want to advance the handlers downstream but reset it once we go back up to the invocation which needs to scatter to multiple calls. As stated by the documentation, state handling of the irp is not taken care of. This is fine, since at this point, we don't know what the function is and the caller needs to take care of regarding saving and recovering any state in the irp. --- src/main/capnhook/hook/usbhook.c | 71 ++++++++++++++++++++------------ src/main/capnhook/hook/usbhook.h | 20 +++++++++ 2 files changed, 65 insertions(+), 26 deletions(-) diff --git a/src/main/capnhook/hook/usbhook.c b/src/main/capnhook/hook/usbhook.c index 89ba9c2..a7aa9e0 100644 --- a/src/main/capnhook/hook/usbhook.c +++ b/src/main/capnhook/hook/usbhook.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,7 @@ typedef int (*cnh_usbhook_usb_control_msg_t)(usb_dev_handle* dev, int requesttyp /* ------------------------------------------------------------------------------------------------------------------ */ static void _cnh_usbhook_init(void); +static enum cnh_result _cnh_usbhook_invoke_next_reset_advance(struct cnh_usbhook_irp *irp, bool reset_next_handler_advance); static enum cnh_result _cnh_usbhook_invoke_real(struct cnh_usbhook_irp* irp); static enum cnh_result _cnh_usbhook_invoke_real_init(struct cnh_usbhook_irp* irp); @@ -114,33 +116,12 @@ enum cnh_result cnh_usbhook_push_handler(cnh_usbhook_fn_t fn) enum cnh_result cnh_usbhook_invoke_next(struct cnh_usbhook_irp *irp) { - cnh_usbhook_fn_t handler; - enum cnh_result result; + return _cnh_usbhook_invoke_next_reset_advance(irp, false); +} - assert(irp != NULL); - assert(_cnh_usbhook_initted > 0); - - pthread_mutex_lock(&_cnh_usbhook_lock); - - assert(irp->next_handler <= _cnh_usbhook_nhandlers); - - if (irp->next_handler < _cnh_usbhook_nhandlers) { - handler = _cnh_usbhook_handlers[irp->next_handler]; - irp->next_handler++; - } else { - handler = _cnh_usbhook_invoke_real; - irp->next_handler = (size_t) -1; - } - - pthread_mutex_unlock(&_cnh_usbhook_lock); - - result = handler(irp); - - if (result != CNH_RESULT_SUCCESS) { - irp->next_handler = (size_t) -1; - } - - return result; +enum cnh_result cnh_usbhook_invoke_next_reset_advance(struct cnh_usbhook_irp *irp) +{ + return _cnh_usbhook_invoke_next_reset_advance(irp, true); } /* ------------------------------------------------------------------------------------------------------------------ */ @@ -461,6 +442,44 @@ static void _cnh_usbhook_init(void) atomic_store(&_cnh_usbhook_init_in_progress, 0); } +static enum cnh_result _cnh_usbhook_invoke_next_reset_advance(struct cnh_usbhook_irp *irp, bool reset_next_handler_advance) +{ + cnh_usbhook_fn_t handler; + enum cnh_result result; + size_t cur_next_handler; + + assert(irp != NULL); + assert(_cnh_usbhook_initted > 0); + + cur_next_handler = irp->next_handler; + + pthread_mutex_lock(&_cnh_usbhook_lock); + + assert(irp->next_handler <= _cnh_usbhook_nhandlers); + + if (irp->next_handler < _cnh_usbhook_nhandlers) { + handler = _cnh_usbhook_handlers[irp->next_handler]; + irp->next_handler++; + } else { + handler = _cnh_usbhook_invoke_real; + irp->next_handler = (size_t) -1; + } + + pthread_mutex_unlock(&_cnh_usbhook_lock); + + result = handler(irp); + + if (result != CNH_RESULT_SUCCESS) { + irp->next_handler = (size_t) -1; + } + + if (reset_next_handler_advance) { + irp->next_handler = cur_next_handler; + } + + return result; +} + static enum cnh_result _cnh_usbhook_invoke_real(struct cnh_usbhook_irp* irp) { cnh_usbhook_fn_t handler; diff --git a/src/main/capnhook/hook/usbhook.h b/src/main/capnhook/hook/usbhook.h index 1cd4bf4..94700f4 100644 --- a/src/main/capnhook/hook/usbhook.h +++ b/src/main/capnhook/hook/usbhook.h @@ -67,9 +67,29 @@ enum cnh_result cnh_usbhook_push_handler(cnh_usbhook_fn_t fn); * Call this from your hook handler if you want to pass on execution to further * hooked calls reaching the original function at the end of the hook chain. * + * Note: This will advance the irp state in the handler chain. It is not + * possible to re-use that irp for multiple/different invocations. + * * @param irp I/O request package to dispatch * @return Result of the following function dispatching the request package */ enum cnh_result cnh_usbhook_invoke_next(struct cnh_usbhook_irp *irp); +/** + * Same as cnh_usbhook_invoke_next but allows you to define if the you want + * to reset the next handler of the irp to the initial one once the call + * returns. This still returns the results as expected from the invocation + * chain. + * + * However, the irp state is modified and NOT rolled back. Depending on the + * call, it might contain state change and data to be returned. If you need + * to invoke an irp multiple times, you as the caller need to take care of + * buffering any states and recover it accordingly for successive calls on the + * same irp. + * + * @param irp I/O request package to dispatch + * @return Result of the following function dispatching the request package + */ +enum cnh_result cnh_usbhook_invoke_next_reset_advance(struct cnh_usbhook_irp *irp); + #endif