From 3b345568bc05f3b0e307eae4c2e8c3c139f48c33 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Tue, 10 Sep 2024 01:13:25 +0200 Subject: [PATCH] uvcsink: Respond to control requests with proper error handling The complete handling on the control interface is currently dead. We return with EOPNOTSUPP for the caller to know that a response to such requests is not valid. The host however may ask control interface why these control requests were not available. For this the UVC_VC_REQUEST_ERROR_CODE_CONTROL is used. As an overall exception for the control interface we just always return 0x06 as an response which is representing "not implemented". This patch is a necessary feature to properly pass the UVC Functionality Test of the USB3CV Compliance Software. Fixes: 69c17461392d ('uvcgadget: Properly implement GET_INFO control responses') Part-of: --- .../sys/uvcgadget/gstuvcsink.c | 3 ++ .../sys/uvcgadget/gstuvcsink.h | 5 +++ .../gst-plugins-bad/sys/uvcgadget/uvc.c | 39 +++++++++++++++---- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.c b/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.c index 94fa95d03e..395cf2ac93 100644 --- a/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.c +++ b/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.c @@ -640,6 +640,8 @@ gst_uvc_sink_init (GstUvcSink * self) gst_pad_set_query_function (self->sinkpad, gst_uvc_sink_query); gst_pad_set_event_function (self->sinkpad, gst_uvc_sink_event); + self->request_error_code = REQUEST_ERROR_CODE_NO_ERROR; + self->cur_caps = gst_caps_new_empty (); } @@ -985,6 +987,7 @@ gst_uvc_sink_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PAUSED_TO_READY: gst_element_sync_state_with_parent (GST_ELEMENT (self->fakesink)); gst_uvc_sink_remove_idle_probe (self); + self->request_error_code = REQUEST_ERROR_CODE_NO_ERROR; break; case GST_STATE_CHANGE_READY_TO_NULL: if (!gst_uvc_sink_unwatch (self)) diff --git a/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.h b/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.h index 27e594eff2..ba75cf8dd9 100644 --- a/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.h +++ b/subprojects/gst-plugins-bad/sys/uvcgadget/gstuvcsink.h @@ -17,6 +17,9 @@ G_BEGIN_DECLS GST_DEBUG_CATEGORY_EXTERN (uvcsink_debug); +#define REQUEST_ERROR_CODE_NO_ERROR 0x0 +#define REQUEST_ERROR_CODE_INVALID_REQUEST 0x6 + #define GST_TYPE_UVCSINK (gst_uvc_sink_get_type()) G_DECLARE_FINAL_TYPE (GstUvcSink, gst_uvc_sink, GST, UVCSINK, GstBin) @@ -54,6 +57,8 @@ struct _GstUvcSink int control; + int request_error_code; + /* probes */ int buffer_peer_probe_id; int idle_probe_id; diff --git a/subprojects/gst-plugins-bad/sys/uvcgadget/uvc.c b/subprojects/gst-plugins-bad/sys/uvcgadget/uvc.c index 26c92b3b2a..918f4c19e9 100644 --- a/subprojects/gst-plugins-bad/sys/uvcgadget/uvc.c +++ b/subprojects/gst-plugins-bad/sys/uvcgadget/uvc.c @@ -343,7 +343,7 @@ uvc_events_process_streaming (GstUvcSink * self, uint8_t req, uint8_t cs, return 0; } -static void +static int uvc_events_parse_control (GstUvcSink * self, uint8_t req, uint8_t cs, uint8_t entity_id, uint8_t len, struct uvc_request_data *resp) { @@ -357,26 +357,50 @@ uvc_events_parse_control (GstUvcSink * self, uint8_t req, case 0: GST_DEBUG_OBJECT (self, "%s", uvc_video_control_interface_control_selector_name (cs)); - break; + /* + * For ERROR_CODE_CONTROL requests we have to return not implemented so + * bail out gracefully here to properly send control is not + * currently implemented. (0x06) (4.2.1.2 Request Error Code Control) + */ + if (cs == UVC_VC_REQUEST_ERROR_CODE_CONTROL) { + resp->data[0] = self->request_error_code; + resp->length = 1; + + break; + } + goto error; case 1: GST_DEBUG_OBJECT (self, "%s: %s", uvc_camera_terminal_control_selector_name (cs), uvc_request_name (req)); - break; + goto error; case 2: GST_DEBUG_OBJECT (self, "%s: %s", uvc_processing_unit_control_selector_name (cs), uvc_request_name (req)); - break; + goto error; default: GST_DEBUG_OBJECT (self, "Unknown entity ID (0x%02x), CS: 0x%02x, Request %s (0x%02x)", entity_id, cs, uvc_request_name (req), req); - break; + goto error; } + + self->request_error_code = REQUEST_ERROR_CODE_NO_ERROR; + + return 0; + +error: + + self->request_error_code = REQUEST_ERROR_CODE_INVALID_REQUEST; + /* + * Stall and don't response the control requests that are + * actually not implemented + */ + return -EOPNOTSUPP; } static int @@ -389,9 +413,8 @@ uvc_events_process_class (GstUvcSink * self, return -EINVAL; if (interface == UVC_STRING_CONTROL_IDX) { - uvc_events_parse_control (self, ctrl->bRequest, ctrl->wValue >> 8, - ctrl->wIndex >> 8, ctrl->wLength, resp); - return -EOPNOTSUPP; + return uvc_events_parse_control (self, ctrl->bRequest, + ctrl->wValue >> 8, ctrl->wIndex >> 8, ctrl->wLength, resp); } else if (interface == UVC_STRING_STREAMING_IDX) { return uvc_events_process_streaming (self, ctrl->bRequest, le16toh (ctrl->wValue) >> 8, resp);