Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package xone for openSUSE:Factory checked in at 2026-02-01 22:04:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/xone (Old) and /work/SRC/openSUSE:Factory/.xone.new.1995 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "xone" Sun Feb 1 22:04:01 2026 rev:2 rq:1330202 version:0.5.4 Changes: -------- --- /work/SRC/openSUSE:Factory/xone/xone.changes 2025-11-18 15:41:05.218606843 +0100 +++ /work/SRC/openSUSE:Factory/.xone.new.1995/xone.changes 2026-02-01 22:04:46.366951150 +0100 @@ -1,0 +2,27 @@ +Sat Jan 31 16:42:35 UTC 2026 - Tobias Görgens <[email protected]> + +- Update to release 0.5.4 + * Work around another sleeping issue +- Update to release 0.5.3 + * Work around sleeping issues + +------------------------------------------------------------------- +Sun Jan 25 04:42:42 UTC 2026 - pallas wept <[email protected]> + +- Update to release 0.5.2 + * Add fw override module parameter +- Update to release 0.5.1 + * Fix pairing through sysfs +- Update to release 0.5.0 + * This version breaks compatibility with kernels older than 6.5.0 + to lessen the maintainership burden. + * Increase the dongle pairing timout + * Support firmwares for all the defined dongles + * Merge mainline changes from medusalix +- Update to release 0.4.12 + * Fix leftover autosuspend handling + * Reduce the sleep before registering a GIP client + * Cleanup a warning + * Improve module unloading for testing + +------------------------------------------------------------------- Old: ---- v0.4.11.tar.gz New: ---- v0.5.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ xone.spec ++++++ --- /var/tmp/diff_new_pack.Z9r0B1/_old 2026-02-01 22:04:47.058980524 +0100 +++ /var/tmp/diff_new_pack.Z9r0B1/_new 2026-02-01 22:04:47.062980694 +0100 @@ -1,7 +1,7 @@ # # spec file for package xone # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -15,8 +15,9 @@ # Please submit bugfixes or comments via https://bugs.opensuse.org/ # + Name: xone -Version: 0.4.11 +Version: 0.5.4 Release: 0 Summary: Driver for Xbox One and Xbox Series X|S controllers License: GPL-2.0-or-later @@ -27,7 +28,7 @@ BuildRequires: %kernel_module_package_buildreqs BuildRequires: systemd-rpm-macros BuildRequires: xz -Requires: xone-dongle-firmware +Requires: xone-dongle-firmware >= 0+git20251208.d8be599 Requires: xone-kmp %kernel_module_package -n %name -p %_sourcedir/preamble ++++++ v0.4.11.tar.gz -> v0.5.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/README.md new/xone-0.5.4/README.md --- old/xone-0.4.11/README.md 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/README.md 2026-01-26 00:47:09.000000000 +0100 @@ -66,7 +66,7 @@ ### Prerequisites -- Linux 5.13+ +- Linux 6.5+ - Linux headers ### Automagically @@ -106,7 +106,7 @@ - Linux (kernel 5.13+ and headers) - DKMS - curl (for firmware download) -- cabextract (for firmware extraction) +- bsdtar (for firmware extraction) - For SecureBoot-enabled systems see [SecureBoot dkms guide](https://github.com/dell/dkms#secure-boot) ### Guide @@ -133,8 +133,11 @@ ``` sudo install/firmware.sh ``` +> [!NOTE] +> The `--skip-disclaimer` flag might be useful for scripting purposes. -**NOTE:** The `--skip-disclaimer` flag might be useful for scripting purposes. +> [!TIP] +> The `xone-dongle.fw_override=0x0000` module paramter can be used to load a different firmware file than the one selected automatically by the driver. The value is the USB PID contained in the fw file eg. `xone_dongle_02fe.bin` 5. Plug in your Xbox devices. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/auth/crypto.c new/xone-0.5.4/auth/crypto.c --- old/xone-0.4.11/auth/crypto.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/auth/crypto.c 2026-01-26 00:47:09.000000000 +0100 @@ -38,7 +38,6 @@ int gip_auth_get_transcript(struct shash_desc *desc, void *transcript) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) void *state = kzalloc(crypto_shash_descsize(desc->tfm), GFP_KERNEL); int err; @@ -55,20 +54,6 @@ get_transcript_error: kfree(state); return err; -#else - struct sha256_state state; - int err; - - err = crypto_shash_export(desc, &state); - if (err) - return err; - - err = crypto_shash_final(desc, transcript); - if (err) - return err; - - return crypto_shash_import(desc, &state); -#endif } int gip_auth_compute_prf(struct shash_desc *desc, const char *label, @@ -110,48 +95,20 @@ u8 *out, int out_len) { struct crypto_akcipher *tfm; - struct akcipher_request *req; - struct scatterlist src, dest; - DECLARE_CRYPTO_WAIT(wait); - u8 *buf; int err; - buf = kzalloc(out_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - tfm = crypto_alloc_akcipher("pkcs1pad(rsa)", 0, 0); - if (IS_ERR(tfm)) { - err = PTR_ERR(tfm); - goto err_free_buf; - } + if (IS_ERR(tfm)) + return PTR_ERR(tfm); err = crypto_akcipher_set_pub_key(tfm, key, key_len); if (err) goto err_free_tfm; - req = akcipher_request_alloc(tfm, GFP_KERNEL); - if (!req) { - err = -ENOMEM; - goto err_free_tfm; - } - - sg_init_one(&src, in, in_len); - sg_init_one(&dest, buf, out_len); - - akcipher_request_set_crypt(req, &src, &dest, in_len, out_len); - akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, &wait); - err = crypto_wait_req(crypto_akcipher_encrypt(req), &wait); - if (!err) - memcpy(out, buf, out_len); - - akcipher_request_free(req); + err = crypto_akcipher_sync_encrypt(tfm, in, in_len, out, out_len); err_free_tfm: crypto_free_akcipher(tfm); -err_free_buf: - kfree(buf); return err; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/bus/bus.c new/xone-0.5.4/bus/bus.c --- old/xone-0.4.11/bus/bus.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/bus/bus.c 2026-01-26 00:47:09.000000000 +0100 @@ -26,12 +26,8 @@ .release = gip_adapter_release, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) -static int gip_client_uevent(struct device *dev, struct kobj_uevent_env *env) -#else static int gip_client_uevent(const struct device *dev, struct kobj_uevent_env *env) -#endif { struct gip_client *client = to_gip_client(dev); struct gip_classes *classes = client->classes; @@ -117,24 +113,11 @@ up(&client->drv_lock); } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 13, 0) -static int gip_bus_remove_compat(struct device *dev) -{ - gip_bus_remove(dev); - - return 0; -} -#endif - static struct bus_type gip_bus_type = { .name = "xone-gip", .match = gip_bus_match, .probe = gip_bus_probe, -#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 13, 0) - .remove = gip_bus_remove_compat, -#else .remove = gip_bus_remove, -#endif }; struct gip_adapter *gip_create_adapter(struct device *parent, @@ -148,12 +131,7 @@ if (!adap) return ERR_PTR(-ENOMEM); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0) adap->id = ida_alloc(&gip_adapter_ida, GFP_KERNEL); -#else - adap->id = ida_simple_get(&gip_adapter_ida, 0, 0, GFP_KERNEL); -#endif - if (adap->id < 0) { err = adap->id; goto err_put_device; @@ -185,11 +163,7 @@ destroy_workqueue(adap->clients_wq); err_remove_ida: -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0) ida_free(&gip_adapter_ida, adap->id); -#else - ida_simple_remove(&gip_adapter_ida, adap->id); -#endif err_put_device: put_device(&adap->dev); @@ -226,11 +200,7 @@ device_unregister(&client->dev); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0) ida_free(&gip_adapter_ida, adap->id); -#else - ida_simple_remove(&gip_adapter_ida, adap->id); -#endif destroy_workqueue(adap->clients_wq); dev_dbg(&adap->dev, "%s: unregistered\n", __func__); @@ -254,7 +224,7 @@ * The unfortunate workaround, that at least works reliably is to add a * delay here. Since this is for human input device, one second is fine. */ - msleep(1000); + msleep(700); client->dev.parent = &client->adapter->dev; client->dev.type = &gip_client_type; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/bus/protocol.c new/xone-0.5.4/bus/protocol.c --- old/xone-0.4.11/bus/protocol.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/bus/protocol.c 2026-01-26 00:47:09.000000000 +0100 @@ -70,6 +70,26 @@ GIP_AUD_VOLUME_MIC_MUTED = 0x05, }; +static uint gip_audio_sample_rate[] = { + 0, + 8000, + 8000, + 12000, + 12000, + 16000, + 16000, + 20000, + 20000, + 24000, + 24000, + 32000, + 32000, + 40000, + 40000, + 48000, + 48000, +}; + enum gip_extended_command { GIP_EXT_GET_CAPABILITIES = 0x00, GIP_EXT_GET_TELEMETRY = 0x01, @@ -182,7 +202,7 @@ } __packed; struct gip_pkt_audio_samples { - __le16 length_out; + __le16 flow_rate; u8 samples[]; } __packed; @@ -482,7 +502,7 @@ } static int gip_set_audio_format_chat(struct gip_client *client, - enum gip_audio_format_chat in_out) + enum gip_audio_format in_out) { struct gip_header hdr = {}; struct gip_pkt_audio_format_chat pkt = {}; @@ -525,7 +545,7 @@ /* special handling for the chat headset */ if (chat) err = gip_set_audio_format_chat(client, - GIP_AUD_FORMAT_CHAT_24KHZ); + GIP_AUD_FORMAT_12KHZ_STEREO); else err = gip_set_audio_format(client, in, out); @@ -624,10 +644,10 @@ dest = buf + i * cfg->packet_size; /* sequence number is always greater than zero */ - do { - hdr.sequence = client->adapter->audio_sequence++; - } while (!hdr.sequence); + if (!++client->adapter->audio_sequence) + ++client->adapter->audio_sequence; + hdr.sequence = client->adapter->audio_sequence; gip_encode_header(&hdr, dest); memcpy(dest + hdr_len, src, cfg->fragment_size); } @@ -785,25 +805,16 @@ { struct gip_header hdr = {}; - switch (cfg->format) { - case GIP_AUD_FORMAT_16KHZ_MONO: - cfg->channels = 1; - cfg->sample_rate = 16000; - break; - case GIP_AUD_FORMAT_24KHZ_MONO: - cfg->channels = 1; - cfg->sample_rate = 24000; - break; - case GIP_AUD_FORMAT_48KHZ_STEREO: - cfg->channels = 2; - cfg->sample_rate = 48000; - break; - default: - gip_err(client, "%s: unknown format: 0x%02x\n", - __func__, cfg->format); + if (cfg->format <= 0 || cfg->format > GIP_AUD_FORMAT_48KHZ_STEREO) { + gip_err(client, "%s: unknown format: 0x%02x\n", __func__, + cfg->format); return -ENOTSUPP; } + /* even-indexed formats are stereo, uneven are mono */ + cfg->channels = 2 - (cfg->format & 1); + cfg->sample_rate = gip_audio_sample_rate[cfg->format]; + cfg->buffer_size = cfg->sample_rate * cfg->channels * sizeof(s16) * GIP_AUDIO_INTERVAL / MSEC_PER_SEC; cfg->fragment_size = cfg->buffer_size / @@ -813,6 +824,9 @@ hdr.packet_length = cfg->fragment_size; cfg->packet_size = gip_get_header_length(&hdr) + cfg->fragment_size; + /* set initial flow rate value */ + cfg->flow_rate = cfg->buffer_size; + gip_dbg(client, "%s: rate=%d/%d, buffer=%d\n", __func__, cfg->sample_rate, cfg->channels, cfg->buffer_size); @@ -842,7 +856,7 @@ if (len < off + total) return ERR_PTR(-EINVAL); - elem = kzalloc(sizeof(*elem) + total, GFP_ATOMIC); + elem = kzalloc(struct_size(elem, data, total), GFP_ATOMIC); if (!elem) return ERR_PTR(-ENOMEM); @@ -983,8 +997,7 @@ if (!count) return -EINVAL; - classes = kzalloc(sizeof(*classes) + sizeof(char *) * count, - GFP_ATOMIC); + classes = kzalloc(struct_size(classes, strings, count), GFP_ATOMIC); if (!classes) return -ENOMEM; @@ -1342,7 +1355,7 @@ return -EINVAL; /* chat headsets apparently default to 24 kHz */ - if (pkt->in_out != GIP_AUD_FORMAT_CHAT_24KHZ || + if (pkt->in_out != GIP_AUD_FORMAT_12KHZ_STEREO || in->buffer_size || out->buffer_size) return -EPROTO; @@ -1508,6 +1521,7 @@ void *data, u32 len) { struct gip_pkt_audio_samples *pkt = data; + struct gip_audio_config *out = &client->audio_config_out; int err = 0; if (len < sizeof(*pkt)) @@ -1516,6 +1530,10 @@ if (down_trylock(&client->drv_lock)) return -EBUSY; + out->flow_rate = le16_to_cpu(pkt->flow_rate); + if (out->flow_rate != out->buffer_size) + gip_dbg(client, "%s: Unusual flow rate control -> %u", __func__, out->flow_rate); + if (client->drv && client->drv->ops.audio_samples) err = client->drv->ops.audio_samples(client, pkt->samples, len - sizeof(*pkt)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/bus/protocol.h new/xone-0.5.4/bus/protocol.h --- old/xone-0.4.11/bus/protocol.h 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/bus/protocol.h 2026-01-26 00:47:09.000000000 +0100 @@ -37,14 +37,23 @@ }; enum gip_audio_format { - GIP_AUD_FORMAT_16KHZ_MONO = 0x05, - GIP_AUD_FORMAT_24KHZ_MONO = 0x09, - GIP_AUD_FORMAT_48KHZ_STEREO = 0x10, -}; - -enum gip_audio_format_chat { - GIP_AUD_FORMAT_CHAT_24KHZ = 0x04, - GIP_AUD_FORMAT_CHAT_16KHZ = 0x05, + GIP_AUD_FORMAT_NONE, + GIP_AUD_FORMAT_8KHZ_MONO, + GIP_AUD_FORMAT_8KHZ_STEREO, + GIP_AUD_FORMAT_12KHZ_MONO, + GIP_AUD_FORMAT_12KHZ_STEREO, + GIP_AUD_FORMAT_16KHZ_MONO, + GIP_AUD_FORMAT_16KHZ_STEREO, + GIP_AUD_FORMAT_20KHZ_MONO, + GIP_AUD_FORMAT_20KHZ_STEREO, + GIP_AUD_FORMAT_24KHZ_MONO, + GIP_AUD_FORMAT_24KHZ_STEREO, + GIP_AUD_FORMAT_32KHZ_MONO, + GIP_AUD_FORMAT_32KHZ_STEREO, + GIP_AUD_FORMAT_40KHZ_MONO, + GIP_AUD_FORMAT_40KHZ_STEREO, + GIP_AUD_FORMAT_48KHZ_MONO, + GIP_AUD_FORMAT_48KHZ_STEREO, }; enum gip_led_mode { @@ -96,6 +105,9 @@ int buffer_size; int fragment_size; int packet_size; + + /* only used for rendered audio (out) */ + u16 flow_rate; }; struct gip_classes { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/driver/common.c new/xone-0.5.4/driver/common.c --- old/xone-0.4.11/driver/common.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/driver/common.c 2026-01-26 00:47:09.000000000 +0100 @@ -122,7 +122,7 @@ struct led_classdev *cdev = dev_get_drvdata(dev); struct gip_led *led = container_of(cdev, typeof(*led), dev); - return sprintf(buf, "%u\n", led->mode); + return sysfs_emit(buf, "%u\n", led->mode); } static ssize_t gip_led_mode_store(struct device *dev, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/driver/gamepad.c new/xone-0.5.4/driver/gamepad.c --- old/xone-0.4.11/driver/gamepad.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/driver/gamepad.c 2026-01-26 00:47:09.000000000 +0100 @@ -124,6 +124,16 @@ PADDLE_ELITE2_511, // Different packet entirely. } PaddleCapability; +struct gip_gamepad_rumble { + /* serializes access to rumble packet */ + spinlock_t lock; + unsigned long last; + struct timer_list timer; + struct gip_gamepad_pkt_rumble pkt; + + struct gip_gamepad *parent; +}; + struct gip_gamepad { struct gip_client *client; struct gip_battery battery; @@ -135,27 +145,20 @@ bool supports_dli; PaddleCapability paddle_support; - struct gip_gamepad_rumble { - /* serializes access to rumble packet */ - spinlock_t lock; - unsigned long last; - struct timer_list timer; - struct gip_gamepad_pkt_rumble pkt; - } rumble; + struct gip_gamepad_rumble rumble; }; static void gip_gamepad_send_rumble(struct timer_list *timer) { // from_timer() has been renamed to timer_container_of() in linux 6.16 struct gip_gamepad_rumble *rumble = -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,16,0) - timer_container_of(rumble, timer, timer); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,16,0) from_timer(rumble, timer, timer); +#else + timer_container_of(rumble, timer, timer); #endif - struct gip_gamepad *gamepad = container_of(rumble, typeof(*gamepad), - rumble); + struct gip_gamepad *gamepad = rumble->parent; unsigned long flags; spin_lock_irqsave(&rumble->lock, flags); @@ -204,6 +207,7 @@ GIP_GP_MOTOR_RT | GIP_GP_MOTOR_LT; rumble->pkt.duration = 0xff; rumble->pkt.repeat = 0xeb; + rumble->parent = gamepad; gip_gamepad_send_rumble(&rumble->timer); input_set_capability(dev, EV_FF, FF_RUMBLE); @@ -309,10 +313,10 @@ return 0; err_delete_timer: -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,15,0) - timer_delete_sync(&gamepad->rumble.timer); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,15,0) del_timer_sync(&gamepad->rumble.timer); +#else + timer_delete_sync(&gamepad->rumble.timer); #endif return err; } @@ -529,10 +533,10 @@ { struct gip_gamepad *gamepad = dev_get_drvdata(&client->dev); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,15,0) - timer_delete_sync(&gamepad->rumble.timer); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,15,0) del_timer_sync(&gamepad->rumble.timer); +#else + timer_delete_sync(&gamepad->rumble.timer); #endif } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/driver/headset.c new/xone-0.5.4/driver/headset.c --- old/xone-0.4.11/driver/headset.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/driver/headset.c 2026-01-26 00:47:09.000000000 +0100 @@ -5,7 +5,6 @@ #include <linux/module.h> #include <linux/hrtimer.h> -#include <linux/vmalloc.h> #include <linux/version.h> #include <sound/core.h> #include <sound/initval.h> @@ -68,8 +67,6 @@ snd_pcm_uframes_t pointer; snd_pcm_uframes_t period; } playback, capture; - - struct snd_card *card; }; static int gip_headset_pcm_open(struct snd_pcm_substream *sub) @@ -89,7 +86,9 @@ hw.channels_max = cfg->channels; hw.buffer_bytes_max = cfg->buffer_size * GIP_HS_NUM_BUFFERS; hw.period_bytes_min = cfg->buffer_size; - hw.period_bytes_max = cfg->buffer_size; + hw.period_bytes_max = cfg->buffer_size * 2; + hw.periods_min = 1; + hw.periods_max = 8; sub->runtime->hw = hw; @@ -101,39 +100,6 @@ return 0; } -static int gip_headset_pcm_hw_params(struct snd_pcm_substream *sub, - struct snd_pcm_hw_params *params) -{ - struct snd_pcm_runtime *runtime = sub->runtime; - size_t size = params_buffer_bytes(params); - - if (runtime->dma_area) { - if (runtime->dma_bytes >= size) - return 0; /* Already large enough */ - vfree(runtime->dma_area); - } - runtime->dma_area = vzalloc(size); - if (!runtime->dma_area) - return -ENOMEM; - runtime->dma_bytes = size; - return 1; -} - -static int gip_headset_pcm_hw_free(struct snd_pcm_substream *sub) -{ - struct snd_pcm_runtime *runtime = sub->runtime; - - vfree(runtime->dma_area); - runtime->dma_area = NULL; - return 0; -} - -static struct page *gip_headset_pcm_get_page(struct snd_pcm_substream *sub, - unsigned long offset) -{ - return vmalloc_to_page(sub->runtime->dma_area + offset); -} - static int gip_headset_pcm_prepare(struct snd_pcm_substream *sub) { return 0; @@ -186,13 +152,9 @@ static const struct snd_pcm_ops gip_headset_pcm_ops = { .open = gip_headset_pcm_open, .close = gip_headset_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = gip_headset_pcm_hw_params, - .hw_free = gip_headset_pcm_hw_free, .prepare = gip_headset_pcm_prepare, .trigger = gip_headset_pcm_trigger, .pointer = gip_headset_pcm_pointer, - .page = gip_headset_pcm_get_page, }; static bool gip_headset_advance_pointer(struct gip_headset_stream *stream, @@ -334,8 +296,8 @@ struct snd_pcm *pcm; int err; - err = snd_card_new(&headset->client->dev, SNDRV_DEFAULT_IDX1, - SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); + err = snd_devm_card_new(&headset->client->dev, SNDRV_DEFAULT_IDX1, + SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); if (err) return err; @@ -344,8 +306,6 @@ snprintf(card->longname, sizeof(card->longname), "%s at %s", GIP_HS_NAME, dev_name(&headset->client->dev)); - headset->card = card; - err = snd_pcm_new(card, GIP_HS_NAME, 0, 1, 1, &pcm); if (err) return err; @@ -355,6 +315,7 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &gip_headset_pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &gip_headset_pcm_ops); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); return snd_card_register(card); } @@ -440,7 +401,7 @@ return; dev_dbg(&client->dev, "%s: init pcm device.\n", __func__); - err = gip_headset_init_pcm(headset); + err = snd_card_free_on_error(&client->dev, gip_headset_init_pcm(headset)); if (err) { dev_err(&client->dev, "%s: init PCM failed: %d\n", __func__, err); @@ -466,6 +427,14 @@ return; } + dev_dbg(&client->dev, "%s: init audio in.\n", __func__); + err = gip_init_audio_in(client); + if (err) { + dev_err(&client->dev, "%s: init audio in failed: %d\n", + __func__, err); + return; + } + /* start audio timer */ hrtimer_start(&headset->timer, 0, HRTIMER_MODE_REL); } @@ -574,28 +543,22 @@ INIT_DELAYED_WORK(&headset->work_power_on, gip_headset_power_on); INIT_WORK(&headset->work_register, gip_headset_register); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,15,0) - hrtimer_setup(&headset->timer, gip_headset_send_samples, - CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer_setup(&headset->start_audio_timer, gip_headset_start_audio, - CLOCK_MONOTONIC, HRTIMER_MODE_REL); -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,15,0) hrtimer_init(&headset->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); headset->timer.function = gip_headset_send_samples; hrtimer_init(&headset->start_audio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); headset->start_audio_timer.function = gip_headset_start_audio; +#else + hrtimer_setup(&headset->timer, gip_headset_send_samples, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_setup(&headset->start_audio_timer, gip_headset_start_audio, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); #endif err = gip_enable_audio(client); if (err) return err; - err = gip_init_audio_in(client); - if (err) { - gip_disable_audio(client); - return err; - } - dev_set_drvdata(&client->dev, headset); /* start audio configuration */ @@ -614,11 +577,6 @@ hrtimer_cancel(&headset->timer); hrtimer_cancel(&headset->start_audio_timer); gip_disable_audio(client); - - if (headset->card) { - snd_card_disconnect(headset->card); - snd_card_free_when_closed(headset->card); - } } static struct gip_driver gip_headset_driver = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/install/firmware.sh new/xone-0.5.4/install/firmware.sh --- old/xone-0.4.11/install/firmware.sh 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/install/firmware.sh 2026-01-26 00:47:09.000000000 +0100 @@ -12,8 +12,8 @@ exit 1 fi -if ! [ -x "$(command -v cabextract)" ]; then - echo 'This script requires cabextract!' >&2 +if ! [ -x "$(command -v bsdtar)" ]; then + echo 'This script requires bsdtar!' >&2 exit 1 fi @@ -25,34 +25,47 @@ read -r _ fi -echo -e "dongle firmware installation...\n" +echo -e "Dongle firmware installation\n" -driver_url='https://catalog.s.download.windowsupdate.com/c/msdownload/update/driver/drvs/2017/07/1cd6a87c-623f-4407-a52d-c31be49e925c_e19f60808bdcbfbd3c3df6be3e71ffc52e43261e.cab' -firmware_hash='48084d9fa53b9bb04358f3bb127b7495dc8f7bb0b3ca1437bd24ef2b6eabdf66' -dest_file="/lib/firmware/xow_dongle.bin" - -if [[ ! -f $dest_file ]]; then - curl -L -o driver.cab "$driver_url" - cabextract -F FW_ACC_00U.bin driver.cab - echo "$firmware_hash" FW_ACC_00U.bin | sha256sum -c - mv FW_ACC_00U.bin $dest_file +urls=( + 'https://catalog.s.download.windowsupdate.com/d/msdownload/update/driver/drvs/2017/03/2ea9591b-f751-442c-80ce-8f4692cdc67b_6b555a3a288153cf04aec6e03cba360afe2fce34.cab' + 'https://catalog.s.download.windowsupdate.com/c/msdownload/update/driver/drvs/2017/07/1cd6a87c-623f-4407-a52d-c31be49e925c_e19f60808bdcbfbd3c3df6be3e71ffc52e43261e.cab' + 'https://catalog.s.download.windowsupdate.com/c/msdownload/update/driver/drvs/2017/06/1dbd7cb4-53bc-4857-a5b0-5955c8acaf71_9081931e7d664429a93ffda0db41b7545b7ac257.cab' + 'https://catalog.s.download.windowsupdate.com/d/msdownload/update/driver/drvs/2017/08/aeff215c-3bc4-4d36-a3ea-e14bfa8fa9d2_e58550c4f74a27e51e5cb6868b10ff633fa77164.cab' +) +hashes=( + '080ce4091e53a4ef3e5fe29939f51fd91f46d6a88be6d67eb6e99a5723b3a223' + '48084d9fa53b9bb04358f3bb127b7495dc8f7bb0b3ca1437bd24ef2b6eabdf66' + '0023a7bae02974834500c665a281e25b1ba52c9226c84989f9084fa5ce591d9b' + 'e2710daf81e7b36d35985348f68a81d18bc537a2b0c508ffdfde6ac3eae1bad7' +) +filenames=('FW_ACC_00U.bin' 'FW_ACC_00U.bin' 'FW_ACC_CL.bin' 'FW_ACC_BR.bin') +pids=('02e6' '02fe' '02f9' '091e') + +function download_firmware() { + local firmware_name="xone_dongle_${1}.bin" + local dest_file="/lib/firmware/$firmware_name" + + if [[ -f $dest_file ]]; then + echo -e "$firmware_name found. Skipping download" + return 0 + fi + + echo -n "Downloading $firmware_name..." + curl -s -L -o driver.cab "$2" + bsdtar -xf driver.cab "$3" > /dev/null 2>&1 + + echo -n " Checking sha256..." + echo "$4" "$3" | sha256sum -c --quiet + mv "$3" $dest_file rm driver.cab -else - echo -e "xow_dongle.bin found. Skipping download\n" -fi -driver_url='https://catalog.s.download.windowsupdate.com/d/msdownload/update/driver/drvs/2015/12/20810869_8ce2975a7fbaa06bcfb0d8762a6275a1cf7c1dd3.cab' -firmware_hash='080ce4091e53a4ef3e5fe29939f51fd91f46d6a88be6d67eb6e99a5723b3a223' -dest_file="/lib/firmware/xow_dongle_045e_02e6.bin" - -if [[ ! -f $dest_file ]]; then - curl -L -o driver.cab "$driver_url" - cabextract -F FW_ACC_00U.bin driver.cab - echo "$firmware_hash" FW_ACC_00U.bin | sha256sum -c - mv FW_ACC_00U.bin $dest_file - rm driver.cab -else - echo -e "xow_dongle_045e_02e6.bin found. Skipping download\n" -fi + echo -e " Done!" +} + +for ((i = 0 ; i < "${#urls[@]}" ; i++)); do + download_firmware "${pids[$i]}" "${urls[$i]}" "${filenames[$i]}" "${hashes[$i]}" +done + +echo -e "\nDongle firmwares installed!" -echo -e "dongle firmware installed\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/install/steam-deck-install.sh new/xone-0.5.4/install/steam-deck-install.sh --- old/xone-0.4.11/install/steam-deck-install.sh 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/install/steam-deck-install.sh 2026-01-26 00:47:09.000000000 +0100 @@ -67,8 +67,8 @@ sudo -u deck makepkg -Ccf -p PKGBUILD_XONE sudo -u deck makepkg -Ccf -p PKGBUILD_FIRMWARE -pacman -U --noconfirm xone-dkms-*.tar.zst pacman -U --noconfirm --asdeps xone-dongle-firmware-*.tar.zst +pacman -U --noconfirm xone-dkms-*.tar.zst # Remove unneeded build dependencies pacman -Rcns --noconfirm w3m html-xml-utils diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/modules_load.sh new/xone-0.5.4/modules_load.sh --- old/xone-0.4.11/modules_load.sh 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/modules_load.sh 2026-01-26 00:47:09.000000000 +0100 @@ -6,8 +6,7 @@ mapfile -t MODULES_TMP < modules.order MODULES=("${MODULES_TMP[@]}") - -LOADED_MODULES=$(lsmod) +LOADED_MODULES=$(lsmod | cut -d " " -f 1) if [[ $1 == "unload" ]]; then OPERATION="rmmod -f" @@ -32,6 +31,9 @@ for module in "${MODULES[@]}"; do module="${module%.o}$SUFFIX" + # remove path and leave only base name + [[ $1 == "unload" ]] && module=${module##*/} + # skip rmmod if module is not loaded [[ $1 == "unload" && ! "$LOADED_MODULES" =~ "$module" ]] && continue diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/transport/dongle.c new/xone-0.5.4/transport/dongle.c --- old/xone-0.4.11/transport/dongle.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/transport/dongle.c 2026-01-26 00:47:09.000000000 +0100 @@ -18,6 +18,10 @@ #include "mt76.h" #include "../bus/bus.h" +ushort fw_override_pid = 0; +MODULE_PARM_DESC(fw_override, "Use firmware for the provided product ID instead of the one detected automatically"); +module_param_named(fw_override, fw_override_pid, ushort, 0600); + #define XONE_DONGLE_NUM_IN_URBS 12 #define XONE_DONGLE_NUM_OUT_URBS 12 @@ -26,16 +30,12 @@ #define XONE_DONGLE_MAX_CLIENTS 16 -#define XONE_DONGLE_PAIRING_TIMEOUT msecs_to_jiffies(30000) +#define XONE_DONGLE_PAIRING_TIMEOUT msecs_to_jiffies(60000) #define XONE_DONGLE_PWR_OFF_TIMEOUT msecs_to_jiffies(5000) #define XONE_DONGLE_FW_REQ_TIMEOUT_MS 3000 #define XONE_DONGLE_FW_REQ_RETRIES 11 // 30 seconds #define XONE_DONGLE_FW_LOAD_RETRIES 3 -#define XONE_DONGLE_OFFICIAL_VENDOR 0x045e -#define XONE_DONGLE_OFFICIAL_PRODUCT 0x02fe -#define XONE_DONGLE_KNOCKOFF_PRODUCT1 0x02e6 - enum xone_dongle_queue { XONE_DONGLE_QUEUE_DATA = 0x00, XONE_DONGLE_QUEUE_AUDIO = 0x02, @@ -62,14 +62,16 @@ struct gip_adapter *adapter; }; +enum xone_dongle_event_type { + XONE_DONGLE_EVT_ADD_CLIENT, + XONE_DONGLE_EVT_REMOVE_CLIENT, + XONE_DONGLE_EVT_PAIR_CLIENT, + XONE_DONGLE_EVT_ENABLE_PAIRING, + XONE_DONGLE_EVT_ENABLE_ENCRYPTION, +}; + struct xone_dongle_event { - enum xone_dongle_event_type { - XONE_DONGLE_EVT_ADD_CLIENT, - XONE_DONGLE_EVT_REMOVE_CLIENT, - XONE_DONGLE_EVT_PAIR_CLIENT, - XONE_DONGLE_EVT_ENABLE_PAIRING, - XONE_DONGLE_EVT_ENABLE_ENCRYPTION, - } type; + enum xone_dongle_event_type type; struct xone_dongle *dongle; u8 address[ETH_ALEN]; @@ -230,7 +232,6 @@ static int xone_dongle_toggle_pairing(struct xone_dongle *dongle, bool enable) { - struct usb_interface *intf = to_usb_interface(dongle->mt.dev); enum xone_mt76_led_mode led; int err = 0; @@ -255,11 +256,6 @@ if (err) goto err_unlock; - if (enable) - usb_autopm_get_interface(intf); - else - usb_autopm_put_interface(intf); - dev_dbg(dongle->mt.dev, "%s: enabled=%d\n", __func__, enable); dongle->pairing = enable; @@ -296,7 +292,7 @@ struct usb_interface *intf = to_usb_interface(dev); struct xone_dongle *dongle = usb_get_intfdata(intf); - return sprintf(buf, "%d\n", dongle->pairing); + return sysfs_emit(buf, "%d\n", dongle->pairing); } static ssize_t xone_dongle_pairing_store(struct device *dev, @@ -312,16 +308,10 @@ if (err) return err; - err = pm_runtime_resume_and_get(dev); - if (err) - return err; - err = xone_dongle_toggle_pairing(dongle, enable); if (err) return err; - pm_runtime_put(dev); - return count; } @@ -778,8 +768,8 @@ if (err) { dev_err(dongle->mt.dev, "%s: process failed: %d\n", __func__, err); - print_hex_dump_debug("xone-dongle packet: ", DUMP_PREFIX_NONE, - 16, 1, data, len, false); + print_hex_dump_bytes("xone-dongle packet: ", DUMP_PREFIX_NONE, + data, len); } dev_kfree_skb(skb); @@ -918,20 +908,16 @@ struct xone_mt76 *mt = &dongle->mt; const struct firmware *fw; - char fwname[25]; + char fwname[21]; int err; + u16 fw_product = dongle->product; - switch (dongle->product) { - case XONE_DONGLE_KNOCKOFF_PRODUCT1: - snprintf(fwname, 25, "xow_dongle_%04x_%04x.bin", dongle->vendor, - dongle->product); - break; - - case XONE_DONGLE_OFFICIAL_PRODUCT: - default: - snprintf(fwname, 15, "xow_dongle.bin"); + if (fw_override_pid) { + dev_info(mt->dev, "Firmware overriden with PID=0x%04x", fw_override_pid); + fw_product = fw_override_pid; } + sprintf(fwname, "xone_dongle_%04x.bin", fw_product); err = xone_dongle_fw_requester(&fw, dongle, fwname); if (dongle->fw_state == XONE_DONGLE_FW_STATE_STOP_LOADING) { dongle->fw_state = XONE_DONGLE_FW_STATE_ERROR; @@ -947,7 +933,12 @@ dev_dbg(mt->dev, "%s: firmware requested successfully\n", __func__); - err = xone_mt76_load_firmware(mt, fw); + for (int i = 0; i < 5; ++i) { + err = xone_mt76_load_firmware(mt, fw); + if (!err) + break; + ssleep(1); + } release_firmware(fw); if (err) { dongle->fw_state = XONE_DONGLE_FW_STATE_ERROR; @@ -1177,6 +1168,8 @@ return 0; } + msleep(1500); + while ((urb = usb_get_from_anchor(&dongle->urbs_in_idle))) { usb_anchor_urb(urb, &dongle->urbs_in_busy); usb_free_urb(urb); @@ -1185,6 +1178,10 @@ if (err) return err; } + + msleep(1000); + return xone_mt76_resume_radio(&dongle->mt); + msleep(500); return xone_mt76_resume_radio(&dongle->mt); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/transport/mt76.c new/xone-0.5.4/transport/mt76.c --- old/xone-0.4.11/transport/mt76.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/transport/mt76.c 2026-01-26 00:47:09.000000000 +0100 @@ -640,7 +640,7 @@ err = xone_mt76_switch_channel(mt, chan); if (err) return err; - + /* pick the highest power channel seen first */ /* the last channel might not be the best one */ if (chan->power > pow) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/transport/wired.c new/xone-0.5.4/transport/wired.c --- old/xone-0.4.11/transport/wired.c 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/transport/wired.c 2026-01-26 00:47:09.000000000 +0100 @@ -71,10 +71,8 @@ urb->actual_length); if (err) { dev_err(dev, "%s: process failed: %d\n", __func__, err); - print_hex_dump_debug("xone-wired packet: ", - DUMP_PREFIX_NONE, 16, 1, - urb->transfer_buffer, urb->actual_length, - false); + print_hex_dump_bytes("xone-wired packet: ", DUMP_PREFIX_NONE, + urb->transfer_buffer, urb->actual_length); } resubmit: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xone-0.4.11/uninstall.sh new/xone-0.5.4/uninstall.sh --- old/xone-0.4.11/uninstall.sh 2025-11-11 17:49:18.000000000 +0100 +++ new/xone-0.5.4/uninstall.sh 2026-01-26 00:47:09.000000000 +0100 @@ -32,5 +32,6 @@ echo -e "All xone versions removed\n" [[ ${1:-} == "--no-firmware" ]] && exit 0 -rm -rf /lib/firmware/xow_dongle* +rm -rf /lib/firmware/xow_dongle* || true +rm -rf /lib/firmware/xone_dongle* || true echo -e "All dongle firmwares removed\n"
