Am 17.02.2014 10:28, schrieb Laurent Pinchart: > Hi Oleksij, > > On Monday 17 February 2014 10:05:41 Oleksij Rempel wrote: >> Am 17.02.2014 01:26, schrieb Laurent Pinchart: >>> On Sunday 16 February 2014 10:59:32 Oleksij Rempel wrote: >>>> Current uvcvideo driver do not correctly disables stream on >>>> Bulk VideoStream EP. This couse problems on a webcam buildin >>>> to Asus Zenbook UX302LA. For example it will be not disabled >>>> and can't be strated after usb port was suspended. >>> >>> s/srated/started/ >>> >>>> Since usb_set_interface can be used only on Iso EP. We need some >>>> way to handle Bulk VS EP. >>>> Widnwos (xp - 8) uvcvideo driver use usb_clear_halt >>> >>> s/Widnwos/Windows/ >>> >>>> in this case. It seems to be correct way to hadle Bulk and Int EPs. >>> >>> s/hadle/handle/ >>> >>> Do you mind if I fix the typos and grammar when applying the patch to my >>> tree ? >> >> It will be great! Thank you :) >> >>>> And it solves this problem on UX302LA ultrabook. >>>> >>>> CC: stable@xxxxxxxxxxxxxxx >>>> Signed-off-by: Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> >>>> --- >>>> >>>> drivers/media/usb/uvc/uvc_video.c | 10 +++++++++- >>>> 1 file changed, 9 insertions(+), 1 deletion(-) >>> >>> I've copied the content of your other e-mail below to keep all discussions >>> in one place. >>> >>>> Hello all, >>>> >>>> i have Asus UX302LA with buildin uvc webcam: 1bcf:2987 Sunplus >>>> Innovation Technology Inc. >>> >>> Could you please send me the lsusb -v output for that camera ? >> >> Suddenly lsusb can't completely decode this descriptor. > > The UVC-specific interface descriptors should come before the endpoint > descriptor. The uvcvideo driver supports both orders, lsusb doesn't seem to. > Patches will be appreciated :-) > >>>> This cam uses bulk stream and has some troubles with current linux uvc >>>> driver. It is driver bug since it works on plain uvcvideo windows driver >>>> (even XP SP2 :)). >>>> >>>> The symptom is fallowing: >>>> - the cam will work first time with all linux apps >>>> - after stream off it will take about 4 seconds untill webcam LED will >>>> go off too. The time is equal to usbautosuspend settings. >>>> - it will be impossible to start webcam second time. Even no reboot to >>>> windows will help. Only complete power off will restore functionality. >>>> - if i disable asbautosuspend, webcam will work, but LED will never go >>>> off even if it is not used. >>>> >>>> It looks like VS interface is not turned off on Linux and in combination >>>> with usb autosuspend, webcam or usb link will go to nirvana. >>> >>> I agree with your analysis. Your camera seems to never stop the stream and >>> to crash when suspended while the stream is active. >>> >>>> After comparing usb log from windows and linux i found that this webcam >>>> need CLEAR HALT of VideoStreaming interface. Linux driver will do only >>>> SET INTERFACE command. Suddenly i don't have other bulk webcams to see >>>> if windows do it with all of them. >>>> >>>> Here is comparison of different webcams and operations which do windows8 >>>> on stream end: >>>> Asus UX302LA (UVC 1.00; Bulk) - 1bcf:2987 Sunplus Innovation Technology >>>> Inc. • CLEAR HALT (Interface=0x1) VS interface >>> >>> The UVC specification doesn't mention clearing the streaming interface >>> control endpoint halt status. The specification actually doesn't state >>> how the host should control stream start/stop for devices that use bulk >>> endpoints. That's a shortcoming of UVC in my opinion >> >> Are there any thing it specification uvc 1.0? I found only 1.1 and 1.5. > > There's a UVC 1.0a. Do you have it? can you please send it me? >> Another documentation from Cypress, "AN75779 How to Implement an Image >> Sensor Interface Using EZ-USB ® FX3TM in a USB Video Class (UVC) >> Framework", paragraph "5.9 Terminating the Video Streaming": >> >> "When the application closes, it issues a clear feature request on a >> Windows platform or a Set Interface with alternate setting = 0 request >> on a Mac platform." >> >> It confirms your assumption that there is no clear way to handle bulk >> stream. > > So I assume your camera won't work on Mac OS. I think Apple don't care about it :) >>> I don't know whether Microsoft has decided to address this issue in their >>> own way by sending a CLEAR_FEATURE(HALT) request when stopping the video >>> stream, or if that's a standard procedure in their UVC driver that hasn't >>> been added specifically for bulk endpoints. Neither the UVC specification >>> nor the USB specification forbid sending a CLEAR_FEATURE(HALT) request to >>> the streaming interface bulk endpoint in this case. >>> >>> It should be noted that the USB specification allows devices to stall a >>> SET_INTERFACE(0) request for an interface that has a single alternate >>> setting (section 9.4.10). In that case the Linux USB core sends a >>> CLEAR_FEATURE(HALT) request to every endpoint of that interface. >> >> I tested this webcam with Windows Xp and 8. It was handled in same way: >> only CLEAR_FEATURE(HALT) was send. >> >>>> Logitech C905: (UVC - 1.00; ISO) >>>> • SET INTERFACE (Interface=1) >>>> • CLEAR HALT (Interface=0x7) Halt interrupt EP >>> >>> The CLEAR_FEATURE(HALT) request is targeted at endpoints, not interfaces. >>> I suppose you mean endpoint 0x87 here (EP 7 IN) ? >> >> yes >> >>> Similarly, with your bulk >>> webcam, to which endpoint is the CLEAR_FEATURE(HALT) request sent ? >> >> EP 1 IN (0x81) >> >>>> Logitech C600: (UVC - 1.00; ISO) >>>> • SET INTERFACE (Interface=1) >>>> • CLEAR HALT (Interface=0x7) Halt interrupt EP >>>> >>>> Logitech E3500: (UVC - 1.00; ISO) >>>> • CLEAR HALT (Interface=0x7) Halt interrupt EP >>>> • SET INTERFACE (Interface=1) (different order then in c600 and c905 ) >>>> >>>> Logitech C920: (UVC 1.00; ISO) >>>> • SET INTERFACE (Interface=1) >>>> • Interrupt is EP 3, why it is not halted? >>>> >>>> Asus UX31A (UVC - 1.00; ISO) - 04f2:b330 Chicony Electronics Co., Ltd >>>> Asus 720p CMOS webcam >>>> • SET INTERFACE (Interface=1) >>>> • Interrupt is EP 3, why it is not halted? >>>> >>>> So far it looks like Windows8 use CLEAR HALT for Bulk and SET INTERFACE >>>> for Iso. >>> >>> What about Windows XP and Windows 7 ? Do they use the same scheme ? >> >> Windows XP uses only SET INTERFACE on this webcam. Windows 7 not tested >> for now - should i? > > If Windows XP and Windows 8 behave the same way I suppose we can skip Windows > 7. > >>>> But i don't understand why Windows8 trying to halt Interrupt if EP=7 and >>>> not doing that if EP=3? >>> >>> No idea... I wonder if the Windows UVC driver includes device quirks. >> >> Yes, Windows 8 knows more about this webcam's then i would like to. For >> example it uses intensively Logitech specific XUs (without installing >> Logitech software): Right Light registers; some think what looks like >> watch dog. May be it knows how to disable LED :) NSA friendly system... > > Nice... If you've reversed-engineered RightLight information would be welcome > :-) That was easy: https://wikidevi.com/wiki/82066163-7050-ab49-b8cc-b3855e8d2252 See attachment with a testprogram. >>>> diff --git a/drivers/media/usb/uvc/uvc_video.c >>>> b/drivers/media/usb/uvc/uvc_video.c index 898c208..9d335c0 100644 >>>> --- a/drivers/media/usb/uvc/uvc_video.c >>>> +++ b/drivers/media/usb/uvc/uvc_video.c >>>> @@ -1847,7 +1847,15 @@ int uvc_video_enable(struct uvc_streaming *stream, >>>> int enable) >>>> >>>> if (!enable) { >>>> uvc_uninit_video(stream, 1); >>>> - usb_set_interface(stream->dev->udev, stream->intfnum, 0); >>>> + if (stream->intf->num_altsetting > 1) { >>>> + usb_set_interface(stream->dev->udev, >>>> + stream->intfnum, 0); >>>> + } else { >>>> + usb_clear_halt(stream->dev->udev, >>>> + usb_rcvctrlpipe(stream->dev->udev, >>>> + stream->intfnum)); >>> >>> usb_rcvctrlpipe() expects an endpoint number as its second argument, not >>> an interface number. The following call should do. >>> >>> usb_clear_halt(stream->dev->udev, >>> stream->header.bEndpointAddress); >>> >>> We probably need to validate the streaming header bEndpointAddress field >>> when probing the device to ensure that we won't call usb_clear_halt() on >>> an unrelated endpoint. >>> >>>> + } >>>> + >>> >>> This might cause a regression with other bulk devices that expect a >>> SET_INTERFACE(0), or don't expect a CLEAR_FEATURE(HALT), although I >>> suppose those devices wouldn't work with Windows then. I've tested you >>> patch (with the above modification) with the only bulk device I own and it >>> doesn't seem to cause any regression. >> >> I assume, my cam was produced after XP uvc driver. So it should be >> default behaviour. > > What would you think about calling both usb_set_interface() and > usb_clear_halt() for bulk-based devices ? Something like > > - usb_set_interface(stream->dev->udev, stream->intfnum, 0); > + if (stream->intf->num_altsetting == 1) > + usb_clear_halt(stream->dev->udev, > + stream->header.bEndpointAddress); > It works too. I was just afraid to be too different form MS implementation. Manufactures usually test it against MS driver so it sort of dictate specification. -- Regards, Oleksij
/* * XU example for uvcvideo driver. * All parameters based on /usr/share/uvcdynctrl/data/046d/logitech.xml * and tested with Logitech, Inc. QuickCam Pro for Notebooks (046d:0991) * Use it on your own risk, it can damage you device. * * Copyright (C) 2012 * Oleksij Rempel <bug-track@xxxxxxxxxxxxxxxxx> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Description: * Most Logitech Cams support so called "RightLight Technology" * It is software based multi field mesuring and adopted exposure * settings. * The thecnology works as fallow: * the image on the cam will splitted to 8 fields, each field has two parts. * ----------------- To get the light intensity of one field, * | | | | | host should SET the adress of field and then * | c4 | c5 | GET the result. The result has two bytes, * ----------------- first byte is the left part of the field, * | | | | | sesond byte is the right part of the field. * | c6 | c7 | * ----------------- * | | | | | * | c8 | c9 | * ----------------- * | | | | | * | ca | cb | * ----------------- */ #include <unistd.h> #include <stdint.h> #include <linux/uvcvideo.h> #include <linux/videodev2.h> #include <linux/usb/video.h> #include <assert.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <stdlib.h> #define DEVVIDEO "/dev/video0" #define EXPOSURE_STEP 5 #define MIN_EXPOSURE 50 #define MAX_EXPOSURE 300 #define UVC_UID_LOGITECH_USER_HW_CONTROL 10 /* This is the GUID of XU repsoinble for mesuring * 82066163-7050-ab49-b8cc-b3855e8d2252. * currently not used. */ #define UVC_XU_GUID \ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x52} /* With CS 5 we set the adress of field * On CS 6 we get results. */ #define XU_CS_MESURE_FIELD 5 #define XU_CS_MESURE_FIELD_RET 6 /* for some unknown reason we need to send two bytes: * filed number and 0x02.*/ #define MESURE_FIELDS \ const uint16_t logitech_field_2x4[8] = { 0x02c4, 0x02c5, 0x02c6, 0x02c7, 0x02c8, 0x02c9, 0x02ca, 0x02cb}; const uint16_t logitech_field_2x2[4] = { 0x02c6, 0x02c7, 0x02c8, 0x02c9,}; struct dyn_exposure { int configured; const uint16_t *fields; unsigned int fields_count; unsigned int width; unsigned int width_offset; unsigned int height; uint8_t results[16]; unsigned int result_size; unsigned int result_width; int exp_state; int fd; struct uvc_xu_control_query xu_set; struct uvc_xu_control_query xu_get; }; static int xioctl(int fd, int request, void *arg) { int ret = ioctl(fd, request, arg); if (ret == -1) { /* check the errno, it can be standart ioctl error or * errno provided by uvcvideo. For possible errors see: * linux/drivers/media/video/uvc/uvc_ctrl.c: uvc_xu_ctrl_query * and linux/include/asm-generic/errno.h */ ret = errno; printf("filed with errno: %d\n", ret); } return ret; } static void set_exposure(int fd, int exp) { struct v4l2_control ctrl; /* check if we use manual exposure * if not set it. */ ctrl.id = V4L2_CID_EXPOSURE_AUTO; ctrl.value = 0; xioctl(fd, VIDIOC_G_CTRL, &ctrl); if (ctrl.value != 1) { ctrl.value = 1; xioctl(fd, VIDIOC_S_CTRL, &ctrl); printf ("Setting manual exposure\n"); } ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE; ctrl.value = 0; xioctl(fd, VIDIOC_G_CTRL, &ctrl); printf (" --- %03u\n", ctrl.value); /* */ if (exp < 0) { ctrl.value -= EXPOSURE_STEP; if (ctrl.value < MIN_EXPOSURE) ctrl.value = MIN_EXPOSURE; xioctl(fd, VIDIOC_S_CTRL, &ctrl); } else if (exp > 0) { ctrl.value += EXPOSURE_STEP; if (ctrl.value < MAX_EXPOSURE) ctrl.value = MAX_EXPOSURE; xioctl(fd, VIDIOC_S_CTRL, &ctrl); } } static void config_loigitech_4x4 (struct dyn_exposure *priv) { priv->fields = (const uint16_t *)&logitech_field_2x4; priv->fields_count = sizeof(logitech_field_2x4)/sizeof(uint16_t); priv->width = 4; priv->width_offset = 0; priv->height = 4; priv->result_size = priv->fields_count * 2; priv->result_width = priv->result_size / priv->height; } static void config_loigitech_4x2 (struct dyn_exposure *priv) { priv->fields = (const uint16_t *)&logitech_field_2x2; priv->fields_count = sizeof(logitech_field_2x2)/sizeof(uint16_t); priv->width = 4; priv->width_offset = 0; priv->height = 2; priv->result_size = priv->fields_count * 2; priv->result_width = priv->result_size / priv->height; } static void config_loigitech_2x2 (struct dyn_exposure *priv) { priv->fields = (const uint16_t *)&logitech_field_2x2; priv->fields_count = sizeof(logitech_field_2x2)/sizeof(uint16_t); priv->width = 2; priv->width_offset = 1; priv->height = 2; priv->result_size = priv->fields_count * 2; priv->result_width = priv->result_size / priv->height; } static void init_xu(struct dyn_exposure *priv) { /* prepare set_ struct */ priv->xu_set.unit = UVC_UID_LOGITECH_USER_HW_CONTROL; priv->xu_set.selector = XU_CS_MESURE_FIELD; priv->xu_set.query = UVC_SET_CUR; priv->xu_set.size = 2; /* prepare get struct */ priv->xu_get.unit = UVC_UID_LOGITECH_USER_HW_CONTROL; priv->xu_get.selector = XU_CS_MESURE_FIELD_RET; priv->xu_get.query = UVC_GET_CUR; priv->xu_get.size = 2; } static void get_xu_fileds(struct dyn_exposure *priv) { int i8, i16; /* 8 - fileds, 16 - mesure squers */ i16 = 0; for (i8 = 0; i8 < 8; i8++) { priv->xu_set.data = (unsigned char*)&priv->fields[i8]; xioctl(priv->fd, UVCIOC_CTRL_QUERY, (void *)&priv->xu_set); priv->xu_get.data = (unsigned char*)&priv->results[i16]; xioctl(priv->fd, UVCIOC_CTRL_QUERY, (void *)&priv->xu_get); i16 += 2; } } /* Easy and naiv precessor. */ static void process_fields (struct dyn_exposure *priv) { int r; unsigned int a, b, c; unsigned long int avarage; a = r = 0; avarage = 0; while (a < priv->result_size) { b = a + priv->width_offset; c = b + priv->width; while (b < c) { if (priv->results[b] < 0x30) { r -= 1; // underexposed } else if (priv->results[b] > 0x60) r += 2; // over exposed else r += 1; // ok avarage += priv->results[b]; printf("| (%02u)%03u ", b, priv->results[b]); b++; } printf("|\n"); a += priv->result_width; } if (r > 18) { printf ("over exposed\n"); set_exposure (priv->fd, 1); } else if (r < 10) { printf ("underexposed \n"); set_exposure (priv->fd, -1); } else printf ("ok\n"); } int main(void) { int fd = open(DEVVIDEO, O_RDWR); assert(fd > 0); struct dyn_exposure priv; priv.fd = fd; init_xu (&priv); // we can choise differnt configuration // to mesure light. config_loigitech_4x4(&priv); //config_loigitech_2x4(&priv); //config_loigitech_2x2(&priv); for(;;) { get_xu_fileds (&priv); process_fields(&priv); usleep(500000); } close(fd); return 0; }
Attachment:
signature.asc
Description: OpenPGP digital signature