Skip to content

Commit 0c28e4d

Browse files
stuarthayhurstJiri Kosina
authored andcommitted
HID: corsair-void: Update power supply values with a unified work handler
corsair_void_process_receiver can be called from an interrupt context, locking battery_mutex in it was causing a kernel panic. Fix it by moving the critical section into its own work, sharing this work with battery_add_work and battery_remove_work to remove the need for any locking Closes: https://bugzilla.suse.com/show_bug.cgi?id=1236843 Fixes: 6ea2a6f ("HID: corsair-void: Add Corsair Void headset family driver") Cc: [email protected] Signed-off-by: Stuart Hayhurst <[email protected]> Reviewed-by: Jiri Slaby <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent b051ffa commit 0c28e4d

File tree

1 file changed

+43
-40
lines changed

1 file changed

+43
-40
lines changed

drivers/hid/hid-corsair-void.c

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,9 @@
7171

7272
#include <linux/bitfield.h>
7373
#include <linux/bitops.h>
74-
#include <linux/cleanup.h>
7574
#include <linux/device.h>
7675
#include <linux/hid.h>
7776
#include <linux/module.h>
78-
#include <linux/mutex.h>
7977
#include <linux/power_supply.h>
8078
#include <linux/usb.h>
8179
#include <linux/workqueue.h>
@@ -120,6 +118,12 @@ enum {
120118
CORSAIR_VOID_BATTERY_CHARGING = 5,
121119
};
122120

121+
enum {
122+
CORSAIR_VOID_ADD_BATTERY = 0,
123+
CORSAIR_VOID_REMOVE_BATTERY = 1,
124+
CORSAIR_VOID_UPDATE_BATTERY = 2,
125+
};
126+
123127
static enum power_supply_property corsair_void_battery_props[] = {
124128
POWER_SUPPLY_PROP_STATUS,
125129
POWER_SUPPLY_PROP_PRESENT,
@@ -155,12 +159,12 @@ struct corsair_void_drvdata {
155159

156160
struct power_supply *battery;
157161
struct power_supply_desc battery_desc;
158-
struct mutex battery_mutex;
159162

160163
struct delayed_work delayed_status_work;
161164
struct delayed_work delayed_firmware_work;
162-
struct work_struct battery_remove_work;
163-
struct work_struct battery_add_work;
165+
166+
unsigned long battery_work_flags;
167+
struct work_struct battery_work;
164168
};
165169

166170
/*
@@ -260,11 +264,9 @@ static void corsair_void_process_receiver(struct corsair_void_drvdata *drvdata,
260264

261265
/* Inform power supply if battery values changed */
262266
if (memcmp(&orig_battery_data, battery_data, sizeof(*battery_data))) {
263-
scoped_guard(mutex, &drvdata->battery_mutex) {
264-
if (drvdata->battery) {
265-
power_supply_changed(drvdata->battery);
266-
}
267-
}
267+
set_bit(CORSAIR_VOID_UPDATE_BATTERY,
268+
&drvdata->battery_work_flags);
269+
schedule_work(&drvdata->battery_work);
268270
}
269271
}
270272

@@ -536,29 +538,11 @@ static void corsair_void_firmware_work_handler(struct work_struct *work)
536538

537539
}
538540

539-
static void corsair_void_battery_remove_work_handler(struct work_struct *work)
540-
{
541-
struct corsair_void_drvdata *drvdata;
542-
543-
drvdata = container_of(work, struct corsair_void_drvdata,
544-
battery_remove_work);
545-
scoped_guard(mutex, &drvdata->battery_mutex) {
546-
if (drvdata->battery) {
547-
power_supply_unregister(drvdata->battery);
548-
drvdata->battery = NULL;
549-
}
550-
}
551-
}
552-
553-
static void corsair_void_battery_add_work_handler(struct work_struct *work)
541+
static void corsair_void_add_battery(struct corsair_void_drvdata *drvdata)
554542
{
555-
struct corsair_void_drvdata *drvdata;
556543
struct power_supply_config psy_cfg = {};
557544
struct power_supply *new_supply;
558545

559-
drvdata = container_of(work, struct corsair_void_drvdata,
560-
battery_add_work);
561-
guard(mutex)(&drvdata->battery_mutex);
562546
if (drvdata->battery)
563547
return;
564548

@@ -583,16 +567,42 @@ static void corsair_void_battery_add_work_handler(struct work_struct *work)
583567
drvdata->battery = new_supply;
584568
}
585569

570+
static void corsair_void_battery_work_handler(struct work_struct *work)
571+
{
572+
struct corsair_void_drvdata *drvdata = container_of(work,
573+
struct corsair_void_drvdata, battery_work);
574+
575+
bool add_battery = test_and_clear_bit(CORSAIR_VOID_ADD_BATTERY,
576+
&drvdata->battery_work_flags);
577+
bool remove_battery = test_and_clear_bit(CORSAIR_VOID_REMOVE_BATTERY,
578+
&drvdata->battery_work_flags);
579+
bool update_battery = test_and_clear_bit(CORSAIR_VOID_UPDATE_BATTERY,
580+
&drvdata->battery_work_flags);
581+
582+
if (add_battery && !remove_battery) {
583+
corsair_void_add_battery(drvdata);
584+
} else if (remove_battery && !add_battery && drvdata->battery) {
585+
power_supply_unregister(drvdata->battery);
586+
drvdata->battery = NULL;
587+
}
588+
589+
if (update_battery && drvdata->battery)
590+
power_supply_changed(drvdata->battery);
591+
592+
}
593+
586594
static void corsair_void_headset_connected(struct corsair_void_drvdata *drvdata)
587595
{
588-
schedule_work(&drvdata->battery_add_work);
596+
set_bit(CORSAIR_VOID_ADD_BATTERY, &drvdata->battery_work_flags);
597+
schedule_work(&drvdata->battery_work);
589598
schedule_delayed_work(&drvdata->delayed_firmware_work,
590599
msecs_to_jiffies(100));
591600
}
592601

593602
static void corsair_void_headset_disconnected(struct corsair_void_drvdata *drvdata)
594603
{
595-
schedule_work(&drvdata->battery_remove_work);
604+
set_bit(CORSAIR_VOID_REMOVE_BATTERY, &drvdata->battery_work_flags);
605+
schedule_work(&drvdata->battery_work);
596606

597607
corsair_void_set_unknown_wireless_data(drvdata);
598608
corsair_void_set_unknown_batt(drvdata);
@@ -678,13 +688,7 @@ static int corsair_void_probe(struct hid_device *hid_dev,
678688
drvdata->battery_desc.get_property = corsair_void_battery_get_property;
679689

680690
drvdata->battery = NULL;
681-
INIT_WORK(&drvdata->battery_remove_work,
682-
corsair_void_battery_remove_work_handler);
683-
INIT_WORK(&drvdata->battery_add_work,
684-
corsair_void_battery_add_work_handler);
685-
ret = devm_mutex_init(drvdata->dev, &drvdata->battery_mutex);
686-
if (ret)
687-
return ret;
691+
INIT_WORK(&drvdata->battery_work, corsair_void_battery_work_handler);
688692

689693
ret = sysfs_create_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
690694
if (ret)
@@ -721,8 +725,7 @@ static void corsair_void_remove(struct hid_device *hid_dev)
721725
struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev);
722726

723727
hid_hw_stop(hid_dev);
724-
cancel_work_sync(&drvdata->battery_remove_work);
725-
cancel_work_sync(&drvdata->battery_add_work);
728+
cancel_work_sync(&drvdata->battery_work);
726729
if (drvdata->battery)
727730
power_supply_unregister(drvdata->battery);
728731

0 commit comments

Comments
 (0)