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; |
