diff options
author | Simon Wood <simon@mungewell.org> | 2015-11-02 09:56:52 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2015-11-06 15:18:06 -0500 |
commit | 29fae1c85166ef525b8b6518e749295e0c9d1e20 (patch) | |
tree | 709315d3d46ca1c33de9e4a681e70d93e1e99e64 | |
parent | bbec1bd0faa211a0a0abaf947cd4a236d080ad28 (diff) |
HID: logitech: Add support for G29
At present the G29 is mis-identified as a DFGT, this patch ensures
that the wheel is correctly detected and allows setting the LEDs and
turning range via the '/sys' interface.
This wheel can also emulate other types of Logitech wheels.
Signed-off-by: Simon Wood <simon@mungewell.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/hid-core.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-lg.c | 9 | ||||
-rw-r--r-- | drivers/hid/hid-lg4ff.c | 57 |
3 files changed, 63 insertions, 4 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..1ec2bea39d9a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1896,6 +1896,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1896 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, | 1896 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, |
1897 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, | 1897 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, |
1898 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, | 1898 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, |
1899 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) }, | ||
1899 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, | 1900 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, |
1900 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, | 1901 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, |
1901 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, | 1902 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, |
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 5332fb7d072a..c20ac76c0a8c 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c | |||
@@ -620,6 +620,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
620 | usage->code == ABS_Y || usage->code == ABS_Z || | 620 | usage->code == ABS_Y || usage->code == ABS_Z || |
621 | usage->code == ABS_RZ)) { | 621 | usage->code == ABS_RZ)) { |
622 | switch (hdev->product) { | 622 | switch (hdev->product) { |
623 | case USB_DEVICE_ID_LOGITECH_G29_WHEEL: | ||
623 | case USB_DEVICE_ID_LOGITECH_WHEEL: | 624 | case USB_DEVICE_ID_LOGITECH_WHEEL: |
624 | case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: | 625 | case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: |
625 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | 626 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: |
@@ -658,10 +659,18 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, | |||
658 | 659 | ||
659 | static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | 660 | static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) |
660 | { | 661 | { |
662 | struct usb_interface *iface = to_usb_interface(hdev->dev.parent); | ||
663 | __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; | ||
661 | unsigned int connect_mask = HID_CONNECT_DEFAULT; | 664 | unsigned int connect_mask = HID_CONNECT_DEFAULT; |
662 | struct lg_drv_data *drv_data; | 665 | struct lg_drv_data *drv_data; |
663 | int ret; | 666 | int ret; |
664 | 667 | ||
668 | /* Only work with the 1st interface (G29 presents multiple) */ | ||
669 | if (iface_num != 0) { | ||
670 | dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num); | ||
671 | return -ENODEV; | ||
672 | } | ||
673 | |||
665 | drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL); | 674 | drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL); |
666 | if (!drv_data) { | 675 | if (!drv_data) { |
667 | hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); | 676 | hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); |
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index b363d88267c2..fbddcb37ae98 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c | |||
@@ -45,7 +45,8 @@ | |||
45 | #define LG4FF_MODE_G25_IDX 3 | 45 | #define LG4FF_MODE_G25_IDX 3 |
46 | #define LG4FF_MODE_DFGT_IDX 4 | 46 | #define LG4FF_MODE_DFGT_IDX 4 |
47 | #define LG4FF_MODE_G27_IDX 5 | 47 | #define LG4FF_MODE_G27_IDX 5 |
48 | #define LG4FF_MODE_MAX_IDX 6 | 48 | #define LG4FF_MODE_G29_IDX 6 |
49 | #define LG4FF_MODE_MAX_IDX 7 | ||
49 | 50 | ||
50 | #define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX) | 51 | #define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX) |
51 | #define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX) | 52 | #define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX) |
@@ -53,6 +54,7 @@ | |||
53 | #define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX) | 54 | #define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX) |
54 | #define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX) | 55 | #define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX) |
55 | #define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX) | 56 | #define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX) |
57 | #define LG4FF_MODE_G29 BIT(LG4FF_MODE_G29_IDX) | ||
56 | 58 | ||
57 | #define LG4FF_DFEX_TAG "DF-EX" | 59 | #define LG4FF_DFEX_TAG "DF-EX" |
58 | #define LG4FF_DFEX_NAME "Driving Force / Formula EX" | 60 | #define LG4FF_DFEX_NAME "Driving Force / Formula EX" |
@@ -62,6 +64,8 @@ | |||
62 | #define LG4FF_G25_NAME "G25 Racing Wheel" | 64 | #define LG4FF_G25_NAME "G25 Racing Wheel" |
63 | #define LG4FF_G27_TAG "G27" | 65 | #define LG4FF_G27_TAG "G27" |
64 | #define LG4FF_G27_NAME "G27 Racing Wheel" | 66 | #define LG4FF_G27_NAME "G27 Racing Wheel" |
67 | #define LG4FF_G29_TAG "G29" | ||
68 | #define LG4FF_G29_NAME "G29 Racing Wheel" | ||
65 | #define LG4FF_DFGT_TAG "DFGT" | 69 | #define LG4FF_DFGT_TAG "DFGT" |
66 | #define LG4FF_DFGT_NAME "Driving Force GT" | 70 | #define LG4FF_DFGT_NAME "Driving Force GT" |
67 | 71 | ||
@@ -140,6 +144,7 @@ static const struct lg4ff_wheel lg4ff_devices[] = { | |||
140 | {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, | 144 | {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, |
141 | {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, | 145 | {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, |
142 | {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, | 146 | {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, |
147 | {USB_DEVICE_ID_LOGITECH_G29_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, | ||
143 | {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL}, | 148 | {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL}, |
144 | {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} | 149 | {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} |
145 | }; | 150 | }; |
@@ -157,6 +162,9 @@ static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = { | |||
157 | {USB_DEVICE_ID_LOGITECH_G27_WHEEL, | 162 | {USB_DEVICE_ID_LOGITECH_G27_WHEEL, |
158 | LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, | 163 | LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, |
159 | LG4FF_G27_TAG, LG4FF_G27_NAME}, | 164 | LG4FF_G27_TAG, LG4FF_G27_NAME}, |
165 | {USB_DEVICE_ID_LOGITECH_G29_WHEEL, | ||
166 | LG4FF_MODE_NATIVE | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, | ||
167 | LG4FF_G29_TAG, LG4FF_G29_NAME}, | ||
160 | }; | 168 | }; |
161 | 169 | ||
162 | static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { | 170 | static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { |
@@ -165,7 +173,8 @@ static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { | |||
165 | [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME}, | 173 | [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME}, |
166 | [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME}, | 174 | [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME}, |
167 | [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME}, | 175 | [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME}, |
168 | [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME} | 176 | [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME}, |
177 | [LG4FF_MODE_G29_IDX] = {USB_DEVICE_ID_LOGITECH_G29_WHEEL, LG4FF_G29_TAG, LG4FF_G29_NAME}, | ||
169 | }; | 178 | }; |
170 | 179 | ||
171 | /* Multimode wheel identificators */ | 180 | /* Multimode wheel identificators */ |
@@ -197,8 +206,24 @@ static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = { | |||
197 | USB_DEVICE_ID_LOGITECH_DFGT_WHEEL | 206 | USB_DEVICE_ID_LOGITECH_DFGT_WHEEL |
198 | }; | 207 | }; |
199 | 208 | ||
209 | static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info = { | ||
210 | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, | ||
211 | 0xfff8, | ||
212 | 0x1350, | ||
213 | USB_DEVICE_ID_LOGITECH_G29_WHEEL | ||
214 | }; | ||
215 | |||
216 | static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info2 = { | ||
217 | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, | ||
218 | 0xff00, | ||
219 | 0x8900, | ||
220 | USB_DEVICE_ID_LOGITECH_G29_WHEEL | ||
221 | }; | ||
222 | |||
200 | /* Multimode wheel identification checklists */ | 223 | /* Multimode wheel identification checklists */ |
201 | static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = { | 224 | static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = { |
225 | &lg4ff_g29_ident_info, | ||
226 | &lg4ff_g29_ident_info2, | ||
202 | &lg4ff_dfgt_ident_info, | 227 | &lg4ff_dfgt_ident_info, |
203 | &lg4ff_g27_ident_info, | 228 | &lg4ff_g27_ident_info, |
204 | &lg4ff_g25_ident_info, | 229 | &lg4ff_g25_ident_info, |
@@ -237,6 +262,12 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = { | |||
237 | 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */ | 262 | 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */ |
238 | }; | 263 | }; |
239 | 264 | ||
265 | static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g29 = { | ||
266 | 2, | ||
267 | {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ | ||
268 | 0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00} /* Switch mode to G29 with detach */ | ||
269 | }; | ||
270 | |||
240 | /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */ | 271 | /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */ |
241 | static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = { | 272 | static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = { |
242 | 1, | 273 | 1, |
@@ -650,6 +681,23 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons | |||
650 | return NULL; | 681 | return NULL; |
651 | } | 682 | } |
652 | break; | 683 | break; |
684 | case USB_DEVICE_ID_LOGITECH_G29_WHEEL: | ||
685 | switch (target_product_id) { | ||
686 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | ||
687 | return &lg4ff_mode_switch_ext09_dfp; | ||
688 | case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: | ||
689 | return &lg4ff_mode_switch_ext09_dfgt; | ||
690 | case USB_DEVICE_ID_LOGITECH_G25_WHEEL: | ||
691 | return &lg4ff_mode_switch_ext09_g25; | ||
692 | case USB_DEVICE_ID_LOGITECH_G27_WHEEL: | ||
693 | return &lg4ff_mode_switch_ext09_g27; | ||
694 | case USB_DEVICE_ID_LOGITECH_G29_WHEEL: | ||
695 | return &lg4ff_mode_switch_ext09_g29; | ||
696 | /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */ | ||
697 | default: | ||
698 | return NULL; | ||
699 | } | ||
700 | break; | ||
653 | case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: | 701 | case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: |
654 | switch (target_product_id) { | 702 | switch (target_product_id) { |
655 | case USB_DEVICE_ID_LOGITECH_WHEEL: | 703 | case USB_DEVICE_ID_LOGITECH_WHEEL: |
@@ -1232,12 +1280,13 @@ int lg4ff_init(struct hid_device *hid) | |||
1232 | entry->wdata.set_range(hid, entry->wdata.range); | 1280 | entry->wdata.set_range(hid, entry->wdata.range); |
1233 | 1281 | ||
1234 | #ifdef CONFIG_LEDS_CLASS | 1282 | #ifdef CONFIG_LEDS_CLASS |
1235 | /* register led subsystem - G27 only */ | 1283 | /* register led subsystem - G27/G29 only */ |
1236 | entry->wdata.led_state = 0; | 1284 | entry->wdata.led_state = 0; |
1237 | for (j = 0; j < 5; j++) | 1285 | for (j = 0; j < 5; j++) |
1238 | entry->wdata.led[j] = NULL; | 1286 | entry->wdata.led[j] = NULL; |
1239 | 1287 | ||
1240 | if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) { | 1288 | if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL || |
1289 | lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G29_WHEEL) { | ||
1241 | struct led_classdev *led; | 1290 | struct led_classdev *led; |
1242 | size_t name_sz; | 1291 | size_t name_sz; |
1243 | char *name; | 1292 | char *name; |