2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2010-05-02 20:57:41 +02:00
/*
* uvc_v4l2 . c - - USB Video Class Gadget driver
*
* Copyright ( C ) 2009 - 2010
* Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*/
# include <linux/device.h>
# include <linux/errno.h>
2018-05-21 11:28:52 +03:00
# include <linux/kernel.h>
2010-05-02 20:57:41 +02:00
# include <linux/list.h>
2018-05-21 11:28:52 +03:00
# include <linux/usb/g_uvc.h>
2010-05-02 20:57:41 +02:00
# include <linux/videodev2.h>
# include <linux/vmalloc.h>
# include <linux/wait.h>
# include <media/v4l2-dev.h>
# include <media/v4l2-event.h>
# include <media/v4l2-ioctl.h>
2014-09-09 02:02:09 +03:00
# include "f_uvc.h"
2010-05-02 20:57:41 +02:00
# include "uvc.h"
# include "uvc_queue.h"
2014-09-09 02:02:09 +03:00
# include "uvc_video.h"
2015-02-05 13:16:26 +00:00
# include "uvc_v4l2.h"
2010-05-02 20:57:41 +02:00
/* --------------------------------------------------------------------------
* Requests handling
*/
static 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 ;
if ( data - > length < 0 )
return usb_ep_set_halt ( cdev - > gadget - > ep0 ) ;
2012-04-24 11:29:42 +02:00
req - > length = min_t ( unsigned int , uvc - > event_length , data - > length ) ;
2010-05-02 20:57:41 +02:00
req - > zero = data - > length < uvc - > event_length ;
2013-03-14 11:01:05 +03:00
memcpy ( req - > buf , data - > data , req - > length ) ;
2010-05-02 20:57:41 +02:00
return usb_ep_queue ( cdev - > gadget - > ep0 , req , GFP_KERNEL ) ;
}
/* --------------------------------------------------------------------------
2014-09-08 11:18:15 +03:00
* V4L2 ioctls
2010-05-02 20:57:41 +02:00
*/
2016-11-11 19:07:01 -05:00
struct uvc_format {
2010-05-02 20:57:41 +02:00
u8 bpp ;
u32 fcc ;
} ;
static struct uvc_format uvc_formats [ ] = {
{ 16 , V4L2_PIX_FMT_YUYV } ,
{ 0 , V4L2_PIX_FMT_MJPEG } ,
} ;
static int
2014-09-08 11:18:15 +03:00
uvc_v4l2_querycap ( struct file * file , void * fh , struct v4l2_capability * cap )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct usb_composite_dev * cdev = uvc - > func . config - > cdev ;
strlcpy ( cap - > driver , " g_uvc " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > card , cdev - > gadget - > name , sizeof ( cap - > card ) ) ;
strlcpy ( cap - > bus_info , dev_name ( & cdev - > gadget - > dev ) ,
sizeof ( cap - > bus_info ) ) ;
return 0 ;
}
static int
uvc_v4l2_get_format ( struct file * file , void * fh , struct v4l2_format * fmt )
2010-05-02 20:57:41 +02:00
{
2014-09-08 11:18:15 +03:00
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_video * video = & uvc - > video ;
2010-05-02 20:57:41 +02:00
fmt - > fmt . pix . pixelformat = video - > fcc ;
fmt - > fmt . pix . width = video - > width ;
fmt - > fmt . pix . height = video - > height ;
fmt - > fmt . pix . field = V4L2_FIELD_NONE ;
fmt - > fmt . pix . bytesperline = video - > bpp * video - > width / 8 ;
fmt - > fmt . pix . sizeimage = video - > imagesize ;
fmt - > fmt . pix . colorspace = V4L2_COLORSPACE_SRGB ;
fmt - > fmt . pix . priv = 0 ;
return 0 ;
}
static int
2014-09-08 11:18:15 +03:00
uvc_v4l2_set_format ( struct file * file , void * fh , struct v4l2_format * fmt )
2010-05-02 20:57:41 +02:00
{
2014-09-08 11:18:15 +03:00
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_video * video = & uvc - > video ;
2010-05-02 20:57:41 +02:00
struct uvc_format * format ;
unsigned int imagesize ;
unsigned int bpl ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( uvc_formats ) ; + + i ) {
format = & uvc_formats [ i ] ;
if ( format - > fcc = = fmt - > fmt . pix . pixelformat )
break ;
}
2010-08-12 09:59:58 +02:00
if ( i = = ARRAY_SIZE ( uvc_formats ) ) {
2018-08-10 15:48:02 +03:00
uvcg_info ( & uvc - > func , " Unsupported format 0x%08x. \n " ,
fmt - > fmt . pix . pixelformat ) ;
2010-05-02 20:57:41 +02:00
return - EINVAL ;
}
bpl = format - > bpp * fmt - > fmt . pix . width / 8 ;
imagesize = bpl ? bpl * fmt - > fmt . pix . height : fmt - > fmt . pix . sizeimage ;
video - > fcc = format - > fcc ;
video - > bpp = format - > bpp ;
video - > width = fmt - > fmt . pix . width ;
video - > height = fmt - > fmt . pix . height ;
video - > imagesize = imagesize ;
fmt - > fmt . pix . field = V4L2_FIELD_NONE ;
fmt - > fmt . pix . bytesperline = bpl ;
fmt - > fmt . pix . sizeimage = imagesize ;
fmt - > fmt . pix . colorspace = V4L2_COLORSPACE_SRGB ;
fmt - > fmt . pix . priv = 0 ;
return 0 ;
}
static int
2014-09-08 11:18:15 +03:00
uvc_v4l2_reqbufs ( struct file * file , void * fh , struct v4l2_requestbuffers * b )
2010-05-02 20:57:41 +02:00
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
2014-09-08 11:18:15 +03:00
struct uvc_video * video = & uvc - > video ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
if ( b - > type ! = video - > queue . queue . type )
return - EINVAL ;
2010-05-02 20:57:41 +02:00
2014-09-09 02:02:08 +03:00
return uvcg_alloc_buffers ( & video - > queue , b ) ;
2010-05-02 20:57:41 +02:00
}
static int
2014-09-08 11:18:15 +03:00
uvc_v4l2_querybuf ( struct file * file , void * fh , struct v4l2_buffer * b )
2010-05-02 20:57:41 +02:00
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
2014-09-08 11:18:15 +03:00
struct uvc_video * video = & uvc - > video ;
2013-03-28 15:11:52 +05:30
2014-09-09 02:02:08 +03:00
return uvcg_query_buffer ( & video - > queue , b ) ;
2010-05-02 20:57:41 +02:00
}
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_qbuf ( struct file * file , void * fh , struct v4l2_buffer * b )
2010-05-02 20:57:41 +02:00
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_video * video = & uvc - > video ;
2014-09-08 11:18:15 +03:00
int ret ;
2010-05-02 20:57:41 +02:00
2014-09-09 02:02:08 +03:00
ret = uvcg_queue_buffer ( & video - > queue , b ) ;
2014-09-08 11:18:15 +03:00
if ( ret < 0 )
return ret ;
2010-05-02 20:57:41 +02:00
2020-04-21 15:28:14 +02:00
schedule_work ( & video - > pump ) ;
return ret ;
2014-09-08 11:18:15 +03:00
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_dqbuf ( struct file * file , void * fh , struct v4l2_buffer * b )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_video * video = & uvc - > video ;
2010-05-02 20:57:41 +02:00
2014-09-09 02:02:08 +03:00
return uvcg_dequeue_buffer ( & video - > queue , b , file - > f_flags & O_NONBLOCK ) ;
2014-09-08 11:18:15 +03:00
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_streamon ( struct file * file , void * fh , enum v4l2_buf_type type )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_video * video = & uvc - > video ;
int ret ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
if ( type ! = video - > queue . queue . type )
return - EINVAL ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
/* Enable UVC video. */
2014-09-09 02:02:08 +03:00
ret = uvcg_video_enable ( video , 1 ) ;
2014-09-08 11:18:15 +03:00
if ( ret < 0 )
return ret ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
/*
* Complete the alternate setting selection setup phase now that
* userspace is ready to provide video frames .
*/
uvc_function_setup_continue ( uvc ) ;
uvc - > state = UVC_STATE_STREAMING ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
return 0 ;
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_streamoff ( struct file * file , void * fh , enum v4l2_buf_type type )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_video * video = & uvc - > video ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
if ( type ! = video - > queue . queue . type )
return - EINVAL ;
2010-05-02 20:57:41 +02:00
2014-09-09 02:02:08 +03:00
return uvcg_video_enable ( video , 0 ) ;
2014-09-08 11:18:15 +03:00
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_subscribe_event ( struct v4l2_fh * fh ,
const struct v4l2_event_subscription * sub )
{
if ( sub - > type < UVC_EVENT_FIRST | | sub - > type > UVC_EVENT_LAST )
return - EINVAL ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
return v4l2_event_subscribe ( fh , sub , 2 , NULL ) ;
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_unsubscribe_event ( struct v4l2_fh * fh ,
const struct v4l2_event_subscription * sub )
{
return v4l2_event_unsubscribe ( fh , sub ) ;
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static long
uvc_v4l2_ioctl_default ( struct file * file , void * fh , bool valid_prio ,
unsigned int cmd , void * arg )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
2013-03-01 20:46:30 +01:00
2014-09-08 11:18:15 +03:00
switch ( cmd ) {
case UVCIOC_SEND_RESPONSE :
return uvc_send_response ( uvc , arg ) ;
2013-03-01 20:46:30 +01:00
2014-09-08 11:18:15 +03:00
default :
return - ENOIOCTLCMD ;
2010-05-02 20:57:41 +02:00
}
2014-09-08 11:18:15 +03:00
}
2010-05-02 20:57:41 +02:00
2014-09-09 02:02:09 +03:00
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
2014-09-08 11:18:15 +03:00
. vidioc_querycap = uvc_v4l2_querycap ,
. vidioc_g_fmt_vid_out = uvc_v4l2_get_format ,
. vidioc_s_fmt_vid_out = uvc_v4l2_set_format ,
. vidioc_reqbufs = uvc_v4l2_reqbufs ,
. vidioc_querybuf = uvc_v4l2_querybuf ,
. vidioc_qbuf = uvc_v4l2_qbuf ,
. vidioc_dqbuf = uvc_v4l2_dqbuf ,
. vidioc_streamon = uvc_v4l2_streamon ,
. vidioc_streamoff = uvc_v4l2_streamoff ,
. vidioc_subscribe_event = uvc_v4l2_subscribe_event ,
. vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event ,
. vidioc_default = uvc_v4l2_ioctl_default ,
} ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
/* --------------------------------------------------------------------------
* V4L2
*/
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_open ( struct file * file )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_file_handle * handle ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
handle = kzalloc ( sizeof ( * handle ) , GFP_KERNEL ) ;
if ( handle = = NULL )
return - ENOMEM ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
v4l2_fh_init ( & handle - > vfh , vdev ) ;
v4l2_fh_add ( & handle - > vfh ) ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
handle - > device = & uvc - > video ;
file - > private_data = & handle - > vfh ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
uvc_function_connect ( uvc ) ;
return 0 ;
}
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
static int
uvc_v4l2_release ( struct file * file )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
struct uvc_file_handle * handle = to_uvc_file_handle ( file - > private_data ) ;
struct uvc_video * video = handle - > device ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
uvc_function_disconnect ( uvc ) ;
2010-05-02 20:57:41 +02:00
2015-02-17 05:44:06 -03:00
mutex_lock ( & video - > mutex ) ;
2014-09-09 02:02:08 +03:00
uvcg_video_enable ( video , 0 ) ;
uvcg_free_buffers ( & video - > queue ) ;
2015-02-17 05:44:06 -03:00
mutex_unlock ( & video - > mutex ) ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
file - > private_data = NULL ;
v4l2_fh_del ( & handle - > vfh ) ;
v4l2_fh_exit ( & handle - > vfh ) ;
kfree ( handle ) ;
2010-05-02 20:57:41 +02:00
2014-09-08 11:18:15 +03:00
return 0 ;
2010-05-02 20:57:41 +02:00
}
static int
uvc_v4l2_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
2014-09-09 02:02:08 +03:00
return uvcg_queue_mmap ( & uvc - > video . queue , vma ) ;
2010-05-02 20:57:41 +02:00
}
2017-07-03 03:02:56 -04:00
static __poll_t
2010-05-02 20:57:41 +02:00
uvc_v4l2_poll ( struct file * file , poll_table * wait )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
2014-09-09 02:02:08 +03:00
return uvcg_queue_poll ( & uvc - > video . queue , file , wait ) ;
2010-05-02 20:57:41 +02:00
}
2013-03-28 15:11:53 +05:30
# ifndef CONFIG_MMU
2014-09-29 14:30:20 +02:00
static unsigned long uvcg_v4l2_get_unmapped_area ( struct file * file ,
2013-03-28 15:11:53 +05:30
unsigned long addr , unsigned long len , unsigned long pgoff ,
unsigned long flags )
{
struct video_device * vdev = video_devdata ( file ) ;
struct uvc_device * uvc = video_get_drvdata ( vdev ) ;
2014-09-09 02:02:08 +03:00
return uvcg_queue_get_unmapped_area ( & uvc - > video . queue , pgoff ) ;
2013-03-28 15:11:53 +05:30
}
# endif
2017-09-27 22:57:07 +05:30
const struct v4l2_file_operations uvc_v4l2_fops = {
2010-05-02 20:57:41 +02:00
. owner = THIS_MODULE ,
. open = uvc_v4l2_open ,
. release = uvc_v4l2_release ,
2015-02-17 05:44:07 -03:00
. unlocked_ioctl = video_ioctl2 ,
2010-05-02 20:57:41 +02:00
. mmap = uvc_v4l2_mmap ,
. poll = uvc_v4l2_poll ,
2013-03-28 15:11:53 +05:30
# ifndef CONFIG_MMU
2014-09-09 02:02:08 +03:00
. get_unmapped_area = uvcg_v4l2_get_unmapped_area ,
2013-03-28 15:11:53 +05:30
# endif
2010-05-02 20:57:41 +02:00
} ;