diff options
Diffstat (limited to 'drivers/hid/hid-lg4ff.c')
| -rw-r--r-- | drivers/hid/hid-lg4ff.c | 198 |
1 files changed, 138 insertions, 60 deletions
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index f3390ee6105c..d7947c701f30 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c | |||
| @@ -43,6 +43,11 @@ | |||
| 43 | #define G27_REV_MAJ 0x12 | 43 | #define G27_REV_MAJ 0x12 |
| 44 | #define G27_REV_MIN 0x38 | 44 | #define G27_REV_MIN 0x38 |
| 45 | 45 | ||
| 46 | #define DFP_X_MIN 0 | ||
| 47 | #define DFP_X_MAX 16383 | ||
| 48 | #define DFP_PEDAL_MIN 0 | ||
| 49 | #define DFP_PEDAL_MAX 255 | ||
| 50 | |||
| 46 | #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) | 51 | #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) |
| 47 | 52 | ||
| 48 | static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); | 53 | static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); |
| @@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at | |||
| 53 | static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); | 58 | static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); |
| 54 | 59 | ||
| 55 | struct lg4ff_device_entry { | 60 | struct lg4ff_device_entry { |
| 61 | __u32 product_id; | ||
| 56 | __u16 range; | 62 | __u16 range; |
| 57 | __u16 min_range; | 63 | __u16 min_range; |
| 58 | __u16 max_range; | 64 | __u16 max_range; |
| @@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = { | |||
| 129 | {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ | 135 | {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ |
| 130 | }; | 136 | }; |
| 131 | 137 | ||
| 138 | /* Recalculates X axis value accordingly to currently selected range */ | ||
| 139 | static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range) | ||
| 140 | { | ||
| 141 | __u16 max_range; | ||
| 142 | __s32 new_value; | ||
| 143 | |||
| 144 | if (range == 900) | ||
| 145 | return value; | ||
| 146 | else if (range == 200) | ||
| 147 | return value; | ||
| 148 | else if (range < 200) | ||
| 149 | max_range = 200; | ||
| 150 | else | ||
| 151 | max_range = 900; | ||
| 152 | |||
| 153 | new_value = 8192 + mult_frac(value - 8192, max_range, range); | ||
| 154 | if (new_value < 0) | ||
| 155 | return 0; | ||
| 156 | else if (new_value > 16383) | ||
| 157 | return 16383; | ||
| 158 | else | ||
| 159 | return new_value; | ||
| 160 | } | ||
| 161 | |||
| 162 | int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | ||
| 163 | struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) | ||
| 164 | { | ||
| 165 | struct lg4ff_device_entry *entry = drv_data->device_props; | ||
| 166 | __s32 new_value = 0; | ||
| 167 | |||
| 168 | if (!entry) { | ||
| 169 | hid_err(hid, "Device properties not found"); | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | switch (entry->product_id) { | ||
| 174 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | ||
| 175 | switch (usage->code) { | ||
| 176 | case ABS_X: | ||
| 177 | new_value = lg4ff_adjust_dfp_x_axis(value, entry->range); | ||
| 178 | input_event(field->hidinput->input, usage->type, usage->code, new_value); | ||
| 179 | return 1; | ||
| 180 | default: | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | default: | ||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 132 | static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) | 188 | static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) |
| 133 | { | 189 | { |
| 134 | struct hid_device *hid = input_get_drvdata(dev); | 190 | struct hid_device *hid = input_get_drvdata(dev); |
| 135 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 191 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 136 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 192 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 193 | __s32 *value = report->field[0]->value; | ||
| 137 | int x; | 194 | int x; |
| 138 | 195 | ||
| 139 | #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff | 196 | #define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0) |
| 140 | 197 | ||
| 141 | switch (effect->type) { | 198 | switch (effect->type) { |
| 142 | case FF_CONSTANT: | 199 | case FF_CONSTANT: |
| 143 | x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ | 200 | x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ |
| 144 | CLAMP(x); | 201 | CLAMP(x); |
| 145 | report->field[0]->value[0] = 0x11; /* Slot 1 */ | 202 | value[0] = 0x11; /* Slot 1 */ |
| 146 | report->field[0]->value[1] = 0x08; | 203 | value[1] = 0x08; |
| 147 | report->field[0]->value[2] = x; | 204 | value[2] = x; |
| 148 | report->field[0]->value[3] = 0x80; | 205 | value[3] = 0x80; |
| 149 | report->field[0]->value[4] = 0x00; | 206 | value[4] = 0x00; |
| 150 | report->field[0]->value[5] = 0x00; | 207 | value[5] = 0x00; |
| 151 | report->field[0]->value[6] = 0x00; | 208 | value[6] = 0x00; |
| 152 | 209 | ||
| 153 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 210 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 154 | break; | 211 | break; |
| @@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud | |||
| 163 | struct hid_device *hid = input_get_drvdata(dev); | 220 | struct hid_device *hid = input_get_drvdata(dev); |
| 164 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 221 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 165 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 222 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 223 | __s32 *value = report->field[0]->value; | ||
| 166 | 224 | ||
| 167 | report->field[0]->value[0] = 0xfe; | 225 | value[0] = 0xfe; |
| 168 | report->field[0]->value[1] = 0x0d; | 226 | value[1] = 0x0d; |
| 169 | report->field[0]->value[2] = magnitude >> 13; | 227 | value[2] = magnitude >> 13; |
| 170 | report->field[0]->value[3] = magnitude >> 13; | 228 | value[3] = magnitude >> 13; |
| 171 | report->field[0]->value[4] = magnitude >> 8; | 229 | value[4] = magnitude >> 8; |
| 172 | report->field[0]->value[5] = 0x00; | 230 | value[5] = 0x00; |
| 173 | report->field[0]->value[6] = 0x00; | 231 | value[6] = 0x00; |
| 174 | 232 | ||
| 175 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 233 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 176 | } | 234 | } |
| @@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude) | |||
| 181 | struct hid_device *hid = input_get_drvdata(dev); | 239 | struct hid_device *hid = input_get_drvdata(dev); |
| 182 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 240 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 183 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 241 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 242 | __s32 *value = report->field[0]->value; | ||
| 184 | magnitude = magnitude * 90 / 65535; | 243 | magnitude = magnitude * 90 / 65535; |
| 185 | |||
| 186 | 244 | ||
| 187 | report->field[0]->value[0] = 0xfe; | 245 | value[0] = 0xfe; |
| 188 | report->field[0]->value[1] = 0x03; | 246 | value[1] = 0x03; |
| 189 | report->field[0]->value[2] = magnitude >> 14; | 247 | value[2] = magnitude >> 14; |
| 190 | report->field[0]->value[3] = magnitude >> 14; | 248 | value[3] = magnitude >> 14; |
| 191 | report->field[0]->value[4] = magnitude; | 249 | value[4] = magnitude; |
| 192 | report->field[0]->value[5] = 0x00; | 250 | value[5] = 0x00; |
| 193 | report->field[0]->value[6] = 0x00; | 251 | value[6] = 0x00; |
| 194 | 252 | ||
| 195 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 253 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 196 | } | 254 | } |
| @@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range) | |||
| 200 | { | 258 | { |
| 201 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 259 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 202 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 260 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 261 | __s32 *value = report->field[0]->value; | ||
| 262 | |||
| 203 | dbg_hid("G25/G27/DFGT: setting range to %u\n", range); | 263 | dbg_hid("G25/G27/DFGT: setting range to %u\n", range); |
| 204 | 264 | ||
| 205 | report->field[0]->value[0] = 0xf8; | 265 | value[0] = 0xf8; |
| 206 | report->field[0]->value[1] = 0x81; | 266 | value[1] = 0x81; |
| 207 | report->field[0]->value[2] = range & 0x00ff; | 267 | value[2] = range & 0x00ff; |
| 208 | report->field[0]->value[3] = (range & 0xff00) >> 8; | 268 | value[3] = (range & 0xff00) >> 8; |
| 209 | report->field[0]->value[4] = 0x00; | 269 | value[4] = 0x00; |
| 210 | report->field[0]->value[5] = 0x00; | 270 | value[5] = 0x00; |
| 211 | report->field[0]->value[6] = 0x00; | 271 | value[6] = 0x00; |
| 212 | 272 | ||
| 213 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 273 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 214 | } | 274 | } |
| @@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) | |||
| 219 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 279 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 220 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 280 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 221 | int start_left, start_right, full_range; | 281 | int start_left, start_right, full_range; |
| 282 | __s32 *value = report->field[0]->value; | ||
| 283 | |||
| 222 | dbg_hid("Driving Force Pro: setting range to %u\n", range); | 284 | dbg_hid("Driving Force Pro: setting range to %u\n", range); |
| 223 | 285 | ||
| 224 | /* Prepare "coarse" limit command */ | 286 | /* Prepare "coarse" limit command */ |
| 225 | report->field[0]->value[0] = 0xf8; | 287 | value[0] = 0xf8; |
| 226 | report->field[0]->value[1] = 0x00; /* Set later */ | 288 | value[1] = 0x00; /* Set later */ |
| 227 | report->field[0]->value[2] = 0x00; | 289 | value[2] = 0x00; |
| 228 | report->field[0]->value[3] = 0x00; | 290 | value[3] = 0x00; |
| 229 | report->field[0]->value[4] = 0x00; | 291 | value[4] = 0x00; |
| 230 | report->field[0]->value[5] = 0x00; | 292 | value[5] = 0x00; |
| 231 | report->field[0]->value[6] = 0x00; | 293 | value[6] = 0x00; |
| 232 | 294 | ||
| 233 | if (range > 200) { | 295 | if (range > 200) { |
| 234 | report->field[0]->value[1] = 0x03; | 296 | report->field[0]->value[1] = 0x03; |
| @@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) | |||
| 240 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 302 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 241 | 303 | ||
| 242 | /* Prepare "fine" limit command */ | 304 | /* Prepare "fine" limit command */ |
| 243 | report->field[0]->value[0] = 0x81; | 305 | value[0] = 0x81; |
| 244 | report->field[0]->value[1] = 0x0b; | 306 | value[1] = 0x0b; |
| 245 | report->field[0]->value[2] = 0x00; | 307 | value[2] = 0x00; |
| 246 | report->field[0]->value[3] = 0x00; | 308 | value[3] = 0x00; |
| 247 | report->field[0]->value[4] = 0x00; | 309 | value[4] = 0x00; |
| 248 | report->field[0]->value[5] = 0x00; | 310 | value[5] = 0x00; |
| 249 | report->field[0]->value[6] = 0x00; | 311 | value[6] = 0x00; |
| 250 | 312 | ||
| 251 | if (range == 200 || range == 900) { /* Do not apply any fine limit */ | 313 | if (range == 200 || range == 900) { /* Do not apply any fine limit */ |
| 252 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 314 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| @@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) | |||
| 257 | start_left = (((full_range - range + 1) * 2047) / full_range); | 319 | start_left = (((full_range - range + 1) * 2047) / full_range); |
| 258 | start_right = 0xfff - start_left; | 320 | start_right = 0xfff - start_left; |
| 259 | 321 | ||
| 260 | report->field[0]->value[2] = start_left >> 4; | 322 | value[2] = start_left >> 4; |
| 261 | report->field[0]->value[3] = start_right >> 4; | 323 | value[3] = start_right >> 4; |
| 262 | report->field[0]->value[4] = 0xff; | 324 | value[4] = 0xff; |
| 263 | report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); | 325 | value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); |
| 264 | report->field[0]->value[6] = 0xff; | 326 | value[6] = 0xff; |
| 265 | 327 | ||
| 266 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 328 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 267 | } | 329 | } |
| @@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) | |||
| 344 | { | 406 | { |
| 345 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 407 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 346 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 408 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 347 | 409 | __s32 *value = report->field[0]->value; | |
| 348 | report->field[0]->value[0] = 0xf8; | 410 | |
| 349 | report->field[0]->value[1] = 0x12; | 411 | value[0] = 0xf8; |
| 350 | report->field[0]->value[2] = leds; | 412 | value[1] = 0x12; |
| 351 | report->field[0]->value[3] = 0x00; | 413 | value[2] = leds; |
| 352 | report->field[0]->value[4] = 0x00; | 414 | value[3] = 0x00; |
| 353 | report->field[0]->value[5] = 0x00; | 415 | value[4] = 0x00; |
| 354 | report->field[0]->value[6] = 0x00; | 416 | value[5] = 0x00; |
| 417 | value[6] = 0x00; | ||
| 355 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 418 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 356 | } | 419 | } |
| 357 | 420 | ||
| @@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev, | |||
| 360 | { | 423 | { |
| 361 | struct device *dev = led_cdev->dev->parent; | 424 | struct device *dev = led_cdev->dev->parent; |
| 362 | struct hid_device *hid = container_of(dev, struct hid_device, dev); | 425 | struct hid_device *hid = container_of(dev, struct hid_device, dev); |
| 363 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); | 426 | struct lg_drv_data *drv_data = hid_get_drvdata(hid); |
| 364 | struct lg4ff_device_entry *entry; | 427 | struct lg4ff_device_entry *entry; |
| 365 | int i, state = 0; | 428 | int i, state = 0; |
| 366 | 429 | ||
| @@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde | |||
| 395 | { | 458 | { |
| 396 | struct device *dev = led_cdev->dev->parent; | 459 | struct device *dev = led_cdev->dev->parent; |
| 397 | struct hid_device *hid = container_of(dev, struct hid_device, dev); | 460 | struct hid_device *hid = container_of(dev, struct hid_device, dev); |
| 398 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); | 461 | struct lg_drv_data *drv_data = hid_get_drvdata(hid); |
| 399 | struct lg4ff_device_entry *entry; | 462 | struct lg4ff_device_entry *entry; |
| 400 | int i, value = 0; | 463 | int i, value = 0; |
| 401 | 464 | ||
| @@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid) | |||
| 501 | /* Check if autocentering is available and | 564 | /* Check if autocentering is available and |
| 502 | * set the centering force to zero by default */ | 565 | * set the centering force to zero by default */ |
| 503 | if (test_bit(FF_AUTOCENTER, dev->ffbit)) { | 566 | if (test_bit(FF_AUTOCENTER, dev->ffbit)) { |
| 504 | if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ | 567 | if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ |
| 505 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; | 568 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; |
| 506 | else | 569 | else |
| 507 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; | 570 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; |
| @@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid) | |||
| 524 | } | 587 | } |
| 525 | drv_data->device_props = entry; | 588 | drv_data->device_props = entry; |
| 526 | 589 | ||
| 590 | entry->product_id = lg4ff_devices[i].product_id; | ||
| 527 | entry->min_range = lg4ff_devices[i].min_range; | 591 | entry->min_range = lg4ff_devices[i].min_range; |
| 528 | entry->max_range = lg4ff_devices[i].max_range; | 592 | entry->max_range = lg4ff_devices[i].max_range; |
| 529 | entry->set_range = lg4ff_devices[i].set_range; | 593 | entry->set_range = lg4ff_devices[i].set_range; |
| @@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid) | |||
| 534 | return error; | 598 | return error; |
| 535 | dbg_hid("sysfs interface created\n"); | 599 | dbg_hid("sysfs interface created\n"); |
| 536 | 600 | ||
| 601 | /* Set default axes parameters */ | ||
| 602 | switch (lg4ff_devices[i].product_id) { | ||
| 603 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | ||
| 604 | dbg_hid("Setting axes parameters for Driving Force Pro\n"); | ||
| 605 | input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0); | ||
| 606 | input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); | ||
| 607 | input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); | ||
| 608 | break; | ||
| 609 | default: | ||
| 610 | break; | ||
| 611 | } | ||
| 612 | |||
| 537 | /* Set the maximum range to start with */ | 613 | /* Set the maximum range to start with */ |
| 538 | entry->range = entry->max_range; | 614 | entry->range = entry->max_range; |
| 539 | if (entry->set_range != NULL) | 615 | if (entry->set_range != NULL) |
| @@ -594,6 +670,8 @@ out: | |||
| 594 | return 0; | 670 | return 0; |
| 595 | } | 671 | } |
| 596 | 672 | ||
| 673 | |||
| 674 | |||
| 597 | int lg4ff_deinit(struct hid_device *hid) | 675 | int lg4ff_deinit(struct hid_device *hid) |
| 598 | { | 676 | { |
| 599 | struct lg4ff_device_entry *entry; | 677 | struct lg4ff_device_entry *entry; |
