diff --git a/scripts/kernel/build-linux-rpi.sh b/scripts/kernel/build-linux-rpi.sh index 6acaf39..5bd17e1 100644 --- a/scripts/kernel/build-linux-rpi.sh +++ b/scripts/kernel/build-linux-rpi.sh @@ -20,6 +20,11 @@ PKGBUILD_REPO=${LESAVKA_KERNEL_PKG_REPO:-https://github.com/archlinuxarm/PKGBUIL BUILD_ROOT=${LESAVKA_KERNEL_BUILD_ROOT:-/var/tmp/lesavka-linux-rpi} PKGREL=${LESAVKA_KERNEL_PKGREL:-2} JOBS=${LESAVKA_KERNEL_JOBS:-2} +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +PATCH_DIR=${LESAVKA_KERNEL_PATCH_DIR:-$SCRIPT_DIR} +PATCH_DWC2_FIFO=${LESAVKA_KERNEL_PATCH_DWC2_FIFO:-} +PATCH_UVC_BULK=${LESAVKA_KERNEL_PATCH_UVC_BULK:-} +PATCH_UVC_DEBUG=${LESAVKA_KERNEL_PATCH_UVC_DEBUG:-} HEARTBEAT=/etc/lesavka/watchdog.touch if [[ -w $HEARTBEAT && -z ${LESAVKA_DISABLE_KEEPALIVE:-} ]]; then @@ -94,10 +99,69 @@ for line in sums: pkgbuild.write_text(text) PY +MAKEPKG_EXTRA="" +KERNEL_SRC="$BUILD_ROOT/linux-rpi/src/linux-$KERNEL_COMMIT" +PATCHES=() +if [[ -n $PATCH_DWC2_FIFO ]]; then + PATCHES+=("dwc2-fifo.patch") +fi +if [[ -n $PATCH_UVC_BULK ]]; then + PATCHES+=("uvc-bulk.patch") +fi +if [[ -n $PATCH_UVC_DEBUG ]]; then + PATCHES+=("uvc-debug.patch") +fi +if [[ ${#PATCHES[@]} -gt 0 ]]; then + sudo -u "$BUILD_USER" bash -c " + set -euo pipefail + cd '$BUILD_ROOT/linux-rpi' + MAKEFLAGS='-j$JOBS' makepkg -o --noconfirm + " + sudo -u "$BUILD_USER" bash -c " + set -euo pipefail + src='$KERNEL_SRC' + if [[ ! -d \$src ]]; then + echo \"missing kernel source \$src\" >&2 + exit 1 + fi + cd \"\$src\" + for patch in ${PATCHES[*]}; do + patch_file='$PATCH_DIR/'\"\$patch\" + if [[ ! -f \$patch_file ]]; then + echo \"missing patch \$patch_file\" >&2 + exit 1 + fi + case \"\$patch\" in + dwc2-fifo.patch) + if grep -q 'dwc2_check_param_fifo_total' \"drivers/usb/dwc2/params.c\"; then + echo \"dwc2-fifo patch already applied\" + continue + fi + ;; + uvc-bulk.patch) + if grep -q 'streaming_bulk' \"drivers/usb/gadget/function/uvc_configfs.c\"; then + echo \"uvc-bulk patch already applied\" + continue + fi + ;; + uvc-debug.patch) + if grep -q 'uvcg_video_enable: missing uvc/func/config' \ + \"drivers/usb/gadget/function/uvc_video.c\"; then + echo \"uvc-debug patch already applied\" + continue + fi + ;; + esac + patch -p1 < \"\$patch_file\" + done + " + MAKEPKG_EXTRA="-e" +fi + sudo -u "$BUILD_USER" bash -c " set -euo pipefail cd '$BUILD_ROOT/linux-rpi' - MAKEFLAGS='-j$JOBS' makepkg -s --noconfirm + MAKEFLAGS='-j$JOBS' makepkg -s --noconfirm $MAKEPKG_EXTRA " mapfile -t PKGS < <(ls "$BUILD_ROOT/linux-rpi"/*.pkg.tar.* 2>/dev/null) diff --git a/scripts/kernel/dwc2-fifo.patch b/scripts/kernel/dwc2-fifo.patch new file mode 100644 index 0000000..051f309 --- /dev/null +++ b/scripts/kernel/dwc2-fifo.patch @@ -0,0 +1,68 @@ +diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c +index 0a10aa2f8f5c..d7c5a3d1c4de 100644 +--- a/drivers/usb/dwc2/gadget.c ++++ b/drivers/usb/dwc2/gadget.c +@@ -226,10 +226,7 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) + { + int addr; + int tx_addr_max; +- u32 np_tx_fifo_size; +- +- np_tx_fifo_size = min_t(u32, hsotg->hw_params.dev_nperio_tx_fifo_size, +- hsotg->params.g_np_tx_fifo_size); ++ u32 np_tx_fifo_size = hsotg->params.g_np_tx_fifo_size; + + /* Get Endpoint Info Control block size in DWORDs. */ + tx_addr_max = hsotg->hw_params.total_fifo_size; +diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c +index 5d29c2157fbe..b25f7399b41c 100644 +--- a/drivers/usb/dwc2/params.c ++++ b/drivers/usb/dwc2/params.c +@@ -767,6 +767,35 @@ static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) + } + } + ++static void dwc2_check_param_fifo_total(struct dwc2_hsotg *hsotg) ++{ ++ struct dwc2_core_params *p = &hsotg->params; ++ u32 total_tx = 0; ++ u32 max_np; ++ int fifo_count; ++ int fifo; ++ ++ fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); ++ for (fifo = 1; fifo <= fifo_count; fifo++) ++ total_tx += p->g_tx_fifo_size[fifo]; ++ ++ if (p->g_rx_fifo_size + p->g_np_tx_fifo_size + total_tx <= ++ hsotg->hw_params.total_fifo_size) ++ return; ++ ++ max_np = 16; ++ if (hsotg->hw_params.total_fifo_size > p->g_rx_fifo_size + total_tx) ++ max_np = hsotg->hw_params.total_fifo_size - ++ p->g_rx_fifo_size - total_tx; ++ if (max_np < 16) ++ max_np = 16; ++ ++ dev_warn(hsotg->dev, ++ "%s: FIFO sizes exceed total, clamping g_np_tx_fifo_size to %u\n", ++ __func__, max_np); ++ p->g_np_tx_fifo_size = max_np; ++} ++ + static void dwc2_check_param_eusb2_disc(struct dwc2_hsotg *hsotg) + { + u32 gsnpsid; +@@ -879,9 +879,10 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) + CHECK_RANGE(g_rx_fifo_size, + 16, hw->rx_fifo_size, + hw->rx_fifo_size); + CHECK_RANGE(g_np_tx_fifo_size, +- 16, hw->dev_nperio_tx_fifo_size, ++ 16, hw->total_fifo_size, + hw->dev_nperio_tx_fifo_size); + dwc2_check_param_tx_fifo_sizes(hsotg); ++ dwc2_check_param_fifo_total(hsotg); + } + } diff --git a/scripts/kernel/uvc-bulk.patch b/scripts/kernel/uvc-bulk.patch new file mode 100644 index 0000000..46deb43 --- /dev/null +++ b/scripts/kernel/uvc-bulk.patch @@ -0,0 +1,173 @@ +diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c +index aa6ab666741a..7fb30b09e980 100644 +--- a/drivers/usb/gadget/function/f_uvc.c ++++ b/drivers/usb/gadget/function/f_uvc.c +@@ -652,22 +652,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + unsigned int max_packet_size; + struct usb_ep *ep; + struct f_uvc_opts *opts; ++ bool bulk; + int ret = -EINVAL; + + uvcg_info(f, "%s()\n", __func__); + + opts = fi_to_f_uvc_opts(f->fi); ++ bulk = opts->streaming_bulk; + /* Sanity check the streaming endpoint module parameters. */ + opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); +- opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); + opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); + +- /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */ +- if (opts->streaming_maxburst && +- (opts->streaming_maxpacket % 1024) != 0) { +- opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024); +- uvcg_info(f, "overriding streaming_maxpacket to %d\n", +- opts->streaming_maxpacket); ++ if (bulk) { ++ opts->streaming_maxpacket = ++ clamp(opts->streaming_maxpacket, 1U, 1024U); ++ } else { ++ opts->streaming_maxpacket = ++ clamp(opts->streaming_maxpacket, 1U, 3072U); ++ /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */ ++ if (opts->streaming_maxburst && ++ (opts->streaming_maxpacket % 1024) != 0) { ++ opts->streaming_maxpacket = ++ roundup(opts->streaming_maxpacket, 1024); ++ uvcg_info(f, ++ "overriding streaming_maxpacket to %d\n", ++ opts->streaming_maxpacket); ++ } + } + + /* +@@ -677,37 +687,70 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + * NOTE: We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ +- if (opts->streaming_maxpacket <= 1024) { ++ if (bulk) { + max_packet_mult = 1; + max_packet_size = opts->streaming_maxpacket; +- } else if (opts->streaming_maxpacket <= 2048) { +- max_packet_mult = 2; +- max_packet_size = opts->streaming_maxpacket / 2; +- } else { +- max_packet_mult = 3; +- max_packet_size = opts->streaming_maxpacket / 3; +- } + +- uvc_fs_streaming_ep.wMaxPacketSize = +- cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); +- uvc_fs_streaming_ep.bInterval = opts->streaming_interval; ++ uvc_fs_streaming_ep.bmAttributes = USB_ENDPOINT_XFER_BULK; ++ uvc_hs_streaming_ep.bmAttributes = USB_ENDPOINT_XFER_BULK; ++ uvc_ss_streaming_ep.bmAttributes = USB_ENDPOINT_XFER_BULK; + +- uvc_hs_streaming_ep.wMaxPacketSize = +- cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); ++ uvc_fs_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(min(opts->streaming_maxpacket, 64U)); ++ uvc_fs_streaming_ep.bInterval = 0; + +- /* A high-bandwidth endpoint must specify a bInterval value of 1 */ +- if (max_packet_mult > 1) +- uvc_hs_streaming_ep.bInterval = 1; +- else +- uvc_hs_streaming_ep.bInterval = opts->streaming_interval; +- +- uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); +- uvc_ss_streaming_ep.bInterval = opts->streaming_interval; +- uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; +- uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; +- uvc_ss_streaming_comp.wBytesPerInterval = +- cpu_to_le16(max_packet_size * max_packet_mult * +- (opts->streaming_maxburst + 1)); ++ uvc_hs_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(min(opts->streaming_maxpacket, 512U)); ++ uvc_hs_streaming_ep.bInterval = 0; ++ ++ uvc_ss_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(min(opts->streaming_maxpacket, 1024U)); ++ uvc_ss_streaming_ep.bInterval = 0; ++ uvc_ss_streaming_comp.bmAttributes = 0; ++ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; ++ uvc_ss_streaming_comp.wBytesPerInterval = cpu_to_le16(0); ++ } else { ++ if (opts->streaming_maxpacket <= 1024) { ++ max_packet_mult = 1; ++ max_packet_size = opts->streaming_maxpacket; ++ } else if (opts->streaming_maxpacket <= 2048) { ++ max_packet_mult = 2; ++ max_packet_size = opts->streaming_maxpacket / 2; ++ } else { ++ max_packet_mult = 3; ++ max_packet_size = opts->streaming_maxpacket / 3; ++ } ++ ++ uvc_fs_streaming_ep.bmAttributes = ++ USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC; ++ uvc_hs_streaming_ep.bmAttributes = ++ USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC; ++ uvc_ss_streaming_ep.bmAttributes = ++ USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC; ++ ++ uvc_fs_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); ++ uvc_fs_streaming_ep.bInterval = opts->streaming_interval; ++ ++ uvc_hs_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(max_packet_size | ++ ((max_packet_mult - 1) << 11)); ++ ++ /* A high-bandwidth endpoint must specify a bInterval value of 1 */ ++ if (max_packet_mult > 1) ++ uvc_hs_streaming_ep.bInterval = 1; ++ else ++ uvc_hs_streaming_ep.bInterval = opts->streaming_interval; ++ ++ uvc_ss_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(max_packet_size); ++ uvc_ss_streaming_ep.bInterval = opts->streaming_interval; ++ uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; ++ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; ++ uvc_ss_streaming_comp.wBytesPerInterval = ++ cpu_to_le16(max_packet_size * max_packet_mult * ++ (opts->streaming_maxburst + 1)); ++ } + + /* Allocate endpoints. */ + if (opts->enable_interrupt_ep) { +diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h +index 3ac392cbb779..b57e2d10ddcd 100644 +--- a/drivers/usb/gadget/function/u_uvc.h ++++ b/drivers/usb/gadget/function/u_uvc.h +@@ -24,6 +24,7 @@ struct f_uvc_opts { + unsigned int streaming_interval; + unsigned int streaming_maxpacket; + unsigned int streaming_maxburst; ++ unsigned int streaming_bulk; + + unsigned int control_interface; + unsigned int streaming_interface; +diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c +index a4a2d3dcb0d6..80817ff1b6fe 100644 +--- a/drivers/usb/gadget/function/uvc_configfs.c ++++ b/drivers/usb/gadget/function/uvc_configfs.c +@@ -3751,6 +3751,7 @@ UVC_ATTR(f_uvc_opts_, cname, cname) + UVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16); + UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072); + UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); ++UVCG_OPTS_ATTR(streaming_bulk, streaming_bulk, 1); + + #undef UVCG_OPTS_ATTR + +@@ -3800,6 +3801,7 @@ static struct configfs_attribute *uvc_attrs[] = { + &f_uvc_opts_attr_streaming_interval, + &f_uvc_opts_attr_streaming_maxpacket, + &f_uvc_opts_attr_streaming_maxburst, ++ &f_uvc_opts_attr_streaming_bulk, + &f_uvc_opts_string_attr_function_name, + NULL, + }; diff --git a/scripts/kernel/uvc-debug.patch b/scripts/kernel/uvc-debug.patch new file mode 100644 index 0000000..4fa6ebe --- /dev/null +++ b/scripts/kernel/uvc-debug.patch @@ -0,0 +1,47 @@ +--- a/drivers/usb/gadget/function/uvc_video.c ++++ b/drivers/usb/gadget/function/uvc_video.c +@@ + int uvcg_video_enable(struct uvc_video *video) + { + int ret; + ++ if (!video) { ++ pr_err("uvcg_video_enable: missing video\n"); ++ return -EINVAL; ++ } ++ ++ if (!video->uvc || !video->uvc->func.config || ++ !video->uvc->func.config->cdev) { ++ pr_err("uvcg_video_enable: missing uvc/func/config video=%p uvc=%p\n", ++ video, video->uvc); ++ return -ENODEV; ++ } ++ + if (video->ep == NULL) { + uvcg_info(&video->uvc->func, + "Video enable failed, device is uninitialized.\n"); + return -ENODEV; + } ++ ++ if (!video->kworker || !video->async_wq) { ++ uvcg_err(&video->uvc->func, ++ "Video enable failed, missing worker(s) kworker=%p async_wq=%p\n", ++ video->kworker, video->async_wq); ++ return -EINVAL; ++ } ++ ++ if (!video->queue.queue.dev) { ++ uvcg_err(&video->uvc->func, ++ "Video enable failed, missing queue device\n"); ++ return -EINVAL; ++ } ++ ++ uvcg_info(&video->uvc->func, ++ "Video enable start: video=%p ep=%p kworker=%p async_wq=%p req_size=%u max_payload=%u requests=%u reqs_per_frame=%u use_sg=%u flags=0x%x\n", ++ video, video->ep, video->kworker, video->async_wq, ++ video->req_size, video->max_payload_size, ++ video->uvc_num_requests, video->reqs_per_frame, ++ video->queue.use_sg, video->queue.flags); + + /* + * Safe to access request related fields without req_lock because