linux-input.vger.kernel.org archive mirror
 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 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).