diff options
Diffstat (limited to 'drivers/usb/gadget/uvc_v4l2.c')
-rw-r--r-- | drivers/usb/gadget/uvc_v4l2.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c new file mode 100644 index 000000000000..a7989f29837e --- /dev/null +++ b/drivers/usb/gadget/uvc_v4l2.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * uvc_v4l2.c -- USB Video Class Gadget driver | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 | ||
5 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/version.h> | ||
20 | #include <linux/videodev2.h> | ||
21 | #include <linux/vmalloc.h> | ||
22 | #include <linux/wait.h> | ||
23 | |||
24 | #include <media/v4l2-dev.h> | ||
25 | #include <media/v4l2-event.h> | ||
26 | #include <media/v4l2-ioctl.h> | ||
27 | |||
28 | #include "uvc.h" | ||
29 | #include "uvc_queue.h" | ||
30 | |||
31 | /* -------------------------------------------------------------------------- | ||
32 | * Requests handling | ||
33 | */ | ||
34 | |||
35 | static int | ||
36 | uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) | ||
37 | { | ||
38 | struct usb_composite_dev *cdev = uvc->func.config->cdev; | ||
39 | struct usb_request *req = uvc->control_req; | ||
40 | |||
41 | if (data->length < 0) | ||
42 | return usb_ep_set_halt(cdev->gadget->ep0); | ||
43 | |||
44 | req->length = min(uvc->event_length, data->length); | ||
45 | req->zero = data->length < uvc->event_length; | ||
46 | req->dma = DMA_ADDR_INVALID; | ||
47 | |||
48 | memcpy(req->buf, data->data, data->length); | ||
49 | |||
50 | return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL); | ||
51 | } | ||
52 | |||
53 | /* -------------------------------------------------------------------------- | ||
54 | * V4L2 | ||
55 | */ | ||
56 | |||
57 | struct uvc_format | ||
58 | { | ||
59 | u8 bpp; | ||
60 | u32 fcc; | ||
61 | }; | ||
62 | |||
63 | static struct uvc_format uvc_formats[] = { | ||
64 | { 16, V4L2_PIX_FMT_YUYV }, | ||
65 | { 0, V4L2_PIX_FMT_MJPEG }, | ||
66 | }; | ||
67 | |||
68 | static int | ||
69 | uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) | ||
70 | { | ||
71 | fmt->fmt.pix.pixelformat = video->fcc; | ||
72 | fmt->fmt.pix.width = video->width; | ||
73 | fmt->fmt.pix.height = video->height; | ||
74 | fmt->fmt.pix.field = V4L2_FIELD_NONE; | ||
75 | fmt->fmt.pix.bytesperline = video->bpp * video->width / 8; | ||
76 | fmt->fmt.pix.sizeimage = video->imagesize; | ||
77 | fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; | ||
78 | fmt->fmt.pix.priv = 0; | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int | ||
84 | uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) | ||
85 | { | ||
86 | struct uvc_format *format; | ||
87 | unsigned int imagesize; | ||
88 | unsigned int bpl; | ||
89 | unsigned int i; | ||
90 | |||
91 | for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) { | ||
92 | format = &uvc_formats[i]; | ||
93 | if (format->fcc == fmt->fmt.pix.pixelformat) | ||
94 | break; | ||
95 | } | ||
96 | |||
97 | if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { | ||
98 | printk(KERN_INFO "Unsupported format 0x%08x.\n", | ||
99 | fmt->fmt.pix.pixelformat); | ||
100 | return -EINVAL; | ||
101 | } | ||
102 | |||
103 | bpl = format->bpp * fmt->fmt.pix.width / 8; | ||
104 | imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage; | ||
105 | |||
106 | video->fcc = format->fcc; | ||
107 | video->bpp = format->bpp; | ||
108 | video->width = fmt->fmt.pix.width; | ||
109 | video->height = fmt->fmt.pix.height; | ||
110 | video->imagesize = imagesize; | ||
111 | |||
112 | fmt->fmt.pix.field = V4L2_FIELD_NONE; | ||
113 | fmt->fmt.pix.bytesperline = bpl; | ||
114 | fmt->fmt.pix.sizeimage = imagesize; | ||
115 | fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; | ||
116 | fmt->fmt.pix.priv = 0; | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static int | ||
122 | uvc_v4l2_open(struct file *file) | ||
123 | { | ||
124 | struct video_device *vdev = video_devdata(file); | ||
125 | struct uvc_device *uvc = video_get_drvdata(vdev); | ||
126 | struct uvc_file_handle *handle; | ||
127 | int ret; | ||
128 | |||
129 | handle = kzalloc(sizeof(*handle), GFP_KERNEL); | ||
130 | if (handle == NULL) | ||
131 | return -ENOMEM; | ||
132 | |||
133 | ret = v4l2_fh_init(&handle->vfh, vdev); | ||
134 | if (ret < 0) | ||
135 | goto error; | ||
136 | |||
137 | ret = v4l2_event_init(&handle->vfh); | ||
138 | if (ret < 0) | ||
139 | goto error; | ||
140 | |||
141 | ret = v4l2_event_alloc(&handle->vfh, 8); | ||
142 | if (ret < 0) | ||
143 | goto error; | ||
144 | |||
145 | v4l2_fh_add(&handle->vfh); | ||
146 | |||
147 | handle->device = &uvc->video; | ||
148 | file->private_data = &handle->vfh; | ||
149 | |||
150 | uvc_function_connect(uvc); | ||
151 | return 0; | ||
152 | |||
153 | error: | ||
154 | v4l2_fh_exit(&handle->vfh); | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | static int | ||
159 | uvc_v4l2_release(struct file *file) | ||
160 | { | ||
161 | struct video_device *vdev = video_devdata(file); | ||
162 | struct uvc_device *uvc = video_get_drvdata(vdev); | ||
163 | struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); | ||
164 | struct uvc_video *video = handle->device; | ||
165 | |||
166 | uvc_function_disconnect(uvc); | ||
167 | |||
168 | uvc_video_enable(video, 0); | ||
169 | mutex_lock(&video->queue.mutex); | ||
170 | if (uvc_free_buffers(&video->queue) < 0) | ||
171 | printk(KERN_ERR "uvc_v4l2_release: Unable to free " | ||
172 | "buffers.\n"); | ||
173 | mutex_unlock(&video->queue.mutex); | ||
174 | |||
175 | file->private_data = NULL; | ||
176 | v4l2_fh_del(&handle->vfh); | ||
177 | v4l2_fh_exit(&handle->vfh); | ||
178 | kfree(handle); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static long | ||
183 | uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | ||
184 | { | ||
185 | struct video_device *vdev = video_devdata(file); | ||
186 | struct uvc_device *uvc = video_get_drvdata(vdev); | ||
187 | struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); | ||
188 | struct usb_composite_dev *cdev = uvc->func.config->cdev; | ||
189 | struct uvc_video *video = &uvc->video; | ||
190 | int ret = 0; | ||
191 | |||
192 | switch (cmd) { | ||
193 | /* Query capabilities */ | ||
194 | case VIDIOC_QUERYCAP: | ||
195 | { | ||
196 | struct v4l2_capability *cap = arg; | ||
197 | |||
198 | memset(cap, 0, sizeof *cap); | ||
199 | strncpy(cap->driver, "g_uvc", sizeof(cap->driver)); | ||
200 | strncpy(cap->card, cdev->gadget->name, sizeof(cap->card)); | ||
201 | strncpy(cap->bus_info, dev_name(&cdev->gadget->dev), | ||
202 | sizeof cap->bus_info); | ||
203 | cap->version = DRIVER_VERSION_NUMBER; | ||
204 | cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; | ||
205 | break; | ||
206 | } | ||
207 | |||
208 | /* Get & Set format */ | ||
209 | case VIDIOC_G_FMT: | ||
210 | { | ||
211 | struct v4l2_format *fmt = arg; | ||
212 | |||
213 | if (fmt->type != video->queue.type) | ||
214 | return -EINVAL; | ||
215 | |||
216 | return uvc_v4l2_get_format(video, fmt); | ||
217 | } | ||
218 | |||
219 | case VIDIOC_S_FMT: | ||
220 | { | ||
221 | struct v4l2_format *fmt = arg; | ||
222 | |||
223 | if (fmt->type != video->queue.type) | ||
224 | return -EINVAL; | ||
225 | |||
226 | return uvc_v4l2_set_format(video, fmt); | ||
227 | } | ||
228 | |||
229 | /* Buffers & streaming */ | ||
230 | case VIDIOC_REQBUFS: | ||
231 | { | ||
232 | struct v4l2_requestbuffers *rb = arg; | ||
233 | |||
234 | if (rb->type != video->queue.type || | ||
235 | rb->memory != V4L2_MEMORY_MMAP) | ||
236 | return -EINVAL; | ||
237 | |||
238 | ret = uvc_alloc_buffers(&video->queue, rb->count, | ||
239 | video->imagesize); | ||
240 | if (ret < 0) | ||
241 | return ret; | ||
242 | |||
243 | rb->count = ret; | ||
244 | ret = 0; | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | case VIDIOC_QUERYBUF: | ||
249 | { | ||
250 | struct v4l2_buffer *buf = arg; | ||
251 | |||
252 | if (buf->type != video->queue.type) | ||
253 | return -EINVAL; | ||
254 | |||
255 | return uvc_query_buffer(&video->queue, buf); | ||
256 | } | ||
257 | |||
258 | case VIDIOC_QBUF: | ||
259 | if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0) | ||
260 | return ret; | ||
261 | |||
262 | return uvc_video_pump(video); | ||
263 | |||
264 | case VIDIOC_DQBUF: | ||
265 | return uvc_dequeue_buffer(&video->queue, arg, | ||
266 | file->f_flags & O_NONBLOCK); | ||
267 | |||
268 | case VIDIOC_STREAMON: | ||
269 | { | ||
270 | int *type = arg; | ||
271 | |||
272 | if (*type != video->queue.type) | ||
273 | return -EINVAL; | ||
274 | |||
275 | return uvc_video_enable(video, 1); | ||
276 | } | ||
277 | |||
278 | case VIDIOC_STREAMOFF: | ||
279 | { | ||
280 | int *type = arg; | ||
281 | |||
282 | if (*type != video->queue.type) | ||
283 | return -EINVAL; | ||
284 | |||
285 | return uvc_video_enable(video, 0); | ||
286 | } | ||
287 | |||
288 | /* Events */ | ||
289 | case VIDIOC_DQEVENT: | ||
290 | { | ||
291 | struct v4l2_event *event = arg; | ||
292 | |||
293 | ret = v4l2_event_dequeue(&handle->vfh, event, | ||
294 | file->f_flags & O_NONBLOCK); | ||
295 | if (ret == 0 && event->type == UVC_EVENT_SETUP) { | ||
296 | struct uvc_event *uvc_event = (void *)&event->u.data; | ||
297 | |||
298 | /* Tell the complete callback to generate an event for | ||
299 | * the next request that will be enqueued by | ||
300 | * uvc_event_write. | ||
301 | */ | ||
302 | uvc->event_setup_out = | ||
303 | !(uvc_event->req.bRequestType & USB_DIR_IN); | ||
304 | uvc->event_length = uvc_event->req.wLength; | ||
305 | } | ||
306 | |||
307 | return ret; | ||
308 | } | ||
309 | |||
310 | case VIDIOC_SUBSCRIBE_EVENT: | ||
311 | { | ||
312 | struct v4l2_event_subscription *sub = arg; | ||
313 | |||
314 | if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) | ||
315 | return -EINVAL; | ||
316 | |||
317 | return v4l2_event_subscribe(&handle->vfh, arg); | ||
318 | } | ||
319 | |||
320 | case VIDIOC_UNSUBSCRIBE_EVENT: | ||
321 | return v4l2_event_unsubscribe(&handle->vfh, arg); | ||
322 | |||
323 | case UVCIOC_SEND_RESPONSE: | ||
324 | ret = uvc_send_response(uvc, arg); | ||
325 | break; | ||
326 | |||
327 | default: | ||
328 | return -ENOIOCTLCMD; | ||
329 | } | ||
330 | |||
331 | return ret; | ||
332 | } | ||
333 | |||
334 | static long | ||
335 | uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
336 | { | ||
337 | return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); | ||
338 | } | ||
339 | |||
340 | static int | ||
341 | uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) | ||
342 | { | ||
343 | struct video_device *vdev = video_devdata(file); | ||
344 | struct uvc_device *uvc = video_get_drvdata(vdev); | ||
345 | |||
346 | return uvc_queue_mmap(&uvc->video.queue, vma); | ||
347 | } | ||
348 | |||
349 | static unsigned int | ||
350 | uvc_v4l2_poll(struct file *file, poll_table *wait) | ||
351 | { | ||
352 | struct video_device *vdev = video_devdata(file); | ||
353 | struct uvc_device *uvc = video_get_drvdata(vdev); | ||
354 | struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); | ||
355 | unsigned int mask = 0; | ||
356 | |||
357 | poll_wait(file, &handle->vfh.events->wait, wait); | ||
358 | if (v4l2_event_pending(&handle->vfh)) | ||
359 | mask |= POLLPRI; | ||
360 | |||
361 | mask |= uvc_queue_poll(&uvc->video.queue, file, wait); | ||
362 | |||
363 | return mask; | ||
364 | } | ||
365 | |||
366 | struct v4l2_file_operations uvc_v4l2_fops = { | ||
367 | .owner = THIS_MODULE, | ||
368 | .open = uvc_v4l2_open, | ||
369 | .release = uvc_v4l2_release, | ||
370 | .ioctl = uvc_v4l2_ioctl, | ||
371 | .mmap = uvc_v4l2_mmap, | ||
372 | .poll = uvc_v4l2_poll, | ||
373 | }; | ||
374 | |||