From 76d4b87b1ad0766864e7001a84701cbe521c77bf Mon Sep 17 00:00:00 2001
From: Michael Neumann <mneumann@ntecs.de>
Date: Tue, 6 May 2025 16:03:42 +0200
Subject: [PATCH] Initial port of uvc driver from FreeBSD

The port is based on [1] (branch feature-uvc-prepare-submit3) as of
May 10, 2025.

Tested with the built-in USB HD Webcam of my Panasonic CF-SV7.  Works in
chrome, cheese and with ffmpeg. Does not work in Firefox (waits for permissions)
or pwcview.

To record from webcam with ffmpeg:

    ffmpeg -f v4l2 -i /dev/video0 -vcodec rawvideo record.mkv

After closing the video device, you still have to kldunload uvc and
kldload uvc again, otherwise it either stops working or you'll get
strange artifacts.

* Applied some automatic DragonFly conversions using the following
  Makefile script:

    replace:
    	ruby -i -p -e '$$_.gsub!(%r[dev/usb], "bus/u4b")' *.c *.h
    	ruby -i -p -e '$$_.gsub!(%r[(malloc|free)\(], "k\\1(")' *.c *.h
    	ruby -i -p -e '$$_.gsub!(%r{([a-z]*)printf\(}, "k\\1printf(")' *.c *.h

* Add STAILQ_FOREACH_SAFE macros adapted from
  netproto/802_11/ieee80211_dragonfly.h

* Add atomic_subtract_64 macro

* Add kqueue support, replacing FreeBSD's poll code

* Fix mmap implementation

* Properly size uvc_data_request packages for UVC versions >= 1.5
  to 48 bytes (taken from OpenBSD). This is required to recognize
  my webcam.

[1]: https://github.com/lwhsu/freebsd-src/tree/feature-uvc-prepare-submit3
---
 sys/bus/u4b/Makefile            |    2 +-
 sys/bus/u4b/quirk/usb_quirk.c   |    4 +
 sys/bus/u4b/quirk/usb_quirk.h   |   23 +
 sys/bus/u4b/usb.h               |    4 +
 sys/bus/u4b/usb/Makefile        |    2 +-
 sys/bus/u4b/usb_device.c        |  147 ++
 sys/bus/u4b/usb_device.h        |    4 +
 sys/bus/u4b/usb_transfer.c      |   26 +
 sys/bus/u4b/usb_transfer.h      |    1 +
 sys/bus/u4b/usbdevs             |    1 +
 sys/bus/u4b/usbdi.h             |    3 +
 sys/bus/u4b/uvc/Makefile        |   40 +
 sys/bus/u4b/uvc/uvc_buf.c       |  762 +++++++++
 sys/bus/u4b/uvc/uvc_buf.h       |  116 ++
 sys/bus/u4b/uvc/uvc_ctrls.c     | 1163 +++++++++++++
 sys/bus/u4b/uvc/uvc_drv.c       | 2789 +++++++++++++++++++++++++++++++
 sys/bus/u4b/uvc/uvc_drv.h       |  780 +++++++++
 sys/bus/u4b/uvc/uvc_v4l2.c      |  799 +++++++++
 sys/bus/u4b/uvc/uvc_v4l2.h      |  100 ++
 sys/contrib/v4l/videodev.h      |  367 ++++
 sys/contrib/v4l/videodev2.h     | 1907 +++++++++++++++++++++
 sys/cpu/x86_64/include/atomic.h |    1 +
 22 files changed, 9039 insertions(+), 2 deletions(-)
 create mode 100644 sys/bus/u4b/uvc/Makefile
 create mode 100644 sys/bus/u4b/uvc/uvc_buf.c
 create mode 100644 sys/bus/u4b/uvc/uvc_buf.h
 create mode 100644 sys/bus/u4b/uvc/uvc_ctrls.c
 create mode 100644 sys/bus/u4b/uvc/uvc_drv.c
 create mode 100644 sys/bus/u4b/uvc/uvc_drv.h
 create mode 100644 sys/bus/u4b/uvc/uvc_v4l2.c
 create mode 100644 sys/bus/u4b/uvc/uvc_v4l2.h
 create mode 100644 sys/contrib/v4l/videodev.h
 create mode 100644 sys/contrib/v4l/videodev2.h

diff --git a/sys/bus/u4b/Makefile b/sys/bus/u4b/Makefile
index 52186e6ec9..170fda53c4 100644
--- a/sys/bus/u4b/Makefile
+++ b/sys/bus/u4b/Makefile
@@ -1,4 +1,4 @@
-SUBDIR=	usb audio controller gadget input misc \
+SUBDIR=	usb uvc audio controller gadget input misc \
 	net quirk serial storage template wlan
 
 # empty to allow concurrent building
diff --git a/sys/bus/u4b/quirk/usb_quirk.c b/sys/bus/u4b/quirk/usb_quirk.c
index 800412dcb2..462a89883c 100644
--- a/sys/bus/u4b/quirk/usb_quirk.c
+++ b/sys/bus/u4b/quirk/usb_quirk.c
@@ -548,6 +548,9 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
 
 	/* Holtek USB gaming keyboard */
 	USB_QUIRK(HOLTEK, F85, 0x0000, 0xffff, UQ_KBD_BOOTPROTO),
+
+	/* USB Cameras */
+	USB_QUIRK(CISCOLINKSYS3, ROOMKIT0B01, 0x0000, 0xffff, UQ_UVC_NO_EOF),
 };
 #undef USB_QUIRK_VP
 #undef USB_QUIRK
@@ -621,6 +624,7 @@ static const char *usb_quirk_str[USB_QUIRK_MAX] = {
 	[UQ_SINGLE_CMD_MIDI]		= "UQ_SINGLE_CMD_MIDI",
 	[UQ_MSC_DYMO_EJECT]		= "UQ_MSC_DYMO_EJECT",
 	[UQ_AU_SET_SPDIF_CM6206]	= "UQ_AU_SET_SPDIF_CM6206",
+	[UQ_UVC_NO_EOF]                 = "UQ_UVC_NO_EOF",
 };
 
 /*------------------------------------------------------------------------*
diff --git a/sys/bus/u4b/quirk/usb_quirk.h b/sys/bus/u4b/quirk/usb_quirk.h
index 1c4af8d17e..35262e2875 100644
--- a/sys/bus/u4b/quirk/usb_quirk.h
+++ b/sys/bus/u4b/quirk/usb_quirk.h
@@ -116,6 +116,29 @@ enum {
 	UQ_AU_SET_SPDIF_CM6206,	/* enable S/PDIF audio output */
 	UQ_WMT_IGNORE,          /* device should be ignored by wmt driver */
 
+	UQ_HID_NO_SETIDLE,	/* hid device should not set idle request */
+	UQ_HID_CLEAN_LOGIC,	/* hid report should clean logical min */
+	UQ_HID_LINUX_ALGORITHM,	/* hid driver uses linux algorithm */
+	UQ_HID_SUSPEND,		/* hid auto-suspend at end of attachin driver */
+	UQ_HID_SUSPEND_SYSCTL,	/* hid auto-suspend at end of attachin driver */
+	UQ_POWERD_NO_WAKE,
+	UQ_POWERD_AUTO_SUSPEND,
+	UQ_POWERD_SUSPEND_CON,
+	UQ_RE_ENUM_ONCE_MORE,	/* some devices need re enum once more like linux*/
+	/* samsung galaxy phone need linux like usb initial procedure */
+	UQ_LINUX_LIKE_PROCEDURE,
+	UQ_UVC_BIG_BUFFER,	/*big data buffer*/
+	UQ_UVC_DROP_BIG_FORMAT,	/*big data buffer*/
+	UQ_UVC_COMMIT_IN_ADVANCE,	/*COMMIT in advance*/
+	UQ_UVC_IR_INTRF_DISABLE,    /*IR Camera interface*/
+	UQ_UVC_DISABLE_HUB_U1U2,    /*DISABLE_HUB_U1U2*/
+	UQ_NO_CANCEL_XFER,	/*no cancel xfer*/
+	UQ_CLEAR_DATA_TOGGLE, /*Clear Data Toggle */
+	UQ_SPEAKER_VOLUME_SEQUENCE,
+	UQ_ADEQUATE_BUF,
+	UQ_EP_RESET_FREE,
+	UQ_UVC_NO_EOF,
+
 	USB_QUIRK_MAX
 };
 
diff --git a/sys/bus/u4b/usb.h b/sys/bus/u4b/usb.h
index f7c223b2fd..d24aa27e39 100644
--- a/sys/bus/u4b/usb.h
+++ b/sys/bus/u4b/usb.h
@@ -495,6 +495,10 @@ typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t;
 #define	UIPROTO_BLUETOOTH		0x01
 #define	UIPROTO_RNDIS			0x03
 
+#define UICLASS_VIDEO		0x0e		/* uvc */
+#define UISUBCLASS_CTRL		0x01
+#define UISUBCLASS_STREAMING	0x02
+
 #define	UICLASS_IAD		0xEF	/* Interface Association Descriptor */
 #define	UISUBCLASS_SYNC			0x01
 #define	UIPROTO_ACTIVESYNC		0x01
diff --git a/sys/bus/u4b/usb/Makefile b/sys/bus/u4b/usb/Makefile
index 63c4b68620..f077fd5703 100644
--- a/sys/bus/u4b/usb/Makefile
+++ b/sys/bus/u4b/usb/Makefile
@@ -2,7 +2,7 @@
 
 KMOD=	usb
 
-SRCS=	bus_if.h device_if.h usb_if.h usb_if.c \
+SRCS=	bus_if.h device_if.h usb_if.h usb_if.c pci_if.h \
 	opt_usb.h opt_ddb.h \
 	usbdevs.h usbdevs_data.h \
 	usb_busdma.c usb_controller.c usb_core.c usb_debug.c \
diff --git a/sys/bus/u4b/usb_device.c b/sys/bus/u4b/usb_device.c
index cd600e017f..94ebe20d26 100644
--- a/sys/bus/u4b/usb_device.c
+++ b/sys/bus/u4b/usb_device.c
@@ -42,6 +42,8 @@
 #include <sys/conf.h>
 #include <sys/fcntl.h>
 #include <sys/devfs.h>
+#include <sys/pciio.h>
+#include <bus/pci/pcivar.h>
 
 #include <bus/u4b/usb.h>
 #include <bus/u4b/usbdi.h>
@@ -1586,6 +1588,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
 	lockinit(&udev->enum_lock, "USB config SX lock", 0, LK_CANRECURSE);
 	/* XXX (mp) is this LK_CANRECURSE necessary? */
 	lockinit(&udev->sr_lock, "USB suspend and resume SX lock", 0, LK_CANRECURSE);
+	lockinit(&udev->ctrl_lock, "USB control transfer SX lock", 0, LK_CANRECURSE);
 
 	cv_init(&udev->ctrlreq_cv, "WCTRL");
 	cv_init(&udev->ref_cv, "UGONE");
@@ -2194,6 +2197,7 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
 
 	lockuninit(&udev->enum_lock);
 	lockuninit(&udev->sr_lock);
+	lockuninit(&udev->ctrl_lock);
 
 	cv_destroy(&udev->ctrlreq_cv);
 	cv_destroy(&udev->ref_cv);
@@ -2237,6 +2241,37 @@ usbd_get_iface(struct usb_device *udev, uint8_t iface_index)
 	return (iface);
 }
 
+void
+usbd_get_phys(struct usb_device *udev, char *phys, uint8_t len)
+{
+	struct pci_devinfo *info;
+	uint8_t no[128], tmp[16], index = 128;
+
+	if (!phys)
+		return;
+
+	info = device_get_ivars(device_get_parent(udev->bus->bdev));
+	ksnprintf(phys, len, "usb-%02x:%02x:%02x.%x-",
+			info->cfg.domain, info->cfg.bus,
+			info->cfg.slot, info->cfg.func);
+
+	while (udev && udev->device_index != USB_ROOT_HUB_ADDR && index) {
+		no[--index] = udev->port_no;
+		udev = (udev->parent_hub != NULL) ?
+			udev->parent_hub : udev->parent_hs_hub;
+	}
+
+	while (index < 128) {
+		ksnprintf(tmp, 16, "%d.", no[index]);
+		if (len <= strlen(phys) + strlen(tmp) + 1)
+			break;
+		strcat(phys, tmp);
+		index++;
+	}
+	index = strlen(phys) - 1;
+	phys[index] = '\0';
+}
+
 /*------------------------------------------------------------------------*
  *	usbd_find_descriptor
  *
@@ -2346,6 +2381,83 @@ struct usb_knowndev {
 #include "usbdevs_data.h"
 #endif					/* USB_VERBOSE */
 
+void
+device_get_usb_vidpid(device_t dev, uint32_t *vid, uint32_t *pid)
+{
+	struct usb_attach_arg *uaa;
+	struct usb_device *udev;
+	uint8_t do_unlock;
+
+	if (dev == NULL) {
+		/* should not happen */
+		return;
+	}
+	uaa = device_get_ivars(dev);
+	if (uaa == NULL) {
+		/* can happen if called at the wrong time */
+		return;
+	}
+	udev = uaa->device;
+
+	if (udev == NULL)
+		return;
+
+	do_unlock = usbd_ctrl_lock(udev);
+
+	*vid = UGETW(udev->ddesc.idVendor);
+	*pid = UGETW(udev->ddesc.idProduct);
+
+	if (do_unlock)
+		usbd_ctrl_unlock(udev);
+}
+
+void
+device_get_usb_iproduct(device_t dev, char *iProduct, size_t size)
+{
+	struct usb_attach_arg *uaa;
+	struct usb_device *udev;
+	uint8_t do_unlock;
+
+	if (dev == NULL) {
+		/* should not happen */
+		return;
+	}
+	uaa = device_get_ivars(dev);
+	if (uaa == NULL) {
+		/* can happen if called at the wrong time */
+		return;
+	}
+	udev = uaa->device;
+
+	do_unlock = usbd_ctrl_lock(udev);
+	/* get product string */
+	usbd_req_get_string_any(udev, NULL, iProduct, size,
+				udev->ddesc.iProduct);
+	usb_trim_spaces(iProduct);
+	if (do_unlock)
+		usbd_ctrl_unlock(udev);
+
+	device_printf(dev, "%s-%d <%s>\n", __func__, __LINE__, iProduct);
+}
+
+int
+usb_device_is_UVC(struct usb_device *udev)
+{
+	struct usb_device_descriptor *udd = NULL;
+
+	if (!udev)
+		return 0;
+	udd = &udev->ddesc;
+
+	if (udd->bDeviceClass == 0xef &&
+	    udd->bDeviceSubClass == 0x02 &&
+	    udd->bDeviceProtocol == 0x01) {
+		return 1;
+	}
+
+	return 0;
+}
+
 static void
 usbd_set_device_strings(struct usb_device *udev)
 {
@@ -2791,6 +2903,41 @@ usbd_sr_unlock(struct usb_device *udev)
 	lockmgr(&udev->sr_lock, LK_RELEASE);
 }
 
+/*
+ * The following function is used to serialize access to USB control
+ * transfers and the USB scratch area. If the lock is already grabbed
+ * this function returns zero. Else a value of one is returned.
+ */
+uint8_t
+usbd_ctrl_lock(struct usb_device *udev)
+{
+	if (lockstatus(&udev->ctrl_lock, curthread)==LK_EXCLUSIVE)
+		return (0);
+
+	lockmgr(&udev->ctrl_lock, LK_EXCLUSIVE);
+
+	/*
+	 * We need to allow suspend and resume at this point, else the
+	 * control transfer will timeout if the device is suspended!
+	 */
+	if (usbd_enum_is_locked(udev))
+		usbd_sr_unlock(udev);
+	return (1);
+}
+
+void
+usbd_ctrl_unlock(struct usb_device *udev)
+{
+	lockmgr(&udev->ctrl_lock, LK_RELEASE);
+
+	/*
+	 * Restore the suspend and resume lock after we have unlocked
+	 * the USB control transfer lock to avoid LOR:
+	 */
+	if (usbd_enum_is_locked(udev))
+		usbd_sr_lock(udev);
+}
+
 /*
  * The following function checks the enumerating lock for the given
  * USB device.
diff --git a/sys/bus/u4b/usb_device.h b/sys/bus/u4b/usb_device.h
index c97a0e3d7b..dc2f8efa6f 100644
--- a/sys/bus/u4b/usb_device.h
+++ b/sys/bus/u4b/usb_device.h
@@ -187,6 +187,7 @@ struct usb_device {
 	struct usb_udev_msg cs_msg[2];
 	struct lock enum_lock;
 	struct lock sr_lock;
+	struct lock ctrl_lock;
 	struct lock device_lock;
 	struct cv ctrlreq_cv;
 	struct cv ref_cv;
@@ -321,6 +322,8 @@ uint8_t	usbd_enum_lock(struct usb_device *);
 void	usbd_enum_unlock(struct usb_device *);
 void	usbd_sr_lock(struct usb_device *);
 void	usbd_sr_unlock(struct usb_device *);
+uint8_t	usbd_ctrl_lock(struct usb_device *);
+void	usbd_ctrl_unlock(struct usb_device *);
 uint8_t usbd_enum_is_locked(struct usb_device *);
 
 #if USB_HAVE_TT_SUPPORT
@@ -328,5 +331,6 @@ void	uhub_tt_buffer_reset_async_locked(struct usb_device *, struct usb_endpoint
 #endif
 
 uint8_t uhub_count_active_host_ports(struct usb_device *, enum usb_dev_speed);
+int    usb_device_is_UVC(struct usb_device *udev);
 
 #endif					/* _USB_DEVICE_H_ */
diff --git a/sys/bus/u4b/usb_transfer.c b/sys/bus/u4b/usb_transfer.c
index c759fb01d5..99be471302 100644
--- a/sys/bus/u4b/usb_transfer.c
+++ b/sys/bus/u4b/usb_transfer.c
@@ -3544,3 +3544,29 @@ usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer)
 {
 	return (xfer->flags_int.maxp_was_clamped);
 }
+
+int
+usb_reset_ep(struct usb_device *udev, struct usb_endpoint *ep)
+{
+	int error;
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_ENDPOINT;
+	req.bRequest = UR_CLEAR_FEATURE;
+	USETW(req.wValue, UF_ENDPOINT_HALT);
+	req.wIndex[0] = ep->edesc->bEndpointAddress;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	DPRINTFN(2, "udev %04x:%04x, ep addr %02x\n",
+		 UGETW(udev->ddesc.idVendor),
+		 UGETW(udev->ddesc.idProduct), req.wIndex[0]);
+	error = usbd_do_request(udev, NULL, &req, NULL);
+	if (error == 0) {
+		usbd_clear_data_toggle(udev, ep);
+	} else {
+		error = ENXIO;
+	}
+
+	return error;
+}
diff --git a/sys/bus/u4b/usb_transfer.h b/sys/bus/u4b/usb_transfer.h
index 20be2bf0c4..628ea17969 100644
--- a/sys/bus/u4b/usb_transfer.h
+++ b/sys/bus/u4b/usb_transfer.h
@@ -250,5 +250,6 @@ void	usbd_transfer_timeout_ms(struct usb_xfer *xfer,
 	    void (*cb) (void *arg), usb_timeout_t ms);
 usb_timeout_t usbd_get_dma_delay(struct usb_device *udev);
 void	usbd_transfer_power_ref(struct usb_xfer *xfer, int val);
+int		usb_reset_ep(struct usb_device *udev, struct usb_endpoint *ep);
 
 #endif					/* _USB_TRANSFER_H_ */
diff --git a/sys/bus/u4b/usbdevs b/sys/bus/u4b/usbdevs
index 6ffc2cddde..13a5462816 100644
--- a/sys/bus/u4b/usbdevs
+++ b/sys/bus/u4b/usbdevs
@@ -1416,6 +1416,7 @@ product CISCOLINKSYS AE1000	0x002f	AE1000
 product CISCOLINKSYS USB3GIGV1	0x0041  USB3GIGV1 USB Ethernet Adapter
 product CISCOLINKSYS2 RT3070	0x4001	RT3070
 product CISCOLINKSYS3 RT3070	0x0101	RT3070
+product CISCOLINKSYS3 ROOMKIT0B01 0x0b01	ROOMKIT0B01
 
 /* Clipsal products */
 product CLIPSAL 560884		0x0101	560884 C-Bus Audio Matrix Switch
diff --git a/sys/bus/u4b/usbdi.h b/sys/bus/u4b/usbdi.h
index 6ede7da7b2..9b9d769558 100644
--- a/sys/bus/u4b/usbdi.h
+++ b/sys/bus/u4b/usbdi.h
@@ -487,6 +487,8 @@ struct usb_endpoint *usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val
 usb_error_t	usbd_interface_count(struct usb_device *udev, uint8_t *count);
 enum usb_hc_mode usbd_get_mode(struct usb_device *udev);
 enum usb_dev_speed usbd_get_speed(struct usb_device *udev);
+void device_get_usb_iproduct(device_t dev, char *iProduct, size_t size);
+void device_get_usb_vidpid(device_t dev, uint32_t *vid, uint32_t *pid);
 void device_set_usb_desc(device_t dev);
 void usb_pause_mtx(struct lock *lock, int _ticks);
 void usb_pause_ls(struct lock *lock, struct lwkt_serialize *slz, int _ticks);
@@ -588,6 +590,7 @@ void	usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset,
 void	usbd_start_re_enumerate(struct usb_device *udev);
 usb_error_t
 	usbd_start_set_config(struct usb_device *, uint8_t);
+void	usbd_get_phys(struct usb_device *udev, char *phys, uint8_t len);
 
 int	usb_fifo_attach(struct usb_device *udev, void *priv_sc,
 	    struct lock *priv_lock, struct usb_fifo_methods *pm,
diff --git a/sys/bus/u4b/uvc/Makefile b/sys/bus/u4b/uvc/Makefile
new file mode 100644
index 0000000000..939eaf9d87
--- /dev/null
+++ b/sys/bus/u4b/uvc/Makefile
@@ -0,0 +1,40 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Dell Inc.
+#
+#	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+.PATH:	${.CURDIR}
+
+KMOD=	uvc
+SRCS=	opt_bus.h opt_usb.h device_if.h \
+	bus_if.h usb_if.h usbdevs.h \
+	uvc_drv.c \
+	uvc_buf.c \
+	uvc_v4l2.c \
+	uvc_ctrls.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/bus/u4b/uvc/uvc_buf.c b/sys/bus/u4b/uvc/uvc_buf.c
new file mode 100644
index 0000000000..45a6b536a9
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_buf.c
@@ -0,0 +1,762 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#include <sys/cdefs.h>
+#include <sys/stdint.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/condvar.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/sbuf.h>
+
+#include <sys/filedesc.h>
+#include <bus/u4b/usb.h>
+#include <bus/u4b/usbdi.h>
+#include <bus/u4b/usbdi_util.h>
+#include <bus/u4b/usbhid.h>
+
+#define USB_DEBUG_VAR uvc_debug
+#include <bus/u4b/usb_debug.h>
+
+#include <bus/u4b/usb_core.h>
+#include <bus/u4b/usb_dev.h>
+#include <bus/u4b/usb_mbuf.h>
+#include <bus/u4b/usb_process.h>
+#include <bus/u4b/usb_device.h>
+#include <bus/u4b/usb_busdma.h>
+#include <bus/u4b/usb_dynamic.h>
+#include <bus/u4b/usb_util.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <contrib/v4l/videodev.h>
+#include <contrib/v4l/videodev2.h>
+
+#include "uvc_drv.h"
+#include "uvc_buf.h"
+#include "uvc_v4l2.h"
+
+#define UVC_LOCK(lkp)   lockmgr(lkp, LK_EXCLUSIVE)
+#define UVC_UNLOCK(lkp) lockmgr(lkp, LK_RELEASE)
+
+#define FRAME_DUMP 0
+#if FRAME_DUMP
+static void uvc_writefile(char *path, void *data, int len);
+#endif
+
+static void
+uvc_buf_fill_v4l2(struct v4l2_buffer *buf, uint32_t index, uint32_t size,
+	uint32_t len)
+{
+	buf->index = index;
+	buf->m.offset = index * size;
+	buf->length = len;
+	buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	buf->sequence = 0;
+	buf->field = V4L2_FIELD_NONE;
+	buf->memory = V4L2_MEMORY_MMAP;
+	buf->flags = 0;
+}
+
+static __inline int
+uvc_buf_queue_mmap_locked(struct uvc_buf_queue *bq, vm_paddr_t *paddr, vm_offset_t offset)
+{
+	 uint64_t max_offset = (uint64_t)(bq->buf_size * bq->buf_count) - PAGE_SIZE;
+
+	if (bq->mem == NULL)
+		return EINVAL;
+
+	if (offset < 0)
+		return EINVAL;
+
+	if (max_offset < 0)
+		return EINVAL;
+
+	if (offset > max_offset) {
+		kprintf("WARNING: %s offset out of bound: %ld\n; max: %ld",
+				__func__,
+				offset,
+				max_offset);
+		return EINVAL;
+	}
+
+	*paddr = atop(vtophys((uint8_t *)bq->mem + offset));
+
+	return (0);
+}
+
+int
+uvc_buf_queue_mmap(struct uvc_buf_queue *bq, vm_paddr_t *paddr, vm_offset_t offset)
+{
+	int res;
+
+	UVC_LOCK(&bq->mtx);
+
+	res = uvc_buf_queue_mmap_locked(bq, paddr, offset);
+
+	UVC_UNLOCK(&bq->mtx);
+
+	return (res);
+}
+
+static void
+uvc_buf_queue_show_qbuf(struct uvc_buf *buf)
+{
+	DPRINTF("buf index:%lu status:%lu mem:%p offset:%lu\n",
+		buf->index, buf->status, buf->mem, buf->offset);
+}
+
+static void
+uvc_buf_queue_show(struct uvc_buf_queue *bq)
+{
+	int i;
+
+	DPRINTF("mem:%p status:%lx flags:%lx seq:%lu buf size:%lu count:%lu\n",
+		bq->mem, bq->status, bq->flags, bq->seq,
+		bq->buf_size, bq->buf_count);
+	for (i = 0; i < UVC_BUF_MAX_BUFFERS; i++)
+		uvc_buf_queue_show_qbuf(bq->buf + i);
+}
+
+int
+uvc_buf_reset_buf(struct uvc_buf_queue *bq)
+{
+	struct uvc_buf *buf;
+
+	UVC_LOCK(&bq->mtx);
+	buf = STAILQ_FIRST(&bq->product);
+	if (buf) {
+		buf->vbuf.bytesused = 0;
+	}
+	UVC_UNLOCK(&bq->mtx);
+
+	return 0;
+}
+
+static int
+uvc_buf_check_length(struct uvc_drv_video *v, struct uvc_buf *buf,
+		     uint32_t len, uint32_t finish)
+{
+	struct v4l2_buffer *vbuf = &buf->vbuf;
+
+	/*
+	 * guvcview enables video stream twice on ThinOS, first time it calls
+	 * VIDIOC_REQBUFS to alloc buf for data stream, then disable video
+	 * but second time it does not call VIDIOC_REQBUFS to alloc mem which
+	 * calls crash not call, we do not figure out why yet, so do not let go
+	 * temply, we will figure out why it behaves this weird here
+	 */
+
+	if (vbuf->length == 0) {
+		return 1;
+	}
+
+
+	if (vbuf->bytesused + len > vbuf->length) {
+		return 1;
+	}
+	/*
+	 * -------------------2208 release----------------------------------
+	 * So far we found that sometime driver will upload incompleted frame
+	 * when using YUYV format so we check whether the data fully occupy
+	 * buffer, but NV12 format's data does not fully occupy buffer
+	 * originally, and now we not fully know all kinds of formats'
+	 * speciality so we quit it now for 2208 relase.
+	 * -------------------------end--------------------------------------
+	 * Logitech BRIO gives wrong dwMaxFrameSize in its NV12 format
+	 * fixed it at video probe, so uncomment "return" here
+	 */
+	if (v->cur_fmt && (v->cur_fmt->fcc != V4L2_PIX_FMT_MJPEG &&
+			   v->cur_fmt->fcc != V4L2_PIX_FMT_H264)) {
+		if (finish && (vbuf->bytesused + len < vbuf->length)) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+int
+uvc_bulkbuf_sell_buf(struct uvc_buf_queue *bq,
+		struct usb_page_cache *pc, usb_frlength_t offset,
+		usb_frlength_t len,
+		usb_frlength_t actlen,
+		usb_frlength_t maxFramelen)
+{
+	struct uvc_buf *buf;
+	struct uvc_drv_video *v;
+	unsigned char *ptr;
+	uint32_t maxlen, nbytes;
+	int ret = 0;
+
+	UVC_LOCK(&bq->mtx);
+	if (!UVC_BUF_QUEUE_IS_RUNNING(bq)) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	v = bq->video;
+
+	buf = STAILQ_FIRST(&bq->product);
+	if (!buf) {
+		ret = EINVAL;
+		v->bulk.skip_payload = 1;
+	}
+
+	if (!v->bulk.skip_payload && buf) {
+		if (buf->status != UVC_BUF_STATE_ACTIVE) {
+			if (v->bulk.last_fid ==
+					(v->bulk.header[1] & UVC_PL_HEADER_BIT_FID)) {
+				v->bulk.skip_payload = 1;
+				buf->vbuf.bytesused = 0;
+				goto done;
+			}
+			buf->status = UVC_BUF_STATE_ACTIVE;
+		}
+	}
+
+	if (v->bulk.last_fid != (v->bulk.header[1] & UVC_PL_HEADER_BIT_FID))
+		v->bulk.last_fid = (v->bulk.header[1] & UVC_PL_HEADER_BIT_FID);
+
+	if (len != 0 && !v->bulk.skip_payload) {
+		maxlen = buf->vbuf.length - buf->vbuf.bytesused;
+		nbytes = min(maxlen, len);
+
+		ptr = (char *)(buf->mem) + buf->offset;
+		usbd_copy_out(pc, offset, ptr + buf->vbuf.bytesused,
+			nbytes);
+		buf->vbuf.bytesused += nbytes;
+
+		if (len > maxlen) {
+			len -= nbytes;
+			goto finished;
+		}
+	}
+
+	if (actlen < maxFramelen ||
+	    v->bulk.payload_size >= v->bulk.max_payload_size) {
+		if (!v->bulk.skip_payload && buf)
+			if (uvc_drv_check_video_context(v, buf->mem,
+						buf->vbuf.bytesused)) {
+				buf->vbuf.bytesused = 0;
+				goto clean;
+			}
+
+		if (!v->bulk.skip_payload &&
+		    ((v->bulk.header[1] & UVC_PL_HEADER_BIT_EOF) ||
+			(bq->video && bq->video->sc &&
+				bq->video->sc->quirks & UVC_NO_EOF)))
+finished:
+			if (buf->vbuf.bytesused > 0) {
+				STAILQ_REMOVE_HEAD(&bq->product, link);
+				buf->vbuf.sequence = bq->seq++;
+				microtime(&buf->vbuf.timestamp);
+				buf->status = UVC_BUF_STATE_DONE;
+				STAILQ_INSERT_TAIL(&bq->consumer, buf, link);
+				cv_broadcast(&bq->io_cv);
+				KNOTE(&bq->sel.ki_note, 0);
+			}
+
+clean:
+		v->bulk.header_size = 0;
+		v->bulk.skip_payload = 0;
+		v->bulk.payload_size = 0;
+	}
+
+done:
+	UVC_UNLOCK(&bq->mtx);
+	return ret;
+}
+
+int
+uvc_buf_sell_buf(struct uvc_buf_queue *bq,
+		struct usb_page_cache *pc, usb_frlength_t offset,
+		usb_frlength_t len, uint32_t finish, uint8_t fid)
+{
+	struct uvc_buf *buf;
+	unsigned char *ptr;
+	int ret = 0;
+
+	UVC_LOCK(&bq->mtx);
+	if (!UVC_BUF_QUEUE_IS_RUNNING(bq)) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	buf = STAILQ_FIRST(&bq->product);
+	if (buf) {
+		if (buf->status != UVC_BUF_STATE_ACTIVE) {
+			if (bq->video->last_fid == fid) {
+				buf->vbuf.bytesused = 0;
+				goto done;
+			}
+			buf->status = UVC_BUF_STATE_ACTIVE;
+		}
+		if (uvc_buf_check_length(bq->video, buf, len, finish)) {
+			buf->vbuf.bytesused = 0;
+			goto done;
+		}
+		if (bq->video->last_fid != fid)
+			bq->video->last_fid = fid;
+
+		if (len != 0) {
+			ptr = (char *)(buf->mem) + buf->offset;
+			usbd_copy_out(pc, offset, ptr + buf->vbuf.bytesused,
+				len);
+			buf->vbuf.bytesused += len;
+		}
+
+		if (!finish)
+			goto done;
+
+		if (uvc_drv_check_video_context(bq->video, (char *)buf->mem + buf->offset,
+			buf->vbuf.bytesused)) {
+			buf->vbuf.bytesused = 0;
+			goto done;
+		}
+
+		if (buf->vbuf.bytesused > 0) {
+			DPRINTF("%s Got frame %ld %4lu\n", __func__, (long)bq->video, bq->seq);
+
+#if FRAME_DUMP
+			char path[PATH_MAX];
+			ksprintf(path, "/tmp/%x_%4lu.data", (short)bq->video, bq->seq);
+			uvc_writefile(path,
+				      (void *)((char *)buf->mem + buf->offset),
+				      buf->vbuf.bytesused);
+#endif
+			STAILQ_REMOVE_HEAD(&bq->product, link);
+			buf->vbuf.sequence = bq->seq++;
+			microtime(&buf->vbuf.timestamp);
+			buf->status = UVC_BUF_STATE_DONE;
+			STAILQ_INSERT_TAIL(&bq->consumer, buf, link);
+			cv_broadcast(&bq->io_cv);
+			KNOTE(&bq->sel.ki_note, 0);
+		}
+	}
+
+done:
+	UVC_UNLOCK(&bq->mtx);
+	return ret;
+}
+
+static void
+uvc_buf_queue_query_buf_locked(struct uvc_buf *buf, struct v4l2_buffer *vbuf)
+{
+	memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
+
+	switch (buf->status) {
+	case UVC_BUF_STATE_QUEUED:
+	case UVC_BUF_STATE_ACTIVE:
+		vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
+		break;
+	case UVC_BUF_STATE_DONE:
+	case UVC_BUF_STATE_ERROR:
+		vbuf->flags |= V4L2_BUF_FLAG_DONE;
+		break;
+	case UVC_BUF_STATE_IDLE:
+	default:
+		break;
+	}
+}
+
+int
+uvc_buf_queue_dequeue_buf(struct uvc_buf_queue *bq,
+	struct v4l2_buffer *vbuf, int nonblock)
+{
+	struct uvc_buf *buf;
+	int ret = 0;
+
+	UVC_LOCK(&bq->mtx);
+	do {
+		if (!UVC_BUF_QUEUE_IS_RUNNING(bq)) {
+			ret = EINVAL;
+			break;
+		}
+
+		buf = STAILQ_FIRST(&bq->consumer);
+		if (buf) {
+			switch (buf->status) {
+			case UVC_BUF_STATE_ERROR:
+				ret = EIO;
+			case UVC_BUF_STATE_DONE:
+				buf->status = UVC_BUF_STATE_IDLE;
+				break;
+			default:
+				DPRINTF("fatal %s\n", __func__);
+				break;
+			}
+			STAILQ_REMOVE_HEAD(&bq->consumer, link);
+			uvc_buf_queue_query_buf_locked(buf, vbuf);
+			break;
+		} else {
+			if (STAILQ_EMPTY(&bq->product)) {
+				DPRINTF("%s buf empty\n", __func__);
+				ret = EPIPE;
+				break;
+			}
+
+			if (!nonblock) {
+				ret = cv_wait_sig(&bq->io_cv, &bq->mtx);
+				if (ret) {
+					DPRINTF("cv wait sig:%d\n", ret);
+					break;
+				}
+			} else {
+				ret = EAGAIN;
+			}
+		}
+	} while (!nonblock);
+
+	UVC_UNLOCK(&bq->mtx);
+	return ret;
+}
+
+int
+uvc_buf_queue_queue_buf(struct uvc_buf_queue *bq, struct v4l2_buffer *vbuf)
+{
+	struct uvc_buf *buf;
+	int ret = 0;
+
+	UVC_LOCK(&bq->mtx);
+	if (vbuf->index >= bq->buf_count) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	if (!UVC_BUF_QUEUE_IS_RUNNING(bq)) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	buf = bq->buf + vbuf->index;
+	if (buf->status != UVC_BUF_STATE_IDLE) {
+		DPRINTF("%s wrong buf type %lu\n", __func__, buf->status);
+		ret = EINVAL;
+		goto done;
+	}
+
+	buf->status = UVC_BUF_STATE_QUEUED;
+	buf->vbuf.bytesused = 0;
+	STAILQ_INSERT_TAIL(&bq->product, buf, link);
+done:
+	UVC_UNLOCK(&bq->mtx);
+	return ret;
+}
+
+int
+uvc_buf_queue_query_buf(struct uvc_buf_queue *bq, struct v4l2_buffer *vbuf)
+{
+	int ret = 0;
+
+	DPRINTF("%s\n", __func__);
+
+	UVC_LOCK(&bq->mtx);
+
+	if (vbuf->index >= bq->buf_count) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	uvc_buf_queue_query_buf_locked(bq->buf + vbuf->index, vbuf);
+done:
+	UVC_UNLOCK(&bq->mtx);
+	return ret;
+}
+
+void
+uvc_buf_queue_set_drop_flag(struct uvc_buf_queue *bq)
+{
+	bq->flags |= UVC_BUFFER_QUEUE_DROP_INCOMPLETE;
+}
+
+static void
+uvc_buf_queue_free_bufs_locked(struct uvc_buf_queue *bq)
+{
+	if (bq->mem) {
+		kfree(bq->mem, M_UVC);
+		bq->mem = NULL;
+		bq->buf_count = 0;
+	}
+}
+
+void
+uvc_buf_queue_free_bufs(struct uvc_buf_queue *bq)
+{
+	UVC_LOCK(&bq->mtx);
+
+	/* check mmap finish */
+	uvc_buf_queue_free_bufs_locked(bq);
+	UVC_UNLOCK(&bq->mtx);
+}
+
+int
+uvc_buf_queue_req_bufs(struct uvc_buf_queue *bq, uint32_t *count, uint32_t len)
+{
+	void *mem;
+	struct uvc_buf *buf;
+	int i, num, ret = 0;
+	unsigned long rl;
+
+	DPRINTF("%s\n", __func__);
+	rl = round_page(len);
+	DPRINTF("page align size:%lu-real size:%u\n", rl, len);
+
+	num = *count;
+	if (num > UVC_BUF_MAX_BUFFERS)
+		num = UVC_BUF_MAX_BUFFERS;
+
+	UVC_LOCK(&bq->mtx);
+	uvc_buf_queue_free_bufs_locked(bq);
+	if (!num)
+		goto done;
+
+	for (; num > 0; num--) {
+		mem = kmalloc(num * rl, M_UVC, M_WAITOK | M_ZERO);
+		if (mem)
+			break;
+	}
+
+	if (!mem) {
+		ret = ENOMEM;
+		goto done;
+	}
+
+	for (i = 0; i < num; i++) {
+		buf = bq->buf + i;
+		buf->index = i;
+		buf->mem = mem;
+		buf->offset = i * rl;
+		buf->status = UVC_BUF_STATE_IDLE;
+		uvc_buf_fill_v4l2(&buf->vbuf, i, rl, len);
+	}
+
+	bq->mem = mem;
+	bq->buf_count = num;
+	bq->buf_size = rl;
+	*count = num;
+
+	uvc_buf_queue_show(bq);
+
+done:
+	UVC_UNLOCK(&bq->mtx);
+	DPRINTF("reqbuf nums:%d\n", *count);
+	return ret;
+}
+
+
+static void
+uvc_buf_queue_init_qbuf(struct uvc_buf *buf, int ind)
+{
+	memset(buf, 0x0, sizeof(*buf));
+	buf->index = ind;
+	buf->status = UVC_BUF_STATE_IDLE;
+}
+
+static void
+uvc_buf_queue_filter_detach(struct knote *kn)
+{
+        struct uvc_buf_queue *queue = (struct uvc_buf_queue *)kn->kn_hook;
+        struct klist *klist = &queue->sel.ki_note;
+
+	UVC_LOCK(&queue->mtx);
+	knote_remove(klist, kn);
+	UVC_UNLOCK(&queue->mtx);
+}
+
+static int
+uvc_buf_queue_kqread(struct knote *kn, long hint __unused)
+{
+        struct uvc_buf_queue *queue = (struct uvc_buf_queue *)kn->kn_hook;
+	int ready = 0;
+
+	UVC_LOCK(&queue->mtx);
+
+	if (STAILQ_FIRST(&queue->consumer))
+		ready = 1;
+
+	if ((queue->flags & UVC_BUFFER_QUEUE_WORKING) == 0) {
+		kn->kn_flags |= EV_ERROR;
+		ready = 1;
+	}
+
+	UVC_UNLOCK(&queue->mtx);
+
+	return ready;
+}
+
+static struct filterops uvc_buf_queue_filtops_read = {
+	.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
+	.f_attach = NULL,
+	.f_detach = uvc_buf_queue_filter_detach,
+	.f_event = uvc_buf_queue_kqread
+};
+
+int
+uvc_buf_queue_kqfilter(struct uvc_buf_queue *queue, struct knote *kn)
+{
+	struct klist *klist;
+
+	switch (kn->kn_filter) {
+		case EVFILT_READ:
+			kn->kn_fop = &uvc_buf_queue_filtops_read;
+			kn->kn_hook = (caddr_t)queue;
+			klist = &queue->sel.ki_note;
+
+			UVC_LOCK(&queue->mtx);
+			knote_insert(klist, kn);
+			UVC_UNLOCK(&queue->mtx);
+
+			return 0;
+
+		default:
+			return EOPNOTSUPP;
+	}
+}
+
+
+int
+uvc_buf_queue_disable(struct uvc_buf_queue *queue)
+{
+	int i;
+
+	DPRINTF("%s\n", __func__);
+
+	UVC_LOCK(&queue->mtx);
+	if ((queue->flags & UVC_BUFFER_QUEUE_WORKING) == 0) {
+		UVC_UNLOCK(&queue->mtx);
+		return EINVAL;
+	}
+
+	STAILQ_INIT(&queue->consumer);
+	STAILQ_INIT(&queue->product);
+	for (i = 0; i < UVC_BUF_MAX_BUFFERS; i++)
+		uvc_buf_queue_init_qbuf(queue->buf + i, i);
+
+	cv_broadcast(&queue->io_cv);
+	KNOTE(&queue->sel.ki_note, 0);
+	queue->flags = 0;
+	UVC_UNLOCK(&queue->mtx);
+	return 0;
+}
+
+int
+uvc_buf_queue_enable(struct uvc_buf_queue *queue)
+{
+	DPRINTF("%s\n", __func__);
+
+	UVC_LOCK(&queue->mtx);
+
+	if (queue->flags & UVC_BUFFER_QUEUE_WORKING) {
+		UVC_UNLOCK(&queue->mtx);
+		return EBUSY;
+	}
+
+	queue->seq = 0;
+
+	queue->flags |= UVC_BUFFER_QUEUE_WORKING;
+	UVC_UNLOCK(&queue->mtx);
+	return 0;
+}
+
+void
+uvc_buf_queue_init(struct uvc_drv_video *v, struct uvc_buf_queue *bq)
+{
+	int i;
+
+	DPRINTF("init buf\n");
+
+	bq->video = v;
+	lockinit(&bq->mtx, "uvc buffer lock", 0, LK_CANRECURSE);
+	cv_init(&bq->io_cv, "uvcbufiocv");
+	STAILQ_INIT(&bq->consumer);
+	STAILQ_INIT(&bq->product);
+	bzero(&bq->sel, sizeof(bq->sel));
+
+	for (i = 0; i < UVC_BUF_MAX_BUFFERS; i++)
+		uvc_buf_queue_init_qbuf(bq->buf + i, i);
+
+	return;
+}
+
+#if FRAME_DUMP
+static void
+uvc_writefile(char *path, void *data, int len)
+{
+	struct thread *td;
+	struct uio auio;
+	struct iovec aiov;
+	int error, fd = -1;
+
+	td = curthread;
+
+	pwd_ensure_dirs();
+
+	KLG("write: %s; %p; %d\n", path, data, len);
+	error = kern_openat(td, AT_FDCWD, path, UIO_SYSSPACE, O_CREAT | O_RDWR, 0666);
+	if (error) {
+		KLG("open %s error: %d\n", path, error);
+		goto out;
+	}
+	fd = td->td_retval[0];
+
+	aiov.iov_base = data;
+	aiov.iov_len = len;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_resid = len;
+	auio.uio_segflg = UIO_SYSSPACE;
+	error = kern_writev(td, fd, &auio);
+	if (error) {
+		KLG("write %s error: %d\n", path, error);
+		goto out;
+	}
+out:
+	if (fd >= 0)
+		kern_close(td, fd);
+}
+#endif
diff --git a/sys/bus/u4b/uvc/uvc_buf.h b/sys/bus/u4b/uvc/uvc_buf.h
new file mode 100644
index 0000000000..a554945a1a
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_buf.h
@@ -0,0 +1,116 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#ifndef _DEV_USB_UVC_BUF_H
+#define _DEV_USB_UVC_BUF_H
+
+#include <sys/event.h>
+
+#define	UVC_BUF_MAX_BUFFERS		8
+#define UVC_BUF_QUEUE_IS_RUNNING(bq)	(bq->buf_count != 0)
+
+enum uvc_buf_state {
+	UVC_BUF_STATE_IDLE	=	0,
+	UVC_BUF_STATE_QUEUED	=	1,
+	UVC_BUF_STATE_ACTIVE	=	2,
+	UVC_BUF_STATE_DONE	=	3,
+	UVC_BUF_STATE_ERROR	=	4,
+};
+
+#define UVC_BUFFER_QUEUE_WORKING		(1 << 0)
+#define UVC_BUFFER_QUEUE_DISCONNECTED		(1 << 1)
+#define UVC_BUFFER_QUEUE_DROP_INCOMPLETE	(1 << 2)
+
+struct uvc_buf {
+	void		*mem;
+	uint64_t	offset;
+	uint64_t	status;
+	uint64_t	index;
+	struct v4l2_buffer vbuf;
+
+	STAILQ_ENTRY(uvc_buf) link;
+};
+
+struct uvc_drv_video;
+struct uvc_buf_queue {
+	struct lock	mtx;
+	struct cv	io_cv;
+	struct uvc_drv_video *video;
+
+	struct kqinfo	sel;
+
+	void		*mem;
+	uint64_t	status;
+	uint64_t	flags;
+	uint64_t	seq;
+
+	uint64_t	buf_size;
+	uint64_t	buf_count;
+	struct uvc_buf	buf[UVC_BUF_MAX_BUFFERS];
+
+	STAILQ_HEAD(, uvc_buf)	consumer;
+	STAILQ_HEAD(, uvc_buf)	product;
+};
+
+extern void uvc_buf_queue_init(struct uvc_drv_video *v,
+	struct uvc_buf_queue *queue);
+extern int uvc_buf_queue_enable(struct uvc_buf_queue *queue);
+extern int uvc_buf_queue_disable(struct uvc_buf_queue *queue);
+extern int uvc_buf_queue_kqfilter(struct uvc_buf_queue *queue,
+	struct knote *kn);
+extern int uvc_buf_queue_req_bufs(struct uvc_buf_queue *queue,
+	uint32_t *count, uint32_t size);
+void uvc_buf_queue_free_bufs(struct uvc_buf_queue *queue);
+void uvc_buf_queue_set_drop_flag(struct uvc_buf_queue *queue);
+extern int uvc_buf_queue_query_buf(struct uvc_buf_queue *queue,
+	struct v4l2_buffer *vbuf);
+extern int uvc_buf_queue_mmap(struct uvc_buf_queue *queue,
+	vm_paddr_t *paddr, vm_offset_t offset);
+extern int uvc_buf_queue_queue_buf(struct uvc_buf_queue *queue,
+	struct v4l2_buffer *vbuf);
+extern int uvc_buf_queue_dequeue_buf(struct uvc_buf_queue *queue,
+	struct v4l2_buffer *vbuf, int nonblock);
+struct usb_page_cache;
+extern int
+uvc_buf_sell_buf(struct uvc_buf_queue *bq, struct usb_page_cache *pc,
+	usb_frlength_t offset, usb_frlength_t len, uint32_t finish,
+	uint8_t fid);
+extern int
+uvc_bulkbuf_sell_buf(struct uvc_buf_queue *bq,
+		struct usb_page_cache *pc, usb_frlength_t offset,
+		usb_frlength_t len,
+		usb_frlength_t actlen,
+		usb_frlength_t maxFramelen);
+extern int uvc_buf_reset_buf(struct uvc_buf_queue *bq);
+
+#endif /* end _DEV_USB_UVC_BUF_H */
diff --git a/sys/bus/u4b/uvc/uvc_ctrls.c b/sys/bus/u4b/uvc/uvc_ctrls.c
new file mode 100644
index 0000000000..72c9414cb6
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_ctrls.c
@@ -0,0 +1,1163 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *	Zhichao Li <Zhichao1.Li@Dell.com>
+ *	Pillar Liang <Pillar.Liang@Dellteam.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/condvar.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+
+#include <bus/u4b/usb.h>
+#define USB_DEBUG_VAR uvc_debug
+#include <bus/u4b/usb_debug.h>
+#include <bus/u4b/usbdi.h>
+
+#include <contrib/v4l/videodev.h>
+#include <contrib/v4l/videodev2.h>
+
+#include "uvc_drv.h"
+#include "uvc_buf.h"
+#include "uvc_v4l2.h"
+
+#define UVC_LOCK(lkp)   lockmgr(lkp, LK_EXCLUSIVE)
+#define UVC_UNLOCK(lkp) lockmgr(lkp, LK_RELEASE)
+
+/*
+ * DragonFly does not implement _SAFE macros because they are generally not
+ * actually safe in a MP environment, and so it is bad programming practice
+ * to use them.
+ */
+#define STAILQ_FOREACH_SAFE(scan, list, next, save)	\
+	for (scan = STAILQ_FIRST(list); (save = scan ? STAILQ_NEXT(scan, next) : NULL), scan; scan = save) 	\
+
+#define UVC_CTRL_DATA_CURRENT	0
+#define UVC_CTRL_DATA_BACKUP	1
+#define UVC_CTRL_DATA_MIN	2
+#define UVC_CTRL_DATA_MAX	3
+#define UVC_CTRL_DATA_RES	4
+#define UVC_CTRL_DATA_DEF	5
+#define UVC_CTRL_DATA_LAST	6
+
+#define ARRAY_SIZE(x)	(sizeof(x) / sizeof((x)[0]))
+
+#define UVC_BITMASK	      0x7
+#define UVC_BITSHIFT	      0x3
+#define UVC_VALMASK	      0x1
+
+static unsigned int
+uvc_test_bit(const uint8_t *buf, int b)
+{
+	return (buf[b >> UVC_BITSHIFT] >> (b & UVC_BITMASK)) & UVC_VALMASK;
+}
+
+
+static int
+uvc_ctrl_init_sub_info(struct uvc_ctrl_info *info,
+    struct uvc_ctrl_sub_info *sub_info)
+{
+	int ret = 0;
+
+	enum v4l2_ctrl_type type = V4L2_CTRL_TYPE_INTEGER;
+	uint8_t bit_offset = 0;
+	uint8_t bit_size = 0;
+	uint8_t name[32] = { 0 };
+	uint32_t superior_id = 0;
+	uint32_t superior_manual = 0;
+	uint32_t inferior_ids[2] = { 0 };
+
+	uint32_t id = sub_info->v4l2_id;
+
+	if (info->topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT) {
+		switch (id) {
+		case V4L2_CID_BRIGHTNESS:
+			ksprintf(name, "Brightness");
+			bit_size = 16;
+			break;
+		case V4L2_CID_CONTRAST:
+			ksprintf(name, "Contrast");
+			bit_size = 16;
+			break;
+		case V4L2_CID_HUE:
+			ksprintf(name, "Hue");
+			bit_size = 16;
+			superior_id = V4L2_CID_HUE_AUTO;
+			break;
+		case V4L2_CID_SATURATION:
+			ksprintf(name, "Saturation");
+			bit_size = 16;
+			break;
+		case V4L2_CID_SHARPNESS:
+			ksprintf(name, "Sharpness");
+			bit_size = 16;
+			break;
+		case V4L2_CID_GAMMA:
+			ksprintf(name, "Gamma");
+			bit_size = 16;
+			break;
+		case V4L2_CID_BACKLIGHT_COMPENSATION:
+			ksprintf(name, "Backlight Compensation");
+			bit_size = 16;
+			break;
+		case V4L2_CID_GAIN:
+			ksprintf(name, "Gain");
+			bit_size = 16;
+			break;
+		case V4L2_CID_POWER_LINE_FREQUENCY:
+			ksprintf(name, "Power Line Frequency");
+			type = V4L2_CTRL_TYPE_MENU;
+			bit_size = 2;
+			break;
+		case V4L2_CID_HUE_AUTO:
+			ksprintf(name, "Hue, Auto");
+			type = V4L2_CTRL_TYPE_BOOLEAN;
+			bit_size = 1;
+			sub_info->inferior_v4l2_ids[0] = V4L2_CID_HUE;
+			break;
+		case V4L2_CID_AUTO_WHITE_BALANCE:
+			if (info->selector ==
+			    UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL) {
+				ksprintf(name, "White Balance Temperature, Auto");
+				type = V4L2_CTRL_TYPE_BOOLEAN;
+				bit_size = 1;
+				inferior_ids[0] =
+				    V4L2_CID_WHITE_BALANCE_TEMPERATURE;
+			} else if (info->selector ==
+			    UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL) {
+				ksprintf(name, "White Balance Component, Auto");
+				type = V4L2_CTRL_TYPE_BOOLEAN;
+				bit_size = 1;
+				inferior_ids[0] = V4L2_CID_BLUE_BALANCE;
+				inferior_ids[1] = V4L2_CID_RED_BALANCE;
+			} else {
+				ret = EINVAL;
+			}
+			break;
+		case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+			ksprintf(name, "White Balance Temperature");
+			bit_size = 16;
+			superior_id = V4L2_CID_AUTO_WHITE_BALANCE;
+			break;
+		case V4L2_CID_BLUE_BALANCE:
+			ksprintf(name, "White Balance Blue Component");
+			bit_size = 16;
+			superior_id = V4L2_CID_AUTO_WHITE_BALANCE;
+			break;
+		case V4L2_CID_RED_BALANCE:
+			ksprintf(name, "White Balance Red Component");
+			bit_offset = 16;
+			bit_size = 16;
+			superior_id = V4L2_CID_AUTO_WHITE_BALANCE;
+			break;
+		default:
+			ret = EINVAL;
+			goto err_done;
+			break;
+		}
+	} else if (info->topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL) {
+		switch (id) {
+		case V4L2_CID_EXPOSURE_AUTO:
+			ksprintf(name, "Exposure, Auto");
+			type = V4L2_CTRL_TYPE_MENU;
+			bit_size = 4;
+			inferior_ids[0] = V4L2_CID_EXPOSURE_ABSOLUTE;
+			break;
+		case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+			ksprintf(name, "Exposure, Auto Priority");
+			type = V4L2_CTRL_TYPE_BOOLEAN;
+			bit_size = 1;
+			break;
+		case V4L2_CID_EXPOSURE_ABSOLUTE:
+			ksprintf(name, "Exposure (Absolute)");
+			bit_size = 32;
+			superior_id = V4L2_CID_EXPOSURE_AUTO;
+			superior_manual = V4L2_EXPOSURE_MANUAL;
+			break;
+		case V4L2_CID_FOCUS_ABSOLUTE:
+			ksprintf(name, "Focus (absolute)");
+			bit_size = 16;
+			superior_id = V4L2_CID_FOCUS_AUTO;
+			break;
+		case V4L2_CID_FOCUS_AUTO:
+			ksprintf(name, "Focus, Auto");
+			type = V4L2_CTRL_TYPE_BOOLEAN;
+			bit_size = 1;
+			inferior_ids[0] = V4L2_CID_FOCUS_ABSOLUTE;
+			break;
+		case V4L2_CID_IRIS_ABSOLUTE:
+			ksprintf(name, "Iris, Absolute");
+			bit_size = 16;
+			break;
+		case V4L2_CID_IRIS_RELATIVE:
+			ksprintf(name, "Iris, Relative");
+			bit_size = 8;
+			break;
+		case V4L2_CID_ZOOM_ABSOLUTE:
+			ksprintf(name, "Zoom, Absolute");
+			bit_size = 16;
+			break;
+		case V4L2_CID_ZOOM_CONTINUOUS:
+			ksprintf(name, "Zoom, Continuous");
+			bit_size = 0; // should use get/set
+			break;
+		case V4L2_CID_PAN_ABSOLUTE:
+			ksprintf(name, "Pan (Absolute)");
+			bit_size = 32;
+			break;
+		case V4L2_CID_TILT_ABSOLUTE:
+			ksprintf(name, "Tilt (Absolute)");
+			bit_offset = 32;
+			bit_size = 32;
+			break;
+		case V4L2_CID_PAN_SPEED:
+			ksprintf(name, "Pan (Speed)");
+			bit_size = 16;
+			break;
+		case V4L2_CID_TILT_SPEED:
+			ksprintf(name, "Tilt (Speed)");
+			bit_offset = 16;
+			bit_size = 16;
+			break;
+		case V4L2_CID_PRIVACY:
+			ksprintf(name, "Privacy");
+			type = V4L2_CTRL_TYPE_BOOLEAN;
+			bit_size = 1;
+			break;
+		default:
+			ret = EINVAL;
+			goto err_done;
+			break;
+		}
+	}
+
+	strcpy(sub_info->v4l2_name, name);
+	sub_info->v4l2_type = type;
+
+	sub_info->bit_offset = bit_offset;
+	sub_info->bit_size = bit_size;
+
+	sub_info->superior_v4l2_id = superior_id;
+	sub_info->superior_manual = superior_manual;
+	sub_info->inferior_v4l2_ids[0] = inferior_ids[0];
+	sub_info->inferior_v4l2_ids[1] = inferior_ids[1];
+
+	return 0;
+
+err_done:
+	kprintf("%s:%d Unknown topo 0x%x, selector 0x%x, v4l2 id 0x%x\n",
+			 __func__, __LINE__, info->topo_type, info->selector, id);
+
+	return ret;
+}
+
+static int
+uvc_ctrl_get_info_ct(uint8_t bit_idx, struct uvc_ctrl_info *info)
+{
+	int ret = 0;
+
+	memset(info, 0, sizeof(*info));
+
+	info->topo_type = UVC_TOPO_TYPE_CAMERA_TERMINAL;
+	info->index = bit_idx;
+
+	// Table 3-6 Camera Terminal Descriptor, bitmap bmControls
+	switch (bit_idx) {
+	case 0: // Table 4-9
+		info->selector = UVC_CT_SCANNING_MODE_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_RESTORE;
+		break;
+	case 1: // Table 4-10
+		info->selector = UVC_CT_AE_MODE_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_GET_DEF | UVC_CTRL_GET_RES | UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_BITMASK;
+		break;
+	case 2: // Table 4-11
+		info->selector = UVC_CT_AE_PRIORITY_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_BOOLEAN;
+		break;
+	case 3: // Table 4-12
+		info->selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL;
+		info->byte_size = 4;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 4: // Table 4-13
+		info->selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_RESTORE;
+		break;
+	case 5: // Table 4-14
+		info->selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 6: // Table 4-15
+		info->selector = UVC_CT_FOCUS_RELATIVE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_MIN |
+		    UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | UVC_CTRL_GET_DEF |
+		    UVC_CTRL_AUTO_UPDATE;
+		break;
+	case 7: // Table 4-18
+		info->selector = UVC_CT_IRIS_ABSOLUTE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 8: // Table 4-19
+		info->selector = UVC_CT_IRIS_RELATIVE_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+		break;
+	case 9: // Table 4-20
+		info->selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 10: // Table 4-21
+		info->selector = UVC_CT_ZOOM_RELATIVE_CONTROL;
+		info->byte_size = 3;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_MIN |
+		    UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | UVC_CTRL_GET_DEF |
+		    UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+		break;
+	case 11: // Table 4-22
+		info->selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL;
+		info->byte_size = 8;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+		break;
+	case 12: // Table 4-23
+		info->selector = UVC_CT_PANTILT_RELATIVE_CONTROL;
+		info->byte_size = 4;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+		break;
+	case 13: // Table 4-24
+		info->selector = UVC_CT_ROLL_ABSOLUTE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		break;
+	case 14: // Table 4-25
+		info->selector = UVC_CT_ROLL_RELATIVE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_MIN |
+		    UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | UVC_CTRL_GET_DEF |
+		    UVC_CTRL_AUTO_UPDATE;
+		break;
+	case 17: // Table 4-17
+		info->selector = UVC_CT_FOCUS_AUTO_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_BOOLEAN;
+		break;
+	case 18: // Table 4-26
+		info->selector = UVC_CT_PRIVACY_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_BOOLEAN;
+		break;
+	default: // Others to be implemented.
+		ret = EINVAL;
+		break;
+	}
+
+	if (ret != 0) {
+		memset(info, 0, sizeof(*info));
+	}
+	return ret;
+}
+
+static int
+uvc_ctrl_get_info_pu(uint8_t bit_idx, struct uvc_ctrl_info *info)
+{
+	int ret = 0;
+
+	memset(info, 0, sizeof(*info));
+
+	info->topo_type = UVC_TOPO_TYPE_PROCESSING_UNIT;
+	info->index = bit_idx;
+
+	// Table 3-8 Processing Unit Descriptor, bitmap bmControls
+	switch (bit_idx) {
+	case 0: // Table 4-31
+		info->selector = UVC_PU_BRIGHTNESS_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+		break;
+	case 1: // Table 4-32
+		info->selector = UVC_PU_CONTRAST_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 2: // Table 4-36
+		info->selector = UVC_PU_HUE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+		break;
+	case 3: // Table 4-38
+		info->selector = UVC_PU_SATURATION_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 4: // Table 4-39
+		info->selector = UVC_PU_SHARPNESS_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 5: // Table 4-40
+		info->selector = UVC_PU_GAMMA_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 6: // Table 4-41
+		info->selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 7: // Table 4-43
+		info->selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL;
+		info->byte_size = 4;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE | UVC_CTRL_AUTO_UPDATE;
+		info->data_type = UVC_CTRL_DATA_SIGNED;
+
+		break;
+	case 8: // Table 4-30
+		info->selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 9: // Table 4-34
+		info->selector = UVC_PU_GAIN_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_UNSIGNED;
+		break;
+	case 10: // Table 4-35
+		info->selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_ENUM;
+		break;
+	case 11: // Table 4-37
+		info->selector = UVC_PU_HUE_AUTO_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_BOOLEAN;
+		break;
+	case 12: // Table 4-42
+		info->selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_BOOLEAN;
+		break;
+	case 13: // Table 4-44
+		info->selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_CUR |
+		    UVC_CTRL_GET_DEF | UVC_CTRL_RESTORE;
+		info->data_type = UVC_CTRL_DATA_BOOLEAN;
+		break;
+	case 14: // Table 4-45
+		info->selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		break;
+	case 15: // Table 4-46
+		info->selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL;
+		info->byte_size = 2;
+		info->flags = UVC_CTRL_SET_CUR | UVC_CTRL_GET_RANGE |
+		    UVC_CTRL_RESTORE;
+		break;
+	case 16: // Table 4-47
+		info->selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_GET_CUR;
+		break;
+	case 17: // Table 4-48
+		info->selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL;
+		info->byte_size = 1;
+		info->flags = UVC_CTRL_GET_CUR;
+		break;
+	default:
+		ret = EINVAL;
+		break;
+	}
+
+	if (ret != 0) {
+		memset(info, 0, sizeof(*info));
+	}
+	return ret;
+}
+
+static const struct uvc_ctrl_menu_item exposure_mode_menu[] = {
+	{ 2, "Auto Mode" },
+	{ 1, "Manual Mode" },
+	{ 4, "Shutter Priority Mode" },
+	{ 8, "Aperture Priority Mode" },
+};
+
+static const struct uvc_ctrl_menu_item power_line_freq_menu[] = {
+	{ 0, "Disabled" },
+	{ 1, "50 Hz" },
+	{ 2, "60 Hz" },
+};
+
+static int
+uvc_ctrl_init_menu(struct uvc_ctrl_info *info)
+{
+	const struct uvc_ctrl_menu_item *src_menu_data = NULL;
+	struct uvc_ctrl_menu_item *dst_menu_data = NULL;
+	uint32_t item_num = 0;
+	uint32_t len = 0;
+
+	if (info->topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL &&
+	    info->selector == UVC_CT_AE_MODE_CONTROL) {
+		src_menu_data = exposure_mode_menu;
+		item_num = ARRAY_SIZE(exposure_mode_menu);
+
+	} else if (info->topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT &&
+	    info->selector == UVC_PU_POWER_LINE_FREQUENCY_CONTROL) {
+		src_menu_data = power_line_freq_menu;
+		item_num = ARRAY_SIZE(power_line_freq_menu);
+	} else {
+		return 0;
+	}
+
+	len = item_num * sizeof(struct uvc_ctrl_menu_item);
+	dst_menu_data = kmalloc(len, M_UVC, M_ZERO | M_WAITOK);
+
+	if (dst_menu_data == NULL) {
+		kprintf("%s:%d fail to alloc mem\n", __func__, __LINE__);
+		return ENOMEM;
+	}
+
+	memcpy(dst_menu_data, src_menu_data, len);
+
+	info->menus[0].item_num = item_num;
+	info->menus[0].menu_data = dst_menu_data;
+
+	return 0;
+}
+
+static void
+uvc_ctrl_init_sub_info_num(struct uvc_ctrl_info *info)
+{
+	info->sub_info_num = 1;
+
+	if (info->topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL) {
+		switch (info->selector) {
+		case UVC_CT_PANTILT_ABSOLUTE_CONTROL:
+		case UVC_CT_PANTILT_RELATIVE_CONTROL:
+			info->sub_info_num = 2;
+			break;
+		}
+	} else if (info->topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT) {
+		switch (info->selector) {
+		case UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL:
+			info->sub_info_num = 2;
+			break;
+		}
+	}
+}
+
+static int
+uvc_ctrl_build_mapping_ct(struct uvc_ctrl_info *info)
+{
+	int ret = 0;
+
+	switch (info->selector) {
+	case UVC_CT_AE_MODE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_EXPOSURE_AUTO;
+		break;
+	case UVC_CT_AE_PRIORITY_CONTROL:
+		info->sub_infos[0]->v4l2_id =
+		    V4L2_CID_EXPOSURE_AUTO_PRIORITY;
+		break;
+	case UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_EXPOSURE_ABSOLUTE;
+		break;
+	case UVC_CT_FOCUS_ABSOLUTE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_FOCUS_ABSOLUTE;
+		break;
+	case UVC_CT_FOCUS_AUTO_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_FOCUS_AUTO;
+		break;
+	case UVC_CT_IRIS_ABSOLUTE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_IRIS_ABSOLUTE;
+		break;
+	case UVC_CT_IRIS_RELATIVE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_IRIS_RELATIVE;
+		break;
+	case UVC_CT_ZOOM_ABSOLUTE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_ZOOM_ABSOLUTE;
+		break;
+	case UVC_CT_ZOOM_RELATIVE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_ZOOM_CONTINUOUS;
+		break;
+	case UVC_CT_PANTILT_ABSOLUTE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_PAN_ABSOLUTE;
+		info->sub_infos[1]->v4l2_id = V4L2_CID_TILT_ABSOLUTE;
+		break;
+	case UVC_CT_PANTILT_RELATIVE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_PAN_SPEED;
+		info->sub_infos[1]->v4l2_id = V4L2_CID_TILT_SPEED;
+		break;
+	case UVC_CT_PRIVACY_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_PRIVACY;
+		break;
+	default:
+		ret = EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+uvc_ctrl_build_mapping_pu(struct uvc_ctrl_info *info)
+{
+	int ret = 0;
+
+	switch (info->selector) {
+	case UVC_PU_BRIGHTNESS_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_BRIGHTNESS;
+		break;
+	case UVC_PU_CONTRAST_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_CONTRAST;
+		break;
+	case UVC_PU_HUE_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_HUE;
+		break;
+	case UVC_PU_SATURATION_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_SATURATION;
+		break;
+	case UVC_PU_SHARPNESS_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_SHARPNESS;
+		break;
+	case UVC_PU_GAMMA_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_GAMMA;
+		break;
+	case UVC_PU_BACKLIGHT_COMPENSATION_CONTROL:
+		info->sub_infos[0]->v4l2_id =
+		    V4L2_CID_BACKLIGHT_COMPENSATION;
+		break;
+	case UVC_PU_GAIN_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_GAIN;
+		break;
+	case UVC_PU_POWER_LINE_FREQUENCY_CONTROL:
+		info->sub_infos[0]->v4l2_id =
+		    V4L2_CID_POWER_LINE_FREQUENCY;
+		break;
+	case UVC_PU_HUE_AUTO_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_HUE_AUTO;
+		break;
+	case UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_AUTO_WHITE_BALANCE;
+		break;
+	case UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
+		info->sub_infos[0]->v4l2_id =
+		    V4L2_CID_WHITE_BALANCE_TEMPERATURE;
+		break;
+	case UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_AUTO_WHITE_BALANCE;
+		break;
+	case UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL:
+		info->sub_infos[0]->v4l2_id = V4L2_CID_BLUE_BALANCE;
+		info->sub_infos[1]->v4l2_id = V4L2_CID_RED_BALANCE;
+		break;
+	default:
+		ret = EINVAL;
+	}
+
+	return ret;
+}
+
+static int
+uvc_ctrl_build_mapping(struct uvc_ctrl_info *info)
+{
+	int ret = 0;
+	uint8_t i = 0;
+
+	enum uvc_topo_type topo_type = info->topo_type;
+
+	uint8_t sub_info_num = info->sub_info_num;
+	struct uvc_ctrl_sub_info *sub_info = NULL;
+
+	if (sub_info_num == 0) {
+		return 0;
+	}
+
+	for (i = 0; i < sub_info_num; i++) {
+		sub_info = kmalloc(sizeof(*sub_info), M_UVC, M_ZERO | M_WAITOK);
+
+		if (sub_info == NULL) {
+			kprintf("%s:%d fail to alloc mem\n", __func__, __LINE__);
+			ret = ENOMEM;
+			goto err_done;
+		}
+
+		sub_info->uvc_info = info;
+		info->sub_infos[i] = sub_info;
+	}
+
+	if (topo_type == UVC_TOPO_TYPE_CAMERA_TERMINAL) {
+		ret = uvc_ctrl_build_mapping_ct(info);
+	} else if (topo_type == UVC_TOPO_TYPE_PROCESSING_UNIT) {
+		ret = uvc_ctrl_build_mapping_pu(info);
+	} else {
+		ret = EINVAL;
+	}
+
+	if (ret != 0) {
+		goto err_done;
+	}
+
+	for (int i = 0; i < sub_info_num; i++) {
+		uvc_ctrl_init_sub_info(info, info->sub_infos[i]);
+	}
+
+	return 0;
+
+err_done:
+	for (i = 0; i < MAX_MAPPING_NUM; i++) {
+		if (info->sub_infos[i] != NULL) {
+			kfree(info->sub_infos[i], M_UVC);
+			info->sub_infos[i] = NULL;
+		}
+	}
+
+	return ret;
+}
+
+static int
+uvc_ctrl_initialize_control(struct uvc_control *ctrl)
+{
+	int ret = 0;
+
+	uint8_t bit_idx = ctrl->index;
+	uint16_t node_type = UVC_ENT_TYPE(ctrl->topo_node);
+
+	struct uvc_ctrl_info tmp_info;
+
+	switch (node_type) {
+	case UVC_ITT_CAMERA:
+		ret = uvc_ctrl_get_info_ct(bit_idx, &tmp_info);
+		if (ret != 0) {
+			return EINVAL;
+		}
+		break;
+	case UDESCSUB_VC_PROCESSING_UNIT:
+		ret = uvc_ctrl_get_info_pu(bit_idx, &tmp_info);
+		if (ret != 0) {
+			return EINVAL;
+		}
+		break;
+	default:
+		return 0;
+	}
+
+	ctrl->uvc_data = kmalloc(tmp_info.byte_size * UVC_CTRL_DATA_LAST + 1, M_UVC,
+	    M_ZERO | M_WAITOK);
+
+	if (ctrl->uvc_data == NULL) {
+		kprintf("%s:%d fail to allocate memory\n", __func__, __LINE__);
+		ret = ENOMEM;
+		goto err;
+	}
+
+	uvc_ctrl_init_sub_info_num(&tmp_info);
+
+	ret = uvc_ctrl_build_mapping(&tmp_info);
+	if (0 != ret) {
+		goto err;
+	}
+
+	ret = uvc_ctrl_init_menu(&tmp_info);
+	if (0 != ret) {
+		goto err;
+	}
+
+	ctrl->info = tmp_info;
+	ctrl->initialized = 1;
+
+	return 0;
+
+err:
+	if (ctrl->uvc_data != NULL) {
+		kfree(ctrl->uvc_data, M_UVC);
+		ctrl->uvc_data = NULL;
+	}
+
+	return ret;
+}
+
+void
+uvc_ctrl_destroy_mappings(struct uvc_control *ctrl)
+{
+	uint8_t i = 0;
+
+	if (!ctrl)
+		return;
+
+	for (i = 0; i < MAX_MAPPING_NUM; i++) {
+		if (ctrl->info.sub_infos[i] != NULL) {
+			DPRINTF("removing v4l2 mapping '%s' \n",
+				ctrl->info.sub_infos[i]->v4l2_name);
+
+			kfree(ctrl->info.sub_infos[i], M_UVC);
+			ctrl->info.sub_infos[i] = NULL;
+		}
+
+		if (ctrl->info.menus[i].menu_data != NULL) {
+			kfree(ctrl->info.menus[i].menu_data, M_UVC);
+			ctrl->info.menus[i].menu_data = NULL;
+		}
+	}
+
+	ctrl->info.sub_info_num = 0;
+
+	return;
+}
+
+static unsigned int
+uvc_ctrl_count_control(const uint8_t *bmCtrls, uint8_t bCtrlSize)
+{
+	int i = 0;
+	unsigned int count = 0;
+	const uint8_t *data = bmCtrls;
+
+	if (data == NULL || bCtrlSize == 0)
+		return 0;
+
+	for (i = 0; i < bCtrlSize * 8; i++) {
+		if ((data[i >> UVC_BITSHIFT] >>
+		     (i & UVC_BITMASK)) & UVC_VALMASK)
+			count++;
+	}
+	return count;
+}
+
+int
+uvc_ctrl_init_dev(struct uvc_softc *sc, struct uvc_drv_ctrl *ctrls)
+{
+	struct uvc_topo_node *topo_node, *tmp;
+	struct uvc_control *ctrl = NULL;
+	uint8_t bCtrlSize = 0;
+	uint32_t nctrls = 0;
+	uint8_t *bmCtrls = NULL;
+	uint8_t i = 0;
+
+	struct uvc_xu_node_info *node_info_xu = NULL;
+	struct uvc_pu_node_info *node_info_pu = NULL;
+	struct uvc_ct_node_info *node_info_ct = NULL;
+
+	if (!ctrls) {
+		return EINVAL;
+	}
+
+	STAILQ_FOREACH_SAFE(topo_node, &ctrls->topo_nodes, link, tmp) {
+		if (UVC_ENT_TYPE(topo_node) == UDESCSUB_VC_EXTENSION_UNIT) {
+			node_info_xu = (struct uvc_xu_node_info*)topo_node->node_info;
+
+			bmCtrls = node_info_xu->bmControls;
+			bCtrlSize = node_info_xu->bControlSize;
+		} else if (UVC_ENT_TYPE(topo_node) == UDESCSUB_VC_PROCESSING_UNIT) {
+			node_info_pu = (struct uvc_pu_node_info*)topo_node->node_info;
+
+			bmCtrls = node_info_pu->bmControls;
+			bCtrlSize = node_info_pu->bControlSize;
+		} else if (UVC_ENT_TYPE(topo_node) == UVC_ITT_CAMERA) {
+			node_info_ct = (struct uvc_ct_node_info*)topo_node->node_info;
+
+			bmCtrls = node_info_ct->bmControls;
+			bCtrlSize = node_info_ct->bControlSize;
+		} else
+			continue;
+
+		nctrls = uvc_ctrl_count_control(bmCtrls, bCtrlSize);
+		if (nctrls == 0)
+			continue;
+		topo_node->controls = kmalloc(nctrls * sizeof(*ctrl), M_UVC,
+		    M_ZERO | M_WAITOK);
+		if (!topo_node->controls)
+			return ENOMEM;
+		topo_node->controls_num = nctrls;
+
+		ctrl = topo_node->controls;
+		for (i = 0; i < bCtrlSize * 8; i++) {
+			if (uvc_test_bit(bmCtrls, i) == 0)
+				continue;
+
+			ctrl->topo_node = topo_node;
+			ctrl->index = i;
+
+			uvc_ctrl_initialize_control(ctrl);
+
+			ctrl++;
+		}
+	}
+
+	return 0;
+}
+
+static int
+uvc_query_v4l2_ctrl_sub(struct uvc_control *ctrl, struct v4l2_queryctrl *query)
+{
+	int i = 0;
+
+	uint8_t sub_info_idx = ctrl->info.cached_sub_info_idx;
+
+	struct uvc_ctrl_menu_item *menu_items =
+	    ctrl->info.menus[sub_info_idx].menu_data;
+	uint8_t menu_item_num = ctrl->info.menus[sub_info_idx].item_num;
+
+	struct uvc_ctrl_sub_info *sub_info = ctrl->info.sub_infos[sub_info_idx];
+
+	memset(query, 0, sizeof(*query));
+
+	if (sub_info == NULL) {
+		return EINVAL;
+	}
+
+	query->id = sub_info->v4l2_id;
+	query->type = sub_info->v4l2_type;
+	strlcpy(query->name, sub_info->v4l2_name, sizeof(query->name));
+
+	query->flags = 0;
+
+	switch (sub_info->v4l2_type) {
+	case V4L2_CTRL_TYPE_BOOLEAN:
+		query->minimum = 0;
+		query->maximum = 1;
+		query->step = 1;
+		goto end;
+
+	case V4L2_CTRL_TYPE_BUTTON:
+		query->minimum = 0;
+		query->maximum = 0;
+		query->step = 0;
+		goto end;
+
+	case V4L2_CTRL_TYPE_MENU:
+		query->minimum = 0;
+		query->maximum = menu_item_num - 1;
+		query->step = 1;
+
+		for (i = 0; i < menu_item_num; i++) {
+			if (menu_items[i].value == query->default_value) {
+				query->default_value = i;
+				break;
+			}
+		}
+		goto end;
+
+	default:
+		break;
+	}
+
+	/*todo*/
+	DPRINTF("WARNING __TO_BE_IMPLEMENT__ other par %s\n", __func__);
+end:
+	return 0;
+}
+
+static struct uvc_control *
+uvc_search_control_sub(struct uvc_topo_node *topo_node, uint32_t v4l2_id)
+{
+	unsigned int i;
+	unsigned int j;
+
+	struct uvc_control *ctrl = NULL;
+
+	if (topo_node == NULL) {
+		return NULL;
+	}
+
+	for (i = 0; i < topo_node->controls_num; ++i) {
+		ctrl = &topo_node->controls[i];
+
+		if (!ctrl->initialized)
+			continue;
+
+		for (j = 0; j < MAX_MAPPING_NUM; j++) {
+			if (ctrl->info.sub_infos[j] == NULL) {
+				continue;
+			}
+
+			if (ctrl->info.sub_infos[j]->v4l2_id == v4l2_id) {
+				ctrl->info.cached_sub_info_idx = j;
+				return ctrl;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static struct uvc_control *
+uvc_search_control(struct uvc_drv_ctrl *ctrls, uint32_t v4l2_id)
+{
+	struct uvc_control *ret_ctrl = NULL;
+	struct uvc_topo_node *topo_node = NULL, *tmp = NULL;
+
+	if (v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+		DPRINTF("Not support V4L2_CTRL_FLAG_NEXT_CTRL (0x%08x )\n",
+		    v4l2_id);
+		return NULL;
+	}
+
+	/* Mask the query flags. */
+	v4l2_id &= V4L2_CTRL_ID_MASK;
+
+	/* Find the control. */
+	STAILQ_FOREACH_SAFE(topo_node, &ctrls->topo_nodes, link, tmp) {
+		ret_ctrl = uvc_search_control_sub(topo_node, v4l2_id);
+		if (ret_ctrl != NULL) {
+			return ret_ctrl;
+		}
+	}
+
+	DPRINTF("Control 0x%08x not found.\n", v4l2_id);
+	return NULL;
+}
+
+int
+uvc_query_v4l2_ctrl(struct uvc_drv_video *video, struct v4l2_queryctrl *query)
+{
+	struct uvc_control *ctrl;
+	int ret = EINVAL;
+
+	UVC_LOCK(&video->ctrl->mtx);
+
+	ctrl = uvc_search_control(video->ctrl, query->id);
+	if (ctrl == NULL) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	ret = uvc_query_v4l2_ctrl_sub(ctrl, query);
+done:
+	UVC_UNLOCK(&video->ctrl->mtx);
+	return ret;
+}
+
+int
+uvc_query_v4l2_menu(struct uvc_drv_video *video,
+		    struct v4l2_querymenu *qm)
+{
+	struct uvc_control *ctrl;
+	uint8_t sub_info_idx = 0;
+	int ret = 0;
+	int id = qm->id;
+	int index = qm->index;
+
+	memset(qm, 0, sizeof(*qm));
+	qm->id = id;
+	qm->index = index;
+
+	UVC_LOCK(&video->ctrl->mtx);
+
+	ctrl = uvc_search_control(video->ctrl, qm->id);
+	if (ctrl == NULL) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	sub_info_idx = ctrl->info.cached_sub_info_idx;
+
+	if (ctrl->info.menus[sub_info_idx].menu_data == NULL) {
+		ret = EINVAL;
+		goto done;
+	}
+
+	strlcpy(qm->name,
+		ctrl->info.sub_infos[sub_info_idx]->v4l2_name,
+		sizeof(qm->name));
+
+done:
+	UVC_UNLOCK(&video->ctrl->mtx);
+	return ret;
+}
diff --git a/sys/bus/u4b/uvc/uvc_drv.c b/sys/bus/u4b/uvc/uvc_drv.c
new file mode 100644
index 0000000000..9de5dd9aa9
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_drv.c
@@ -0,0 +1,2789 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *	Zhichao Li <Zhichao1.Li@Dell.com>
+ *	Pillar Liang <Pillar.Liang@Dellteam.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/condvar.h>
+
+#include <bus/u4b/usb.h>
+#include <bus/u4b/usbdi.h>
+#include <bus/u4b/usbdi_util.h>
+#include <bus/u4b/usb_request.h>
+
+#define USB_DEBUG_VAR uvc_debug
+#include <bus/u4b/usb_debug.h>
+#include <bus/u4b/usb_core.h>
+#include <bus/u4b/usb_device.h>
+#include <bus/u4b/quirk/usb_quirk.h>
+
+#include <contrib/v4l/videodev.h>
+#include <contrib/v4l/videodev2.h>
+
+#include "uvc_drv.h"
+#include "uvc_buf.h"
+#include "uvc_v4l2.h"
+
+#define UVC_LOCK(lkp)   lockmgr(lkp, LK_EXCLUSIVE)
+#define UVC_UNLOCK(lkp) lockmgr(lkp, LK_RELEASE)
+#define UVC_ASSERT_LOCKED(lkp) KKASSERT(lockowned(lkp))
+
+/*
+ * DragonFly does not implement _SAFE macros because they are generally not
+ * actually safe in a MP environment, and so it is bad programming practice
+ * to use them.
+ */
+#define STAILQ_FOREACH_SAFE(scan, list, next, save)	\
+	for (scan = STAILQ_FIRST(list); (save = scan ? STAILQ_NEXT(scan, next) : NULL), scan; scan = save) 	\
+
+
+int uvc_debug = 0;
+#ifdef USB_DEBUG
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uvc, CTLFLAG_RW, 0, "USB uvc");
+SYSCTL_INT(_hw_usb_uvc, OID_AUTO, debug, CTLFLAG_RW, &uvc_debug, 0, "Debug level");
+TUNABLE_INT("hw.usb.uvc.debug", &uvc_debug); // XXX
+#endif
+
+MALLOC_DEFINE(M_UVC, "uvc", "USB Video Camera");
+#define	UVC_BULK_HS_BUFFER_SIZE   (64 * 1024) /* bytes */
+
+/*
+ * Controls
+ * Formats
+ */
+static struct uvc_drv_format_desc uvc_fmts[] = {
+	{
+		.name	=	"YUV 4:2:2 (YUYV)",
+		.guid	=	UVC_GUID_FORMAT_YUY2,
+		.fcc	=	V4L2_PIX_FMT_YUYV,
+	},
+	{
+		.name	=	"YUV 4:2:0 (NV12)",
+		.guid	=	UVC_GUID_FORMAT_NV12,
+		.fcc	=	V4L2_PIX_FMT_NV12,
+	},
+	{
+		.name	=	"MJPEG",
+		.guid	=	UVC_GUID_FORMAT_MJPEG,
+		.fcc	=	V4L2_PIX_FMT_MJPEG,
+	},
+	{
+		.name	=	"YVU 4:2:0 (YV12)",
+		.guid	=	UVC_GUID_FORMAT_YV12,
+		.fcc	=	V4L2_PIX_FMT_YVU420,
+	},
+	{
+		.name	=	"YUV 4:2:0 (I420)",
+		.guid	=	UVC_GUID_FORMAT_I420,
+		.fcc	=	V4L2_PIX_FMT_YUV420,
+	},
+	{
+		.name	=	"YUV 4:2:2 (UYVY)",
+		.guid	=	UVC_GUID_FORMAT_UYVY,
+		.fcc	=	V4L2_PIX_FMT_UYVY,
+	},
+	{
+		.name	=	"Greyscale",
+		.guid	=	UVC_GUID_FORMAT_Y800,
+		.fcc	=	V4L2_PIX_FMT_GREY,
+	},
+	{
+		.name	=	"RGB Bayer",
+		.guid	=	UVC_GUID_FORMAT_BY8,
+		.fcc	=	V4L2_PIX_FMT_SBGGR8,
+	},
+	{
+		.name	=	"H.264",
+		.guid	=	UVC_GUID_FORMAT_H264,
+		.fcc	=	V4L2_PIX_FMT_H264,
+	},
+
+};
+
+const unsigned char
+vc_ext_h264ctrl[16] =
+{
+	0x41, 0x76, 0x9e, 0xa2,
+	0x4, 0xde, 0xe3, 0x47,
+	0x8b, 0x2b, 0xf4, 0x34,
+	0x1a, 0xff, 0x0, 0x3b
+};
+
+__unused static void
+dump_hex(void *src, int len)
+{
+	int i;
+	unsigned char *pkt = src;
+
+	if (!pkt || !len)
+		return;
+
+	for (i = 0; i < len; i++) {
+		kprintf("%02x  ", pkt[i]);
+		if ((i + 1) % 8 == 0)
+			kprintf("    ");
+		if ((i + 1) % 16 == 0)
+			kprintf("\n");
+	}
+	kprintf("\n");
+}
+
+static uint8_t
+usbd_iface_num_to_index(struct usb_device *udev, int num)
+{
+	struct usb_interface_descriptor *id;
+	struct usb_interface *iface;
+	struct usb_idesc_parse_state ips;
+	uint8_t index = -1;
+
+	memset(&ips, 0x0, sizeof(ips));
+	while ((id = usb_idesc_foreach(udev->cdesc, &ips))) {
+		iface = udev->ifaces + ips.iface_index;
+		if (iface->idesc->bInterfaceNumber == num &&
+			ips.iface_index_alt == 0) {
+			index = ips.iface_index;
+			break;
+		}
+	}
+
+	return index;
+}
+
+static uint8_t
+usbd_iface_get_altsetting_num(struct usb_device *udev,
+	struct usb_interface *iface)
+{
+	struct usb_descriptor *desc;
+	int num = 1;
+	uint8_t iface_num = iface->idesc->bInterfaceNumber;
+
+	desc = (struct usb_descriptor *)(iface->idesc);
+	while ((desc = usb_desc_foreach(udev->cdesc, desc))) {
+		if (desc->bDescriptorType == UDESC_INTERFACE) {
+			if (desc->bDescriptorSubtype != iface_num)
+				break;
+			num++;
+		}
+	}
+
+	return num;
+}
+
+static int
+uvc_drv_do_request(struct usb_device *udev, uint8_t query, uint8_t unit,
+	uint8_t iface_num, uint8_t cs, void *data, uint16_t size, int timeout)
+{
+	struct usb_device_request req;
+	int err;
+	uint8_t type = UT_CLASS | UT_INTERFACE;
+
+	memset(&req, 0x00, sizeof(req));
+	req.bRequest = query;
+	type |= (query & 0x80) ? UT_READ : UT_WRITE;
+	req.bmRequestType = type;
+	USETW(req.wValue, cs << 8);
+	USETW(req.wIndex, unit << 8 | iface_num);
+	USETW(req.wLength, size);
+
+	err = usbd_do_request_flags(udev, NULL, &req, data, 0, NULL, timeout);
+	if (err)
+		kprintf("%s-%d-%s\n", __func__, query, usbd_errstr(err));
+	return err;
+}
+
+static void
+uvc_drv_show_video_ctrl(struct uvc_data_request *req)
+{
+	DPRINTF("hint:%x formatindex:%u frameindex:%u itv:%u\n",
+		UGETW(req->wHint), req->bFormatIndex, req->bFrameIndex,
+		UGETDW(req->dwFrameInterval));
+	DPRINTF("keyrate:%u prate:%u compq:%u compwinsize:%u\n",
+		UGETW(req->wKeyFrameRate), UGETW(req->wPFrameRate),
+		UGETW(req->wCompQuality), UGETW(req->wCompWindowSize));
+	DPRINTF("delay:%u framesize:%u payloadsize:%u framinginfo:%x\n", UGETW(req->wDelay),
+		UGETDW(req->dwMaxFrameSize), UGETDW(req->dwMaxPayloadSize), req->bFramingInfo);
+}
+
+static __inline int
+uvc_drv_get_max_ctrl_size(struct uvc_drv_video *video)
+{
+       if (video->ctrl->revision < 0x0110)
+               return 26;
+       else if (video->ctrl->revision < 0x0150)
+               return 34;
+       else
+               return 48;
+}
+
+static int
+uvc_drv_set_video_ctrl(struct uvc_drv_video *video,
+	struct uvc_data_request *req, int probe)
+{
+	int ret = 0;
+	int size = uvc_drv_get_max_ctrl_size(video);
+
+	UVC_ASSERT_LOCKED(&video->mtx);
+	UVC_UNLOCK(&video->mtx);
+
+	KKASSERT(sizeof(*req) >= size);
+
+	ret = uvc_drv_do_request(video->sc->udev, SET_CUR, 0,
+		video->data->iface_index,
+		probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL,
+		req, size, USB_DEFAULT_TIMEOUT);
+
+	UVC_LOCK(&video->mtx);
+
+	return ret;
+}
+
+static void
+uvc_drv_fixup_req(struct uvc_drv_video *video,
+		       struct uvc_data_request *req)
+{
+	struct uvc_data_format *fmt = NULL;
+	struct uvc_data_frame *frm = NULL;
+	uint32_t sizeimage = 0;
+	uint8_t i = 0;
+
+	for (i = 0; i < video->data->nfmt; i++) {
+		if (req->bFormatIndex == video->data->fmt[i].index) {
+			fmt = &video->data->fmt[i];
+			break;
+		}
+	}
+	if (fmt == NULL)
+		return;
+
+	for (i = 0; i < fmt->nfrm; i++) {
+		if (req->bFrameIndex == fmt->frm[i].index) {
+			frm = &fmt->frm[i];
+			break;
+		}
+	}
+	if (frm == NULL)
+		return;
+
+	/* Logitech BRIO (046d:085e) give wrong NV12 dwMaxFrameSize */
+	if (!(fmt->flags & UVC_FMT_FLAG_COMPRESSED)) {
+		sizeimage = frm->width * frm->height / 8 * fmt->bpp;
+		if (sizeimage != UGETDW(req->dwMaxFrameSize)) {
+			kprintf("sizeimage %d != dmMaxFrameSize %d\n",
+				sizeimage, UGETDW(req->dwMaxFrameSize));
+			USETDW(req->dwMaxFrameSize, sizeimage);
+		}
+	}
+
+}
+
+static int
+uvc_drv_get_video_ctrl(struct uvc_drv_video *video,
+	struct uvc_data_request *req, int probe, uint8_t query)
+{
+	struct uvc_data_request tmp;
+	int size, ret;
+
+	memset(&tmp, 0x0, sizeof(tmp));
+	size = uvc_drv_get_max_ctrl_size(video);
+
+	UVC_ASSERT_LOCKED(&video->mtx);
+	UVC_UNLOCK(&video->mtx);
+
+	KKASSERT(size <= sizeof(tmp));
+
+	ret = uvc_drv_do_request(video->sc->udev, query, 0,
+		video->data->iface_index,
+		probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL,
+		&tmp, size, USB_DEFAULT_TIMEOUT);
+	UVC_LOCK(&video->mtx);
+	if (ret)
+		return ret;
+
+	if (query != GET_MIN && query != GET_MAX)
+		uvc_drv_fixup_req(video, &tmp);
+	memcpy(req, &tmp, size);
+	return 0;
+}
+
+int
+uvc_drv_xu_ctrl_query(struct uvc_drv_video *v, struct uvc_xu_control_query *q)
+{
+	int ret;
+	char tmp[128];
+
+	DPRINTF("%s query:%x unit:%d selector:%d size:%d\n",
+		__func__, q->query, q->unit, q->selector, q->size);
+
+	memset(tmp, 0, 128);
+
+	if (!(q->query & 0x80))
+		ret = copyin(q->data, tmp, q->size);
+
+	UVC_LOCK(&v->mtx);
+	ret = uvc_drv_do_request(v->sc->udev, q->query, q->unit, 0, q->selector,
+		tmp, q->size, USB_DEFAULT_TIMEOUT);
+	if (ret)
+		DPRINTF("%s %d ret:%d\n", __func__, __LINE__, ret);
+	else {
+		//hexdump(tmp, q->size, 0, 0);
+		if (q->query & 0x80)
+			ret = copyout(tmp, q->data, q->size);
+		else
+			if (q->unit == v->ctrl->h264id) {
+				DPRINTF("--%s open h264\n", __func__);
+				v->htsf = 1;
+			}
+	}
+	UVC_UNLOCK(&v->mtx);
+
+	return ret;
+}
+
+static int
+uvc_drv_probe_video(struct uvc_drv_video *video, struct uvc_data_request *req)
+{
+	struct uvc_data_request reqmin, reqmax;
+	int ret, i;
+	uint32_t ps;
+
+	UVC_LOCK(&video->mtx);
+
+	ret = uvc_drv_set_video_ctrl(video, req, 1);
+	if (ret)
+		goto done;
+
+	if (!(video->sc->quirks & UVC_QUIRK_PROBE_MINMAX)) {
+		ret = uvc_drv_get_video_ctrl(video, &reqmin, 1, GET_MIN);
+		if (ret)
+			goto done;
+
+		if (uvc_debug >= 5)
+			uvc_drv_show_video_ctrl(&reqmin);
+		ret = uvc_drv_get_video_ctrl(video, &reqmax, 1, GET_MAX);
+		if (ret)
+			goto done;
+		if (uvc_debug >= 5)
+			uvc_drv_show_video_ctrl(&reqmax);
+		USETW(req->wCompQuality, UGETW(reqmax.wCompQuality));
+	}
+
+	for (i = 0; i < 2; i++) {
+		ret = uvc_drv_set_video_ctrl(video, req, 1);
+		if (ret)
+			goto done;
+		ret = uvc_drv_get_video_ctrl(video, req, 1, GET_CUR);
+		if (ret)
+			goto done;
+		if (video->data->num_altsetting == 1)
+			break;
+		ps = UGETDW(req->dwMaxPayloadSize);
+		if (ps <= video->data->maxpsize)
+			break;
+
+		if (video->sc->quirks & UVC_QUIRK_PROBE_MINMAX) {
+			ret = ENOSPC;
+			goto done;
+		}
+
+		USETW(req->wKeyFrameRate, UGETW(reqmin.wKeyFrameRate));
+		USETW(req->wPFrameRate, UGETW(reqmin.wPFrameRate));
+		USETW(req->wCompQuality, UGETW(reqmax.wCompQuality));
+		USETW(req->wCompWindowSize, UGETW(reqmin.wCompWindowSize));
+	}
+
+done:
+	UVC_UNLOCK(&video->mtx);
+	return ret;
+}
+
+int
+uvc_drv_set_video(struct uvc_drv_video *video,
+	struct uvc_data_request *req, struct uvc_data_format *fmt,
+	struct uvc_data_frame *frm)
+{
+	int ret;
+	struct uvc_softc *sc = video->sc;
+
+	DPRINTF("set video %p\n", curthread);
+
+	uvc_drv_show_video_ctrl(req);
+	UVC_LOCK(&video->mtx);
+
+	if (video->enable) {
+		DPRINTF("video is enable\n");
+		UVC_UNLOCK(&video->mtx);
+		return EBUSY;
+	}
+	if (sc->quirks & UVC_COMMIT_IN_ADVANCE)
+		ret = uvc_drv_set_video_ctrl(video, req, 0);
+	else
+		ret = uvc_drv_set_video_ctrl(video, req, 1);
+	if (ret) {
+		DPRINTF("video CONTROL COMMIT fault %d\n", ret);
+		UVC_UNLOCK(&video->mtx);
+		return ret;
+	}
+
+	video->cur_fmt = fmt;
+	video->cur_frm = frm;
+	memcpy(&video->req, req, sizeof(struct uvc_data_request));
+
+	UVC_UNLOCK(&video->mtx);
+	return 0;
+}
+
+int
+uvc_drv_check_video_context(struct uvc_drv_video *v, unsigned char *data,
+	uint32_t len)
+{
+	if (!v->cur_fmt || v->cur_fmt->fcc != V4L2_PIX_FMT_MJPEG || v->htsf)
+		return 0;
+
+	if (data[0] != 0xFF || data[1] != 0xD8)
+		return 1;
+
+	return 0;
+}
+
+static uint32_t
+uvc_drv_try_frame_interval(struct uvc_data_frame *frame, uint32_t interval)
+{
+	unsigned int i;
+
+	if (frame->interval_type) {
+		uint32_t best = -1, dist, val;
+
+		for (i = 0; i < frame->interval_type; ++i) {
+			val = frame->interval[i].val;
+			dist = interval > val ? interval - val : val - interval;
+
+			if (dist > best)
+				break;
+
+			best = dist;
+		}
+
+		interval = frame->interval[i-1].val;
+	} else {
+		const uint32_t min = frame->interval[0].val;
+		const uint32_t max = frame->interval[1].val;
+		const uint32_t step = frame->interval[2].val;
+
+		interval = min + (interval - min + step/2) / step * step;
+		if (interval > max)
+			interval = max;
+	}
+
+	return interval;
+}
+
+int
+uvc_drv_get_pixelaspect(void)
+{
+	DPRINTF("there is no such function in linux yet, either\n");
+	return 0;
+}
+
+int
+uvc_drv_get_selection(struct uvc_drv_video *v, struct v4l2_selection *sel)
+{
+	if (sel->type != v->type)
+		return EINVAL;
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		if (v->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		break;
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		if (v->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sel->r.left = 0;
+	sel->r.top = 0;
+	UVC_LOCK(&v->mtx);
+	sel->r.width = v->cur_frm->width;
+	sel->r.height = v->cur_frm->height;
+	UVC_UNLOCK(&v->mtx);
+
+	return 0;
+}
+
+int
+uvc_drv_get_v4l2_fmt(struct uvc_drv_video *v, struct v4l2_format *vfmt)
+{
+	struct uvc_data_format *fmt = NULL;
+	struct uvc_data_frame *frm = NULL;
+	struct v4l2_pix_format *pix = &vfmt->fmt.pix;
+	uint8_t *b = NULL;
+#if 0
+	int ret = 0;
+	struct uvc_data_request *req = &(v->req);
+
+	DPRINTF("v4l2-dev get format\n");
+	ret = uvc_drv_get_video_ctrl(v, req, 1, GET_CUR);
+	if (ret) {
+		DPRINTF("Get video ctrl failed\n");
+		return ret;
+	}
+
+	uvc_drv_show_video_ctrl(req);
+
+	if (v->data->nfmt >= req->bFormatIndex && req->bFormatIndex > 0) {
+		fmt = v->data->fmt + (req->bFormatIndex - 1);
+		if (fmt->nfrm < req->bFrameIndex)
+			return EINVAL;
+	} else
+		return EINVAL;
+
+	fmt = v->data->fmt + (req->bFormatIndex - 1);
+	frm = fmt->frm + (req->bFrameIndex - 1);
+#endif
+	DPRINTF("v4l2-dev get format\n");
+	if (v->cur_fmt == NULL || v->cur_frm == NULL) {
+		kprintf("get cur format failed\n");
+		return EINVAL;
+	}
+
+	fmt = v->cur_fmt;
+	frm = v->cur_frm;
+
+	pix->pixelformat = fmt->fcc;
+	pix->width = frm->width;
+	pix->height = frm->height;
+	pix->field = V4L2_FIELD_NONE;
+	pix->bytesperline = fmt->bpp * frm->width / 8;
+	pix->sizeimage = UGETDW(v->req.dwMaxFrameSize);
+	pix->colorspace = fmt->colorspace;
+	pix->priv = 0;
+
+	b = (uint8_t *)&pix->pixelformat;
+	DPRINTF("%s: format fcc:%c%c%c%c\n", __func__, b[0], b[1], b[2], b[3]);
+	DPRINTF("width:%d height:%d bytesperline:%d ", pix->width,
+		pix->height, pix->bytesperline);
+	DPRINTF("sizeimage:%d colorspace:%d\n", pix->sizeimage,
+		pix->colorspace);
+
+	return 0;
+}
+
+int
+uvc_drv_try_v4l2_fmt(struct uvc_drv_video *video, struct v4l2_format *vfmt,
+	struct uvc_data_request *req, struct uvc_data_format **rfmt,
+	struct uvc_data_frame **rfrm)
+{
+	struct uvc_data_format *fmt = NULL;
+	struct uvc_data_frame *frm = NULL;
+	uint16_t rw, rh, w, h;
+	uint32_t maxd, d;
+	uint8_t *fcc;
+	int i, ret, retry = 5;
+
+	DPRINTF("fcc:%x\n", vfmt->fmt.pix.pixelformat);
+	fcc = (uint8_t *)&vfmt->fmt.pix.pixelformat;
+	DPRINTF("Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
+		vfmt->fmt.pix.pixelformat, fcc[0], fcc[1], fcc[2], fcc[3],
+		vfmt->fmt.pix.width, vfmt->fmt.pix.height);
+
+	for (i = video->data->nfmt; i > 0; i--) {
+		fmt = video->data->fmt + i - 1;
+		if (fmt->fcc == vfmt->fmt.pix.pixelformat) {
+			break;
+		}
+	}
+
+	if (i == 0) {
+		fmt = video->cur_fmt;
+		if (!fmt) {
+			DPRINTF("unsupport fmt and get cur format failed\n");
+			return EINVAL;
+		}
+		vfmt->fmt.pix.pixelformat = fmt->fcc;
+	}
+
+	DPRINTF("format:%d\n", fmt->index);
+
+	/* check frame */
+	rw = vfmt->fmt.pix.width;
+	rh = vfmt->fmt.pix.height;
+	maxd = (unsigned int)-1;
+
+	for (i = 0; i < fmt->nfrm; ++i) {
+		w = fmt->frm[i].width;
+		h = fmt->frm[i].height;
+
+		d = min(w, rw) * min(h, rh);
+		d = w * h + rw * rh - 2 * d;
+		if (d < maxd) {
+			maxd = d;
+			frm = &fmt->frm[i];
+		}
+		if (maxd == 0)
+			break;
+	}
+
+	if (!frm) {
+		DPRINTF("Unsupport frm\n");
+		return EINVAL;
+	}
+
+	DPRINTF("frm width:%u height:%u\n", frm->width, frm->height);
+	vfmt->fmt.pix.width = frm->width;
+	vfmt->fmt.pix.height = frm->height;
+
+	while (retry-- > 0) {
+		memset(req, 0, sizeof(*req));
+		UVC_LOCK(&video->mtx);
+		ret = uvc_drv_get_video_ctrl(video, req, 1, GET_CUR);
+		UVC_UNLOCK(&video->mtx);
+		if (ret) {
+			continue;
+		}
+
+		USETW(req->wHint, 0x1);
+		req->bFormatIndex = fmt->index;
+		req->bFrameIndex = frm->index;
+		USETDW(req->dwFrameInterval, uvc_drv_try_frame_interval(frm,
+			frm->default_interval));
+		DPRINTF("itv:%u\n", uvc_drv_try_frame_interval(frm,
+			frm->default_interval));
+		/* TODO: some webcams have UVC_QUIRK_PROBE_EXTRAFIELDS*/
+		//USETDW(req->dwMaxFrameSize,
+		//	UGETDW(video->req.dwMaxFrameSize));
+		//dump_hex(req, 26);
+
+		//ret = uvc_drv_probe_video(video, req);
+		//if (ret) {
+		//	continue;
+		//}
+
+		//ret = uvc_drv_get_video_ctrl(video, req, 1, GET_CUR);
+		//if (ret) {
+		//	continue;
+		//}
+		//dump_hex(req, 26);
+
+		ret = uvc_drv_probe_video(video, req);
+		if (ret) {
+			continue;
+		} else {
+			break;
+		}
+	}
+
+	if (ret) {
+		DPRINTF("probe streaming error\n");
+		return ret;
+	}
+	vfmt->fmt.pix.width = frm->width;
+	vfmt->fmt.pix.height = frm->height;
+	vfmt->fmt.pix.field = V4L2_FIELD_NONE;
+	vfmt->fmt.pix.bytesperline = fmt->bpp * frm->width / 8;
+	vfmt->fmt.pix.sizeimage = UGETDW(req->dwMaxFrameSize);
+	vfmt->fmt.pix.colorspace = fmt->colorspace;
+	vfmt->fmt.pix.priv = 0;
+
+	if (rfmt != NULL)
+		*rfmt = fmt;
+	if (rfrm != NULL)
+		*rfrm = frm;
+
+	return 0;
+}
+
+int
+uvc_drv_enum_v4l2_frameintervals(struct uvc_drv_video *v,
+	struct v4l2_frmivalenum *itv)
+{
+	struct uvc_data_format *loop, *fmt = NULL;
+	struct uvc_data_frame *frm = NULL;
+	int i, index, nintervals;
+
+	for (i = 0; i < v->data->nfmt; i++) {
+		loop = v->data->fmt + i;
+		if (loop->fcc == itv->pixel_format) {
+			fmt = loop;
+			break;
+		}
+	}
+
+	if (fmt == NULL)
+		return EINVAL;
+
+	index = itv->index;
+	for (i = 0; i < fmt->nfrm; i++) {
+		if (fmt->frm[i].width == itv->width &&
+		    fmt->frm[i].height == itv->height) {
+			frm = &fmt->frm[i];
+			nintervals = frm->interval_type ? frm->interval_type : 1;
+			if (index < nintervals)
+				break;
+			index -= nintervals;
+		}
+	}
+
+	if (i == fmt->nfrm) {
+		return EINVAL;
+	}
+
+	if (frm->interval_type) {
+		itv->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+		itv->x.discrete.numerator = frm->interval[index].val;
+		itv->x.discrete.denominator = 10000000;
+		uvc_simple_frac(&itv->x.discrete.numerator,
+			&itv->x.discrete.denominator, 8, 333);
+	} else {
+		itv->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+		itv->x.stepwise.min.numerator = frm->interval[0].val;
+		itv->x.stepwise.min.denominator = 10000000;
+		itv->x.stepwise.max.numerator = frm->interval[1].val;
+		itv->x.stepwise.max.denominator = 10000000;
+		itv->x.stepwise.step.numerator = frm->interval[2].val;
+		itv->x.stepwise.step.denominator = 10000000;
+		uvc_simple_frac(&itv->x.stepwise.min.numerator,
+			&itv->x.stepwise.min.denominator, 8, 333);
+		uvc_simple_frac(&itv->x.stepwise.max.numerator,
+			&itv->x.stepwise.max.denominator, 8, 333);
+		uvc_simple_frac(&itv->x.stepwise.step.numerator,
+			&itv->x.stepwise.step.denominator, 8, 333);
+	}
+
+	return 0;
+}
+
+int
+uvc_drv_enum_v4l2_framesizes(struct uvc_drv_video *v,
+	struct v4l2_frmsizeenum *fs)
+{
+	struct uvc_data_format *loop, *fmt = NULL;
+	struct uvc_data_frame *frm;
+	int i;
+
+	for (i = 0; i < v->data->nfmt; i++) {
+		loop = v->data->fmt + i;
+		if (loop->fcc == fs->pixel_format) {
+			fmt = loop;
+			break;
+		}
+	}
+
+	if (fmt == NULL)
+		return EINVAL;
+
+	if (fs->index >= fmt->nfrm)
+		return EINVAL;
+
+	frm = fmt->frm + fs->index;
+	fs->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	fs->x.discrete.width = frm->width;
+	fs->x.discrete.height = frm->height;
+
+	return 0;
+}
+
+int
+uvc_drv_enum_v4l2_fmt(struct uvc_drv_video *v, struct v4l2_fmtdesc *vfmt)
+{
+	struct uvc_data_format *fmt;
+
+	if (v->data->nfmt <= vfmt->index)
+		return EINVAL;
+
+	fmt = v->data->fmt + vfmt->index;
+	vfmt->flags = 0;
+	if (fmt->flags & UVC_FMT_FLAG_COMPRESSED)
+		vfmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+	strlcpy(vfmt->description, fmt->name, sizeof(vfmt->description));
+	vfmt->pixelformat = fmt->fcc;
+	DPRINTF("pixelformat:%x, fmt name: %s\n",
+		vfmt->pixelformat,
+		vfmt->description);
+	return 0;
+}
+
+static int
+uvc_drv_bulkfill_buf(struct uvc_drv_video *v, struct usb_page_cache *pc,
+	usb_frlength_t maxFramelen, usb_frlength_t actlen)
+{
+	struct uvc_data_payload_header h;
+	usb_frlength_t offset = 0;
+	usb_frlength_t datalen = actlen;
+
+	if (actlen < 2)
+		return EINVAL;
+
+	v->bulk.payload_size += actlen;
+	if (v->bulk.header_size == 0 && !v->bulk.skip_payload) {
+		usbd_copy_out(pc, 0, &h, sizeof(h));
+		if (h.len < sizeof(h) || h.len > actlen)
+			return EINVAL;
+
+		if (actlen - h.len <= 0)
+			return EINVAL;
+
+		usbd_copy_out(pc, 0, &(v->bulk.header), h.len);
+		v->bulk.header_size = h.len;
+		offset = h.len;
+		datalen = actlen - h.len;
+	}
+	/* forgot why, so leave this here just in case */
+	//if (actlen < maxFramelen - 4)
+	//	finish = 1;
+
+	uvc_bulkbuf_sell_buf(&v->bq, pc, offset,
+		datalen, actlen, maxFramelen);
+	return 0;
+}
+
+static int
+uvc_drv_fill_buf(struct uvc_drv_video *v, struct usb_page_cache *pc, int i,
+	usb_frlength_t size, usb_frlength_t len)
+{
+	struct uvc_data_payload_header h;
+	usb_frlength_t offset = i * size;
+
+	if (len < sizeof(h))
+		return EINVAL;
+
+	usbd_copy_out(pc, offset, &h, sizeof(h));
+
+	if (h.len < sizeof(h) || h.len > len)
+		return EINVAL;
+
+	if (h.header & UVC_PL_HEADER_BIT_ERR || v->bad_frame) {
+		if (h.header & UVC_PL_HEADER_BIT_EOF) {
+			v->bad_frame = 0;
+			uvc_buf_reset_buf(&v->bq);
+		} else {
+			v->bad_frame = 1;
+		}
+		DPRINTF("%d - %x\n", h.len, h.header);
+		return EINVAL;
+	}
+
+	uvc_buf_sell_buf(&v->bq, pc, offset + h.len,
+		len - h.len, h.header & UVC_PL_HEADER_BIT_EOF,
+		h.header & UVC_PL_HEADER_BIT_FID);
+
+	return 0;
+}
+
+static void
+uvc_drv_bulkdata_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvc_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_xfer *xfer_other = sc->video->data->xfer[2];
+
+	if (usbd_clear_stall_callback(xfer, xfer_other)) {
+		usbd_transfer_start(xfer_other);
+	}
+}
+
+static void
+uvc_drv_bulkdata_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvc_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int dir = UE_GET_DIR(xfer->endpoint->edesc->bEndpointAddress);
+	int actlen, sumlen;
+
+	if (xfer->endpoint == NULL || xfer->endpoint->edesc == NULL)
+		return;
+	dir = UE_GET_DIR(xfer->endpoint->edesc->bEndpointAddress);
+	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (dir == UE_DIR_IN)
+			uvc_drv_bulkfill_buf(sc->video, pc,
+				usbd_xfer_max_len(xfer), actlen);
+
+	case USB_ST_SETUP:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		break;
+	default:
+		DPRINTF("error=%s\n", usbd_errstr(error));
+
+		break;
+	}
+}
+
+static void
+uvc_drv_data_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvc_softc *sc = usbd_xfer_softc(xfer);
+	int actlen, nframes, i, size;
+	struct usb_page_cache *pc;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		for (i = 0; i < nframes; i++) {
+			size = usbd_xfer_frame_len(xfer, i);
+			uvc_drv_fill_buf(sc->video, pc, i,
+				usbd_xfer_max_framelen(xfer), size);
+		}
+
+	case USB_ST_SETUP:
+tr_setup:
+		for (i = 0; i < nframes; i++)
+			usbd_xfer_set_frame_len(xfer, i,
+				usbd_xfer_max_framelen(xfer));
+		usbd_xfer_set_frames(xfer, UVC_N_ISOFRAMES);
+		usbd_transfer_submit(xfer);
+		break;
+	default:
+		DPRINTF("error=%s\n", usbd_errstr(error));
+
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+uvc_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct usb_page_cache *pc;
+	int actlen;
+	int cmd;
+	uint8_t data[16];
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+	cmd = USB_GET_STATE(xfer);
+	switch (cmd) {
+	case USB_ST_TRANSFERRED: {
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if ((actlen <= sizeof(data)) &&
+		    (actlen > 0)) {
+			usbd_copy_out(pc, 0, data, actlen);
+		} else {
+			/* ignore it */
+			DPRINTF("ignored transfer, %d bytes\n", actlen);
+		}
+	}
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer));
+		usbd_transfer_submit(xfer);
+		break;
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static const struct usb_config uvc_intr_config[8] = {
+	[0] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1, .short_xfer_ok = 1,},
+		.bufsize = 0,//UHID_BSIZE,
+		.callback = &uvc_intr_callback,
+	},
+};
+
+static struct usb_config uvc_bulk_config[3] = {
+	[0] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1, .short_xfer_ok = 1,},
+		.bufsize = UVC_BULK_HS_BUFFER_SIZE,
+		.callback = &uvc_drv_bulkdata_callback,
+		.timeout = 0,	/* overwritten later */
+	},
+	[1] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1, .short_xfer_ok = 1,},
+		.bufsize = UVC_BULK_HS_BUFFER_SIZE,
+		.callback = &uvc_drv_bulkdata_callback,
+		.timeout = 0,	/* overwritten later */
+	},
+	[2] = {
+		.type = UE_CONTROL,
+		.endpoint = 0,
+		.direction = UE_DIR_ANY,
+		.flags = {.pipe_bof = 1, .short_xfer_ok = 1,},
+		.bufsize = sizeof(struct usb_device_request),
+		.callback = &uvc_drv_bulkdata_clear_stall_callback,
+		.timeout = 20000,	/* overwritten later */
+		.interval = 50,
+		.usb_mode = USB_MODE_HOST,
+	},
+};
+
+static const struct usb_config uvc_config[8] = {
+	[0] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[1] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[2] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[3] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[4] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[5] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[6] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+	[7] = {
+	.type = UE_ISOCHRONOUS,
+	.endpoint = UE_ADDR_ANY,
+	.direction = UE_DIR_IN,
+	.bufsize = 0,
+	.frames = UVC_N_ISOFRAMES,
+	.flags = {.short_xfer_ok = 1,},
+	.callback = uvc_drv_data_callback,
+	},
+};
+
+int
+uvc_drv_start_video(struct uvc_drv_video *video)
+{
+	struct uvc_softc *sc = video->sc;
+	struct usb_descriptor *desc;
+	struct usb_interface_descriptor *idesc;
+	struct usb_endpoint_descriptor *ed;
+	struct usb_endpoint_ss_comp_descriptor *essd;
+	int ret, num, found, mps, ps/*, burst, packet*/;
+	int i;
+
+	ps = 0;
+
+	UVC_LOCK(&video->mtx);
+
+	if (video->enable) {
+		DPRINTF("video is on\n");
+		UVC_UNLOCK(&video->mtx);
+		return EBUSY;
+	}
+
+	video->bad_frame = 0;
+
+	ret = uvc_buf_queue_enable(&video->bq);
+	if (ret) {
+		DPRINTF("buffer queue enable fault\n");
+		UVC_UNLOCK(&video->mtx);
+		return EBUSY;
+	}
+
+	if (video->data->num_altsetting	== 1) {
+		uvc_drv_get_video_ctrl(video, &video->req, 1, GET_CUR);
+		ret = uvc_drv_set_video_ctrl(video, &video->req, 0);
+		if (ret) {
+			suspend_kproc(curthread, 300);
+			ret = uvc_drv_set_video_ctrl(video, &video->req, 0);
+		}
+		mps = UGETDW(video->req.dwMaxPayloadSize);
+		if (!mps) {
+			UVC_UNLOCK(&video->mtx);
+			DPRINTF("Max Packet Size is %u\n", mps);
+			return EINVAL;
+		}
+		video->bulk.max_payload_size = mps;
+		video->bulk.last_fid = 3;
+
+		/* setup xfer */
+		if (sc->quirks & UVC_BIGGER_TRANSFER_BUF) {
+			uvc_bulk_config[0].bufsize = 125 * 1024;
+			uvc_bulk_config[1].bufsize = 125 * 1024;
+		}
+		UVC_UNLOCK(&video->mtx);
+		ret = usbd_transfer_setup(sc->udev, &video->data->iface_index,
+			video->data->xfer, uvc_bulk_config,
+			UVC_N_BULKTRANSFER, sc, &video->mtx);
+
+		UVC_LOCK(&video->mtx);
+		/* start xfer */
+		for (i = 0; i < UVC_N_TRANSFER; i++)
+			usbd_transfer_start(video->data->xfer[i]);
+		goto done;
+	}
+
+	/* choose setting */
+	/* 1. check */
+	num = usbd_iface_get_altsetting_num(sc->udev, video->data->iface);
+	DPRINTF("streaming setting num:%u\n", num);
+	if (num <= 1) {
+		UVC_UNLOCK(&video->mtx);
+		DPRINTF("WARNING: _TO_BE_IMPLEMENT_ BULK ep or bad Intf\n");
+		return EINVAL;
+	}
+	/* 2. loop ep */
+	mps = UGETDW(video->req.dwMaxPayloadSize);
+	if (!mps) {
+		UVC_UNLOCK(&video->mtx);
+		DPRINTF("Max Packet Size is %u\n", mps);
+		return EINVAL;
+	}
+	DPRINTF("payload --- %d\n", mps);
+	found = 0;
+
+	desc = (struct usb_descriptor *)(video->data->iface->idesc);
+	while ((desc = usb_desc_foreach(sc->udev->cdesc, desc))) {
+		if (desc->bDescriptorType == UDESC_INTERFACE) {
+			if (desc->bDescriptorSubtype != video->data->iface_num)
+				break;
+
+			idesc = (struct usb_interface_descriptor *)desc;
+			num = idesc->bAlternateSetting;
+			continue;
+		}
+		if (((desc->bDescriptorType != UDESC_ENDPOINT) &&
+		      (desc->bDescriptorType != UDESC_ENDPOINT_SS_COMP)))
+			continue;
+		ed = (struct usb_endpoint_descriptor *)desc;
+		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
+			essd = (struct usb_endpoint_ss_comp_descriptor *)desc;
+			ps = UGETW(essd->wBytesPerInterval);
+		}
+		if (desc->bDescriptorType == UDESC_ENDPOINT) {
+			ps = UGETW(ed->wMaxPacketSize);
+			ps = (ps & 0x07ff) * (1 + ((ps >> 11) & 3));
+		}
+
+		if (ps >= mps && !video->htsf) {
+			found = 1;
+			break;
+		}
+	}
+	if (video->htsf)
+		found = 1;
+	if (!found) {
+		UVC_UNLOCK(&video->mtx);
+		DPRINTF("don't have fit ep\n");
+		return EINVAL;
+	}
+	DPRINTF("max packet size:%u ep size:%u\n", mps, ps);
+
+	/* 3. set */
+	if (video->htsf) {
+		uvc_drv_get_video_ctrl(video, &video->req, 1, GET_CUR);
+		ret = uvc_drv_set_video_ctrl(video, &video->req, 0);
+		DPRINTF("uvc_drv_set_video_ctrl ret:%d\n", ret);
+	} else {
+		if (!(sc->quirks & UVC_COMMIT_IN_ADVANCE))
+			uvc_drv_set_video_ctrl(video, &video->req, 0);
+	}
+	DPRINTF("streaming interface setting:%u\n", num);
+
+	UVC_UNLOCK(&video->mtx);
+
+	usbd_set_alt_interface_index(sc->udev, video->data->iface_index, num);
+
+	/* setup xfer */
+	ret = usbd_transfer_setup(sc->udev, &video->data->iface_index,
+	    video->data->xfer, uvc_config, UVC_N_TRANSFER, sc, &video->mtx);
+
+	UVC_LOCK(&video->mtx);
+	/* start xfer */
+	for (i = 0; i < UVC_N_TRANSFER; i++)
+		usbd_transfer_start(video->data->xfer[i]);
+done:
+	video->enable = 1;
+	UVC_UNLOCK(&video->mtx);
+	return 0;
+}
+
+__unused static int
+uvc_drv_halt_ep_request(struct usb_device *udev, uint8_t epaddr, int timeout)
+{
+	struct usb_device_request req;
+	int err;
+
+	memset(&req, 0x00, sizeof(req));
+	req.bRequest = UR_CLEAR_FEATURE;
+	req.bmRequestType = UT_WRITE_ENDPOINT;
+	USETW(req.wValue, UF_ENDPOINT_HALT);
+	USETW(req.wIndex, epaddr);
+	USETW(req.wLength, 0);
+
+	err = usbd_do_request_flags(udev, NULL, &req, NULL, 0, NULL, timeout);
+	if (err)
+		kprintf("%s-%s\n", __func__, usbd_errstr(err));
+	return err;
+
+}
+
+int
+uvc_drv_stop_video(struct uvc_drv_video *video, int close)
+{
+	uint8_t ep_addr = 0;
+	struct usb_endpoint *pep = NULL;
+
+	DPRINTF("uvc_drv_stop_video\n");
+
+	if (video->data->num_altsetting > 1) {
+		usbd_transfer_unsetup(video->data->xfer, UVC_N_TRANSFER);
+		usbd_set_alt_interface_index(video->sc->udev,
+			video->data->iface_index, 0);
+	} else {
+		if (video->data->xfer[0])
+			ep_addr =
+			video->data->xfer[0]->endpoint->edesc->bEndpointAddress;
+		usbd_transfer_unsetup(video->data->xfer, UVC_N_BULKTRANSFER);
+		/*
+		 * uvc spec does not tell how to stop a bulk camera
+		 * which will leave the indicator on.
+		 * windows system send a clear halt request to it
+		 * mimic the same, then the light off
+		 */
+		while (ep_addr &&
+		       (pep = usb_endpoint_foreach(video->sc->udev, pep))) {
+			if (pep->edesc->bEndpointAddress == ep_addr) {
+				usb_reset_ep(video->sc->udev, pep);
+			}
+		}
+	}
+
+	UVC_LOCK(&video->mtx);
+	uvc_buf_queue_disable(&video->bq);
+	video->enable = 0;
+	if (close)
+		video->htsf = 0;
+	UVC_UNLOCK(&video->mtx);
+
+	return 0;
+}
+
+static uint32_t
+uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
+{
+	uint32_t multiplier;
+
+	if (denominator == 0 ||
+		numerator / denominator >= ((uint32_t)-1) / 10000000)
+		return (uint32_t)-1;
+
+	multiplier = 10000000;
+
+	while (numerator > ((uint32_t)-1)/multiplier) {
+		multiplier /= 2;
+		denominator /= 2;
+	}
+
+	return denominator ? numerator * multiplier / denominator : 0;
+}
+
+int
+uvc_drv_set_streampar(struct uvc_drv_video *v, struct v4l2_streamparm *p)
+{
+	struct uvc_data_request probe;
+	struct v4l2_fract timeperframe;
+	uint32_t interval;
+	int ret;
+
+	kprintf("uvc_v4l2_set_streampar\n");
+	timeperframe = p->parm.capture.timeperframe;
+
+	interval = uvc_fraction_to_interval(timeperframe.numerator,
+		timeperframe.denominator);
+	kprintf("Setting frame interval to %u/%u (%u).\n",
+		timeperframe.numerator, timeperframe.denominator, interval);
+
+	probe = v->req;
+	USETDW(probe.dwFrameInterval,
+		uvc_drv_try_frame_interval(v->cur_frm, interval));
+
+	ret = uvc_drv_probe_video(v, &probe);
+	if (ret < 0) {
+		return ret;
+	}
+	v->req = probe;
+	timeperframe.numerator = UGETDW(probe.dwFrameInterval);
+	timeperframe.denominator = 10000000;
+	uvc_simple_frac(&timeperframe.numerator,
+		&timeperframe.denominator, 8, 333);
+	p->parm.capture.timeperframe = timeperframe;
+	return 0;
+}
+
+static void
+uvc_init_intrxfer(struct uvc_drv_video *video)
+{
+	struct uvc_softc *sc = video->sc;
+	struct usb_descriptor *desc;
+	struct usb_interface_descriptor *idesc;
+	struct usb_endpoint_descriptor *ed;
+	int hit_intr = 0, ret;
+	uint8_t iface_index;
+
+	return; //not used now
+
+	desc = (struct usb_descriptor *)NULL;
+	while ((desc = usb_desc_foreach(sc->udev->cdesc, desc))) {
+		if (desc->bDescriptorType == UDESC_INTERFACE) {
+			idesc = (struct usb_interface_descriptor *)desc;
+			iface_index = idesc->bInterfaceNumber;
+			continue;
+		}
+		if (desc->bDescriptorType != UDESC_ENDPOINT)
+			continue;
+		ed = (struct usb_endpoint_descriptor *)desc;
+		if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+			hit_intr = 1;
+			break;
+		}
+	}
+
+	if (!hit_intr) {
+		return;
+	}
+
+	ret = usbd_transfer_setup(sc->udev, &iface_index, video->intr_xfer,
+			uvc_intr_config, UVCINTR_N_TRANSFER, sc, &video->mtx);
+	if (ret)
+		return;
+
+	UVC_LOCK(&video->mtx);
+	usbd_transfer_start(video->intr_xfer[0]);
+	UVC_UNLOCK(&video->mtx);
+}
+
+static int
+uvc_drv_init_cur_fmt_frm(struct uvc_drv_video *v, struct uvc_data_request *req)
+{
+	struct uvc_data_format *fmt = NULL;
+	struct uvc_data_frame *frm = NULL;
+	int i = 0;
+
+	if (v == NULL || v->data == NULL || req == NULL)
+		return EINVAL;
+
+	/* Check if the default format descriptor exists. Use the first
+	 * available format otherwise.
+	 */
+	for (i = v->data->nfmt; i > 0; i--) {
+		fmt = v->data->fmt + i - 1;
+		if (fmt->index == req->bFormatIndex) {
+			break;
+		}
+	}
+	if (fmt->nfrm == 0) {
+		kprintf("No frame desc found for default format.\n");
+		return EINVAL;
+	}
+
+	/* Zero bFrameIndex might be correct. Stream-based formats (including
+	 * MPEG-2 TS and DV) do not support frames but have a dummy frame
+	 * descriptor with bFrameIndex set to zero. If the default frame
+	 * descriptor is not found, use the first available frame.
+	 */
+	for (i = fmt->nfrm; i > 0; i--) {
+		frm = fmt->frm + i - 1;
+		if (frm->index == req->bFrameIndex) {
+			break;
+		}
+	}
+
+	DPRINTF("width:%u height:%u\n", frm->width, frm->height);
+
+	v->cur_fmt = fmt;
+	v->cur_frm = frm;
+
+	return 0;
+}
+
+static struct uvc_drv_video *
+uvc_drv_init_video(struct uvc_softc *sc, struct uvc_drv_ctrl *ctrl,
+	struct uvc_drv_data *data)
+{
+	struct uvc_drv_video *v;
+	struct uvc_data_format *fmt = NULL;
+	struct uvc_data_frame *frm = NULL;
+	struct uvc_data_request *req;
+	int ret, i;
+
+	v = kmalloc(sizeof(*v), M_UVC, M_ZERO | M_WAITOK);
+	if (v == NULL)
+		return v;
+
+	lockinit(&v->mtx, device_get_nameunit(sc->dev),
+		0, LK_CANRECURSE);
+	v->sc = sc;
+	v->type = sc->type;
+	v->unit = device_get_unit(sc->dev);
+	v->ctrl = ctrl;
+	v->data = data;
+	v->last_fid = -1;
+	atomic_store_64(&v->users, 0);
+	atomic_store_64(&v->pri, 0);
+
+	if (data->num_altsetting > 1)
+		usbd_set_alt_interface_index(sc->udev, data->iface_index, 0);
+
+	/* fill video data request */
+	req = &v->req;
+	UVC_LOCK(&v->mtx);
+	ret = uvc_drv_get_video_ctrl(v, req, 1, GET_DEF);
+	if (ret) {
+		DPRINTF("video GET_DEF fault %d\n", ret);
+		ret = uvc_drv_get_video_ctrl(v, req, 1, GET_CUR);
+		UVC_UNLOCK(&v->mtx);
+		if (ret) {
+			DPRINTF("video GET_CUR fault %d\n", ret);
+			goto done;
+		}
+		for (i = v->data->nfmt; i > 0; i--) {
+			fmt = v->data->fmt + i - 1;
+			if (fmt->index == req->bFormatIndex) {
+				break;
+			}
+		}
+
+		if (fmt->nfrm == 0) {
+			DPRINTF("No frame desc found for default format.\n");
+			goto done;
+		}
+
+		DPRINTF("format:%u-%s\n", fmt->index, fmt->name);
+
+		for (i = fmt->nfrm; i > 0; i--) {
+			frm = fmt->frm + i - 1;
+			if (frm->index == req->bFrameIndex) {
+				break;
+			}
+		}
+
+		DPRINTF("width:%u height:%u\n", frm->width, frm->height);
+		req->bFormatIndex = fmt->index;
+		req->bFrameIndex = frm->index;
+
+		ret = uvc_drv_set_video(v, req, fmt, frm);
+		if (ret) {
+			DPRINTF("uvc drv set video fault %d\n", ret);
+			goto done;
+		}
+
+	} else {
+		/*do not care whether it successes or not*/
+		uvc_drv_set_video_ctrl(v, req, 1);
+		UVC_UNLOCK(&v->mtx);
+	}
+
+	/*
+	 * Update this here because C922 Pro from Richard
+	 * 1. GET_DEF don't fill max payload size
+	 * 2. GET_DEF inherbs config set before
+	 */
+	UVC_LOCK(&v->mtx);
+	ret = uvc_drv_get_video_ctrl(v, req, 1, GET_CUR);
+	UVC_UNLOCK(&v->mtx);
+	if (ret) {
+		DPRINTF("uvc drv GET_CUR fault %d\n", ret);
+		goto done;
+	}
+
+	uvc_drv_show_video_ctrl(req);
+
+	if (uvc_drv_init_cur_fmt_frm(v, req))
+		goto done;
+
+	uvc_buf_queue_init(v, &v->bq);
+
+	ret = uvc_v4l2_reg(v);
+	if (ret) {
+		DPRINTF("uvc v4l2 reg error\n");
+		goto done;
+	}
+	uvc_init_intrxfer(v);
+
+	return v;
+done:
+	kfree(v, M_UVC);
+	return NULL;
+}
+
+static void
+uvc_drv_destroy_video(struct uvc_drv_video *v)
+{
+	if (v) {
+		/* thread stop */
+		if (v->data)
+			usbd_transfer_unsetup(v->data->xfer, UVC_N_TRANSFER);
+
+		uvc_buf_queue_disable(&v->bq);
+		uvc_buf_queue_free_bufs(&v->bq);
+	}
+}
+
+static void
+uvc_drv_show_interval(struct uvc_data_interval *itv)
+{
+	DPRINTF("%u	\n", itv->val);
+}
+
+static void
+uvc_drv_show_frame(struct uvc_data_frame *frm)
+{
+	struct uvc_data_interval *itv;
+	int i;
+
+	DPRINTF("interval num:%u\n", frm->interval_type);
+	DPRINTF("frm index:%u width:%u height:%u\n", frm->index,
+		frm->width, frm->height);
+
+	for (i = 0; i < frm->interval_type; i++) {
+		itv = frm->interval + i;
+		uvc_drv_show_interval(itv);
+	}
+	DPRINTF("\n");
+}
+
+static void
+uvc_drv_show_format(struct uvc_data_format *fmt)
+{
+	struct uvc_data_frame *frm;
+	int i;
+
+	DPRINTF("frame num:%lu flags:0x%lx\n", fmt->nfrm, fmt->flags);
+	DPRINTF("format index:%u name:%s fcc:%u bpp:%u colorspace:%u\n",
+		fmt->index, fmt->name, fmt->fcc, fmt->bpp, fmt->colorspace);
+
+	for (i = 0; i < fmt->nfrm; i++) {
+		frm = fmt->frm + i;
+		uvc_drv_show_frame(frm);
+	}
+}
+
+static void
+uvc_drv_show_data(struct uvc_drv_data *data)
+{
+	struct uvc_data_format *fmt;
+	int i;
+
+	DPRINTF("streaming interface index:%u num:%u\n"
+		"format num:%u frame num:%d interval:%d\n",
+		data->iface_index, data->iface_num,
+		data->nfmt, data->nfrm, data->nitv);
+
+	for (i = 0; i < data->nfmt; i++) {
+		fmt = data->fmt + i;
+		uvc_drv_show_format(fmt);
+	}
+}
+
+static int
+uvc_drv_init_data_fmt(struct uvc_softc *sc, struct uvc_drv_data *data)
+{
+	struct usb_descriptor *desc;
+	struct uvc_vs_frame_desc *fdesc;
+	struct uvc_vs_frame_based_desc *fbdesc;
+	uint32_t size;
+	uint16_t nfmt, nfrm, nitv;
+
+	nfmt = nfrm = nitv = 0;
+	desc = NULL;
+	while ((desc = usbd_find_descriptor(sc->udev, desc, data->iface_index,
+		UDESC_CS_INTERFACE, 0xFF, 0, 0)) != NULL) {
+		switch (desc->bDescriptorSubtype) {
+		case UDESCSUB_VS_INPUT_HEADER:
+			sc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			break;
+		case UDESCSUB_VS_OUTPUT_HEADER:
+			sc->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+			break;
+		case UDESCSUB_VS_COLORFORMAT:
+		case UDESCSUB_VS_STILL_IMAGE_FRAME:
+			break;
+		case UDESCSUB_VS_FORMAT_MJPEG:
+		case UDESCSUB_VS_FORMAT_FRAME_BASED:
+			nfmt++;
+			break;
+		case UDESCSUB_VS_FORMAT_UNCOMPRESSED:
+			if (sc->quirks & UVC_DROP_UNCOMPRESS_FORMAT)
+				break;
+			nfmt++;
+			break;
+		case UDESCSUB_VS_FRAME_UNCOMPRESSED:
+			if (sc->quirks & UVC_DROP_UNCOMPRESS_FORMAT)
+				break;
+		case UDESCSUB_VS_FRAME_MJPEG:
+			fdesc = (struct uvc_vs_frame_desc *)desc;
+			nfrm++;
+			nitv += (fdesc->bFrameIntervalType > 0) ?
+				fdesc->bFrameIntervalType : 3;
+			break;
+		case UDESCSUB_VS_FRAME_FRAME_BASED:
+			fbdesc = (struct uvc_vs_frame_based_desc *)desc;
+			nfrm++;
+			nitv += (fbdesc->bFrameIntervalType > 0) ?
+				fbdesc->bFrameIntervalType : 3;
+			break;
+		/* not support */
+		default:
+			DPRINTF("WARNING __TO_BE_IMPLEMENT__ %s\n", __func__);
+			DPRINTF("notsupport--%u\n", desc->bDescriptorSubtype);
+			return EINVAL;
+
+		}
+	}
+
+	if (nfmt == 0)
+		return EINVAL;
+
+	size =  nfmt * sizeof(struct uvc_data_format) +
+		nfrm * sizeof(struct uvc_data_frame) +
+		nitv * sizeof(struct uvc_data_interval);
+	DPRINTF("format:%u frame:%u interval:%u size:%d\n",
+		nfmt, nfrm, nitv, size);
+	data->fmt = kmalloc(size, M_UVC, M_ZERO | M_WAITOK);
+	if (!data->fmt) {
+		DPRINTF("Memory Leak.\n");
+		return ENOMEM;
+	}
+
+	data->nfmt = nfmt;
+	data->nfrm = nfrm;
+	data->nitv = nitv;
+	return 0;
+}
+
+static uint8_t
+uvc_drv_get_colorspace(uint8_t pri)
+{
+	uint8_t colorpris[] = {
+		0,
+		V4L2_COLORSPACE_SRGB,
+		V4L2_COLORSPACE_470_SYSTEM_M,
+		V4L2_COLORSPACE_470_SYSTEM_BG,
+		V4L2_COLORSPACE_SMPTE170M,
+		V4L2_COLORSPACE_SMPTE240M,
+	};
+
+	if (pri < nitems(colorpris))
+		return colorpris[pri];
+
+	return 0;
+}
+
+static struct uvc_drv_format_desc *
+uvc_drv_format_by_guid(const uint8_t guid[16])
+{
+	unsigned int len = nitems(uvc_fmts);
+	unsigned int i;
+
+	for (i = 0; i < len; ++i) {
+		if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+			return &uvc_fmts[i];
+	}
+
+	return NULL;
+}
+
+static int
+uvc_drv_parse_data(struct uvc_softc *sc, struct uvc_drv_data *data)
+{
+	struct uvc_data_format *fmt;
+	struct uvc_data_frame *frm;
+	struct uvc_data_interval *itv;
+
+	struct usb_descriptor *desc;
+	struct uvc_drv_format_desc *fmtdes;
+	struct uvc_vs_color_desc *cld;
+	struct uvc_vs_uncompressed_format_desc *raw;
+	struct uvc_vs_frame_based_format_desc *fbraw;
+	struct uvc_vs_frame_desc *frmd;
+	struct uvc_vs_frame_based_desc *fbrmd;
+	struct usb_endpoint_descriptor *ed;
+	struct usb_endpoint_ss_comp_descriptor *essd;
+	uint32_t interval;
+	int ret, i, ps;
+
+	if (data->iface->idesc->bInterfaceSubClass != UISUBCLASS_STREAMING) {
+		return EINVAL;
+	}
+
+	if (data->iface->idesc->bAlternateSetting == 0)
+		data->num_altsetting = 1;
+
+	desc = (struct usb_descriptor *)(data->iface->idesc);
+	while ((desc = usb_desc_foreach(sc->udev->cdesc, desc))) {
+		if (desc->bDescriptorType == UDESC_INTERFACE) {
+			if (desc->bDescriptorSubtype != data->iface_num)
+				break;
+			data->num_altsetting++;
+		}
+		ed = (struct usb_endpoint_descriptor *)desc;
+		if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
+			essd = (struct usb_endpoint_ss_comp_descriptor *)desc;
+			ps = UGETW(essd->wBytesPerInterval);
+			if (ps > data->maxpsize) {
+				data->maxpsize = ps;
+			}
+		}
+		if (desc->bDescriptorType == UDESC_ENDPOINT) {
+			ps = UGETW(ed->wMaxPacketSize);
+			ps = (ps & 0x07ff) * (1 + ((ps >> 11) & 3));
+			if (ps > data->maxpsize) {
+				data->maxpsize = ps;
+			}
+		}
+
+	}
+
+	if (data->num_altsetting > 1)
+		/* cs interface descriptors in altsetting zero */
+		usbd_set_alt_interface_index(sc->udev, data->iface_index, 0);
+
+	ret = uvc_drv_init_data_fmt(sc, data);
+	if (ret)
+		return ret;
+
+	frm = (struct uvc_data_frame *)&data->fmt[data->nfmt];
+	itv = (struct uvc_data_interval *)&frm[data->nfrm];
+	fmt = data->fmt - 1;
+
+	desc = NULL;
+	while ((desc = usbd_find_descriptor(sc->udev, desc, data->iface_index,
+		UDESC_CS_INTERFACE, 0xFF, 0, 0)) != NULL) {
+		switch (desc->bDescriptorSubtype) {
+		case UDESCSUB_VS_COLORFORMAT:
+			cld = (struct uvc_vs_color_desc *)desc;
+			fmt->colorspace
+				= uvc_drv_get_colorspace(cld->bColorPris);
+			break;
+		case UDESCSUB_VS_FORMAT_UNCOMPRESSED:
+			if (sc->quirks & UVC_DROP_UNCOMPRESS_FORMAT)
+				break;
+		case UDESCSUB_VS_FORMAT_MJPEG:
+			fmt++;
+			/* MJPEG/RAW is same */
+			raw = (struct uvc_vs_uncompressed_format_desc *)desc;
+			fmt->frm = frm;
+			fmt->index = raw->bFormatIndex;
+
+			if (desc->bDescriptorSubtype ==
+				UDESCSUB_VS_FORMAT_MJPEG) {
+				fmt->fcc = V4L2_PIX_FMT_MJPEG;
+				fmt->bpp = 0;
+				fmt->flags = UVC_FMT_FLAG_COMPRESSED;
+				strlcpy(fmt->name, "MJPEG", sizeof(fmt->name));
+			} else {
+				fmtdes = uvc_drv_format_by_guid(raw->bGuidFmt);
+				if (fmtdes) {
+					strlcpy(fmt->name, fmtdes->name,
+						sizeof(fmt->name));
+					fmt->fcc = fmtdes->fcc;
+				} else {
+					strlcpy(fmt->name, "RAW",
+						sizeof(fmt->name));
+					fmt->fcc = 0;
+				}
+				fmt->bpp = raw->bBpp;
+			}
+			break;
+		case UDESCSUB_VS_FORMAT_FRAME_BASED:
+			fmt++;
+			fbraw = (struct uvc_vs_frame_based_format_desc *)desc;
+			fmt->frm = frm;
+			fmt->index = fbraw->bFormatIndex;
+			fmtdes = uvc_drv_format_by_guid(fbraw->bGuidFmt);
+			if (fmtdes) {
+				strlcpy(fmt->name, fmtdes->name,
+					sizeof(fmt->name));
+				fmt->fcc = fmtdes->fcc;
+			} else {
+				strlcpy(fmt->name,
+					(const char *)(fbraw->bGuidFmt),
+					sizeof(fmt->name));
+				fmt->fcc = 0;
+			}
+			fmt->bpp = fbraw->bBitsPerpixel;
+			if (fbraw->bVariableSize)
+				fmt->flags = UVC_FMT_FLAG_COMPRESSED;
+			break;
+		case UDESCSUB_VS_FRAME_UNCOMPRESSED:
+			if (sc->quirks & UVC_DROP_UNCOMPRESS_FORMAT)
+				break;
+		case UDESCSUB_VS_FRAME_MJPEG:
+			frmd = (struct uvc_vs_frame_desc *)desc;
+			frm->index = frmd->bFrameIndex;
+			frm->width = UGETW(frmd->wWidth);
+			frm->height = UGETW(frmd->wHeight);
+			frm->cap = frmd->bCapabilities;
+			frm->min_bit_rate = UGETDW(frmd->dMinBitRate);
+			frm->max_bit_rate = UGETDW(frmd->dMaxBitRate);
+			frm->default_interval
+				= UGETDW(frmd->dDefaultFrameInterval);
+			frm->max_buffer_size
+				= UGETDW(frmd->dMaxFrameBufferSize);
+			if (!(fmt->flags & UVC_FMT_FLAG_COMPRESSED))
+				frm->max_buffer_size = fmt->bpp *
+					frm->width * frm->height / 8;
+			frm->interval_type = frmd->bFrameIntervalType;
+			frm->interval = itv;
+			for (i = 0; i < frm->interval_type; i++) {
+				interval = UGETDW(frmd->dFrameInterval[i]);
+				itv->val = interval ? interval : 1;
+				itv++;
+			}
+			if (frm->interval_type == 0)
+				itv += 3;
+			fmt->nfrm++;
+			frm++;
+			break;
+		case UDESCSUB_VS_FRAME_FRAME_BASED:
+			fbrmd = (struct uvc_vs_frame_based_desc *)desc;
+			frm->index = fbrmd->bFrameIndex;
+			frm->width = UGETW(fbrmd->wWidth);
+			frm->height = UGETW(fbrmd->wHeight);
+			frm->cap = fbrmd->bCapabilities;
+			frm->min_bit_rate = UGETDW(fbrmd->dMinBitRate);
+			frm->max_bit_rate = UGETDW(fbrmd->dMaxBitRate);
+			frm->default_interval
+				= UGETDW(fbrmd->dDefaultFrameInterval);
+			/*
+			 * set it zero like linux does, does not know why
+			 * but it is not used for sure
+			 */
+			frm->max_buffer_size = 0;
+
+			if (!(fmt->flags & UVC_FMT_FLAG_COMPRESSED))
+				frm->max_buffer_size = fmt->bpp *
+					frm->width * frm->height / 8;
+			frm->interval_type = fbrmd->bFrameIntervalType;
+			frm->interval = itv;
+			for (i = 0; i < frm->interval_type; i++) {
+				interval = UGETDW(fbrmd->dFrameInterval[i]);
+				itv->val = interval ? interval : 1;
+				itv++;
+			}
+			if (frm->interval_type == 0)
+				itv += 3;
+			fmt->nfrm++;
+			frm++;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void
+uvc_drv_destroy_data(struct uvc_drv_data *data)
+{
+	if (data) {
+		if (data->fmt) {
+			kfree(data->fmt, M_UVC);
+		}
+		kfree(data, M_UVC);
+	}
+}
+
+static struct uvc_drv_data*
+uvc_drv_init_data(struct usb_interface *iface, uint8_t iface_index,
+	uint8_t iface_num)
+{
+	struct uvc_drv_data *pd;
+
+	pd = kmalloc(sizeof(*pd), M_UVC, M_ZERO | M_WAITOK);
+	if (pd) {
+		pd->iface = iface;
+		pd->iface_index = iface_index;
+		pd->iface_num = iface_num;
+	}
+	return pd;
+}
+
+static void uvc_free_topo_node(struct uvc_topo_node *topo_node)
+{
+	if (topo_node->src_ids != NULL) {
+		kfree(topo_node->src_ids , M_UVC);
+	}
+
+	if (topo_node->controls_mask != NULL) {
+		kfree(topo_node->controls_mask , M_UVC);
+	}
+
+	if (topo_node->node_info != NULL) {
+		kfree(topo_node->node_info , M_UVC);
+	}
+
+	if (topo_node->controls != NULL) {
+		kfree(topo_node->controls, M_UVC);
+	}
+
+	kfree(topo_node, M_UVC);
+}
+
+static struct uvc_topo_node *
+uvc_alloc_topo_node(uint16_t node_info_size,
+					uint16_t ctrls_mask_size, // size in bytes
+					uint16_t src_ids_num)
+{
+	struct uvc_topo_node *topo_node = NULL;
+	uint8_t *node_info = NULL;
+	uint8_t *ctrls_mask = NULL;
+	uint8_t *src_ids = NULL;
+
+	topo_node = kmalloc(sizeof(struct uvc_topo_node), M_UVC, M_ZERO | M_WAITOK);
+	if (topo_node == NULL) {
+		goto mem_fail;
+	}
+
+	if (node_info_size != 0) {
+		node_info = kmalloc(node_info_size, M_UVC, M_ZERO | M_WAITOK);
+		if (node_info == NULL) {
+			goto mem_fail;
+		}
+	}
+
+	if (ctrls_mask_size != 0) {
+		ctrls_mask = kmalloc(ctrls_mask_size, M_UVC, M_ZERO | M_WAITOK);
+		if (ctrls_mask == NULL) {
+			goto mem_fail;
+		}
+	}
+
+	if (src_ids_num != 0) {
+		src_ids = kmalloc(src_ids_num, M_UVC, M_ZERO | M_WAITOK);
+		if (src_ids == NULL) {
+			goto mem_fail;
+		}
+	}
+
+	topo_node->node_info = node_info;
+
+	topo_node->controls_mask = ctrls_mask;
+	topo_node->controls_mask_len = ctrls_mask_size;
+
+	topo_node->src_ids = src_ids;
+	topo_node->src_ids_num = src_ids_num;
+
+	return topo_node;
+
+mem_fail:
+	kprintf("%s:%d Lack of mem\n", __func__, __LINE__);
+
+	if (src_ids != NULL) {
+		kfree(src_ids , M_UVC);
+	}
+
+	if (ctrls_mask != NULL) {
+		kfree(ctrls_mask, M_UVC);
+	}
+
+	if (node_info != NULL) {
+		kfree(node_info, M_UVC);
+	}
+
+	if (topo_node != NULL) {
+		kfree(topo_node, M_UVC);
+	}
+
+	return NULL;
+}
+
+
+static int
+uvc_drv_parse_standard_ctrl(struct uvc_softc *sc,
+	struct usb_descriptor *desc, struct uvc_drv_ctrl *ctrl)
+{
+	struct uvc_vc_header_desc *hdesc;
+	struct uvc_vc_input_terminal_desc *it_desc;
+	struct uvc_vc_output_terminal_desc *ot_desc;
+	struct uvc_vc_selector_unit_desc *su_desc;
+	struct uvc_vc_processing_unit_desc *pu_desc;
+	struct uvc_vc_extension_unit_desc *xu_desc;
+	struct uvc_topo_node *topo_node;
+	struct usb_interface *iface;
+	int i, vsnum, ret = 0;
+
+	uint16_t node_info_size = 0;
+	uint16_t ctrls_mask_size = 0;
+	uint16_t src_ids_num = 0;
+
+	uint8_t idx;
+
+	if (desc->bDescriptorType != UDESC_CS_INTERFACE)
+		return 0;
+
+	switch (desc->bDescriptorSubtype) {
+	case UDESCSUB_VC_HEADER: {
+
+		hdesc = (struct uvc_vc_header_desc *)desc;
+		vsnum = hdesc->bLength >= 12 ? hdesc->bIfaceNums : 0;
+
+
+		if (vsnum > 1 || hdesc->bLength < 12 + vsnum) {
+			DPRINTF("WARNING: to_be_implement or bad interface.\n");
+			if (vsnum > 1)
+				vsnum = 1;
+		}
+
+		ctrl->revision = UGETW(hdesc->wRevision);
+
+		ctrl->clock_freq = UGETDW(hdesc->dClockFreq);
+
+		for (i = 0; i < vsnum; i++) {
+			idx = usbd_iface_num_to_index(sc->udev,
+					hdesc->bIfaceList[i]);
+			if (idx == (uint8_t)(-1)) {
+				DPRINTF("bad vs interface\n");
+				return EINVAL;
+			}
+
+			iface = usbd_get_iface(sc->udev, idx);
+			if (!iface) {
+				DPRINTF("bad vs interface\n");
+				return EINVAL;
+			}
+
+			sc->data = uvc_drv_init_data(iface, idx,
+				iface->idesc->bInterfaceNumber);
+			if (sc->data == NULL)
+				return ENOMEM;
+			ret = uvc_drv_parse_data(sc, sc->data);
+			if (ret)
+				return ret;
+			if (uvc_debug >= 5)
+				uvc_drv_show_data(sc->data);
+		}
+		break;
+	}
+	case UDESCSUB_VC_SELECTOR_UNIT: {
+
+		su_desc = (struct uvc_vc_selector_unit_desc *)desc;
+		if (su_desc->bLength < sizeof(*su_desc)) {
+			DPRINTF("bad interface. UDESCSUB_VC_INPUT_TERMINAL\n");
+			return EINVAL;
+		}
+
+		ctrl->sid = *((char *)desc + 3);
+		DPRINTF("selector unit id:%d\n", ctrl->sid);
+
+		node_info_size = 0;
+		ctrls_mask_size = 0;
+		src_ids_num = su_desc->bLength >= 5 ? su_desc->bNrInPins : 0;
+
+		if (su_desc->bLength < 5 || su_desc->bLength < src_ids_num + 6) {
+			DPRINTF("bad interface. UDESCSUB_VC_SEL_TERMINAL\n");
+			return EINVAL;
+		}
+
+		topo_node = uvc_alloc_topo_node(node_info_size,
+									ctrls_mask_size, src_ids_num);
+		if (!topo_node)
+			return ENOMEM;
+
+		topo_node->node_id = su_desc->bUnitID;
+		topo_node->node_type = su_desc->bDescriptorSubType;
+
+		memcpy(topo_node->src_ids,
+		    (uint8_t *)su_desc + 5, src_ids_num);
+
+		if (su_desc->iSelector != 0) {
+			DPRINTF("WARNING: need to get usb string by id\n");
+		} else {
+			ksnprintf(topo_node->node_name, 64, "Selector %u", topo_node->node_id);
+		}
+
+		STAILQ_INSERT_TAIL(&ctrl->topo_nodes, topo_node, link);
+		break;
+	}
+	case UDESCSUB_VC_INPUT_TERMINAL: {
+
+		it_desc = (struct uvc_vc_input_terminal_desc *)desc;
+
+		if (it_desc->bLength < sizeof(*it_desc)) {
+			DPRINTF("bad interface. UDESCSUB_VC_INPUT_TERMINAL\n");
+			return EINVAL;
+		}
+
+		if (it_desc->wTerminalType[1] == 0) {
+			DPRINTF("bad interface. type is unit\n");
+			return EINVAL;
+		}
+
+		// only support UVC_ITT_CAMERA node
+		if (UGETW(it_desc->wTerminalType) != UVC_ITT_CAMERA) {
+			kprintf("WARNING: to_be_implement\n");
+			return EINVAL;
+		}
+
+		node_info_size = sizeof(struct uvc_ct_node_info);
+		memcpy((void *)&ctrls_mask_size, (uint8_t *)it_desc + 14, 1);
+		src_ids_num = 0;
+
+		if (it_desc->bLength < 15 + ctrls_mask_size) {
+			DPRINTF("%s:%d bad interface. \n", __func__, __LINE__);
+			return EINVAL;
+		}
+
+		topo_node = uvc_alloc_topo_node(node_info_size,
+									ctrls_mask_size, src_ids_num);
+		if (!topo_node)
+			return ENOMEM;
+
+		topo_node->node_id = it_desc->bTerminalID;
+		topo_node->node_type = UGETW(it_desc->wTerminalType) | UVC_TERM_INPUT;
+
+		struct uvc_ct_node_info* node_info = (struct uvc_ct_node_info*)topo_node->node_info;
+		memcpy((void *)&node_info->wObjectiveFocalLengthMin,
+			(uint8_t *)it_desc + 8, 2);
+		memcpy((void *)&node_info->wObjectiveFocalLengthMax,
+			(uint8_t *)it_desc + 10, 2);
+		memcpy((void *)&node_info->wOcularFocalLength,
+			(uint8_t *)it_desc + 12, 2);
+
+		node_info->bControlSize = ctrls_mask_size;
+		node_info->bmControls = topo_node->controls_mask;
+		memcpy((void *)node_info->bmControls,
+			(uint8_t *)it_desc + 15, ctrls_mask_size);
+
+		if (it_desc->bITerminal != 0) {
+			kprintf("WARNING: need to get usb string by id\n");
+		} else if (UVC_ENT_TYPE(topo_node) == UVC_ITT_CAMERA) {
+			ksnprintf(topo_node->node_name, 64, "Camera %u", topo_node->node_id);
+		} else
+			ksnprintf(topo_node->node_name, 64, "Input %u", topo_node->node_id);
+
+		STAILQ_INSERT_TAIL(&ctrl->topo_nodes, topo_node, link);
+		break;
+	}
+	case UDESCSUB_VC_OUTPUT_TERMINAL: {
+
+		ot_desc = (struct uvc_vc_output_terminal_desc *)desc;
+		if (ot_desc->bLength < sizeof(*ot_desc)) {
+			DPRINTF("bad interface. UDESCSUB_VC_OUTPUT_TERMINAL\n");
+			return EINVAL;
+		}
+
+		if (ot_desc->wTerminalType[1] == 0) {
+			DPRINTF("bad interface. type is unit\n");
+			return EINVAL;
+		}
+
+		node_info_size = 0;
+		ctrls_mask_size = 0;
+		src_ids_num = 1;
+
+		topo_node = uvc_alloc_topo_node(node_info_size, ctrls_mask_size, src_ids_num);
+		if (!topo_node)
+			return ENOMEM;
+
+		topo_node->node_id = ot_desc->bTerminalID;
+		topo_node->node_type =  UGETW(ot_desc->wTerminalType) | UVC_TERM_OUTPUT;
+
+		memcpy((void *)topo_node->src_ids,
+		    (uint8_t *)ot_desc + 7, 1);
+
+		if (ot_desc->bITerminal != 0) {
+			kprintf("WARNING: need to get usb string by id\n");
+		} else
+			ksnprintf(topo_node->node_name, 64, "Output %u", topo_node->node_id);
+
+		STAILQ_INSERT_TAIL(&ctrl->topo_nodes, topo_node, link);
+		break;
+	}
+	case UDESCSUB_VC_PROCESSING_UNIT: {
+		uint8_t p = ctrl->revision >= 0x0110 ? 10 : 9;
+
+		pu_desc = (struct uvc_vc_processing_unit_desc *)desc;
+
+		ctrls_mask_size = pu_desc->bLength >= 8 ? pu_desc->bControlSize : 0;
+		if (pu_desc->bLength < ctrls_mask_size + p) {
+			DPRINTF("bad interface. UDESCSUB_VC_PROCESSING_UNIT\n");
+			return EINVAL;
+		}
+
+		node_info_size = sizeof(struct uvc_pu_node_info);
+		src_ids_num = 1;
+
+		topo_node = uvc_alloc_topo_node(node_info_size,
+									ctrls_mask_size, src_ids_num);
+		if (!topo_node)
+			return ENOMEM;
+
+		topo_node->node_id = pu_desc->bUnitID;
+		topo_node->node_type = pu_desc->bDescriptorSubType;
+
+		struct uvc_pu_node_info* node_info = (struct uvc_pu_node_info*)topo_node->node_info;
+		node_info->wMaxMultiplier = UGETW(pu_desc->wMaxMultiplier);
+
+		node_info->bControlSize = ctrls_mask_size;
+		node_info->bmControls = topo_node->controls_mask;
+		memcpy((void *)node_info->bmControls,
+		       (uint8_t *)pu_desc + 8, ctrls_mask_size);
+
+		memcpy(topo_node->src_ids, (uint8_t *)pu_desc + 4, src_ids_num);
+
+		if (ctrl->revision >= 0x0110)
+			node_info->bmVideoStandards =
+				pu_desc->bmVideoStandards;
+
+		if (pu_desc->iProcessing != 0) {
+			kprintf("WARNING: need to get usb string by id\n");
+		} else
+			ksnprintf(topo_node->node_name, 64, "Processing %u", topo_node->node_id);
+
+		STAILQ_INSERT_TAIL(&ctrl->topo_nodes, topo_node, link);
+		break;
+	}
+	case UDESCSUB_VC_EXTENSION_UNIT: {
+		xu_desc = (struct uvc_vc_extension_unit_desc *)desc;
+
+		node_info_size = sizeof(struct uvc_xu_node_info);
+		src_ids_num = xu_desc->bLength >= 22 ? xu_desc->bNrInPins : 0;
+		ctrls_mask_size = xu_desc->bLength >= 24 ?
+			*(&xu_desc->bControlSize + src_ids_num) : 0;
+
+		if (xu_desc->bLength < ctrls_mask_size + src_ids_num + 24) {
+			DPRINTF("bad interface. UDESCSUB_VC_EXTENSION_UNIT\n");
+			return EINVAL;
+		}
+
+		DPRINTF("extension unit id:%d\n", *((char *)desc + 3));
+		if (!memcmp((char *)desc + 4, vc_ext_h264ctrl,
+			sizeof(vc_ext_h264ctrl))) {
+			kprintf("this is h264 extension.\n");
+			ctrl->h264id = *((char *)desc + 3);
+		}
+
+		topo_node = uvc_alloc_topo_node(node_info_size,
+									ctrls_mask_size, src_ids_num);
+		if (!topo_node)
+			return ENOMEM;
+
+		topo_node->node_id = xu_desc->bUnitID;
+		topo_node->node_type = xu_desc->bDescriptorSubType;
+
+		struct uvc_xu_node_info* node_info = (struct uvc_xu_node_info* )topo_node->node_info;
+
+		memcpy((void *)&node_info->guidExtensionCode,
+		       (uint8_t *)xu_desc + 4, 16);
+		node_info->bNumControls = xu_desc->bNumControls;
+
+		node_info->bControlSize = ctrls_mask_size;
+		node_info->bmControls = topo_node->controls_mask;
+		memcpy((void *)node_info->bmControls,
+		       (uint8_t *)xu_desc + 23 + src_ids_num, ctrls_mask_size);
+
+		memcpy((void *)topo_node->src_ids,
+		       (uint8_t *)xu_desc + 22, src_ids_num);
+
+		if (*(&xu_desc->iExtension + ctrls_mask_size + src_ids_num) != 0)
+			kprintf("WARNING: need to get usb string by id\n");
+		else
+			ksprintf(topo_node->node_name, "Extension %u", topo_node->node_id);
+
+		STAILQ_INSERT_TAIL(&ctrl->topo_nodes, topo_node, link);
+		break;
+	}
+	}
+
+	return ret;
+}
+
+static void
+uvc_drv_show_ctrl(struct uvc_drv_ctrl *ctrl)
+{
+	KASSERT(ctrl != NULL, ("Input Error"));
+
+	kprintf("\n");
+	kprintf("ctrl interface idx:%u num:%u\n", ctrl->iface_index,
+		ctrl->iface_num);
+	kprintf("revision:0x%x clock_freq:%u\n", ctrl->revision,
+		ctrl->clock_freq);
+	kprintf("select unit id:%d\n", ctrl->sid);
+}
+
+static int
+uvc_drv_parse_vendor_ctrl(struct uvc_softc *sc, struct usb_descriptor *desc,
+	struct uvc_drv_ctrl *ctrl)
+{
+	switch (UGETW(sc->udev->ddesc.idVendor)) {
+	case USB_VENDOR_ID_LOGITECH:
+		if (desc->bDescriptorType != 0x41 ||
+			desc->bDescriptorSubtype != 0x01)
+			break;
+		DPRINTF("WARNING: __TO_BE_IMPLEMENT__\n");
+		return 1;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int
+uvc_drv_parse_ctrl(struct uvc_softc *sc, struct uvc_drv_ctrl *ctrl)
+{
+	struct usb_descriptor *desc = NULL;
+	int ret;
+
+	while ((desc = usbd_find_descriptor(sc->udev, desc,
+		ctrl->iface_index, 0, 0, 0, 0)) != NULL) {
+		if (uvc_drv_parse_vendor_ctrl(sc, desc, ctrl))
+			continue;
+		ret = uvc_drv_parse_standard_ctrl(sc, desc, ctrl);
+		if (ret) {
+			return ret;
+		}
+	}
+
+	if (uvc_debug >= 5)
+		uvc_drv_show_ctrl(ctrl);
+	return 0;
+}
+
+static struct uvc_drv_ctrl *
+uvc_drv_init_ctrl(struct usb_interface *iface, uint8_t iface_index,
+	uint8_t iface_num)
+{
+	struct uvc_drv_ctrl *pc;
+
+	pc = kmalloc(sizeof(*pc), M_UVC, M_ZERO | M_WAITOK);
+	if (pc) {
+		pc->iface = iface;
+		pc->iface_index = iface_index;
+		pc->iface_num = iface_num;
+		lockinit(&pc->mtx, "uvc ctrl lock", 0, 0);
+		STAILQ_INIT(&pc->topo_nodes);
+	}
+	return pc;
+}
+
+static void
+uvc_drv_destroy_ctrl(struct uvc_drv_ctrl *c)
+{
+	struct uvc_topo_node *topo_node, *tmp;
+	uint8_t i = 0;
+
+	if (c) {
+		STAILQ_FOREACH_SAFE(topo_node, &c->topo_nodes, link, tmp) {
+			for (i = 0; i < topo_node->controls_num; ++i) {
+				struct uvc_control *ctrl = &topo_node->controls[i];
+
+				if (!ctrl->initialized)
+					continue;
+
+				uvc_ctrl_destroy_mappings(ctrl);
+				kfree(ctrl->uvc_data, M_UVC);
+			}
+
+			uvc_free_topo_node(topo_node);
+		}
+		kfree(c, M_UVC);
+	}
+}
+
+static int
+uvc_drv_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return ENXIO;
+
+	if ((uaa->info.bInterfaceClass == UICLASS_VIDEO) &&
+			(uaa->info.bInterfaceSubClass == UISUBCLASS_CTRL))
+		return BUS_PROBE_SPECIFIC;
+
+	/* TODO: streaming interface should be flag as attached */
+
+	return ENXIO;
+}
+
+static int
+uvc_drv_detach(device_t self)
+{
+	struct uvc_softc *sc = device_get_softc(self);
+
+	DPRINTF("%s-%d-%p\n", __func__, __LINE__, self);
+	//delete sysfs
+	//sysfs_video_del_device(sc->udev, sc->iface);
+
+	/* destroy video hard code only one */
+	uvc_drv_destroy_video(sc->video);
+
+	/* driver cleanup */
+	uvc_drv_destroy_data(sc->data);
+	uvc_drv_destroy_ctrl(sc->ctrl);
+
+	if (sc->video) {
+		/* entry close */
+		uvc_v4l2_unreg(sc->video);
+		/* data free */
+		kfree(sc->video, M_UVC);
+		sc->video = NULL;
+	}
+	DPRINTF("%s-%d-%p\n", __func__, __LINE__, self);
+	return 0;
+}
+
+struct usb_vid_pid {
+	uint16_t	idVendor;
+	uint16_t	idProduct;
+};
+
+static struct usb_vid_pid usb_webcam_power_save_lut[] = {
+	{0x1bcf, 0x28c4},       // 5470 MTC Webcam
+	{0x1bcf, 0x2b98},	// 5470 MTC Webcam
+	{0x0c45, 0x671e},       // 5470 MTC Webcam
+	{0x0bda, 0x5520},	// 5470 MTC CNFHH46P27540009CCD0 IntegratedWebcamHD
+	{0x0bda, 0x5675},	// 5470 MTC AzureWave IntegratedWebcamHD
+	{0x0b0e, 0x3013},	// jabra panacast 50
+};
+
+static int
+usb_webcam_power_save(device_t dev, const char *product)
+{
+	int	i;
+	uint32_t vid, pid;
+
+	device_get_usb_vidpid(dev, &vid, &pid);
+	for (i = 0; i < nitems(usb_webcam_power_save_lut); i++) {
+		if (vid == usb_webcam_power_save_lut[i].idVendor &&
+		    pid == usb_webcam_power_save_lut[i].idProduct)
+			return 1;
+	}
+
+	if (product && !strncmp(product, "IntegratedWebcam", 16)) {
+		return 1;
+	}
+
+	return 0;
+}
+
+//import from linux kernel
+static const STRUCT_USB_HOST_ID uvc_quirks_devs[] = {
+	/* LogiLink Wireless Webcam */
+	{ .idVendor		= 0x0416,
+	  .idProduct		= 0xa91a,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Genius eFace 2025 */
+	{ .idVendor		= 0x0458,
+	  .idProduct		= 0x706e,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Microsoft Lifecam NX-6000 */
+	{ .idVendor		= 0x045e,
+	  .idProduct		= 0x00f8,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Microsoft Lifecam VX-7000 */
+	{ .idVendor		= 0x045e,
+	  .idProduct		= 0x0723,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
+	{  .idVendor		= 0x058f,
+	  .idProduct		= 0x3820,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Apple Built-In iSight */
+	{ .idVendor		= 0x05ac,
+	  .idProduct		= 0x8501,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Ophir Optronics - SPCAM 620U */
+	{ .idVendor		= 0x0bd3,
+	  .idProduct		= 0x0555,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* MT6227 */
+	{ .idVendor		= 0x0e8d,
+	  .idProduct		= 0x0004,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX},
+	/* JMicron USB2.0 XGA WebCam */
+	{ .idVendor		= 0x152d,
+	  .idProduct		= 0x0310,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Aveo Technology USB 2.0 Camera */
+	{ .idVendor		= 0x1871,
+	  .idProduct		= 0x0306,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* Manta MM-353 Plako */
+	{ .idVendor		= 0x18ec,
+	  .idProduct		= 0x3188,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* FSC WebCam V30S */
+	{ .idVendor		= 0x18ec,
+	  .idProduct		= 0x3288,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* MSI StarCam 370i */
+	{ .idVendor		= 0x1b3b,
+	  .idProduct		= 0x2951,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+	/* SiGma Micro USB Web Camera */
+	{ .idVendor		= 0x1c4f,
+	  .idProduct		= 0x3000,
+	  .bInterfaceClass	= UICLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
+};
+
+static int
+uvc_ignore_probe_mi_max(device_t dev)
+{
+	int	i;
+	uint32_t vid, pid;
+
+	device_get_usb_vidpid(dev, &vid, &pid);
+	for (i = 0; i < nitems(uvc_quirks_devs); i++) {
+		if (vid == uvc_quirks_devs[i].idVendor &&
+		    pid == uvc_quirks_devs[i].idProduct &&
+		    (uvc_quirks_devs[i].driver_info & UVC_QUIRK_PROBE_MINMAX))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int
+uvc_drv_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uvc_softc *sc = device_get_softc(dev);
+	const char *str;
+	int ret;
+	int interface = 0;
+
+	DPRINTF("%s-%p\n", __func__, dev);
+	device_set_usb_desc(dev);
+	sc->udev = uaa->device;
+	sc->dev = dev;
+	sc->iface = uaa->iface;
+	interface = uaa->info.bIfaceIndex;
+
+	str = usb_get_product(uaa->device);
+	if (!str || !strncmp(str, "product", strlen("product")))
+		ksnprintf(sc->name, 64, "UVC Camera");
+	else
+		ksnprintf(sc->name, 64, "%s", str);
+
+	if (usb_test_quirk(uaa, UQ_UVC_IR_INTRF_DISABLE)) {
+		if (interface == 2) {
+			sc->quirks |= UQ_UVC_IR_INTRF_DISABLE;
+			return ENXIO;
+		}
+	}
+	if (usb_test_quirk(uaa, UQ_UVC_DROP_BIG_FORMAT))
+		sc->quirks |= UVC_DROP_UNCOMPRESS_FORMAT;
+	if (usb_test_quirk(uaa, UQ_UVC_BIG_BUFFER))
+		sc->quirks |= UVC_BIGGER_TRANSFER_BUF;
+	//if (usb_test_quirk(uaa, UQ_UVC_COMMIT_IN_ADVANCE))
+	//	sc->quirks |= UVC_COMMIT_IN_ADVANCE;
+	if (uvc_ignore_probe_mi_max(dev))
+		sc->quirks |= UVC_QUIRK_PROBE_MINMAX;
+	if (usb_test_quirk(uaa, UQ_UVC_DISABLE_HUB_U1U2))
+		sc->quirks |= UVC_DISABLE_HUB_U1U2;
+
+	if (usb_test_quirk(uaa, UQ_UVC_NO_EOF))
+		sc->quirks |= UVC_NO_EOF;
+
+	sc->ctrl = uvc_drv_init_ctrl(uaa->iface, uaa->info.bIfaceIndex,
+		uaa->info.bIfaceNum);
+	if (!sc->ctrl) {
+		DPRINTF("UVC alloc ctrl fault.\n");
+		ret = ENOMEM;
+		goto detach;
+	}
+
+	ret = uvc_drv_parse_ctrl(sc, sc->ctrl);
+	if (ret) {
+		DPRINTF("UVC Parse Ctrl Error.\n");
+		goto detach;
+	}
+	uvc_ctrl_init_dev(sc, sc->ctrl);
+
+	//hard code here, maybe not only one
+	sc->video = uvc_drv_init_video(sc, sc->ctrl, sc->data);
+	if (!sc->video) {
+		ret = EINVAL;
+		goto detach;
+	}
+
+	//add sysfs
+	//sysfs_video_add_device(sc->udev, uaa->iface);
+
+	// power saving
+	if (usb_webcam_power_save(dev, str)) {
+		usbd_set_power_mode(sc->udev, USB_POWER_MODE_SAVE);
+	}
+	if (sc->quirks & UVC_DISABLE_HUB_U1U2 &&
+	    sc->udev->parent_hub &&
+	    sc->udev->speed >= USB_SPEED_SUPER) {
+		usbd_req_set_hub_u1_timeout(sc->udev->parent_hub, NULL,
+					    sc->udev->port_no, 0);
+		usbd_req_set_hub_u2_timeout(sc->udev->parent_hub, NULL,
+					    sc->udev->port_no, 0);
+	}
+	return 0;
+detach:
+	uvc_drv_detach(dev);
+	return ret;
+}
+
+static device_method_t uvc_methods[] = {
+	DEVMETHOD(device_probe, uvc_drv_probe),
+	DEVMETHOD(device_attach, uvc_drv_attach),
+	DEVMETHOD(device_detach, uvc_drv_detach),
+
+	DEVMETHOD_END
+};
+
+static devclass_t uvc_devclass;
+
+static driver_t uvc_driver = {
+	.name = UVC_DRIVER_NAME,
+	.methods = uvc_methods,
+	.size = sizeof(struct uvc_softc),
+};
+
+DRIVER_MODULE(uvc, uhub, uvc_driver, uvc_devclass, NULL, NULL);
+MODULE_DEPEND(uvc, usb, 1, 1, 1);
+MODULE_VERSION(uvc, 1);
+
+/* A match on these entries will load uvc */
+static const STRUCT_USB_HOST_ID __used uvc_devs[] = {
+	{USB_IFACE_CLASS(UICLASS_VIDEO),
+	 USB_IFACE_SUBCLASS(UISUBCLASS_CTRL),},
+};
+
+// TODO
+// USB_PNP_HOST_INFO(uvc_devs);
diff --git a/sys/bus/u4b/uvc/uvc_drv.h b/sys/bus/u4b/uvc/uvc_drv.h
new file mode 100644
index 0000000000..0550f699ca
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_drv.h
@@ -0,0 +1,780 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *	Zhichao Li <Zhichao1.Li@Dell.com>
+ *	Pillar Liang <Pillar.Liang@Dellteam.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#ifndef _DEV_USB_UVC_DRV_H
+#define _DEV_USB_UVC_DRV_H
+
+#include "uvc_buf.h"
+
+/* Vendor */
+#define USB_VENDOR_ID_LOGITECH			0x046D
+/* Driver */
+#define UVC_DRIVER_NAME				"uvcvideo"
+/* V4l2 Version */
+#define	V4L_VERSION(a, b, c)			(((a) << 16) + ((b) << 8) + (c))
+#define	UVC_DEVICE_NAME				"video"
+/* UVC Transfer */
+#define	UVC_N_TRANSFER				2
+#define	UVC_N_BULKTRANSFER			3
+#define UVC_N_ISOFRAMES				0x8
+
+#define	UVCINTR_N_TRANSFER			1
+
+/* Format flags */
+#define UVC_FMT_FLAG_COMPRESSED			0x00000001
+#define UVC_FMT_FLAG_STREAM			0x00000002
+#define UDESCSUB_VC_HEADER			0x1
+#define UDESCSUB_VC_INPUT_TERMINAL		0x2
+#define UDESCSUB_VC_OUTPUT_TERMINAL		0x3
+#define UDESCSUB_VC_SELECTOR_UNIT		0x4
+#define UDESCSUB_VC_PROCESSING_UNIT		0x5
+#define UDESCSUB_VC_EXTENSION_UNIT		0x6
+/* VideoStreaming class specific interface descriptor */
+#define UDESCSUB_VS_UNDEFINED			0x00
+#define UDESCSUB_VS_INPUT_HEADER		0x01
+#define UDESCSUB_VS_OUTPUT_HEADER		0x02
+#define UDESCSUB_VS_STILL_IMAGE_FRAME		0x03
+#define UDESCSUB_VS_FORMAT_UNCOMPRESSED		0x04
+#define UDESCSUB_VS_FRAME_UNCOMPRESSED		0x05
+#define UDESCSUB_VS_FORMAT_MJPEG		0x06
+#define UDESCSUB_VS_FRAME_MJPEG			0x07
+#define UDESCSUB_VS_FORMAT_MPEG2TS		0x0a
+#define UDESCSUB_VS_FORMAT_DV			0x0c
+#define UDESCSUB_VS_COLORFORMAT			0x0d
+#define UDESCSUB_VS_FORMAT_FRAME_BASED		0x10
+#define UDESCSUB_VS_FRAME_FRAME_BASED		0x11
+/* VideoStreaming interface controls */
+#define VS_CONTROL_UNDEFINED			0x00
+#define VS_PROBE_CONTROL			0x01
+#define VS_COMMIT_CONTROL			0x02
+#define VS_STILL_PROBE_CONTROL			0x03
+#define VS_STILL_COMMIT_CONTROL			0x04
+#define VS_STILL_IMAGE_TRIGGER_CONTROL		0x05
+#define VS_STREAM_ERROR_CODE_CONTROL		0x06
+#define VS_GENERATE_KEY_FRAME_CONTROL		0x07
+#define VS_UPDATE_FRAME_SEGMENT_CONTROL		0x08
+#define VS_SYNC_DELAY_CONTROL			0x09
+/* Selector Unit controls */
+#define SU_CONTROL_UNDEFINED			0x00
+#define SU_INPUT_SELECT_CONTROL			0x01
+/* Input Terminal types */
+#define UVC_ITT_VENDOR_SPECIFIC			0x0200
+#define UVC_ITT_CAMERA				0x0201
+#define UVC_ITT_MEDIA_TRANSPORT_INPUT		0x0202
+/* Request codes */
+#define RC_UNDEFINED				0x00
+#define SET_CUR					0x01
+#define GET_CUR					0x81
+#define GET_MIN					0x82
+#define GET_MAX					0x83
+#define GET_RES					0x84
+#define GET_LEN					0x85
+#define GET_INFO				0x86
+#define GET_DEF					0x87
+
+/* A.9.1. VideoControl Interface Control Selectors */
+#define UVC_VC_CONTROL_UNDEFINED                        0x00
+#define UVC_VC_VIDEO_POWER_MODE_CONTROL                 0x01
+#define UVC_VC_REQUEST_ERROR_CODE_CONTROL               0x02
+
+/* A.9.2. Terminal Control Selectors */
+#define UVC_TE_CONTROL_UNDEFINED                        0x00
+
+/* A.9.3. Selector Unit Control Selectors */
+#define UVC_SU_CONTROL_UNDEFINED                        0x00
+#define UVC_SU_INPUT_SELECT_CONTROL                     0x01
+
+/* A.9.4. Camera Terminal Control Selectors */
+#define UVC_CT_CONTROL_UNDEFINED                        0x00
+#define UVC_CT_SCANNING_MODE_CONTROL                    0x01
+#define UVC_CT_AE_MODE_CONTROL                          0x02
+#define UVC_CT_AE_PRIORITY_CONTROL                      0x03
+#define UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL           0x04
+#define UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL           0x05
+#define UVC_CT_FOCUS_ABSOLUTE_CONTROL                   0x06
+#define UVC_CT_FOCUS_RELATIVE_CONTROL                   0x07
+#define UVC_CT_FOCUS_AUTO_CONTROL                       0x08
+#define UVC_CT_IRIS_ABSOLUTE_CONTROL                    0x09
+#define UVC_CT_IRIS_RELATIVE_CONTROL                    0x0a
+#define UVC_CT_ZOOM_ABSOLUTE_CONTROL                    0x0b
+#define UVC_CT_ZOOM_RELATIVE_CONTROL                    0x0c
+#define UVC_CT_PANTILT_ABSOLUTE_CONTROL                 0x0d
+#define UVC_CT_PANTILT_RELATIVE_CONTROL                 0x0e
+#define UVC_CT_ROLL_ABSOLUTE_CONTROL                    0x0f
+#define UVC_CT_ROLL_RELATIVE_CONTROL                    0x10
+#define UVC_CT_PRIVACY_CONTROL                          0x11
+
+/* A.9.5. Processing Unit Control Selectors */
+#define UVC_PU_CONTROL_UNDEFINED                        0x00
+#define UVC_PU_BACKLIGHT_COMPENSATION_CONTROL           0x01
+#define UVC_PU_BRIGHTNESS_CONTROL                       0x02
+#define UVC_PU_CONTRAST_CONTROL                         0x03
+#define UVC_PU_GAIN_CONTROL                             0x04
+#define UVC_PU_POWER_LINE_FREQUENCY_CONTROL             0x05
+#define UVC_PU_HUE_CONTROL                              0x06
+#define UVC_PU_SATURATION_CONTROL                       0x07
+#define UVC_PU_SHARPNESS_CONTROL                        0x08
+#define UVC_PU_GAMMA_CONTROL                            0x09
+#define UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL        0x0a
+#define UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL   0x0b
+#define UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL          0x0c
+#define UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL     0x0d
+#define UVC_PU_DIGITAL_MULTIPLIER_CONTROL               0x0e
+#define UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL         0x0f
+#define UVC_PU_HUE_AUTO_CONTROL                         0x10
+#define UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL            0x11
+#define UVC_PU_ANALOG_LOCK_STATUS_CONTROL               0x12
+
+/* --------------------------------------------------------------------------
+ * UVC constants
+ */
+
+#define UVC_TERM_INPUT                  0x0000
+#define UVC_TERM_OUTPUT                 0x8000
+#define UVC_TERM_DIRECTION(term)        ((term)->type & 0x8000)
+
+#define UVC_ENT_TYPE(ent)		((ent)->node_type & 0x7fff)
+#define UVC_ENT_IS_UNIT(ent)		(((ent)->node_type & 0xff00) == 0)
+#define UVC_ENT_IS_TERM(ent)		(((ent)->node_type & 0xff00) != 0)
+#define UVC_ENT_IS_ITERM(ent) \
+	(UVC_ENT_IS_TERM(ent) && \
+	((ent)->node_type & 0x8000) == UVC_TERM_INPUT)
+#define UVC_ENT_IS_OTERM(ent) \
+	(UVC_ENT_IS_TERM(ent) && \
+	((ent)->node_type & 0x8000) == UVC_TERM_OUTPUT)
+
+// 2.3 Video Function Topology
+enum uvc_topo_type {
+	UVC_TOPO_TYPE_UNKNOWN,
+	UVC_TOPO_TYPE_INPUT_TERMINAL,
+	UVC_TOPO_TYPE_OUTPUT_TERMINAL,
+	UVC_TOPO_TYPE_SELECTOR_UNIT,
+	UVC_TOPO_TYPE_PROCESSING_UNIT,
+	UVC_TOPO_TYPE_ENCODING_UNIT,
+	UVC_TOPO_TYPE_EXTENSION_UNIT,
+	UVC_TOPO_TYPE_CAMERA_TERMINAL,
+	UVC_TOPO_TYPE_MEDIA_TRANSPORT_TERMINAL
+};
+
+/* ------------------------------------------------------------------------
+ * GUIDs
+ */
+#define UVC_GUID_LOGITECH_DEV_INFO \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e}
+#define UVC_GUID_LOGITECH_USER_HW \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f}
+#define UVC_GUID_LOGITECH_VIDEO \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50}
+#define UVC_GUID_LOGITECH_MOTOR \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56}
+
+#define UVC_GUID_FORMAT_MJPEG \
+	{ 'M',  'J',  'P',  'G', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2 \
+	{ 'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_NV12 \
+	{ 'N',  'V',  '1',  '2', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YV12 \
+	{ 'Y',  'V',  '1',  '2', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_I420 \
+	{ 'I',  '4',  '2',  '0', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_UYVY \
+	{ 'U',  'Y',  'V',  'Y', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y800 \
+	{ 'Y',  '8',  '0',  '0', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_BY8 \
+	{ 'B',  'Y',  '8',  ' ', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_H264 \
+	{ 'H',  '2',  '6',  '4', 0x00, 0x00, 0x10, 0x00, \
+	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
+
+struct uvc_softc;
+struct uvc_v4l2;
+struct uvc_buf_queue;
+
+/* Video Descriptor */
+struct uvc_vc_header_desc {
+	uByte	bLength;
+	uByte	bDescriptorType;
+	uByte	bDescriptorSubtype;
+	uWord	wRevision;
+	uWord	wTotalLen;
+	uDWord	dClockFreq;
+	uByte	bIfaceNums;
+	uByte   bIfaceList[0];		/*  Interface Index Lists   */
+} __packed;
+
+struct uvc_vc_input_terminal_desc {
+	uByte	bLength;
+	uByte	bDescriptorType;
+	uByte	bDescriptorSubtype;
+	uByte	bTerminalID;
+	uWord	wTerminalType;
+	uByte	bAssocTerminal;
+	uByte	bITerminal;
+} __packed;
+
+struct uvc_vc_output_terminal_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubType;
+	uByte bTerminalID;
+	uWord wTerminalType;
+	uByte bAssocTerminal;
+	uByte bSourceID;
+	uByte bITerminal;
+} __packed;
+
+struct uvc_vc_selector_unit_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubType;
+	uByte bUnitID;
+	uByte bNrInPins;
+	uByte baSourceID[0];
+	uByte iSelector;
+} __packed;
+
+struct uvc_vc_processing_unit_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubType;
+	uByte bUnitID;
+	uByte bSourceID;
+	uWord wMaxMultiplier;
+	uByte bControlSize;
+	uByte bmControls[2];
+	uByte iProcessing;
+	uByte bmVideoStandards;
+} __packed;
+
+struct uvc_vc_extension_unit_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubType;
+	uByte bUnitID;
+	uByte guidExtensionCode[16];
+	uByte bNumControls;
+	uByte bNrInPins;
+	uByte baSourceID[0];
+	uByte bControlSize;
+	uByte bmControls[0];
+	uByte iExtension;
+} __packed;
+
+struct uvc_vs_in_header_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubtype;
+	uByte bFormatNum;
+	uWord wTotalLen;
+	uByte bEp;
+	uByte bInfo;
+	uByte bTerminalLink;
+	uByte bStillCaptureMethod;
+	uByte bTriggerSupport;
+	uByte bTriggerUsage;
+	uByte bControlSize;
+	uByte bControls[0];         /* Controls */
+} __packed;
+
+struct uvc_vs_color_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubtype;
+	uByte bColorPris;
+	uByte bTranChars;
+	uByte bMatrix;
+};
+
+struct uvc_vs_uncompressed_format_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubtype;
+	uByte bFormatIndex;
+	uByte bFrameNum;
+	uByte bGuidFmt[16];
+	uByte bBpp;
+	uByte bDefaultFrameIndex;
+	uByte bAspectRadiox;
+	uByte bAspectRadioy;
+	uByte bInterlaceFlags;
+	uByte bCopyProtect;
+} __packed;
+
+struct uvc_vs_frame_based_format_desc {
+	uByte bLength;
+	uByte bDescriptorType;
+	uByte bDescriptorSubtype;
+	uByte bFormatIndex;
+	uByte bFrameNum;
+	uByte bGuidFmt[16];
+	uByte bBitsPerpixel;
+	uByte bDefaultFrameIndex;
+	uByte bAspectRadiox;
+	uByte bAspectRadioy;
+	uByte bInterlaceFlags;
+	uByte bCopyProtect;
+	uByte bVariableSize;
+} __packed;
+
+struct uvc_vs_frame_desc {
+	uByte	bLength;
+	uByte	bDescriptorType;
+	uByte	bDescriptorSubtype;
+	uByte	bFrameIndex;
+	uByte	bCapabilities;
+	uWord	wWidth;
+	uWord	wHeight;
+	uDWord	dMinBitRate;
+	uDWord	dMaxBitRate;
+	uDWord	dMaxFrameBufferSize;
+	uDWord	dDefaultFrameInterval;
+	uByte	bFrameIntervalType;
+	uDWord	dFrameInterval[0];
+} __packed;
+
+struct uvc_vs_frame_based_desc {
+	uByte	bLength;
+	uByte	bDescriptorType;
+	uByte	bDescriptorSubtype;
+	uByte	bFrameIndex;
+	uByte	bCapabilities;
+	uWord	wWidth;
+	uWord	wHeight;
+	uDWord	dMinBitRate;
+	uDWord	dMaxBitRate;
+	uDWord	dDefaultFrameInterval;
+	uByte	bFrameIntervalType;
+	uDWord	dwBytesPerLine;
+	uDWord	dFrameInterval[0];
+} __packed;
+
+struct uvc_data_request {
+	uWord wHint;
+	uByte bFormatIndex;
+	uByte bFrameIndex;
+	uDWord dwFrameInterval;
+	uWord wKeyFrameRate;
+	uWord wPFrameRate;
+	uWord wCompQuality;
+	uWord wCompWindowSize;
+	uWord wDelay;
+	uDWord dwMaxFrameSize;
+	uDWord dwMaxPayloadSize;
+	uDWord dwClockFrequency;
+	uByte bFramingInfo;
+	uByte bPreferedVersion;
+	uByte bMinVersion;
+	uByte bMaxVersion;
+	uByte __dummy[14];
+} __packed;
+
+typedef char assert_uvc_data_request_is_48_bytes[(!!(sizeof(struct uvc_data_request)==48))*2-1];
+
+struct uvc_data_payload_header {
+	uint8_t	len;
+#define	UVC_PL_HEADER_BIT_FID		(1)
+#define	UVC_PL_HEADER_BIT_EOF		(1 << 1)
+#define	UVC_PL_HEADER_BIT_PTS		(1 << 2)
+#define	UVC_PL_HEADER_BIT_SCR		(1 << 3)
+#define	UVC_PL_HEADER_BIT_RES		(1 << 4)
+#define	UVC_PL_HEADER_BIT_STI		(1 << 5)
+#define	UVC_PL_HEADER_BIT_ERR		(1 << 6)
+#define	UVC_PL_HEADER_BIT_EOH		(1 << 7)
+	uint8_t	header;
+#if 0
+	uint32_t pts;
+	uint8_t scr[6];
+#endif
+} __attribute__ ((packed));
+
+/*
+ *  * Dynamic controls
+ */
+
+/* Data types for UVC control data */
+#define UVC_CTRL_DATA_RAW          0
+#define UVC_CTRL_DATA_SIGNED       1
+#define UVC_CTRL_DATA_UNSIGNED     2
+#define UVC_CTRL_DATA_BOOLEAN      3
+#define UVC_CTRL_DATA_ENUM         4
+#define UVC_CTRL_DATA_BITMASK      5
+
+/* Control flags */
+#define UVC_CTRL_SET_CUR           0x0001
+#define UVC_CTRL_GET_CUR           0x0002
+#define UVC_CTRL_GET_MIN           0x0004
+#define UVC_CTRL_GET_MAX           0x0008
+#define UVC_CTRL_GET_RES           0x0010
+#define UVC_CTRL_GET_DEF           0x0020
+#define UVC_CTRL_RESTORE           0x0040
+#define UVC_CTRL_AUTO_UPDATE       0x0080
+#define UVC_CTRL_ASYNCHRONOUS	0x0100
+
+#define UVC_CTRL_GET_RANGE \
+	(UVC_CTRL_GET_CUR | UVC_CTRL_GET_MIN | \
+	UVC_CTRL_GET_MAX | UVC_CTRL_GET_RES | \
+	UVC_CTRL_GET_DEF)
+
+struct uvc_ctrl_menu_item {
+	uint32_t value;
+	char name[32];
+};
+
+struct uvc_ctrl_menu {
+	uint32_t item_num;
+	struct uvc_ctrl_menu_item* menu_data;
+};
+
+struct uvc_ctrl_info {
+	uint8_t index;       /* Bit index in bmControls */
+
+	enum uvc_topo_type topo_type;
+	uint8_t selector;
+
+	uint16_t byte_size;
+	uint32_t flags;
+	uint32_t data_type;
+
+#define MAX_MAPPING_NUM 5
+	uint8_t sub_info_num;
+	struct uvc_ctrl_sub_info* sub_infos[MAX_MAPPING_NUM];
+	struct uvc_ctrl_menu menus[MAX_MAPPING_NUM];
+
+	uint8_t cached_sub_info_idx;
+};
+
+struct uvc_ctrl_sub_info {
+	struct uvc_ctrl_info* uvc_info;
+
+	uint32_t v4l2_id;
+	uint8_t v4l2_name[32];
+	enum v4l2_ctrl_type v4l2_type;
+
+	uint8_t bit_offset;
+	uint8_t bit_size;
+
+	/*
+		superior & inferior is used to determine flag 'inactive';
+		E.g. Setting the V4L2_CID_HUE when V4L2_CID_HUE_AUTO is selected doesn't do anything.
+		Only when the V4L2_CID_HUE_AUTO is turned off can you set the gain control.
+		In this case, V4L2_CID_HUE_AUTO is superior of V4L2_CID_HUE.
+	*/
+	uint32_t superior_v4l2_id;
+	uint32_t superior_manual;
+	uint32_t inferior_v4l2_ids[2];
+
+	int32_t (*get)(struct uvc_ctrl_sub_info *this, uint8_t query,
+		       const uint8_t *data);
+	void (*set)(struct uvc_ctrl_sub_info *this, int32_t value,
+		    uint8_t *data);
+};
+
+
+struct uvc_control {
+	struct uvc_topo_node *topo_node;
+	struct uvc_ctrl_info info;
+	uint8_t index; /*
+			*  Used to match the uvc_control entry
+			*  with a uvc_ctrl_info
+			*/
+	uint8_t dirty:1,
+		loaded:1,
+		modified:1,
+		cached:1,
+		initialized:1;
+
+	uint8_t *uvc_data;
+	/* File handle that last changed the control. */
+	struct uvc_drv_video *video;
+};
+struct uvc_ct_node_info {
+	uint16_t wObjectiveFocalLengthMin;
+	uint16_t wObjectiveFocalLengthMax;
+	uint16_t wOcularFocalLength;
+	uint8_t bControlSize;
+	uint8_t *bmControls;
+}; // Camera Terminal
+
+struct uvc_media_node_info {
+	uint8_t bControlSize;
+	uint8_t *bmControls;
+	uint8_t bTransportModeSize;
+	uint8_t *bmTransportModes;
+}; // MEDIA Transport Terminal
+
+struct uvc_pu_node_info {
+	uint16_t wMaxMultiplier;
+	uint8_t bControlSize;
+	uint8_t *bmControls;
+	uint8_t bmVideoStandards;
+}; // Processing Unit
+
+struct uvc_xu_node_info {
+	uint8_t guidExtensionCode[16];
+	uint8_t bNumControls;
+	uint8_t bControlSize;
+	uint8_t *bmControls;
+	uint8_t *bmControlsType;
+}; // Extension Unit
+
+struct uvc_topo_node {
+	STAILQ_ENTRY(uvc_topo_node) link;
+
+	char node_name[64];
+
+	uint16_t node_type;
+
+	uint32_t flags;
+
+	uint8_t node_id;
+	// e.g.:
+	// If Video Function Topology like this:
+	//  o---o
+	//  | 2 |
+	//  o---o
+	//    \___ o---o
+	//     ___ | 5 |
+	//    /    o---o
+	//  o---o
+	//  | 3 |
+	//  o---o
+	//
+	// node 5 has two source ids,
+	// one is node 2, another is node 3.
+	uint16_t src_ids_num;
+	uint8_t *src_ids;
+
+	// mask of controls
+	uint16_t controls_mask_len;
+	uint8_t *controls_mask;
+
+	uint32_t controls_num;
+	struct uvc_control *controls;
+
+	// data pointed by node_info is specified by topo_type
+	void *node_info;
+};
+
+struct uvc_data_interval {
+	uint32_t val;
+};
+
+struct uvc_data_frame {
+/* begin interval for this frame */
+	struct uvc_data_interval *interval;
+	/* interval nums, desc string */
+	uint8_t interval_type;
+
+/* attribute */
+	uint8_t index;
+	uint8_t cap;
+	uint8_t nouse;
+	uint32_t min_bit_rate;
+	uint32_t max_bit_rate;
+	uint32_t default_interval;
+	uint32_t max_buffer_size;
+
+/* important */
+	uint16_t width;
+	uint16_t height;
+};
+
+struct uvc_data_format {
+/* begin frame for this format */
+	struct uvc_data_frame *frm;
+	uint64_t nfrm;
+	/* for COMPRESSED or not */
+	uint64_t flags;
+
+/* attributes */
+	uint32_t fcc;
+	uint8_t index;
+	uint8_t bpp;
+	uint8_t colorspace;
+	uint8_t unuse;
+	char name[64];
+};
+
+struct uvc_drv_data {
+/* interface */
+	struct usb_interface	*iface;
+	/* for device configure iface_num + unit */
+	uint8_t	iface_num;
+	/* for usb framework */
+	uint8_t	iface_index;
+	/*for distinguishing bulk and iso uvc*/
+	uint8_t num_altsetting;
+
+	uint16_t maxpsize;
+
+/* transfer */
+	//struct usb_xfer	*xfer[UVC_N_TRANSFER];
+	struct usb_xfer	*xfer[UVC_N_BULKTRANSFER];
+
+/* data format */
+	uint8_t nfmt;
+	uint8_t nfrm;
+	uint8_t nitv;
+	uint8_t unuse[3];
+	struct uvc_data_format *fmt;
+};
+
+struct uvc_drv_ctrl {
+/* interface */
+	struct usb_interface	*iface;
+	/* for device configure iface_num + unit */
+	uint8_t	iface_num;
+	/* for usb framework */
+	uint8_t	iface_index;
+	struct lock	mtx;
+
+/* desc infomation */
+	STAILQ_HEAD(, uvc_topo_node) topo_nodes;
+	uint8_t	sid;
+	uint8_t	h264id;
+	uint8_t unuse;
+	uint16_t	revision;
+	uint16_t	clock_freq;
+};
+
+struct uvc_drv_video {
+	uint64_t		unit;
+	uint64_t		users;
+	uint64_t		pri;
+	uint64_t		htsf;
+
+	struct uvc_softc	*sc;
+	struct uvc_drv_ctrl	*ctrl;
+	struct uvc_drv_data	*data;
+	struct usb_xfer		*intr_xfer[UVCINTR_N_TRANSFER];
+
+	struct lock mtx;
+	struct uvc_data_request	req;
+	struct uvc_data_format *cur_fmt;
+	struct uvc_data_frame *cur_frm;
+	uint64_t enable;
+	uint64_t bad_frame;
+	enum v4l2_buf_type type;
+	uint8_t last_fid;
+
+	struct {
+		uint8_t header[256];
+		uint8_t last_fid;
+		uint32_t header_size;
+		uint32_t skip_payload;
+		uint32_t payload_size;
+		uint32_t max_payload_size;
+	} bulk;
+
+	struct uvc_v4l2 *v4l2;
+	struct uvc_buf_queue bq;
+};
+
+struct uvc_drv_format_desc {
+	char *name;
+	uint8_t guid[16];
+	uint32_t fcc;
+};
+
+struct uvc_softc {
+	char	name[64];
+	device_t		dev;
+	struct usb_device	*udev;
+	struct usb_interface	*iface;
+
+	struct uvc_drv_ctrl	*ctrl;
+	/* TODO: not only one */
+	struct uvc_drv_data	*data;
+	struct uvc_drv_video	*video;
+	enum v4l2_buf_type	type;
+	//struct uvc_buf_queue	*bq;
+#define UVC_DROP_UNCOMPRESS_FORMAT 0x1
+#define UVC_QUIRK_PROBE_MINMAX	   0x2
+#define UVC_BIGGER_TRANSFER_BUF	   0x4
+#define UVC_COMMIT_IN_ADVANCE	   0x8
+#define UVC_DISABLE_HUB_U1U2	   0x16
+#define UVC_NO_EOF		   (1<<6)
+	uint8_t			quirks;
+};
+
+extern int uvc_debug;
+struct uvc_xu_control_query;
+
+MALLOC_DECLARE(M_UVC);
+
+int uvc_drv_check_video_context(struct uvc_drv_video *v, unsigned char *data,
+	uint32_t len);
+int uvc_drv_get_v4l2_fmt(struct uvc_drv_video *v, struct v4l2_format *f);
+int uvc_drv_enum_v4l2_fmt(struct uvc_drv_video *v, struct v4l2_fmtdesc *vfmt);
+int uvc_drv_enum_v4l2_framesizes(struct uvc_drv_video *v,
+	struct v4l2_frmsizeenum *fs);
+int uvc_drv_enum_v4l2_frameintervals(struct uvc_drv_video *v,
+	struct v4l2_frmivalenum *itv);
+int uvc_drv_try_v4l2_fmt(struct uvc_drv_video *video, struct v4l2_format *vfmt,
+	struct uvc_data_request *req, struct uvc_data_format **rfmt,
+	struct uvc_data_frame **rfrm);
+int uvc_drv_set_video(struct uvc_drv_video *video,
+	struct uvc_data_request *req, struct uvc_data_format *fmt,
+	struct uvc_data_frame *frm);
+int uvc_drv_xu_ctrl_query(struct uvc_drv_video *v,
+	struct uvc_xu_control_query *q);
+int uvc_drv_start_video(struct uvc_drv_video *video);
+int uvc_drv_stop_video(struct uvc_drv_video *video, int close);
+int uvc_drv_set_streampar(struct uvc_drv_video *v, struct v4l2_streamparm *a);
+int uvc_drv_get_selection(struct uvc_drv_video *v, struct v4l2_selection *sel);
+int uvc_drv_get_pixelaspect(void);
+//uvc controls
+int uvc_ctrl_init_dev(struct uvc_softc *sc, struct uvc_drv_ctrl *ctrls);
+void uvc_ctrl_destroy_mappings(struct uvc_control *ctrl);
+int uvc_query_v4l2_ctrl(struct uvc_drv_video *video,
+			struct v4l2_queryctrl *v4l2_ctrl);
+int uvc_query_v4l2_menu(struct uvc_drv_video *video,
+			struct v4l2_querymenu *qm);
+#endif /* end _DEV_USB_UVC_DRV_H */
diff --git a/sys/bus/u4b/uvc/uvc_v4l2.c b/sys/bus/u4b/uvc/uvc_v4l2.c
new file mode 100644
index 0000000000..10e4fc2429
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_v4l2.c
@@ -0,0 +1,799 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *	Zhichao Li <Zhichao1.Li@Dell.com>
+ *	Pilar Liang <Pillar.Liang@Dellteam.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/condvar.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/device.h>
+#include <sys/devfs.h>
+#include <sys/conf.h>
+
+#include <bus/u4b/usb.h>
+#define USB_DEBUG_VAR uvc_debug
+#include <bus/u4b/usb_debug.h>
+#include <bus/u4b/usbdi.h>
+
+#include <contrib/v4l/videodev.h>
+#include <contrib/v4l/videodev2.h>
+
+#include "uvc_drv.h"
+#include "uvc_buf.h"
+#include "uvc_v4l2.h"
+
+int v4l2_not_allowed;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, v4l2, CTLFLAG_RW, 0, "USB v4l2");
+
+TUNABLE_INT("hw.usb.v4l2.v4l2_not_allowed", &v4l2_not_allowed);
+SYSCTL_INT(_hw_usb_v4l2, OID_AUTO, v4l2_not_allowed, CTLFLAG_RW,
+		    &v4l2_not_allowed, 0, "V4L2 Permission");
+
+static int
+uvc_v4l2_acquire_pri(struct uvc_v4l2_cdev_priv *priv)
+{
+	DPRINTF("=======v4l2 acquire pri %s-%s %d-%d mem:%lu===\n",
+		curthread->td_proc->p_comm, curthread->td_proc->p_pptr->p_comm,
+		curthread->td_proc->p_pid, curthread->td_proc->p_pptr->p_pid,
+		priv->num);
+	DPRINTF("pri:%lu video pri:%lu\n", priv->work_pri, priv->v->pri);
+
+	if (priv->work_pri == UVC_V4L2_PRI_ACTIVE) {
+		DPRINTF("%s already get\n", __func__);
+		return 0;
+	}
+
+	if (atomic_cmpset_64(&priv->v->pri, 0, 1) == 0) {
+		DPRINTF("%s busy\n", __func__);
+		return EBUSY;
+	}
+
+	priv->work_pri = UVC_V4L2_PRI_ACTIVE;
+
+	return 0;
+}
+
+static void
+uvc_v4l2_dismiss_pri(struct uvc_v4l2_cdev_priv *priv)
+{
+	DPRINTF("%s num:%lu\n", __func__, priv->num);
+	if (priv->work_pri == UVC_V4L2_PRI_ACTIVE) {
+		DPRINTF("%s free\n", __func__);
+		atomic_store_64(&priv->v->pri, 0);
+	}
+
+	priv->work_pri = UVC_V4L2_PRI_PASSIVE;
+}
+
+static int
+uvc_v4l2_has_pri(struct uvc_v4l2_cdev_priv *priv)
+{
+	return priv->work_pri == UVC_V4L2_PRI_ACTIVE;
+}
+
+static int
+uvc_v4l2_query_cap(struct uvc_drv_video *v, void *addr)
+{
+	struct v4l2_capability cap;
+	struct usb_device *udev;
+	char bus_info[128];
+
+	memset(&cap, 0x00, sizeof(cap));
+	strlcpy(cap.driver, UVC_DRIVER_NAME, sizeof(cap.driver));
+	ksnprintf(cap.card, sizeof(cap.card), "%s", v->sc->name);
+
+	if (!v->sc)
+		return EINVAL;
+	udev = v->sc->udev;
+	if (!udev)
+		return EINVAL;
+	usbd_get_phys(udev, bus_info, 128);
+	ksnprintf(cap.bus_info, sizeof(cap.bus_info), "%s", bus_info);
+	cap.version = V4L_VERSION(3, 14, 1);
+
+	/*
+	 *
+	 * V4L2_CAP_VIDEO_CAPTURE input device
+	 * V4L2_CAP_READWRITE read method
+	 * V4L2_CAP_STREAMING mmap/userptr
+	 *
+	 */
+	cap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+		V4L2_CAP_ASYNCIO | 0x80000000;
+	cap.reserved[0] = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	memcpy(addr, &cap, sizeof(cap));
+	return 0;
+}
+
+static int
+uvc_v4l2_g_selection(struct uvc_drv_video *v, void *addr)
+{
+	struct v4l2_selection *p = addr;
+	uint32_t old_type = p->type;
+	int ret = 0;
+
+	if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		p->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		p->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	ret = uvc_drv_get_selection(v, p);
+	p->type = old_type;
+	return ret;
+}
+
+__unused static int
+uvc_v4l2_enumstd(struct v4l2_standard *std)
+{
+	std->id = V4L2_STD_UNKNOWN;
+
+	std->framelines = (std->id & V4L2_STD_525_60) ? 525 : 625;
+	if (std->id & V4L2_STD_525_60) {
+		std->frameperiod.numerator = 1001;
+		std->frameperiod.denominator = 30000;
+	} else {
+		std->frameperiod.numerator = 1;
+		std->frameperiod.denominator = 25;
+	}
+	strncpy(std->name, "UVC Camera", sizeof(std->name) - 1);
+
+	return 0;
+}
+
+static int
+uvc_v4l2_cropcap(struct uvc_drv_video *v, void *addr)
+{
+	struct v4l2_cropcap *p = NULL;
+	struct v4l2_selection s = {};
+	uint32_t ret = 0;
+
+	p = (struct v4l2_cropcap *)addr;
+
+	p->pixelaspect.numerator = 1;
+	p->pixelaspect.denominator = 1;
+
+	s.type = p->type;
+
+	if (s.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		s.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	else if (s.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		s.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+	ret = uvc_drv_get_pixelaspect();
+	//515 is ENOIOCTLCMD in linux
+	if (ret && ret != ENOTTY && ret != 515)
+		return ret;
+	if (V4L2_TYPE_IS_OUTPUT(p->type))
+		s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+	else
+		s.target = V4L2_SEL_TGT_CROP_BOUNDS;
+
+	ret = uvc_v4l2_g_selection(v, &s);
+	if (ret)
+		return ret;
+	p->bounds = s.r;
+	//obtaining defrect
+	if (s.target == V4L2_SEL_TGT_COMPOSE_BOUNDS)
+		s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+	else
+		s.target = V4L2_SEL_TGT_CROP_DEFAULT;
+	ret = uvc_v4l2_g_selection(v, &s);
+	if (ret)
+		return ret;
+
+	p->defrect = s.r;
+
+	return 0;
+}
+
+void
+uvc_simple_frac(uint32_t *numerator, uint32_t *denominator,
+	uint32_t max_iters, uint32_t threshold)
+{
+	uint32_t *coeff;
+	uint32_t best_den, best_num, remainder;
+	uint32_t i, j;
+
+	 if (!numerator || !denominator ||
+	     *denominator == 0 || max_iters == 0) {
+		 return;
+	 }
+
+	coeff = (uint32_t *)kmalloc(max_iters * sizeof(uint32_t),
+		M_UVC, M_ZERO | M_WAITOK);
+	if (coeff == NULL)
+		return;
+
+	best_den = *numerator;
+	best_num = *denominator;
+
+	for (j = 0; j < max_iters && best_num != 0; ++j) {
+		coeff[j] = best_den / best_num;
+		if (coeff[j] >= threshold) {
+			if (j < 2)
+				j++;
+			break;
+		}
+
+		remainder = best_den % best_num;
+		best_den = best_num;
+		best_num = remainder;
+	}
+
+	best_den = 0;
+	best_num = 1;
+
+	for (i = j; i > 0; --i) {
+		remainder = best_num;
+		best_num = coeff[i-1] * best_num + best_den;
+		best_den = remainder;
+	}
+
+	*numerator = best_num;
+	*denominator = best_den;
+	kfree(coeff, M_UVC);
+}
+
+static int
+uvc_v4l2_get_parm(struct uvc_drv_video *video, struct v4l2_streamparm *arg)
+{
+	uint32_t numerator, denominator;
+
+	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return EINVAL;
+
+	numerator = UGETW(video->req.dwFrameInterval);
+	denominator = 10000000;
+	uvc_simple_frac(&numerator, &denominator, 8, 333);
+
+	memset(arg, 0, sizeof(*arg));
+	arg->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	arg->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+	arg->parm.capture.capturemode = 0;
+	arg->parm.capture.timeperframe.numerator = numerator;
+	arg->parm.capture.timeperframe.denominator = denominator;
+	arg->parm.capture.extendedmode = 0;
+	arg->parm.capture.readbuffers = 0;
+
+	return 0;
+}
+
+//only stub function
+static int
+uvc_v4l2_enum_input(struct uvc_drv_ctrl *ctrl, struct v4l2_input *input)
+{
+	uint32_t index = input->index;
+	struct uvc_topo_node *it = NULL; // input terminal
+
+	/*WARNING: to_be_implement here, hard code no SELECTOR */
+	if (index)
+		return EINVAL;
+
+	it = STAILQ_FIRST(&ctrl->topo_nodes);
+	if (!it)
+		return EINVAL;
+
+	memset(input, 0, sizeof(*input));
+	input->index = index;
+	strlcpy(input->name, it->node_name, sizeof(input->name));
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+static void
+uvc_v4l2_dtor(void *data)
+{
+	struct uvc_v4l2_cdev_priv *priv = data;
+	struct uvc_drv_video *v;
+
+	v = priv->v;
+	if (priv->work_pri == UVC_V4L2_PRI_ACTIVE) {
+		atomic_store_64(&v->pri, 0);
+	}
+
+	atomic_subtract_64(&v->users, 1);
+
+	kfree(data, M_UVC);
+
+	DPRINTF("%s\n", __func__);
+}
+
+static int
+uvc_v4l2_queryctrl(struct uvc_drv_video *v, struct v4l2_queryctrl *qc)
+{
+	int ret = EINVAL;
+
+	if (qc == NULL)
+		return ret;
+
+	ret = uvc_query_v4l2_ctrl(v, qc);
+	return ret;
+}
+
+static int
+uvc_v4l2_querymenu(struct uvc_drv_video *v, struct v4l2_querymenu *qm)
+{
+	int ret = EINVAL;
+
+	if (qm == NULL)
+		return ret;
+
+	ret = uvc_query_v4l2_menu(v, qm);
+	return ret;
+}
+
+static int
+uvc_v4l2_open(struct dev_open_args *ap)
+{
+	cdev_t dev = ap->a_head.a_dev;
+	struct uvc_v4l2_cdev_priv *priv;
+	struct uvc_drv_video *v = dev->si_drv1;
+
+	if (v4l2_not_allowed != 0) {
+		return ENXIO;			/* failure */
+	}
+
+	atomic_add_64(&v->users, 1);
+
+	DPRINTF("===v4l2 open %s-%s %d-%d mem:%lu pri:%lu===\n",
+		curthread->td_proc->p_comm, curthread->td_proc->p_pptr->p_comm,
+		curthread->td_proc->p_pid, curthread->td_proc->p_pptr->p_pid,
+		v->users, v->pri);
+
+	DPRINTF("====%s====%d %d %s\n", __func__, curthread->td_proc->p_pid,
+		curthread->td_proc->p_pptr->p_pid,
+		curthread->td_proc->p_pptr->p_comm);
+	DPRINTF("enable:%lu\n", v->enable);
+
+	priv = (struct uvc_v4l2_cdev_priv *)
+		kmalloc(sizeof(*priv), M_UVC, M_ZERO | M_WAITOK);
+	if (!priv) {
+		DPRINTF("%s %d------>Error.\n", __func__, __LINE__);
+		atomic_subtract_64(&v->users, 1);
+		return ENOMEM;
+	}
+
+	priv->work_mode = UVC_V4L2_MODE_READ;
+	priv->work_pri = UVC_V4L2_PRI_PASSIVE;
+	priv->v = v;
+	priv->num = v->users;
+	(void)devfs_set_cdevpriv(ap->a_fpp ? *ap->a_fpp : NULL, priv, uvc_v4l2_dtor);
+	return 0;
+}
+
+static int
+uvc_v4l2_close(struct dev_close_args *ap)
+{
+	cdev_t dev = ap->a_head.a_dev;
+	struct uvc_drv_video *v = dev->si_drv1;
+	struct uvc_v4l2_cdev_priv *priv;
+	int ret;
+
+	ret = devfs_get_cdevpriv(ap->a_fp, (void **)&priv);
+	if (ret != 0) {
+		DPRINTF("error===================================\n");
+		return (ret);
+	}
+
+	DPRINTF("===v4l2 begin to close %s-%s %d-%d num:%lu mem:%lu===\n",
+		curthread->td_proc->p_comm, curthread->td_proc->p_pptr->p_comm,
+		curthread->td_proc->p_pid, curthread->td_proc->p_pptr->p_pid,
+		priv->num, v->users);
+	DPRINTF("=vedio device pri:%lu--thispri:%lu\n", v->pri, priv->work_pri);
+	DPRINTF("----%s----\n", __func__);
+
+	if (uvc_v4l2_has_pri(priv)) {
+		ret = uvc_drv_stop_video(v, 1);
+		if (ret)
+			DPRINTF("close stop video fault\n");
+		uvc_buf_queue_free_bufs(&v->bq);
+		uvc_v4l2_dismiss_pri(priv);
+	}
+	//atomic_subtract_64(&v->users, 1);
+
+	return 0;
+}
+
+static int
+uvc_v4l2_ioctl(struct dev_ioctl_args *ap)
+{
+	cdev_t dev = ap->a_head.a_dev;
+	u_long cmd = ap->a_cmd;
+	caddr_t data = ap->a_data;
+	int fflag = ap->a_fflag;
+	struct uvc_v4l2_cdev_priv *priv;
+	struct uvc_drv_video *v = dev->si_drv1;
+
+	struct v4l2_fmtdesc *f_d;
+	struct v4l2_format *fmt;
+	struct v4l2_streamparm *strp;
+
+	struct uvc_data_format *rfmt;
+	struct uvc_data_frame *rfrm;
+	struct uvc_data_request req;
+	struct v4l2_requestbuffers *rb;
+	struct v4l2_buffer *buf;
+	struct uvc_xu_control_query qry;
+	int ret = ENOTTY;
+
+	ret = devfs_get_cdevpriv(ap->a_fp, (void **)&priv);
+	if (ret != 0) {
+		DPRINTF("%s %d---------->err:%d\n", __func__, __LINE__, ret);
+		return (ret);
+	}
+
+	/* check pri */
+	switch (cmd) {
+	/* case VIDIOC_S_INPUT:*/
+	case VIDIOC_REQBUFS:
+	case VIDIOC_S_PARM:
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_S_FMT:
+		ret = uvc_v4l2_acquire_pri(priv);
+		if (ret)
+			return ret;
+		break;
+
+	case VIDIOC_QUERYBUF:
+	case VIDIOC_QBUF:
+	case VIDIOC_DQBUF:
+	case VIDIOC_STREAMON:
+	case VIDIOC_STREAMOFF:
+		if (!uvc_v4l2_has_pri(priv))
+			return EBUSY;
+		break;
+	default:
+		break;
+	}
+
+	switch (cmd) {
+	case VIDIOC_QUERYCAP:
+		DPRINTF("VIDIOC_QUERYCAP\n");
+		ret = uvc_v4l2_query_cap(v, data);
+		break;
+
+	case VIDIOC_G_PARM:
+		DPRINTF("VIDIOC_G_PARM\n");
+		ret = uvc_v4l2_get_parm(v, (struct v4l2_streamparm *)data);
+		break;
+
+	case VIDIOC_S_PARM:
+		DPRINTF("VIDIOC_S_PARM\n");
+		strp = (struct v4l2_streamparm *)data;
+		if (strp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		ret = uvc_drv_set_streampar(v, strp);
+		break;
+
+	case VIDIOC_ENUMINPUT:
+		DPRINTF("VIDIOC_ENUMINPUT\n");
+		ret = uvc_v4l2_enum_input(v->ctrl, (struct v4l2_input *)data);
+		break;
+
+	case VIDIOC_G_INPUT:
+		kprintf("unsupport ioctl VIDIOC_G_INPUT.\n");
+		*(int *)data = 0;
+		break;
+	case VIDIOC_ENUMSTD:
+		kprintf("unsupport ioctl VIDIOC_ENUMSTD\n");
+		ret = ENOTTY;
+		break;
+
+	case VIDIOC_QUERYCTRL:
+		DPRINTF("VIDIOC_QUERYCTRL\n");
+		ret = uvc_v4l2_queryctrl(v, (struct v4l2_queryctrl *)data);
+		break;
+
+	case VIDIOC_QUERYMENU:
+		DPRINTF("VIDIOC_QUERYMENU.\n");
+		ret = uvc_v4l2_querymenu(v, (struct v4l2_querymenu *)data);
+		break;
+
+	case VIDIOC_G_CTRL:
+		kprintf("unsupport ioctl VIDIOC_G_CTRL.\n");
+		ret = EINVAL;
+		break;
+	case VIDIOC_G_STD:
+		kprintf("unsupport ioctl VIDIOC_G_STD.\n");
+		ret = ENOTTY;
+		break;
+
+	case VIDIOC_CROPCAP:
+		ret = uvc_v4l2_cropcap(v, data);
+		break;
+
+	case VIDIOC_ENUM_FMT:
+		DPRINTF("VIDIOC_ENUM_FMT\n");
+		f_d = (struct v4l2_fmtdesc *)data;
+		if (f_d->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		ret = uvc_drv_enum_v4l2_fmt(v, f_d);
+		break;
+
+	case VIDIOC_ENUM_FRAMESIZES:
+		//DPRINTF("VIDIOC_ENUM_FRAMESIZES\n");
+		ret = uvc_drv_enum_v4l2_framesizes(v,
+			(struct v4l2_frmsizeenum *)data);
+		break;
+
+	case VIDIOC_ENUM_FRAMEINTERVALS:
+		//DPRINTF("VIDIOC_ENUM_FRAMEINTERVAL\n");
+		ret = uvc_drv_enum_v4l2_frameintervals(v,
+			(struct v4l2_frmivalenum *)data);
+		break;
+
+	case VIDIOC_G_FMT:
+		DPRINTF("VIDIOC_G_FMT\n");
+		fmt = (struct v4l2_format *)data;
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		ret = uvc_drv_get_v4l2_fmt(v, fmt);
+		break;
+
+	case VIDIOC_TRY_FMT:
+		DPRINTF("VIDIOC_TRY_FMT\n");
+		fmt = (struct v4l2_format *)data;
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		ret = uvc_drv_try_v4l2_fmt(v, fmt, &req, NULL, NULL);
+		break;
+
+	case VIDIOC_S_FMT:
+		DPRINTF("VIDIOC_S_FMT\n");
+		fmt = (struct v4l2_format *)data;
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		ret = uvc_drv_try_v4l2_fmt(v, fmt, &req, &rfmt, &rfrm);
+		if (!ret) {
+			ret = uvc_drv_set_video(v, &req, rfmt, rfrm);
+		}
+
+		break;
+
+	case VIDIOC_STREAMON:
+		DPRINTF("VIDIOC_STREAMON\n");
+		if (*(int *)data != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+
+		ret = uvc_drv_start_video(v);
+
+		break;
+
+	case VIDIOC_STREAMOFF:
+		DPRINTF("VIDIOC_STREAMOFF\n");
+		if (*(int *)data != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+
+		ret = uvc_drv_stop_video(v, 0);
+		break;
+
+	case VIDIOC_REQBUFS:
+		DPRINTF("VIDIOC_REQBUFS default size:%u\n",
+			UGETDW(v->req.dwMaxFrameSize));
+		rb = (struct v4l2_requestbuffers *)data;
+		if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+			/* todo */rb->memory != V4L2_MEMORY_MMAP)
+			return EINVAL;
+		ret = uvc_buf_queue_req_bufs(&v->bq, &rb->count,
+			UGETDW(v->req.dwMaxFrameSize));
+		if (!ret) {
+			if (!(v->cur_fmt->flags & UVC_FMT_FLAG_COMPRESSED)) {
+				DPRINTF("drop incomplete\n");
+				uvc_buf_queue_set_drop_flag(&v->bq);
+			}
+			priv->work_mode = (rb->count)
+				? UVC_V4L2_MODE_MMAP : UVC_V4L2_MODE_READ;
+		}
+		break;
+
+	case VIDIOC_EXPBUF:
+		kprintf("unsupport ioctl VIDIOC_EXPBUF.\n");
+		ret = ENOTTY;
+		break;
+
+	case VIDIOC_QUERYBUF:
+		DPRINTF("VIDIOC_QUERYBUF\n");
+		buf = (struct v4l2_buffer *)data;
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return EINVAL;
+		ret = uvc_buf_queue_query_buf(&v->bq, buf);
+		break;
+
+	case VIDIOC_QBUF:
+		//DPRINTF("VIDIOC_QBUF\n");
+		buf = (struct v4l2_buffer *)data;
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+			buf->memory != V4L2_MEMORY_MMAP) {
+			DPRINTF("memory:%d\n", buf->memory);
+			return EINVAL;
+		}
+		ret = uvc_buf_queue_queue_buf(&v->bq, buf);
+		if (ret)
+			DPRINTF("return:%d\n", ret);
+		return ret;
+
+	case VIDIOC_DQBUF:
+		//DPRINTF("VIDIOC_DQBUF\n");
+		buf = (struct v4l2_buffer *)data;
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+			buf->memory != V4L2_MEMORY_MMAP) {
+			return EINVAL;
+		}
+
+		ret = uvc_buf_queue_dequeue_buf(&v->bq, buf,
+				(fflag & O_NONBLOCK)?1:0);
+		if (ret)
+			DPRINTF("return:%d\n", ret);
+		return ret;
+
+	case UVCIOC_CTRL_MAP:
+		kprintf("unsupport ioctl UVCIOC_CTRL_MAP.\n");
+		ret = ENXIO;
+		break;
+
+	case UVCIOC_CTRL_QUERY:
+		if (copyin((void *)data, &qry, sizeof(qry)))
+			return EFAULT;
+
+		if (qry.size > 64)
+			return ENXIO;
+		ret = uvc_drv_xu_ctrl_query(v, &qry);
+		break;
+
+	case VIDIOC_LOG_STATUS:
+		ret = 0; /* UVC can't support VIDIOC_LOG_STATUS */
+		break;
+
+	default:
+		kprintf("%lx-%s need to be implement\n", cmd, __func__);
+		//ret = EINVAL;
+		ret = ENOTTY;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+uvc_v4l2_mmap(struct dev_mmap_args *ap)
+{
+	cdev_t dev = ap->a_head.a_dev;
+	struct uvc_drv_video *v = dev->si_drv1;
+	vm_offset_t offset = ap->a_offset;
+	vm_paddr_t paddr = 0;
+	int res;
+
+	res = uvc_buf_queue_mmap(&v->bq, &paddr, offset);
+	ap->a_result = paddr;
+
+	return res;
+}
+
+static int
+uvc_v4l2_read(struct dev_read_args *ap __unused)
+{
+	DPRINTF("%s\n", __func__);
+	return EINVAL;
+}
+
+static int
+uvc_v4l2_write(struct dev_write_args *ap __unused)
+{
+	DPRINTF("%s\n", __func__);
+	return EINVAL;
+}
+
+static int
+uvc_v4l2_kqfilter(struct dev_kqfilter_args *ap)
+{
+	cdev_t dev = ap->a_head.a_dev;
+	struct uvc_drv_video *v = dev->si_drv1;
+	struct knote *kn = ap->a_kn;
+
+	ap->a_result = uvc_buf_queue_kqfilter(&v->bq, kn);
+
+	return (0);
+}
+
+static struct dev_ops uvc_v4l2_cdevsw = {
+	{ .name = UVC_V4L2_DEVICE_NAME,
+	  .maj = 0,
+	  .flags = D_MPSAFE | D_MEM
+	},
+	.d_open	=	uvc_v4l2_open,
+	.d_read	=	uvc_v4l2_read,
+	.d_write =	uvc_v4l2_write,
+	.d_close =	uvc_v4l2_close,
+	.d_ioctl =	uvc_v4l2_ioctl,
+	.d_mmap =	uvc_v4l2_mmap,
+	.d_kqfilter =	uvc_v4l2_kqfilter,
+};
+
+void
+uvc_v4l2_unreg(struct uvc_drv_video *v)
+{
+	DPRINTF("%s\n", __func__);
+	/* destroy v4l2 */
+	if (v->v4l2) {
+
+		if (v->v4l2->cdev)
+			destroy_dev(v->v4l2->cdev);
+
+		kfree(v->v4l2, M_UVC);
+	}
+	/* remove */
+	v->v4l2 = NULL;
+}
+
+int
+uvc_v4l2_reg(struct uvc_drv_video *v)
+{
+	struct cdev *cdev;
+	struct uvc_v4l2 *v4l2;
+	int ret;
+
+	DPRINTF("%s\n", __func__);
+	v4l2 = kmalloc(sizeof(*v4l2), M_UVC, M_ZERO | M_WAITOK);
+	if (!v4l2)
+		return ENOMEM;
+
+	cdev = make_dev(&uvc_v4l2_cdevsw, v->unit,
+			UID_ROOT, GID_VIDEO, 0666,
+			"%s%lu", UVC_V4L2_DEVICE_NAME, v->unit);
+	if (cdev == NULL) {
+		DPRINTF("failed to create v4l2 char device.\n");
+		return ret;
+	}
+	cdev->si_drv1 = v;
+
+	make_dev_alias(cdev, "char/%u:%lu",
+		       LINUX_MAJAR, LINUX_MINOR + v->unit);
+
+	v4l2->cdev = cdev;
+	v->v4l2 = v4l2;
+
+	return 0;
+}
diff --git a/sys/bus/u4b/uvc/uvc_v4l2.h b/sys/bus/u4b/uvc/uvc_v4l2.h
new file mode 100644
index 0000000000..dc8c932915
--- /dev/null
+++ b/sys/bus/u4b/uvc/uvc_v4l2.h
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Dell Inc.
+ *
+ *	Alvin Chen <weike_chen@dell.com, vico.chern@qq.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * UVC spec:https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip
+ */
+
+#ifndef _DEV_USB_UVC_V4L2_H
+#define _DEV_USB_UVC_V4L2_H
+
+#define UVC_V4L2_DEVICE_NAME		"video"
+
+#ifndef LINUX_MAJAR
+#define LINUX_MAJAR 81
+#endif
+#ifndef LINUX_MINOR
+#define LINUX_MINOR 0
+#endif
+
+enum uvc_v4l2_work_mode {
+	UVC_V4L2_MODE_READ	=	0x0,
+	UVC_V4L2_MODE_MMAP,
+	UVC_V4L2_MODE_MAX,
+};
+
+enum uvc_v4l2_work_pri {
+	UVC_V4L2_PRI_PASSIVE	=	0x0,
+	UVC_V4L2_PRI_ACTIVE,
+	UVC_V4L2_PRI_MAX,
+};
+
+struct uvc_v4l2_cdev_priv {
+	uint64_t	work_mode;
+	uint64_t	work_pri;
+	uint64_t	num;
+	struct uvc_drv_video	*v;
+};
+
+struct uvc_v4l2 {
+	struct cdev *cdev;
+};
+
+struct uvc_xu_control_mapping {
+	uint32_t id;
+	uint8_t name[32];
+	uint8_t entity[16];
+	uint8_t selector;
+	uint8_t size;
+	uint8_t offset;
+	uint32_t v4l2_type;
+	uint32_t data_type;
+	char *menu_info;
+	uint32_t menu_count;
+	uint32_t reserved[4];
+};
+
+struct uvc_xu_control_query {
+	uint8_t unit;
+	uint8_t selector;
+	uint8_t query;
+	uint16_t size;
+	uint8_t *data;
+};
+
+#define UVCIOC_CTRL_MAP	_IOWR('u', 0x20, struct uvc_xu_control_mapping)
+#define UVCIOC_CTRL_QUERY	_IOWR('u', 0x21, struct uvc_xu_control_query)
+
+struct uvc_drv_video;
+extern int uvc_v4l2_reg(struct uvc_drv_video *v);
+extern void uvc_v4l2_unreg(struct uvc_drv_video *v);
+extern void uvc_simple_frac(uint32_t *numerator, uint32_t *denominator,
+	uint32_t n_terms, uint32_t threshold);
+
+#endif /* end _DEV_USB_UVC_V4L2_H */
diff --git a/sys/contrib/v4l/videodev.h b/sys/contrib/v4l/videodev.h
new file mode 100644
index 0000000000..a2e512190e
--- /dev/null
+++ b/sys/contrib/v4l/videodev.h
@@ -0,0 +1,367 @@
+/*
+ * This header comes from linux, but it has no license. The author
+ * (Alan Cox @ Redhat) gave explicit permissions to use it in FreeBSD.
+ * The FreeBSD vendor branch for v4l gives a more detailed description
+ * about this in the README.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __LINUX_VIDEODEV_H
+#define __LINUX_VIDEODEV_H
+
+#include <sys/types.h>
+typedef int32_t __s32;
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
+
+#if 0
+#define HAVE_V4L1 1
+
+#include <linux/videodev2.h>
+#endif 
+
+#define VID_TYPE_CAPTURE	1	/* Can capture */
+#define VID_TYPE_TUNER		2	/* Can tune */
+#define VID_TYPE_TELETEXT	4	/* Does teletext */
+#define VID_TYPE_OVERLAY	8	/* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY	16	/* Overlay by chromakey */
+#define VID_TYPE_CLIPPING	32	/* Can clip */
+#define VID_TYPE_FRAMERAM	64	/* Uses the frame buffer memory */
+#define VID_TYPE_SCALES		128	/* Scalable */
+#define VID_TYPE_MONOCHROME	256	/* Monochrome only */
+#define VID_TYPE_SUBCAPTURE	512	/* Can capture subareas of the image */
+#define VID_TYPE_MPEG_DECODER	1024	/* Can decode MPEG streams */
+#define VID_TYPE_MPEG_ENCODER	2048	/* Can encode MPEG streams */
+#define VID_TYPE_MJPEG_DECODER	4096	/* Can decode MJPEG streams */
+#define VID_TYPE_MJPEG_ENCODER	8192	/* Can encode MJPEG streams */
+
+struct video_capability
+{
+	char name[32];
+	int type;
+	int channels;	/* Num channels */
+	int audios;	/* Num audio devices */
+	int maxwidth;	/* Supported width */
+	int maxheight;	/* And height */
+	int minwidth;	/* Supported width */
+	int minheight;	/* And height */
+};
+
+
+struct video_channel
+{
+	int channel;
+	char name[32];
+	int tuners;
+	__u32  flags;
+#define VIDEO_VC_TUNER		1	/* Channel has a tuner */
+#define VIDEO_VC_AUDIO		2	/* Channel has audio */
+	__u16  type;
+#define VIDEO_TYPE_TV		1
+#define VIDEO_TYPE_CAMERA	2
+	__u16 norm;			/* Norm set by channel */
+};
+
+struct video_tuner
+{
+	int tuner;
+	char name[32];
+	unsigned long rangelow, rangehigh;	/* Tuner range */
+	__u32 flags;
+#define VIDEO_TUNER_PAL		1
+#define VIDEO_TUNER_NTSC	2
+#define VIDEO_TUNER_SECAM	4
+#define VIDEO_TUNER_LOW		8	/* Uses KHz not MHz */
+#define VIDEO_TUNER_NORM	16	/* Tuner can set norm */
+#define VIDEO_TUNER_STEREO_ON	128	/* Tuner is seeing stereo */
+#define VIDEO_TUNER_RDS_ON      256     /* Tuner is seeing an RDS datastream */
+#define VIDEO_TUNER_MBS_ON      512     /* Tuner is seeing an MBS datastream */
+	__u16 mode;			/* PAL/NTSC/SECAM/OTHER */
+#define VIDEO_MODE_PAL		0
+#define VIDEO_MODE_NTSC		1
+#define VIDEO_MODE_SECAM	2
+#define VIDEO_MODE_AUTO		3
+	__u16 signal;			/* Signal strength 16bit scale */
+};
+
+struct video_picture
+{
+	__u16	brightness;
+	__u16	hue;
+	__u16	colour;
+	__u16	contrast;
+	__u16	whiteness;	/* Black and white only */
+	__u16	depth;		/* Capture depth */
+	__u16   palette;	/* Palette in use */
+#define VIDEO_PALETTE_GREY	1	/* Linear greyscale */
+#define VIDEO_PALETTE_HI240	2	/* High 240 cube (BT848) */
+#define VIDEO_PALETTE_RGB565	3	/* 565 16 bit RGB */
+#define VIDEO_PALETTE_RGB24	4	/* 24bit RGB */
+#define VIDEO_PALETTE_RGB32	5	/* 32bit RGB */
+#define VIDEO_PALETTE_RGB555	6	/* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422	7	/* YUV422 capture */
+#define VIDEO_PALETTE_YUYV	8
+#define VIDEO_PALETTE_UYVY	9	/* The great thing about standards is ... */
+#define VIDEO_PALETTE_YUV420	10
+#define VIDEO_PALETTE_YUV411	11	/* YUV411 capture */
+#define VIDEO_PALETTE_RAW	12	/* RAW capture (BT848) */
+#define VIDEO_PALETTE_YUV422P	13	/* YUV 4:2:2 Planar */
+#define VIDEO_PALETTE_YUV411P	14	/* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P	15	/* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P	16	/* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR	13	/* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7	/* start of component entries */
+};
+
+struct video_audio
+{
+	int	audio;		/* Audio channel */
+	__u16	volume;		/* If settable */
+	__u16	bass, treble;
+	__u32	flags;
+#define VIDEO_AUDIO_MUTE	1
+#define VIDEO_AUDIO_MUTABLE	2
+#define VIDEO_AUDIO_VOLUME	4
+#define VIDEO_AUDIO_BASS	8
+#define VIDEO_AUDIO_TREBLE	16
+#define VIDEO_AUDIO_BALANCE	32
+	char    name[16];
+#define VIDEO_SOUND_MONO	1
+#define VIDEO_SOUND_STEREO	2
+#define VIDEO_SOUND_LANG1	4
+#define VIDEO_SOUND_LANG2	8
+	__u16   mode;
+	__u16	balance;	/* Stereo balance */
+	__u16	step;		/* Step actual volume uses */
+};
+
+struct video_clip
+{
+	__s32	x,y;
+	__s32	width, height;
+	struct	video_clip *next;	/* For user use/driver use only */
+};
+
+struct video_window
+{
+	__u32	x,y;			/* Position of window */
+	__u32	width,height;		/* Its size */
+	__u32	chromakey;
+	__u32	flags;
+	struct	video_clip *clips;	/* Set only */
+	int	clipcount;
+#define VIDEO_WINDOW_INTERLACE	1
+#define VIDEO_WINDOW_CHROMAKEY	16	/* Overlay by chromakey */
+#define VIDEO_CLIP_BITMAP	-1
+/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
+#define VIDEO_CLIPMAP_SIZE	(128 * 625)
+};
+
+struct video_capture
+{
+	__u32 	x,y;			/* Offsets into image */
+	__u32	width, height;		/* Area to capture */
+	__u16	decimation;		/* Decimation divider */
+	__u16	flags;			/* Flags for capture */
+#define VIDEO_CAPTURE_ODD		0	/* Temporal */
+#define VIDEO_CAPTURE_EVEN		1
+};
+
+struct video_buffer
+{
+	void	*base;
+	int	height,width;
+	int	depth;
+	int	bytesperline;
+};
+
+struct video_mmap
+{
+	unsigned	int frame;		/* Frame (0 - n) for double buffer */
+	int		height,width;
+	unsigned	int format;		/* should be VIDEO_PALETTE_* */
+};
+
+struct video_key
+{
+	__u8	key[8];
+	__u32	flags;
+};
+
+#define VIDEO_MAX_FRAME		32
+
+struct video_mbuf
+{
+	int	size;		/* Total memory to map */
+	int	frames;		/* Frames */
+	int	offsets[VIDEO_MAX_FRAME];
+};
+
+#define 	VIDEO_NO_UNIT	(-1)
+
+struct video_unit
+{
+	int 	video;		/* Video minor */
+	int	vbi;		/* VBI minor */
+	int	radio;		/* Radio minor */
+	int	audio;		/* Audio minor */
+	int	teletext;	/* Teletext minor */
+};
+
+struct vbi_format {
+	__u32	sampling_rate;	/* in Hz */
+	__u32	samples_per_line;
+	__u32	sample_format;	/* VIDEO_PALETTE_RAW only (1 byte) */
+	__s32	start[2];	/* starting line for each frame */
+	__u32	count[2];	/* count of lines for each frame */
+	__u32	flags;
+#define	VBI_UNSYNC	1	/* can distingues between top/bottom field */
+#define	VBI_INTERLACED	2	/* lines are interlaced */
+};
+
+/* video_info is biased towards hardware mpeg encode/decode */
+/* but it could apply generically to any hardware compressor/decompressor */
+struct video_info
+{
+	__u32	frame_count;	/* frames output since decode/encode began */
+	__u32	h_size;		/* current unscaled horizontal size */
+	__u32	v_size;		/* current unscaled veritcal size */
+	__u32	smpte_timecode;	/* current SMPTE timecode (for current GOP) */
+	__u32	picture_type;	/* current picture type */
+	__u32	temporal_reference;	/* current temporal reference */
+	__u8	user_data[256];	/* user data last found in compressed stream */
+	/* user_data[0] contains user data flags, user_data[1] has count */
+};
+
+/* generic structure for setting playback modes */
+struct video_play_mode
+{
+	int	mode;
+	int	p1;
+	int	p2;
+};
+
+/* for loading microcode / fpga programming */
+struct video_code
+{
+	char	loadwhat[16];	/* name or tag of file being passed */
+	int	datasize;
+	__u8	*data;
+};
+
+#define VIDIOCGCAP		_IOR('v',1,struct video_capability)	/* Get capabilities */
+#define VIDIOCGCHAN		_IOWR('v',2,struct video_channel)	/* Get channel info (sources) */
+#define VIDIOCSCHAN		_IOW('v',3,struct video_channel)	/* Set channel 	*/
+#define VIDIOCGTUNER		_IOWR('v',4,struct video_tuner)		/* Get tuner abilities */
+#define VIDIOCSTUNER		_IOW('v',5,struct video_tuner)		/* Tune the tuner for the current channel */
+#define VIDIOCGPICT		_IOR('v',6,struct video_picture)	/* Get picture properties */
+#define VIDIOCSPICT		_IOW('v',7,struct video_picture)	/* Set picture properties */
+#define VIDIOCCAPTURE		_IOW('v',8,int)				/* Start, end capture */
+#define VIDIOCGWIN		_IOR('v',9, struct video_window)	/* Get the video overlay window */
+#define VIDIOCSWIN		_IOW('v',10, struct video_window)	/* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
+#define VIDIOCGFBUF		_IOR('v',11, struct video_buffer)	/* Get frame buffer */
+#define VIDIOCSFBUF		_IOW('v',12, struct video_buffer)	/* Set frame buffer - root only */
+#define VIDIOCKEY		_IOR('v',13, struct video_key)		/* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
+#define VIDIOCGFREQ		_IOR('v',14, unsigned long)		/* Set tuner */
+#define VIDIOCSFREQ		_IOW('v',15, unsigned long)		/* Set tuner */
+#define VIDIOCGAUDIO		_IOR('v',16, struct video_audio)	/* Get audio info */
+#define VIDIOCSAUDIO		_IOW('v',17, struct video_audio)	/* Audio source, mute etc */
+#define VIDIOCSYNC		_IOW('v',18, int)			/* Sync with mmap grabbing */
+#define VIDIOCMCAPTURE		_IOW('v',19, struct video_mmap)		/* Grab frames */
+#define VIDIOCGMBUF		_IOR('v',20, struct video_mbuf)		/* Memory map buffer info */
+#define VIDIOCGUNIT		_IOR('v',21, struct video_unit)		/* Get attached units */
+#define VIDIOCGCAPTURE		_IOR('v',22, struct video_capture)	/* Get subcapture */
+#define VIDIOCSCAPTURE		_IOW('v',23, struct video_capture)	/* Set subcapture */
+#define VIDIOCSPLAYMODE		_IOW('v',24, struct video_play_mode)	/* Set output video mode/feature */
+#define VIDIOCSWRITEMODE	_IOW('v',25, int)			/* Set write mode */
+#define VIDIOCGPLAYINFO		_IOR('v',26, struct video_info)		/* Get current playback info from hardware */
+#define VIDIOCSMICROCODE	_IOW('v',27, struct video_code)		/* Load microcode into hardware */
+#define	VIDIOCGVBIFMT		_IOR('v',28, struct vbi_format)		/* Get VBI information */
+#define	VIDIOCSVBIFMT		_IOW('v',29, struct vbi_format)		/* Set VBI information */
+
+
+#define BASE_VIDIOCPRIVATE	192		/* 192-255 are private */
+
+/* VIDIOCSWRITEMODE */
+#define VID_WRITE_MPEG_AUD		0
+#define VID_WRITE_MPEG_VID		1
+#define VID_WRITE_OSD			2
+#define VID_WRITE_TTX			3
+#define VID_WRITE_CC			4
+#define VID_WRITE_MJPEG			5
+
+/* VIDIOCSPLAYMODE */
+#define VID_PLAY_VID_OUT_MODE		0
+	/* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */
+#define VID_PLAY_GENLOCK		1
+	/* p1: 0 = OFF, 1 = ON */
+	/* p2: GENLOCK FINE DELAY value */
+#define VID_PLAY_NORMAL			2
+#define VID_PLAY_PAUSE			3
+#define VID_PLAY_SINGLE_FRAME		4
+#define VID_PLAY_FAST_FORWARD		5
+#define VID_PLAY_SLOW_MOTION		6
+#define VID_PLAY_IMMEDIATE_NORMAL	7
+#define VID_PLAY_SWITCH_CHANNELS	8
+#define VID_PLAY_FREEZE_FRAME		9
+#define VID_PLAY_STILL_MODE		10
+#define VID_PLAY_MASTER_MODE		11
+	/* p1: see below */
+#define		VID_PLAY_MASTER_NONE	1
+#define		VID_PLAY_MASTER_VIDEO	2
+#define		VID_PLAY_MASTER_AUDIO	3
+#define VID_PLAY_ACTIVE_SCANLINES	12
+	/* p1 = first active; p2 = last active */
+#define VID_PLAY_RESET			13
+#define VID_PLAY_END_MARK		14
+
+
+
+#define VID_HARDWARE_BT848	1
+#define VID_HARDWARE_QCAM_BW	2
+#define VID_HARDWARE_PMS	3
+#define VID_HARDWARE_QCAM_C	4
+#define VID_HARDWARE_PSEUDO	5
+#define VID_HARDWARE_SAA5249	6
+#define VID_HARDWARE_AZTECH	7
+#define VID_HARDWARE_SF16MI	8
+#define VID_HARDWARE_RTRACK	9
+#define VID_HARDWARE_ZOLTRIX	10
+#define VID_HARDWARE_SAA7146    11
+#define VID_HARDWARE_VIDEUM	12	/* Reserved for Winnov videum */
+#define VID_HARDWARE_RTRACK2	13
+#define VID_HARDWARE_PERMEDIA2	14	/* Reserved for Permedia2 */
+#define VID_HARDWARE_RIVA128	15	/* Reserved for RIVA 128 */
+#define VID_HARDWARE_PLANB	16	/* PowerMac motherboard video-in */
+#define VID_HARDWARE_BROADWAY	17	/* Broadway project */
+#define VID_HARDWARE_GEMTEK	18
+#define VID_HARDWARE_TYPHOON	19
+#define VID_HARDWARE_VINO	20	/* SGI Indy Vino */
+#define VID_HARDWARE_CADET	21	/* Cadet radio */
+#define VID_HARDWARE_TRUST	22	/* Trust FM Radio */
+#define VID_HARDWARE_TERRATEC	23	/* TerraTec ActiveRadio */
+#define VID_HARDWARE_CPIA	24
+#define VID_HARDWARE_ZR36120	25	/* Zoran ZR36120/ZR36125 */
+#define VID_HARDWARE_ZR36067	26	/* Zoran ZR36067/36060 */
+#define VID_HARDWARE_OV511	27
+#define VID_HARDWARE_ZR356700	28	/* Zoran 36700 series */
+#define VID_HARDWARE_W9966	29
+#define VID_HARDWARE_SE401	30	/* SE401 USB webcams */
+#define VID_HARDWARE_PWC	31	/* Philips webcams */
+#define VID_HARDWARE_MEYE	32	/* Sony Vaio MotionEye cameras */
+#define VID_HARDWARE_CPIA2	33
+#define VID_HARDWARE_VICAM      34
+#define VID_HARDWARE_SF16FMR2	35
+#define VID_HARDWARE_W9968CF	36
+#define VID_HARDWARE_SAA7114H   37
+#define VID_HARDWARE_SN9C102	38
+#define VID_HARDWARE_ARV	39
+#endif /* __LINUX_VIDEODEV_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/sys/contrib/v4l/videodev2.h b/sys/contrib/v4l/videodev2.h
new file mode 100644
index 0000000000..9ccff3347d
--- /dev/null
+++ b/sys/contrib/v4l/videodev2.h
@@ -0,0 +1,1907 @@
+/*
+ *  Video for Linux Two header file
+ *
+ *  Copyright (C) 1999-2007 the contributors
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  Alternatively you can redistribute this file under the terms of the
+ *  BSD license as stated below:
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *  3. The names of its contributors may not be used to endorse or promote
+ *     products derived from this software without specific prior written
+ *     permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *	Header file for v4l or V4L2 drivers and applications
+ * with public API.
+ * All kernel-specific stuff were moved to media/v4l2-dev.h, so
+ * no #if __KERNEL tests are allowed here
+ *
+ *	See http://linuxtv.org for more info
+ *
+ *	Author: Bill Dirks <bill@thedirks.org>
+ *		Justin Schoeman
+ *              Hans Verkuil <hverkuil@xs4all.nl>
+ *		et al.
+ *
+ * $FreeBSD$
+ */
+#ifndef __LINUX_VIDEODEV2_H
+#define __LINUX_VIDEODEV2_H
+
+#include <sys/ioccom.h>
+
+#ifdef __DragonFly__
+#define __user
+typedef uint64_t	__u64;
+typedef int64_t		__s64;
+typedef uint32_t	__le32;
+#else
+#ifdef __KERNEL__
+#include <linux/time.h>     /* need struct timeval */
+#else
+#include <sys/time.h>
+#endif
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#endif
+
+/*
+ * Common stuff for both V4L1 and V4L2
+ * Moved from videodev.h
+ */
+#define VIDEO_MAX_FRAME               32
+
+#ifndef __KERNEL__
+
+/* These defines are V4L1 specific and should not be used with the V4L2 API!
+   They will be removed from this header in the future. */
+
+#define VID_TYPE_CAPTURE	1	/* Can capture */
+#define VID_TYPE_TUNER		2	/* Can tune */
+#define VID_TYPE_TELETEXT	4	/* Does teletext */
+#define VID_TYPE_OVERLAY	8	/* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY	16	/* Overlay by chromakey */
+#define VID_TYPE_CLIPPING	32	/* Can clip */
+#define VID_TYPE_FRAMERAM	64	/* Uses the frame buffer memory */
+#define VID_TYPE_SCALES		128	/* Scalable */
+#define VID_TYPE_MONOCHROME	256	/* Monochrome only */
+#define VID_TYPE_SUBCAPTURE	512	/* Can capture subareas of the image */
+#define VID_TYPE_MPEG_DECODER	1024	/* Can decode MPEG streams */
+#define VID_TYPE_MPEG_ENCODER	2048	/* Can encode MPEG streams */
+#define VID_TYPE_MJPEG_DECODER	4096	/* Can decode MJPEG streams */
+#define VID_TYPE_MJPEG_ENCODER	8192	/* Can encode MJPEG streams */
+#endif
+
+/*
+ *	M I S C E L L A N E O U S
+ */
+
+/*  Four-character-code (FOURCC) */
+#define v4l2_fourcc(a, b, c, d)\
+	((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
+
+/*
+ *	E N U M S
+ */
+enum v4l2_field {
+	V4L2_FIELD_ANY           = 0, /* driver can choose from none,
+					 top, bottom, interlaced
+					 depending on whatever it thinks
+					 is approximate ... */
+	V4L2_FIELD_NONE          = 1, /* this device has no fields ... */
+	V4L2_FIELD_TOP           = 2, /* top field only */
+	V4L2_FIELD_BOTTOM        = 3, /* bottom field only */
+	V4L2_FIELD_INTERLACED    = 4, /* both fields interlaced */
+	V4L2_FIELD_SEQ_TB        = 5, /* both fields sequential into one
+					 buffer, top-bottom order */
+	V4L2_FIELD_SEQ_BT        = 6, /* same as above + bottom-top order */
+	V4L2_FIELD_ALTERNATE     = 7, /* both fields alternating into
+					 separate buffers */
+	V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field
+					 first and the top field is
+					 transmitted first */
+	V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field
+					 first and the bottom field is
+					 transmitted first */
+};
+#define V4L2_FIELD_HAS_TOP(field)	\
+	((field) == V4L2_FIELD_TOP 	||\
+	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_INTERLACED_TB ||\
+	 (field) == V4L2_FIELD_INTERLACED_BT ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_FIELD_HAS_BOTTOM(field)	\
+	((field) == V4L2_FIELD_BOTTOM 	||\
+	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_INTERLACED_TB ||\
+	 (field) == V4L2_FIELD_INTERLACED_BT ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_FIELD_HAS_BOTH(field)	\
+	((field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_INTERLACED_TB ||\
+	 (field) == V4L2_FIELD_INTERLACED_BT ||\
+	 (field) == V4L2_FIELD_SEQ_TB ||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_TYPE_IS_OUTPUT(type)                               \
+	((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT                   \
+	|| (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE         \
+	|| (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY               \
+	|| (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY        \
+	|| (type) == V4L2_BUF_TYPE_VBI_OUTPUT                  \
+	|| (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT           \
+	|| (type) == V4L2_BUF_TYPE_SDR_OUTPUT                  \
+	|| (type) == V4L2_BUF_TYPE_META_OUTPUT)
+
+/* Current cropping area */
+#define V4L2_SEL_TGT_CROP               0x0000
+/* Default cropping area */
+#define V4L2_SEL_TGT_CROP_DEFAULT       0x0001
+/* Cropping bounds */
+#define V4L2_SEL_TGT_CROP_BOUNDS        0x0002
+/* Native frame size */
+#define V4L2_SEL_TGT_NATIVE_SIZE        0x0003
+/* Current composing area */
+#define V4L2_SEL_TGT_COMPOSE            0x0100
+/* Default composing area */
+#define V4L2_SEL_TGT_COMPOSE_DEFAULT    0x0101
+/* Composing bounds */
+#define V4L2_SEL_TGT_COMPOSE_BOUNDS     0x0102
+/* Current composing area plus all padding pixels */
+#define V4L2_SEL_TGT_COMPOSE_PADDED     0x0103
+
+/* Selection flags */
+#define V4L2_SEL_FLAG_GE                (1 << 0)
+#define V4L2_SEL_FLAG_LE                (1 << 1)
+#define V4L2_SEL_FLAG_KEEP_CONFIG       (1 << 2)
+
+enum v4l2_buf_type {
+	V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
+	V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
+	V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
+	V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
+	V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
+	V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
+	V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
+#if 1
+	/* Experimental */
+	V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
+#endif
+	V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
+	V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE  = 10,
+	V4L2_BUF_TYPE_SDR_CAPTURE          = 11,
+	V4L2_BUF_TYPE_SDR_OUTPUT           = 12,
+	V4L2_BUF_TYPE_META_CAPTURE         = 13,
+	V4L2_BUF_TYPE_META_OUTPUT          = 14,
+
+	V4L2_BUF_TYPE_PRIVATE              = 0x80,
+};
+
+enum v4l2_ctrl_type {
+	V4L2_CTRL_TYPE_INTEGER	     = 1,
+	V4L2_CTRL_TYPE_BOOLEAN	     = 2,
+	V4L2_CTRL_TYPE_MENU	     = 3,
+	V4L2_CTRL_TYPE_BUTTON	     = 4,
+	V4L2_CTRL_TYPE_INTEGER64     = 5,
+	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
+	V4L2_CTRL_TYPE_STRING        = 7,
+};
+
+enum v4l2_tuner_type {
+	V4L2_TUNER_RADIO	     = 1,
+	V4L2_TUNER_ANALOG_TV	     = 2,
+	V4L2_TUNER_DIGITAL_TV	     = 3,
+};
+
+enum v4l2_memory {
+	V4L2_MEMORY_MMAP             = 1,
+	V4L2_MEMORY_USERPTR          = 2,
+	V4L2_MEMORY_OVERLAY          = 3,
+};
+
+/* see also http://vektor.theorem.ca/graphics/ycbcr/ */
+enum v4l2_colorspace {
+	/* ITU-R 601 -- broadcast NTSC/PAL */
+	V4L2_COLORSPACE_SMPTE170M     = 1,
+
+	/* 1125-Line (US) HDTV */
+	V4L2_COLORSPACE_SMPTE240M     = 2,
+
+	/* HD and modern captures. */
+	V4L2_COLORSPACE_REC709        = 3,
+
+	/* broken BT878 extents (601, luma range 16-253 instead of 16-235) */
+	V4L2_COLORSPACE_BT878         = 4,
+
+	/* These should be useful.  Assume 601 extents. */
+	V4L2_COLORSPACE_470_SYSTEM_M  = 5,
+	V4L2_COLORSPACE_470_SYSTEM_BG = 6,
+
+	/* I know there will be cameras that send this.  So, this is
+	 * unspecified chromaticities and full 0-255 on each of the
+	 * Y'CbCr components
+	 */
+	V4L2_COLORSPACE_JPEG          = 7,
+
+	/* For RGB colourspaces, this is probably a good start. */
+	V4L2_COLORSPACE_SRGB          = 8,
+};
+
+enum v4l2_priority {
+	V4L2_PRIORITY_UNSET       = 0,  /* not initialized */
+	V4L2_PRIORITY_BACKGROUND  = 1,
+	V4L2_PRIORITY_INTERACTIVE = 2,
+	V4L2_PRIORITY_RECORD      = 3,
+	V4L2_PRIORITY_DEFAULT     = V4L2_PRIORITY_INTERACTIVE,
+};
+
+struct v4l2_rect {
+	__s32   left;
+	__s32   top;
+	__s32   width;
+	__s32   height;
+};
+
+struct v4l2_fract {
+	__u32   numerator;
+	__u32   denominator;
+};
+
+/*
+ *	D R I V E R   C A P A B I L I T I E S
+ */
+struct v4l2_capability {
+	__u8	driver[16];	/* i.e. "bttv" */
+	__u8	card[32];	/* i.e. "Hauppauge WinTV" */
+	__u8	bus_info[32];	/* "PCI:" + pci_name(pci_dev) */
+	__u32   version;        /* should use KERNEL_VERSION() */
+	__u32	capabilities;	/* Device capabilities */
+	__u32	reserved[4];
+};
+
+/* Values for 'capabilities' field */
+#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* Is a video capture device */
+#define V4L2_CAP_VIDEO_OUTPUT		0x00000002  /* Is a video output device */
+#define V4L2_CAP_VIDEO_OVERLAY		0x00000004  /* Can do video overlay */
+#define V4L2_CAP_VBI_CAPTURE		0x00000010  /* Is a raw VBI capture device */
+#define V4L2_CAP_VBI_OUTPUT		0x00000020  /* Is a raw VBI output device */
+#define V4L2_CAP_SLICED_VBI_CAPTURE	0x00000040  /* Is a sliced VBI capture device */
+#define V4L2_CAP_SLICED_VBI_OUTPUT	0x00000080  /* Is a sliced VBI output device */
+#define V4L2_CAP_RDS_CAPTURE		0x00000100  /* RDS data capture */
+#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY	0x00000200  /* Can do video output overlay */
+#define V4L2_CAP_HW_FREQ_SEEK		0x00000400  /* Can do hardware frequency seek  */
+#define V4L2_CAP_RDS_OUTPUT		0x00000800  /* Is an RDS encoder */
+
+#define V4L2_CAP_TUNER			0x00010000  /* has a tuner */
+#define V4L2_CAP_AUDIO			0x00020000  /* has audio support */
+#define V4L2_CAP_RADIO			0x00040000  /* is a radio device */
+#define V4L2_CAP_MODULATOR		0x00080000  /* has a modulator */
+
+#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
+#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
+#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */
+
+/*
+ *	V I D E O   I M A G E   F O R M A T
+ */
+struct v4l2_pix_format {
+	__u32         		width;
+	__u32			height;
+	__u32			pixelformat;
+	enum v4l2_field  	field;
+	__u32            	bytesperline;	/* for padding, zero if unused */
+	__u32          		sizeimage;
+	enum v4l2_colorspace	colorspace;
+	__u32			priv;		/* private data, depends on pixelformat */
+};
+
+/*      Pixel format         FOURCC                          depth  Description  */
+
+/* RGB formats */
+#define V4L2_PIX_FMT_RGB332  v4l2_fourcc('R', 'G', 'B', '1') /*  8  RGB-3-3-2     */
+#define V4L2_PIX_FMT_RGB444  v4l2_fourcc('R', '4', '4', '4') /* 16  xxxxrrrr ggggbbbb */
+#define V4L2_PIX_FMT_RGB555  v4l2_fourcc('R', 'G', 'B', 'O') /* 16  RGB-5-5-5     */
+#define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
+#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
+#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
+#define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
+#define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
+#define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
+#define V4L2_PIX_FMT_RGB32   v4l2_fourcc('R', 'G', 'B', '4') /* 32  RGB-8-8-8-8   */
+
+/* Grey formats */
+#define V4L2_PIX_FMT_GREY    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
+#define V4L2_PIX_FMT_Y10     v4l2_fourcc('Y', '1', '0', ' ') /* 10  Greyscale     */
+#define V4L2_PIX_FMT_Y16     v4l2_fourcc('Y', '1', '6', ' ') /* 16  Greyscale     */
+
+/* Palette formats */
+#define V4L2_PIX_FMT_PAL8    v4l2_fourcc('P', 'A', 'L', '8') /*  8  8-bit palette */
+
+/* Luminance+Chrominance formats */
+#define V4L2_PIX_FMT_YVU410  v4l2_fourcc('Y', 'V', 'U', '9') /*  9  YVU 4:1:0     */
+#define V4L2_PIX_FMT_YVU420  v4l2_fourcc('Y', 'V', '1', '2') /* 12  YVU 4:2:0     */
+#define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_YVYU    v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
+#define V4L2_PIX_FMT_UYVY    v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_VYUY    v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16  YVU422 planar */
+#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16  YVU411 planar */
+#define V4L2_PIX_FMT_Y41P    v4l2_fourcc('Y', '4', '1', 'P') /* 12  YUV 4:1:1     */
+#define V4L2_PIX_FMT_YUV444  v4l2_fourcc('Y', '4', '4', '4') /* 16  xxxxyyyy uuuuvvvv */
+#define V4L2_PIX_FMT_YUV555  v4l2_fourcc('Y', 'U', 'V', 'O') /* 16  YUV-5-5-5     */
+#define V4L2_PIX_FMT_YUV565  v4l2_fourcc('Y', 'U', 'V', 'P') /* 16  YUV-5-6-5     */
+#define V4L2_PIX_FMT_YUV32   v4l2_fourcc('Y', 'U', 'V', '4') /* 32  YUV-8-8-8-8   */
+#define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y', 'U', 'V', '9') /*  9  YUV 4:1:0     */
+#define V4L2_PIX_FMT_YUV420  v4l2_fourcc('Y', 'U', '1', '2') /* 12  YUV 4:2:0     */
+#define V4L2_PIX_FMT_HI240   v4l2_fourcc('H', 'I', '2', '4') /*  8  8-bit color   */
+#define V4L2_PIX_FMT_HM12    v4l2_fourcc('H', 'M', '1', '2') /*  8  YUV 4:2:0 16x16 macroblocks */
+
+/* two planes -- one Y, one Cr + Cb interleaved  */
+#define V4L2_PIX_FMT_NV12    v4l2_fourcc('N', 'V', '1', '2') /* 12  Y/CbCr 4:2:0  */
+#define V4L2_PIX_FMT_NV21    v4l2_fourcc('N', 'V', '2', '1') /* 12  Y/CrCb 4:2:0  */
+#define V4L2_PIX_FMT_NV16    v4l2_fourcc('N', 'V', '1', '6') /* 16  Y/CbCr 4:2:2  */
+#define V4L2_PIX_FMT_NV61    v4l2_fourcc('N', 'V', '6', '1') /* 16  Y/CrCb 4:2:2  */
+
+/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
+#define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */
+#define V4L2_PIX_FMT_SGBRG8  v4l2_fourcc('G', 'B', 'R', 'G') /*  8  GBGB.. RGRG.. */
+#define V4L2_PIX_FMT_SGRBG8  v4l2_fourcc('G', 'R', 'B', 'G') /*  8  GRGR.. BGBG.. */
+#define V4L2_PIX_FMT_SRGGB8  v4l2_fourcc('R', 'G', 'G', 'B') /*  8  RGRG.. GBGB.. */
+#define V4L2_PIX_FMT_SBGGR10 v4l2_fourcc('B', 'G', '1', '0') /* 10  BGBG.. GRGR.. */
+#define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10  GBGB.. RGRG.. */
+#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10  GRGR.. BGBG.. */
+#define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10  RGRG.. GBGB.. */
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+#define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0')
+	/*
+	 * 10bit raw bayer, expanded to 16 bits
+	 * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb...
+	 */
+#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16  BGBG.. GRGR.. */
+
+/* compressed formats */
+#define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */
+#define V4L2_PIX_FMT_JPEG     v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG     */
+#define V4L2_PIX_FMT_DV       v4l2_fourcc('d', 'v', 's', 'd') /* 1394          */
+#define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4    */
+#define V4L2_PIX_FMT_H264     v4l2_fourcc('H', '2', '6', '4') /* H264 */
+
+/*  Vendor-specific formats   */
+#define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
+#define V4L2_PIX_FMT_WNVA     v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
+#define V4L2_PIX_FMT_SN9C10X  v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */
+#define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */
+#define V4L2_PIX_FMT_PWC1     v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */
+#define V4L2_PIX_FMT_PWC2     v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */
+#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */
+#define V4L2_PIX_FMT_SPCA501  v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */
+#define V4L2_PIX_FMT_SPCA505  v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */
+#define V4L2_PIX_FMT_SPCA508  v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */
+#define V4L2_PIX_FMT_SPCA561  v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
+#define V4L2_PIX_FMT_PAC207   v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
+#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
+#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */
+#define V4L2_PIX_FMT_SQ905C   v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
+#define V4L2_PIX_FMT_PJPG     v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
+#define V4L2_PIX_FMT_OV511    v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
+#define V4L2_PIX_FMT_OV518    v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
+#define V4L2_PIX_FMT_STV0680  v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
+
+/*
+ *	F O R M A T   E N U M E R A T I O N
+ */
+struct v4l2_fmtdesc {
+	__u32		    index;             /* Format number      */
+	enum v4l2_buf_type  type;              /* buffer type        */
+	__u32               flags;
+	__u8		    description[32];   /* Description string */
+	__u32		    pixelformat;       /* Format fourcc      */
+	__u32		    reserved[4];
+};
+
+#define V4L2_FMT_FLAG_COMPRESSED 0x0001
+#define V4L2_FMT_FLAG_EMULATED   0x0002
+
+#if 1
+	/* Experimental Frame Size and frame rate enumeration */
+/*
+ *	F R A M E   S I Z E   E N U M E R A T I O N
+ */
+enum v4l2_frmsizetypes {
+	V4L2_FRMSIZE_TYPE_DISCRETE	= 1,
+	V4L2_FRMSIZE_TYPE_CONTINUOUS	= 2,
+	V4L2_FRMSIZE_TYPE_STEPWISE	= 3,
+};
+
+struct v4l2_frmsize_discrete {
+	__u32			width;		/* Frame width [pixel] */
+	__u32			height;		/* Frame height [pixel] */
+};
+
+struct v4l2_frmsize_stepwise {
+	__u32			min_width;	/* Minimum frame width [pixel] */
+	__u32			max_width;	/* Maximum frame width [pixel] */
+	__u32			step_width;	/* Frame width step size [pixel] */
+	__u32			min_height;	/* Minimum frame height [pixel] */
+	__u32			max_height;	/* Maximum frame height [pixel] */
+	__u32			step_height;	/* Frame height step size [pixel] */
+};
+
+struct v4l2_frmsizeenum {
+	__u32			index;		/* Frame size number */
+	__u32			pixel_format;	/* Pixel format */
+	__u32			type;		/* Frame size type the device supports. */
+
+	union {					/* Frame size */
+		struct v4l2_frmsize_discrete	discrete;
+		struct v4l2_frmsize_stepwise	stepwise;
+	} x;
+
+	__u32   reserved[2];			/* Reserved space for future use */
+};
+
+/*
+ *	F R A M E   R A T E   E N U M E R A T I O N
+ */
+enum v4l2_frmivaltypes {
+	V4L2_FRMIVAL_TYPE_DISCRETE	= 1,
+	V4L2_FRMIVAL_TYPE_CONTINUOUS	= 2,
+	V4L2_FRMIVAL_TYPE_STEPWISE	= 3,
+};
+
+struct v4l2_frmival_stepwise {
+	struct v4l2_fract	min;		/* Minimum frame interval [s] */
+	struct v4l2_fract	max;		/* Maximum frame interval [s] */
+	struct v4l2_fract	step;		/* Frame interval step size [s] */
+};
+
+struct v4l2_frmivalenum {
+	__u32			index;		/* Frame format index */
+	__u32			pixel_format;	/* Pixel format */
+	__u32			width;		/* Frame width */
+	__u32			height;		/* Frame height */
+	__u32			type;		/* Frame interval type the device supports. */
+
+	union {					/* Frame interval */
+		struct v4l2_fract		discrete;
+		struct v4l2_frmival_stepwise	stepwise;
+	} x;
+
+	__u32	reserved[2];			/* Reserved space for future use */
+};
+#endif
+
+/*
+ *	T I M E C O D E
+ */
+struct v4l2_timecode {
+	__u32	type;
+	__u32	flags;
+	__u8	frames;
+	__u8	seconds;
+	__u8	minutes;
+	__u8	hours;
+	__u8	userbits[4];
+};
+
+/*  Type  */
+#define V4L2_TC_TYPE_24FPS		1
+#define V4L2_TC_TYPE_25FPS		2
+#define V4L2_TC_TYPE_30FPS		3
+#define V4L2_TC_TYPE_50FPS		4
+#define V4L2_TC_TYPE_60FPS		5
+
+/*  Flags  */
+#define V4L2_TC_FLAG_DROPFRAME		0x0001 /* "drop-frame" mode */
+#define V4L2_TC_FLAG_COLORFRAME		0x0002
+#define V4L2_TC_USERBITS_field		0x000C
+#define V4L2_TC_USERBITS_USERDEFINED	0x0000
+#define V4L2_TC_USERBITS_8BITCHARS	0x0008
+/* The above is based on SMPTE timecodes */
+
+struct v4l2_jpegcompression {
+	int quality;
+
+	int  APPn;              /* Number of APP segment to be written,
+				 * must be 0..15 */
+	int  APP_len;           /* Length of data in JPEG APPn segment */
+	char APP_data[60];      /* Data in the JPEG APPn segment. */
+
+	int  COM_len;           /* Length of data in JPEG COM segment */
+	char COM_data[60];      /* Data in JPEG COM segment */
+
+	__u32 jpeg_markers;     /* Which markers should go into the JPEG
+				 * output. Unless you exactly know what
+				 * you do, leave them untouched.
+				 * Inluding less markers will make the
+				 * resulting code smaller, but there will
+				 * be fewer applications which can read it.
+				 * The presence of the APP and COM marker
+				 * is influenced by APP_len and COM_len
+				 * ONLY, not by this property! */
+
+#define V4L2_JPEG_MARKER_DHT (1<<3)    /* Define Huffman Tables */
+#define V4L2_JPEG_MARKER_DQT (1<<4)    /* Define Quantization Tables */
+#define V4L2_JPEG_MARKER_DRI (1<<5)    /* Define Restart Interval */
+#define V4L2_JPEG_MARKER_COM (1<<6)    /* Comment segment */
+#define V4L2_JPEG_MARKER_APP (1<<7)    /* App segment, driver will
+					* allways use APP0 */
+};
+
+/*
+ *	M E M O R Y - M A P P I N G   B U F F E R S
+ */
+struct v4l2_requestbuffers {
+	__u32			count;
+	enum v4l2_buf_type      type;
+	enum v4l2_memory        memory;
+	__u32			reserved[2];
+};
+
+struct v4l2_buffer {
+	__u32			index;
+	enum v4l2_buf_type      type;
+	__u32			bytesused;
+	__u32			flags;
+	enum v4l2_field		field;
+	struct timeval		timestamp;
+	struct v4l2_timecode	timecode;
+	__u32			sequence;
+
+	/* memory location */
+	enum v4l2_memory        memory;
+	union {
+		__u32           offset;
+		unsigned long   userptr;
+	} m;
+	__u32			length;
+	__u32			input;
+	__u32			reserved;
+};
+
+/*  Flags for 'flags' field */
+#define V4L2_BUF_FLAG_MAPPED	0x0001  /* Buffer is mapped (flag) */
+#define V4L2_BUF_FLAG_QUEUED	0x0002	/* Buffer is queued for processing */
+#define V4L2_BUF_FLAG_DONE	0x0004	/* Buffer is ready */
+#define V4L2_BUF_FLAG_KEYFRAME	0x0008	/* Image is a keyframe (I-frame) */
+#define V4L2_BUF_FLAG_PFRAME	0x0010	/* Image is a P-frame */
+#define V4L2_BUF_FLAG_BFRAME	0x0020	/* Image is a B-frame */
+#define V4L2_BUF_FLAG_TIMECODE	0x0100	/* timecode field is valid */
+#define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */
+
+/*
+ *	O V E R L A Y   P R E V I E W
+ */
+struct v4l2_framebuffer {
+	__u32			capability;
+	__u32			flags;
+/* FIXME: in theory we should pass something like PCI device + memory
+ * region + offset instead of some physical address */
+	void                    *base;
+	struct v4l2_pix_format	fmt;
+};
+/*  Flags for the 'capability' field. Read only */
+#define V4L2_FBUF_CAP_EXTERNOVERLAY	0x0001
+#define V4L2_FBUF_CAP_CHROMAKEY		0x0002
+#define V4L2_FBUF_CAP_LIST_CLIPPING     0x0004
+#define V4L2_FBUF_CAP_BITMAP_CLIPPING	0x0008
+#define V4L2_FBUF_CAP_LOCAL_ALPHA	0x0010
+#define V4L2_FBUF_CAP_GLOBAL_ALPHA	0x0020
+#define V4L2_FBUF_CAP_LOCAL_INV_ALPHA	0x0040
+#define V4L2_FBUF_CAP_SRC_CHROMAKEY	0x0080
+/*  Flags for the 'flags' field. */
+#define V4L2_FBUF_FLAG_PRIMARY		0x0001
+#define V4L2_FBUF_FLAG_OVERLAY		0x0002
+#define V4L2_FBUF_FLAG_CHROMAKEY	0x0004
+#define V4L2_FBUF_FLAG_LOCAL_ALPHA	0x0008
+#define V4L2_FBUF_FLAG_GLOBAL_ALPHA	0x0010
+#define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA	0x0020
+#define V4L2_FBUF_FLAG_SRC_CHROMAKEY	0x0040
+
+struct v4l2_clip {
+	struct v4l2_rect        c;
+	struct v4l2_clip	__user *next;
+};
+
+struct v4l2_window {
+	struct v4l2_rect        w;
+	enum v4l2_field  	field;
+	__u32			chromakey;
+	struct v4l2_clip	__user *clips;
+	__u32			clipcount;
+	void			__user *bitmap;
+	__u8                    global_alpha;
+};
+
+/*
+ *	C A P T U R E   P A R A M E T E R S
+ */
+struct v4l2_captureparm {
+	__u32		   capability;	  /*  Supported modes */
+	__u32		   capturemode;	  /*  Current mode */
+	struct v4l2_fract  timeperframe;  /*  Time per frame in .1us units */
+	__u32		   extendedmode;  /*  Driver-specific extensions */
+	__u32              readbuffers;   /*  # of buffers for read */
+	__u32		   reserved[4];
+};
+
+/*  Flags for 'capability' and 'capturemode' fields */
+#define V4L2_MODE_HIGHQUALITY	0x0001	/*  High quality imaging mode */
+#define V4L2_CAP_TIMEPERFRAME	0x1000	/*  timeperframe field is supported */
+
+struct v4l2_outputparm {
+	__u32		   capability;	 /*  Supported modes */
+	__u32		   outputmode;	 /*  Current mode */
+	struct v4l2_fract  timeperframe; /*  Time per frame in seconds */
+	__u32		   extendedmode; /*  Driver-specific extensions */
+	__u32              writebuffers; /*  # of buffers for write */
+	__u32		   reserved[4];
+};
+
+/*
+ *	I N P U T   I M A G E   C R O P P I N G
+ */
+struct v4l2_cropcap {
+	enum v4l2_buf_type      type;
+	struct v4l2_rect        bounds;
+	struct v4l2_rect        defrect;
+	struct v4l2_fract       pixelaspect;
+};
+
+struct v4l2_crop {
+	enum v4l2_buf_type      type;
+	struct v4l2_rect        c;
+};
+
+struct v4l2_selection {
+	__u32                   type;
+	__u32                   target;
+	__u32                   flags;
+	struct v4l2_rect        r;
+	__u32                   reserved[9];
+};
+
+
+/*
+ *      A N A L O G   V I D E O   S T A N D A R D
+ */
+
+typedef __u64 v4l2_std_id;
+
+/* one bit for each */
+#define V4L2_STD_PAL_B          ((v4l2_std_id)0x00000001)
+#define V4L2_STD_PAL_B1         ((v4l2_std_id)0x00000002)
+#define V4L2_STD_PAL_G          ((v4l2_std_id)0x00000004)
+#define V4L2_STD_PAL_H          ((v4l2_std_id)0x00000008)
+#define V4L2_STD_PAL_I          ((v4l2_std_id)0x00000010)
+#define V4L2_STD_PAL_D          ((v4l2_std_id)0x00000020)
+#define V4L2_STD_PAL_D1         ((v4l2_std_id)0x00000040)
+#define V4L2_STD_PAL_K          ((v4l2_std_id)0x00000080)
+
+#define V4L2_STD_PAL_M          ((v4l2_std_id)0x00000100)
+#define V4L2_STD_PAL_N          ((v4l2_std_id)0x00000200)
+#define V4L2_STD_PAL_Nc         ((v4l2_std_id)0x00000400)
+#define V4L2_STD_PAL_60         ((v4l2_std_id)0x00000800)
+
+#define V4L2_STD_NTSC_M         ((v4l2_std_id)0x00001000)
+#define V4L2_STD_NTSC_M_JP      ((v4l2_std_id)0x00002000)
+#define V4L2_STD_NTSC_443       ((v4l2_std_id)0x00004000)
+#define V4L2_STD_NTSC_M_KR      ((v4l2_std_id)0x00008000)
+
+#define V4L2_STD_SECAM_B        ((v4l2_std_id)0x00010000)
+#define V4L2_STD_SECAM_D        ((v4l2_std_id)0x00020000)
+#define V4L2_STD_SECAM_G        ((v4l2_std_id)0x00040000)
+#define V4L2_STD_SECAM_H        ((v4l2_std_id)0x00080000)
+#define V4L2_STD_SECAM_K        ((v4l2_std_id)0x00100000)
+#define V4L2_STD_SECAM_K1       ((v4l2_std_id)0x00200000)
+#define V4L2_STD_SECAM_L        ((v4l2_std_id)0x00400000)
+#define V4L2_STD_SECAM_LC       ((v4l2_std_id)0x00800000)
+
+/* ATSC/HDTV */
+#define V4L2_STD_ATSC_8_VSB     ((v4l2_std_id)0x01000000)
+#define V4L2_STD_ATSC_16_VSB    ((v4l2_std_id)0x02000000)
+
+/* FIXME:
+   Although std_id is 64 bits, there is an issue on PPC32 architecture that
+   makes switch(__u64) to break. So, there's a hack on v4l2-common.c rounding
+   this value to 32 bits.
+   As, currently, the max value is for V4L2_STD_ATSC_16_VSB (30 bits wide),
+   it should work fine. However, if needed to add more than two standards,
+   v4l2-common.c should be fixed.
+ */
+
+/* some merged standards */
+#define V4L2_STD_MN	(V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC)
+#define V4L2_STD_B	(V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B)
+#define V4L2_STD_GH	(V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)
+#define V4L2_STD_DK	(V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK)
+
+/* some common needed stuff */
+#define V4L2_STD_PAL_BG		(V4L2_STD_PAL_B		|\
+				 V4L2_STD_PAL_B1	|\
+				 V4L2_STD_PAL_G)
+#define V4L2_STD_PAL_DK		(V4L2_STD_PAL_D		|\
+				 V4L2_STD_PAL_D1	|\
+				 V4L2_STD_PAL_K)
+#define V4L2_STD_PAL		(V4L2_STD_PAL_BG	|\
+				 V4L2_STD_PAL_DK	|\
+				 V4L2_STD_PAL_H		|\
+				 V4L2_STD_PAL_I)
+#define V4L2_STD_NTSC           (V4L2_STD_NTSC_M	|\
+				 V4L2_STD_NTSC_M_JP     |\
+				 V4L2_STD_NTSC_M_KR)
+#define V4L2_STD_SECAM_DK      	(V4L2_STD_SECAM_D	|\
+				 V4L2_STD_SECAM_K	|\
+				 V4L2_STD_SECAM_K1)
+#define V4L2_STD_SECAM		(V4L2_STD_SECAM_B	|\
+				 V4L2_STD_SECAM_G	|\
+				 V4L2_STD_SECAM_H	|\
+				 V4L2_STD_SECAM_DK	|\
+				 V4L2_STD_SECAM_L       |\
+				 V4L2_STD_SECAM_LC)
+
+#define V4L2_STD_525_60		(V4L2_STD_PAL_M		|\
+				 V4L2_STD_PAL_60	|\
+				 V4L2_STD_NTSC		|\
+				 V4L2_STD_NTSC_443)
+#define V4L2_STD_625_50		(V4L2_STD_PAL		|\
+				 V4L2_STD_PAL_N		|\
+				 V4L2_STD_PAL_Nc	|\
+				 V4L2_STD_SECAM)
+#define V4L2_STD_ATSC           (V4L2_STD_ATSC_8_VSB    |\
+				 V4L2_STD_ATSC_16_VSB)
+
+#define V4L2_STD_UNKNOWN        0
+#define V4L2_STD_ALL            (V4L2_STD_525_60	|\
+				 V4L2_STD_625_50)
+
+struct v4l2_standard {
+	__u32		     index;
+	v4l2_std_id          id;
+	__u8		     name[24];
+	struct v4l2_fract    frameperiod; /* Frames, not fields */
+	__u32		     framelines;
+	__u32		     reserved[4];
+};
+
+/*
+ *	V I D E O	T I M I N G S	D V	P R E S E T
+ */
+struct v4l2_dv_preset {
+	__u32	preset;
+	__u32	reserved[4];
+};
+
+/*
+ *	D V	P R E S E T S	E N U M E R A T I O N
+ */
+struct v4l2_dv_enum_preset {
+	__u32	index;
+	__u32	preset;
+	__u8	name[32]; /* Name of the preset timing */
+	__u32	width;
+	__u32	height;
+	__u32	reserved[4];
+};
+
+/*
+ * 	D V	P R E S E T	V A L U E S
+ */
+#define		V4L2_DV_INVALID		0
+#define		V4L2_DV_480P59_94	1 /* BT.1362 */
+#define		V4L2_DV_576P50		2 /* BT.1362 */
+#define		V4L2_DV_720P24		3 /* SMPTE 296M */
+#define		V4L2_DV_720P25		4 /* SMPTE 296M */
+#define		V4L2_DV_720P30		5 /* SMPTE 296M */
+#define		V4L2_DV_720P50		6 /* SMPTE 296M */
+#define		V4L2_DV_720P59_94	7 /* SMPTE 274M */
+#define		V4L2_DV_720P60		8 /* SMPTE 274M/296M */
+#define		V4L2_DV_1080I29_97	9 /* BT.1120/ SMPTE 274M */
+#define		V4L2_DV_1080I30		10 /* BT.1120/ SMPTE 274M */
+#define		V4L2_DV_1080I25		11 /* BT.1120 */
+#define		V4L2_DV_1080I50		12 /* SMPTE 296M */
+#define		V4L2_DV_1080I60		13 /* SMPTE 296M */
+#define		V4L2_DV_1080P24		14 /* SMPTE 296M */
+#define		V4L2_DV_1080P25		15 /* SMPTE 296M */
+#define		V4L2_DV_1080P30		16 /* SMPTE 296M */
+#define		V4L2_DV_1080P50		17 /* BT.1120 */
+#define		V4L2_DV_1080P60		18 /* BT.1120 */
+
+/*
+ *	D V 	B T	T I M I N G S
+ */
+
+/* BT.656/BT.1120 timing data */
+struct v4l2_bt_timings {
+	__u32	width;		/* width in pixels */
+	__u32	height;		/* height in lines */
+	__u32	interlaced;	/* Interlaced or progressive */
+	__u32	polarities;	/* Positive or negative polarity */
+	__u64	pixelclock;	/* Pixel clock in HZ. Ex. 74.25MHz->74250000 */
+	__u32	hfrontporch;	/* Horizpontal front porch in pixels */
+	__u32	hsync;		/* Horizontal Sync length in pixels */
+	__u32	hbackporch;	/* Horizontal back porch in pixels */
+	__u32	vfrontporch;	/* Vertical front porch in pixels */
+	__u32	vsync;		/* Vertical Sync length in lines */
+	__u32	vbackporch;	/* Vertical back porch in lines */
+	__u32	il_vfrontporch;	/* Vertical front porch for bottom field of
+				 * interlaced field formats
+				 */
+	__u32	il_vsync;	/* Vertical sync length for bottom field of
+				 * interlaced field formats
+				 */
+	__u32	il_vbackporch;	/* Vertical back porch for bottom field of
+				 * interlaced field formats
+				 */
+	__u32	reserved[16];
+} __attribute__ ((packed));
+
+/* Interlaced or progressive format */
+#define	V4L2_DV_PROGRESSIVE	0
+#define	V4L2_DV_INTERLACED	1
+
+/* Polarities. If bit is not set, it is assumed to be negative polarity */
+#define V4L2_DV_VSYNC_POS_POL	0x00000001
+#define V4L2_DV_HSYNC_POS_POL	0x00000002
+
+
+/* DV timings */
+struct v4l2_dv_timings {
+	__u32 type;
+	union {
+		struct v4l2_bt_timings	bt;
+		__u32	reserved[32];
+	} x;
+} __attribute__ ((packed));
+
+/* Values for the type field */
+#define V4L2_DV_BT_656_1120	0	/* BT.656/1120 timing type */
+
+/*
+ *	V I D E O   I N P U T S
+ */
+struct v4l2_input {
+	__u32	     index;		/*  Which input */
+	__u8	     name[32];		/*  Label */
+	__u32	     type;		/*  Type of input */
+	__u32	     audioset;		/*  Associated audios (bitfield) */
+	__u32        tuner;             /*  Associated tuner */
+	v4l2_std_id  std;
+	__u32	     status;
+	__u32	     capabilities;
+	__u32	     reserved[3];
+};
+
+/*  Values for the 'type' field */
+#define V4L2_INPUT_TYPE_TUNER		1
+#define V4L2_INPUT_TYPE_CAMERA		2
+
+/* field 'status' - general */
+#define V4L2_IN_ST_NO_POWER    0x00000001  /* Attached device is off */
+#define V4L2_IN_ST_NO_SIGNAL   0x00000002
+#define V4L2_IN_ST_NO_COLOR    0x00000004
+
+/* field 'status' - sensor orientation */
+/* If sensor is mounted upside down set both bits */
+#define V4L2_IN_ST_HFLIP       0x00000010 /* Frames are flipped horizontally */
+#define V4L2_IN_ST_VFLIP       0x00000020 /* Frames are flipped vertically */
+
+/* field 'status' - analog */
+#define V4L2_IN_ST_NO_H_LOCK   0x00000100  /* No horizontal sync lock */
+#define V4L2_IN_ST_COLOR_KILL  0x00000200  /* Color killer is active */
+
+/* field 'status' - digital */
+#define V4L2_IN_ST_NO_SYNC     0x00010000  /* No synchronization lock */
+#define V4L2_IN_ST_NO_EQU      0x00020000  /* No equalizer lock */
+#define V4L2_IN_ST_NO_CARRIER  0x00040000  /* Carrier recovery failed */
+
+/* field 'status' - VCR and set-top box */
+#define V4L2_IN_ST_MACROVISION 0x01000000  /* Macrovision detected */
+#define V4L2_IN_ST_NO_ACCESS   0x02000000  /* Conditional access denied */
+#define V4L2_IN_ST_VTR         0x04000000  /* VTR time constant */
+
+/* capabilities flags */
+#define V4L2_IN_CAP_PRESETS		0x00000001 /* Supports S_DV_PRESET */
+#define V4L2_IN_CAP_CUSTOM_TIMINGS	0x00000002 /* Supports S_DV_TIMINGS */
+#define V4L2_IN_CAP_STD			0x00000004 /* Supports S_STD */
+
+/*
+ *	V I D E O   O U T P U T S
+ */
+struct v4l2_output {
+	__u32	     index;		/*  Which output */
+	__u8	     name[32];		/*  Label */
+	__u32	     type;		/*  Type of output */
+	__u32	     audioset;		/*  Associated audios (bitfield) */
+	__u32	     modulator;         /*  Associated modulator */
+	v4l2_std_id  std;
+	__u32	     capabilities;
+	__u32	     reserved[3];
+};
+/*  Values for the 'type' field */
+#define V4L2_OUTPUT_TYPE_MODULATOR		1
+#define V4L2_OUTPUT_TYPE_ANALOG			2
+#define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY	3
+
+/* capabilities flags */
+#define V4L2_OUT_CAP_PRESETS		0x00000001 /* Supports S_DV_PRESET */
+#define V4L2_OUT_CAP_CUSTOM_TIMINGS	0x00000002 /* Supports S_DV_TIMINGS */
+#define V4L2_OUT_CAP_STD		0x00000004 /* Supports S_STD */
+
+/*
+ *	C O N T R O L S
+ */
+struct v4l2_control {
+	__u32		     id;
+	__s32		     value;
+};
+
+struct v4l2_ext_control {
+	__u32 id;
+	__u32 size;
+	__u32 reserved2[1];
+	union {
+		__s32 value;
+		__s64 value64;
+		char *string;
+	} x;
+} __attribute__ ((packed));
+
+struct v4l2_ext_controls {
+	__u32 ctrl_class;
+	__u32 count;
+	__u32 error_idx;
+	__u32 reserved[2];
+	struct v4l2_ext_control *controls;
+};
+
+/*  Values for ctrl_class field */
+#define V4L2_CTRL_CLASS_USER 0x00980000	/* Old-style 'user' controls */
+#define V4L2_CTRL_CLASS_MPEG 0x00990000	/* MPEG-compression controls */
+#define V4L2_CTRL_CLASS_CAMERA 0x009a0000	/* Camera class controls */
+#define V4L2_CTRL_CLASS_FM_TX 0x009b0000	/* FM Modulator control class */
+
+#define V4L2_CTRL_ID_MASK      	  (0x0fffffff)
+#define V4L2_CTRL_ID2CLASS(id)    ((id) & 0x0fff0000UL)
+#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000)
+
+/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
+struct v4l2_queryctrl {
+	__u32		     id;
+	enum v4l2_ctrl_type  type;
+	__u8		     name[32];	/* Whatever */
+	__s32		     minimum;	/* Note signedness */
+	__s32		     maximum;
+	__s32		     step;
+	__s32		     default_value;
+	__u32                flags;
+	__u32		     reserved[2];
+};
+
+/*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
+struct v4l2_querymenu {
+	__u32		id;
+	__u32		index;
+	__u8		name[32];	/* Whatever */
+	__u32		reserved;
+};
+
+/*  Control flags  */
+#define V4L2_CTRL_FLAG_DISABLED		0x0001
+#define V4L2_CTRL_FLAG_GRABBED		0x0002
+#define V4L2_CTRL_FLAG_READ_ONLY 	0x0004
+#define V4L2_CTRL_FLAG_UPDATE 		0x0008
+#define V4L2_CTRL_FLAG_INACTIVE 	0x0010
+#define V4L2_CTRL_FLAG_SLIDER 		0x0020
+#define V4L2_CTRL_FLAG_WRITE_ONLY 	0x0040
+
+/*  Query flag, to be ORed with the control ID */
+#define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000
+
+/*  User-class control IDs defined by V4L2 */
+#define V4L2_CID_BASE			(V4L2_CTRL_CLASS_USER | 0x900)
+#define V4L2_CID_USER_BASE 		V4L2_CID_BASE
+/*  IDs reserved for driver specific controls */
+#define V4L2_CID_PRIVATE_BASE		0x08000000
+
+#define V4L2_CID_USER_CLASS 		(V4L2_CTRL_CLASS_USER | 1)
+#define V4L2_CID_BRIGHTNESS		(V4L2_CID_BASE+0)
+#define V4L2_CID_CONTRAST		(V4L2_CID_BASE+1)
+#define V4L2_CID_SATURATION		(V4L2_CID_BASE+2)
+#define V4L2_CID_HUE			(V4L2_CID_BASE+3)
+#define V4L2_CID_AUDIO_VOLUME		(V4L2_CID_BASE+5)
+#define V4L2_CID_AUDIO_BALANCE		(V4L2_CID_BASE+6)
+#define V4L2_CID_AUDIO_BASS		(V4L2_CID_BASE+7)
+#define V4L2_CID_AUDIO_TREBLE		(V4L2_CID_BASE+8)
+#define V4L2_CID_AUDIO_MUTE		(V4L2_CID_BASE+9)
+#define V4L2_CID_AUDIO_LOUDNESS		(V4L2_CID_BASE+10)
+#define V4L2_CID_BLACK_LEVEL		(V4L2_CID_BASE+11) /* Deprecated */
+#define V4L2_CID_AUTO_WHITE_BALANCE	(V4L2_CID_BASE+12)
+#define V4L2_CID_DO_WHITE_BALANCE	(V4L2_CID_BASE+13)
+#define V4L2_CID_RED_BALANCE		(V4L2_CID_BASE+14)
+#define V4L2_CID_BLUE_BALANCE		(V4L2_CID_BASE+15)
+#define V4L2_CID_GAMMA			(V4L2_CID_BASE+16)
+#define V4L2_CID_WHITENESS		(V4L2_CID_GAMMA) /* Deprecated */
+#define V4L2_CID_EXPOSURE		(V4L2_CID_BASE+17)
+#define V4L2_CID_AUTOGAIN		(V4L2_CID_BASE+18)
+#define V4L2_CID_GAIN			(V4L2_CID_BASE+19)
+#define V4L2_CID_HFLIP			(V4L2_CID_BASE+20)
+#define V4L2_CID_VFLIP			(V4L2_CID_BASE+21)
+
+/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */
+#define V4L2_CID_HCENTER		(V4L2_CID_BASE+22)
+#define V4L2_CID_VCENTER		(V4L2_CID_BASE+23)
+
+#define V4L2_CID_POWER_LINE_FREQUENCY	(V4L2_CID_BASE+24)
+enum v4l2_power_line_frequency {
+	V4L2_CID_POWER_LINE_FREQUENCY_DISABLED	= 0,
+	V4L2_CID_POWER_LINE_FREQUENCY_50HZ	= 1,
+	V4L2_CID_POWER_LINE_FREQUENCY_60HZ	= 2,
+};
+#define V4L2_CID_HUE_AUTO			(V4L2_CID_BASE+25)
+#define V4L2_CID_WHITE_BALANCE_TEMPERATURE	(V4L2_CID_BASE+26)
+#define V4L2_CID_SHARPNESS			(V4L2_CID_BASE+27)
+#define V4L2_CID_BACKLIGHT_COMPENSATION 	(V4L2_CID_BASE+28)
+#define V4L2_CID_CHROMA_AGC                     (V4L2_CID_BASE+29)
+#define V4L2_CID_COLOR_KILLER                   (V4L2_CID_BASE+30)
+#define V4L2_CID_COLORFX			(V4L2_CID_BASE+31)
+enum v4l2_colorfx {
+	V4L2_COLORFX_NONE	= 0,
+	V4L2_COLORFX_BW		= 1,
+	V4L2_COLORFX_SEPIA	= 2,
+};
+#define V4L2_CID_AUTOBRIGHTNESS			(V4L2_CID_BASE+32)
+#define V4L2_CID_BAND_STOP_FILTER		(V4L2_CID_BASE+33)
+
+#define V4L2_CID_ROTATE				(V4L2_CID_BASE+34)
+#define V4L2_CID_BG_COLOR			(V4L2_CID_BASE+35)
+/* last CID + 1 */
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+36)
+
+/*  MPEG-class control IDs defined by V4L2 */
+#define V4L2_CID_MPEG_BASE 			(V4L2_CTRL_CLASS_MPEG | 0x900)
+#define V4L2_CID_MPEG_CLASS 			(V4L2_CTRL_CLASS_MPEG | 1)
+
+/*  MPEG streams */
+#define V4L2_CID_MPEG_STREAM_TYPE 		(V4L2_CID_MPEG_BASE+0)
+enum v4l2_mpeg_stream_type {
+	V4L2_MPEG_STREAM_TYPE_MPEG2_PS   = 0, /* MPEG-2 program stream */
+	V4L2_MPEG_STREAM_TYPE_MPEG2_TS   = 1, /* MPEG-2 transport stream */
+	V4L2_MPEG_STREAM_TYPE_MPEG1_SS   = 2, /* MPEG-1 system stream */
+	V4L2_MPEG_STREAM_TYPE_MPEG2_DVD  = 3, /* MPEG-2 DVD-compatible stream */
+	V4L2_MPEG_STREAM_TYPE_MPEG1_VCD  = 4, /* MPEG-1 VCD-compatible stream */
+	V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */
+};
+#define V4L2_CID_MPEG_STREAM_PID_PMT 		(V4L2_CID_MPEG_BASE+1)
+#define V4L2_CID_MPEG_STREAM_PID_AUDIO 		(V4L2_CID_MPEG_BASE+2)
+#define V4L2_CID_MPEG_STREAM_PID_VIDEO 		(V4L2_CID_MPEG_BASE+3)
+#define V4L2_CID_MPEG_STREAM_PID_PCR 		(V4L2_CID_MPEG_BASE+4)
+#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO 	(V4L2_CID_MPEG_BASE+5)
+#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO 	(V4L2_CID_MPEG_BASE+6)
+#define V4L2_CID_MPEG_STREAM_VBI_FMT 		(V4L2_CID_MPEG_BASE+7)
+enum v4l2_mpeg_stream_vbi_fmt {
+	V4L2_MPEG_STREAM_VBI_FMT_NONE = 0,  /* No VBI in the MPEG stream */
+	V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1,  /* VBI in private packets, IVTV format */
+};
+
+/*  MPEG audio */
+#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ 	(V4L2_CID_MPEG_BASE+100)
+enum v4l2_mpeg_audio_sampling_freq {
+	V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0,
+	V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1,
+	V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2,
+};
+#define V4L2_CID_MPEG_AUDIO_ENCODING 		(V4L2_CID_MPEG_BASE+101)
+enum v4l2_mpeg_audio_encoding {
+	V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0,
+	V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1,
+	V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2,
+	V4L2_MPEG_AUDIO_ENCODING_AAC     = 3,
+	V4L2_MPEG_AUDIO_ENCODING_AC3     = 4,
+};
+#define V4L2_CID_MPEG_AUDIO_L1_BITRATE 		(V4L2_CID_MPEG_BASE+102)
+enum v4l2_mpeg_audio_l1_bitrate {
+	V4L2_MPEG_AUDIO_L1_BITRATE_32K  = 0,
+	V4L2_MPEG_AUDIO_L1_BITRATE_64K  = 1,
+	V4L2_MPEG_AUDIO_L1_BITRATE_96K  = 2,
+	V4L2_MPEG_AUDIO_L1_BITRATE_128K = 3,
+	V4L2_MPEG_AUDIO_L1_BITRATE_160K = 4,
+	V4L2_MPEG_AUDIO_L1_BITRATE_192K = 5,
+	V4L2_MPEG_AUDIO_L1_BITRATE_224K = 6,
+	V4L2_MPEG_AUDIO_L1_BITRATE_256K = 7,
+	V4L2_MPEG_AUDIO_L1_BITRATE_288K = 8,
+	V4L2_MPEG_AUDIO_L1_BITRATE_320K = 9,
+	V4L2_MPEG_AUDIO_L1_BITRATE_352K = 10,
+	V4L2_MPEG_AUDIO_L1_BITRATE_384K = 11,
+	V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12,
+	V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13,
+};
+#define V4L2_CID_MPEG_AUDIO_L2_BITRATE 		(V4L2_CID_MPEG_BASE+103)
+enum v4l2_mpeg_audio_l2_bitrate {
+	V4L2_MPEG_AUDIO_L2_BITRATE_32K  = 0,
+	V4L2_MPEG_AUDIO_L2_BITRATE_48K  = 1,
+	V4L2_MPEG_AUDIO_L2_BITRATE_56K  = 2,
+	V4L2_MPEG_AUDIO_L2_BITRATE_64K  = 3,
+	V4L2_MPEG_AUDIO_L2_BITRATE_80K  = 4,
+	V4L2_MPEG_AUDIO_L2_BITRATE_96K  = 5,
+	V4L2_MPEG_AUDIO_L2_BITRATE_112K = 6,
+	V4L2_MPEG_AUDIO_L2_BITRATE_128K = 7,
+	V4L2_MPEG_AUDIO_L2_BITRATE_160K = 8,
+	V4L2_MPEG_AUDIO_L2_BITRATE_192K = 9,
+	V4L2_MPEG_AUDIO_L2_BITRATE_224K = 10,
+	V4L2_MPEG_AUDIO_L2_BITRATE_256K = 11,
+	V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12,
+	V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13,
+};
+#define V4L2_CID_MPEG_AUDIO_L3_BITRATE 		(V4L2_CID_MPEG_BASE+104)
+enum v4l2_mpeg_audio_l3_bitrate {
+	V4L2_MPEG_AUDIO_L3_BITRATE_32K  = 0,
+	V4L2_MPEG_AUDIO_L3_BITRATE_40K  = 1,
+	V4L2_MPEG_AUDIO_L3_BITRATE_48K  = 2,
+	V4L2_MPEG_AUDIO_L3_BITRATE_56K  = 3,
+	V4L2_MPEG_AUDIO_L3_BITRATE_64K  = 4,
+	V4L2_MPEG_AUDIO_L3_BITRATE_80K  = 5,
+	V4L2_MPEG_AUDIO_L3_BITRATE_96K  = 6,
+	V4L2_MPEG_AUDIO_L3_BITRATE_112K = 7,
+	V4L2_MPEG_AUDIO_L3_BITRATE_128K = 8,
+	V4L2_MPEG_AUDIO_L3_BITRATE_160K = 9,
+	V4L2_MPEG_AUDIO_L3_BITRATE_192K = 10,
+	V4L2_MPEG_AUDIO_L3_BITRATE_224K = 11,
+	V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12,
+	V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13,
+};
+#define V4L2_CID_MPEG_AUDIO_MODE 		(V4L2_CID_MPEG_BASE+105)
+enum v4l2_mpeg_audio_mode {
+	V4L2_MPEG_AUDIO_MODE_STEREO       = 0,
+	V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1,
+	V4L2_MPEG_AUDIO_MODE_DUAL         = 2,
+	V4L2_MPEG_AUDIO_MODE_MONO         = 3,
+};
+#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION 	(V4L2_CID_MPEG_BASE+106)
+enum v4l2_mpeg_audio_mode_extension {
+	V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4  = 0,
+	V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8  = 1,
+	V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2,
+	V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3,
+};
+#define V4L2_CID_MPEG_AUDIO_EMPHASIS 		(V4L2_CID_MPEG_BASE+107)
+enum v4l2_mpeg_audio_emphasis {
+	V4L2_MPEG_AUDIO_EMPHASIS_NONE         = 0,
+	V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1,
+	V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17    = 2,
+};
+#define V4L2_CID_MPEG_AUDIO_CRC 		(V4L2_CID_MPEG_BASE+108)
+enum v4l2_mpeg_audio_crc {
+	V4L2_MPEG_AUDIO_CRC_NONE  = 0,
+	V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
+};
+#define V4L2_CID_MPEG_AUDIO_MUTE 		(V4L2_CID_MPEG_BASE+109)
+#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE		(V4L2_CID_MPEG_BASE+110)
+#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE		(V4L2_CID_MPEG_BASE+111)
+enum v4l2_mpeg_audio_ac3_bitrate {
+	V4L2_MPEG_AUDIO_AC3_BITRATE_32K  = 0,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_40K  = 1,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_48K  = 2,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_56K  = 3,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_64K  = 4,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_80K  = 5,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_96K  = 6,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
+	V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
+};
+
+/*  MPEG video */
+#define V4L2_CID_MPEG_VIDEO_ENCODING 		(V4L2_CID_MPEG_BASE+200)
+enum v4l2_mpeg_video_encoding {
+	V4L2_MPEG_VIDEO_ENCODING_MPEG_1     = 0,
+	V4L2_MPEG_VIDEO_ENCODING_MPEG_2     = 1,
+	V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2,
+};
+#define V4L2_CID_MPEG_VIDEO_ASPECT 		(V4L2_CID_MPEG_BASE+201)
+enum v4l2_mpeg_video_aspect {
+	V4L2_MPEG_VIDEO_ASPECT_1x1     = 0,
+	V4L2_MPEG_VIDEO_ASPECT_4x3     = 1,
+	V4L2_MPEG_VIDEO_ASPECT_16x9    = 2,
+	V4L2_MPEG_VIDEO_ASPECT_221x100 = 3,
+};
+#define V4L2_CID_MPEG_VIDEO_B_FRAMES 		(V4L2_CID_MPEG_BASE+202)
+#define V4L2_CID_MPEG_VIDEO_GOP_SIZE 		(V4L2_CID_MPEG_BASE+203)
+#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE 	(V4L2_CID_MPEG_BASE+204)
+#define V4L2_CID_MPEG_VIDEO_PULLDOWN 		(V4L2_CID_MPEG_BASE+205)
+#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE 	(V4L2_CID_MPEG_BASE+206)
+enum v4l2_mpeg_video_bitrate_mode {
+	V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0,
+	V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1,
+};
+#define V4L2_CID_MPEG_VIDEO_BITRATE 		(V4L2_CID_MPEG_BASE+207)
+#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK 	(V4L2_CID_MPEG_BASE+208)
+#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
+#define V4L2_CID_MPEG_VIDEO_MUTE 		(V4L2_CID_MPEG_BASE+210)
+#define V4L2_CID_MPEG_VIDEO_MUTE_YUV 		(V4L2_CID_MPEG_BASE+211)
+
+/*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
+#define V4L2_CID_MPEG_CX2341X_BASE 				(V4L2_CTRL_CLASS_MPEG | 0x1000)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE 	(V4L2_CID_MPEG_CX2341X_BASE+0)
+enum v4l2_mpeg_cx2341x_video_spatial_filter_mode {
+	V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0,
+	V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO   = 1,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER 		(V4L2_CID_MPEG_CX2341X_BASE+1)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE 	(V4L2_CID_MPEG_CX2341X_BASE+2)
+enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type {
+	V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF                  = 0,
+	V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR               = 1,
+	V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT              = 2,
+	V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE      = 3,
+	V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE 	(V4L2_CID_MPEG_CX2341X_BASE+3)
+enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type {
+	V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF    = 0,
+	V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE 	(V4L2_CID_MPEG_CX2341X_BASE+4)
+enum v4l2_mpeg_cx2341x_video_temporal_filter_mode {
+	V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0,
+	V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO   = 1,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER 		(V4L2_CID_MPEG_CX2341X_BASE+5)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE 		(V4L2_CID_MPEG_CX2341X_BASE+6)
+enum v4l2_mpeg_cx2341x_video_median_filter_type {
+	V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF      = 0,
+	V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR      = 1,
+	V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT     = 2,
+	V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3,
+	V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG     = 4,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM 	(V4L2_CID_MPEG_CX2341X_BASE+7)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP 	(V4L2_CID_MPEG_CX2341X_BASE+8)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM	(V4L2_CID_MPEG_CX2341X_BASE+9)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP 	(V4L2_CID_MPEG_CX2341X_BASE+10)
+#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS 	(V4L2_CID_MPEG_CX2341X_BASE+11)
+
+/*  Camera class control IDs */
+#define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)
+#define V4L2_CID_CAMERA_CLASS 		(V4L2_CTRL_CLASS_CAMERA | 1)
+
+#define V4L2_CID_EXPOSURE_AUTO			(V4L2_CID_CAMERA_CLASS_BASE+1)
+enum  v4l2_exposure_auto_type {
+	V4L2_EXPOSURE_AUTO = 0,
+	V4L2_EXPOSURE_MANUAL = 1,
+	V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
+	V4L2_EXPOSURE_APERTURE_PRIORITY = 3
+};
+#define V4L2_CID_EXPOSURE_ABSOLUTE		(V4L2_CID_CAMERA_CLASS_BASE+2)
+#define V4L2_CID_EXPOSURE_AUTO_PRIORITY		(V4L2_CID_CAMERA_CLASS_BASE+3)
+
+#define V4L2_CID_PAN_RELATIVE			(V4L2_CID_CAMERA_CLASS_BASE+4)
+#define V4L2_CID_TILT_RELATIVE			(V4L2_CID_CAMERA_CLASS_BASE+5)
+#define V4L2_CID_PAN_RESET			(V4L2_CID_CAMERA_CLASS_BASE+6)
+#define V4L2_CID_TILT_RESET			(V4L2_CID_CAMERA_CLASS_BASE+7)
+
+#define V4L2_CID_PAN_ABSOLUTE			(V4L2_CID_CAMERA_CLASS_BASE+8)
+#define V4L2_CID_TILT_ABSOLUTE			(V4L2_CID_CAMERA_CLASS_BASE+9)
+
+#define V4L2_CID_FOCUS_ABSOLUTE			(V4L2_CID_CAMERA_CLASS_BASE+10)
+#define V4L2_CID_FOCUS_RELATIVE			(V4L2_CID_CAMERA_CLASS_BASE+11)
+#define V4L2_CID_FOCUS_AUTO			(V4L2_CID_CAMERA_CLASS_BASE+12)
+
+#define V4L2_CID_ZOOM_ABSOLUTE			(V4L2_CID_CAMERA_CLASS_BASE+13)
+#define V4L2_CID_ZOOM_RELATIVE			(V4L2_CID_CAMERA_CLASS_BASE+14)
+#define V4L2_CID_ZOOM_CONTINUOUS		(V4L2_CID_CAMERA_CLASS_BASE+15)
+
+#define V4L2_CID_PRIVACY			(V4L2_CID_CAMERA_CLASS_BASE+16)
+
+#define V4L2_CID_IRIS_ABSOLUTE			(V4L2_CID_CAMERA_CLASS_BASE+17)
+#define V4L2_CID_IRIS_RELATIVE			(V4L2_CID_CAMERA_CLASS_BASE+18)
+#define V4L2_CID_AUTO_EXPOSURE_BIAS		(V4L2_CID_CAMERA_CLASS_BASE+19)
+
+#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE	(V4L2_CID_CAMERA_CLASS_BASE+20)
+enum v4l2_auto_n_preset_white_balance {
+	V4L2_WHITE_BALANCE_MANUAL		= 0,
+	V4L2_WHITE_BALANCE_AUTO			= 1,
+	V4L2_WHITE_BALANCE_INCANDESCENT		= 2,
+	V4L2_WHITE_BALANCE_FLUORESCENT		= 3,
+	V4L2_WHITE_BALANCE_FLUORESCENT_H	= 4,
+	V4L2_WHITE_BALANCE_HORIZON		= 5,
+	V4L2_WHITE_BALANCE_DAYLIGHT		= 6,
+	V4L2_WHITE_BALANCE_FLASH		= 7,
+	V4L2_WHITE_BALANCE_CLOUDY		= 8,
+	V4L2_WHITE_BALANCE_SHADE		= 9,
+};
+#define V4L2_CID_WIDE_DYNAMIC_RANGE		(V4L2_CID_CAMERA_CLASS_BASE+21)
+#define V4L2_CID_IMAGE_STABILIZATION		(V4L2_CID_CAMERA_CLASS_BASE+22)
+
+#define V4L2_CID_ISO_SENSITIVITY		(V4L2_CID_CAMERA_CLASS_BASE+23)
+#define V4L2_CID_ISO_SENSITIVITY_AUTO		(V4L2_CID_CAMERA_CLASS_BASE+24)
+enum v4l2_iso_sensitivity_auto_type {
+	V4L2_ISO_SENSITIVITY_MANUAL		= 0,
+	V4L2_ISO_SENSITIVITY_AUTO		= 1,
+};
+
+#define V4L2_CID_EXPOSURE_METERING		(V4L2_CID_CAMERA_CLASS_BASE+25)
+enum v4l2_exposure_metering {
+	V4L2_EXPOSURE_METERING_AVERAGE		= 0,
+	V4L2_EXPOSURE_METERING_CENTER_WEIGHTED	= 1,
+	V4L2_EXPOSURE_METERING_SPOT		= 2,
+	V4L2_EXPOSURE_METERING_MATRIX		= 3,
+};
+
+#define V4L2_CID_SCENE_MODE			(V4L2_CID_CAMERA_CLASS_BASE+26)
+enum v4l2_scene_mode {
+	V4L2_SCENE_MODE_NONE			= 0,
+	V4L2_SCENE_MODE_BACKLIGHT		= 1,
+	V4L2_SCENE_MODE_BEACH_SNOW		= 2,
+	V4L2_SCENE_MODE_CANDLE_LIGHT		= 3,
+	V4L2_SCENE_MODE_DAWN_DUSK		= 4,
+	V4L2_SCENE_MODE_FALL_COLORS		= 5,
+	V4L2_SCENE_MODE_FIREWORKS		= 6,
+	V4L2_SCENE_MODE_LANDSCAPE		= 7,
+	V4L2_SCENE_MODE_NIGHT			= 8,
+	V4L2_SCENE_MODE_PARTY_INDOOR		= 9,
+	V4L2_SCENE_MODE_PORTRAIT		= 10,
+	V4L2_SCENE_MODE_SPORTS			= 11,
+	V4L2_SCENE_MODE_SUNSET			= 12,
+	V4L2_SCENE_MODE_TEXT			= 13,
+};
+
+#define V4L2_CID_3A_LOCK			(V4L2_CID_CAMERA_CLASS_BASE+27)
+#define V4L2_LOCK_EXPOSURE			(1 << 0)
+#define V4L2_LOCK_WHITE_BALANCE			(1 << 1)
+#define V4L2_LOCK_FOCUS				(1 << 2)
+
+#define V4L2_CID_AUTO_FOCUS_START		(V4L2_CID_CAMERA_CLASS_BASE+28)
+#define V4L2_CID_AUTO_FOCUS_STOP		(V4L2_CID_CAMERA_CLASS_BASE+29)
+#define V4L2_CID_AUTO_FOCUS_STATUS		(V4L2_CID_CAMERA_CLASS_BASE+30)
+#define V4L2_AUTO_FOCUS_STATUS_IDLE		(0 << 0)
+#define V4L2_AUTO_FOCUS_STATUS_BUSY		(1 << 0)
+#define V4L2_AUTO_FOCUS_STATUS_REACHED		(1 << 1)
+#define V4L2_AUTO_FOCUS_STATUS_FAILED		(1 << 2)
+
+#define V4L2_CID_AUTO_FOCUS_RANGE		(V4L2_CID_CAMERA_CLASS_BASE+31)
+enum v4l2_auto_focus_range {
+	V4L2_AUTO_FOCUS_RANGE_AUTO		= 0,
+	V4L2_AUTO_FOCUS_RANGE_NORMAL		= 1,
+	V4L2_AUTO_FOCUS_RANGE_MACRO		= 2,
+	V4L2_AUTO_FOCUS_RANGE_INFINITY		= 3,
+};
+
+#define V4L2_CID_PAN_SPEED			(V4L2_CID_CAMERA_CLASS_BASE+32)
+#define V4L2_CID_TILT_SPEED			(V4L2_CID_CAMERA_CLASS_BASE+33)
+
+/* FM Modulator class control IDs */
+#define V4L2_CID_FM_TX_CLASS_BASE		(V4L2_CTRL_CLASS_FM_TX | 0x900)
+#define V4L2_CID_FM_TX_CLASS			(V4L2_CTRL_CLASS_FM_TX | 1)
+
+#define V4L2_CID_RDS_TX_DEVIATION		(V4L2_CID_FM_TX_CLASS_BASE + 1)
+#define V4L2_CID_RDS_TX_PI			(V4L2_CID_FM_TX_CLASS_BASE + 2)
+#define V4L2_CID_RDS_TX_PTY			(V4L2_CID_FM_TX_CLASS_BASE + 3)
+#define V4L2_CID_RDS_TX_PS_NAME			(V4L2_CID_FM_TX_CLASS_BASE + 5)
+#define V4L2_CID_RDS_TX_RADIO_TEXT		(V4L2_CID_FM_TX_CLASS_BASE + 6)
+
+#define V4L2_CID_AUDIO_LIMITER_ENABLED		(V4L2_CID_FM_TX_CLASS_BASE + 64)
+#define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME	(V4L2_CID_FM_TX_CLASS_BASE + 65)
+#define V4L2_CID_AUDIO_LIMITER_DEVIATION	(V4L2_CID_FM_TX_CLASS_BASE + 66)
+
+#define V4L2_CID_AUDIO_COMPRESSION_ENABLED	(V4L2_CID_FM_TX_CLASS_BASE + 80)
+#define V4L2_CID_AUDIO_COMPRESSION_GAIN		(V4L2_CID_FM_TX_CLASS_BASE + 81)
+#define V4L2_CID_AUDIO_COMPRESSION_THRESHOLD	(V4L2_CID_FM_TX_CLASS_BASE + 82)
+#define V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME	(V4L2_CID_FM_TX_CLASS_BASE + 83)
+#define V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME	(V4L2_CID_FM_TX_CLASS_BASE + 84)
+
+#define V4L2_CID_PILOT_TONE_ENABLED		(V4L2_CID_FM_TX_CLASS_BASE + 96)
+#define V4L2_CID_PILOT_TONE_DEVIATION		(V4L2_CID_FM_TX_CLASS_BASE + 97)
+#define V4L2_CID_PILOT_TONE_FREQUENCY		(V4L2_CID_FM_TX_CLASS_BASE + 98)
+
+#define V4L2_CID_TUNE_PREEMPHASIS		(V4L2_CID_FM_TX_CLASS_BASE + 112)
+enum v4l2_preemphasis {
+	V4L2_PREEMPHASIS_DISABLED	= 0,
+	V4L2_PREEMPHASIS_50_uS		= 1,
+	V4L2_PREEMPHASIS_75_uS		= 2,
+};
+#define V4L2_CID_TUNE_POWER_LEVEL		(V4L2_CID_FM_TX_CLASS_BASE + 113)
+#define V4L2_CID_TUNE_ANTENNA_CAPACITOR		(V4L2_CID_FM_TX_CLASS_BASE + 114)
+
+/*
+ *	T U N I N G
+ */
+struct v4l2_tuner {
+	__u32                   index;
+	__u8			name[32];
+	enum v4l2_tuner_type    type;
+	__u32			capability;
+	__u32			rangelow;
+	__u32			rangehigh;
+	__u32			rxsubchans;
+	__u32			audmode;
+	__s32			signal;
+	__s32			afc;
+	__u32			reserved[4];
+};
+
+struct v4l2_modulator {
+	__u32			index;
+	__u8			name[32];
+	__u32			capability;
+	__u32			rangelow;
+	__u32			rangehigh;
+	__u32			txsubchans;
+	__u32			reserved[4];
+};
+
+/*  Flags for the 'capability' field */
+#define V4L2_TUNER_CAP_LOW		0x0001
+#define V4L2_TUNER_CAP_NORM		0x0002
+#define V4L2_TUNER_CAP_STEREO		0x0010
+#define V4L2_TUNER_CAP_LANG2		0x0020
+#define V4L2_TUNER_CAP_SAP		0x0020
+#define V4L2_TUNER_CAP_LANG1		0x0040
+#define V4L2_TUNER_CAP_RDS		0x0080
+
+/*  Flags for the 'rxsubchans' field */
+#define V4L2_TUNER_SUB_MONO		0x0001
+#define V4L2_TUNER_SUB_STEREO		0x0002
+#define V4L2_TUNER_SUB_LANG2		0x0004
+#define V4L2_TUNER_SUB_SAP		0x0004
+#define V4L2_TUNER_SUB_LANG1		0x0008
+#define V4L2_TUNER_SUB_RDS		0x0010
+
+/*  Values for the 'audmode' field */
+#define V4L2_TUNER_MODE_MONO		0x0000
+#define V4L2_TUNER_MODE_STEREO		0x0001
+#define V4L2_TUNER_MODE_LANG2		0x0002
+#define V4L2_TUNER_MODE_SAP		0x0002
+#define V4L2_TUNER_MODE_LANG1		0x0003
+#define V4L2_TUNER_MODE_LANG1_LANG2	0x0004
+
+struct v4l2_frequency {
+	__u32		      tuner;
+	enum v4l2_tuner_type  type;
+	__u32		      frequency;
+	__u32		      reserved[8];
+};
+
+struct v4l2_hw_freq_seek {
+	__u32		      tuner;
+	enum v4l2_tuner_type  type;
+	__u32		      seek_upward;
+	__u32		      wrap_around;
+	__u32		      reserved[8];
+};
+
+/*
+ *	R D S
+ */
+
+struct v4l2_rds_data {
+	__u8 	lsb;
+	__u8 	msb;
+	__u8 	block;
+} __attribute__ ((packed));
+
+#define V4L2_RDS_BLOCK_MSK 	 0x7
+#define V4L2_RDS_BLOCK_A 	 0
+#define V4L2_RDS_BLOCK_B 	 1
+#define V4L2_RDS_BLOCK_C 	 2
+#define V4L2_RDS_BLOCK_D 	 3
+#define V4L2_RDS_BLOCK_C_ALT 	 4
+#define V4L2_RDS_BLOCK_INVALID 	 7
+
+#define V4L2_RDS_BLOCK_CORRECTED 0x40
+#define V4L2_RDS_BLOCK_ERROR 	 0x80
+
+/*
+ *	A U D I O
+ */
+struct v4l2_audio {
+	__u32	index;
+	__u8	name[32];
+	__u32	capability;
+	__u32	mode;
+	__u32	reserved[2];
+};
+
+/*  Flags for the 'capability' field */
+#define V4L2_AUDCAP_STEREO		0x00001
+#define V4L2_AUDCAP_AVL			0x00002
+
+/*  Flags for the 'mode' field */
+#define V4L2_AUDMODE_AVL		0x00001
+
+struct v4l2_audioout {
+	__u32	index;
+	__u8	name[32];
+	__u32	capability;
+	__u32	mode;
+	__u32	reserved[2];
+};
+
+/*
+ *	M P E G   S E R V I C E S
+ *
+ *	NOTE: EXPERIMENTAL API
+ */
+#if 1
+#define V4L2_ENC_IDX_FRAME_I    (0)
+#define V4L2_ENC_IDX_FRAME_P    (1)
+#define V4L2_ENC_IDX_FRAME_B    (2)
+#define V4L2_ENC_IDX_FRAME_MASK (0xf)
+
+struct v4l2_enc_idx_entry {
+	__u64 offset;
+	__u64 pts;
+	__u32 length;
+	__u32 flags;
+	__u32 reserved[2];
+};
+
+#define V4L2_ENC_IDX_ENTRIES (64)
+struct v4l2_enc_idx {
+	__u32 entries;
+	__u32 entries_cap;
+	__u32 reserved[4];
+	struct v4l2_enc_idx_entry entry[V4L2_ENC_IDX_ENTRIES];
+};
+
+
+#define V4L2_ENC_CMD_START      (0)
+#define V4L2_ENC_CMD_STOP       (1)
+#define V4L2_ENC_CMD_PAUSE      (2)
+#define V4L2_ENC_CMD_RESUME     (3)
+
+/* Flags for V4L2_ENC_CMD_STOP */
+#define V4L2_ENC_CMD_STOP_AT_GOP_END    (1 << 0)
+
+struct v4l2_encoder_cmd {
+	__u32 cmd;
+	__u32 flags;
+	union {
+		struct {
+			__u32 data[8];
+		} raw;
+	} x;
+};
+
+#endif
+
+
+/*
+ *	D A T A   S E R V I C E S   ( V B I )
+ *
+ *	Data services API by Michael Schimek
+ */
+
+/* Raw VBI */
+struct v4l2_vbi_format {
+	__u32	sampling_rate;		/* in 1 Hz */
+	__u32	offset;
+	__u32	samples_per_line;
+	__u32	sample_format;		/* V4L2_PIX_FMT_* */
+	__s32	start[2];
+	__u32	count[2];
+	__u32	flags;			/* V4L2_VBI_* */
+	__u32	reserved[2];		/* must be zero */
+};
+
+/*  VBI flags  */
+#define V4L2_VBI_UNSYNC		(1 << 0)
+#define V4L2_VBI_INTERLACED	(1 << 1)
+
+/* Sliced VBI
+ *
+ *    This implements is a proposal V4L2 API to allow SLICED VBI
+ * required for some hardware encoders. It should change without
+ * notice in the definitive implementation.
+ */
+
+struct v4l2_sliced_vbi_format {
+	__u16   service_set;
+	/* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
+	   service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
+				 (equals frame lines 313-336 for 625 line video
+				  standards, 263-286 for 525 line standards) */
+	__u16   service_lines[2][24];
+	__u32   io_size;
+	__u32   reserved[2];            /* must be zero */
+};
+
+/* Teletext World System Teletext
+   (WST), defined on ITU-R BT.653-2 */
+#define V4L2_SLICED_TELETEXT_B          (0x0001)
+/* Video Program System, defined on ETS 300 231*/
+#define V4L2_SLICED_VPS                 (0x0400)
+/* Closed Caption, defined on EIA-608 */
+#define V4L2_SLICED_CAPTION_525         (0x1000)
+/* Wide Screen System, defined on ITU-R BT1119.1 */
+#define V4L2_SLICED_WSS_625             (0x4000)
+
+#define V4L2_SLICED_VBI_525             (V4L2_SLICED_CAPTION_525)
+#define V4L2_SLICED_VBI_625             (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625)
+
+struct v4l2_sliced_vbi_cap {
+	__u16   service_set;
+	/* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
+	   service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
+				 (equals frame lines 313-336 for 625 line video
+				  standards, 263-286 for 525 line standards) */
+	__u16   service_lines[2][24];
+	enum v4l2_buf_type type;
+	__u32   reserved[3];    /* must be 0 */
+};
+
+struct v4l2_sliced_vbi_data {
+	__u32   id;
+	__u32   field;          /* 0: first field, 1: second field */
+	__u32   line;           /* 1-23 */
+	__u32   reserved;       /* must be 0 */
+	__u8    data[48];
+};
+
+/*
+ * Sliced VBI data inserted into MPEG Streams
+ */
+
+/*
+ * V4L2_MPEG_STREAM_VBI_FMT_IVTV:
+ *
+ * Structure of payload contained in an MPEG 2 Private Stream 1 PES Packet in an
+ * MPEG-2 Program Pack that contains V4L2_MPEG_STREAM_VBI_FMT_IVTV Sliced VBI
+ * data
+ *
+ * Note, the MPEG-2 Program Pack and Private Stream 1 PES packet header
+ * definitions are not included here.  See the MPEG-2 specifications for details
+ * on these headers.
+ */
+
+/* Line type IDs */
+#define V4L2_MPEG_VBI_IVTV_TELETEXT_B     (1)
+#define V4L2_MPEG_VBI_IVTV_CAPTION_525    (4)
+#define V4L2_MPEG_VBI_IVTV_WSS_625        (5)
+#define V4L2_MPEG_VBI_IVTV_VPS            (7)
+
+struct v4l2_mpeg_vbi_itv0_line {
+	__u8 id;	/* One of V4L2_MPEG_VBI_IVTV_* above */
+	__u8 data[42];	/* Sliced VBI data for the line */
+} __attribute__ ((packed));
+
+struct v4l2_mpeg_vbi_itv0 {
+	__le32 linemask[2]; /* Bitmasks of VBI service lines present */
+	struct v4l2_mpeg_vbi_itv0_line line[35];
+} __attribute__ ((packed));
+
+struct v4l2_mpeg_vbi_ITV0 {
+	struct v4l2_mpeg_vbi_itv0_line line[36];
+} __attribute__ ((packed));
+
+#define V4L2_MPEG_VBI_IVTV_MAGIC0	"itv0"
+#define V4L2_MPEG_VBI_IVTV_MAGIC1	"ITV0"
+
+struct v4l2_mpeg_vbi_fmt_ivtv {
+	__u8 magic[4];
+	union {
+		struct v4l2_mpeg_vbi_itv0 itv0;
+		struct v4l2_mpeg_vbi_ITV0 ITV0;
+	} x;
+} __attribute__ ((packed));
+
+/*
+ *	A G G R E G A T E   S T R U C T U R E S
+ */
+
+/*	Stream data format
+ */
+struct v4l2_format {
+	enum v4l2_buf_type type;
+	union {
+		struct v4l2_pix_format		pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
+		struct v4l2_window		win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
+		struct v4l2_vbi_format		vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
+		struct v4l2_sliced_vbi_format	sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
+		__u8	raw_data[200];                   /* user-defined */
+	} fmt;
+};
+
+
+/*	Stream type-dependent parameters
+ */
+struct v4l2_streamparm {
+	enum v4l2_buf_type type;
+	union {
+		struct v4l2_captureparm	capture;
+		struct v4l2_outputparm	output;
+		__u8	raw_data[200];  /* user-defined */
+	} parm;
+};
+
+/*
+ *	A D V A N C E D   D E B U G G I N G
+ *
+ *	NOTE: EXPERIMENTAL API, NEVER RELY ON THIS IN APPLICATIONS!
+ *	FOR DEBUGGING, TESTING AND INTERNAL USE ONLY!
+ */
+
+/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */
+
+#define V4L2_CHIP_MATCH_HOST       0  /* Match against chip ID on host (0 for the host) */
+#define V4L2_CHIP_MATCH_I2C_DRIVER 1  /* Match against I2C driver name */
+#define V4L2_CHIP_MATCH_I2C_ADDR   2  /* Match against I2C 7-bit address */
+#define V4L2_CHIP_MATCH_AC97       3  /* Match against anciliary AC97 chip */
+
+struct v4l2_dbg_match {
+	__u32 type; /* Match type */
+	union {     /* Match this chip, meaning determined by type */
+		__u32 addr;
+		char name[32];
+	} x;
+} __attribute__ ((packed));
+
+struct v4l2_dbg_register {
+	struct v4l2_dbg_match match;
+	__u32 size;	/* register size in bytes */
+	__u64 reg;
+	__u64 val;
+} __attribute__ ((packed));
+
+/* VIDIOC_DBG_G_CHIP_IDENT */
+struct v4l2_dbg_chip_ident {
+	struct v4l2_dbg_match match;
+	__u32 ident;       /* chip identifier as specified in <media/v4l2-chip-ident.h> */
+	__u32 revision;    /* chip revision, chip specific */
+} __attribute__ ((packed));
+
+struct v4l2_exportbuffer {
+	__u32 type;
+	__u32 index;
+	__u32 plane;
+	__u32 flags;
+	__s32 fd;
+	__u32 reserved[11];
+} __attribute__ ((packed));
+
+/*
+ *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
+ *
+ */
+#define VIDIOC_QUERYCAP		 _IOR('V',  0, struct v4l2_capability)
+#define VIDIOC_RESERVED		  _IO('V',  1)
+#define VIDIOC_ENUM_FMT         _IOWR('V',  2, struct v4l2_fmtdesc)
+#define VIDIOC_G_FMT		_IOWR('V',  4, struct v4l2_format)
+#define VIDIOC_S_FMT		_IOWR('V',  5, struct v4l2_format)
+#define VIDIOC_REQBUFS		_IOWR('V',  8, struct v4l2_requestbuffers)
+#define VIDIOC_QUERYBUF		_IOWR('V',  9, struct v4l2_buffer)
+#define VIDIOC_G_FBUF		 _IOR('V', 10, struct v4l2_framebuffer)
+#define VIDIOC_S_FBUF		 _IOW('V', 11, struct v4l2_framebuffer)
+#define VIDIOC_OVERLAY		 _IOW('V', 14, int)
+#define VIDIOC_QBUF		_IOWR('V', 15, struct v4l2_buffer)
+#define VIDIOC_EXPBUF		_IOWR('V', 16, struct v4l2_exportbuffer)
+#define VIDIOC_DQBUF		_IOWR('V', 17, struct v4l2_buffer)
+#define VIDIOC_STREAMON		 _IOW('V', 18, int)
+#define VIDIOC_STREAMOFF	 _IOW('V', 19, int)
+#define VIDIOC_G_PARM		_IOWR('V', 21, struct v4l2_streamparm)
+#define VIDIOC_S_PARM		_IOWR('V', 22, struct v4l2_streamparm)
+#define VIDIOC_G_STD		 _IOR('V', 23, v4l2_std_id)
+#define VIDIOC_S_STD		 _IOW('V', 24, v4l2_std_id)
+#define VIDIOC_ENUMSTD		_IOWR('V', 25, struct v4l2_standard)
+#define VIDIOC_ENUMINPUT	_IOWR('V', 26, struct v4l2_input)
+#define VIDIOC_G_CTRL		_IOWR('V', 27, struct v4l2_control)
+#define VIDIOC_S_CTRL		_IOWR('V', 28, struct v4l2_control)
+#define VIDIOC_G_TUNER		_IOWR('V', 29, struct v4l2_tuner)
+#define VIDIOC_S_TUNER		 _IOW('V', 30, struct v4l2_tuner)
+#define VIDIOC_G_AUDIO		 _IOR('V', 33, struct v4l2_audio)
+#define VIDIOC_S_AUDIO		 _IOW('V', 34, struct v4l2_audio)
+#define VIDIOC_QUERYCTRL	_IOWR('V', 36, struct v4l2_queryctrl)
+#define VIDIOC_QUERYMENU	_IOWR('V', 37, struct v4l2_querymenu)
+#define VIDIOC_G_INPUT		 _IOR('V', 38, int)
+#define VIDIOC_S_INPUT		_IOWR('V', 39, int)
+#define VIDIOC_G_OUTPUT		 _IOR('V', 46, int)
+#define VIDIOC_S_OUTPUT		_IOWR('V', 47, int)
+#define VIDIOC_ENUMOUTPUT	_IOWR('V', 48, struct v4l2_output)
+#define VIDIOC_G_AUDOUT		 _IOR('V', 49, struct v4l2_audioout)
+#define VIDIOC_S_AUDOUT		 _IOW('V', 50, struct v4l2_audioout)
+#define VIDIOC_G_MODULATOR	_IOWR('V', 54, struct v4l2_modulator)
+#define VIDIOC_S_MODULATOR	 _IOW('V', 55, struct v4l2_modulator)
+#define VIDIOC_G_FREQUENCY	_IOWR('V', 56, struct v4l2_frequency)
+#define VIDIOC_S_FREQUENCY	 _IOW('V', 57, struct v4l2_frequency)
+#define VIDIOC_CROPCAP		_IOWR('V', 58, struct v4l2_cropcap)
+#define VIDIOC_G_CROP		_IOWR('V', 59, struct v4l2_crop)
+#define VIDIOC_S_CROP		 _IOW('V', 60, struct v4l2_crop)
+#define VIDIOC_G_JPEGCOMP	 _IOR('V', 61, struct v4l2_jpegcompression)
+#define VIDIOC_S_JPEGCOMP	 _IOW('V', 62, struct v4l2_jpegcompression)
+#define VIDIOC_QUERYSTD      	 _IOR('V', 63, v4l2_std_id)
+#define VIDIOC_TRY_FMT      	_IOWR('V', 64, struct v4l2_format)
+#define VIDIOC_ENUMAUDIO	_IOWR('V', 65, struct v4l2_audio)
+#define VIDIOC_ENUMAUDOUT	_IOWR('V', 66, struct v4l2_audioout)
+#define VIDIOC_G_PRIORITY        _IOR('V', 67, enum v4l2_priority)
+#define VIDIOC_S_PRIORITY        _IOW('V', 68, enum v4l2_priority)
+#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap)
+#define VIDIOC_LOG_STATUS         _IO('V', 70)
+#define VIDIOC_G_EXT_CTRLS	_IOWR('V', 71, struct v4l2_ext_controls)
+#define VIDIOC_S_EXT_CTRLS	_IOWR('V', 72, struct v4l2_ext_controls)
+#define VIDIOC_TRY_EXT_CTRLS	_IOWR('V', 73, struct v4l2_ext_controls)
+#if 1
+#define VIDIOC_ENUM_FRAMESIZES	_IOWR('V', 74, struct v4l2_frmsizeenum)
+#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum)
+#define VIDIOC_G_ENC_INDEX       _IOR('V', 76, struct v4l2_enc_idx)
+#define VIDIOC_ENCODER_CMD      _IOWR('V', 77, struct v4l2_encoder_cmd)
+#define VIDIOC_TRY_ENCODER_CMD  _IOWR('V', 78, struct v4l2_encoder_cmd)
+#endif
+
+#if 1
+/* Experimental, meant for debugging, testing and internal use.
+   Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined.
+   You must be root to use these ioctls. Never use these in applications! */
+#define	VIDIOC_DBG_S_REGISTER 	 _IOW('V', 79, struct v4l2_dbg_register)
+#define	VIDIOC_DBG_G_REGISTER 	_IOWR('V', 80, struct v4l2_dbg_register)
+
+/* Experimental, meant for debugging, testing and internal use.
+   Never use this ioctl in applications! */
+#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident)
+#endif
+
+#define VIDIOC_S_HW_FREQ_SEEK	 _IOW('V', 82, struct v4l2_hw_freq_seek)
+#define	VIDIOC_ENUM_DV_PRESETS	_IOWR('V', 83, struct v4l2_dv_enum_preset)
+#define	VIDIOC_S_DV_PRESET	_IOWR('V', 84, struct v4l2_dv_preset)
+#define	VIDIOC_G_DV_PRESET	_IOWR('V', 85, struct v4l2_dv_preset)
+#define	VIDIOC_QUERY_DV_PRESET	_IOR('V',  86, struct v4l2_dv_preset)
+#define	VIDIOC_S_DV_TIMINGS	_IOWR('V', 87, struct v4l2_dv_timings)
+#define	VIDIOC_G_DV_TIMINGS	_IOWR('V', 88, struct v4l2_dv_timings)
+
+/* Reminder: when adding new ioctls please add support for them to
+   drivers/media/video/v4l2-compat-ioctl32.c as well! */
+
+#ifdef __OLD_VIDIOC_
+/* for compatibility, will go away some day */
+#define VIDIOC_OVERLAY_OLD     	_IOWR('V', 14, int)
+#define VIDIOC_S_PARM_OLD      	 _IOW('V', 22, struct v4l2_streamparm)
+#define VIDIOC_S_CTRL_OLD      	 _IOW('V', 28, struct v4l2_control)
+#define VIDIOC_G_AUDIO_OLD     	_IOWR('V', 33, struct v4l2_audio)
+#define VIDIOC_G_AUDOUT_OLD    	_IOWR('V', 49, struct v4l2_audioout)
+#define VIDIOC_CROPCAP_OLD     	 _IOR('V', 58, struct v4l2_cropcap)
+#endif
+
+#define BASE_VIDIOC_PRIVATE	192		/* 192-255 are private */
+
+#endif /* __LINUX_VIDEODEV2_H */
diff --git a/sys/cpu/x86_64/include/atomic.h b/sys/cpu/x86_64/include/atomic.h
index 1f6c0d5a9e..85b10ae097 100644
--- a/sys/cpu/x86_64/include/atomic.h
+++ b/sys/cpu/x86_64/include/atomic.h
@@ -829,6 +829,7 @@ ATOMIC_STORE_LOAD(long, "cmpxchgq %0,%1",  "xchgq %1,%0");
 #define	atomic_swap_64		atomic_swap_long
 #define	atomic_fetchadd_64	atomic_fetchadd_long
 #define	atomic_add_64		atomic_add_long
+#define	atomic_subtract_64	atomic_subtract_long
 #define	atomic_cmpset_64	atomic_cmpset_long
 #define	atomic_fcmpset_64	atomic_fcmpset_long
 #define	atomic_set_64		atomic_set_long
-- 
2.50.1

