diff options
| author | Simon Wood <simon@mungewell.org> | 2015-11-19 18:42:13 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2015-11-20 04:29:59 -0500 |
| commit | 7f4b49fef6ffb5021c01a915c21b3221fd521e81 (patch) | |
| tree | ed23590c09c25e70d8f251ed95df291d4d0fa1d8 | |
| parent | 7bfd2927adcacac2930a2709a9bcc1231e5bba1c (diff) | |
HID: hid-logitech-hidpp: Add range sysfs for Logitech G920
The G920 can adjust the amount of 'turn' it permits, this patch adds
a sysfs file 'range' to control this.
Signed-off-by: Simon Wood <simon@mungewell.org>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 140 |
1 files changed, 139 insertions, 1 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 98b8f096d7ee..fc553e3f948d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c | |||
| @@ -1295,6 +1295,133 @@ static int k400_connect(struct hid_device *hdev, bool connected) | |||
| 1295 | return k400_disable_tap_to_click(hidpp); | 1295 | return k400_disable_tap_to_click(hidpp); |
| 1296 | } | 1296 | } |
| 1297 | 1297 | ||
| 1298 | /* ------------------------------------------------------------------------- */ | ||
| 1299 | /* Logitech G920 Driving Force Racing Wheel for Xbox One */ | ||
| 1300 | /* ------------------------------------------------------------------------- */ | ||
| 1301 | |||
| 1302 | #define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123 | ||
| 1303 | |||
| 1304 | /* Using session ID = 1 */ | ||
| 1305 | #define CMD_G920_FORCE_GET_APERTURE 0x51 | ||
| 1306 | #define CMD_G920_FORCE_SET_APERTURE 0x61 | ||
| 1307 | |||
| 1308 | struct g920_private_data { | ||
| 1309 | u8 force_feature; | ||
| 1310 | u16 range; | ||
| 1311 | }; | ||
| 1312 | |||
| 1313 | #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) | ||
| 1314 | |||
| 1315 | static ssize_t g920_range_show(struct device *dev, struct device_attribute *attr, | ||
| 1316 | char *buf) | ||
| 1317 | { | ||
| 1318 | struct hid_device *hid = to_hid_device(dev); | ||
| 1319 | struct hidpp_device *hidpp = hid_get_drvdata(hid); | ||
| 1320 | struct g920_private_data *pdata; | ||
| 1321 | |||
| 1322 | pdata = hidpp->private_data; | ||
| 1323 | if (!pdata) { | ||
| 1324 | hid_err(hid, "Private driver data not found!\n"); | ||
| 1325 | return -EINVAL; | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->range); | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | static ssize_t g920_range_store(struct device *dev, struct device_attribute *attr, | ||
| 1332 | const char *buf, size_t count) | ||
| 1333 | { | ||
| 1334 | struct hid_device *hid = to_hid_device(dev); | ||
| 1335 | struct hidpp_device *hidpp = hid_get_drvdata(hid); | ||
| 1336 | struct g920_private_data *pdata; | ||
| 1337 | struct hidpp_report response; | ||
| 1338 | u8 params[2]; | ||
| 1339 | int ret; | ||
| 1340 | u16 range = simple_strtoul(buf, NULL, 10); | ||
| 1341 | |||
| 1342 | pdata = hidpp->private_data; | ||
| 1343 | if (!pdata) { | ||
| 1344 | hid_err(hid, "Private driver data not found!\n"); | ||
| 1345 | return -EINVAL; | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | if (range < 180) | ||
| 1349 | range = 180; | ||
| 1350 | else if (range > 900) | ||
| 1351 | range = 900; | ||
| 1352 | |||
| 1353 | params[0] = range >> 8; | ||
| 1354 | params[1] = range & 0x00FF; | ||
| 1355 | |||
| 1356 | ret = hidpp_send_fap_command_sync(hidpp, pdata->force_feature, | ||
| 1357 | CMD_G920_FORCE_SET_APERTURE, params, 2, &response); | ||
| 1358 | if (ret) | ||
| 1359 | return ret; | ||
| 1360 | |||
| 1361 | pdata->range = range; | ||
| 1362 | return count; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, g920_range_show, g920_range_store); | ||
| 1366 | |||
| 1367 | static int g920_allocate(struct hid_device *hdev) | ||
| 1368 | { | ||
| 1369 | struct hidpp_device *hidpp = hid_get_drvdata(hdev); | ||
| 1370 | struct g920_private_data *pdata; | ||
| 1371 | |||
| 1372 | pdata = devm_kzalloc(&hdev->dev, sizeof(struct g920_private_data), | ||
| 1373 | GFP_KERNEL); | ||
| 1374 | if (!pdata) | ||
| 1375 | return -ENOMEM; | ||
| 1376 | |||
| 1377 | hidpp->private_data = pdata; | ||
| 1378 | |||
| 1379 | return 0; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | static int g920_get_config(struct hidpp_device *hidpp) | ||
| 1383 | { | ||
| 1384 | struct g920_private_data *pdata = hidpp->private_data; | ||
| 1385 | struct hidpp_report response; | ||
| 1386 | u8 feature_type; | ||
| 1387 | u8 feature_index; | ||
| 1388 | int ret; | ||
| 1389 | |||
| 1390 | pdata = hidpp->private_data; | ||
| 1391 | if (!pdata) { | ||
| 1392 | hid_err(hidpp->hid_dev, "Private driver data not found!\n"); | ||
| 1393 | return -EINVAL; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | /* Find feature and store for later use */ | ||
| 1397 | ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK, | ||
| 1398 | &feature_index, &feature_type); | ||
| 1399 | if (ret) | ||
| 1400 | return ret; | ||
| 1401 | |||
| 1402 | pdata->force_feature = feature_index; | ||
| 1403 | |||
| 1404 | /* Read current Range */ | ||
| 1405 | ret = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
| 1406 | CMD_G920_FORCE_GET_APERTURE, NULL, 0, &response); | ||
| 1407 | if (ret > 0) { | ||
| 1408 | hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", | ||
| 1409 | __func__, ret); | ||
| 1410 | return -EPROTO; | ||
| 1411 | } | ||
| 1412 | if (ret) | ||
| 1413 | return ret; | ||
| 1414 | |||
| 1415 | pdata->range = get_unaligned_be16(&response.fap.params[0]); | ||
| 1416 | |||
| 1417 | /* Create sysfs interface */ | ||
| 1418 | ret = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range); | ||
| 1419 | if (ret) | ||
| 1420 | hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d\n", ret); | ||
| 1421 | |||
| 1422 | return 0; | ||
| 1423 | } | ||
| 1424 | |||
| 1298 | /* -------------------------------------------------------------------------- */ | 1425 | /* -------------------------------------------------------------------------- */ |
| 1299 | /* Generic HID++ devices */ | 1426 | /* Generic HID++ devices */ |
| 1300 | /* -------------------------------------------------------------------------- */ | 1427 | /* -------------------------------------------------------------------------- */ |
| @@ -1595,6 +1722,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 1595 | ret = k400_allocate(hdev); | 1722 | ret = k400_allocate(hdev); |
| 1596 | if (ret) | 1723 | if (ret) |
| 1597 | goto allocate_fail; | 1724 | goto allocate_fail; |
| 1725 | } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { | ||
| 1726 | ret = g920_allocate(hdev); | ||
| 1727 | if (ret) | ||
| 1728 | goto allocate_fail; | ||
| 1598 | } | 1729 | } |
| 1599 | 1730 | ||
| 1600 | INIT_WORK(&hidpp->work, delayed_work_cb); | 1731 | INIT_WORK(&hidpp->work, delayed_work_cb); |
| @@ -1648,6 +1779,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 1648 | ret = wtp_get_config(hidpp); | 1779 | ret = wtp_get_config(hidpp); |
| 1649 | if (ret) | 1780 | if (ret) |
| 1650 | goto hid_hw_open_failed; | 1781 | goto hid_hw_open_failed; |
| 1782 | } else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) { | ||
| 1783 | ret = g920_get_config(hidpp); | ||
| 1784 | if (ret) | ||
| 1785 | goto hid_hw_open_failed; | ||
| 1651 | } | 1786 | } |
| 1652 | 1787 | ||
| 1653 | /* Block incoming packets */ | 1788 | /* Block incoming packets */ |
| @@ -1673,6 +1808,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 1673 | hid_hw_open_failed: | 1808 | hid_hw_open_failed: |
| 1674 | hid_device_io_stop(hdev); | 1809 | hid_device_io_stop(hdev); |
| 1675 | if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { | 1810 | if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { |
| 1811 | device_remove_file(&hdev->dev, &dev_attr_range); | ||
| 1676 | hid_hw_close(hdev); | 1812 | hid_hw_close(hdev); |
| 1677 | hid_hw_stop(hdev); | 1813 | hid_hw_stop(hdev); |
| 1678 | } | 1814 | } |
| @@ -1689,8 +1825,10 @@ static void hidpp_remove(struct hid_device *hdev) | |||
| 1689 | { | 1825 | { |
| 1690 | struct hidpp_device *hidpp = hid_get_drvdata(hdev); | 1826 | struct hidpp_device *hidpp = hid_get_drvdata(hdev); |
| 1691 | 1827 | ||
| 1692 | if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) | 1828 | if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { |
| 1829 | device_remove_file(&hdev->dev, &dev_attr_range); | ||
| 1693 | hid_hw_close(hdev); | 1830 | hid_hw_close(hdev); |
| 1831 | } | ||
| 1694 | hid_hw_stop(hdev); | 1832 | hid_hw_stop(hdev); |
| 1695 | cancel_work_sync(&hidpp->work); | 1833 | cancel_work_sync(&hidpp->work); |
| 1696 | mutex_destroy(&hidpp->send_mutex); | 1834 | mutex_destroy(&hidpp->send_mutex); |
