Message ID | 20211205225803.268492-8-m.grzeschik@pengutronix.de |
---|---|
State | New |
Headers | show |
Series | usb: gadget: uvc: use configfs entries for negotiation and v4l2 VIDIOCS | expand |
Hi Michael, I love your patch! Perhaps something to improve: [auto build test WARNING on media-tree/master] [also build test WARNING on usb/usb-testing v5.16-rc4 next-20211206] [cannot apply to balbi-usb/testing/next peter-chen-usb/for-usb-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Michael-Grzeschik/usb-gadget-uvc-use-configfs-entries-for-negotiation-and-v4l2-VIDIOCS/20211206-070014 base: git://linuxtv.org/media_tree.git master config: i386-randconfig-s002-20211206 (https://download.01.org/0day-ci/archive/20211206/202112061803.4OpPVpwb-lkp@intel.com/config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.4-dirty # https://github.com/0day-ci/linux/commit/8fdc5cd9a8845e9a061b42155d8d05ddb8514921 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Michael-Grzeschik/usb-gadget-uvc-use-configfs-entries-for-negotiation-and-v4l2-VIDIOCS/20211206-070014 git checkout 8fdc5cd9a8845e9a061b42155d8d05ddb8514921 # save the config file to linux build tree mkdir build_dir make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash drivers/usb/gadget/function/ If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) >> drivers/usb/gadget/function/f_uvc.c:240:48: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] dwMaxPayloadTransferSize @@ got restricted __le16 const [usertype] wMaxPacketSize @@ drivers/usb/gadget/function/f_uvc.c:240:48: sparse: expected unsigned int [usertype] dwMaxPayloadTransferSize drivers/usb/gadget/function/f_uvc.c:240:48: sparse: got restricted __le16 const [usertype] wMaxPacketSize >> drivers/usb/gadget/function/f_uvc.c:359:18: sparse: sparse: restricted __le16 degrades to integer drivers/usb/gadget/function/f_uvc.c:361:23: sparse: sparse: restricted __le16 degrades to integer drivers/usb/gadget/function/f_uvc.c:363:50: sparse: sparse: restricted __le16 degrades to integer vim +240 drivers/usb/gadget/function/f_uvc.c 197 198 /* -------------------------------------------------------------------------- 199 * Control requests 200 */ 201 202 void uvc_fill_streaming_control(struct uvc_device *uvc, 203 struct uvc_streaming_control *ctrl, 204 int iframe, int iformat, unsigned int ival) 205 { 206 struct uvcg_format *uformat; 207 struct uvcg_frame *uframe; 208 209 /* Restrict the iformat, iframe and ival to valid values. Negative 210 * values for ifrmat and iframe will result in the maximum valid value 211 * being selected 212 */ 213 iformat = clamp((unsigned int)iformat, 1U, 214 (unsigned int)uvc->header->num_fmt); 215 uformat = find_format_by_index(uvc, iformat); 216 if (!uformat) 217 return; 218 219 iframe = clamp((unsigned int)iframe, 1U, 220 (unsigned int)uformat->num_frames); 221 uframe = find_frame_by_index(uvc, uformat, iframe); 222 if (!uframe) 223 return; 224 225 ival = clamp((unsigned int)ival, 1U, 226 (unsigned int)uframe->frame.b_frame_interval_type); 227 if (!uframe->dw_frame_interval[ival - 1]) 228 return; 229 230 memset(ctrl, 0, sizeof(*ctrl)); 231 232 ctrl->bmHint = 1; 233 ctrl->bFormatIndex = iformat; 234 ctrl->bFrameIndex = iframe; 235 ctrl->dwFrameInterval = uframe->dw_frame_interval[ival - 1]; 236 ctrl->dwMaxVideoFrameSize = 237 uframe->frame.dw_max_video_frame_buffer_size; 238 239 if (uvc->video.ep->desc) > 240 ctrl->dwMaxPayloadTransferSize = 241 uvc->video.ep->desc->wMaxPacketSize; 242 ctrl->bmFramingInfo = 3; 243 ctrl->bPreferedVersion = 1; 244 ctrl->bMaxVersion = 1; 245 } 246 247 static int uvc_events_process_data(struct uvc_device *uvc, 248 struct usb_request *req) 249 { 250 struct uvc_video *video = &uvc->video; 251 struct uvc_streaming_control *target; 252 struct uvc_streaming_control *ctrl; 253 struct uvcg_frame *uframe; 254 struct uvcg_format *uformat; 255 256 switch (video->control) { 257 case UVC_VS_PROBE_CONTROL: 258 pr_debug("setting probe control, length = %d\n", req->actual); 259 target = &video->probe; 260 break; 261 262 case UVC_VS_COMMIT_CONTROL: 263 pr_debug("setting commit control, length = %d\n", req->actual); 264 target = &video->commit; 265 break; 266 267 default: 268 pr_err("setting unknown control, length = %d\n", req->actual); 269 return -EOPNOTSUPP; 270 } 271 272 ctrl = (struct uvc_streaming_control *)req->buf; 273 274 uvc_fill_streaming_control(uvc, target, ctrl->bFrameIndex, 275 ctrl->bFormatIndex, ctrl->dwFrameInterval); 276 277 if (video->control == UVC_VS_COMMIT_CONTROL) { 278 uformat = find_format_by_index(uvc, target->bFormatIndex); 279 if (!uformat) 280 return -EINVAL; 281 282 uframe = find_frame_by_index(uvc, uformat, ctrl->bFrameIndex); 283 if (!uframe) 284 return -EINVAL; 285 286 spin_lock(&video->frame_lock); 287 288 video->cur_frame = uframe; 289 video->cur_format = uformat; 290 video->cur_ival = find_ival_index(uframe, ctrl->dwFrameInterval); 291 292 spin_unlock(&video->frame_lock); 293 } 294 295 return 0; 296 } 297 298 static void 299 uvc_events_process_streaming(struct uvc_device *uvc, uint8_t req, uint8_t cs, 300 struct uvc_request_data *resp) 301 { 302 struct uvc_streaming_control *ctrl; 303 304 pr_debug("streaming request (req %02x cs %02x)\n", req, cs); 305 306 if (cs != UVC_VS_PROBE_CONTROL && cs != UVC_VS_COMMIT_CONTROL) 307 return; 308 309 ctrl = (struct uvc_streaming_control *)&resp->data; 310 resp->length = sizeof(*ctrl); 311 312 switch (req) { 313 case UVC_SET_CUR: 314 uvc->video.control = cs; 315 resp->length = 34; 316 break; 317 318 case UVC_GET_CUR: 319 if (cs == UVC_VS_PROBE_CONTROL) 320 memcpy(ctrl, &uvc->video.probe, sizeof(*ctrl)); 321 else 322 memcpy(ctrl, &uvc->video.commit, sizeof(*ctrl)); 323 break; 324 325 case UVC_GET_MIN: 326 case UVC_GET_MAX: 327 case UVC_GET_DEF: 328 if (req == UVC_GET_MAX) 329 uvc_fill_streaming_control(uvc, ctrl, -1, -1, UINT_MAX); 330 else 331 uvc_fill_streaming_control(uvc, ctrl, 1, 1, 0); 332 break; 333 334 case UVC_GET_RES: 335 memset(ctrl, 0, sizeof(*ctrl)); 336 break; 337 338 case UVC_GET_LEN: 339 resp->data[0] = 0x00; 340 resp->data[1] = 0x22; 341 resp->length = 2; 342 break; 343 344 case UVC_GET_INFO: 345 resp->data[0] = 0x03; 346 resp->length = 1; 347 break; 348 } 349 } 350 351 static int 352 uvc_events_process_class(struct uvc_device *uvc, 353 const struct usb_ctrlrequest *ctrl, 354 struct uvc_request_data *resp) 355 { 356 if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE) 357 return -EINVAL; 358 > 359 if ((ctrl->wIndex & 0xff) == uvc->control_intf) 360 return -EOPNOTSUPP; 361 else if ((ctrl->wIndex & 0xff) == uvc->streaming_intf) 362 uvc_events_process_streaming(uvc, ctrl->bRequest, 363 ctrl->wValue >> 8, resp); 364 365 return 0; 366 } 367 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Michael, I love your patch! Yet something to improve: [auto build test ERROR on media-tree/master] [also build test ERROR on usb/usb-testing v5.16-rc4 next-20211206] [cannot apply to balbi-usb/testing/next peter-chen-usb/for-usb-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Michael-Grzeschik/usb-gadget-uvc-use-configfs-entries-for-negotiation-and-v4l2-VIDIOCS/20211206-070014 base: git://linuxtv.org/media_tree.git master config: riscv-randconfig-r024-20211205 (https://download.01.org/0day-ci/archive/20211206/202112062124.HEJZJ0fR-lkp@intel.com/config) compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 6e8678903523019903222e4521a5e41af743cab0) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install riscv cross compiling tool for clang build # apt-get install binutils-riscv64-linux-gnu # https://github.com/0day-ci/linux/commit/8fdc5cd9a8845e9a061b42155d8d05ddb8514921 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Michael-Grzeschik/usb-gadget-uvc-use-configfs-entries-for-negotiation-and-v4l2-VIDIOCS/20211206-070014 git checkout 8fdc5cd9a8845e9a061b42155d8d05ddb8514921 # save the config file to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/usb/gadget/function/ If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): drivers/usb/gadget/function/uvc_v4l2.c:73:2: warning: variable 'uformat' is used uninitialized whenever 'for' loop exits because its condition is false [-Wsometimes-uninitialized] list_for_each_entry(format, &uvc->header->formats, entry) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/list.h:631:7: note: expanded from macro 'list_for_each_entry' !list_entry_is_head(pos, head, member); \ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/usb/gadget/function/uvc_v4l2.c:81:9: note: uninitialized use occurs here return uformat; ^~~~~~~ drivers/usb/gadget/function/uvc_v4l2.c:73:2: note: remove the condition if it is always true list_for_each_entry(format, &uvc->header->formats, entry) { ^ include/linux/list.h:631:7: note: expanded from macro 'list_for_each_entry' !list_entry_is_head(pos, head, member); \ ^ drivers/usb/gadget/function/uvc_v4l2.c:70:29: note: initialize the variable 'uformat' to silence this warning struct uvcg_format *uformat; ^ = NULL drivers/usb/gadget/function/uvc_v4l2.c:139:2: warning: variable 'uformat' is used uninitialized whenever 'for' loop exits because its condition is false [-Wsometimes-uninitialized] list_for_each_entry(format, &uvc->header->formats, entry) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/list.h:631:7: note: expanded from macro 'list_for_each_entry' !list_entry_is_head(pos, head, member); \ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/usb/gadget/function/uvc_v4l2.c:148:9: note: uninitialized use occurs here return uformat; ^~~~~~~ drivers/usb/gadget/function/uvc_v4l2.c:139:2: note: remove the condition if it is always true list_for_each_entry(format, &uvc->header->formats, entry) { ^ include/linux/list.h:631:7: note: expanded from macro 'list_for_each_entry' !list_entry_is_head(pos, head, member); \ ^ drivers/usb/gadget/function/uvc_v4l2.c:137:29: note: initialize the variable 'uformat' to silence this warning struct uvcg_format *uformat; ^ = NULL >> drivers/usb/gadget/function/uvc_v4l2.c:334:14: error: use of undeclared identifier 'video' spin_lock(&video->frame_lock); ^ drivers/usb/gadget/function/uvc_v4l2.c:339:16: error: use of undeclared identifier 'video' spin_unlock(&video->frame_lock); ^ 2 warnings and 2 errors generated. vim +/video +334 drivers/usb/gadget/function/uvc_v4l2.c 305 306 static int 307 uvc_v4l2_enum_frameintervals(struct file *file, void *fh, 308 struct v4l2_frmivalenum *fival) 309 { 310 struct video_device *vdev = video_devdata(file); 311 struct uvc_device *uvc = video_get_drvdata(vdev); 312 struct uvcg_format *uformat = NULL; 313 struct uvcg_frame *uframe = NULL; 314 struct uvcg_frame_ptr *frame; 315 316 uformat = find_format_by_pix(uvc, fival->pixel_format); 317 if (!uformat) 318 return -EINVAL; 319 320 list_for_each_entry(frame, &uformat->frames, entry) { 321 if (frame->frm->frame.w_width == fival->width && 322 frame->frm->frame.w_height == fival->height) { 323 uframe = frame->frm; 324 break; 325 } 326 } 327 if (!uframe) 328 return -EINVAL; 329 330 if (uvc->streamon) { 331 if (fival->index >= 1) 332 return -EINVAL; 333 > 334 spin_lock(&video->frame_lock); 335 336 fival->discrete.numerator = 337 uframe->dw_frame_interval[uvc->video.cur_ival - 1]; 338 339 spin_unlock(&video->frame_lock); 340 } else { 341 if (fival->index >= uframe->frame.b_frame_interval_type) 342 return -EINVAL; 343 344 fival->discrete.numerator = 345 uframe->dw_frame_interval[fival->index]; 346 } 347 348 /* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */ 349 fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 350 fival->discrete.denominator = 10000000; 351 v4l2_simplify_fraction(&fival->discrete.numerator, 352 &fival->discrete.denominator, 8, 333); 353 354 return 0; 355 } 356 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e081a4e6572ee2..de97a6e29ea955 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -16,7 +16,6 @@ #include <linux/string.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -#include <linux/usb/g_uvc.h> #include <linux/usb/video.h> #include <linux/vmalloc.h> #include <linux/wait.h> @@ -200,16 +199,229 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = { * Control requests */ +void uvc_fill_streaming_control(struct uvc_device *uvc, + struct uvc_streaming_control *ctrl, + int iframe, int iformat, unsigned int ival) +{ + struct uvcg_format *uformat; + struct uvcg_frame *uframe; + + /* Restrict the iformat, iframe and ival to valid values. Negative + * values for ifrmat and iframe will result in the maximum valid value + * being selected + */ + iformat = clamp((unsigned int)iformat, 1U, + (unsigned int)uvc->header->num_fmt); + uformat = find_format_by_index(uvc, iformat); + if (!uformat) + return; + + iframe = clamp((unsigned int)iframe, 1U, + (unsigned int)uformat->num_frames); + uframe = find_frame_by_index(uvc, uformat, iframe); + if (!uframe) + return; + + ival = clamp((unsigned int)ival, 1U, + (unsigned int)uframe->frame.b_frame_interval_type); + if (!uframe->dw_frame_interval[ival - 1]) + return; + + memset(ctrl, 0, sizeof(*ctrl)); + + ctrl->bmHint = 1; + ctrl->bFormatIndex = iformat; + ctrl->bFrameIndex = iframe; + ctrl->dwFrameInterval = uframe->dw_frame_interval[ival - 1]; + ctrl->dwMaxVideoFrameSize = + uframe->frame.dw_max_video_frame_buffer_size; + + if (uvc->video.ep->desc) + ctrl->dwMaxPayloadTransferSize = + uvc->video.ep->desc->wMaxPacketSize; + ctrl->bmFramingInfo = 3; + ctrl->bPreferedVersion = 1; + ctrl->bMaxVersion = 1; +} + +static int uvc_events_process_data(struct uvc_device *uvc, + struct usb_request *req) +{ + struct uvc_video *video = &uvc->video; + struct uvc_streaming_control *target; + struct uvc_streaming_control *ctrl; + struct uvcg_frame *uframe; + struct uvcg_format *uformat; + + switch (video->control) { + case UVC_VS_PROBE_CONTROL: + pr_debug("setting probe control, length = %d\n", req->actual); + target = &video->probe; + break; + + case UVC_VS_COMMIT_CONTROL: + pr_debug("setting commit control, length = %d\n", req->actual); + target = &video->commit; + break; + + default: + pr_err("setting unknown control, length = %d\n", req->actual); + return -EOPNOTSUPP; + } + + ctrl = (struct uvc_streaming_control *)req->buf; + + uvc_fill_streaming_control(uvc, target, ctrl->bFrameIndex, + ctrl->bFormatIndex, ctrl->dwFrameInterval); + + if (video->control == UVC_VS_COMMIT_CONTROL) { + uformat = find_format_by_index(uvc, target->bFormatIndex); + if (!uformat) + return -EINVAL; + + uframe = find_frame_by_index(uvc, uformat, ctrl->bFrameIndex); + if (!uframe) + return -EINVAL; + + spin_lock(&video->frame_lock); + + video->cur_frame = uframe; + video->cur_format = uformat; + video->cur_ival = find_ival_index(uframe, ctrl->dwFrameInterval); + + spin_unlock(&video->frame_lock); + } + + return 0; +} + +static void +uvc_events_process_streaming(struct uvc_device *uvc, uint8_t req, uint8_t cs, + struct uvc_request_data *resp) +{ + struct uvc_streaming_control *ctrl; + + pr_debug("streaming request (req %02x cs %02x)\n", req, cs); + + if (cs != UVC_VS_PROBE_CONTROL && cs != UVC_VS_COMMIT_CONTROL) + return; + + ctrl = (struct uvc_streaming_control *)&resp->data; + resp->length = sizeof(*ctrl); + + switch (req) { + case UVC_SET_CUR: + uvc->video.control = cs; + resp->length = 34; + break; + + case UVC_GET_CUR: + if (cs == UVC_VS_PROBE_CONTROL) + memcpy(ctrl, &uvc->video.probe, sizeof(*ctrl)); + else + memcpy(ctrl, &uvc->video.commit, sizeof(*ctrl)); + break; + + case UVC_GET_MIN: + case UVC_GET_MAX: + case UVC_GET_DEF: + if (req == UVC_GET_MAX) + uvc_fill_streaming_control(uvc, ctrl, -1, -1, UINT_MAX); + else + uvc_fill_streaming_control(uvc, ctrl, 1, 1, 0); + break; + + case UVC_GET_RES: + memset(ctrl, 0, sizeof(*ctrl)); + break; + + case UVC_GET_LEN: + resp->data[0] = 0x00; + resp->data[1] = 0x22; + resp->length = 2; + break; + + case UVC_GET_INFO: + resp->data[0] = 0x03; + resp->length = 1; + break; + } +} + +static int +uvc_events_process_class(struct uvc_device *uvc, + const struct usb_ctrlrequest *ctrl, + struct uvc_request_data *resp) +{ + if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE) + return -EINVAL; + + if ((ctrl->wIndex & 0xff) == uvc->control_intf) + return -EOPNOTSUPP; + else if ((ctrl->wIndex & 0xff) == uvc->streaming_intf) + uvc_events_process_streaming(uvc, ctrl->bRequest, + ctrl->wValue >> 8, resp); + + return 0; +} + +static int +uvc_events_process_setup(struct uvc_device *uvc, + const struct usb_ctrlrequest *ctrl, + struct uvc_request_data *resp) +{ + uvc->video.control = 0; + + pr_debug("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %04x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, + ctrl->wIndex, ctrl->wLength); + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + return -EOPNOTSUPP; + + case USB_TYPE_CLASS: + return uvc_events_process_class(uvc, ctrl, resp); + + default: + break; + } + + return 0; +} + static void uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req) { struct uvc_device *uvc = req->context; struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + struct uvc_request_data resp; + int ret; if (uvc->event_setup_out) { uvc->event_setup_out = 0; + memset(&resp, 0, sizeof(resp)); + resp.length = -EL2HLT; + + ret = uvc_events_process_data(uvc, req); + /* If we have no error on process */ + if (!ret) { + resp.length = req->length; + uvc_send_response(uvc, &resp); + return; + } + + /* If we have a real error on process */ + if (ret != -EOPNOTSUPP) + return; + + /* If we have -EOPNOTSUPP */ + if (!uvc->data_subscribed) + return; + + /* If we have data subscribed */ memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_DATA; uvc_event->data.length = req->actual; @@ -224,6 +436,8 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) struct uvc_device *uvc = to_uvc(f); struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + struct uvc_request_data resp; + int ret = 0; if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) { uvcg_info(f, "invalid request type\n"); @@ -240,6 +454,23 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN); uvc->event_length = le16_to_cpu(ctrl->wLength); + memset(&resp, 0, sizeof(resp)); + resp.length = -EL2HLT; + + ret = uvc_events_process_setup(uvc, ctrl, &resp); + /* If we have no error on process */ + if (!ret) + return uvc_send_response(uvc, &resp); + + /* If we have a real error on process */ + if (ret != -EOPNOTSUPP) + return ret; + + /* If we have -EOPNOTSUPP */ + if (!uvc->setup_subscribed) + return uvc_send_response(uvc, &resp); + + /* If we have setup subscribed */ memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_SETUP; memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index b7246b4bee1d87..a9984d26ebe043 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -13,6 +13,8 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/usb/composite.h> +#include <linux/usb/g_uvc.h> +#include <linux/usb/video.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> @@ -93,6 +95,12 @@ struct uvc_video { unsigned int cur_ival; struct mutex mutex; /* protects frame parameters */ + spinlock_t frame_lock; + + struct uvc_streaming_control probe; + struct uvc_streaming_control commit; + + int control; unsigned int uvc_num_requests; @@ -128,6 +136,8 @@ struct uvc_device { struct usb_function func; struct uvc_video video; bool func_connected; + bool setup_subscribed; + bool data_subscribed; struct uvcg_streaming_header *header; @@ -183,5 +193,14 @@ extern struct uvcg_format *find_format_by_index(struct uvc_device *uvc, extern struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, struct uvcg_format *uformat, int index); +extern int find_format_index(struct uvc_device *uvc, + struct uvcg_format *uformat); +extern int find_ival_index(struct uvcg_frame *uframe, int dwFrameInterval); +extern void uvc_fill_streaming_control(struct uvc_device *uvc, + struct uvc_streaming_control *ctrl, + int iframe, int iformat, + unsigned int ival); +extern int uvc_send_response(struct uvc_device *uvc, + struct uvc_request_data *data); #endif /* _UVC_GADGET_H_ */ diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 1663e70c41eb24..8eac45e3a31a12 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -81,6 +81,33 @@ struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index) return uformat; } +int find_format_index(struct uvc_device *uvc, struct uvcg_format *uformat) +{ + struct uvcg_format_ptr *format; + int i = 1; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (uformat == format->fmt) + return i; + i++; + } + + return 0; +} + +int find_ival_index(struct uvcg_frame *uframe, int dwFrameInterval) +{ + int i; + + for (i = 0; i < uframe->frame.b_frame_interval_type; i++) { + if (dwFrameInterval == uframe->dw_frame_interval[i]) + return i + 1; + } + + /* fallback */ + return 1; +} + struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, struct uvcg_format *uformat, int index) @@ -169,8 +196,7 @@ static struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc, * Requests handling */ -static int -uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) +int uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) { struct usb_composite_dev *cdev = uvc->func.config->cdev; struct usb_request *req = uvc->control_req; @@ -212,6 +238,8 @@ uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) struct uvc_video *video = &uvc->video; struct uvc_format_desc *fmtdesc; + spin_lock(&video->frame_lock); + fmtdesc = to_uvc_format(video->cur_format); fmt->fmt.pix.pixelformat = fmtdesc->fcc; @@ -225,6 +253,8 @@ uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; fmt->fmt.pix.priv = 0; + spin_unlock(&video->frame_lock); + return 0; } @@ -236,6 +266,7 @@ uvc_v4l2_try_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt) struct uvc_video *video = &uvc->video; struct uvcg_format *uformat; struct uvcg_frame *uframe; + int iformat; u8 *fcc; if (fmt->type != video->queue.queue.type) @@ -251,6 +282,10 @@ uvc_v4l2_try_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt) if (!uformat) return -EINVAL; + iformat = find_format_index(uvc, uformat); + if (!iformat) + return -EINVAL; + uframe = find_closest_frame_by_size(uvc, uformat, fmt->fmt.pix.width, fmt->fmt.pix.height); if (!uframe) @@ -296,8 +331,12 @@ uvc_v4l2_enum_frameintervals(struct file *file, void *fh, if (fival->index >= 1) return -EINVAL; + spin_lock(&video->frame_lock); + fival->discrete.numerator = uframe->dw_frame_interval[uvc->video.cur_ival - 1]; + + spin_unlock(&video->frame_lock); } else { if (fival->index >= uframe->frame.b_frame_interval_type) return -EINVAL; @@ -329,8 +368,12 @@ uvc_v4l2_enum_framesizes(struct file *file, void *fh, if (fsize->index >= 1) return -EINVAL; + spin_lock(&video->frame_lock); + uformat = video->cur_format; uframe = video->cur_frame; + + spin_unlock(&video->frame_lock); } else { uformat = find_format_by_pix(uvc, fsize->pixel_format); if (!uformat) @@ -364,7 +407,11 @@ uvc_v4l2_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) if (f->index >= 1) return -EINVAL; + spin_lock(&video->frame_lock); + uformat = video->cur_format; + + spin_unlock(&video->frame_lock); } else { if (f->index >= uvc->header->num_fmt) return -EINVAL; @@ -488,14 +535,20 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) return -EINVAL; - if (sub->type == UVC_EVENT_SETUP && uvc->func_connected) + if (sub->type == UVC_EVENT_STREAMON && uvc->func_connected) return -EBUSY; ret = v4l2_event_subscribe(fh, sub, 2, NULL); if (ret < 0) return ret; - if (sub->type == UVC_EVENT_SETUP) { + if (sub->type == UVC_EVENT_SETUP) + uvc->setup_subscribed = true; + + if (sub->type == UVC_EVENT_DATA) + uvc->data_subscribed = true; + + if (sub->type == UVC_EVENT_STREAMON) { uvc->func_connected = true; handle->is_uvc_app_handle = true; uvc_function_connect(uvc); @@ -524,7 +577,10 @@ uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh, if (ret < 0) return ret; - if (sub->type == UVC_EVENT_SETUP && handle->is_uvc_app_handle) { + if (sub->type == UVC_EVENT_SETUP) + uvc->setup_subscribed = false; + + if (sub->type == UVC_EVENT_STREAMON && handle->is_uvc_app_handle) { uvc_v4l2_disable(uvc); handle->is_uvc_app_handle = false; } diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 9fb2b9c575ed34..7eb5364997028b 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -517,10 +517,11 @@ static int uvc_default_frame_interval(struct uvc_video *video) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { - int iframe; + int iframe, iformat; INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); + spin_lock_init(&video->frame_lock); INIT_WORK(&video->pump, uvcg_video_pump); if (list_empty(&uvc->header->formats)) @@ -531,6 +532,10 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) if (!video->cur_format) return -EINVAL; + iformat = find_format_index(uvc, video->cur_format); + if (!iformat) + return -EINVAL; + iframe = uvc_frame_default(video->cur_format); if (!iframe) return -EINVAL; @@ -541,6 +546,11 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) video->cur_ival = uvc_default_frame_interval(video); + uvc_fill_streaming_control(uvc, &video->probe, iframe, iformat, + video->cur_ival); + uvc_fill_streaming_control(uvc, &video->commit, iframe, iformat, + video->cur_ival); + /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
The Hostside format selection is currently only done in userspace, as the events for SET_CUR and GET_CUR are allways moved to the application layer. Since the v4l2 device parses the configfs data, the format negotiation can be done in the kernel. This patch adds the functions to set the current configuration while continuing to forward all unknown events to the userspace level. Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> --- v1 -> v2: - fixed the commit message - changed pr_debug to pr_err in events_process_data - aligned many indentations - simplified uvc_events_process_data - fixed uvc_fill_streaming_control calls in uvcg_video_init - added setup_subcribed to decide if userspace takes over on EOPNOTSUPP - added data_subscribed to decide if userspace takes over on EOPNOTSUPP - removed duplicate send_response - wrting fmt and frm in full v2 -> v3: - added find_format_index to set the right probe v3 -> v4: - add function find_ival_index and use for cur_ival - fix swapped frame and format in uvc_events_process_data on uvc_fill_streaming_control - set proper resp.length on ep0 complete - dropped setting cur_probe on set_format since function was removed - added locking around getting correspondent cur_{frame,format,ival} drivers/usb/gadget/function/f_uvc.c | 233 +++++++++++++++++++++++- drivers/usb/gadget/function/uvc.h | 19 ++ drivers/usb/gadget/function/uvc_v4l2.c | 66 ++++++- drivers/usb/gadget/function/uvc_video.c | 12 +- 4 files changed, 323 insertions(+), 7 deletions(-)