aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
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 /drivers/hid
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>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-lg4ff.c205
1 files changed, 198 insertions, 7 deletions
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;