We install an kthread with pfifo priority that is iterating over all prepared requests and keeps the isoc queue busy. This way it will be scheduled with the same priority as the interrupt handler. As the kthread is triggered with video_enable it will immediately queue some zero length requests into the hw if there is no buffer data available. It also watches the level of needed zero length requests in the hardware not to fall under the UVCG_REQ_MAX_ZERO_COUNT threshold. This way we can drop the function uvc_video_ep_queue_initial_requests entirely. By using the kthread to do the actual request handling the interrupt handler will not be running into the time consuming and eventually locking work of actually enqueueing the requests back into its own pipeline. This work can now even be scheduled on another cpu. Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Link: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v7-3-e224bb1035f0@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
202 lines
5 KiB
C
202 lines
5 KiB
C
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
/*
|
|
* uvc_gadget.h -- USB Video Class Gadget driver
|
|
*
|
|
* Copyright (C) 2009-2010
|
|
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
|
*/
|
|
|
|
#ifndef _UVC_GADGET_H_
|
|
#define _UVC_GADGET_H_
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/usb/composite.h>
|
|
#include <linux/videodev2.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-dev.h>
|
|
#include <media/v4l2-fh.h>
|
|
|
|
#include "uvc_queue.h"
|
|
|
|
struct usb_ep;
|
|
struct usb_request;
|
|
struct uvc_descriptor_header;
|
|
struct uvc_device;
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* Debugging, printing and logging
|
|
*/
|
|
|
|
#define UVC_TRACE_PROBE (1 << 0)
|
|
#define UVC_TRACE_DESCR (1 << 1)
|
|
#define UVC_TRACE_CONTROL (1 << 2)
|
|
#define UVC_TRACE_FORMAT (1 << 3)
|
|
#define UVC_TRACE_CAPTURE (1 << 4)
|
|
#define UVC_TRACE_CALLS (1 << 5)
|
|
#define UVC_TRACE_IOCTL (1 << 6)
|
|
#define UVC_TRACE_FRAME (1 << 7)
|
|
#define UVC_TRACE_SUSPEND (1 << 8)
|
|
#define UVC_TRACE_STATUS (1 << 9)
|
|
|
|
#define UVC_WARN_MINMAX 0
|
|
#define UVC_WARN_PROBE_DEF 1
|
|
|
|
extern unsigned int uvc_gadget_trace_param;
|
|
|
|
#define uvc_trace(flag, msg...) \
|
|
do { \
|
|
if (uvc_gadget_trace_param & flag) \
|
|
printk(KERN_DEBUG "uvcvideo: " msg); \
|
|
} while (0)
|
|
|
|
#define uvcg_dbg(f, fmt, args...) \
|
|
dev_dbg(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
|
#define uvcg_info(f, fmt, args...) \
|
|
dev_info(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
|
#define uvcg_warn(f, fmt, args...) \
|
|
dev_warn(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
|
#define uvcg_err(f, fmt, args...) \
|
|
dev_err(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* Driver specific constants
|
|
*/
|
|
|
|
#define UVC_MAX_REQUEST_SIZE 64
|
|
#define UVC_MAX_EVENTS 4
|
|
|
|
#define UVCG_REQUEST_HEADER_LEN 12
|
|
|
|
#define UVCG_REQ_MAX_INT_COUNT 16
|
|
#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT)
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* Structures
|
|
*/
|
|
struct uvc_request {
|
|
struct usb_request *req;
|
|
u8 *req_buffer;
|
|
struct uvc_video *video;
|
|
struct sg_table sgt;
|
|
u8 header[UVCG_REQUEST_HEADER_LEN];
|
|
struct uvc_buffer *last_buf;
|
|
struct list_head list;
|
|
};
|
|
|
|
struct uvc_video {
|
|
struct uvc_device *uvc;
|
|
struct usb_ep *ep;
|
|
|
|
struct work_struct pump;
|
|
struct workqueue_struct *async_wq;
|
|
|
|
struct kthread_worker *kworker;
|
|
struct kthread_work hw_submit;
|
|
|
|
atomic_t queued;
|
|
|
|
/* Frame parameters */
|
|
u8 bpp;
|
|
u32 fcc;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int imagesize;
|
|
struct mutex mutex; /* protects frame parameters */
|
|
|
|
unsigned int uvc_num_requests;
|
|
|
|
/* Requests */
|
|
bool is_enabled; /* tracks whether video stream is enabled */
|
|
unsigned int req_size;
|
|
struct list_head ureqs; /* all uvc_requests allocated by uvc_video */
|
|
|
|
/* USB requests that the video pump thread can encode into */
|
|
struct list_head req_free;
|
|
|
|
/*
|
|
* USB requests video pump thread has already encoded into. These are
|
|
* ready to be queued to the endpoint.
|
|
*/
|
|
struct list_head req_ready;
|
|
spinlock_t req_lock;
|
|
|
|
unsigned int req_int_count;
|
|
|
|
void (*encode) (struct usb_request *req, struct uvc_video *video,
|
|
struct uvc_buffer *buf);
|
|
|
|
/* Context data used by the completion handler */
|
|
__u32 payload_size;
|
|
__u32 max_payload_size;
|
|
|
|
struct uvc_video_queue queue;
|
|
unsigned int fid;
|
|
};
|
|
|
|
enum uvc_state {
|
|
UVC_STATE_DISCONNECTED,
|
|
UVC_STATE_CONNECTED,
|
|
UVC_STATE_STREAMING,
|
|
};
|
|
|
|
struct uvc_device {
|
|
struct video_device vdev;
|
|
struct v4l2_device v4l2_dev;
|
|
enum uvc_state state;
|
|
struct usb_function func;
|
|
struct uvc_video video;
|
|
bool func_connected;
|
|
wait_queue_head_t func_connected_queue;
|
|
|
|
struct uvcg_streaming_header *header;
|
|
|
|
/* Descriptors */
|
|
struct {
|
|
const struct uvc_descriptor_header * const *fs_control;
|
|
const struct uvc_descriptor_header * const *ss_control;
|
|
const struct uvc_descriptor_header * const *fs_streaming;
|
|
const struct uvc_descriptor_header * const *hs_streaming;
|
|
const struct uvc_descriptor_header * const *ss_streaming;
|
|
struct list_head *extension_units;
|
|
} desc;
|
|
|
|
unsigned int control_intf;
|
|
struct usb_ep *interrupt_ep;
|
|
struct usb_request *control_req;
|
|
void *control_buf;
|
|
bool enable_interrupt_ep;
|
|
|
|
unsigned int streaming_intf;
|
|
|
|
/* Events */
|
|
unsigned int event_length;
|
|
unsigned int event_setup_out : 1;
|
|
};
|
|
|
|
static inline struct uvc_device *to_uvc(struct usb_function *f)
|
|
{
|
|
return container_of(f, struct uvc_device, func);
|
|
}
|
|
|
|
struct uvc_file_handle {
|
|
struct v4l2_fh vfh;
|
|
struct uvc_video *device;
|
|
bool is_uvc_app_handle;
|
|
};
|
|
|
|
#define to_uvc_file_handle(handle) \
|
|
container_of(handle, struct uvc_file_handle, vfh)
|
|
|
|
/* ------------------------------------------------------------------------
|
|
* Functions
|
|
*/
|
|
|
|
extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep);
|
|
extern void uvc_function_connect(struct uvc_device *uvc);
|
|
extern void uvc_function_disconnect(struct uvc_device *uvc);
|
|
|
|
#endif /* _UVC_GADGET_H_ */
|