From 3d19f5a05b59ccdcd0178051967953f76d4e0ef5 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Mon, 16 Sep 2019 04:53:20 +0200 Subject: [PATCH 01/27] ZEN: Add sysctl and CONFIG to disallow unprivileged CLONE_NEWUSER Our default behavior continues to match the vanilla kernel. --- include/linux/user_namespace.h | 4 ++++ init/Kconfig | 16 ++++++++++++++++ kernel/fork.c | 14 ++++++++++++++ kernel/sysctl.c | 12 ++++++++++++ kernel/user_namespace.c | 7 +++++++ 5 files changed, 53 insertions(+) diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 45f09bec02c485..87b20e2ee27445 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -148,6 +148,8 @@ static inline void set_userns_rlimit_max(struct user_namespace *ns, #ifdef CONFIG_USER_NS +extern int unprivileged_userns_clone; + static inline struct user_namespace *get_user_ns(struct user_namespace *ns) { if (ns) @@ -181,6 +183,8 @@ extern bool current_in_userns(const struct user_namespace *target_ns); struct ns_common *ns_get_owner(struct ns_common *ns); #else +#define unprivileged_userns_clone 0 + static inline struct user_namespace *get_user_ns(struct user_namespace *ns) { return &init_user_ns; diff --git a/init/Kconfig b/init/Kconfig index 6d35728b94b2b3..0562c8ca7b308d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1226,6 +1226,22 @@ config USER_NS If unsure, say N. +config USER_NS_UNPRIVILEGED + bool "Allow unprivileged users to create namespaces" + default y + depends on USER_NS + help + When disabled, unprivileged users will not be able to create + new namespaces. Allowing users to create their own namespaces + has been part of several recent local privilege escalation + exploits, so if you need user namespaces but are + paranoid^Wsecurity-conscious you want to disable this. + + This setting can be overridden at runtime via the + kernel.unprivileged_userns_clone sysctl. + + If unsure, say Y. + config PID_NS bool "PID Namespaces" default y diff --git a/kernel/fork.c b/kernel/fork.c index 3b6d20dfb9a85e..200a77738a803a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -100,6 +100,10 @@ #include #include +#ifdef CONFIG_USER_NS +#include +#endif + #include #include #include @@ -2260,6 +2264,10 @@ __latent_entropy struct task_struct *copy_process( if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS)) return ERR_PTR(-EINVAL); + if ((clone_flags & CLONE_NEWUSER) && !unprivileged_userns_clone) + if (!capable(CAP_SYS_ADMIN)) + return ERR_PTR(-EPERM); + /* * Thread groups must share signals as well, and detached threads * can only be started up within the thread group. @@ -3413,6 +3421,12 @@ int ksys_unshare(unsigned long unshare_flags) if (unshare_flags & CLONE_NEWNS) unshare_flags |= CLONE_FS; + if ((unshare_flags & CLONE_NEWUSER) && !unprivileged_userns_clone) { + err = -EPERM; + if (!capable(CAP_SYS_ADMIN)) + goto bad_unshare_out; + } + err = check_unshare_flags(unshare_flags); if (err) goto bad_unshare_out; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 354a2d294f526a..5bc5605e7cdbcd 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -80,6 +80,9 @@ #ifdef CONFIG_RT_MUTEXES #include #endif +#ifdef CONFIG_USER_NS +#include +#endif /* shared constants to be used in various sysctls */ const int sysctl_vals[] = { 0, 1, 2, 3, 4, 100, 200, 1000, 3000, INT_MAX, 65535, -1 }; @@ -1623,6 +1626,15 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#ifdef CONFIG_USER_NS + { + .procname = "unprivileged_userns_clone", + .data = &unprivileged_userns_clone, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, +#endif #ifdef CONFIG_PROC_SYSCTL { .procname = "tainted", diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 1d8e47bed3f118..fec01d016a351f 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -22,6 +22,13 @@ #include #include +/* sysctl */ +#ifdef CONFIG_USER_NS_UNPRIVILEGED +int unprivileged_userns_clone = 1; +#else +int unprivileged_userns_clone; +#endif + static struct kmem_cache *user_ns_cachep __read_mostly; static DEFINE_MUTEX(userns_state_mutex); From 9d57f6549aa84b0b631847507b0edda8f74df895 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 19 May 2022 14:40:07 +0200 Subject: [PATCH 02/27] drivers/firmware: skip simpledrm if nvidia-drm.modeset=1 is set The Nvidia proprietary driver has some bugs that leads to issues if used with the simpledrm driver. The most noticeable is that does not register an emulated fbdev device. It just relies on a fbdev to be registered by another driver, that could be that could be attached to the framebuffer console. On UEFI machines, this is the efifb driver. This means that disabling the efifb driver will cause virtual consoles to not be present in the system when using the Nvidia driver. Legacy BIOS is not affected just because fbcon is not used there, but instead vgacon. Unless a VGA mode is specified using the vga= kernel command line option, in that case the vesafb driver is used instead and its fbdev attached to the fbcon. This is a problem because with CONFIG_SYSFB_SIMPLEFB=y, the sysfb platform code attempts to register a "simple-framebuffer" platform device (that is matched against simpledrm) and only registers either an "efi-framebuffer" or "vesa-framebuffer" if this fails to be registered due the video modes not being compatible. The Nvidia driver relying on another driver to register the fbdev is quite fragile, since it can't really assume those will stick around. For example there are patches posted to remove the EFI and VESA platform devices once a real DRM or fbdev driver probes. But in any case, moving to a simpledrm + emulated fbdev only breaks this assumption and causes users to not have VT if the Nvidia driver is used. So to prevent this, let's add a workaround and make the sysfb to skip the "simple-framebuffer" registration when nvidia-drm.modeset=1 option is set. This is quite horrible, but honestly I can't think of any other approach. For this to work, the CONFIG_FB_EFI and CONFIG_FB_VESA config options must be enabled besides CONFIG_DRM_SIMPLEDRM. Signed-off-by: Javier Martinez Canillas Cherry-picked-for: https://bugs.archlinux.org/task/73720 --- drivers/firmware/sysfb.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 82fcfd29bc4d29..17b7e096b6828b 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -34,6 +34,22 @@ #include #include +static int skip_simpledrm; + +static int __init simpledrm_disable(char *opt) +{ + if (!opt) + return -EINVAL; + + get_option(&opt, &skip_simpledrm); + + if (skip_simpledrm) + pr_info("The simpledrm driver will not be probed\n"); + + return 0; +} +early_param("nvidia-drm.modeset", simpledrm_disable); + static struct platform_device *pd; static DEFINE_MUTEX(disable_lock); static bool disabled; @@ -85,7 +101,7 @@ static __init int sysfb_init(void) /* try to create a simple-framebuffer device */ compatible = sysfb_parse_mode(si, &mode); - if (compatible) { + if (compatible && !skip_simpledrm) { pd = sysfb_create_simplefb(si, &mode); if (!IS_ERR(pd)) goto unlock_mutex; From d9620fd9e4a3480ed0e1d00bb9097df5acedf262 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 May 2020 14:37:44 +0300 Subject: [PATCH 03/27] mm: Support soft dirty flag reset for VA range. --- fs/proc/task_mmu.c | 129 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 3dd5be96691b4c..f10c34ecaa248f 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -1072,6 +1072,8 @@ enum clear_refs_types { struct clear_refs_private { enum clear_refs_types type; + unsigned long start, end; + bool clear_range; }; #ifdef CONFIG_MEM_SOFT_DIRTY @@ -1163,6 +1165,8 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, spinlock_t *ptl; struct page *page; + BUG_ON(addr < cp->start || end > cp->end); + ptl = pmd_trans_huge_lock(pmd, vma); if (ptl) { if (cp->type == CLEAR_REFS_SOFT_DIRTY) { @@ -1220,9 +1224,11 @@ static int clear_refs_test_walk(unsigned long start, unsigned long end, struct clear_refs_private *cp = walk->private; struct vm_area_struct *vma = walk->vma; - if (vma->vm_flags & VM_PFNMAP) + if (!cp->clear_range && (vma->vm_flags & VM_PFNMAP)) return 1; + BUG_ON(start < cp->start || end > cp->end); + /* * Writing 1 to /proc/pid/clear_refs affects all pages. * Writing 2 to /proc/pid/clear_refs only affects anonymous pages. @@ -1246,10 +1252,12 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct task_struct *task; - char buffer[PROC_NUMBUF]; + char buffer[18]; struct mm_struct *mm; struct vm_area_struct *vma; enum clear_refs_types type; + unsigned long start, end; + bool clear_range; int itype; int rv; @@ -1258,12 +1266,34 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; - rv = kstrtoint(strstrip(buffer), 10, &itype); - if (rv < 0) - return rv; - type = (enum clear_refs_types)itype; - if (type < CLEAR_REFS_ALL || type >= CLEAR_REFS_LAST) - return -EINVAL; + + if (buffer[0] == '6') + { + static int once; + + if (!once++) + printk(KERN_DEBUG "task_mmu: Using POC clear refs range implementation.\n"); + + if (count != 17) + return -EINVAL; + + type = CLEAR_REFS_SOFT_DIRTY; + start = *(unsigned long *)(buffer + 1); + end = *(unsigned long *)(buffer + 1 + 8); + } + else + { + rv = kstrtoint(strstrip(buffer), 10, &itype); + if (rv < 0) + return rv; + type = (enum clear_refs_types)itype; + + if (type < CLEAR_REFS_ALL || type >= CLEAR_REFS_LAST) + return -EINVAL; + + start = 0; + end = -1UL; + } task = get_proc_task(file_inode(file)); if (!task) @@ -1276,40 +1306,86 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, .type = type, }; - if (mmap_write_lock_killable(mm)) { - count = -EINTR; - goto out_mm; + if (start || end != -1UL) + { + start = min(start, -1) & PAGE_MASK; + end = min(end, -1) & PAGE_MASK; + + if (start >= end) + { + count = -EINVAL; + goto out_mm; + } + clear_range = true; } + else + { + clear_range = false; + } + + cp.start = start; + cp.end = end; + cp.clear_range = clear_range; + if (type == CLEAR_REFS_MM_HIWATER_RSS) { + if (mmap_write_lock_killable(mm)) { + count = -EINTR; + goto out_mm; + } + /* * Writing 5 to /proc/pid/clear_refs resets the peak * resident set size to this mm's current rss value. */ reset_mm_hiwater_rss(mm); - goto out_unlock; + mmap_write_unlock(mm); + goto out_mm; } if (type == CLEAR_REFS_SOFT_DIRTY) { - for_each_vma(vmi, vma) { - if (!(vma->vm_flags & VM_SOFTDIRTY)) - continue; - vm_flags_clear(vma, VM_SOFTDIRTY); - vma_set_page_prot(vma); + if (mmap_read_lock_killable(mm)) { + count = -EINTR; + goto out_mm; } - + if (!clear_range) + for_each_vma(vmi, vma) { + if (!(vma->vm_flags & VM_SOFTDIRTY)) + continue; + mmap_read_unlock(mm); + if (mmap_write_lock_killable(mm)) { + count = -EINTR; + goto out_mm; + } + for_each_vma(vmi, vma) { + vm_flags_clear(vma, VM_SOFTDIRTY); + vma_set_page_prot(vma); + } + mmap_write_downgrade(mm); + break; + } inc_tlb_flush_pending(mm); mmu_notifier_range_init(&range, MMU_NOTIFY_SOFT_DIRTY, - 0, mm, 0, -1UL); + 0, mm, start, end); mmu_notifier_invalidate_range_start(&range); } - walk_page_range(mm, 0, -1, &clear_refs_walk_ops, &cp); + else + { + if (mmap_write_lock_killable(mm)) { + count = -EINTR; + goto out_mm; + } + } + walk_page_range(mm, start, end == -1UL ? -1 : end, &clear_refs_walk_ops, &cp); if (type == CLEAR_REFS_SOFT_DIRTY) { mmu_notifier_invalidate_range_end(&range); flush_tlb_mm(mm); dec_tlb_flush_pending(mm); + mmap_read_unlock(mm); + } + else + { + mmap_write_unlock(mm); } -out_unlock: - mmap_write_unlock(mm); out_mm: mmput(mm); } @@ -1341,6 +1417,7 @@ struct pagemapread { #define PM_PFRAME_MASK GENMASK_ULL(PM_PFRAME_BITS - 1, 0) #define PM_SOFT_DIRTY BIT_ULL(55) #define PM_MMAP_EXCLUSIVE BIT_ULL(56) +#define PM_SOFT_DIRTY_PAGE BIT_ULL(57) #define PM_UFFD_WP BIT_ULL(57) #define PM_FILE BIT_ULL(61) #define PM_SWAP BIT_ULL(62) @@ -1415,13 +1492,13 @@ static pagemap_entry_t pte_to_pagemap_entry(struct pagemapread *pm, flags |= PM_PRESENT; page = vm_normal_page(vma, addr, pte); if (pte_soft_dirty(pte)) - flags |= PM_SOFT_DIRTY; + flags |= PM_SOFT_DIRTY | PM_SOFT_DIRTY_PAGE; if (pte_uffd_wp(pte)) flags |= PM_UFFD_WP; } else if (is_swap_pte(pte)) { swp_entry_t entry; if (pte_swp_soft_dirty(pte)) - flags |= PM_SOFT_DIRTY; + flags |= PM_SOFT_DIRTY | PM_SOFT_DIRTY_PAGE; if (pte_swp_uffd_wp(pte)) flags |= PM_UFFD_WP; entry = pte_to_swp_entry(pte); @@ -1481,7 +1558,7 @@ static int pagemap_pmd_range(pmd_t *pmdp, unsigned long addr, unsigned long end, flags |= PM_PRESENT; if (pmd_soft_dirty(pmd)) - flags |= PM_SOFT_DIRTY; + flags |= PM_SOFT_DIRTY | PM_SOFT_DIRTY_PAGE; if (pmd_uffd_wp(pmd)) flags |= PM_UFFD_WP; if (pm->show_pfn) @@ -1505,7 +1582,7 @@ static int pagemap_pmd_range(pmd_t *pmdp, unsigned long addr, unsigned long end, } flags |= PM_SWAP; if (pmd_swp_soft_dirty(pmd)) - flags |= PM_SOFT_DIRTY; + flags |= PM_SOFT_DIRTY | PM_SOFT_DIRTY_PAGE; if (pmd_swp_uffd_wp(pmd)) flags |= PM_UFFD_WP; VM_BUG_ON(!is_pmd_migration_entry(pmd)); From 1eb9656dbe58763f6bdc1910c72e487644c7196e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20Ignacio=20Aramend=C3=ADa?= Date: Wed, 21 Jun 2023 18:22:19 -0300 Subject: [PATCH 04/27] drm: panel-orientation-quirks: Add quirk for AYA NEO 2 model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add quirk orientation for AYA NEO 2. The name appears without spaces in dmi strings. That made it difficult to reuse the 2021 match and the display is greater in resolution. Tested by the JELOS team that has been patching their own kernel for a while now and confirmed by users in the AYA NEO and ChimeraOS discord servers. Signed-off-by: Joaquín Ignacio Aramendía --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index d5c15292ae9378..df8e385ed6fcae 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -178,6 +178,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* AYA NEO AYANEO 2 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "AYANEO 2"), + }, + .driver_data = (void *)&lcd1200x1920_rightside_up, }, { /* AYA NEO 2021 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYADEVICE"), From ce95863836c965e0228fbf889ba23471552bf633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20Ignacio=20Aramend=C3=ADa?= Date: Wed, 21 Jun 2023 18:40:10 -0300 Subject: [PATCH 05/27] drm: panel-orientation-quirks: Add quirk for AYA NEO Founder edition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add quirk orientation for AYA NEO Founder. The name appears with spaces in dmi strings as other devices of the brand. The panel is the same as the NEXT and 2021 models. Those could not be reused as the former has VENDOR name as "AYANEO" without spaces and the latter has "AYADEVICE". Tested by the JELOS team that has been patching their own kernel for a while now and confirmed by users in the AYA NEO and ChimeraOS discord servers. Signed-off-by: Joaquín Ignacio Aramendía --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index df8e385ed6fcae..0f51e5c4d5287b 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -196,6 +196,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_PRODUCT_NAME, "AIR"), }, .driver_data = (void *)&lcd1080x1920_leftside_up, + }, { /* AYA NEO Founder */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYA NEO"), + DMI_MATCH(DMI_PRODUCT_NAME, "AYA NEO Founder"), + }, + .driver_data = (void *)&lcd800x1280_rightside_up, }, { /* AYA NEO NEXT */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AYANEO"), From 3b6d29c4f0237920e9c4d8b573c21b0922ce5940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20Ignacio=20Aramend=C3=ADa?= Date: Wed, 21 Jun 2023 18:54:44 -0300 Subject: [PATCH 06/27] drm: panel-orientation-quirks: Add quirk for AYA NEO GEEK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add quirk orientation for AYA NEO GEEK. One of the more recent devices by the brand. The name appears without spaces in dmi strings. The board name is completely different to the previous models making it difficult to reuse their quirks despite being the same resolution and mounting. Tested by the JELOS team that has been patching their own kernel for a while now and confirmed by users in the AYA NEO and ChimeraOS discord servers. Signed-off-by: Joaquín Ignacio Aramendía --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 0f51e5c4d5287b..b02c3d7c4e6ccd 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -202,6 +202,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_PRODUCT_NAME, "AYA NEO Founder"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* AYA NEO GEEK */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), + DMI_MATCH(DMI_PRODUCT_NAME, "GEEK"), + }, + .driver_data = (void *)&lcd800x1280_rightside_up, }, { /* AYA NEO NEXT */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AYANEO"), From 2cc14fb03d65d8b695aa4bb83ba5d49f8f0f7b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20Ignacio=20Aramend=C3=ADa?= Date: Wed, 12 Jul 2023 16:12:55 -0300 Subject: [PATCH 07/27] usb: Add hid_id and keys for ASUS ROG ALLY handheld --- drivers/hid/hid-asus.c | 9 ++++++++- drivers/hid/hid-ids.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index fd61dba882338e..37bb10ab975a00 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -897,6 +897,10 @@ static int asus_input_mapping(struct hid_device *hdev, case 0xb3: asus_map_key_clear(KEY_PROG3); break; /* Fn+Left next aura */ case 0x6a: asus_map_key_clear(KEY_F13); break; /* Screenpad toggle */ case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle */ + case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally left back */ + case 0xa6: asus_map_key_clear(KEY_F16); break; /* ROG Ally QAM button */ + case 0xa7: asus_map_key_clear(KEY_F17); break; /* ROG Ally ROG long-press */ + case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG long-press-release */ default: @@ -1258,6 +1262,9 @@ static const struct hid_device_id asus_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD), QUIRK_ROG_CLAYMORE_II_KEYBOARD }, @@ -1300,4 +1307,4 @@ static struct hid_driver asus_driver = { }; module_hid_driver(asus_driver); -MODULE_LICENSE("GPL"); \ No newline at end of file +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e4d2dfd5d2536e..7e1a119d2751f3 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -208,6 +208,7 @@ #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30 +#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe #define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 From 3065c266fccb7e50aa249b67a4f0637708c2c28c Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Wed, 23 Aug 2023 11:05:59 +1200 Subject: [PATCH 08/27] ALSA: hda: cs35l41: Support ASUS 2023 laptops with missing DSD Support adding the missing DSD properties required for ASUS ROG 2023 laptops and other ASUS laptops to properly utilise the cs35l41. The currently added laptops are: - ASUS GS650P, i2c - ASUS GA402X, i2c - ASUS GU604V, spi - ASUS GU603V, spi - ASUS GV601V, spi - ASUS GZ301V, spi - ASUS ROG ALLY, i2c - ASUS G614J, spi - ASUS G634J, spi - ASUS G614JI, spi - ASUS G713P, i2c The SPI connected amps may be required to use an external DSD patch to fix or add the "cs-gpios" property. Co-developed-by: Jonathan LoBue Signed-off-by: Jonathan LoBue Co-developed-by: Luke D. Jones Signed-off-by: Luke D. Jones --- sound/pci/hda/cs35l41_hda_property.c | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index b62a4e6968e269..6fc2b790d0a3f7 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -74,6 +74,49 @@ static int hp_vision_acpi_fix(struct cs35l41_hda *cs35l41, struct device *physde return 0; } +static int asus_rog_2023_i2c_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; + hw_cfg->valid = true; + + return 0; +} + +/* + * Some SPI connected versions may be missing a chip select GPIO and require a DSD table patch. + */ +static int asus_rog_2023_spi_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + /* check SPI address to assign the index */ + cs35l41->index = id == 0 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->bst_type = CS35L41_EXT_BOOST; + hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; + hw_cfg->gpio1.valid = true; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->valid = true; + + return 0; +} + struct cs35l41_prop_model { const char *hid; const char *ssid; @@ -85,6 +128,17 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CLSA0100", NULL, lenovo_legion_no_acpi }, { "CLSA0101", NULL, lenovo_legion_no_acpi }, { "CSC3551", "103C89C6", hp_vision_acpi_fix }, + { "CSC3551", "10431433", asus_rog_2023_i2c_no_acpi }, // ASUS GS650P + { "CSC3551", "10431463", asus_rog_2023_i2c_no_acpi }, // ASUS GA402X + { "CSC3551", "10431473", asus_rog_2023_spi_no_acpi }, // ASUS GU604V + { "CSC3551", "10431483", asus_rog_2023_spi_no_acpi }, // ASUS GU603V + { "CSC3551", "10431493", asus_rog_2023_spi_no_acpi }, // ASUS GV601V + { "CSC3551", "10431573", asus_rog_2023_spi_no_acpi }, // ASUS GZ301V + { "CSC3551", "104317F3", asus_rog_2023_i2c_no_acpi }, // ASUS ROG ALLY + { "CSC3551", "10431B93", asus_rog_2023_spi_no_acpi }, // ASUS G614J + { "CSC3551", "10431CAF", asus_rog_2023_spi_no_acpi }, // ASUS G634J + { "CSC3551", "10431C9F", asus_rog_2023_spi_no_acpi }, // ASUS G614JI + { "CSC3551", "10431D1F", asus_rog_2023_i2c_no_acpi }, // ASUS G713P {} }; From f4b10be75f413842de58a43977c0c23dfa6e1d2b Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Mon, 23 Oct 2023 21:35:46 +0200 Subject: [PATCH 09/27] ROG ally bios check DSD override only for bios <330 --- sound/pci/hda/cs35l41_hda_property.c | 38 +++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index 6fc2b790d0a3f7..f667e5b436d977 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -6,7 +6,9 @@ // // Author: Stefan Binding +#include #include +#include #include #include "cs35l41_hda_property.h" @@ -74,6 +76,40 @@ static int hp_vision_acpi_fix(struct cs35l41_hda *cs35l41, struct device *physde return 0; } +static int asus_rog_2023_ally_fix(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + const char *rog_ally_bios_ver = dmi_get_system_info(DMI_BIOS_VERSION); + const char *rog_ally_bios_num = rog_ally_bios_ver + 6; // Dropping the RC71L. part before the number + int rog_ally_bios_int; + kstrtoint(rog_ally_bios_num, 10, &rog_ally_bios_int); + if(rog_ally_bios_int >= 330){ + printk(KERN_INFO "DSD properties exist in the %d BIOS\n", rog_ally_bios_int); + return 0; //Patch not applicable. Exiting successfully + } + + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id); + + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->gpio1.func = CS35L41_NOT_USED; + hw_cfg->gpio1.valid = true; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->bst_type = CS35L41_INT_BOOST; + hw_cfg->bst_ind = 1000; /* 1,000nH Inductance value */ + hw_cfg->bst_ipk = 4500; /* 4,500mA peak current */ + hw_cfg->bst_cap = 24; /* 24 microFarad cap value */ + hw_cfg->valid = true; + + return 0; +} + static int asus_rog_2023_i2c_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, const char *hid) { @@ -134,7 +170,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "10431483", asus_rog_2023_spi_no_acpi }, // ASUS GU603V { "CSC3551", "10431493", asus_rog_2023_spi_no_acpi }, // ASUS GV601V { "CSC3551", "10431573", asus_rog_2023_spi_no_acpi }, // ASUS GZ301V - { "CSC3551", "104317F3", asus_rog_2023_i2c_no_acpi }, // ASUS ROG ALLY + { "CSC3551", "104317F3", asus_rog_2023_ally_fix }, // ASUS ROG ALLY { "CSC3551", "10431B93", asus_rog_2023_spi_no_acpi }, // ASUS G614J { "CSC3551", "10431CAF", asus_rog_2023_spi_no_acpi }, // ASUS G634J { "CSC3551", "10431C9F", asus_rog_2023_spi_no_acpi }, // ASUS G614JI From 4358fb2390e3df87d58c808c790c71bc3dc87e44 Mon Sep 17 00:00:00 2001 From: bouhaa Date: Fri, 22 Sep 2023 21:53:06 +0200 Subject: [PATCH 10/27] Ayaneo geek headset patch --- sound/pci/hda/patch_realtek.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9677c09cf7a98e..20e83bc7af9377 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6537,6 +6537,20 @@ static void alc294_gx502_toggle_output(struct hda_codec *codec, alc_write_coef_idx(codec, 0x10, 0x0a20); } +static void alc269_fixup_headphone_volume(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Pin 0x21: Some devices share 0x14 for headphones and speakers. + * This will fix ensure these devices have volume controls. */ + if (!is_jack_detectable(codec, 0x21)) + return; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn1[] = { 0x02 }; + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + } +} + static void alc294_fixup_gx502_hp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -7143,6 +7157,7 @@ enum { ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, ALC269_FIXUP_HEADSET_MODE, + ALC269_FIXUP_HEADSET_AYA_GEEK, ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC269_FIXUP_ASPIRE_HEADSET_MIC, ALC269_FIXUP_ASUS_X101_FUNC, @@ -8572,6 +8587,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE }, + [ALC269_FIXUP_HEADSET_AYA_GEEK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_headphone_volume, + }, [ALC299_FIXUP_PREDATOR_SPK] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -10120,6 +10139,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1f66, 0x0101, "GEEK", ALC269_FIXUP_HEADSET_AYA_GEEK), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), From 027dcffb5d03c394bd55b20c77c6d3cb5852ddc3 Mon Sep 17 00:00:00 2001 From: bouhaa Date: Fri, 22 Sep 2023 22:08:35 +0200 Subject: [PATCH 11/27] ayaneo 2 headphone fix --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 20e83bc7af9377..a4aafc339c3fcc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7157,6 +7157,7 @@ enum { ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, ALC269_FIXUP_HEADSET_MODE, + ALC269_FIXUP_HEADSET_AYA_2, ALC269_FIXUP_HEADSET_AYA_GEEK, ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC269_FIXUP_ASPIRE_HEADSET_MIC, @@ -8587,6 +8588,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE }, + [ALC269_FIXUP_HEADSET_AYA_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_headphone_volume, + }, [ALC269_FIXUP_HEADSET_AYA_GEEK] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_headphone_volume, @@ -10139,6 +10144,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1f66, 0x0101, "AYANEO 2", ALC269_FIXUP_HEADSET_AYA_2), SND_PCI_QUIRK(0x1f66, 0x0101, "GEEK", ALC269_FIXUP_HEADSET_AYA_GEEK), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), From ac3f4d0170d3984d593758ebe197d731cdc33ba2 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 7 Mar 2022 12:32:49 +0100 Subject: [PATCH 12/27] drm: Add GPU reset sysfs event This patch adds a new sysfs event, which will indicate the userland about a GPU reset, and can also provide some information like: - process ID of the process involved with the GPU reset - process name of the involved process - the GPU status info (using flags) This patch also introduces the first flag of the flags bitmap, which can be appended as and when required. V2: Addressed review comments from Christian and Amar - move the reset information structure to DRM layer - drop _ctx from struct name - make pid 32 bit(than 64) - set flag when VRAM invalid (than valid) - add process name as well (Amar) Cc: Alexandar Deucher Cc: Christian Koenig Cc: Amaranath Somalapuram Signed-off-by: Shashank Sharma (cherry picked from commit 90230bd9d9c7d979038547460c9a2cbbeff8d6b9) [Forward port to 6.0] Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/drm_sysfs.c | 31 +++++++++++++++++++++++++++++++ include/drm/drm_sysfs.h | 10 ++++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index b169b3e44a921b..5b3835b3ce428a 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -486,6 +486,37 @@ void drm_sysfs_connector_hotplug_event(struct drm_connector *connector) } EXPORT_SYMBOL(drm_sysfs_connector_hotplug_event); +/** + * drm_sysfs_reset_event - generate a DRM uevent to indicate GPU reset + * @dev: DRM device + * @reset_info: The contextual information about the reset (like PID, flags) + * + * Send a uevent for the DRM device specified by @dev. This informs + * user that a GPU reset has occurred, so that an interested client + * can take any recovery or profiling measure. + */ +void drm_sysfs_reset_event(struct drm_device *dev, struct drm_reset_event *reset_info) +{ + unsigned char pid_str[13]; + unsigned char flags_str[15]; + unsigned char pname_str[TASK_COMM_LEN + 6]; + unsigned char reset_str[] = "RESET=1"; + char *envp[] = { reset_str, pid_str, pname_str, flags_str, NULL }; + + if (!reset_info) { + DRM_WARN("No reset info, not sending the event\n"); + return; + } + + DRM_DEBUG("generating reset event\n"); + + snprintf(pid_str, ARRAY_SIZE(pid_str), "PID=%u", reset_info->pid); + snprintf(pname_str, ARRAY_SIZE(pname_str), "NAME=%s", reset_info->pname); + snprintf(flags_str, ARRAY_SIZE(flags_str), "FLAGS=%u", reset_info->flags); + kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); +} +EXPORT_SYMBOL(drm_sysfs_reset_event); + /** * drm_sysfs_connector_property_event - generate a DRM uevent for connector * property change diff --git a/include/drm/drm_sysfs.h b/include/drm/drm_sysfs.h index 96a5d858404b07..c3d94ec5fadcd2 100644 --- a/include/drm/drm_sysfs.h +++ b/include/drm/drm_sysfs.h @@ -1,12 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _DRM_SYSFS_H_ #define _DRM_SYSFS_H_ +#include + +#define DRM_GPU_RESET_FLAG_VRAM_INVALID (1 << 0) struct drm_device; struct device; struct drm_connector; struct drm_property; +struct drm_reset_event { + uint32_t pid; + uint32_t flags; + char pname[TASK_COMM_LEN]; +}; + int drm_class_device_register(struct device *dev); void drm_class_device_unregister(struct device *dev); @@ -14,4 +23,5 @@ void drm_sysfs_hotplug_event(struct drm_device *dev); void drm_sysfs_connector_hotplug_event(struct drm_connector *connector); void drm_sysfs_connector_property_event(struct drm_connector *connector, struct drm_property *property); +void drm_sysfs_reset_event(struct drm_device *dev, struct drm_reset_event *reset_info); #endif From cb6890969d3755743b42e9d5bcda6b215b889e29 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 7 Mar 2022 15:33:00 +0100 Subject: [PATCH 13/27] drm/amdgpu: add work function for GPU reset event This patch adds a work function, which sends a GPU reset uevent and some contextual infomration, like the PID and some status flags. This work should be scheduled during a GPU reset. The userspace can do some recovery and post-processing work based on this event and information. V2: Addressed review comments from Christian - Changed the name of the work to gpu_reset_event_work - Added a structure to accommodate some additional information (like a PID and some flags) - Do not add new structure in amdgpu.h Cc: Alexander Deucher Cc: Christian Koenig Cc: Amaranath Somalapuram Signed-off-by: Shashank Sharma (cherry picked from commit f63b09e78126f7da67b69409e2cce1d3ab2d7f46) [Forward port to 6.0] Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 3 +++ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index a79d53bdbe136a..cf2309ec1da9ac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -57,6 +57,7 @@ #include #include #include +#include #include #include "dm_pp_interface.h" @@ -1017,6 +1018,7 @@ struct amdgpu_device { int asic_reset_res; struct work_struct xgmi_reset_work; + struct work_struct gpu_reset_event_work; struct list_head reset_list; long gfx_timeout; @@ -1051,6 +1053,7 @@ struct amdgpu_device { bool barrier_has_auto_waitcnt; struct amdgpu_reset_control *reset_cntl; + struct drm_reset_event reset_event_info; uint32_t ip_versions[MAX_HWIP][HWIP_MAX_INSTANCE]; bool ram_is_direct_mapped; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 2b8356699f235d..ace5a79042fc12 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -80,6 +80,7 @@ #include #include +#include #if IS_ENABLED(CONFIG_X86) #include @@ -3332,6 +3333,17 @@ bool amdgpu_device_has_dc_support(struct amdgpu_device *adev) return amdgpu_device_asic_has_dc_support(adev->asic_type); } +static void amdgpu_device_reset_event_func(struct work_struct *__work) +{ + struct amdgpu_device *adev = container_of(__work, struct amdgpu_device, + gpu_reset_event_work); + /* + * A GPU reset has happened, inform the userspace and pass the + * reset related information. + */ + drm_sysfs_reset_event(&adev->ddev, &adev->reset_event_info); +} + static void amdgpu_device_xgmi_reset_func(struct work_struct *__work) { struct amdgpu_device *adev = @@ -3603,6 +3615,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, amdgpu_device_delay_enable_gfx_off); INIT_WORK(&adev->xgmi_reset_work, amdgpu_device_xgmi_reset_func); + INIT_WORK(&adev->gpu_reset_event_work, amdgpu_device_reset_event_func); adev->gfx.gfx_off_req_count = 1; adev->gfx.gfx_off_residency = 0; From 7ecbc315e5f82a304cb8f36478fe1f559350d10b Mon Sep 17 00:00:00 2001 From: Somalapuram Amaranath Date: Thu, 10 Mar 2022 11:31:44 +0530 Subject: [PATCH 14/27] drm/amdgpu: schedule GPU reset event work function Schedule work function with valid PID, process name, and vram lost status during a GPU reset/ recovery. Signed-off-by: Somalapuram Amaranath (cherry picked from commit 293c019a84c6402b08db9579819b555b01cd613b) [Forward ported to 6.0] Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index ace5a79042fc12..77243ff1f428cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4970,6 +4970,20 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, reset_context->job->vm->task_info; amdgpu_reset_capture_coredumpm(tmp_adev); #endif + if (reset_context->job && reset_context->job->vm) { + tmp_adev->reset_event_info.pid = + reset_context->job->vm->task_info.pid; + memset(tmp_adev->reset_event_info.pname, 0, TASK_COMM_LEN); + strcpy(tmp_adev->reset_event_info.pname, + reset_context->job->vm->task_info.process_name); + } else { + tmp_adev->reset_event_info.pid = 0; + memset(tmp_adev->reset_event_info.pname, 0, TASK_COMM_LEN); + } + + tmp_adev->reset_event_info.flags = vram_lost; + schedule_work(&tmp_adev->gpu_reset_event_work); + if (vram_lost) { DRM_INFO("VRAM is lost due to GPU reset!\n"); amdgpu_inc_vram_lost(tmp_adev); From e42b11d4599f1735c189aed747eca9e01c57ff2b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 30 Jun 2022 18:42:10 -0700 Subject: [PATCH 15/27] 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 ea85e2c701a15f..6fec92b5a0bd99 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 00000000000000..c6068b4863543f --- /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 835473910a4980..9754822b2a409e 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). */ From 0521d9847090acbd469670304eeca88815f48d7f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 30 Jun 2022 18:43:10 -0700 Subject: [PATCH 16/27] 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 6fec92b5a0bd99..172cba91aded11 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 c6068b4863543f..54814c2c68d608 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 */ From eb3cadc61ba46490a432950a4cfb80b2a8b48591 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 29 Nov 2022 18:32:58 -0800 Subject: [PATCH 17/27] 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 b110818fc94586..39a9bf3b7f77d8 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); } From 9eae2c2b8d5585f0c4e26844301b66da19e9e230 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 16 Nov 2022 19:54:26 -0800 Subject: [PATCH 18/27] 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 39a9bf3b7f77d8..0620046b142ef5 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); + } } /* From 7da77255c228752caf156e87f6c09c0abd9d0dc9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 8 May 2023 20:24:56 -0700 Subject: [PATCH 19/27] 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 0620046b142ef5..845ca71b8bd3ab 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; From 3bc24b768eb9e22cae313570cc843b16635da414 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 10 May 2023 17:27:12 -0700 Subject: [PATCH 20/27] 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 845ca71b8bd3ab..0c2fe51b29bc10 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"); } From 31c08d2f3e3ca11e6f2552450134a5809b61a0c5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 18 May 2023 18:00:35 -0700 Subject: [PATCH 21/27] 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 0c2fe51b29bc10..92e3e1052fa423 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; From 0d22816a2841cff5ae2492af230b8d44a071f68e Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Sun, 8 Oct 2023 00:22:05 +0200 Subject: [PATCH 22/27] drm: panel-orientation-quirks: Add quirk for Ayn Loki Zero Add quirk orientation for the Ayn Loki Zero. This also has been tested/used by the JELOS team. Signed-off-by: Bouke Sybren Haarsma --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index b02c3d7c4e6ccd..30451fb8579676 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -214,6 +214,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_BOARD_NAME, "NEXT"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* AYN Loki Zero */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ayn"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Loki Zero"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, }, { /* Chuwi HiBook (CWI514) */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), From c537101a9af6b9f894cff8d5d60ec163fc55a10f Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Sun, 8 Oct 2023 00:22:06 +0200 Subject: [PATCH 23/27] drm: panel-orientation-quirks: Add quirk for Ayn Loki Max Add quirk orientation for Ayn Loki Max model. This has been tested by JELOS team that uses their own patched kernel for a while now and confirmed by users in the ChimeraOS discord servers. Signed-off-by: Bouke Sybren Haarsma --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 30451fb8579676..94dcadb3f9dfef 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -214,6 +214,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_BOARD_NAME, "NEXT"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* AYN Loki Max */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ayn"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Loki Max"), + }, + .driver_data = (void *)&lcd1080x1920_leftside_up, }, { /* AYN Loki Zero */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ayn"), From 09cbb3d028b62ab40d7fd5bba2027cfff0933087 Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Mon, 23 Oct 2023 21:37:14 +0200 Subject: [PATCH 24/27] linux v6.6.0-chos1-rc7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5fc735c7fed184..b45c86aff6688d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 6 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = -chos1-rc7 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* From 53695d138f2a20b900e9b1b4dd6a5ab3deabcb21 Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Tue, 24 Oct 2023 21:56:02 +0200 Subject: [PATCH 25/27] use previous configuration for bios <330 --- sound/pci/hda/cs35l41_hda_property.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index f667e5b436d977..cf56721da472e4 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -97,14 +97,9 @@ static int asus_rog_2023_ally_fix(struct cs35l41_hda *cs35l41, struct device *ph cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); hw_cfg->spk_pos = cs35l41->index; - hw_cfg->gpio1.func = CS35L41_NOT_USED; - hw_cfg->gpio1.valid = true; hw_cfg->gpio2.func = CS35L41_INTERRUPT; hw_cfg->gpio2.valid = true; - hw_cfg->bst_type = CS35L41_INT_BOOST; - hw_cfg->bst_ind = 1000; /* 1,000nH Inductance value */ - hw_cfg->bst_ipk = 4500; /* 4,500mA peak current */ - hw_cfg->bst_cap = 24; /* 24 microFarad cap value */ + hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; hw_cfg->valid = true; return 0; From c26ab4561c4314c15dfbbabe0b8f006194425578 Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Tue, 24 Oct 2023 21:56:48 +0200 Subject: [PATCH 26/27] linux v6.6.0-chos2-rc7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b45c86aff6688d..7408693ea80ce0 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 6 SUBLEVEL = 0 -EXTRAVERSION = -chos1-rc7 +EXTRAVERSION = -chos2-rc7 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* From 7384adfe3b6d49e1bfa33d7a80435e74f93e177f Mon Sep 17 00:00:00 2001 From: jlobue10 Date: Tue, 24 Oct 2023 18:47:44 -0700 Subject: [PATCH 27/27] Update cs35l41_hda_property.c Fixing no patch applied exit code --- sound/pci/hda/cs35l41_hda_property.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index cf56721da472e4..1cda9aadd2c98d 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -85,7 +85,7 @@ static int asus_rog_2023_ally_fix(struct cs35l41_hda *cs35l41, struct device *ph kstrtoint(rog_ally_bios_num, 10, &rog_ally_bios_int); if(rog_ally_bios_int >= 330){ printk(KERN_INFO "DSD properties exist in the %d BIOS\n", rog_ally_bios_int); - return 0; //Patch not applicable. Exiting successfully + return -ENOENT; //Patch not necessary. Exiting... } struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;