Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion driver/headset.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ static const struct snd_pcm_ops gip_headset_pcm_ops = {
static bool gip_headset_advance_pointer(struct gip_headset_stream *stream,
int len, size_t buf_size)
{
snd_pcm_uframes_t period = stream->substream->runtime->period_size;
size_t period = snd_pcm_lib_period_bytes(stream->substream);

stream->pointer += len;
if (stream->pointer >= buf_size)
Expand Down
111 changes: 38 additions & 73 deletions transport/dongle.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,11 @@ static int xone_dongle_get_buffer(struct gip_adapter *adap,
return -ENOSPC;

skb = xone_mt76_alloc_message(XONE_DONGLE_LEN_CMD_PKT, GFP_ATOMIC);
if (!skb)
if (!skb) {
usb_anchor_urb(urb, &client->dongle->urbs_out_idle);
usb_free_urb(urb);
return -ENOMEM;
}

/* command header + WCID data + TXWI + QoS header + padding */
/* see xone_dongle_prep_packet and xone_mt76_prep_message */
Expand Down Expand Up @@ -514,17 +517,24 @@ xone_dongle_create_client(struct xone_dongle *dongle, u8 *addr)
return client;
}

static int xone_dongle_remove_client(struct xone_dongle *dongle, u8 wcid);

static int xone_dongle_add_client(struct xone_dongle *dongle, u8 *addr)
{
struct xone_dongle_client *client;
int i, err;
int err;
unsigned long flags;

/* reject duplicate: controller already has a WCID slot */
for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++)
if (dongle->clients[i] &&
ether_addr_equal(dongle->clients[i]->address, addr))
return 0;
dev_dbg(dongle->mt.dev, "%s: new mac=%pM\n", __func__, addr);

for (int i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++)
if (dongle->clients[i] && ether_addr_equal(dongle->clients[i]->address, addr)) {
dev_dbg(dongle->mt.dev,
"%s: Client with this address already exists! Removing old client\n",
__func__);
xone_dongle_remove_client(dongle, dongle->clients[i]->wcid);
break;
}

client = xone_dongle_create_client(dongle, addr);
if (IS_ERR(client))
Expand Down Expand Up @@ -1134,6 +1144,7 @@ static void xone_dongle_fw_load(struct work_struct *work)
err = xone_mt76_load_firmware(mt, fw);
if (!err)
break;
pr_err("%s: Error loading firmware into dongle\n", __func__);
ssleep(1);
}
release_firmware(fw);
Expand Down Expand Up @@ -1260,6 +1271,8 @@ static void xone_dongle_destroy(struct xone_dongle *dongle)
dongle->fw_state = XONE_DONGLE_FW_STATE_STOP_LOADING;
}

xone_dongle_toggle_pairing(dongle, false);

usb_kill_anchored_urbs(&dongle->urbs_in_busy);
/* cancel fw load before destroying workqueues to avoid use-after-free */
cancel_work_sync(&dongle->load_fw_work);
Expand Down Expand Up @@ -1318,18 +1331,7 @@ static int xone_dongle_probe(struct usb_interface *intf,
spin_lock_init(&dongle->clients_lock);
init_waitqueue_head(&dongle->disconnect_wait);

/*
* Do not call usb_reset_device() here. On cold boot the MT76 chip
* disconnects from USB as a normal part of its firmware startup
* sequence (inside xone_mt76_load_ivb). A preceding USB reset leaves
* the XHCI port in a state where it cannot cleanly handle that
* subsequent disconnect/reconnect cycle, causing the chip to
* permanently disappear from the USB bus until a physical replug.
*
* On warm reboot the firmware survives in RAM, so the chip does not
* disconnect at all and the faster xone_mt76_reset_firmware() path
* is taken instead — a reset is equally unnecessary there.
*/
usb_reset_device(dongle->mt.udev);
err = xone_dongle_init(dongle);
if (err) {
xone_dongle_destroy(dongle);
Expand Down Expand Up @@ -1457,6 +1459,16 @@ static int xone_dongle_pre_reset(struct usb_interface *intf)
usb_kill_anchored_urbs(&dongle->urbs_in_busy);
usb_kill_anchored_urbs(&dongle->urbs_out_busy);

for (int i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) {
struct xone_dongle_client *client = dongle->clients[i];
if (!client)
continue;
gip_destroy_adapter(client->adapter);
kfree(client);
dongle->clients[i] = NULL;
}
atomic_set(&dongle->client_count, 0);

while ((urb = usb_get_from_anchor(&dongle->urbs_out_idle)))
usb_free_urb(urb);

Expand All @@ -1480,68 +1492,21 @@ static int xone_dongle_post_reset(struct usb_interface *intf)
return 0;

pr_debug("%s: Re-initializing dongle after reset", __func__);

dongle->pairing = false;
dongle->pairing_scan_idx = 0;
dongle->last_wlan_rx = 0;

return xone_dongle_init(dongle);
}

static int xone_dongle_reset_resume(struct usb_interface *intf)
{
struct xone_dongle *dongle = usb_get_intfdata(intf);
struct xone_dongle_client *client;
struct urb *urb;
int i;

pr_debug("%s", __func__);

/*
* The kernel already reset the USB device before calling
* reset_resume — a second usb_reset_device() is redundant and
* can leave the XHCI port in a bad state (the same class of bug
* as the former usb_reset_device() call in probe).
*
* Instead, clean up all stale state and reinitialize from scratch.
* This also ensures old GIP adapters are destroyed so the
* reconnecting controller gets a fresh input device.
*/
if (dongle->fw_state < XONE_DONGLE_FW_STATE_ERROR)
dongle->fw_state = XONE_DONGLE_FW_STATE_STOP_LOADING;

usb_kill_anchored_urbs(&dongle->urbs_in_busy);
cancel_work_sync(&dongle->load_fw_work);
/*
* If load_fw_work raced past the STOP_LOADING check and created
* new URBs before cancel_work_sync returned, kill them now.
*/
usb_kill_anchored_urbs(&dongle->urbs_in_busy);
drain_workqueue(dongle->event_wq);
cancel_delayed_work_sync(&dongle->pairing_work);
cancel_delayed_work_sync(&dongle->pairing_scan_work);

for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) {
client = dongle->clients[i];
if (!client)
continue;
gip_destroy_adapter(client->adapter);
kfree(client);
dongle->clients[i] = NULL;
}
atomic_set(&dongle->client_count, 0);

usb_kill_anchored_urbs(&dongle->urbs_out_busy);

while ((urb = usb_get_from_anchor(&dongle->urbs_out_idle)))
usb_free_urb(urb);

while ((urb = usb_get_from_anchor(&dongle->urbs_in_idle))) {
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
usb_free_urb(urb);
}
/* This should be handeled by post_reset already */

dongle->pairing = false;
dongle->pairing_scan_idx = 0;
dongle->last_wlan_rx = 0;

return xone_dongle_init(dongle);
return 0;
}

static const struct usb_device_id xone_dongle_id_table[] = {
Expand Down