Lines Matching +full:force +full:- +full:mode
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Force feedback support for Logitech Gaming Wheels
6 * Speed Force Wireless (WiiWheel)
20 #include "hid-lg.h"
21 #include "hid-lg4ff.h"
22 #include "hid-ids.h"
45 #define LG4FF_DFEX_TAG "DF-EX"
46 #define LG4FF_DFEX_NAME "Driving Force / Formula EX"
48 #define LG4FF_DFP_NAME "Driving Force Pro"
56 #define LG4FF_DFGT_NAME "Driving Force GT"
91 -1
95 -1
226 /* Compatibility mode switching commands */
227 /* EXT_CMD9 - Understood by G27 and DFGT */
230 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
231 0xf8, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DF-EX with detach */
236 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
237 0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFP with detach */
242 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
243 0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G25 with detach */
248 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
249 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFGT with detach */
254 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
255 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */
260 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */
261 0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00} /* Switch mode to G29 with detach */
264 /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */
270 /* EXT_CMD16 - Understood by G25 and G27 */
291 new_value = 8192 + mult_frac(value - 8192, max_range, range); in lg4ff_adjust_dfp_x_axis()
303 struct lg4ff_device_entry *entry = drv_data->device_props; in lg4ff_adjust_input_event()
311 switch (entry->wdata.product_id) { in lg4ff_adjust_input_event()
313 switch (usage->code) { in lg4ff_adjust_input_event()
315 new_value = lg4ff_adjust_dfp_x_axis(value, entry->wdata.range); in lg4ff_adjust_input_event()
316 input_event(field->hidinput->input, usage->type, usage->code, new_value); in lg4ff_adjust_input_event()
330 struct lg4ff_device_entry *entry = drv_data->device_props; in lg4ff_raw_event()
336 if (entry->wdata.combine) { in lg4ff_raw_event()
337 switch (entry->wdata.product_id) { in lg4ff_raw_event()
369 rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1; in lg4ff_raw_event()
386 alternate_modes = mmode_wheel->alternate_modes; in lg4ff_init_wheel_data()
387 real_tag = mmode_wheel->real_tag; in lg4ff_init_wheel_data()
388 real_name = mmode_wheel->real_name; in lg4ff_init_wheel_data()
392 struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id, in lg4ff_init_wheel_data()
395 .min_range = wheel->min_range, in lg4ff_init_wheel_data()
396 .max_range = wheel->max_range, in lg4ff_init_wheel_data()
397 .set_range = wheel->set_range, in lg4ff_init_wheel_data()
418 return -EINVAL; in lg4ff_play()
421 entry = drv_data->device_props; in lg4ff_play()
424 return -EINVAL; in lg4ff_play()
426 value = entry->report->field[0]->value; in lg4ff_play()
430 switch (effect->type) { in lg4ff_play()
432 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ in lg4ff_play()
435 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_play()
437 /* De-activate force in slot-1*/ in lg4ff_play()
446 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_play()
447 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_play()
459 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_play()
460 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_play()
467 * all wheels except Formula Force EX */
483 entry = drv_data->device_props; in lg4ff_set_autocenter_default()
488 value = entry->report->field[0]->value; in lg4ff_set_autocenter_default()
490 /* De-activate Auto-Center */ in lg4ff_set_autocenter_default()
491 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_set_autocenter_default()
501 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_autocenter_default()
502 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_autocenter_default()
510 expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa); in lg4ff_set_autocenter_default()
511 expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa); in lg4ff_set_autocenter_default()
514 /* Adjust for non-MOMO wheels */ in lg4ff_set_autocenter_default()
515 switch (entry->wdata.product_id) { in lg4ff_set_autocenter_default()
532 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_autocenter_default()
534 /* Activate Auto-Center */ in lg4ff_set_autocenter_default()
543 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_autocenter_default()
544 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_autocenter_default()
547 /* Sends autocentering command compatible with Formula Force EX */
563 entry = drv_data->device_props; in lg4ff_set_autocenter_ffex()
568 value = entry->report->field[0]->value; in lg4ff_set_autocenter_ffex()
570 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_set_autocenter_ffex()
579 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_autocenter_ffex()
580 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_autocenter_ffex()
583 /* Sends command to set range compatible with G25/G27/Driving Force GT */
597 entry = drv_data->device_props; in lg4ff_set_range_g25()
602 value = entry->report->field[0]->value; in lg4ff_set_range_g25()
605 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_set_range_g25()
614 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_range_g25()
615 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_range_g25()
618 /* Sends commands to set range compatible with Driving Force Pro wheel */
633 entry = drv_data->device_props; in lg4ff_set_range_dfp()
638 value = entry->report->field[0]->value; in lg4ff_set_range_dfp()
639 dbg_hid("Driving Force Pro: setting range to %u\n", range); in lg4ff_set_range_dfp()
642 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_set_range_dfp()
658 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_range_dfp()
670 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_range_dfp()
671 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_range_dfp()
676 start_left = (((full_range - range + 1) * 2047) / full_range); in lg4ff_set_range_dfp()
677 start_right = 0xfff - start_left; in lg4ff_set_range_dfp()
685 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_range_dfp()
686 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_range_dfp()
696 /* DFP can only be switched to its native mode */ in lg4ff_get_mode_switch_command()
707 /* G25 can only be switched to DFP mode or its native mode */ in lg4ff_get_mode_switch_command()
722 /* G27 can only be switched to DF-EX, DFP, G25 or its native mode */ in lg4ff_get_mode_switch_command()
739 /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */ in lg4ff_get_mode_switch_command()
752 /* DFGT can only be switched to DF-EX, DFP or its native mode */ in lg4ff_get_mode_switch_command()
774 return -EINVAL; in lg4ff_switch_compatibility_mode()
777 entry = drv_data->device_props; in lg4ff_switch_compatibility_mode()
780 return -EINVAL; in lg4ff_switch_compatibility_mode()
782 value = entry->report->field[0]->value; in lg4ff_switch_compatibility_mode()
784 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_switch_compatibility_mode()
785 for (i = 0; i < s->cmd_count; i++) { in lg4ff_switch_compatibility_mode()
789 value[j] = s->cmd[j + (7*i)]; in lg4ff_switch_compatibility_mode()
791 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_switch_compatibility_mode()
793 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_switch_compatibility_mode()
812 entry = drv_data->device_props; in lg4ff_alternate_modes_show()
818 if (!entry->wdata.real_name) { in lg4ff_alternate_modes_show()
824 if (entry->wdata.alternate_modes & BIT(i)) { in lg4ff_alternate_modes_show()
826 count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s", in lg4ff_alternate_modes_show()
828 … !lg4ff_alternate_modes[i].product_id ? entry->wdata.real_name : lg4ff_alternate_modes[i].name); in lg4ff_alternate_modes_show()
829 if (count >= PAGE_SIZE - 1) in lg4ff_alternate_modes_show()
832 /* Mark the currently active mode with an asterisk */ in lg4ff_alternate_modes_show()
833 if (lg4ff_alternate_modes[i].product_id == entry->wdata.product_id || in lg4ff_alternate_modes_show()
834 …(lg4ff_alternate_modes[i].product_id == 0 && entry->wdata.product_id == entry->wdata.real_product_… in lg4ff_alternate_modes_show()
835 count += scnprintf(buf + count, PAGE_SIZE - count, " *\n"); in lg4ff_alternate_modes_show()
837 count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); in lg4ff_alternate_modes_show()
839 if (count >= PAGE_SIZE - 1) in lg4ff_alternate_modes_show()
860 return -EINVAL; in lg4ff_alternate_modes_store()
863 entry = drv_data->device_props; in lg4ff_alternate_modes_store()
866 return -EINVAL; in lg4ff_alternate_modes_store()
872 return -ENOMEM; in lg4ff_alternate_modes_store()
878 return -EINVAL; in lg4ff_alternate_modes_store()
881 if (lbuf[i-1] == '\n') { in lg4ff_alternate_modes_store()
884 return -EINVAL; in lg4ff_alternate_modes_store()
886 lbuf[i-1] = '\0'; in lg4ff_alternate_modes_store()
893 if (entry->wdata.alternate_modes & BIT(i)) { in lg4ff_alternate_modes_store()
896 target_product_id = entry->wdata.real_product_id; in lg4ff_alternate_modes_store()
905 hid_info(hid, "Requested mode \"%s\" is not supported by the device\n", lbuf); in lg4ff_alternate_modes_store()
907 return -EINVAL; in lg4ff_alternate_modes_store()
911 if (target_product_id == entry->wdata.product_id) /* Nothing to do */ in lg4ff_alternate_modes_store()
914 /* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */ in lg4ff_alternate_modes_store()
916 …hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with … in lg4ff_alternate_modes_store()
917 entry->wdata.real_name); in lg4ff_alternate_modes_store()
918 return -EINVAL; in lg4ff_alternate_modes_store()
922 …if ((entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->wdata.real_product… in lg4ff_alternate_modes_store()
923 entry->wdata.product_id > target_product_id) { in lg4ff_alternate_modes_store()
924 …hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->wdata.real_name, lg4ff_a… in lg4ff_alternate_modes_store()
925 return -EINVAL; in lg4ff_alternate_modes_store()
928 s = lg4ff_get_mode_switch_command(entry->wdata.real_product_id, target_product_id); in lg4ff_alternate_modes_store()
931 return -EINVAL; in lg4ff_alternate_modes_store()
953 entry = drv_data->device_props; in lg4ff_combine_show()
959 count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine); in lg4ff_combine_show()
974 return -EINVAL; in lg4ff_combine_store()
977 entry = drv_data->device_props; in lg4ff_combine_store()
980 return -EINVAL; in lg4ff_combine_store()
986 entry->wdata.combine = combine; in lg4ff_combine_store()
1006 entry = drv_data->device_props; in lg4ff_range_show()
1012 count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range); in lg4ff_range_show()
1029 return -EINVAL; in lg4ff_range_store()
1032 entry = drv_data->device_props; in lg4ff_range_store()
1035 return -EINVAL; in lg4ff_range_store()
1039 range = entry->wdata.max_range; in lg4ff_range_store()
1043 if (entry->wdata.set_range && range >= entry->wdata.min_range && range <= entry->wdata.max_range) { in lg4ff_range_store()
1044 entry->wdata.set_range(hid, range); in lg4ff_range_store()
1045 entry->wdata.range = range; in lg4ff_range_store()
1065 entry = drv_data->device_props; in lg4ff_real_id_show()
1071 if (!entry->wdata.real_tag || !entry->wdata.real_name) { in lg4ff_real_id_show()
1076 count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name); in lg4ff_real_id_show()
1082 /* Real ID is a read-only value */ in lg4ff_real_id_store()
1083 return -EPERM; in lg4ff_real_id_store()
1101 entry = drv_data->device_props; in lg4ff_set_leds()
1106 value = entry->report->field[0]->value; in lg4ff_set_leds()
1108 spin_lock_irqsave(&entry->report_lock, flags); in lg4ff_set_leds()
1116 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT); in lg4ff_set_leds()
1117 spin_unlock_irqrestore(&entry->report_lock, flags); in lg4ff_set_leds()
1123 struct device *dev = led_cdev->dev->parent; in lg4ff_led_set_brightness()
1134 entry = drv_data->device_props; in lg4ff_led_set_brightness()
1142 if (led_cdev != entry->wdata.led[i]) in lg4ff_led_set_brightness()
1144 state = (entry->wdata.led_state >> i) & 1; in lg4ff_led_set_brightness()
1146 entry->wdata.led_state &= ~(1 << i); in lg4ff_led_set_brightness()
1147 lg4ff_set_leds(hid, entry->wdata.led_state); in lg4ff_led_set_brightness()
1149 entry->wdata.led_state |= 1 << i; in lg4ff_led_set_brightness()
1150 lg4ff_set_leds(hid, entry->wdata.led_state); in lg4ff_led_set_brightness()
1158 struct device *dev = led_cdev->dev->parent; in lg4ff_led_get_brightness()
1169 entry = drv_data->device_props; in lg4ff_led_get_brightness()
1177 if (led_cdev == entry->wdata.led[i]) { in lg4ff_led_get_brightness()
1178 value = (entry->wdata.led_state >> i) & 1; in lg4ff_led_get_brightness()
1191 /* identify current mode from USB PID */ in lg4ff_identify_multimode_wheel()
1204 const u16 mask = lg4ff_main_checklist[i]->mask; in lg4ff_identify_multimode_wheel()
1205 const u16 result = lg4ff_main_checklist[i]->result; in lg4ff_identify_multimode_wheel()
1206 const u16 real_product_id = lg4ff_main_checklist[i]->real_product_id; in lg4ff_identify_multimode_wheel()
1208 if ((current_mode & lg4ff_main_checklist[i]->modes) && \ in lg4ff_identify_multimode_wheel()
1215 /* No match found. This is either Driving Force or an unknown in lg4ff_identify_multimode_wheel()
1217 …ith bcdDevice %X was not recognized as multimode wheel, leaving in its current mode\n", bcdDevice); in lg4ff_identify_multimode_wheel()
1223 const u16 reported_product_id = hid->product; in lg4ff_handle_multimode_wheel()
1234 /* Switch from "Driving Force" mode to native mode automatically. in lg4ff_handle_multimode_wheel()
1235 * Otherwise keep the wheel in its current mode */ in lg4ff_handle_multimode_wheel()
1248 /* Wheel could not have been switched to native mode, in lg4ff_handle_multimode_wheel()
1249 * leave it in "Driving Force" mode and continue */ in lg4ff_handle_multimode_wheel()
1250 hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret); in lg4ff_handle_multimode_wheel()
1264 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; in lg4ff_init()
1265 struct hid_report *report = list_entry(report_list->next, struct hid_report, list); in lg4ff_init()
1266 const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor); in lg4ff_init()
1267 const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice); in lg4ff_init()
1272 int mmode_ret, mmode_idx = -1; in lg4ff_init()
1275 if (list_empty(&hid->inputs)) { in lg4ff_init()
1277 return -ENODEV; in lg4ff_init()
1279 hidinput = list_entry(hid->inputs.next, struct hid_input, list); in lg4ff_init()
1280 dev = hidinput->input; in lg4ff_init()
1284 return -1; in lg4ff_init()
1289 return -1; in lg4ff_init()
1293 return -ENOMEM; in lg4ff_init()
1294 spin_lock_init(&entry->report_lock); in lg4ff_init()
1295 entry->report = report; in lg4ff_init()
1296 drv_data->device_props = entry; in lg4ff_init()
1302 /* Wheel has been told to switch to native mode. There is no point in going on in lg4ff_init()
1303 * with the initialization as the wheel will do a USB reset when it switches mode in lg4ff_init()
1308 hid_err(hid, "Unable to switch device mode during initialization, errno %d\n", mmode_ret); in lg4ff_init()
1315 if (hid->product == lg4ff_devices[i].product_id) { in lg4ff_init()
1324 "Michal Maly <madcatxster@devoid-pointer.net>\n"); in lg4ff_init()
1325 error = -1; in lg4ff_init()
1337 error = -1; in lg4ff_init()
1342 /* Set supported force feedback capabilities */ in lg4ff_init()
1344 set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit); in lg4ff_init()
1353 BUG_ON(mmode_idx == -1); in lg4ff_init()
1356 lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id); in lg4ff_init()
1359 * set the centering force to zero by default */ in lg4ff_init()
1360 if (test_bit(FF_AUTOCENTER, dev->ffbit)) { in lg4ff_init()
1361 /* Formula Force EX expects different autocentering command */ in lg4ff_init()
1364 dev->ff->set_autocenter = lg4ff_set_autocenter_ffex; in lg4ff_init()
1366 dev->ff->set_autocenter = lg4ff_set_autocenter_default; in lg4ff_init()
1368 dev->ff->set_autocenter(dev, 0); in lg4ff_init()
1372 error = device_create_file(&hid->dev, &dev_attr_combine_pedals); in lg4ff_init()
1375 error = device_create_file(&hid->dev, &dev_attr_range); in lg4ff_init()
1379 error = device_create_file(&hid->dev, &dev_attr_real_id); in lg4ff_init()
1382 error = device_create_file(&hid->dev, &dev_attr_alternate_modes); in lg4ff_init()
1389 entry->wdata.range = entry->wdata.max_range; in lg4ff_init()
1390 if (entry->wdata.set_range) in lg4ff_init()
1391 entry->wdata.set_range(hid, entry->wdata.range); in lg4ff_init()
1394 /* register led subsystem - G27/G29 only */ in lg4ff_init()
1395 entry->wdata.led_state = 0; in lg4ff_init()
1397 entry->wdata.led[j] = NULL; in lg4ff_init()
1407 name_sz = strlen(dev_name(&hid->dev)) + 8; in lg4ff_init()
1417 snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1); in lg4ff_init()
1418 led->name = name; in lg4ff_init()
1419 led->brightness = 0; in lg4ff_init()
1420 led->max_brightness = 1; in lg4ff_init()
1421 led->brightness_get = lg4ff_led_get_brightness; in lg4ff_init()
1422 led->brightness_set = lg4ff_led_set_brightness; in lg4ff_init()
1424 entry->wdata.led[j] = led; in lg4ff_init()
1425 error = led_classdev_register(&hid->dev, led); in lg4ff_init()
1432 led = entry->wdata.led[j]; in lg4ff_init()
1433 entry->wdata.led[j] = NULL; in lg4ff_init()
1445 hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n"); in lg4ff_init()
1449 drv_data->device_props = NULL; in lg4ff_init()
1462 return -1; in lg4ff_deinit()
1464 entry = drv_data->device_props; in lg4ff_deinit()
1469 if (entry->wdata.alternate_modes) { in lg4ff_deinit()
1470 device_remove_file(&hid->dev, &dev_attr_real_id); in lg4ff_deinit()
1471 device_remove_file(&hid->dev, &dev_attr_alternate_modes); in lg4ff_deinit()
1474 device_remove_file(&hid->dev, &dev_attr_combine_pedals); in lg4ff_deinit()
1475 device_remove_file(&hid->dev, &dev_attr_range); in lg4ff_deinit()
1484 led = entry->wdata.led[j]; in lg4ff_deinit()
1485 entry->wdata.led[j] = NULL; in lg4ff_deinit()
1493 drv_data->device_props = NULL; in lg4ff_deinit()