diff --git a/hid-xpadneo/src/hid-xpadneo.c b/hid-xpadneo/src/hid-xpadneo.c index d75344b3..c969d763 100644 --- a/hid-xpadneo/src/hid-xpadneo.c +++ b/hid-xpadneo/src/hid-xpadneo.c @@ -117,16 +117,18 @@ enum { }; struct ff_data { - u8 enable_actuators; + u8 enable; u8 magnitude_left_trigger; u8 magnitude_right_trigger; u8 magnitude_left; u8 magnitude_right; - u8 duration; - u8 start_delay; + u8 pulse_sustain_10ms; + u8 pulse_release_10ms; u8 loop_count; } __packed; +#define XPADNEO_ONES_FF_REPORT 3 + struct ff_report { u8 report_id; struct ff_data ff; @@ -182,31 +184,53 @@ struct xpadneo_devdata { /* axis states */ s32 last_abs_z; s32 last_abs_rz; + + /* buffer for ff_worker */ + struct work_struct ff_worker; + struct ff_data ff; + void *output_report_dmabuf; }; -void create_ff_pck (struct ff_report *pck, u8 id, u8 en_act, - u8 mag_lt, u8 mag_rt, u8 mag_l, u8 mag_r, - u8 start_delay) { +/* + * Force Feedback Worker + * + * This function is called by the kernel worker thread. + */ +static void xpadneo_ff_worker(struct work_struct *work) +{ + struct xpadneo_devdata *xdata = container_of(work, struct xpadneo_devdata, ff_worker); + struct hid_device *hdev = xdata->hdev; + struct ff_report *r = xdata->output_report_dmabuf; + int ret; - pck->report_id = id; + memset(r, 0, sizeof(*r)); - pck->ff.enable_actuators = en_act; - pck->ff.magnitude_left_trigger = mag_lt; - pck->ff.magnitude_right_trigger = mag_rt; - pck->ff.magnitude_left = mag_l; - pck->ff.magnitude_right = mag_r; - pck->ff.duration = 0xFF; - pck->ff.start_delay = start_delay; - pck->ff.loop_count = 0xFF; + r->report_id = XPADNEO_ONES_FF_REPORT; - /* It is up to the Input-Subsystem to start and stop effects as needed. - * All WE need to do is to play the effect at least 32767 ms long. - * Take a look here: - * https://stackoverflow.com/questions/48034091/ - * We therefore simply play the effect as long as possible, which is - * 2, 55s * 255 = 650, 25s ~ = 10min - */ + /* the user can disable some rumble motors */ + r->ff.enable = FF_ENABLE_ALL; + if (param_disable_ff & PARAM_DISABLE_FF_TRIGGER) + r->ff.enable &= ~(FF_ENABLE_LEFT_TRIGGER | FF_ENABLE_RIGHT_TRIGGER); + if (param_disable_ff & PARAM_DISABLE_FF_MAIN) + r->ff.enable &= ~(FF_ENABLE_LEFT | FF_ENABLE_RIGHT); + + /* ff-memless has a time resolution of 50ms but we pulse the motors + * as long as possible */ + r->ff.pulse_sustain_10ms = U8_MAX; + r->ff.loop_count = U8_MAX; + + /* trigger motors */ + r->ff.magnitude_left_trigger = xdata->ff.magnitude_left_trigger; + r->ff.magnitude_right_trigger = xdata->ff.magnitude_right_trigger; + + /* main motors */ + r->ff.magnitude_left = xdata->ff.magnitude_left; + r->ff.magnitude_right = xdata->ff.magnitude_right; + + ret = hid_hw_output_report(hdev, (__u8 *)r, sizeof(*r)); + if (ret < 0) + hid_warn(hdev, "failed to send FF report: %d\n", ret); } @@ -224,17 +248,9 @@ void create_ff_pck (struct ff_report *pck, u8 id, u8 en_act, static int xpadneo_ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct ff_report ff_pck; - u32 weak, strong, direction, max, max_damped, max_unscaled; - u8 mag_main_right, mag_main_left, mag_trigger_right, mag_trigger_left; - u8 ff_active; - const int fractions_percent[] = {100, 96, 85, 69, 50, 31, 15, 4, 0, 4, 15, 31, 50, 69, 85, 96, 100}; const int proportions_idx_max = 16; - u8 index_left, index_right; - int fraction_TL, fraction_TR; - u8 trigger_rumble_damping_nonzero; enum { DIRECTION_DOWN = 0x0000, @@ -243,11 +259,11 @@ static int xpadneo_ff_play(struct input_dev *dev, void *data, DIRECTION_RIGHT = 0xC000, }; + int fraction_TL, fraction_TR; + u32 weak, strong, direction, max_damped, max_unscaled; struct hid_device *hdev = input_get_drvdata(dev); - - if (param_disable_ff == PARAM_DISABLE_FF_ALL) - return 0; + struct xpadneo_devdata *xdata = hid_get_drvdata(hdev); if (effect->type != FF_RUMBLE) return 0; @@ -260,59 +276,42 @@ static int xpadneo_ff_play(struct input_dev *dev, void *data, hid_dbg_lvl(DBG_LVL_FEW, hdev, "playing effect: strong: %#04x, weak: %#04x, direction: %#04x\n", strong, weak, direction); - /* calculate the physical magnitudes */ - mag_main_right = (u8)((weak * 100) / 0xFFFF); /* scale from 16 bit to 0..100 */ - mag_main_left = (u8)((strong * 100) / 0xFFFF); /* scale from 16 bit to 0..100 */ + /* calculate the physical magnitudes, scale from 16 bit to 0..100 */ + xdata->ff.magnitude_left = (u8)((strong * 100) / U16_MAX); + xdata->ff.magnitude_right = (u8)((weak * 100) / U16_MAX); + + /* we want to keep the rumbling at the triggers below the maximum + * of the weak and strong main rumble + */ + max_unscaled = weak > strong ? weak : strong; /* get the proportions from a precalculated cosine table * calculation goes like: * cosine(a) * 100 = {100, 96, 85, 69, 50, 31, 15, 4, 0, 4, 15, 31, 50, 69, 85, 96, 100} * fractions_percent(a) = round(50 + (cosine * 50)) */ - fraction_TL = 0; - fraction_TR = 0; + fraction_TL = fraction_TR = 0; if (direction >= DIRECTION_LEFT && direction <= DIRECTION_RIGHT) { - index_left = (direction - DIRECTION_LEFT) >> 11; - index_right = proportions_idx_max - index_left; + u8 index_left = (direction - DIRECTION_LEFT) >> 11; + u8 index_right = proportions_idx_max - index_left; fraction_TL = fractions_percent[index_left]; fraction_TR = fractions_percent[index_right]; } - /* we want to keep the rumbling at the triggers below the maximum - * of the weak and strong main rumble - */ - max = mag_main_right > mag_main_left ? mag_main_right : mag_main_left; - max_unscaled = weak > strong ? weak : strong; - /* the user can change the damping at runtime, hence check the range */ - trigger_rumble_damping_nonzero - = param_trigger_rumble_damping == 0 ? 1 : param_trigger_rumble_damping; - max_damped = max_unscaled / trigger_rumble_damping_nonzero; - mag_trigger_left = (u8)((max_damped * fraction_TL) / 0xFFFF); - mag_trigger_right = (u8)((max_damped * fraction_TR) / 0xFFFF); - - ff_active = FF_ENABLE_ALL; - if (param_disable_ff & PARAM_DISABLE_FF_TRIGGER) - ff_active &= ~(FF_ENABLE_LEFT_TRIGGER | FF_ENABLE_RIGHT_TRIGGER); - if (param_disable_ff & PARAM_DISABLE_FF_MAIN) - ff_active &= ~(FF_ENABLE_LEFT | FF_ENABLE_RIGHT); - - create_ff_pck( - &ff_pck, 0x03, - ff_active, - mag_trigger_left, mag_trigger_right, - mag_main_left, mag_main_right, - 0); - - hid_dbg_lvl(DBG_LVL_FEW, hdev, - "active: %#04x, max: %#04x, prop_left: %#04x, prop_right: %#04x, left trigger: %#04x, right: %#04x\n", - ff_active, - max, fraction_TL, fraction_TR, - ff_pck.ff.magnitude_left_trigger, - ff_pck.ff.magnitude_right_trigger); + if (param_trigger_rumble_damping > 0) { + max_damped = max_unscaled / param_trigger_rumble_damping; + } + else { + max_damped = max_unscaled; + } - hid_hw_output_report(hdev, (u8 *)&ff_pck, sizeof(ff_pck)); + /* calculate the physical magnitudes, scale from 16 bit to 0..100 */ + xdata->ff.magnitude_left_trigger = (u8)((max_damped * fraction_TL) / U16_MAX); + xdata->ff.magnitude_right_trigger = (u8)((max_damped * fraction_TR) / U16_MAX); + /* schedule writing a rumble report to the controller */ + schedule_work(&xdata->ff_worker); return 0; } @@ -328,7 +327,9 @@ static void xpadneo_welcome_rumble(struct hid_device *hdev) ff_pck.ff.magnitude_left = 20; ff_pck.ff.magnitude_right_trigger = 10; ff_pck.ff.magnitude_left_trigger = 10; - ff_pck.ff.duration = 33; + ff_pck.ff.pulse_sustain_10ms = 5; + ff_pck.ff.pulse_release_10ms = 5; + ff_pck.ff.loop_count = 3; ff_pck.ff.enable = FF_ENABLE_RIGHT; hid_hw_output_report(hdev, (u8 *)&ff_pck, sizeof(ff_pck)); @@ -350,21 +351,22 @@ static void xpadneo_welcome_rumble(struct hid_device *hdev) */ static int xpadneo_initDevice(struct hid_device *hdev) { - int error; - struct xpadneo_devdata *xdata = hid_get_drvdata(hdev); struct input_dev *idev = xdata->idev; + INIT_WORK(&xdata->ff_worker, xpadneo_ff_worker); + xdata->output_report_dmabuf = devm_kzalloc(&hdev->dev, + sizeof(struct ff_report), + GFP_KERNEL); + if (xdata->output_report_dmabuf == NULL) + return -ENOMEM; + /* 'HELLO' FROM THE OTHER SIDE */ if (!param_disable_ff) xpadneo_welcome_rumble(hdev); /* Init Input System for Force Feedback (FF) */ input_set_capability(idev, EV_FF, FF_RUMBLE); - error = input_ff_create_memless(idev, NULL, xpadneo_ff_play); - if (error) - return error; - - return 0; + return input_ff_create_memless(idev, NULL, xpadneo_ff_play); } @@ -1232,6 +1234,7 @@ static void xpadneo_remove_device(struct hid_device *hdev) { struct xpadneo_devdata *xdata = hid_get_drvdata(hdev); + cancel_work_sync(&xdata->ff_worker); hid_hw_close(hdev); /* Cleaning up here */