162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Force feedback support for Logitech RumblePad and Rumblepad 2 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/input.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/hid.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "hid-lg.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct lg2ff_device { 1962306a36Sopenharmony_ci struct hid_report *report; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int play_effect(struct input_dev *dev, void *data, 2362306a36Sopenharmony_ci struct ff_effect *effect) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct hid_device *hid = input_get_drvdata(dev); 2662306a36Sopenharmony_ci struct lg2ff_device *lg2ff = data; 2762306a36Sopenharmony_ci int weak, strong; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci strong = effect->u.rumble.strong_magnitude; 3062306a36Sopenharmony_ci weak = effect->u.rumble.weak_magnitude; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (weak || strong) { 3362306a36Sopenharmony_ci weak = weak * 0xff / 0xffff; 3462306a36Sopenharmony_ci strong = strong * 0xff / 0xffff; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci lg2ff->report->field[0]->value[0] = 0x51; 3762306a36Sopenharmony_ci lg2ff->report->field[0]->value[2] = weak; 3862306a36Sopenharmony_ci lg2ff->report->field[0]->value[4] = strong; 3962306a36Sopenharmony_ci } else { 4062306a36Sopenharmony_ci lg2ff->report->field[0]->value[0] = 0xf3; 4162306a36Sopenharmony_ci lg2ff->report->field[0]->value[2] = 0x00; 4262306a36Sopenharmony_ci lg2ff->report->field[0]->value[4] = 0x00; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci hid_hw_request(hid, lg2ff->report, HID_REQ_SET_REPORT); 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciint lg2ff_init(struct hid_device *hid) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct lg2ff_device *lg2ff; 5262306a36Sopenharmony_ci struct hid_report *report; 5362306a36Sopenharmony_ci struct hid_input *hidinput; 5462306a36Sopenharmony_ci struct input_dev *dev; 5562306a36Sopenharmony_ci int error; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (list_empty(&hid->inputs)) { 5862306a36Sopenharmony_ci hid_err(hid, "no inputs found\n"); 5962306a36Sopenharmony_ci return -ENODEV; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci hidinput = list_entry(hid->inputs.next, struct hid_input, list); 6262306a36Sopenharmony_ci dev = hidinput->input; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Check that the report looks ok */ 6562306a36Sopenharmony_ci report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7); 6662306a36Sopenharmony_ci if (!report) 6762306a36Sopenharmony_ci return -ENODEV; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL); 7062306a36Sopenharmony_ci if (!lg2ff) 7162306a36Sopenharmony_ci return -ENOMEM; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci set_bit(FF_RUMBLE, dev->ffbit); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci error = input_ff_create_memless(dev, lg2ff, play_effect); 7662306a36Sopenharmony_ci if (error) { 7762306a36Sopenharmony_ci kfree(lg2ff); 7862306a36Sopenharmony_ci return error; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci lg2ff->report = report; 8262306a36Sopenharmony_ci report->field[0]->value[0] = 0xf3; 8362306a36Sopenharmony_ci report->field[0]->value[1] = 0x00; 8462306a36Sopenharmony_ci report->field[0]->value[2] = 0x00; 8562306a36Sopenharmony_ci report->field[0]->value[3] = 0x00; 8662306a36Sopenharmony_ci report->field[0]->value[4] = 0x00; 8762306a36Sopenharmony_ci report->field[0]->value[5] = 0x00; 8862306a36Sopenharmony_ci report->field[0]->value[6] = 0x00; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci hid_hw_request(hid, report, HID_REQ_SET_REPORT); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci hid_info(hid, "Force feedback for Logitech variant 2 rumble devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 96