From: "Michal Malý" <madcatxster@devoid-pointer.net>
To: jkosina@suse.cz
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.or,
elias.vds@gmail.com, simon@mungewell.org,
"Michal Malý" <madcatxster@devoid-pointer.net>
Subject: [PATCH v2 10/15] HID: hid-lg4ff: Protect concurrent access to output HID report with a spinlock
Date: Wed, 8 Apr 2015 22:56:48 +0200 [thread overview]
Message-ID: <1428526613-24239-11-git-send-email-madcatxster@devoid-pointer.net> (raw)
In-Reply-To: <1428526613-24239-1-git-send-email-madcatxster@devoid-pointer.net>
Protect concurrent access to output HID report with a spinlock. The HID report
structure used to submit commands to wheels is shared amongst all functions that
need to do so and some of these functions can be executed in parallel.
Additionally, lg4ff_play() can be called from interrupt context by ff-memless
module. Locking is necessary to prevent sending bogus data to the wheels.
Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
drivers/hid/hid-lg4ff.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 110 insertions(+)
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 031b8ab..f7f4eb6 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -89,6 +89,7 @@ struct lg4ff_wheel_data {
};
struct lg4ff_device_entry {
+ spinlock_t report_lock; /* Protect output HID report */
struct lg4ff_wheel_data wdata;
};
@@ -303,9 +304,24 @@ static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effec
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
s32 *value = report->field[0]->value;
int x;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return -EINVAL;
+ }
+
#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch (effect->type) {
@@ -313,6 +329,7 @@ static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effec
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
+ spin_lock_irqsave(&entry->report_lock, flags);
if (x == 0x80) {
/* De-activate force in slot-1*/
value[0] = 0x13;
@@ -324,6 +341,7 @@ static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effec
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return 0;
}
@@ -336,6 +354,7 @@ static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effec
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
break;
}
return 0;
@@ -352,6 +371,7 @@ static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
u32 expand_a, expand_b;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
+ unsigned long flags;
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
@@ -366,6 +386,7 @@ static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
}
/* De-activate Auto-Center */
+ spin_lock_irqsave(&entry->report_lock, flags);
if (magnitude == 0) {
value[0] = 0xf5;
value[1] = 0x00;
@@ -376,6 +397,7 @@ static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return;
}
@@ -417,6 +439,7 @@ static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends autocentering command compatible with Formula Force EX */
@@ -425,9 +448,25 @@ static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
s32 *value = report->field[0]->value;
magnitude = magnitude * 90 / 65535;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xfe;
value[1] = 0x03;
value[2] = magnitude >> 14;
@@ -437,6 +476,7 @@ static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends command to set range compatible with G25/G27/Driving Force GT */
@@ -444,10 +484,26 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
s32 *value = report->field[0]->value;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x81;
value[2] = range & 0x00ff;
@@ -457,6 +513,7 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends commands to set range compatible with Driving Force Pro wheel */
@@ -465,11 +522,27 @@ static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
int start_left, start_right, full_range;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
s32 *value = report->field[0]->value;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x00; /* Set later */
value[2] = 0x00;
@@ -498,6 +571,7 @@ static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
if (range == 200 || range == 900) { /* Do not apply any fine limit */
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return;
}
@@ -512,6 +586,7 @@ static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
value[6] = 0xff;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
@@ -575,9 +650,25 @@ static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
s32 *value = report->field[0]->value;
u8 i;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&entry->report_lock, flags);
for (i = 0; i < s->cmd_count; i++) {
u8 j;
@@ -586,6 +677,7 @@ static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
}
+ spin_unlock_irqrestore(&entry->report_lock, flags);
hid_hw_wait(hid);
return 0;
}
@@ -826,8 +918,24 @@ static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg_drv_data *drv_data;
+ struct lg4ff_device_entry *entry;
+ unsigned long flags;
s32 *value = report->field[0]->value;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x12;
value[2] = leds;
@@ -836,6 +944,7 @@ static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
value[5] = 0x00;
value[6] = 0x00;
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
@@ -1015,6 +1124,7 @@ int lg4ff_init(struct hid_device *hid)
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
+ spin_lock_init(&entry->report_lock);
drv_data->device_props = entry;
/* Check if a multimode wheel has been connected and
--
2.3.5
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2015-04-08 20:57 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-08 20:56 [PATCH v2 00/15] HID: hid-lg, hid-lg4ff: Mostly cleanup patches Michal Malý
2015-04-08 20:56 ` [PATCH v2 01/15] HID: hid-lg4ff: (Cleanup) Remove double underscore prefix from numeric types Michal Malý
2015-04-08 20:56 ` [PATCH v2 02/15] HID: hid-lg4ff: (Cleanup) Remove "hid_" prefix from some functions' names Michal Malý
2015-04-08 20:56 ` [PATCH v2 03/15] HID: hid-lg4ff: (Cleanup) Replace DEVICE_ATTR_RW with DEVICE_ATTR to have all internal functions prefixed with "lg4ff_" Michal Malý
2015-04-08 20:56 ` [PATCH v2 04/15] HID: hid-lg4ff: (Cleanup) Remove unused variable from the "lg4ff_device_entry" struct Michal Malý
2015-04-08 20:56 ` [PATCH v2 05/15] HID: hid-lg4ff: (Cleanup) Explicit casts from void * are not necessary Michal Malý
2015-04-08 20:56 ` [PATCH v2 06/15] HID: hid-lg4ff: Update a warning message for a case where device is incorrectly flagged to be handled by hid-lg4ff in hid-lg Michal Malý
2015-04-08 20:56 ` [PATCH v2 07/15] HID: hid-lg: Check return values from lg[N]ff_init() Michal Malý
2015-04-08 20:56 ` [PATCH v2 08/15] HID: hid-lg4ff: Make lg4ff_wheel_data a separate structure stored within lg4ff_device_entry. Adjust the initialization process accordingly Michal Malý
2015-04-08 20:56 ` [PATCH v2 09/15] HID: hid-lg4ff: Stop the hid device from lg4ff Michal Malý
2015-04-08 20:56 ` Michal Malý [this message]
2015-04-08 20:56 ` [PATCH v2 11/15] HID: hid-lg4ff: Store pointer to the output HID report struct in the device entry struct Michal Malý
2015-04-08 20:56 ` [PATCH v2 12/15] HID: hid-lg4ff: Constify those members of lg4ff_device_entry struct whose value is not supposed to change Michal Malý
2015-04-08 20:56 ` [PATCH v2 13/15] HID: hid-lg4ff: Allow the driver to continue without sysfs interface Michal Malý
2015-04-08 20:56 ` [PATCH v2 14/15] HID: hid-lg4ff: Update respective sysfs interface documentation Michal Malý
2015-04-08 20:56 ` [PATCH v2 15/15] HID: hid-lg: Only one of LG_FF flags can be set for a given device Michal Malý
2015-04-10 21:09 ` [PATCH v2 00/15] HID: hid-lg, hid-lg4ff: Mostly cleanup patches Jiri Kosina
2015-05-07 14:29 ` Jiri Kosina
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1428526613-24239-11-git-send-email-madcatxster@devoid-pointer.net \
--to=madcatxster@devoid-pointer.net \
--cc=elias.vds@gmail.com \
--cc=jkosina@suse.cz \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.or \
--cc=simon@mungewell.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).