All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 1/4] HID:hid-logitech: Add modparam to allow/disable switch to native mode
@ 2014-08-21  4:51 Simon Wood
  2014-08-21  4:51 ` [RFC 2/4] HID:hid-logitech: New detection of native capable devices Simon Wood
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Simon Wood @ 2014-08-21  4:51 UTC (permalink / raw)
  To: linux-input; +Cc: linux-kernel, Jiri Kosina, Simon Wood

---
 drivers/hid/hid-lg.c    | 17 ++++++++++++++++-
 drivers/hid/hid-lg.h    |  7 +++++--
 drivers/hid/hid-lg4ff.c |  4 ++--
 3 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index a976f48..81ba24d 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -334,6 +334,16 @@ static __u8 momo2_rdesc_fixed[] = {
 };
 
 /*
+ * Certain Logitech wheels provide various compatibililty modes
+ * for games that cannot handle their advanced features properly.
+ * This switch forces the wheel into a specific compatibililty
+ * instead of its native mode
+ */
+#ifdef CONFIG_LOGIWHEELS_FF
+static int lg4ff_switch_mode = LG4FF_MSW_NAT;	/* Default to native mode */
+#endif
+
+/*
  * Certain Logitech keyboards send in report #3 keys which are far
  * above the logical maximum described in descriptor. This extends
  * the original value of 0x28c of logical maximum to 0x104d
@@ -717,7 +727,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	if (drv_data->quirks & LG_FF3)
 		lg3ff_init(hdev);
 	if (drv_data->quirks & LG_FF4)
-		lg4ff_init(hdev);
+		lg4ff_init(hdev, lg4ff_switch_mode);
 
 	return 0;
 err_free:
@@ -818,4 +828,9 @@ static struct hid_driver lg_driver = {
 };
 module_hid_driver(lg_driver);
 
+#ifdef CONFIG_LOGIWHEELS_FF
+module_param_named(lg4ff_switch_mode, lg4ff_switch_mode, int, S_IRUGO);
+MODULE_PARM_DESC(lg4ff_switch_mode, "Enable switch from compatibililty mode to native mode (only certain devices)");
+#endif
+
 MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index 142ce3f..fc4bdae 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -25,14 +25,17 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
 #endif
 
 #ifdef CONFIG_LOGIWHEELS_FF
+#define LG4FF_MSW_NAT -1	/* allow native mode */
+#define LG4FF_MSW_EMU 0		/* remain in or force emulation mode */
+
 int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 			     struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
-int lg4ff_init(struct hid_device *hdev);
+int lg4ff_init(struct hid_device *hdev, const int switch_mode);
 int lg4ff_deinit(struct hid_device *hdev);
 #else
 static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 					   struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
-static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
+static inline int lg4ff_init(struct hid_device *hdev, const int switch_mode) { return -1; }
 static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
 #endif
 
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index cc2bd20..9247227 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -556,7 +556,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
 }
 #endif
 
-int lg4ff_init(struct hid_device *hid)
+int lg4ff_init(struct hid_device *hid, const int switch_mode)
 {
 	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 	struct input_dev *dev = hidinput->input;
@@ -594,7 +594,7 @@ int lg4ff_init(struct hid_device *hid)
 	rev_maj = bcdDevice >> 8;
 	rev_min = bcdDevice & 0xff;
 
-	if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
+	if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL && switch_mode != LG4FF_MSW_EMU) {
 		dbg_hid("Generic wheel detected, can it do native?\n");
 		dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC 2/4] HID:hid-logitech: New detection of native capable devices
  2014-08-21  4:51 [RFC 1/4] HID:hid-logitech: Add modparam to allow/disable switch to native mode Simon Wood
@ 2014-08-21  4:51 ` Simon Wood
  2014-08-21  4:51 ` [RFC 3/4] HID:hid-logitech: Use new native switch method Simon Wood
  2014-08-21  4:51 ` [RFC 4/4] HID:hid-logitech: Add mode control via /sys interface Simon Wood
  2 siblings, 0 replies; 4+ messages in thread
From: Simon Wood @ 2014-08-21  4:51 UTC (permalink / raw)
  To: linux-input; +Cc: linux-kernel, Jiri Kosina, Simon Wood

---
 drivers/hid/hid-lg.h    |   5 +++
 drivers/hid/hid-lg4ff.c | 115 ++++++++++++++++++++++++------------------------
 2 files changed, 63 insertions(+), 57 deletions(-)

diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index fc4bdae..cf442e5 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -27,6 +27,11 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
 #ifdef CONFIG_LOGIWHEELS_FF
 #define LG4FF_MSW_NAT -1	/* allow native mode */
 #define LG4FF_MSW_EMU 0		/* remain in or force emulation mode */
+#define LG4FF_MSW_DFP 1
+#define LG4FF_MSW_G25 2
+#define LG4FF_MSW_DFGT 3
+#define LG4FF_MSW_G27 4
+#define LG4FF_MSW_MAX 5		/* end-stop */
 
 int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 			     struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 9247227..eda07a2 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -56,6 +56,7 @@ static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_r
 
 struct lg4ff_device_entry {
 	__u32 product_id;
+	__u16 type;
 	__u16 range;
 	__u16 min_range;
 	__u16 max_range;
@@ -73,23 +74,19 @@ static const signed short lg4ff_wheel_effects[] = {
 	-1
 };
 
-struct lg4ff_wheel {
-	const __u32 product_id;
-	const signed short *ff_effects;
-	const __u16 min_range;
-	const __u16 max_range;
+struct lg4ff_mode_switcher {
+	const u16 bcdDevice;
+	const u16 mask;
+	const u16 type;
+	const __u32 native_pid;
 	void (*set_range)(struct hid_device *hid, u16 range);
 };
 
-static const struct lg4ff_wheel lg4ff_devices[] = {
-	{USB_DEVICE_ID_LOGITECH_WHEEL,       lg4ff_wheel_effects, 40, 270, NULL},
-	{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL,  lg4ff_wheel_effects, 40, 270, NULL},
-	{USB_DEVICE_ID_LOGITECH_DFP_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
-	{USB_DEVICE_ID_LOGITECH_G25_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
-	{USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,  lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
-	{USB_DEVICE_ID_LOGITECH_G27_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
-	{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
-	{USB_DEVICE_ID_LOGITECH_WII_WHEEL,   lg4ff_wheel_effects, 40, 270, NULL}
+static const struct lg4ff_mode_switcher lg4ff_mode_switchers[] = {	/* Note: Order is important for detection process */
+	{0x1300, 0xff00, LG4FF_MSW_DFGT, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, hid_lg4ff_set_range_g25},
+	{0x1230, 0xfff0, LG4FF_MSW_G27, USB_DEVICE_ID_LOGITECH_G27_WHEEL, hid_lg4ff_set_range_g25},
+	{0x1200, 0xff00, LG4FF_MSW_G25, USB_DEVICE_ID_LOGITECH_G25_WHEEL, hid_lg4ff_set_range_g25},
+	{0x1000, 0xf000, LG4FF_MSW_DFP, USB_DEVICE_ID_LOGITECH_DFP_WHEEL, hid_lg4ff_set_range_dfp},
 };
 
 struct lg4ff_native_cmd {
@@ -570,50 +567,12 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 	if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
 		return -1;
 
-	/* Check what wheel has been connected */
-	for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
-		if (hid->product == lg4ff_devices[i].product_id) {
-			dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
-			break;
-		}
-	}
-
-	if (i == ARRAY_SIZE(lg4ff_devices)) {
-		hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
-			     "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
-		return -1;
-	}
-
 	/* Attempt to switch wheel to native mode when applicable */
 	udesc = &(hid_to_usb_dev(hid)->descriptor);
 	if (!udesc) {
 		hid_err(hid, "NULL USB device descriptor\n");
 		return -1;
 	}
-	bcdDevice = le16_to_cpu(udesc->bcdDevice);
-	rev_maj = bcdDevice >> 8;
-	rev_min = bcdDevice & 0xff;
-
-	if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL && switch_mode != LG4FF_MSW_EMU) {
-		dbg_hid("Generic wheel detected, can it do native?\n");
-		dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
-
-		for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
-			if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
-				hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
-				hid_info(hid, "Switched to native mode\n");
-			}
-		}
-	}
-
-	/* Set supported force feedback capabilities */
-	for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
-		set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
-
-	error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
-
-	if (error)
-		return error;
 
 	/* Get private driver data */
 	drv_data = hid_get_drvdata(hid);
@@ -630,10 +589,52 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 	}
 	drv_data->device_props = entry;
 
-	entry->product_id = lg4ff_devices[i].product_id;
-	entry->min_range = lg4ff_devices[i].min_range;
-	entry->max_range = lg4ff_devices[i].max_range;
-	entry->set_range = lg4ff_devices[i].set_range;
+	entry->product_id = hid->product;
+	entry->set_range = NULL;
+	entry->type = LG4FF_MSW_EMU;
+
+	/* Check which wheel has been connected */
+	bcdDevice = le16_to_cpu(udesc->bcdDevice);
+	rev_maj = bcdDevice >> 8;
+	rev_min = bcdDevice & 0xff;
+
+	for (i = 0; i < ARRAY_SIZE(lg4ff_mode_switchers); i++) {
+		const struct lg4ff_mode_switcher *s = &lg4ff_mode_switchers[i];
+
+		if (s->bcdDevice != (bcdDevice & s->mask))
+			continue;
+
+		entry->type = s->type;
+		dbg_hid("Native capable device detected (Native ID %04X, type %d)\n", s->native_pid, s->type);
+
+		if (hid->product == s->native_pid) {
+			entry->product_id = s->native_pid;
+			entry->min_range = 40;
+			entry->max_range = 900;
+			entry->set_range = s->set_range;
+		}
+
+		if (hid->product != s->native_pid && switch_mode != LG4FF_MSW_EMU) {
+			dbg_hid("Switching to native mode\n");
+
+			for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
+				if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
+					hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
+					hid_info(hid, "Switched to native mode\n");
+				}
+			}
+		}
+		break;
+	}
+
+	/* Set supported force feedback capabilities */
+	for (j = 0; lg4ff_wheel_effects[j] >= 0; j++)
+		set_bit(lg4ff_wheel_effects[j], dev->ffbit);
+
+	error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
+
+	if (error)
+		return error;
 
 	/* Check if autocentering is available and
 	 * set the centering force to zero by default */
@@ -663,7 +664,7 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 	for (j = 0; j < 5; j++)
 		entry->led[j] = NULL;
 
-	if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
+	if (entry->type == LG4FF_MSW_G27) {
 		struct led_classdev *led;
 		size_t name_sz;
 		char *name;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC 3/4] HID:hid-logitech: Use new native switch method
  2014-08-21  4:51 [RFC 1/4] HID:hid-logitech: Add modparam to allow/disable switch to native mode Simon Wood
  2014-08-21  4:51 ` [RFC 2/4] HID:hid-logitech: New detection of native capable devices Simon Wood
@ 2014-08-21  4:51 ` Simon Wood
  2014-08-21  4:51 ` [RFC 4/4] HID:hid-logitech: Add mode control via /sys interface Simon Wood
  2 siblings, 0 replies; 4+ messages in thread
From: Simon Wood @ 2014-08-21  4:51 UTC (permalink / raw)
  To: linux-input; +Cc: linux-kernel, Jiri Kosina, Simon Wood

---
 drivers/hid/hid-lg4ff.c | 126 +++++++++++++++++++++---------------------------
 1 file changed, 55 insertions(+), 71 deletions(-)

diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index eda07a2..0ba0838 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -32,21 +32,10 @@
 #include "hid-lg.h"
 #include "hid-ids.h"
 
-#define DFGT_REV_MAJ 0x13
-#define DFGT_REV_MIN 0x22
-#define DFGT2_REV_MIN 0x26
-#define DFP_REV_MAJ 0x11
-#define DFP_REV_MIN 0x06
-#define FFEX_REV_MAJ 0x21
-#define FFEX_REV_MIN 0x00
-#define G25_REV_MAJ 0x12
-#define G25_REV_MIN 0x22
-#define G27_REV_MAJ 0x12
-#define G27_REV_MIN 0x38
-#define G27_2_REV_MIN 0x39
-
 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
 
+#define LG4FF_FFEX_BCDDEVICE 0x2100
+
 static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
 static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
 static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
@@ -89,48 +78,6 @@ static const struct lg4ff_mode_switcher lg4ff_mode_switchers[] = {	/* Note: Orde
 	{0x1000, 0xf000, LG4FF_MSW_DFP, USB_DEVICE_ID_LOGITECH_DFP_WHEEL, hid_lg4ff_set_range_dfp},
 };
 
-struct lg4ff_native_cmd {
-	const __u8 cmd_num;	/* Number of commands to send */
-	const __u8 cmd[];
-};
-
-struct lg4ff_usb_revision {
-	const __u16 rev_maj;
-	const __u16 rev_min;
-	const struct lg4ff_native_cmd *command;
-};
-
-static const struct lg4ff_native_cmd native_dfp = {
-	1,
-	{0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
-};
-
-static const struct lg4ff_native_cmd native_dfgt = {
-	2,
-	{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 1st command */
-	 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00}	/* 2nd command */
-};
-
-static const struct lg4ff_native_cmd native_g25 = {
-	1,
-	{0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
-};
-
-static const struct lg4ff_native_cmd native_g27 = {
-	2,
-	{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 1st command */
-	 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00}	/* 2nd command */
-};
-
-static const struct lg4ff_usb_revision lg4ff_revs[] = {
-	{DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt},	/* Driving Force GT */
-	{DFGT_REV_MAJ, DFGT2_REV_MIN, &native_dfgt},	/* Driving Force GT v2 */
-	{DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},	/* Driving Force Pro */
-	{G25_REV_MAJ,  G25_REV_MIN,  &native_g25},	/* G25 */
-	{G27_REV_MAJ,  G27_REV_MIN,  &native_g27},	/* G27 */
-	{G27_REV_MAJ,  G27_2_REV_MIN,  &native_g27},	/* G27 v2 */
-};
-
 /* Recalculates X axis value accordingly to currently selected range */
 static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
 {
@@ -397,19 +344,63 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
 	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 }
 
-static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
+static int lg4ff_switch_mode(struct hid_device *hid, __u16 type, int mode)
 {
 	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);
-	__u8 i, j;
+	__s32 *value = report->field[0]->value;
+
+	if (mode >= LG4FF_MSW_MAX || mode <= LG4FF_MSW_NAT) mode = type;
+
+	if (type == LG4FF_MSW_G25 && mode == LG4FF_MSW_G25) {
+		value[0] = 0xf8;
+		value[1] = 0x10;
+		value[2] = 0x00;
+		value[3] = 0x00;
+		value[4] = 0x00;
+		value[5] = 0x00;
+		value[6] = 0x00;
+
+		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+		return 0;
+	}
 
-	j = 0;
-	while (j < 7*cmd->cmd_num) {
-		for (i = 0; i < 7; i++)
-			report->field[0]->value[i] = cmd->cmd[j++];
+	if (mode == LG4FF_MSW_DFP) {
+		value[0] = 0xf8;
+		value[1] = 0x01;
+		value[2] = 0x00;
+		value[3] = 0x00;
+		value[4] = 0x00;
+		value[5] = 0x00;
+		value[6] = 0x00;
 
 		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+		return 0;
 	}
+
+	/* Prevent compat mode on USB reset */
+	if (type == LG4FF_MSW_DFGT || type == LG4FF_MSW_G27) {
+		value[0] = 0xf8;
+		value[1] = 0x0a;
+		value[2] = 0x00;
+		value[3] = 0x00;
+		value[4] = 0x00;
+		value[5] = 0x00;
+		value[6] = 0x00;
+
+		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	}
+
+	value[0] = 0xf8;
+	value[1] = 0x09;
+	value[2] = mode;
+	value[3] = 0x01;
+	value[4] = 0x00;
+	value[5] = 0x00;
+	value[6] = 0x00;
+
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	return 0;
 }
 
 /* Read current range and display it in terminal */
@@ -608,21 +599,14 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 		dbg_hid("Native capable device detected (Native ID %04X, type %d)\n", s->native_pid, s->type);
 
 		if (hid->product == s->native_pid) {
-			entry->product_id = s->native_pid;
 			entry->min_range = 40;
 			entry->max_range = 900;
 			entry->set_range = s->set_range;
 		}
 
-		if (hid->product != s->native_pid && switch_mode != LG4FF_MSW_EMU) {
+		if (hid->product == USB_DEVICE_ID_LOGITECH_WHEEL && switch_mode != LG4FF_MSW_EMU) {
 			dbg_hid("Switching to native mode\n");
-
-			for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
-				if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
-					hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
-					hid_info(hid, "Switched to native mode\n");
-				}
-			}
+			lg4ff_switch_mode(hid, s->type, switch_mode);
 		}
 		break;
 	}
@@ -639,7 +623,7 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 	/* Check if autocentering is available and
 	 * set the centering force to zero by default */
 	if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
-		if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)	/* Formula Force EX expects different autocentering command */
+		if (bcdDevice == LG4FF_FFEX_BCDDEVICE)	/* Formula Force EX does not seem to support hi-res autocentering */
 			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
 		else
 			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC 4/4] HID:hid-logitech: Add mode control via /sys interface
  2014-08-21  4:51 [RFC 1/4] HID:hid-logitech: Add modparam to allow/disable switch to native mode Simon Wood
  2014-08-21  4:51 ` [RFC 2/4] HID:hid-logitech: New detection of native capable devices Simon Wood
  2014-08-21  4:51 ` [RFC 3/4] HID:hid-logitech: Use new native switch method Simon Wood
@ 2014-08-21  4:51 ` Simon Wood
  2 siblings, 0 replies; 4+ messages in thread
From: Simon Wood @ 2014-08-21  4:51 UTC (permalink / raw)
  To: linux-input; +Cc: linux-kernel, Jiri Kosina, Simon Wood

---
 drivers/hid/hid-lg4ff.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 78 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 0ba0838..1be561e 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -41,11 +41,16 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
 static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
 static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
 
+static ssize_t lg4ff_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t lg4ff_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+
 static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_range_store);
+static DEVICE_ATTR(mode, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_mode_show, lg4ff_mode_store);
 
 struct lg4ff_device_entry {
 	__u32 product_id;
 	__u16 type;
+	__u16 mode;
 	__u16 range;
 	__u16 min_range;
 	__u16 max_range;
@@ -362,7 +367,7 @@ static int lg4ff_switch_mode(struct hid_device *hid, __u16 type, int mode)
 		value[6] = 0x00;
 
 		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
-		return 0;
+		return LG4FF_MSW_G25;
 	}
 
 	if (mode == LG4FF_MSW_DFP) {
@@ -375,7 +380,7 @@ static int lg4ff_switch_mode(struct hid_device *hid, __u16 type, int mode)
 		value[6] = 0x00;
 
 		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
-		return 0;
+		return LG4FF_MSW_DFP;
 	}
 
 	/* Prevent compat mode on USB reset */
@@ -400,7 +405,7 @@ static int lg4ff_switch_mode(struct hid_device *hid, __u16 type, int mode)
 	value[6] = 0x00;
 
 	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
-	return 0;
+	return mode;
 }
 
 /* Read current range and display it in terminal */
@@ -461,6 +466,66 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
 	return count;
 }
 
+/* Read current mode and display it in terminal */
+static ssize_t lg4ff_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hid_device *hid = to_hid_device(dev);
+	struct lg4ff_device_entry *entry;
+	struct lg_drv_data *drv_data;
+	size_t count;
+
+	drv_data = hid_get_drvdata(hid);
+	if (!drv_data) {
+		hid_err(hid, "Private driver data not found!\n");
+		return 0;
+	}
+
+	entry = drv_data->device_props;
+	if (!entry) {
+		hid_err(hid, "Device properties not found!\n");
+		return 0;
+	}
+
+	count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->mode);
+	return count;
+}
+
+/* Set mode to user specified value */
+static ssize_t lg4ff_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hid_device *hid = to_hid_device(dev);
+	struct lg4ff_device_entry *entry;
+	struct lg_drv_data *drv_data;
+	int err;
+	__u16 mode = simple_strtoul(buf, NULL, 10);
+
+	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;
+	}
+
+	if (mode == entry->mode) {
+		dbg_hid("Device is already in mode %d\n", mode);
+		return count;
+	}
+
+	err = lg4ff_switch_mode(hid, entry->type, mode);
+	if (err != mode) {
+		hid_err(hid, "Unable to switch mode\n");
+		return -1;
+	}
+
+	entry->mode = mode;
+	return count;
+}
+
 #ifdef CONFIG_LEDS_CLASS
 static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
 {
@@ -583,6 +648,7 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 	entry->product_id = hid->product;
 	entry->set_range = NULL;
 	entry->type = LG4FF_MSW_EMU;
+	entry->mode = LG4FF_MSW_EMU;
 
 	/* Check which wheel has been connected */
 	bcdDevice = le16_to_cpu(udesc->bcdDevice);
@@ -602,6 +668,11 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 			entry->min_range = 40;
 			entry->max_range = 900;
 			entry->set_range = s->set_range;
+
+			if (switch_mode == LG4FF_MSW_NAT)
+				entry->mode = s->type;
+			else
+				entry->mode = switch_mode;
 		}
 
 		if (hid->product == USB_DEVICE_ID_LOGITECH_WHEEL && switch_mode != LG4FF_MSW_EMU) {
@@ -635,6 +706,9 @@ int lg4ff_init(struct hid_device *hid, const int switch_mode)
 	error = device_create_file(&hid->dev, &dev_attr_range);
 	if (error)
 		return error;
+	error = device_create_file(&hid->dev, &dev_attr_mode);
+	if (error)
+		return error;
 	dbg_hid("sysfs interface created\n");
 
 	/* Set the maximum range to start with */
@@ -705,6 +779,7 @@ int lg4ff_deinit(struct hid_device *hid)
 	struct lg_drv_data *drv_data;
 
 	device_remove_file(&hid->dev, &dev_attr_range);
+	device_remove_file(&hid->dev, &dev_attr_mode);
 
 	drv_data = hid_get_drvdata(hid);
 	if (!drv_data) {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-08-21  5:01 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-21  4:51 [RFC 1/4] HID:hid-logitech: Add modparam to allow/disable switch to native mode Simon Wood
2014-08-21  4:51 ` [RFC 2/4] HID:hid-logitech: New detection of native capable devices Simon Wood
2014-08-21  4:51 ` [RFC 3/4] HID:hid-logitech: Use new native switch method Simon Wood
2014-08-21  4:51 ` [RFC 4/4] HID:hid-logitech: Add mode control via /sys interface Simon Wood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.