aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Malý <madcatxster@devoid-pointer.net>2015-02-18 11:59:21 -0500
committerJiri Kosina <jkosina@suse.cz>2015-02-18 15:14:54 -0500
commitb96d23ec698fdc1fdf904e5547d9abb6354eef5c (patch)
tree042da86c415119ce04290685012627d32a79f937
parente7c234496d01c90a4b042d899a65e10f1f63ebc1 (diff)
HID: hid-lg4ff: Export the real wheel model and supported alternate modes
Display the real wheel model and supported alternate modes through sysfs. This applies only to multimode wheels. Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net> Tested-by: Simon Wood <simon@mungewell.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff20
-rw-r--r--drivers/hid/hid-lg4ff.c205
2 files changed, 218 insertions, 7 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
index 167d9032b970..60f24a1d8119 100644
--- a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
+++ b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
@@ -5,3 +5,23 @@ Contact: Michal Malý <madcatxster@gmail.com>
5Description: Display minimum, maximum and current range of the steering 5Description: Display minimum, maximum and current range of the steering
6 wheel. Writing a value within min and max boundaries sets the 6 wheel. Writing a value within min and max boundaries sets the
7 range of the wheel. 7 range of the wheel.
8
9What: /sys/bus/hid/drivers/logitech/<dev>/alternate_modes
10Date: Feb 2015
11KernelVersion: 4.1
12Contact: Michal Malý <madcatxster@gmail.com>
13Description: Displays a set of alternate modes supported by a wheel. Each
14 mode is listed as follows:
15 Tag: Mode Name
16 Currently active mode is marked with an asterisk. List also
17 contains an abstract item "native" which always denotes the
18 native mode of the wheel.
19
20What: /sys/bus/hid/drivers/logitech/<dev>/real_id
21Date: Feb 2015
22KernelVersion: 4.1
23Contact: Michal Malý <madcatxster@gmail.com>
24Description: Displays the real model of the wheel regardless of any
25 alternate mode the wheel might be switched to.
26 It is a read-only value.
27 This entry is not created for devices that have only one mode.
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 190c5e3f46ce..a64a35ed291f 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -34,10 +34,36 @@
34 34
35#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) 35#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
36 36
37#define LG4FF_MMODE_DONE 0 37#define LG4FF_MMODE_IS_MULTIMODE 0
38#define LG4FF_MMODE_SWITCHED 1 38#define LG4FF_MMODE_SWITCHED 1
39#define LG4FF_MMODE_NOT_MULTIMODE 2 39#define LG4FF_MMODE_NOT_MULTIMODE 2
40 40
41#define LG4FF_MODE_NATIVE_IDX 0
42#define LG4FF_MODE_DFEX_IDX 1
43#define LG4FF_MODE_DFP_IDX 2
44#define LG4FF_MODE_G25_IDX 3
45#define LG4FF_MODE_DFGT_IDX 4
46#define LG4FF_MODE_G27_IDX 5
47#define LG4FF_MODE_MAX_IDX 6
48
49#define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX)
50#define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX)
51#define LG4FF_MODE_DFP BIT(LG4FF_MODE_DFP_IDX)
52#define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX)
53#define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX)
54#define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX)
55
56#define LG4FF_DFEX_TAG "DF-EX"
57#define LG4FF_DFEX_NAME "Driving Force / Formula EX"
58#define LG4FF_DFP_TAG "DFP"
59#define LG4FF_DFP_NAME "Driving Force Pro"
60#define LG4FF_G25_TAG "G25"
61#define LG4FF_G25_NAME "G25 Racing Wheel"
62#define LG4FF_G27_TAG "G27"
63#define LG4FF_G27_NAME "G27 Racing Wheel"
64#define LG4FF_DFGT_TAG "DFGT"
65#define LG4FF_DFGT_NAME "Driving Force GT"
66
41#define LG4FF_FFEX_REV_MAJ 0x21 67#define LG4FF_FFEX_REV_MAJ 0x21
42#define LG4FF_FFEX_REV_MIN 0x00 68#define LG4FF_FFEX_REV_MIN 0x00
43 69
@@ -53,6 +79,10 @@ struct lg4ff_device_entry {
53 __u8 led_state; 79 __u8 led_state;
54 struct led_classdev *led[5]; 80 struct led_classdev *led[5];
55#endif 81#endif
82 u32 alternate_modes;
83 const char *real_tag;
84 const char *real_name;
85 u16 real_product_id;
56 struct list_head list; 86 struct list_head list;
57 void (*set_range)(struct hid_device *hid, u16 range); 87 void (*set_range)(struct hid_device *hid, u16 range);
58}; 88};
@@ -87,6 +117,19 @@ struct lg4ff_wheel_ident_checklist {
87 const struct lg4ff_wheel_ident_info *models[]; 117 const struct lg4ff_wheel_ident_info *models[];
88}; 118};
89 119
120struct lg4ff_multimode_wheel {
121 const u16 product_id;
122 const u32 alternate_modes;
123 const char *real_tag;
124 const char *real_name;
125};
126
127struct lg4ff_alternate_mode {
128 const u16 product_id;
129 const char *tag;
130 const char *name;
131};
132
90static const struct lg4ff_wheel lg4ff_devices[] = { 133static const struct lg4ff_wheel lg4ff_devices[] = {
91 {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, 134 {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
92 {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, 135 {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
@@ -98,6 +141,30 @@ static const struct lg4ff_wheel lg4ff_devices[] = {
98 {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} 141 {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}
99}; 142};
100 143
144static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = {
145 {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,
146 LG4FF_MODE_NATIVE | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
147 LG4FF_DFP_TAG, LG4FF_DFP_NAME},
148 {USB_DEVICE_ID_LOGITECH_G25_WHEEL,
149 LG4FF_MODE_NATIVE | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
150 LG4FF_G25_TAG, LG4FF_G25_NAME},
151 {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,
152 LG4FF_MODE_NATIVE | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
153 LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
154 {USB_DEVICE_ID_LOGITECH_G27_WHEEL,
155 LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
156 LG4FF_G27_TAG, LG4FF_G27_NAME},
157};
158
159static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = {
160 [LG4FF_MODE_NATIVE_IDX] = {0, "native", ""},
161 [LG4FF_MODE_DFEX_IDX] = {USB_DEVICE_ID_LOGITECH_WHEEL, LG4FF_DFEX_TAG, LG4FF_DFEX_NAME},
162 [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME},
163 [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME},
164 [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
165 [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME}
166};
167
101/* Multimode wheel identificators */ 168/* Multimode wheel identificators */
102static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = { 169static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = {
103 0xf000, 170 0xf000,
@@ -439,6 +506,61 @@ static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct
439 return 0; 506 return 0;
440} 507}
441 508
509static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attribute *attr, char *buf)
510{
511 struct hid_device *hid = to_hid_device(dev);
512 struct lg4ff_device_entry *entry;
513 struct lg_drv_data *drv_data;
514 ssize_t count = 0;
515 int i;
516
517 drv_data = hid_get_drvdata(hid);
518 if (!drv_data) {
519 hid_err(hid, "Private driver data not found!\n");
520 return 0;
521 }
522
523 entry = drv_data->device_props;
524 if (!entry) {
525 hid_err(hid, "Device properties not found!\n");
526 return 0;
527 }
528
529 if (!entry->real_name) {
530 hid_err(hid, "NULL pointer to string\n");
531 return 0;
532 }
533
534 for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
535 if (entry->alternate_modes & BIT(i)) {
536 /* Print tag and full name */
537 count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",
538 lg4ff_alternate_modes[i].tag,
539 !lg4ff_alternate_modes[i].product_id ? entry->real_name : lg4ff_alternate_modes[i].name);
540 if (count >= PAGE_SIZE - 1)
541 return count;
542
543 /* Mark the currently active mode with an asterisk */
544 if (lg4ff_alternate_modes[i].product_id == entry->product_id ||
545 (lg4ff_alternate_modes[i].product_id == 0 && entry->product_id == entry->real_product_id))
546 count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");
547 else
548 count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
549
550 if (count >= PAGE_SIZE - 1)
551 return count;
552 }
553 }
554
555 return count;
556}
557
558static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
559{
560 return -ENOSYS;
561}
562static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
563
442/* Read current range and display it in terminal */ 564/* Read current range and display it in terminal */
443static ssize_t range_show(struct device *dev, struct device_attribute *attr, 565static ssize_t range_show(struct device *dev, struct device_attribute *attr,
444 char *buf) 566 char *buf)
@@ -500,6 +622,41 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr,
500} 622}
501static DEVICE_ATTR_RW(range); 623static DEVICE_ATTR_RW(range);
502 624
625static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)
626{
627 struct hid_device *hid = to_hid_device(dev);
628 struct lg4ff_device_entry *entry;
629 struct lg_drv_data *drv_data;
630 size_t count;
631
632 drv_data = hid_get_drvdata(hid);
633 if (!drv_data) {
634 hid_err(hid, "Private driver data not found!\n");
635 return 0;
636 }
637
638 entry = drv_data->device_props;
639 if (!entry) {
640 hid_err(hid, "Device properties not found!\n");
641 return 0;
642 }
643
644 if (!entry->real_tag || !entry->real_name) {
645 hid_err(hid, "NULL pointer to string\n");
646 return 0;
647 }
648
649 count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->real_tag, entry->real_name);
650 return count;
651}
652
653static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
654{
655 /* Real ID is a read-only value */
656 return -EPERM;
657}
658static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
659
503#ifdef CONFIG_LEDS_CLASS 660#ifdef CONFIG_LEDS_CLASS
504static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) 661static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
505{ 662{
@@ -664,7 +821,7 @@ static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_produc
664 break; 821 break;
665 default: 822 default:
666 hid_err(hid, "Invalid product id %X\n", *real_product_id); 823 hid_err(hid, "Invalid product id %X\n", *real_product_id);
667 return LG4FF_MMODE_DONE; 824 return LG4FF_MMODE_NOT_MULTIMODE;
668 } 825 }
669 826
670 ret = lg4ff_switch_compatibility_mode(hid, s); 827 ret = lg4ff_switch_compatibility_mode(hid, s);
@@ -672,12 +829,12 @@ static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_produc
672 /* Wheel could not have been switched to native mode, 829 /* Wheel could not have been switched to native mode,
673 * leave it in "Driving Force" mode and continue */ 830 * leave it in "Driving Force" mode and continue */
674 hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret); 831 hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret);
675 return LG4FF_MMODE_DONE; 832 return LG4FF_MMODE_IS_MULTIMODE;
676 } 833 }
677 return LG4FF_MMODE_SWITCHED; 834 return LG4FF_MMODE_SWITCHED;
678 } 835 }
679 836
680 return LG4FF_MMODE_DONE; 837 return LG4FF_MMODE_IS_MULTIMODE;
681} 838}
682 839
683 840
@@ -689,7 +846,8 @@ int lg4ff_init(struct hid_device *hid)
689 const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice); 846 const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
690 struct lg4ff_device_entry *entry; 847 struct lg4ff_device_entry *entry;
691 struct lg_drv_data *drv_data; 848 struct lg_drv_data *drv_data;
692 int error, i, j, ret; 849 int error, i, j;
850 int mmode_ret, mmode_idx = -1;
693 u16 real_product_id; 851 u16 real_product_id;
694 852
695 /* Check that the report looks ok */ 853 /* Check that the report looks ok */
@@ -698,12 +856,12 @@ int lg4ff_init(struct hid_device *hid)
698 856
699 /* Check if a multimode wheel has been connected and 857 /* Check if a multimode wheel has been connected and
700 * handle it appropriately */ 858 * handle it appropriately */
701 ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice); 859 mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);
702 860
703 /* Wheel has been told to switch to native mode. There is no point in going on 861 /* Wheel has been told to switch to native mode. There is no point in going on
704 * with the initialization as the wheel will do a USB reset when it switches mode 862 * with the initialization as the wheel will do a USB reset when it switches mode
705 */ 863 */
706 if (ret == LG4FF_MMODE_SWITCHED) 864 if (mmode_ret == LG4FF_MMODE_SWITCHED)
707 return 0; 865 return 0;
708 866
709 /* Check what wheel has been connected */ 867 /* Check what wheel has been connected */
@@ -720,6 +878,18 @@ int lg4ff_init(struct hid_device *hid)
720 return -1; 878 return -1;
721 } 879 }
722 880
881 if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
882 for (mmode_idx = 0; mmode_idx < ARRAY_SIZE(lg4ff_multimode_wheels); mmode_idx++) {
883 if (real_product_id == lg4ff_multimode_wheels[mmode_idx].product_id)
884 break;
885 }
886
887 if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {
888 hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);
889 return -1;
890 }
891 }
892
723 /* Set supported force feedback capabilities */ 893 /* Set supported force feedback capabilities */
724 for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++) 894 for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
725 set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit); 895 set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
@@ -745,9 +915,16 @@ int lg4ff_init(struct hid_device *hid)
745 drv_data->device_props = entry; 915 drv_data->device_props = entry;
746 916
747 entry->product_id = lg4ff_devices[i].product_id; 917 entry->product_id = lg4ff_devices[i].product_id;
918 entry->real_product_id = real_product_id;
748 entry->min_range = lg4ff_devices[i].min_range; 919 entry->min_range = lg4ff_devices[i].min_range;
749 entry->max_range = lg4ff_devices[i].max_range; 920 entry->max_range = lg4ff_devices[i].max_range;
750 entry->set_range = lg4ff_devices[i].set_range; 921 entry->set_range = lg4ff_devices[i].set_range;
922 if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
923 BUG_ON(mmode_idx == -1);
924 entry->alternate_modes = lg4ff_multimode_wheels[mmode_idx].alternate_modes;
925 entry->real_tag = lg4ff_multimode_wheels[mmode_idx].real_tag;
926 entry->real_name = lg4ff_multimode_wheels[mmode_idx].real_name;
927 }
751 928
752 /* Check if autocentering is available and 929 /* Check if autocentering is available and
753 * set the centering force to zero by default */ 930 * set the centering force to zero by default */
@@ -766,6 +943,14 @@ int lg4ff_init(struct hid_device *hid)
766 error = device_create_file(&hid->dev, &dev_attr_range); 943 error = device_create_file(&hid->dev, &dev_attr_range);
767 if (error) 944 if (error)
768 return error; 945 return error;
946 if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
947 error = device_create_file(&hid->dev, &dev_attr_real_id);
948 if (error)
949 return error;
950 error = device_create_file(&hid->dev, &dev_attr_alternate_modes);
951 if (error)
952 return error;
953 }
769 dbg_hid("sysfs interface created\n"); 954 dbg_hid("sysfs interface created\n");
770 955
771 /* Set the maximum range to start with */ 956 /* Set the maximum range to start with */
@@ -844,6 +1029,12 @@ int lg4ff_deinit(struct hid_device *hid)
844 1029
845 device_remove_file(&hid->dev, &dev_attr_range); 1030 device_remove_file(&hid->dev, &dev_attr_range);
846 1031
1032 /* Multimode devices will have at least the "MODE_NATIVE" bit set */
1033 if (entry->alternate_modes) {
1034 device_remove_file(&hid->dev, &dev_attr_real_id);
1035 device_remove_file(&hid->dev, &dev_attr_alternate_modes);
1036 }
1037
847#ifdef CONFIG_LEDS_CLASS 1038#ifdef CONFIG_LEDS_CLASS
848 { 1039 {
849 int j; 1040 int j;