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