From 886c75718617c8a478395484187d06a6801fc334 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Mon, 25 Sep 2023 13:02:54 +0200 Subject: kernel 6.5.4 --- SOURCES/steam-deck.patch | 1654 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1654 insertions(+) (limited to 'SOURCES/steam-deck.patch') diff --git a/SOURCES/steam-deck.patch b/SOURCES/steam-deck.patch index e8570a0..696f642 100644 --- a/SOURCES/steam-deck.patch +++ b/SOURCES/steam-deck.patch @@ -919,3 +919,1657 @@ index fab9e9460bd4..9d0a5471b181 100644 if (IS_ERR(hwmon)) { dev_err(dev, "Failed to register HWMON device"); return PTR_ERR(hwmon); +From b899859fe49cccda9e8739d29d883dbd6dd057f3 Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Thu, 30 Jun 2022 18:42:10 -0700 +Subject: [PATCH 01/10] USB: gadget: f_hid: Add Get-Feature report + +While the HID gadget implementation has been sufficient for devices that only +use INTERRUPT transfers, the USB HID standard includes provisions for Set- and +Get-Feature report CONTROL transfers that go over endpoint 0. These were +previously impossible with the existing implementation, and would either send +an empty reply, or stall out. + +As the feature is a standard part of USB HID, it stands to reason that devices +would use it, and that the HID gadget should support it. This patch adds +support for (polled) device-to-host Get-Feature reports through a new ioctl +interface to the hidg class dev nodes. + +Signed-off-by: Vicki Pfau +(cherry picked from commit 8437fa3861c7198a3e286f393c8637c4fc08d2bc) +Signed-off-by: Cristian Ciocaltea +--- + drivers/usb/gadget/function/f_hid.c | 121 ++++++++++++++++++++++++++-- + include/uapi/linux/usb/g_hid.h | 38 +++++++++ + include/uapi/linux/usb/gadgetfs.h | 2 +- + 3 files changed, 154 insertions(+), 7 deletions(-) + create mode 100644 include/uapi/linux/usb/g_hid.h + +diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c +index ea85e2c701a15..6fec92b5a0bd9 100644 +--- a/drivers/usb/gadget/function/f_hid.c ++++ b/drivers/usb/gadget/function/f_hid.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #include "u_f.h" + #include "u_hid.h" +@@ -75,6 +76,13 @@ struct f_hidg { + wait_queue_head_t write_queue; + struct usb_request *req; + ++ /* get report */ ++ struct usb_request *get_req; ++ struct usb_hidg_report get_report; ++ spinlock_t get_spinlock; ++ bool get_pending; ++ wait_queue_head_t get_queue; ++ + struct device dev; + struct cdev cdev; + struct usb_function func; +@@ -523,6 +531,64 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, + return status; + } + ++ ++static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer) ++{ ++ struct f_hidg *hidg = file->private_data; ++ struct usb_composite_dev *cdev = hidg->func.config->cdev; ++ ++ int status = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&hidg->get_spinlock, flags); ++ ++#define GET_REPORT_COND (!hidg->get_pending) ++ ++ while (!GET_REPORT_COND) { ++ spin_unlock_irqrestore(&hidg->get_spinlock, flags); ++ ++ if (file->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ ++ if (wait_event_interruptible_exclusive(hidg->get_queue, ++ GET_REPORT_COND)) ++ return -ERESTARTSYS; ++ ++ spin_lock_irqsave(&hidg->get_spinlock, flags); ++ if (!hidg->get_pending) { ++ spin_unlock_irqrestore(&hidg->get_spinlock, flags); ++ return -EINVAL; ++ } ++ } ++ ++ hidg->get_pending = true; ++ spin_unlock_irqrestore(&hidg->get_spinlock, flags); ++ ++ status = copy_from_user(&hidg->get_report, buffer, ++ sizeof(struct usb_hidg_report)); ++ if (status != 0) { ++ ERROR(cdev, "copy_from_user error\n"); ++ status = -EINVAL; ++ } ++ ++ spin_lock_irqsave(&hidg->get_spinlock, flags); ++ hidg->get_pending = false; ++ spin_unlock_irqrestore(&hidg->get_spinlock, flags); ++ ++ wake_up(&hidg->get_queue); ++ return status; ++} ++ ++static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg) ++{ ++ switch (code) { ++ case GADGET_HID_WRITE_GET_REPORT: ++ return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg); ++ default: ++ return -ENOTTY; ++ } ++} ++ + static __poll_t f_hidg_poll(struct file *file, poll_table *wait) + { + struct f_hidg *hidg = file->private_data; +@@ -548,6 +614,7 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) + #undef WRITE_COND + #undef READ_COND_SSREPORT + #undef READ_COND_INTOUT ++#undef GET_REPORT_COND + + static int f_hidg_release(struct inode *inode, struct file *fd) + { +@@ -640,6 +707,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req) + wake_up(&hidg->read_queue); + } + ++static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++} ++ + static int hidg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) + { +@@ -647,6 +718,8 @@ static int hidg_setup(struct usb_function *f, + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int status = 0; ++ unsigned long flags; ++ bool do_wake = false; + __u16 value, length; + + value = __le16_to_cpu(ctrl->wValue); +@@ -659,14 +732,29 @@ static int hidg_setup(struct usb_function *f, + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_REPORT): +- VDBG(cdev, "get_report\n"); ++ VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength); + +- /* send an empty report */ +- length = min_t(unsigned, length, hidg->report_length); +- memset(req->buf, 0x0, length); ++ req = hidg->get_req; ++ req->zero = 0; ++ req->length = min_t(unsigned, length, hidg->report_length); ++ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (status < 0) { ++ ERROR(cdev, "usb_ep_queue error on get_report %d\n", ++ status); + +- goto respond; +- break; ++ spin_lock_irqsave(&hidg->get_spinlock, flags); ++ if (hidg->get_pending) { ++ hidg->get_pending = false; ++ do_wake = true; ++ } ++ spin_unlock_irqrestore(&hidg->get_spinlock, flags); ++ ++ if (do_wake) { ++ wake_up(&hidg->get_queue); ++ } ++ } ++ ++ return status; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_PROTOCOL): +@@ -800,6 +888,14 @@ static void hidg_disable(struct usb_function *f) + + hidg->req = NULL; + spin_unlock_irqrestore(&hidg->write_spinlock, flags); ++ ++ spin_lock_irqsave(&hidg->get_spinlock, flags); ++ if (!hidg->get_pending) { ++ usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req); ++ hidg->get_pending = true; ++ } ++ hidg->get_req = NULL; ++ spin_unlock_irqrestore(&hidg->get_spinlock, flags); + } + + static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +@@ -908,6 +1004,7 @@ static const struct file_operations f_hidg_fops = { + .write = f_hidg_write, + .read = f_hidg_read, + .poll = f_hidg_poll, ++ .unlocked_ioctl = f_hidg_ioctl, + .llseek = noop_llseek, + }; + +@@ -918,6 +1015,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) + struct usb_string *us; + int status; + ++ hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC); ++ if (!hidg->get_req) ++ return -ENOMEM; ++ hidg->get_req->buf = hidg->get_report.data; ++ hidg->get_req->zero = 0; ++ hidg->get_req->complete = hidg_get_report_complete; ++ hidg->get_req->context = hidg; ++ + /* maybe allocate device-global string IDs, and patch descriptors */ + us = usb_gstrings_attach(c->cdev, ct_func_strings, + ARRAY_SIZE(ct_func_string_defs)); +@@ -1003,8 +1108,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) + hidg->write_pending = 1; + hidg->req = NULL; + spin_lock_init(&hidg->read_spinlock); ++ spin_lock_init(&hidg->get_spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); ++ init_waitqueue_head(&hidg->get_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); + + /* create char device */ +@@ -1021,6 +1128,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) + if (hidg->req != NULL) + free_ep_req(hidg->in_ep, hidg->req); + ++ usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req); ++ + return status; + } + +diff --git a/include/uapi/linux/usb/g_hid.h b/include/uapi/linux/usb/g_hid.h +new file mode 100644 +index 0000000000000..c6068b4863543 +--- /dev/null ++++ b/include/uapi/linux/usb/g_hid.h +@@ -0,0 +1,38 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* ++ * g_hid.h -- Header file for USB HID gadget driver ++ * ++ * Copyright (C) 2022 Valve Software ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __UAPI_LINUX_USB_G_HID_H ++#define __UAPI_LINUX_USB_G_HID_H ++ ++#include ++ ++struct usb_hidg_report { ++ __u16 length; ++ __u8 data[512]; ++}; ++ ++/* The 'g' code is also used by gadgetfs and hid gadget ioctl requests. ++ * Don't add any colliding codes to either driver, and keep ++ * them in unique ranges (size 0x20 for now). ++ */ ++#define GADGET_HID_WRITE_GET_REPORT _IOW('g', 0x42, struct usb_hidg_report) ++ ++#endif /* __UAPI_LINUX_USB_G_HID_H */ +diff --git a/include/uapi/linux/usb/gadgetfs.h b/include/uapi/linux/usb/gadgetfs.h +index 835473910a498..9754822b2a409 100644 +--- a/include/uapi/linux/usb/gadgetfs.h ++++ b/include/uapi/linux/usb/gadgetfs.h +@@ -62,7 +62,7 @@ struct usb_gadgetfs_event { + }; + + +-/* The 'g' code is also used by printer gadget ioctl requests. ++/* The 'g' code is also used by printer and hid gadget ioctl requests. + * Don't add any colliding codes to either driver, and keep + * them in unique ranges (size 0x20 for now). + */ +-- +2.41.0 + + +From 20ebaf7b44ff03078cf53e43306d6c5a3d0613e6 Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Thu, 30 Jun 2022 18:43:10 -0700 +Subject: [PATCH 02/10] USB: gadget: f_hid: Add Set-Feature report + +While the HID gadget implementation has been sufficient for devices that only +use INTERRUPT transfers, the USB HID standard includes provisions for Set- and +Get-Feature report CONTROL transfers that go over endpoint 0. These were +previously impossible with the existing implementation, and would either send +an empty reply, or stall out. + +As the feature is a standard part of USB HID, it stands to reason that devices +would use it, and that the HID gadget should support it. This patch adds +support for host-to-device Set-Feature reports through a new ioctl +interface to the hidg class dev nodes. + +Signed-off-by: Vicki Pfau +(cherry picked from commit 3d82be0ec3aa3b947d9c927d7b06c433de15be8b) +Signed-off-by: Cristian Ciocaltea +--- + drivers/usb/gadget/function/f_hid.c | 110 ++++++++++++++++++++++++++-- + include/uapi/linux/usb/g_hid.h | 24 +----- + 2 files changed, 106 insertions(+), 28 deletions(-) + +diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c +index 6fec92b5a0bd9..172cba91aded1 100644 +--- a/drivers/usb/gadget/function/f_hid.c ++++ b/drivers/usb/gadget/function/f_hid.c +@@ -76,6 +76,11 @@ struct f_hidg { + wait_queue_head_t write_queue; + struct usb_request *req; + ++ /* set report */ ++ struct list_head completed_set_req; ++ spinlock_t set_spinlock; ++ wait_queue_head_t set_queue; ++ + /* get report */ + struct usb_request *get_req; + struct usb_hidg_report get_report; +@@ -531,6 +536,54 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, + return status; + } + ++static int f_hidg_set_report(struct file *file, struct usb_hidg_report __user *buffer) ++{ ++ struct f_hidg *hidg = file->private_data; ++ struct f_hidg_req_list *list; ++ struct usb_request *req; ++ unsigned long flags; ++ unsigned short length; ++ int status; ++ ++ spin_lock_irqsave(&hidg->set_spinlock, flags); ++ ++#define SET_REPORT_COND (!list_empty(&hidg->completed_set_req)) ++ ++ /* wait for at least one buffer to complete */ ++ while (!SET_REPORT_COND) { ++ spin_unlock_irqrestore(&hidg->set_spinlock, flags); ++ if (file->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ ++ if (wait_event_interruptible(hidg->set_queue, SET_REPORT_COND)) ++ return -ERESTARTSYS; ++ ++ spin_lock_irqsave(&hidg->set_spinlock, flags); ++ } ++ ++ /* pick the first one */ ++ list = list_first_entry(&hidg->completed_set_req, ++ struct f_hidg_req_list, list); ++ ++ /* ++ * Remove this from list to protect it from being free() ++ * while host disables our function ++ */ ++ list_del(&list->list); ++ ++ req = list->req; ++ spin_unlock_irqrestore(&hidg->set_spinlock, flags); ++ ++ /* copy to user outside spinlock */ ++ length = min_t(unsigned short, sizeof(buffer->data), req->actual); ++ status = copy_to_user(&buffer->length, &length, sizeof(buffer->length)); ++ if (!status) { ++ status = copy_to_user(&buffer->data, req->buf, length); ++ } ++ kfree(list); ++ free_ep_req(hidg->func.config->cdev->gadget->ep0, req); ++ return status; ++} + + static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer) + { +@@ -582,6 +635,8 @@ static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *b + static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg) + { + switch (code) { ++ case GADGET_HID_READ_SET_REPORT: ++ return f_hidg_set_report(file, (struct usb_hidg_report __user *)arg); + case GADGET_HID_WRITE_GET_REPORT: + return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg); + default: +@@ -596,6 +651,7 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) + + poll_wait(file, &hidg->read_queue, wait); + poll_wait(file, &hidg->write_queue, wait); ++ poll_wait(file, &hidg->set_queue, wait); + + if (WRITE_COND) + ret |= EPOLLOUT | EPOLLWRNORM; +@@ -608,12 +664,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) + ret |= EPOLLIN | EPOLLRDNORM; + } + ++ if (SET_REPORT_COND) ++ ret |= EPOLLPRI; ++ + return ret; + } + + #undef WRITE_COND + #undef READ_COND_SSREPORT + #undef READ_COND_INTOUT ++#undef SET_REPORT_COND + #undef GET_REPORT_COND + + static int f_hidg_release(struct inode *inode, struct file *fd) +@@ -658,11 +718,19 @@ static void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req) + + req_list->req = req; + +- spin_lock_irqsave(&hidg->read_spinlock, flags); +- list_add_tail(&req_list->list, &hidg->completed_out_req); +- spin_unlock_irqrestore(&hidg->read_spinlock, flags); ++ if (ep == cdev->gadget->ep0) { ++ spin_lock_irqsave(&hidg->set_spinlock, flags); ++ list_add_tail(&req_list->list, &hidg->completed_set_req); ++ spin_unlock_irqrestore(&hidg->set_spinlock, flags); + +- wake_up(&hidg->read_queue); ++ wake_up(&hidg->set_queue); ++ } else { ++ spin_lock_irqsave(&hidg->read_spinlock, flags); ++ list_add_tail(&req_list->list, &hidg->completed_out_req); ++ spin_unlock_irqrestore(&hidg->read_spinlock, flags); ++ ++ wake_up(&hidg->read_queue); ++ } + break; + default: + ERROR(cdev, "Set report failed %d\n", req->status); +@@ -775,12 +843,27 @@ static int hidg_setup(struct usb_function *f, + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_REPORT): + VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength); +- if (hidg->use_out_ep) ++ if (!hidg->use_out_ep) { ++ req->complete = hidg_ssreport_complete; ++ req->context = hidg; ++ goto respond; ++ } ++ if (!length) + goto stall; +- req->complete = hidg_ssreport_complete; ++ req = alloc_ep_req(cdev->gadget->ep0, GFP_ATOMIC); ++ if (!req) ++ return -ENOMEM; ++ req->complete = hidg_intout_complete; + req->context = hidg; +- goto respond; +- break; ++ req->zero = 0; ++ req->length = length; ++ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (status < 0) { ++ ERROR(cdev, "usb_ep_queue error on set_report %d\n", status); ++ free_ep_req(cdev->gadget->ep0, req); ++ } ++ ++ return status; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_PROTOCOL): +@@ -880,6 +963,14 @@ static void hidg_disable(struct usb_function *f) + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + } + ++ spin_lock_irqsave(&hidg->set_spinlock, flags); ++ list_for_each_entry_safe(list, next, &hidg->completed_set_req, list) { ++ free_ep_req(f->config->cdev->gadget->ep0, list->req); ++ list_del(&list->list); ++ kfree(list); ++ } ++ spin_unlock_irqrestore(&hidg->set_spinlock, flags); ++ + spin_lock_irqsave(&hidg->write_spinlock, flags); + if (!hidg->write_pending) { + free_ep_req(hidg->in_ep, hidg->req); +@@ -1108,11 +1199,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) + hidg->write_pending = 1; + hidg->req = NULL; + spin_lock_init(&hidg->read_spinlock); ++ spin_lock_init(&hidg->set_spinlock); + spin_lock_init(&hidg->get_spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); ++ init_waitqueue_head(&hidg->set_queue); + init_waitqueue_head(&hidg->get_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); ++ INIT_LIST_HEAD(&hidg->completed_set_req); + + /* create char device */ + cdev_init(&hidg->cdev, &f_hidg_fops); +diff --git a/include/uapi/linux/usb/g_hid.h b/include/uapi/linux/usb/g_hid.h +index c6068b4863543..54814c2c68d60 100644 +--- a/include/uapi/linux/usb/g_hid.h ++++ b/include/uapi/linux/usb/g_hid.h +@@ -1,38 +1,22 @@ + /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +-/* +- * g_hid.h -- Header file for USB HID gadget driver +- * +- * Copyright (C) 2022 Valve Software +- * +- * 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. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ + + #ifndef __UAPI_LINUX_USB_G_HID_H + #define __UAPI_LINUX_USB_G_HID_H + + #include + ++#define HIDG_REPORT_SIZE_MAX 64 ++ + struct usb_hidg_report { + __u16 length; +- __u8 data[512]; ++ __u8 data[HIDG_REPORT_SIZE_MAX]; + }; + + /* The 'g' code is also used by gadgetfs and hid gadget ioctl requests. + * Don't add any colliding codes to either driver, and keep + * them in unique ranges (size 0x20 for now). + */ ++#define GADGET_HID_READ_SET_REPORT _IOR('g', 0x41, struct usb_hidg_report) + #define GADGET_HID_WRITE_GET_REPORT _IOW('g', 0x42, struct usb_hidg_report) + + #endif /* __UAPI_LINUX_USB_G_HID_H */ +-- +2.41.0 + + +From 146e98d6f595e3a4e6c09a00ee9ec2d48a6703cb Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Tue, 29 Nov 2022 18:32:58 -0800 +Subject: [PATCH 03/10] HID: hid-steam: Update list of identifiers from SDL + +SDL includes a list of settings (registers), reports (cmds), and various other +identifiers that were provided by Valve. This commit imports a significant +chunk of that list as well as updating the guessed names and replacing a +handful of magic constants. It also replaces bitmask definitions that used hex +with the BIT macro. + +Signed-off-by: Vicki Pfau +--- + drivers/hid/hid-steam.c | 156 +++++++++++++++++++++++++++++++--------- + 1 file changed, 121 insertions(+), 35 deletions(-) + +diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c +index b110818fc9458..39a9bf3b7f77d 100644 +--- a/drivers/hid/hid-steam.c ++++ b/drivers/hid/hid-steam.c +@@ -71,7 +71,7 @@ static LIST_HEAD(steam_devices); + + /* + * Commands that can be sent in a feature report. +- * Thanks to Valve for some valuable hints. ++ * Thanks to Valve and SDL for some valuable hints. + */ + #define STEAM_CMD_SET_MAPPINGS 0x80 + #define STEAM_CMD_CLEAR_MAPPINGS 0x81 +@@ -80,27 +80,98 @@ static LIST_HEAD(steam_devices); + #define STEAM_CMD_GET_ATTRIB_LABEL 0x84 + #define STEAM_CMD_DEFAULT_MAPPINGS 0x85 + #define STEAM_CMD_FACTORY_RESET 0x86 +-#define STEAM_CMD_WRITE_REGISTER 0x87 ++#define STEAM_CMD_SET_REGISTER 0x87 + #define STEAM_CMD_CLEAR_REGISTER 0x88 +-#define STEAM_CMD_READ_REGISTER 0x89 ++#define STEAM_CMD_GET_REGISTER 0x89 + #define STEAM_CMD_GET_REGISTER_LABEL 0x8a + #define STEAM_CMD_GET_REGISTER_MAX 0x8b + #define STEAM_CMD_GET_REGISTER_DEFAULT 0x8c + #define STEAM_CMD_SET_MODE 0x8d +-#define STEAM_CMD_DEFAULT_MOUSE 0x8e +-#define STEAM_CMD_FORCEFEEDBAK 0x8f +-#define STEAM_CMD_REQUEST_COMM_STATUS 0xb4 +-#define STEAM_CMD_GET_SERIAL 0xae ++#define STEAM_CMD_DEFAULT_REGISTER 0x8e ++#define STEAM_CMD_HAPTIC_PULSE 0x8f ++#define STEAM_CMD_TURN_OFF_CONTROLLER 0x9f ++#define STEAM_CMD_GET_DEVICE_IFNO 0xa1 ++#define STEAM_CMD_CALIBRATE_TRACKPADS 0xa7 ++#define STEAM_CMD_SET_SERIAL 0xa9 ++#define STEAM_CMD_GET_TRACKPAD_CALIB 0xaa ++#define STEAM_CMD_GET_TRACKPAD_FACTORY_CALIB 0xab ++#define STEAM_CMD_GET_TRACKPAD_RAW_DATA 0xac ++#define STEAM_CMD_ENABLE_PAIRING 0xad ++#define STEAM_CMD_GET_STRING_ATTRIB 0xae ++#define STEAM_CMD_RADIO_ERASE_RECORDS 0xaf ++#define STEAM_CMD_RADIO_WRITE_RECORD 0xb0 ++#define STEAM_CMD_SET_DONGLE_SETTING 0xb1 ++#define STEAM_CMD_DONGLE_DISCONNECT_DEV 0xb2 ++#define STEAM_CMD_DONGLE_COMMIT_DEV 0xb3 ++#define STEAM_CMD_DONGLE_GET_STATE 0xb4 ++#define STEAM_CMD_CALIBRATE_GYRO 0xb5 ++#define STEAM_CMD_PLAY_AUDIO 0xb6 ++#define STEAM_CMD_AUDIO_UPDATE_START 0xb7 ++#define STEAM_CMD_AUDIO_UPDATE_DATA 0xb8 ++#define STEAM_CMD_AUDIO_UPDATE_COMPLETE 0xb9 ++#define STEAM_CMD_GET_CHIPID 0xba ++#define STEAM_CMD_CALIBRATE_JOYSTICK 0xbf ++#define STEAM_CMD_CALIBRATE_TRIGGERS 0xc0 ++#define STEAM_CMD_SET_AUDIO_MAPPING 0xc1 ++#define STEAM_CMD_CHECK_GYRO_FW_LOAD 0xc2 ++#define STEAM_CMD_CALIBRATE_ANALOG 0xc3 ++#define STEAM_CMD_DONGLE_GET_CONN_SLOTS 0xc4 ++#define STEAM_CMD_HAPTIC_CMD 0xea + #define STEAM_CMD_HAPTIC_RUMBLE 0xeb + + /* Some useful register ids */ +-#define STEAM_REG_LPAD_MODE 0x07 +-#define STEAM_REG_RPAD_MODE 0x08 +-#define STEAM_REG_RPAD_MARGIN 0x18 +-#define STEAM_REG_LED 0x2d +-#define STEAM_REG_GYRO_MODE 0x30 +-#define STEAM_REG_LPAD_CLICK_PRESSURE 0x34 +-#define STEAM_REG_RPAD_CLICK_PRESSURE 0x35 ++#define STEAM_REG_MOUSE_SENSITIVITY 0x00 ++#define STEAM_REG_MOUSE_ACCELERATION 0x01 ++#define STEAM_REG_TRACKBALL_ROTATION_ANGLE 0x02 ++#define STEAM_REG_HAPTIC_INTENSITY 0x03 ++#define STEAM_REG_LEFT_GAMEPAD_STICK_ENABLED 0x04 ++#define STEAM_REG_RIGHT_GAMEPAD_STICK_ENABLED 0x05 ++#define STEAM_REG_USB_DEBUG_MODE 0x06 ++#define STEAM_REG_LEFT_TRACKPAD_MODE 0x07 ++#define STEAM_REG_RIGHT_TRACKPAD_MODE 0x08 ++#define STEAM_REG_MOUSE_POINTER_ENABLED 0x09 ++#define STEAM_REG_DPAD_DEADZONE 0x0a ++#define STEAM_REG_MINIMUM_MOMENTUM_VEL 0x0b ++#define STEAM_REG_MOMENTUM_DECAY_AMOUNT 0x0c ++#define STEAM_REG_PAD_REL_MODE_TICKS_PER_PIXEL 0x0d ++#define STEAM_REG_HAPTIC_INCREMENT 0x0e ++#define STEAM_REG_DPAD_ANGLE_SIN 0x0f ++#define STEAM_REG_DPAD_ANGLE_COS 0x10 ++#define STEAM_REG_MOMENTUM_VERTICAL_DIVISOR 0x11 ++#define STEAM_REG_MOMENTUM_MAXIMUM_VELOCITY 0x12 ++#define STEAM_REG_TRACKPAD_Z_ON 0x13 ++#define STEAM_REG_TRACKPAD_Z_OFF 0x14 ++#define STEAM_REG_SENSITIVY_SCALE_AMOUNT 0x15 ++#define STEAM_REG_LEFT_TRACKPAD_SECONDARY_MODE 0x16 ++#define STEAM_REG_RIGHT_TRACKPAD_SECONDARY_MODE 0x17 ++#define STEAM_REG_SMOOTH_ABSOLUTE_MOUSE 0x18 ++#define STEAM_REG_STEAMBUTTON_POWEROFF_TIME 0x19 ++#define STEAM_REG_TRACKPAD_OUTER_RADIUS 0x1b ++#define STEAM_REG_TRACKPAD_Z_ON_LEFT 0x1c ++#define STEAM_REG_TRACKPAD_Z_OFF_LEFT 0x1d ++#define STEAM_REG_TRACKPAD_OUTER_SPIN_VEL 0x1e ++#define STEAM_REG_TRACKPAD_OUTER_SPIN_RADIUS 0x1f ++#define STEAM_REG_TRACKPAD_OUTER_SPIN_HORIZONTAL_ONLY 0x20 ++#define STEAM_REG_TRACKPAD_RELATIVE_MODE_DEADZONE 0x21 ++#define STEAM_REG_TRACKPAD_RELATIVE_MODE_MAX_VEL 0x22 ++#define STEAM_REG_TRACKPAD_RELATIVE_MODE_INVERT_Y 0x23 ++#define STEAM_REG_TRACKPAD_DOUBLE_TAP_BEEP_ENABLED 0x24 ++#define STEAM_REG_TRACKPAD_DOUBLE_TAP_BEEP_PERIOD 0x25 ++#define STEAM_REG_TRACKPAD_DOUBLE_TAP_BEEP_COUNT 0x26 ++#define STEAM_REG_TRACKPAD_OUTER_RADIUS_RELEASE_ON_TRANSITION 0x27 ++#define STEAM_REG_RADIAL_MODE_ANGLE 0x28 ++#define STEAM_REG_HAPTIC_INTENSITY_MOUSE_MODE 0x29 ++#define STEAM_REG_LEFT_DPAD_REQUIRES_CLICK 0x2a ++#define STEAM_REG_RIGHT_DPAD_REQUIRES_CLICK 0x2b ++#define STEAM_REG_LED_BASELINE_BRIGHTNESS 0x2c ++#define STEAM_REG_LED_USER_BRIGHTNESS 0x2d ++#define STEAM_REG_ENABLE_RAW_JOYSTICK 0x2e ++#define STEAM_REG_ENABLE_FAST_SCAN 0x2f ++#define STEAM_REG_GYRO_MODE 0x30 ++#define STEAM_REG_WIRELESS_PACKET_VERSION 0x31 ++#define STEAM_REG_SLEEP_INACTIVITY_TIMEOUT 0x32 ++#define STEAM_REG_LEFT_TRACKPAD_CLICK_PRESSURE 0x34 ++#define STEAM_REG_RIGHT_TRACKPAD_CLICK_PRESSURE 0x35 + + /* Raw event identifiers */ + #define STEAM_EV_INPUT_DATA 0x01 +@@ -108,13 +179,28 @@ static LIST_HEAD(steam_devices); + #define STEAM_EV_BATTERY 0x04 + #define STEAM_EV_DECK_INPUT_DATA 0x09 + ++/* String attribute idenitifiers */ ++#define STEAM_ATTRIB_STR_BOARD_SERIAL 0x00 ++#define STEAM_ATTRIB_STR_UNIT_SERIAL 0x01 ++ + /* Values for GYRO_MODE (bitmask) */ +-#define STEAM_GYRO_MODE_OFF 0x0000 +-#define STEAM_GYRO_MODE_STEERING 0x0001 +-#define STEAM_GYRO_MODE_TILT 0x0002 +-#define STEAM_GYRO_MODE_SEND_ORIENTATION 0x0004 +-#define STEAM_GYRO_MODE_SEND_RAW_ACCEL 0x0008 +-#define STEAM_GYRO_MODE_SEND_RAW_GYRO 0x0010 ++#define STEAM_GYRO_MODE_OFF 0 ++#define STEAM_GYRO_MODE_STEERING BIT(0) ++#define STEAM_GYRO_MODE_TILT BIT(1) ++#define STEAM_GYRO_MODE_SEND_ORIENTATION BIT(2) ++#define STEAM_GYRO_MODE_SEND_RAW_ACCEL BIT(3) ++#define STEAM_GYRO_MODE_SEND_RAW_GYRO BIT(4) ++ ++/* Trackpad modes */ ++#define STEAM_TRACKPAD_ABSOLUTE_MOUSE 0x00 ++#define STEAM_TRACKPAD_RELATIVE_MOUSE 0x01 ++#define STEAM_TRACKPAD_DPAD_FOUR_WAY_DISCRETE 0x02 ++#define STEAM_TRACKPAD_DPAD_FOUR_WAY_OVERLAP 0x03 ++#define STEAM_TRACKPAD_DPAD_EIGHT_WAY 0x04 ++#define STEAM_TRACKPAD_RADIAL_MODE 0x05 ++#define STEAM_TRACKPAD_ABSOLUTE_DPAD 0x06 ++#define STEAM_TRACKPAD_NONE 0x07 ++#define STEAM_TRACKPAD_GESTURE_KEYBOARD 0x08 + + /* Other random constants */ + #define STEAM_SERIAL_LEN 10 +@@ -232,7 +318,7 @@ static int steam_write_registers(struct steam_device *steam, + /* Send: 0x87 len (reg valLo valHi)* */ + u8 reg; + u16 val; +- u8 cmd[64] = {STEAM_CMD_WRITE_REGISTER, 0x00}; ++ u8 cmd[64] = {STEAM_CMD_SET_REGISTER, 0x00}; + int ret; + va_list args; + +@@ -268,7 +354,7 @@ static int steam_get_serial(struct steam_device *steam) + * Recv: 0xae 0x15 0x01 serialnumber (10 chars) + */ + int ret; +- u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01}; ++ u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, 0x15, STEAM_ATTRIB_STR_UNIT_SERIAL}; + u8 reply[3 + STEAM_SERIAL_LEN + 1]; + + ret = steam_send_report(steam, cmd, sizeof(cmd)); +@@ -277,7 +363,7 @@ static int steam_get_serial(struct steam_device *steam) + ret = steam_recv_report(steam, reply, sizeof(reply)); + if (ret < 0) + return ret; +- if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01) ++ if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != STEAM_ATTRIB_STR_UNIT_SERIAL) + return -EIO; + reply[3 + STEAM_SERIAL_LEN] = 0; + strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no)); +@@ -291,7 +377,7 @@ static int steam_get_serial(struct steam_device *steam) + */ + static inline int steam_request_conn_status(struct steam_device *steam) + { +- return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS); ++ return steam_send_report_byte(steam, STEAM_CMD_DONGLE_GET_STATE); + } + + static inline int steam_haptic_rumble(struct steam_device *steam, +@@ -339,9 +425,9 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + /* enable esc, enter, cursors */ + steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS); + /* enable mouse */ +- steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE); ++ steam_send_report_byte(steam, STEAM_CMD_DEFAULT_REGISTER); + steam_write_registers(steam, +- STEAM_REG_RPAD_MARGIN, 0x01, /* enable margin */ ++ STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x01, /* enable smooth */ + 0); + + cancel_delayed_work_sync(&steam->heartbeat); +@@ -351,11 +437,11 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + + if (steam->quirks & STEAM_QUIRK_DECK) { + steam_write_registers(steam, +- STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ +- STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */ +- STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ +- STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ +- STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ ++ STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x00, /* disable smooth */ ++ STEAM_REG_LEFT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ ++ STEAM_REG_RIGHT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ ++ STEAM_REG_LEFT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ ++ STEAM_REG_RIGHT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ + 0); + /* + * The Steam Deck has a watchdog that automatically enables +@@ -365,9 +451,9 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + schedule_delayed_work(&steam->heartbeat, 5 * HZ); + } else { + steam_write_registers(steam, +- STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ +- STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */ +- STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ ++ STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x00, /* disable smooth */ ++ STEAM_REG_LEFT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ ++ STEAM_REG_RIGHT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ + 0); + } + } +@@ -747,7 +833,7 @@ static void steam_lizard_mode_heartbeat(struct work_struct *work) + if (!steam->client_opened && steam->client_hdev) { + steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); + steam_write_registers(steam, +- STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ ++ STEAM_REG_RIGHT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ + 0); + schedule_delayed_work(&steam->heartbeat, 5 * HZ); + } +-- +2.41.0 + + +From 4b1dd1ebfd2d3f123212e1296d304909e5b3a406 Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Wed, 16 Nov 2022 19:54:26 -0800 +Subject: [PATCH 04/10] HID: hid-steam: Add gamepad-only mode switched to by + holding options + +Signed-off-by: Vicki Pfau +--- + drivers/hid/hid-steam.c | 72 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 72 insertions(+) + +diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c +index 39a9bf3b7f77d..0620046b142ef 100644 +--- a/drivers/hid/hid-steam.c ++++ b/drivers/hid/hid-steam.c +@@ -202,6 +202,11 @@ static LIST_HEAD(steam_devices); + #define STEAM_TRACKPAD_NONE 0x07 + #define STEAM_TRACKPAD_GESTURE_KEYBOARD 0x08 + ++/* Pad identifiers for the deck */ ++#define STEAM_PAD_LEFT 0 ++#define STEAM_PAD_RIGHT 1 ++#define STEAM_PAD_BOTH 2 ++ + /* Other random constants */ + #define STEAM_SERIAL_LEN 10 + +@@ -221,6 +226,9 @@ struct steam_device { + u8 battery_charge; + u16 voltage; + struct delayed_work heartbeat; ++ struct delayed_work mode_switch; ++ bool did_mode_switch; ++ bool gamepad_mode; + struct work_struct rumble_work; + u16 rumble_left; + u16 rumble_right; +@@ -380,6 +388,33 @@ static inline int steam_request_conn_status(struct steam_device *steam) + return steam_send_report_byte(steam, STEAM_CMD_DONGLE_GET_STATE); + } + ++/* ++ * Send a haptic pulse to the trackpads ++ * Duration and interval are measured in microseconds, count is the number ++ * of pulses to send for duration time with interval microseconds between them ++ * and gain is measured in decibels, ranging from -24 to +6 ++ */ ++static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad, ++ u16 duration, u16 interval, u16 count, u8 gain) ++{ ++ u8 report[10] = {STEAM_CMD_HAPTIC_PULSE, 8}; ++ ++ /* Left and right are swapped on this report for legacy reasons */ ++ if (pad < STEAM_PAD_BOTH) ++ pad ^= 1; ++ ++ report[2] = pad; ++ report[3] = duration & 0xFF; ++ report[4] = duration >> 8; ++ report[5] = interval & 0xFF; ++ report[6] = interval >> 8; ++ report[7] = count & 0xFF; ++ report[8] = count >> 8; ++ report[9] = gain; ++ ++ return steam_send_report(steam, report, sizeof(report)); ++} ++ + static inline int steam_haptic_rumble(struct steam_device *steam, + u16 intensity, u16 left_speed, u16 right_speed, + u8 left_gain, u8 right_gain) +@@ -421,6 +456,9 @@ static int steam_play_effect(struct input_dev *dev, void *data, + + static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + { ++ if (steam->gamepad_mode) ++ enable = false; ++ + if (enable) { + /* enable esc, enter, cursors */ + steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS); +@@ -805,6 +843,29 @@ static void steam_work_connect_cb(struct work_struct *work) + } + } + ++static void steam_mode_switch_cb(struct work_struct *work) ++{ ++ struct steam_device *steam = container_of(to_delayed_work(work), ++ struct steam_device, mode_switch); ++ steam->gamepad_mode = !steam->gamepad_mode; ++ if (!lizard_mode) ++ return; ++ ++ mutex_lock(&steam->mutex); ++ if (steam->gamepad_mode) ++ steam_set_lizard_mode(steam, false); ++ else if (!steam->client_opened) ++ steam_set_lizard_mode(steam, lizard_mode); ++ mutex_unlock(&steam->mutex); ++ ++ steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0); ++ if (steam->gamepad_mode) { ++ steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x14D, 0x14D, 0x2D, 0); ++ } else { ++ steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x1F4, 0x1F4, 0x1E, 0); ++ } ++} ++ + static bool steam_is_valve_interface(struct hid_device *hdev) + { + struct hid_report_enum *rep_enum; +@@ -977,6 +1038,7 @@ static int steam_probe(struct hid_device *hdev, + mutex_init(&steam->mutex); + steam->quirks = id->driver_data; + INIT_WORK(&steam->work_connect, steam_work_connect_cb); ++ INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb); + INIT_LIST_HEAD(&steam->list); + INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat); + INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb); +@@ -1036,6 +1098,7 @@ static int steam_probe(struct hid_device *hdev, + client_hdev_fail: + cancel_work_sync(&steam->work_connect); + cancel_delayed_work_sync(&steam->heartbeat); ++ cancel_delayed_work_sync(&steam->mode_switch); + cancel_work_sync(&steam->rumble_work); + steam_alloc_fail: + hid_err(hdev, "%s: failed with error %d\n", +@@ -1059,6 +1122,7 @@ static void steam_remove(struct hid_device *hdev) + cancel_delayed_work_sync(&steam->heartbeat); + mutex_unlock(&steam->mutex); + cancel_work_sync(&steam->work_connect); ++ cancel_delayed_work_sync(&steam->mode_switch); + if (steam->quirks & STEAM_QUIRK_WIRELESS) { + hid_info(hdev, "Steam wireless receiver disconnected"); + } +@@ -1393,6 +1457,14 @@ static void steam_do_deck_input_event(struct steam_device *steam, + input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2))); + + input_sync(input); ++ ++ if (!(b9 & BIT(6)) && steam->did_mode_switch) { ++ steam->did_mode_switch = false; ++ cancel_delayed_work_sync(&steam->mode_switch); ++ } else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) { ++ steam->did_mode_switch = true; ++ schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100); ++ } + } + + /* +-- +2.41.0 + + +From 187582492c359d56865759f120214cfe6fa4ed50 Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Mon, 8 May 2023 20:24:56 -0700 +Subject: [PATCH 05/10] HID: hid-steam: Clean up locking + +This cleans up the locking logic so that the spinlock is consistently used for +access to a small handful of struct variables, and the mutex is exclusively and +consistently used for ensuring that mutliple threads aren't trying to +send/receive reports at the same time. Previously, only some report +transactions were guarded by this mutex, potentially breaking atomicity. The +mutex has been renamed to reflect this usage. + +Signed-off-by: Vicki Pfau +--- + drivers/hid/hid-steam.c | 148 ++++++++++++++++++++++++---------------- + 1 file changed, 90 insertions(+), 58 deletions(-) + +diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c +index 0620046b142ef..845ca71b8bd3a 100644 +--- a/drivers/hid/hid-steam.c ++++ b/drivers/hid/hid-steam.c +@@ -214,7 +214,7 @@ struct steam_device { + struct list_head list; + spinlock_t lock; + struct hid_device *hdev, *client_hdev; +- struct mutex mutex; ++ struct mutex report_mutex; + bool client_opened; + struct input_dev __rcu *input; + unsigned long quirks; +@@ -361,21 +361,26 @@ static int steam_get_serial(struct steam_device *steam) + * Send: 0xae 0x15 0x01 + * Recv: 0xae 0x15 0x01 serialnumber (10 chars) + */ +- int ret; ++ int ret = 0; + u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, 0x15, STEAM_ATTRIB_STR_UNIT_SERIAL}; + u8 reply[3 + STEAM_SERIAL_LEN + 1]; + ++ mutex_lock(&steam->report_mutex); + ret = steam_send_report(steam, cmd, sizeof(cmd)); + if (ret < 0) +- return ret; ++ goto out; + ret = steam_recv_report(steam, reply, sizeof(reply)); + if (ret < 0) +- return ret; +- if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != STEAM_ATTRIB_STR_UNIT_SERIAL) +- return -EIO; ++ goto out; ++ if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != STEAM_ATTRIB_STR_UNIT_SERIAL) { ++ ret = -EIO; ++ goto out; ++ } + reply[3 + STEAM_SERIAL_LEN] = 0; + strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no)); +- return 0; ++out: ++ mutex_unlock(&steam->report_mutex); ++ return ret; + } + + /* +@@ -385,7 +390,11 @@ static int steam_get_serial(struct steam_device *steam) + */ + static inline int steam_request_conn_status(struct steam_device *steam) + { +- return steam_send_report_byte(steam, STEAM_CMD_DONGLE_GET_STATE); ++ int ret; ++ mutex_lock(&steam->report_mutex); ++ ret = steam_send_report_byte(steam, STEAM_CMD_DONGLE_GET_STATE); ++ mutex_unlock(&steam->report_mutex); ++ return ret; + } + + /* +@@ -397,6 +406,7 @@ static inline int steam_request_conn_status(struct steam_device *steam) + static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad, + u16 duration, u16 interval, u16 count, u8 gain) + { ++ int ret; + u8 report[10] = {STEAM_CMD_HAPTIC_PULSE, 8}; + + /* Left and right are swapped on this report for legacy reasons */ +@@ -412,13 +422,17 @@ static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad, + report[8] = count >> 8; + report[9] = gain; + +- return steam_send_report(steam, report, sizeof(report)); ++ mutex_lock(&steam->report_mutex); ++ ret = steam_send_report(steam, report, sizeof(report)); ++ mutex_unlock(&steam->report_mutex); ++ return ret; + } + + static inline int steam_haptic_rumble(struct steam_device *steam, + u16 intensity, u16 left_speed, u16 right_speed, + u8 left_gain, u8 right_gain) + { ++ int ret; + u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9}; + + report[3] = intensity & 0xFF; +@@ -430,7 +444,10 @@ static inline int steam_haptic_rumble(struct steam_device *steam, + report[9] = left_gain; + report[10] = right_gain; + +- return steam_send_report(steam, report, sizeof(report)); ++ mutex_lock(&steam->report_mutex); ++ ret = steam_send_report(steam, report, sizeof(report)); ++ mutex_unlock(&steam->report_mutex); ++ return ret; + } + + static void steam_haptic_rumble_cb(struct work_struct *work) +@@ -460,6 +477,7 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + enable = false; + + if (enable) { ++ mutex_lock(&steam->report_mutex); + /* enable esc, enter, cursors */ + steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS); + /* enable mouse */ +@@ -467,9 +485,11 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + steam_write_registers(steam, + STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x01, /* enable smooth */ + 0); ++ mutex_unlock(&steam->report_mutex); + + cancel_delayed_work_sync(&steam->heartbeat); + } else { ++ mutex_lock(&steam->report_mutex); + /* disable esc, enter, cursor */ + steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); + +@@ -481,18 +501,19 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + STEAM_REG_LEFT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ + STEAM_REG_RIGHT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ + 0); ++ mutex_unlock(&steam->report_mutex); + /* + * The Steam Deck has a watchdog that automatically enables + * lizard mode if it doesn't see any traffic for too long + */ +- if (!work_busy(&steam->heartbeat.work)) +- schedule_delayed_work(&steam->heartbeat, 5 * HZ); ++ schedule_delayed_work(&steam->heartbeat, 5 * HZ); + } else { + steam_write_registers(steam, + STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x00, /* disable smooth */ + STEAM_REG_LEFT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ + STEAM_REG_RIGHT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ + 0); ++ mutex_unlock(&steam->report_mutex); + } + } + } +@@ -500,22 +521,29 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) + static int steam_input_open(struct input_dev *dev) + { + struct steam_device *steam = input_get_drvdata(dev); ++ unsigned long flags; ++ bool set_lizard_mode; + +- mutex_lock(&steam->mutex); +- if (!steam->client_opened && lizard_mode) ++ spin_lock_irqsave(&steam->lock, flags); ++ set_lizard_mode = !steam->client_opened && lizard_mode; ++ spin_unlock_irqrestore(&steam->lock, flags); ++ if (set_lizard_mode) + steam_set_lizard_mode(steam, false); +- mutex_unlock(&steam->mutex); ++ + return 0; + } + + static void steam_input_close(struct input_dev *dev) + { + struct steam_device *steam = input_get_drvdata(dev); ++ unsigned long flags; ++ bool set_lizard_mode; + +- mutex_lock(&steam->mutex); +- if (!steam->client_opened && lizard_mode) ++ spin_lock_irqsave(&steam->lock, flags); ++ set_lizard_mode = !steam->client_opened && lizard_mode; ++ spin_unlock_irqrestore(&steam->lock, flags); ++ if (set_lizard_mode) + steam_set_lizard_mode(steam, true); +- mutex_unlock(&steam->mutex); + } + + static enum power_supply_property steam_battery_props[] = { +@@ -760,6 +788,7 @@ static int steam_register(struct steam_device *steam) + { + int ret; + bool client_opened; ++ unsigned long flags; + + /* + * This function can be called several times in a row with the +@@ -772,11 +801,9 @@ static int steam_register(struct steam_device *steam) + * Unlikely, but getting the serial could fail, and it is not so + * important, so make up a serial number and go on. + */ +- mutex_lock(&steam->mutex); + if (steam_get_serial(steam) < 0) + strscpy(steam->serial_no, "XXXXXXXXXX", + sizeof(steam->serial_no)); +- mutex_unlock(&steam->mutex); + + hid_info(steam->hdev, "Steam Controller '%s' connected", + steam->serial_no); +@@ -791,11 +818,11 @@ static int steam_register(struct steam_device *steam) + mutex_unlock(&steam_devices_lock); + } + +- mutex_lock(&steam->mutex); ++ spin_lock_irqsave(&steam->lock, flags); + client_opened = steam->client_opened; ++ spin_unlock_irqrestore(&steam->lock, flags); + if (!client_opened) + steam_set_lizard_mode(steam, lizard_mode); +- mutex_unlock(&steam->mutex); + + if (!client_opened) + ret = steam_input_register(steam); +@@ -847,16 +874,21 @@ static void steam_mode_switch_cb(struct work_struct *work) + { + struct steam_device *steam = container_of(to_delayed_work(work), + struct steam_device, mode_switch); ++ unsigned long flags; ++ bool client_opened; + steam->gamepad_mode = !steam->gamepad_mode; + if (!lizard_mode) + return; + +- mutex_lock(&steam->mutex); + if (steam->gamepad_mode) + steam_set_lizard_mode(steam, false); +- else if (!steam->client_opened) +- steam_set_lizard_mode(steam, lizard_mode); +- mutex_unlock(&steam->mutex); ++ else { ++ spin_lock_irqsave(&steam->lock, flags); ++ client_opened = steam->client_opened; ++ spin_unlock_irqrestore(&steam->lock, flags); ++ if (!client_opened) ++ steam_set_lizard_mode(steam, lizard_mode); ++ } + + steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0); + if (steam->gamepad_mode) { +@@ -889,16 +921,21 @@ static void steam_lizard_mode_heartbeat(struct work_struct *work) + { + struct steam_device *steam = container_of(work, struct steam_device, + heartbeat.work); ++ bool client_opened; ++ unsigned long flags; + +- mutex_lock(&steam->mutex); +- if (!steam->client_opened && steam->client_hdev) { ++ spin_lock_irqsave(&steam->lock, flags); ++ client_opened = steam->client_opened; ++ spin_unlock_irqrestore(&steam->lock, flags); ++ if (!client_opened) { ++ mutex_lock(&steam->report_mutex); + steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); + steam_write_registers(steam, + STEAM_REG_RIGHT_TRACKPAD_MODE, STEAM_TRACKPAD_NONE, /* disable mouse */ + 0); ++ mutex_unlock(&steam->report_mutex); + schedule_delayed_work(&steam->heartbeat, 5 * HZ); + } +- mutex_unlock(&steam->mutex); + } + + static int steam_client_ll_parse(struct hid_device *hdev) +@@ -921,10 +958,11 @@ static void steam_client_ll_stop(struct hid_device *hdev) + static int steam_client_ll_open(struct hid_device *hdev) + { + struct steam_device *steam = hdev->driver_data; ++ unsigned long flags; + +- mutex_lock(&steam->mutex); ++ spin_lock_irqsave(&steam->lock, flags); + steam->client_opened = true; +- mutex_unlock(&steam->mutex); ++ spin_unlock_irqrestore(&steam->lock, flags); + + steam_input_unregister(steam); + +@@ -939,14 +977,12 @@ static void steam_client_ll_close(struct hid_device *hdev) + bool connected; + + spin_lock_irqsave(&steam->lock, flags); +- connected = steam->connected; ++ steam->client_opened = false; ++ connected = steam->connected && !steam->client_opened; + spin_unlock_irqrestore(&steam->lock, flags); + +- mutex_lock(&steam->mutex); +- steam->client_opened = false; + if (connected) + steam_set_lizard_mode(steam, lizard_mode); +- mutex_unlock(&steam->mutex); + + if (connected) + steam_input_register(steam); +@@ -1035,7 +1071,7 @@ static int steam_probe(struct hid_device *hdev, + steam->hdev = hdev; + hid_set_drvdata(hdev, steam); + spin_lock_init(&steam->lock); +- mutex_init(&steam->mutex); ++ mutex_init(&steam->report_mutex); + steam->quirks = id->driver_data; + INIT_WORK(&steam->work_connect, steam_work_connect_cb); + INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb); +@@ -1043,13 +1079,6 @@ static int steam_probe(struct hid_device *hdev, + INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat); + INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb); + +- steam->client_hdev = steam_create_client_hid(hdev); +- if (IS_ERR(steam->client_hdev)) { +- ret = PTR_ERR(steam->client_hdev); +- goto client_hdev_fail; +- } +- steam->client_hdev->driver_data = steam; +- + /* + * With the real steam controller interface, do not connect hidraw. + * Instead, create the client_hid and connect that. +@@ -1058,10 +1087,6 @@ static int steam_probe(struct hid_device *hdev, + if (ret) + goto hid_hw_start_fail; + +- ret = hid_add_device(steam->client_hdev); +- if (ret) +- goto client_hdev_add_fail; +- + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, +@@ -1087,15 +1112,26 @@ static int steam_probe(struct hid_device *hdev, + } + } + ++ steam->client_hdev = steam_create_client_hid(hdev); ++ if (IS_ERR(steam->client_hdev)) { ++ ret = PTR_ERR(steam->client_hdev); ++ goto client_hdev_fail; ++ } ++ steam->client_hdev->driver_data = steam; ++ ++ ret = hid_add_device(steam->client_hdev); ++ if (ret) ++ goto client_hdev_add_fail; ++ + return 0; + +-input_register_fail: +-hid_hw_open_fail: + client_hdev_add_fail: + hid_hw_stop(hdev); +-hid_hw_start_fail: +- hid_destroy_device(steam->client_hdev); + client_hdev_fail: ++ hid_destroy_device(steam->client_hdev); ++input_register_fail: ++hid_hw_open_fail: ++hid_hw_start_fail: + cancel_work_sync(&steam->work_connect); + cancel_delayed_work_sync(&steam->heartbeat); + cancel_delayed_work_sync(&steam->mode_switch); +@@ -1115,14 +1151,12 @@ static void steam_remove(struct hid_device *hdev) + return; + } + ++ cancel_delayed_work_sync(&steam->heartbeat); ++ cancel_delayed_work_sync(&steam->mode_switch); ++ cancel_work_sync(&steam->work_connect); + hid_destroy_device(steam->client_hdev); +- mutex_lock(&steam->mutex); + steam->client_hdev = NULL; + steam->client_opened = false; +- cancel_delayed_work_sync(&steam->heartbeat); +- mutex_unlock(&steam->mutex); +- cancel_work_sync(&steam->work_connect); +- cancel_delayed_work_sync(&steam->mode_switch); + if (steam->quirks & STEAM_QUIRK_WIRELESS) { + hid_info(hdev, "Steam wireless receiver disconnected"); + } +@@ -1597,10 +1631,8 @@ static int steam_param_set_lizard_mode(const char *val, + + mutex_lock(&steam_devices_lock); + list_for_each_entry(steam, &steam_devices, list) { +- mutex_lock(&steam->mutex); + if (!steam->client_opened) + steam_set_lizard_mode(steam, lizard_mode); +- mutex_unlock(&steam->mutex); + } + mutex_unlock(&steam_devices_lock); + return 0; +-- +2.41.0 + + +From d4490c88bed06b4c18af4a6029d67374df5218e1 Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Wed, 10 May 2023 17:27:12 -0700 +Subject: [PATCH 06/10] HID: hid-steam: Make client_opened a counter + +The client_opened variable was used to track if the hidraw was opened by any +clients to silence keyboard/mouse events while opened. However, there was no +counting of how many clients were opened, so opening two at the same time and +then closing one would fool the driver into thinking it had no remaining opened +clients. + +Signed-off-by: Vicki Pfau +--- + drivers/hid/hid-steam.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c +index 845ca71b8bd3a..0c2fe51b29bc1 100644 +--- a/drivers/hid/hid-steam.c ++++ b/drivers/hid/hid-steam.c +@@ -215,7 +215,7 @@ struct steam_device { + spinlock_t lock; + struct hid_device *hdev, *client_hdev; + struct mutex report_mutex; +- bool client_opened; ++ unsigned long client_opened; + struct input_dev __rcu *input; + unsigned long quirks; + struct work_struct work_connect; +@@ -787,7 +787,7 @@ static void steam_battery_unregister(struct steam_device *steam) + static int steam_register(struct steam_device *steam) + { + int ret; +- bool client_opened; ++ unsigned long client_opened; + unsigned long flags; + + /* +@@ -961,7 +961,7 @@ static int steam_client_ll_open(struct hid_device *hdev) + unsigned long flags; + + spin_lock_irqsave(&steam->lock, flags); +- steam->client_opened = true; ++ steam->client_opened++; + spin_unlock_irqrestore(&steam->lock, flags); + + steam_input_unregister(steam); +@@ -977,7 +977,7 @@ static void steam_client_ll_close(struct hid_device *hdev) + bool connected; + + spin_lock_irqsave(&steam->lock, flags); +- steam->client_opened = false; ++ steam->client_opened--; + connected = steam->connected && !steam->client_opened; + spin_unlock_irqrestore(&steam->lock, flags); + +@@ -1156,7 +1156,7 @@ static void steam_remove(struct hid_device *hdev) + cancel_work_sync(&steam->work_connect); + hid_destroy_device(steam->client_hdev); + steam->client_hdev = NULL; +- steam->client_opened = false; ++ steam->client_opened = 0; + if (steam->quirks & STEAM_QUIRK_WIRELESS) { + hid_info(hdev, "Steam wireless receiver disconnected"); + } +-- +2.41.0 + + +From 58a8667b251984ecc85a503c5dec3fc8f98028ff Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Thu, 18 May 2023 18:00:35 -0700 +Subject: [PATCH 07/10] HID: hid-steam: Better handling of serial number length + +The second byte of the GET_STRING_ATTRIB report is a length, so we should set +the size of the buffer to be the size we're actually requesting, and only +reject the reply if the length out is nonsensical. + +Signed-off-by: Vicki Pfau +--- + drivers/hid/hid-steam.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c +index 0c2fe51b29bc1..92e3e1052fa42 100644 +--- a/drivers/hid/hid-steam.c ++++ b/drivers/hid/hid-steam.c +@@ -208,7 +208,7 @@ static LIST_HEAD(steam_devices); + #define STEAM_PAD_BOTH 2 + + /* Other random constants */ +-#define STEAM_SERIAL_LEN 10 ++#define STEAM_SERIAL_LEN 0x15 + + struct steam_device { + struct list_head list; +@@ -359,10 +359,10 @@ static int steam_get_serial(struct steam_device *steam) + { + /* + * Send: 0xae 0x15 0x01 +- * Recv: 0xae 0x15 0x01 serialnumber (10 chars) ++ * Recv: 0xae 0x15 0x01 serialnumber + */ + int ret = 0; +- u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, 0x15, STEAM_ATTRIB_STR_UNIT_SERIAL}; ++ u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, sizeof(steam->serial_no), STEAM_ATTRIB_STR_UNIT_SERIAL}; + u8 reply[3 + STEAM_SERIAL_LEN + 1]; + + mutex_lock(&steam->report_mutex); +@@ -372,12 +372,12 @@ static int steam_get_serial(struct steam_device *steam) + ret = steam_recv_report(steam, reply, sizeof(reply)); + if (ret < 0) + goto out; +- if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != STEAM_ATTRIB_STR_UNIT_SERIAL) { ++ if (reply[0] != 0xae || reply[1] < 1 || reply[1] > sizeof(steam->serial_no) || reply[2] != STEAM_ATTRIB_STR_UNIT_SERIAL) { + ret = -EIO; + goto out; + } + reply[3 + STEAM_SERIAL_LEN] = 0; +- strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no)); ++ strscpy(steam->serial_no, reply + 3, reply[1]); + out: + mutex_unlock(&steam->report_mutex); + return ret; +-- +2.41.0 + + +From 7460867bd78651a6187ac44c73d1be653c09973b Mon Sep 17 00:00:00 2001 +From: Vicki Pfau +Date: Fri, 24 Mar 2023 10:42:27 -0700 +Subject: [PATCH 08/10] Input: xpad - fix support for some third-party + controllers + +Some third-party controllers, such as the HORPIAD FPS for Nintendo Switch and +Gamesir-G3w, require a specific packet that the first-party XInput driver sends +before it will start sending reports. It's not currently known what this packet +does, but since the first-party driver always sends it's unlikely that this +could cause issues with existing controllers. + +Co-authored-by: Andrey Smirnov +Signed-off-by: Vicki Pfau +Link: https://lore.kernel.org/r/20230324040446.3487725-3-vi@endrift.com +Signed-off-by: Dmitry Torokhov +--- + drivers/input/joystick/xpad.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c +index cdb193317c3b6..fc680b45f936e 100644 +--- a/drivers/input/joystick/xpad.c ++++ b/drivers/input/joystick/xpad.c +@@ -264,6 +264,7 @@ static const struct xpad_device { + { 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE }, + { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, + { 0x0f0d, 0x00c5, "Hori Fighting Commander ONE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, ++ { 0x0f0d, 0x00dc, "HORIPAD FPS for Nintendo Switch", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX }, + { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, + { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, +@@ -1720,6 +1721,27 @@ static int xpad_start_input(struct usb_xpad *xpad) + return error; + } + } ++ if (xpad->xtype == XTYPE_XBOX360) { ++ /* ++ * Some third-party controllers Xbox 360-style controllers ++ * require this message to finish initialization. ++ */ ++ u8 dummy[20]; ++ ++ error = usb_control_msg_recv(xpad->udev, 0, ++ /* bRequest */ 0x01, ++ /* bmRequestType */ ++ USB_TYPE_VENDOR | USB_DIR_IN | ++ USB_RECIP_INTERFACE, ++ /* wValue */ 0x100, ++ /* wIndex */ 0x00, ++ dummy, sizeof(dummy), ++ 25, GFP_KERNEL); ++ if (error) ++ dev_warn(&xpad->dev->dev, ++ "unable to receive magic message: %d\n", ++ error); ++ } + + return 0; + } +-- +2.41.0 + + +From 469ab7efd0383f60e83c086347526273ed1d1a33 Mon Sep 17 00:00:00 2001 +From: Timothee Besset +Date: Mon, 22 May 2023 20:25:57 -0500 +Subject: [PATCH 09/10] Input: xpad - Add GameSir VID for Xbox One controllers + +Co-authored-by: Sam Lantinga +Signed-off-by: Sam Lantinga +Signed-off-by: Vicki Pfau +--- + drivers/input/joystick/xpad.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c +index fc680b45f936e..bb2f69faa2a81 100644 +--- a/drivers/input/joystick/xpad.c ++++ b/drivers/input/joystick/xpad.c +@@ -500,6 +500,7 @@ static const struct usb_device_id xpad_table[] = { + XPAD_XBOX360_VENDOR(0x2f24), /* GameSir controllers */ + XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */ + XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */ ++ XPAD_XBOXONE_VENDOR(0x3537), /* GameSir Controllers */ + { } + }; + +-- +2.41.0 + + +From 4fd74c574f8554056facabd4e36e5e397f2e6b98 Mon Sep 17 00:00:00 2001 +From: Jonathan Frederick +Date: Fri, 7 Jul 2023 15:11:33 -0700 +Subject: [PATCH 10/10] Input: xpad - add GameSir T4 Kaleid Controller support + +Add VID and PID to the xpad_device table to allow driver +to use the GameSir T4 Kaleid Controller, which is +XTYPE_XBOX360 compatible in xinput mode. + +Signed-off-by: Jonathan Frederick +Link: https://lore.kernel.org/r/ZKeKSbP3faIPv5jB@dbj-hp-flip +Signed-off-by: Dmitry Torokhov +--- + drivers/input/joystick/xpad.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c +index bb2f69faa2a81..ede380551e55c 100644 +--- a/drivers/input/joystick/xpad.c ++++ b/drivers/input/joystick/xpad.c +@@ -366,6 +366,7 @@ static const struct xpad_device { + { 0x31e3, 0x1300, "Wooting 60HE (AVR)", 0, XTYPE_XBOX360 }, + { 0x31e3, 0x1310, "Wooting 60HE (ARM)", 0, XTYPE_XBOX360 }, + { 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 }, ++ { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, + { 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX }, + { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, + { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } +@@ -500,6 +501,7 @@ static const struct usb_device_id xpad_table[] = { + XPAD_XBOX360_VENDOR(0x2f24), /* GameSir controllers */ + XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */ + XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */ ++ XPAD_XBOX360_VENDOR(0x3537), /* GameSir Controllers */ + XPAD_XBOXONE_VENDOR(0x3537), /* GameSir Controllers */ + { } + }; +-- +2.41.0 + + -- cgit v1.2.3