aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff27
-rw-r--r--drivers/hid/hid-lg4ff.c204
2 files changed, 201 insertions, 30 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
index 60f24a1d8119..b3f6a2ac5007 100644
--- a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
+++ b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
@@ -15,7 +15,32 @@ Description: Displays a set of alternate modes supported by a wheel. Each
15 Tag: Mode Name 15 Tag: Mode Name
16 Currently active mode is marked with an asterisk. List also 16 Currently active mode is marked with an asterisk. List also
17 contains an abstract item "native" which always denotes the 17 contains an abstract item "native" which always denotes the
18 native mode of the wheel. 18 native mode of the wheel. Echoing the mode tag switches the
19 wheel into the corresponding mode. Depending on the exact model
20 of the wheel not all listed modes might always be selectable.
21 If a wheel cannot be switched into the desired mode, -EINVAL
22 is returned accompanied with an explanatory message in the
23 kernel log.
24 This entry is not created for devices that have only one mode.
25
26 Currently supported mode switches:
27 Driving Force Pro:
28 DF-EX --> DFP
29
30 G25:
31 DF-EX --> DFP --> G25
32
33 G27:
34 DF-EX <*> DFP <-> G25 <-> G27
35 DF-EX <*--------> G25 <-> G27
36 DF-EX <*----------------> G27
37
38 DFGT:
39 DF-EX <*> DFP <-> DFGT
40 DF-EX <*--------> DFGT
41
42 * hid_logitech module must be loaded with lg4ff_no_autoswitch=1
43 parameter set in order for the switch to DF-EX mode to work.
19 44
20What: /sys/bus/hid/drivers/logitech/<dev>/real_id 45What: /sys/bus/hid/drivers/logitech/<dev>/real_id
21Date: Feb 2015 46Date: Feb 2015
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index dd307724965f..854982be3194 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -201,26 +201,47 @@ static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = {
201}; 201};
202 202
203/* Compatibility mode switching commands */ 203/* Compatibility mode switching commands */
204static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_dfp = { 204/* EXT_CMD9 - Understood by G27 and DFGT */
205 1, 205static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfex = {
206 {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} 206 2,
207 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
208 0xf8, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DF-EX with detach */
207}; 209};
208 210
209static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_dfgt = { 211static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfp = {
210 2, 212 2,
211 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ 213 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
212 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ 214 0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFP with detach */
213}; 215};
214 216
215static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_g25 = { 217static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g25 = {
216 1, 218 2,
217 {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00} 219 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
220 0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G25 with detach */
218}; 221};
219 222
220static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_g27 = { 223static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfgt = {
221 2, 224 2,
222 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ 225 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
223 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ 226 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFGT with detach */
227};
228
229static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = {
230 2,
231 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
232 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */
233};
234
235/* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */
236static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = {
237 1,
238 {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
239};
240
241/* EXT_CMD16 - Understood by G25 and G27 */
242static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
243 1,
244 {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
224}; 245};
225 246
226/* Recalculates X axis value accordingly to currently selected range */ 247/* Recalculates X axis value accordingly to currently selected range */
@@ -489,6 +510,63 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
489 hid_hw_request(hid, report, HID_REQ_SET_REPORT); 510 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
490} 511}
491 512
513static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
514{
515 switch (real_product_id) {
516 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
517 switch (target_product_id) {
518 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
519 return &lg4ff_mode_switch_ext01_dfp;
520 /* DFP can only be switched to its native mode */
521 default:
522 return NULL;
523 }
524 break;
525 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
526 switch (target_product_id) {
527 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
528 return &lg4ff_mode_switch_ext01_dfp;
529 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
530 return &lg4ff_mode_switch_ext16_g25;
531 /* G25 can only be switched to DFP mode or its native mode */
532 default:
533 return NULL;
534 }
535 break;
536 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
537 switch (target_product_id) {
538 case USB_DEVICE_ID_LOGITECH_WHEEL:
539 return &lg4ff_mode_switch_ext09_dfex;
540 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
541 return &lg4ff_mode_switch_ext09_dfp;
542 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
543 return &lg4ff_mode_switch_ext09_g25;
544 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
545 return &lg4ff_mode_switch_ext09_g27;
546 /* G27 can only be switched to DF-EX, DFP, G25 or its native mode */
547 default:
548 return NULL;
549 }
550 break;
551 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
552 switch (target_product_id) {
553 case USB_DEVICE_ID_LOGITECH_WHEEL:
554 return &lg4ff_mode_switch_ext09_dfex;
555 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
556 return &lg4ff_mode_switch_ext09_dfp;
557 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
558 return &lg4ff_mode_switch_ext09_dfgt;
559 /* DFGT can only be switched to DF-EX, DFP or its native mode */
560 default:
561 return NULL;
562 }
563 break;
564 /* No other wheels have multiple modes */
565 default:
566 return NULL;
567 }
568}
569
492static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s) 570static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
493{ 571{
494 struct usb_device *usbdev = hid_to_usb_dev(hid); 572 struct usb_device *usbdev = hid_to_usb_dev(hid);
@@ -558,7 +636,87 @@ static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attr
558 636
559static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 637static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
560{ 638{
561 return -ENOSYS; 639 struct hid_device *hid = to_hid_device(dev);
640 struct lg4ff_device_entry *entry;
641 struct lg_drv_data *drv_data;
642 const struct lg4ff_compat_mode_switch *s;
643 u16 target_product_id = 0;
644 int i, ret;
645 char *lbuf;
646
647 drv_data = hid_get_drvdata(hid);
648 if (!drv_data) {
649 hid_err(hid, "Private driver data not found!\n");
650 return -EINVAL;
651 }
652
653 entry = drv_data->device_props;
654 if (!entry) {
655 hid_err(hid, "Device properties not found!\n");
656 return -EINVAL;
657 }
658
659 /* Allow \n at the end of the input parameter */
660 lbuf = kasprintf(GFP_KERNEL, "%s", buf);
661 if (!lbuf)
662 return -ENOMEM;
663
664 i = strlen(lbuf);
665 if (lbuf[i-1] == '\n') {
666 if (i == 1) {