[New Driver]: usbvideo2 webcam core + pac207 driver using it.

Brandon Philips brandon at ifup.org
Thu Apr 3 21:27:28 UTC 2008


On 22:53 Fri 28 Mar 2008, Hans de Goede wrote:
>  I'm currently posting these as .c files for easy reading and
>  compilation / testing, but I still hope to get a lot of feedback / a
>  thorough review, esp of the core <-> pac207 split version as I hope
>  to submit that as a patch for mainline inclusion soon.

The driver look pretty good.  Comments inline.

> struct pac207_decompress_table_t {
> 	u8 is_abs;
> 	u8 len;
> 	s8 val;
> };

Why add the _t?

> int pac207_read_reg(struct usbvideo2_device* cam, u16 index)
> {
> 	struct usb_device* udev = cam->usbdev;
> 	u8* buff = cam->control_buffer;
> 	int res;
> 
> 	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
> 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
> 			0x00, index, buff, 1, USBVIDEO2_CTRL_TIMEOUT);
> 	if (res < 0)
> 		DBG(1, "Failed to read a register (index 0x%04X, error %d)",
> 			index, res);
> 
> 	return (res >= 0) ? (int)(*buff) : res;

Why not do the obvious thing and return from the if (res < 0) statement?

> /*****************************************************************************/
> 
> /* auto gain and exposure algorithm based on the knee algorithm described here:
>    http://ytse.tricolour.net/docs/LowLightOptimization.html */

URL is dead.

> #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))

Add a comment about what this is doing?  Could you just do it as a
static function instead?

> static int
> pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
> {
> 	struct usbvideo2_device* cam = fh;
> 	struct pac207_data *data = cam->cam_data;
> 	int new_value = a->value;
> 	int err;
> 
> 	if ((err = pac207_vidioc_g_ctrl(file, fh, a)))
> 		return err;
> 
> 	if (a->value == new_value)
> 		return 0;

This all needs some locking to protect from multi-threaded applications.
Otherwise the hardware and data structures could be in two different
states.

> 	/* don't allow mucking with gain / exposure when using autogain */
> 	if (data->autogain && (a->id == V4L2_CID_GAIN ||
> 			a->id == V4L2_CID_EXPOSURE))
> 		return -EINVAL;
> 
> 	switch (a->id) {
> 		case V4L2_CID_BRIGHTNESS:
> 			data->brightness = new_value;
> 			pac207_write_reg(cam, 0x0008, data->brightness);
> 			/* give brightness change time to take effect before
> 			   doing autogain based on the new brightness */
> 			data->autogain_ignore_frames =
> 						PAC207_AUTOGAIN_IGNORE_FRAMES;
> 			break;
> 
> 		case V4L2_CID_EXPOSURE:
> 			data->exposure = new_value;
> 			pac207_write_reg(cam, 0x0002, data->exposure);
> 			break;
> 
> 		case V4L2_CID_AUTOGAIN:
> 			data->autogain = new_value;
> 			/* when switching to autogain set defaults to make sure
> 			   we are on a valid point of the autogain gain /
> 			   exposure knee graph, and give this change time to
> 			   take effect before doing autogain. */
> 			if (data->autogain) {
> 				data->exposure = PAC207_EXPOSURE_DEFAULT;
> 				data->gain = PAC207_GAIN_DEFAULT;
> 				data->autogain_ignore_frames =
> 						PAC207_AUTOGAIN_IGNORE_FRAMES;
> 				pac207_write_reg(cam, 0x0002, data->exposure);
> 				pac207_write_reg(cam, 0x000e, data->gain);
> 			}
> 			break;
> 
> 		case V4L2_CID_GAIN:
> 			data->gain = new_value;
> 			pac207_write_reg(cam, 0x000e, data->gain);
> 			break;
> 	
> 		/* no default needed already checked in pac207_vidioc_g_ctrl */
> 	}
> 
> 	pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor */
> 	pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
> 
> 	return 0;
> }

> static void usbvideo2_urb_complete(struct urb *urb)
> {
> 	struct usbvideo2_device* cam = urb->context;
> 	struct usbvideo2_frame_t** f;
> 	int i, ret;
> 
> 	switch (urb->status) {
> 		case 0:
> 			break;
> 		case -ENOENT:		/* usb_kill_urb() called. */
> 		case -ECONNRESET:	/* usb_unlink_urb() called. */
> 		case -ESHUTDOWN:	/* The endpoint is being disabled. */
> 			return;
> 		default:
> 			goto resubmit_urb;
> 	}
> 
> 	f = &cam->frame_current;
> 
> 	if (!(*f)) {
> 		if (list_empty(&cam->inqueue))
> 			goto resubmit_urb;
> 
> 		(*f) = list_entry(cam->inqueue.next, struct usbvideo2_frame_t,
> 					frame);
> 	} 

Don't you want to take a spinlock here?  Most accesses of inqueue seem
to take a spinlock.

> static ssize_t
> usbvideo2_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
> {
> 	struct usbvideo2_device *cam = filp->private_data;
> 	struct usbvideo2_frame_t *f;
> 	unsigned long lock_flags;
> 	long timeout;
> 	int err = 0;
> 
> 
> 	if (cam->disconnected) {
> 		err = -ENODEV;
> 		goto out;
> 	}
> 
> 	if (cam->io == IO_MMAP) {
> 		DBG(2, "Close and open the device again to choose the read "
> 			"method");
> 		err = -EBUSY;
> 		goto out;
> 	}
> 
> static void usbvideo2_vm_close(struct vm_area_struct* vma)
> {
> 	/* NOTE: buffers are not freed here */

Why is that worth noting?

> 	struct usbvideo2_frame_t* f = vma->vm_private_data;
> 	f->vma_use_count--;
> }
> 
> 
> static int
> usbvideo2_vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
> {
> 	u32 i;
> 	struct usbvideo2_device* cam = fh;
> 
> 	if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
> 			b->memory != V4L2_MEMORY_MMAP)
> 		return -EINVAL;
> 
> 	if (cam->io == IO_READ) {
> 		DBG(2, "Close and open the device again to choose the "
> 			"mmap I/O method");
> 		return -EBUSY;
> 	}

Again, why is close then open required?

> static int
> usbvideo2_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> {
> 	struct usbvideo2_device* cam = fh;
> 	struct usbvideo2_frame_t *f;
> 	unsigned long lock_flags;
> 	long timeout;
> 
> 	if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP ||
> 			cam->stream == STREAM_OFF)
> 		return -EINVAL;
> 
> 	if (list_empty(&cam->outqueue)) {
> 		if (file->f_flags & O_NONBLOCK)
> 			return -EAGAIN;
> 
> 		timeout = wait_event_interruptible_timeout(cam->wait_frame,
> 				!list_empty(&cam->outqueue) ||
> 				cam->disconnected,
> 				msecs_to_jiffies(USBVIDEO2_FRAME_TIMEOUT) );
> 		if (cam->disconnected)
> 			return -ENODEV;
> 
> 		if (timeout <= 0)
> 			return (timeout < 0)? timeout : -EIO;
> 	}

Where is the locking?  What happens if two threads call dqbuf on this
device at the same time?  

> 	if (cam->funcs->frame_dequeued)
> 		cam->funcs->frame_dequeued(cam);
> 
> 	spin_lock_irqsave(&cam->queue_lock, lock_flags);

You will probably hit LIST_POISON on a second thread because of the list
being empty when it comes through.

> 	f = list_entry(cam->outqueue.next, struct usbvideo2_frame_t, frame);
> 	list_del(cam->outqueue.next);
> 	spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
> 
> 	f->state = F_UNUSED;
> 
> 	memcpy(b, &f->buf, sizeof(*b));
> 	if (f->vma_use_count)
> 		b->flags |= V4L2_BUF_FLAG_MAPPED;
> 
> 	return 0;
> }

> struct usbvideo2_frame_t {
> 	void* bufmem;
> 	struct v4l2_buffer buf;
> 	enum usbvideo2_frame_state state;
> 	struct list_head frame;
> 	unsigned long vma_use_count;
> };

Why the _t on the end?

Thanks,

	Brandon




More information about the Fedora-kernel-list mailing list