📄 uvc_driver.c
字号:
} frame->dwFrameInterval = *intervals; /* Some bogus devices report dwMinFrameInterval equal to * dwMaxFrameInterval and have dwFrameIntervalStep set to * zero. Setting all null intervals to 1 fixes the problem and * some other divisions by zero which could happen. */ for (i = 0; i < n; ++i) { interval = le32_to_cpup((__le32*)&buffer[26+4*i]); *(*intervals)++ = interval ? interval : 1; } /* Make sure that the default frame interval stays between * the boundaries. */ n -= frame->bFrameIntervalType ? 1 : 2; frame->dwDefaultFrameInterval = min(frame->dwFrameInterval[n], max(frame->dwFrameInterval[0], frame->dwDefaultFrameInterval)); uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", frame->wWidth, frame->wHeight, 10000000/frame->dwDefaultFrameInterval, (100000000/frame->dwDefaultFrameInterval)%10); format->nframes++; buflen -= buffer[0]; buffer += buffer[0]; } if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" "interface %d COLORFORMAT error\n", dev->udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } format->colorspace = uvc_colorspace(buffer[3]); buflen -= buffer[0]; buffer += buffer[0]; } return buffer - start;}static int uvc_parse_streaming(struct uvc_device *dev, struct uvc_streaming *streaming){ struct uvc_format *format; struct uvc_frame *frame; struct usb_interface *intf = streaming->intf; struct usb_host_interface *alts = &intf->altsetting[0]; unsigned char *_buffer, *buffer = alts->extra; int _buflen, buflen = alts->extralen; unsigned int nformats = 0, nframes = 0, nintervals = 0; unsigned int size, i, n, p; __u32 *interval; __u16 psize; int ret; /* The Pico iMage webcam has its class-specific interface descriptors * after the endpoint descriptors. */ if (buflen == 0) { for (i = 0; i < alts->desc.bNumEndpoints; ++i) { struct usb_host_endpoint *ep = &alts->endpoint[i]; if (ep->extralen == 0) continue; if (ep->extralen > 2 && ep->extra[1] == USB_DT_CS_INTERFACE) { uvc_trace(UVC_TRACE_DESCR, "trying extra data " "from endpoint %u.\n", i); buffer = alts->endpoint[i].extra; buflen = alts->endpoint[i].extralen; break; } } } /* Skip the standard interface descriptors. */ while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { buflen -= buffer[0]; buffer += buffer[0]; } if (buflen <= 2) { uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " "interface descriptors found.\n"); return -EINVAL; } /* Parse the header descriptor. */ if (buffer[2] == VS_OUTPUT_HEADER) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " "%d OUTPUT HEADER descriptor is not supported.\n", dev->udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } else if (buffer[2] == VS_INPUT_HEADER) { p = buflen >= 5 ? buffer[3] : 0; n = buflen >= 12 ? buffer[12] : 0; if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d INPUT HEADER descriptor is " "invalid.\n", dev->udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } streaming->header.bNumFormats = p; streaming->header.bEndpointAddress = buffer[6]; streaming->header.bmInfo = buffer[7]; streaming->header.bTerminalLink = buffer[8]; streaming->header.bStillCaptureMethod = buffer[9]; streaming->header.bTriggerSupport = buffer[10]; streaming->header.bTriggerUsage = buffer[11]; streaming->header.bControlSize = n; streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); if (streaming->header.bmaControls == NULL) return -ENOMEM; memcpy(streaming->header.bmaControls, &buffer[13], p*n); } else { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " "%d HEADER descriptor not found.\n", dev->udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } buflen -= buffer[0]; buffer += buffer[0]; _buffer = buffer; _buflen = buflen; /* Count the format and frame descriptors. */ while (_buflen > 2) { switch (_buffer[2]) { case VS_FORMAT_UNCOMPRESSED: case VS_FORMAT_MJPEG: case VS_FORMAT_FRAME_BASED: nformats++; break; case VS_FORMAT_DV: /* DV format has no frame descriptor. We will create a * dummy frame descriptor with a dummy frame interval. */ nformats++; nframes++; nintervals++; break; case VS_FORMAT_MPEG2TS: case VS_FORMAT_STREAM_BASED: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT %u is not supported.\n", dev->udev->devnum, alts->desc.bInterfaceNumber, _buffer[2]); break; case VS_FRAME_UNCOMPRESSED: case VS_FRAME_MJPEG: nframes++; if (_buflen > 25) nintervals += _buffer[25] ? _buffer[25] : 3; break; case VS_FRAME_FRAME_BASED: nframes++; if (_buflen > 21) nintervals += _buffer[21] ? _buffer[21] : 3; break; } _buflen -= _buffer[0]; _buffer += _buffer[0]; } if (nformats == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " "%d has no supported formats defined.\n", dev->udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } size = nformats * sizeof *format + nframes * sizeof *frame + nintervals * sizeof *interval; format = kzalloc(size, GFP_KERNEL); if (format == NULL) return -ENOMEM; frame = (struct uvc_frame*)&format[nformats]; interval = (__u32*)&frame[nframes]; streaming->format = format; streaming->nformats = nformats; /* Parse the format descriptors. */ while (buflen > 2) { switch (buffer[2]) { case VS_FORMAT_UNCOMPRESSED: case VS_FORMAT_MJPEG: case VS_FORMAT_DV: case VS_FORMAT_FRAME_BASED: format->frame = frame; ret = uvc_parse_format(dev, streaming, format, &interval, buffer, buflen); if (ret < 0) return ret; frame += format->nframes; format++; buflen -= ret; buffer += ret; continue; default: break; } buflen -= buffer[0]; buffer += buffer[0]; } /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_endpoint *ep; alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts, streaming->header.bEndpointAddress); if (ep == NULL) continue; psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); if (psize > streaming->maxpsize) streaming->maxpsize = psize; } return 0;}/* Parse vendor-specific extensions. */static int uvc_parse_vendor_control(struct uvc_device *dev, const unsigned char *buffer, int buflen){ struct usb_device *udev = dev->udev; struct uvc_entity *unit; unsigned int n, p; int handled = 0; switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { case 0x046d: /* Logitech */ if (buffer[1] != 0x41 || buffer[2] != 0x01) break; /* Logitech implements several vendor specific functions * through vendor specific extension units (LXU). * * The LXU descriptors are similar to XU descriptors * (see "USB Device Video Class for Video Devices", section * 3.7.2.6 "Extension Unit Descriptor") with the following * differences: * * ---------------------------------------------------------- * 0 bLength 1 Number * Size of this descriptor, in bytes: 24+p+n*2 * ---------------------------------------------------------- * 23+p+n bmControlsType N Bitmap * Individual bits in the set are defined: * 0: Absolute * 1: Relative * * This bitset is mapped exactly the same as bmControls. * ---------------------------------------------------------- * 23+p+n*2 bReserved 1 Boolean * ---------------------------------------------------------- * 24+p+n*2 iExtension 1 Index * Index of a string descriptor that describes this * extension unit. * ---------------------------------------------------------- */ p = buflen >= 22 ? buffer[21] : 0; n = buflen >= 25 + p ? buffer[22+p] : 0; if (buflen < 25 + p + 2*n) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d EXTENSION_UNIT error\n", udev->devnum, dev->intf->cur_altsetting->desc.bInterfaceNumber); break; } unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); if (unit == NULL) return -ENOMEM; unit->id = buffer[3]; unit->type = VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; unit->extension.bNrInPins = le16_to_cpup((const __le16*)&buffer[21]); unit->extension.baSourceID = (__u8*)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; unit->extension.bmControls = (__u8*)unit + sizeof *unit + p; unit->extension.bmControlsType = (__u8*)unit + sizeof *unit + p + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); if (buffer[24+p+2*n] != 0) usb_string(udev, buffer[24+p+2*n], unit->name, sizeof unit->name); else sprintf(unit->name, "Extension %u", buffer[3]); list_add_tail(&unit->list, &dev->entities); handled = 1; break; } return handled;}static int uvc_parse_control(struct uvc_device *dev){ struct usb_device *udev = dev->udev; struct uvc_streaming *streaming; struct uvc_entity *unit, *term; struct usb_interface *intf; struct usb_host_interface *alts = dev->intf->cur_altsetting; unsigned char *buffer = alts->extra; int buflen = alts->extralen; unsigned int i, n, p, len; __u16 type; /* Parse the default alternate setting only, as the UVC specification * defines a single alternate setting, the default alternate setting * zero. */ while (buflen > 2) { if (uvc_parse_vendor_control(dev, buffer, buflen) || buffer[1] != USB_DT_CS_INTERFACE) goto next_descriptor; switch (buffer[2]) { case VC_HEADER: n = buflen >= 12 ? buffer[11] : 0; if (buflen < 12 || buflen < 12 + n) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d HEADER error\n", udev->devnum, dev->intf->cur_altsetting->desc.bInterfaceNumber); return -EINVAL; } dev->uvc_version = le16_to_cpup((__le16*)&buffer[3]); dev->clock_frequency = le32_to_cpup((__le32*)&buffer[7]); /* Parse all USB Video Streaming interfaces. */ for (i = 0; i < n; ++i) { intf = usb_ifnum_to_if(udev, buffer[12+i]); if (intf == NULL) { uvc_trace(UVC_TRACE_DESCR, "device %d " "interface %d doesn't exists\n", udev->devnum, i); continue; } if (intf->cur_altsetting->desc.bInterfaceSubClass != SC_VIDEOSTREAMING) { uvc_trace(UVC_TRACE_DESCR, "device %d " "interface %d isn't a video " "streaming interface\n", udev->devnum, i); continue; } if (usb_interface_claimed(intf)) { uvc_trace(UVC_TRACE_DESCR, "device %d " "interface %d is already claimed\n", udev->devnum, i); continue; } usb_driver_claim_interface(&uvc_driver.driver, intf, dev); streaming = kzalloc(sizeof *streaming, GFP_KERNEL); if (streaming == NULL) continue; mutex_init(&streaming->mutex); streaming->intf = usb_get_intf(intf); streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; if (uvc_parse_streaming(dev, streaming) < 0) { usb_put_intf(intf); kfree(streaming->format); kfree(streaming->header.bmaControls); kfree(streaming); continue; } list_add_tail(&streaming->list, &dev->streaming); } break; case VC_INPUT_TERMINAL: if (buflen < 8) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL error\n", udev->devnum, dev->intf->cur_altsetting->desc.bInterfaceNumber); return -EINVAL; } /* Make sure the terminal type MSB is not null, otherwise it could be * confused with a unit. */ type = le16_to_cpup((__le16*)&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL %d has invalid " "type 0x%04x, skipping\n", udev->devnum, dev->intf->cur_altsetting->desc.bInterfaceNumber, buffer[3], type); continue; } n = 0; p = 0; len = 8; if (type == ITT_CAMERA) { n = buflen >= 15 ? buffer[14] : 0; len = 15; } else if (type == ITT_MEDIA_TRANSPORT_INPUT) { n = buflen >= 9 ? buffer[8] : 0; p = buflen >= 10 + n ? buffer[9+n] : 0; len = 10; } if (buflen < len + n + p) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL error\n", udev->devnum, dev->intf->cur_altsetting->desc.bInterfaceNumber); return -EINVAL; } term = kzalloc(sizeof *term + n + p, GFP_KERNEL); if (term == NULL) return -ENOMEM; term->id = buffer[3]; term->type = type | UVC_TERM_INPUT; if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8*)term + sizeof *term; term->camera.wObjectiveFocalLengthMin = le16_to_cpup((__le16*)&buffer[8]); term->camera.wObjectiveFocalLengthMax = le16_to_cpup((__le16*)&buffer[10]); term->camera.wOcularFocalLength = le16_to_cpup((__le16*)&buffer[12]); memcpy(term->camera.bmControls, &buffer[15], n);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -