aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hid-tmff2.c69
-rw-r--r--src/hid-tmff2.h3
-rw-r--r--src/tmt248/hid-tmt248.c9
-rw-r--r--src/tmt300rs/hid-tmt300rs.c381
-rw-r--r--src/tmtsxw/hid-tmtsxw.c324
-rw-r--r--src/tmtx/hid-tmtx.c9
6 files changed, 608 insertions, 187 deletions
diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c
index ee4232a..3b51445 100644
--- a/src/hid-tmff2.c
+++ b/src/hid-tmff2.c
@@ -292,7 +292,7 @@ static void tmff2_work_handler(struct work_struct *w)
struct tmff2_effect_state *state;
int max_count = 0, effect_id;
unsigned long time_now;
- __u16 effect_length;
+ __u16 effect_delay, effect_length;
if (!tmff2)
@@ -304,17 +304,14 @@ static void tmff2_work_handler(struct work_struct *w)
time_now = JIFFIES2MS(jiffies);
state = &tmff2->states[effect_id];
+ effect_delay = state->effect.replay.delay;
effect_length = state->effect.replay.length;
if (test_bit(FF_EFFECT_PLAYING, &state->flags) && effect_length) {
- if ((time_now - state->start_time) >= effect_length) {
+ if ((time_now - state->start_time) >= (effect_delay + effect_length) * state->count) {
__clear_bit(FF_EFFECT_PLAYING, &state->flags);
__clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags);
- if (state->count)
- state->count--;
-
- if (state->count)
- __set_bit(FF_EFFECT_QUEUE_START, &state->flags);
+ state->count = 0;
}
}
@@ -359,6 +356,7 @@ static void tmff2_work_handler(struct work_struct *w)
max_count = state->count;
spin_unlock(&tmff2->lock);
+
/* wait for each effect update to actually be sent out to avoid
* filling up usb output queue */
hid_hw_wait(tmff2->hdev);
@@ -368,6 +366,32 @@ static void tmff2_work_handler(struct work_struct *w)
schedule_delayed_work(&tmff2->work, msecs_to_jiffies(timer_msecs));
}
+static void tmff2_rewrite_rumble(struct ff_effect *effect)
+{
+ /* this is more or less directly copied from
+ * https://elixir.bootlin.com/linux/latest/source/drivers/input/ff-core.c#L48
+ * except we set the direction to east, to make sure the direction
+ * scaling doesn't do any funny business */
+ int magnitude = 0;
+ if (effect->type != FF_RUMBLE)
+ return;
+
+ magnitude = effect->u.rumble.strong_magnitude / 3
+ + effect->u.rumble.weak_magnitude / 6;
+
+ effect->type = FF_PERIODIC;
+ effect->direction = 16384;
+ effect->u.periodic.waveform = FF_SINE;
+ effect->u.periodic.period = 50;
+ effect->u.periodic.magnitude = magnitude;
+ effect->u.periodic.offset = 0;
+ effect->u.periodic.phase = 0;
+ effect->u.periodic.envelope.attack_length = 0;
+ effect->u.periodic.envelope.attack_level = 0;
+ effect->u.periodic.envelope.fade_length = 0;
+ effect->u.periodic.envelope.fade_level = 0;
+}
+
static int tmff2_upload(struct input_dev *dev,
struct ff_effect *effect, struct ff_effect *old)
{
@@ -385,10 +409,13 @@ static int tmff2_upload(struct input_dev *dev,
spin_lock(&tmff2->lock);
state->effect = *effect;
+ tmff2_rewrite_rumble(&state->effect);
if (old) {
- if (!test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags))
+ if (!test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) {
state->old = *old;
+ tmff2_rewrite_rumble(&state->old);
+ }
__set_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags);
} else {
@@ -555,10 +582,22 @@ static int tmff2_wheel_init(struct tmff2_device_entry *tmff2)
for (i = 0; tmff2->supported_effects[i] >= 0; ++i)
__set_bit(tmff2->supported_effects[i], tmff2->input_dev->ffbit);
+ /* tell ffb subsystem that we want to handle our own rumble effects,
+ * this is a (maybe kind of silly) workaround to the ffb subsystem
+ * setting the direction of rumble to 0, which in our case means 0
+ * force. Instead, do exactly what the subsystem would do, but update
+ * the direction.
+ *
+ * It *might* be a good idea to change this in the subsystem proper, but
+ * I don't know if anyone relies on the rumble to be 0 degrees, consider
+ * this just a proof of concept for now */
+ if (test_bit(FF_PERIODIC, tmff2->input_dev->ffbit))
+ __set_bit(FF_RUMBLE, tmff2->input_dev->ffbit);
+
/* create actual ff device*/
if ((ret = input_ff_create(tmff2->input_dev, tmff2->max_effects))) {
hid_err(tmff2->hdev, "could not create input_ff\n");
- goto err;
+ goto ff_err;
}
/* set ff callbacks */
@@ -589,12 +628,15 @@ static int tmff2_wheel_init(struct tmff2_device_entry *tmff2)
/* create files */
if ((ret = tmff2_create_files(tmff2)))
- goto err;
+ goto file_err;
tmff2->allow_scheduling = 1;
return 0;
+file_err:
input_ff_destroy(tmff2->input_dev);
+ff_err:
+ kfree(tmff2->states);
err:
return ret;
}
@@ -632,7 +674,10 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id)
if ((ret = tx_populate_api(tmff2)))
goto wheel_err;
break;
-
+ case TSXW_ACTIVE:
+ if ((ret = tsxw_populate_api(tmff2)))
+ goto wheel_err;
+ break;
default:
ret = -ENODEV;
goto wheel_err;
@@ -727,6 +772,8 @@ static const struct hid_device_id tmff2_devices[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)},
/* tx */
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TX_ACTIVE)},
+ /* tsxw */
+ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TSXW_ACTIVE)},
{}
};
diff --git a/src/hid-tmff2.h b/src/hid-tmff2.h
index 1eb3546..666d249 100644
--- a/src/hid-tmff2.h
+++ b/src/hid-tmff2.h
@@ -102,6 +102,7 @@ struct tmff2_device_entry {
int t300rs_populate_api(struct tmff2_device_entry *tmff2);
int t248_populate_api(struct tmff2_device_entry *tmff2);
int tx_populate_api(struct tmff2_device_entry *tmff2);
+int tsxw_populate_api(struct tmff2_device_entry *tmff2);
#define TMT300RS_PS3_NORM_ID 0xb66e
#define TMT300RS_PS3_ADV_ID 0xb66f
@@ -111,6 +112,8 @@ int tx_populate_api(struct tmff2_device_entry *tmff2);
#define TX_ACTIVE 0xb669
+#define TSXW_ACTIVE 0xb692
+
/* APIs to different wheel families */
/* T248 and TX at least uses the T300RS api, not sure if there are other wheels
* but that's why these functions are given global linkage */
diff --git a/src/tmt248/hid-tmt248.c b/src/tmt248/hid-tmt248.c
index af3aaa9..9ac43d9 100644
--- a/src/tmt248/hid-tmt248.c
+++ b/src/tmt248/hid-tmt248.c
@@ -163,7 +163,7 @@ err:
return ret;
}
-int t248_wheel_destroy(void *data)
+static int t248_wheel_destroy(void *data)
{
struct t300rs_device_entry *t300rs = data;
@@ -175,7 +175,7 @@ int t248_wheel_destroy(void *data)
return 0;
}
-int t248_set_range(void *data, uint16_t value)
+static int t248_set_range(void *data, uint16_t value)
{
struct t300rs_device_entry *t248 = data;
@@ -251,9 +251,10 @@ static int t248_close(void *data, int open_mode)
return 0;
}
-int t248_wheel_init(struct tmff2_device_entry *tmff2, int open_mode)
+static int t248_wheel_init(struct tmff2_device_entry *tmff2, int open_mode)
{
- struct t300rs_device_entry *t248 = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL);
+ struct t300rs_device_entry *t248 = kzalloc(sizeof(struct t300rs_device_entry),
+ GFP_KERNEL);
struct list_head *report_list;
int ret;
diff --git a/src/tmt300rs/hid-tmt300rs.c b/src/tmt300rs/hid-tmt300rs.c
index 0191620..40be801 100644
--- a/src/tmt300rs/hid-tmt300rs.c
+++ b/src/tmt300rs/hid-tmt300rs.c
@@ -330,19 +330,94 @@ static u8 t300rs_rdesc_ps4_fixed[] = {
0xc0,
};
-static u8 spring_values[] = {
- 0xa6, 0x6a, 0xa6, 0x6a, 0xfe,
- 0xff, 0xfe, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xdf, 0x58, 0xa6,
- 0x6a, 0x06
+static u8 condition_values[] = {
+ 0xfe, 0xff, 0xfe, 0xff, 0xfe,
+ 0xff, 0xfe, 0xff
};
-static u8 damper_values[] = {
- 0xfc, 0x7f, 0xfc, 0x7f, 0xfe,
- 0xff, 0xfe, 0xff, 0xfe, 0xff,
- 0xfe, 0xff, 0xfc, 0x7f, 0xfc,
- 0x7f, 0x07
-};
+static void t300rs_calculate_periodic_values(struct ff_effect *effect)
+{
+ struct ff_periodic_effect *periodic = &effect->u.periodic;
+ int16_t headroom;
+
+ effect->replay.length -= 1;
+
+ periodic->magnitude = (periodic->magnitude * fixp_sin16(effect->direction * 360 / 0x10000)) / 0x7fff;
+
+ if (periodic->magnitude < 0){
+ /* the wheel handles positive magnitudes only */
+ periodic->magnitude = -periodic->magnitude;
+
+ /* to give the expected result 180 deg is added to the phase */
+ periodic->phase = (periodic->phase + (0x10000 / 2)) % 0x10000;
+ }
+
+ /* the interval [0; 32677[ is used by the wheel for the [0; 360[ degree phase shift */
+ periodic->phase = periodic->phase * 32677 / 0x10000;
+
+ headroom = 0x7fff - periodic->magnitude;
+ /* magnitude + offset cannot be outside the valid magnitude range, */
+ /* otherwise the wheel behaves incorrectly */
+ periodic->offset = clamp(periodic->offset, -headroom, headroom);
+}
+
+static uint16_t t300rs_condition_max_saturation(uint16_t effect_type)
+{
+ if(effect_type == FF_SPRING)
+ return 0x6aa6;
+
+ return 0x7ffc;
+}
+
+static uint16_t t300rs_condition_effect_type(uint16_t effect_type)
+{
+ if(effect_type == FF_SPRING)
+ return 0x06;
+
+ return 0x07;
+}
+
+static int16_t t300rs_calculate_coefficient(int16_t coeff, uint16_t effect_type)
+{
+ uint8_t input_level;
+
+ switch (effect_type)
+ {
+ case FF_SPRING:
+ input_level = spring_level;
+ break;
+ case FF_DAMPER:
+ input_level = damper_level;
+ break;
+ case FF_FRICTION:
+ input_level = friction_level;
+ break;
+ default:
+ input_level = 100;
+ break;
+ }
+
+ return coeff * input_level / 100;
+}
+
+static uint16_t t300rs_calculate_saturation(uint16_t sat, uint16_t effect_type)
+{
+ uint16_t max = t300rs_condition_max_saturation(effect_type);
+
+ if(sat == 0)
+ return max;
+
+ return sat * max / 0xffff;
+}
+
+static void t300rs_calculate_deadband(int16_t *out_rband, int16_t *out_lband,
+ uint16_t deadband, int16_t offset)
+{
+ /* max deadband value is 0x7fff in either direction */
+ /* deadband is the width of the deadzone, one direction is half of it */
+ *out_rband = clamp(offset + (deadband / 2), -0x7fff, 0x7fff);
+ *out_lband = clamp(offset - (deadband / 2), -0x7fff, 0x7fff);
+}
int t300rs_send_buf(struct t300rs_device_entry *t300rs, u8 *send_buffer, size_t len)
{
@@ -383,14 +458,20 @@ int t300rs_play_effect(void *data, struct tmff2_effect_state *state)
struct t300rs_device_entry *t300rs = data;
struct __packed t300rs_packet_play {
struct t300rs_packet_header header;
- uint8_t value;
+ uint8_t code;
+ uint16_t count;
} *play_packet = (struct t300rs_packet_play *)t300rs->send_buffer;
int ret;
t300rs_fill_header(&play_packet->header, state->effect.id, 0x89);
- play_packet->value = 0x01;
+ play_packet->code = 0x41;
+
+ if (state->count == 0 || state->count >= 65535)
+ play_packet->count = 0;
+ else
+ play_packet->count = cpu_to_le16(state->count);
ret = t300rs_send_int(t300rs);
if (ret)
@@ -630,6 +711,8 @@ static int t300rs_update_constant(struct t300rs_device_entry *t300rs,
int16_t level;
level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+ /* the Windows driver uses the range [-16385;16381] */
+ level = level / 2;
if ((constant.level != constant_old.level) || (effect.direction != old.direction)) {
@@ -738,114 +821,108 @@ error:
return ret;
}
-static int t300rs_update_damper(struct t300rs_device_entry *t300rs,
+static int t300rs_update_condition(struct t300rs_device_entry *t300rs,
struct tmff2_effect_state *state)
{
struct ff_effect effect = state->effect;
struct ff_effect old = state->old;
- struct ff_condition_effect damper = effect.u.condition[0];
- struct ff_condition_effect damper_old = old.u.condition[0];
- struct __packed t300rs_packet_mod_damper {
+ struct ff_condition_effect cond = effect.u.condition[0];
+ struct ff_condition_effect cond_old = old.u.condition[0];
+ struct __packed t300rs_packet_mod_condition
+ {
struct t300rs_packet_header header;
- uint8_t attribute;
- uint16_t value0;
- uint16_t value1;
- } *packet_mod_damper = (struct t300rs_packet_mod_damper *)t300rs->send_buffer;
-
- int ret, input_level;
-
- input_level = damper_level;
- if (state->effect.type == FF_FRICTION)
- input_level = friction_level;
+ uint16_t right_coeff;
+ uint16_t left_coeff;
+ uint16_t right_deadband;
+ uint16_t left_deadband;
+ uint16_t right_saturation;
+ uint16_t left_saturation;
+ uint8_t effect_type;
+ uint8_t update_type;
+ uint16_t duration;
+ uint16_t delay;
+ } *packet_mod_condition = (struct t300rs_packet_mod_condition *)t300rs->send_buffer;
- if (state->effect.type == FF_SPRING)
- input_level = spring_level;
+ int ret = 0;
+ uint16_t duration, duration_old;
+ uint16_t right_sat, right_sat_old, left_sat, left_sat_old;
+ uint16_t right_coeff, right_coeff_old, left_coeff, left_coeff_old;
+ int16_t right_deadband, right_deadband_old, left_deadband, left_deadband_old;
- if (damper.right_coeff != damper_old.right_coeff) {
- int16_t coeff = damper.right_coeff * input_level / 100;
+ right_coeff = t300rs_calculate_coefficient(cond.right_coeff, effect.type);
+ right_coeff_old = t300rs_calculate_coefficient(cond_old.right_coeff, old.type);
- t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e);
- packet_mod_damper->attribute = 0x41;
- packet_mod_damper->value0 = cpu_to_le16(coeff);
+ left_coeff = t300rs_calculate_coefficient(cond.left_coeff, effect.type);
+ left_coeff_old = t300rs_calculate_coefficient(cond_old.left_coeff, old.type);
- ret = t300rs_send_int(t300rs);
- if (ret) {
- hid_err(t300rs->hdev, "failed modifying damper rc\n");
- goto error;
- }
+ t300rs_calculate_deadband(&right_deadband, &left_deadband,
+ cond.deadband, cond.center);
+ t300rs_calculate_deadband(&right_deadband_old, &left_deadband_old,
+ cond_old.deadband, cond_old.center);
- }
+ right_sat = t300rs_calculate_saturation(cond.right_saturation, effect.type);
+ right_sat_old = t300rs_calculate_saturation(cond_old.right_saturation, old.type);
- if (damper.left_coeff != damper_old.left_coeff) {
- int16_t coeff = damper.left_coeff * input_level / 100;
+ left_sat = t300rs_calculate_saturation(cond.left_saturation, effect.type);
+ left_sat_old = t300rs_calculate_saturation(cond_old.left_saturation, old.type);
- t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e);
- packet_mod_damper->attribute = 0x42;
- packet_mod_damper->value0 = cpu_to_le16(coeff);
-
- ret = t300rs_send_int(t300rs);
- if (ret) {
- hid_err(t300rs->hdev, "failed modifying damper lc\n");
- goto error;
- }
-
- }
+ duration = effect.replay.length - 1;
+ duration_old = old.replay.length - 1;
- if ((damper.deadband != damper_old.deadband)
- || (damper.center != damper_old.center)) {
+ if (right_coeff != right_coeff_old
+ || left_coeff != left_coeff_old
+ || right_deadband != right_deadband_old
+ || left_deadband != left_deadband_old
+ || right_sat != right_sat_old
+ || left_sat != left_sat_old
+ || duration != duration_old
+ || effect.replay.delay != old.replay.delay) {
- uint16_t right_deadband = 0xfffe - damper.deadband - damper.center;
- uint16_t left_deadband = 0xfffe - damper.deadband + damper.center;
+ packet_mod_condition->right_coeff = cpu_to_le16(right_coeff);
+ packet_mod_condition->left_coeff = cpu_to_le16(left_coeff);
+ packet_mod_condition->right_deadband = cpu_to_le16(right_deadband);
+ packet_mod_condition->left_deadband = cpu_to_le16(left_deadband);
+ packet_mod_condition->right_saturation = cpu_to_le16(right_sat);
+ packet_mod_condition->left_saturation = cpu_to_le16(left_sat);
+ packet_mod_condition->effect_type = 0x06;
+ packet_mod_condition->update_type = 0x45;
+ packet_mod_condition->duration = cpu_to_le16(duration);
+ packet_mod_condition->delay = cpu_to_le16(effect.replay.delay);
- t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e);
- packet_mod_damper->attribute = 0x4c;
- packet_mod_damper->value0 = cpu_to_le16(right_deadband);
- packet_mod_damper->value1 = cpu_to_le16(left_deadband);
+ t300rs_fill_header(&packet_mod_condition->header, effect.id, 0x4c);
ret = t300rs_send_int(t300rs);
- if (ret) {
- hid_err(t300rs->hdev, "failed modifying damper deadband\n");
- goto error;
- }
-
- }
-
- ret = t300rs_update_simple_duration(t300rs, state, 0x06);
- if (ret) {
- hid_err(t300rs->hdev, "failed modifying damper duration\n");
- goto error;
+ if (ret)
+ hid_err(t300rs->hdev, "failed modifying condition effect\n");
}
-error:
return ret;
}
-static int t300rs_update_spring(struct t300rs_device_entry *t300rs,
- struct tmff2_effect_state *state)
-{
- return t300rs_update_damper(t300rs, state);
-}
-
-
static int t300rs_update_periodic(struct t300rs_device_entry *t300rs,
struct tmff2_effect_state *state)
{
struct ff_effect effect = state->effect;
struct ff_effect old = state->old;
- struct ff_periodic_effect periodic = effect.u.periodic;
- struct ff_periodic_effect periodic_old = old.u.periodic;
struct __packed t300rs_packet_mod_periodic {
struct t300rs_packet_header header;
uint8_t attribute;
uint16_t value;
} *packet_mod_periodic = (struct t300rs_packet_mod_periodic *)t300rs->send_buffer;
+ struct ff_periodic_effect periodic, periodic_old;
int ret;
- int16_t magnitude, phase;
+ uint16_t phase;
+ int16_t magnitude;
- magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+ t300rs_calculate_periodic_values(&effect);
+ periodic = effect.u.periodic;
+ magnitude = periodic.magnitude;
phase = periodic.phase;
+ t300rs_calculate_periodic_values(&old);
+ periodic_old = old.u.periodic;
+
if ((periodic.magnitude != periodic_old.magnitude)
|| (effect.direction != old.direction)) {
@@ -948,6 +1025,8 @@ static int t300rs_upload_constant(struct t300rs_device_entry *t300rs,
int ret;
level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+ /* the Windows driver uses the range [-16385;16381] */
+ level = level / 2;
duration = effect.replay.length - 1;
offset = effect.replay.delay;
@@ -1020,99 +1099,67 @@ static int t300rs_upload_ramp(struct t300rs_device_entry *t300rs,
return ret;
}
-static int t300rs_upload_spring(struct t300rs_device_entry *t300rs,
+static int t300rs_upload_condition(struct t300rs_device_entry *t300rs,
struct tmff2_effect_state *state)
{
struct ff_effect effect = state->effect;
/* we only care about the first axis */
- struct ff_condition_effect spring = state->effect.u.condition[0];
- struct __packed t300rs_packet_spring {
+ struct ff_condition_effect cond = state->effect.u.condition[0];
+ struct __packed t300rs_packet_condition {
struct t300rs_packet_header header;
- uint16_t right_coeff;
- uint16_t left_coeff;
- uint16_t right_deadband;
- uint16_t left_deadband;
- uint8_t spring_start[17];
+ int16_t right_coeff;
+ int16_t left_coeff;
+ int16_t right_deadband;
+ int16_t left_deadband;
+ uint16_t right_saturation;
+ uint16_t left_saturation;
+ uint8_t hardcoded[ARRAY_SIZE(condition_values)];
+ uint16_t max_right_saturation;
+ uint16_t max_left_saturation;
+ uint8_t type;
struct t300rs_packet_timing timing;
- } *packet_spring = (struct t300rs_packet_spring *)t300rs->send_buffer;
+ } *packet_condition = (struct t300rs_packet_condition *)t300rs->send_buffer;
int ret;
- uint16_t duration, right_coeff, left_coeff, right_deadband, left_deadband, offset;
-
- duration = effect.replay.length - 1;
-
- right_coeff = spring.right_coeff * spring_level / 100;
- left_coeff = spring.left_coeff * spring_level / 100;
-
- right_deadband = 0xfffe - spring.deadband - spring.center;
- left_deadband = 0xfffe - spring.deadband + spring.center;
-
- offset = effect.replay.delay;
+ uint16_t duration, right_sat, left_sat, right_coeff, left_coeff, max_sat, offset;
+ int16_t right_deadband, left_deadband;
- t300rs_fill_header(&packet_spring->header, effect.id, 0x64);
+ right_coeff = t300rs_calculate_coefficient(cond.right_coeff, effect.type);
+ left_coeff = t300rs_calculate_coefficient(cond.left_coeff, effect.type);
- packet_spring->right_coeff = cpu_to_le16(right_coeff);
- packet_spring->left_coeff = cpu_to_le16(left_coeff);
- packet_spring->right_deadband = cpu_to_le16(right_deadband);
- packet_spring->left_deadband = cpu_to_le16(left_deadband);
+ t300rs_calculate_deadband(&right_deadband, &left_deadband,
+ cond.deadband, cond.center);
- memcpy(&packet_spring->spring_start, spring_values, ARRAY_SIZE(spring_values));
- t300rs_fill_timing(&packet_spring->timing, duration, offset);
-
- ret = t300rs_send_int(t300rs);
- if (ret)
- hid_err(t300rs->hdev, "failed uploading spring\n");
-
- return ret;
-}
-
-static int t300rs_upload_damper(struct t300rs_device_entry *t300rs,
- struct tmff2_effect_state *state)
-{
-
- struct ff_effect effect = state->effect;
- /* we only care about the first axis */
- struct ff_condition_effect spring = state->effect.u.condition[0];
- struct __packed t300rs_packet_damper {
- struct t300rs_packet_header header;
- uint16_t right_coeff;
- uint16_t left_coeff;
- uint16_t right_deadband;
- uint16_t left_deadband;
- uint8_t damper_start[17];
- struct t300rs_packet_timing timing;
- } *packet_damper = (struct t300rs_packet_damper *)t300rs->send_buffer;
-
- int ret, input_level;
- uint16_t duration, right_coeff, left_coeff, right_deadband, left_deadband, offset;
+ right_sat = t300rs_calculate_saturation(cond.right_saturation, effect.type);
+ left_sat = t300rs_calculate_saturation(cond.left_saturation, effect.type);
duration = effect.replay.length - 1;
- input_level = damper_level;
- if (state->effect.type == FF_FRICTION)
- input_level = friction_level;
-
- right_coeff = spring.right_coeff * input_level / 100;
- left_coeff = spring.left_coeff * input_level / 100;
+ offset = effect.replay.delay;
- right_deadband = 0xfffe - spring.deadband - spring.center;
- left_deadband = 0xfffe - spring.deadband + spring.center;
+ t300rs_fill_header(&packet_condition->header, effect.id, 0x64);
- offset = effect.replay.delay;
+ packet_condition->right_coeff = cpu_to_le16(right_coeff);
+ packet_condition->left_coeff = cpu_to_le16(left_coeff);
+ packet_condition->right_deadband = cpu_to_le16(right_deadband);
+ packet_condition->left_deadband = cpu_to_le16(left_deadband);
+ packet_condition->right_saturation = cpu_to_le16(right_sat);
+ packet_condition->left_saturation = cpu_to_le16(left_sat);
- t300rs_fill_header(&packet_damper->header, effect.id, 0x64);
+ memcpy(&packet_condition->hardcoded, condition_values,
+ ARRAY_SIZE(condition_values));
- packet_damper->right_coeff = cpu_to_le16(right_coeff);
- packet_damper->left_coeff = cpu_to_le16(left_coeff);
- packet_damper->right_deadband = cpu_to_le16(right_deadband);
- packet_damper->left_deadband = cpu_to_le16(left_deadband);
+ max_sat = t300rs_condition_max_saturation(effect.type);
+ /* it seems that the maximum values do not affect the wheel. */
+ packet_condition->max_right_saturation = cpu_to_le16(max_sat);
+ packet_condition->max_left_saturation = cpu_to_le16(max_sat);
+ packet_condition->type = t300rs_condition_effect_type(effect.type);
- memcpy(&packet_damper->damper_start, damper_values, ARRAY_SIZE(damper_values));
- t300rs_fill_timing(&packet_damper->timing, duration, offset);
+ t300rs_fill_timing(&packet_condition->timing, duration, offset);
ret = t300rs_send_int(t300rs);
if (ret)
- hid_err(t300rs->hdev, "failed uploading spring\n");
+ hid_err(t300rs->hdev, "failed uploading condition\n");
return ret;
}
@@ -1121,7 +1168,6 @@ static int t300rs_upload_periodic(struct t300rs_device_entry *t300rs,
struct tmff2_effect_state *state)
{
struct ff_effect effect = state->effect;
- struct ff_periodic_effect periodic = state->effect.u.periodic;
struct __packed t300rs_packet_periodic {
struct t300rs_packet_header header;
uint16_t magnitude;
@@ -1134,19 +1180,20 @@ static int t300rs_upload_periodic(struct t300rs_device_entry *t300rs,
struct t300rs_packet_timing timing;
} *packet_periodic = (struct t300rs_packet_periodic *)t300rs->send_buffer;
+ struct ff_periodic_effect periodic;
int ret;
- uint16_t duration, period, offset, periodic_offset;
- int16_t phase, magnitude;
-
- duration = effect.replay.length - 1;
- magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+ uint16_t duration, period, phase, offset, periodic_offset;
+ int16_t magnitude;
+ t300rs_calculate_periodic_values(&effect);
+ periodic = effect.u.periodic;
+ duration = effect.replay.length;
+ offset = effect.replay.delay;
+ magnitude = periodic.magnitude;
+ period = periodic.period;
phase = periodic.phase;
periodic_offset = periodic.offset;
- period = periodic.period;
- offset = effect.replay.delay;
-
t300rs_fill_header(&packet_periodic->header, effect.id, 0x6b);
packet_periodic->magnitude = cpu_to_le16(magnitude);
@@ -1179,11 +1226,10 @@ int t300rs_update_effect(void *data, struct tmff2_effect_state *state)
case FF_RAMP:
return t300rs_update_ramp(t300rs, state);
case FF_SPRING:
- return t300rs_update_spring(t300rs, state);
case FF_DAMPER:
case FF_FRICTION:
case FF_INERTIA:
- return t300rs_update_damper(t300rs, state);
+ return t300rs_update_condition(t300rs, state);
case FF_PERIODIC:
return t300rs_update_periodic(t300rs, state);
default:
@@ -1202,11 +1248,10 @@ int t300rs_upload_effect(void *data, struct tmff2_effect_state *state)
case FF_RAMP:
return t300rs_upload_ramp(t300rs, state);
case FF_SPRING:
- return t300rs_upload_spring(t300rs, state);
case FF_DAMPER:
case FF_FRICTION:
case FF_INERTIA:
- return t300rs_upload_damper(t300rs, state);
+ return t300rs_upload_condition(t300rs, state);
case FF_PERIODIC:
return t300rs_upload_periodic(t300rs, state);
default:
diff --git a/src/tmtsxw/hid-tmtsxw.c b/src/tmtsxw/hid-tmtsxw.c
new file mode 100644
index 0000000..b8ee1fd
--- /dev/null
+++ b/src/tmtsxw/hid-tmtsxw.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/usb.h>
+#include <linux/hid.h>
+#include "../hid-tmff2.h"
+
+#define TMTSXW_MAX_EFFECTS 16
+#define TMTSXW_BUFFER_LENGTH 63
+
+static const u8 setup_0[64] = { 0x42, 0x01 };
+static const u8 setup_1[64] = { 0x0a, 0x04, 0x90, 0x03 };
+static const u8 setup_2[64] = { 0x0a, 0x04, 0x00, 0x0c };
+static const u8 setup_3[64] = { 0x0a, 0x04, 0x12, 0x10 };
+static const u8 setup_4[64] = { 0x0a, 0x04, 0x00, 0x06 };
+static const u8 setup_5[64] = { 0x0a, 0x04, 0x00, 0x0e };
+static const u8 setup_6[64] = { 0x0a, 0x04, 0x00, 0x0e, 0x01 };
+static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6 };
+static const unsigned int setup_arr_sizes[] = {
+ ARRAY_SIZE(setup_0),
+ ARRAY_SIZE(setup_1),
+ ARRAY_SIZE(setup_2),
+ ARRAY_SIZE(setup_3),
+ ARRAY_SIZE(setup_4),
+ ARRAY_SIZE(setup_5),
+ ARRAY_SIZE(setup_6)
+};
+
+static const unsigned long tsxw_params =
+ PARAM_SPRING_LEVEL
+ | PARAM_DAMPER_LEVEL
+ | PARAM_FRICTION_LEVEL
+ | PARAM_RANGE
+ | PARAM_GAIN
+ ;
+
+static const signed short tsxw_effects[] = {
+ FF_CONSTANT,
+ FF_RAMP,
+ FF_SPRING,
+ FF_DAMPER,
+ FF_FRICTION,
+ FF_INERTIA,
+ FF_PERIODIC,
+ FF_SINE,
+ FF_TRIANGLE,
+ FF_SQUARE,
+ FF_SAW_UP,
+ FF_SAW_DOWN,
+ FF_AUTOCENTER,
+ FF_GAIN,
+ -1
+};
+
+/* TODO: sort through this stuff */
+static u8 tsxw_pc_rdesc_fixed[] = {
+ 0x05, 0x01, /* Usage page (Generic Desktop) */
+ 0x09, 0x04, /* Usage (Joystick) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x85, 0x07, /* Report ID (7) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x15, 0x00, /* Logical minimum (0) */
+ 0x27, 0xff, 0xff, 0x00, 0x00, /* Logical maximum (65535) */
+ 0x35, 0x00, /* Physical minimum (0) */
+ 0x47, 0xff, 0xff, 0x00, 0x00, /* Physical maximum (65535) */
+ 0x75, 0x10, /* Report size (16) */
+ 0x95, 0x01, /* Report count (1) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0x09, 0x35, /* Usage (Rz) (Brake) */
+ 0x26, 0xff, 0x03, /* Logical maximum (1023) */
+ 0x46, 0xff, 0x03, /* Physical maximum (1023) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0x09, 0x32, /* Usage (Z) (Gas) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0x09, 0x31, /* Usage (Y) (Clutch) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0x81, 0x03, /* Input (Variable, Absolute, Constant) */
+ 0x05, 0x09, /* Usage page (Button) */
+ 0x19, 0x01, /* Usage minimum (1) */
+ 0x29, 0x0d, /* Usage maximum (13) */
+ 0x25, 0x01, /* Logical maximum (1) */
+ 0x45, 0x01, /* Physical maximum (1) */
+ 0x75, 0x01, /* Report size (1) */
+ 0x95, 0x0d, /* Report count (13) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0x75, 0x0b, /* Report size (13) */
+ 0x95, 0x01, /* Report count (1) */
+ 0x81, 0x03, /* Usage (Variable, Absolute, Constant) */
+ 0x05, 0x01, /* Usage page (Generic Desktop) */
+ 0x09, 0x39, /* Usage (Hat Switch) */
+ 0x25, 0x07, /* Logical maximum (7) */
+ 0x46, 0x3b, 0x01, /* Physical maximum (315) */
+ 0x55, 0x00, /* Unit exponent (0) */
+ 0x65, 0x14, /* Unit (Eng Rot, Angular Pos) */
+ 0x75, 0x04, /* Report size (4) */
+ 0x81, 0x42, /* Input (Variable, Absolute, NullState) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x81, 0x03, /* Input (Variable, Absolute, Constant) */
+ 0x85, 0x60, /* Report ID (96), prev 10 */
+ 0x06, 0x00, 0xff, /* Usage page (Vendor 1) */
+ 0x09, 0x60, /* Usage (96), prev 10 */
+ 0x75, 0x08, /* Report size (8) */
+ 0x95, 0x3f, /* Report count (63) */
+ 0x26, 0xff, 0x7f, /* Logical maximum (32767) */
+ 0x15, 0x00, /* Logical minimum (0) */
+ 0x46, 0xff, 0x7f, /* Physical maximum (32767) */
+ 0x36, 0x00, 0x80, /* Physical minimum (-32768) */
+ 0x91, 0x02, /* Output (Variable, Absolute) */
+ 0x85, 0x02, /* Report ID (2) */
+ 0x09, 0x02, /* Usage (2) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0x09, 0x14, /* Usage (20) */
+ 0x85, 0x14, /* Report ID (20) */
+ 0x81, 0x02, /* Input (Variable, Absolute) */
+ 0xc0, /* End collection */
+ 0xc0, /* End collection */
+};
+
+static int tsxw_interrupts(struct t300rs_device_entry *tsxw)
+{
+ u8 *send_buf = kmalloc(256, GFP_KERNEL);
+ struct usb_interface *usbif = to_usb_interface(tsxw->hdev->dev.parent);
+ struct usb_host_endpoint *ep;
+ int ret, trans, b_ep, i;
+
+ if (!send_buf) {
+ hid_err(tsxw->hdev, "failed allocating send buffer\n");
+ return -ENOMEM;
+ }
+
+ ep = &usbif->cur_altsetting->endpoint[1];
+ b_ep = ep->desc.bEndpointAddress;
+
+ for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) {
+ memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]);
+
+ ret = usb_interrupt_msg(tsxw->usbdev,
+ usb_sndintpipe(tsxw->usbdev, b_ep),
+ send_buf, setup_arr_sizes[i],
+ &trans,
+ USB_CTRL_SET_TIMEOUT);
+
+ if (ret) {
+ hid_err(tsxw->hdev, "setup data couldn't be sent\n");
+ goto err;
+ }
+ }
+
+err:
+ kfree(send_buf);
+ return ret;
+}
+
+static int tsxw_wheel_destroy(void *data)
+{
+ struct t300rs_device_entry *t300rs = data;
+
+ if (!t300rs)
+ return -ENODEV;
+
+ kfree(t300rs->send_buffer);
+ kfree(t300rs);
+ return 0;
+}
+
+static int tsxw_set_range(void *data, uint16_t value)
+{
+ struct t300rs_device_entry *tsxw = data;
+
+ if (value < 140) {
+ hid_info(tsxw->hdev, "value %i too small, clamping to 140\n", value);
+ value = 140;
+ }
+
+ if (value > 1080) {
+ hid_info(tsxw->hdev, "value %i too large, clamping to 1080\n", value);
+ value = 1080;
+ }
+
+ return t300rs_set_range(data, value);
+}
+
+static int tsxw_send_open(struct t300rs_device_entry *tsxw)
+{
+ int r1, r2;
+ tsxw->send_buffer[0] = 0x01;
+ tsxw->send_buffer[1] = 0x04;
+ if ((r1 = t300rs_send_int(tsxw)))
+ return r1;
+
+ tsxw->send_buffer[0] = 0x01;
+ tsxw->send_buffer[1] = 0x05;
+ if ((r2 = t300rs_send_int(tsxw)))
+ return r2;
+
+ return 0;
+}
+
+static int tsxw_open(void *data, int open_mode)
+{
+ struct t300rs_device_entry *tsxw = data;
+
+ if (!tsxw)
+ return -ENODEV;
+
+ if (open_mode)
+ tsxw_send_open(tsxw);
+
+ return tsxw->open(tsxw->input_dev);
+}
+
+static int tsxw_send_close(struct t300rs_device_entry *tsxw)
+{
+ int r1, r2;
+ tsxw->send_buffer[0] = 0x01;
+ tsxw->send_buffer[1] = 0x05;
+ if ((r1 = t300rs_send_int(tsxw)))
+ return r1;
+
+ tsxw->send_buffer[0] = 0x01;
+ tsxw->send_buffer[1] = 0x00;
+ if ((r2 = t300rs_send_int(tsxw)))
+ return r2;
+
+ return 0;
+}
+
+static int tsxw_close(void *data, int open_mode)
+{
+ struct t300rs_device_entry *tsxw = data;
+
+ if (!tsxw)
+ return -ENODEV;
+
+ if (open_mode)
+ tsxw_send_close(tsxw);
+
+ tsxw->close(tsxw->input_dev);
+ return 0;
+}
+
+static int tsxw_wheel_init(struct tmff2_device_entry *tmff2, int open_mode)
+{
+ struct t300rs_device_entry *tsxw = kzalloc(sizeof(struct t300rs_device_entry),
+ GFP_KERNEL);
+ struct list_head *report_list;
+ int ret;
+
+
+ if (!tsxw) {
+ ret = -ENOMEM;
+ goto tsxw_err;
+ }
+
+ tsxw->hdev = tmff2->hdev;
+ tsxw->input_dev = tmff2->input_dev;
+ tsxw->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent);
+ tsxw->buffer_length = TMTSXW_BUFFER_LENGTH;
+
+ tsxw->send_buffer = kzalloc(tsxw->buffer_length, GFP_KERNEL);
+ if (!tsxw->send_buffer) {
+ ret = -ENOMEM;
+ goto send_err;
+ }
+
+ report_list = &tsxw->hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+ tsxw->report = list_entry(report_list->next, struct hid_report, list);
+ tsxw->ff_field = tsxw->report->field[0];
+
+ tsxw->open = tsxw->input_dev->open;
+ tsxw->close = tsxw->input_dev->close;
+
+ if ((ret = tsxw_interrupts(tsxw)))
+ goto interrupt_err;
+
+ /* everything went OK */
+ tmff2->data = tsxw;
+ tmff2->params = tsxw_params;
+ tmff2->max_effects = TMTSXW_MAX_EFFECTS;
+ memcpy(tmff2->supported_effects, tsxw_effects, sizeof(tsxw_effects));
+
+ if (!open_mode)
+ tsxw_send_open(tsxw);
+
+ hid_info(tsxw->hdev, "force feedback for TS-XW\n");
+ return 0;
+
+interrupt_err:
+send_err:
+ kfree(tsxw);
+tsxw_err:
+ hid_err(tmff2->hdev, "failed initializing TS-XW\n");
+ return ret;
+}
+
+static __u8 *tsxw_wheel_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ rdesc = tsxw_pc_rdesc_fixed;
+ *rsize = sizeof(tsxw_pc_rdesc_fixed);
+ return rdesc;
+}
+
+int tsxw_populate_api(struct tmff2_device_entry *tmff2)
+{
+ tmff2->play_effect = t300rs_play_effect;
+ tmff2->upload_effect = t300rs_upload_effect;
+ tmff2->update_effect = t300rs_update_effect;
+ tmff2->stop_effect = t300rs_stop_effect;
+
+ tmff2->set_gain = t300rs_set_gain;
+ tmff2->set_autocenter = t300rs_set_autocenter;
+ /* TS-XW has 1080 degree range, just like T300RS 1080 */
+ tmff2->set_range = tsxw_set_range;
+ tmff2->wheel_fixup = tsxw_wheel_fixup;
+
+ tmff2->open = tsxw_open;
+ tmff2->close = tsxw_close;
+
+ tmff2->wheel_init = tsxw_wheel_init;
+ tmff2->wheel_destroy = tsxw_wheel_destroy;
+
+ return 0;
+}
diff --git a/src/tmtx/hid-tmtx.c b/src/tmtx/hid-tmtx.c
index afca799..857886c 100644
--- a/src/tmtx/hid-tmtx.c
+++ b/src/tmtx/hid-tmtx.c
@@ -151,7 +151,7 @@ err:
return ret;
}
-int tx_wheel_destroy(void *data)
+static int tx_wheel_destroy(void *data)
{
struct t300rs_device_entry *t300rs = data;
@@ -163,7 +163,7 @@ int tx_wheel_destroy(void *data)
return 0;
}
-int tx_set_range(void *data, uint16_t value)
+static int tx_set_range(void *data, uint16_t value)
{
struct t300rs_device_entry *tx = data;
@@ -239,9 +239,10 @@ static int tx_close(void *data, int open_mode)
return 0;
}
-int tx_wheel_init(struct tmff2_device_entry *tmff2, int open_mode)
+static int tx_wheel_init(struct tmff2_device_entry *tmff2, int open_mode)
{
- struct t300rs_device_entry *tx = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL);
+ struct t300rs_device_entry *tx = kzalloc(sizeof(struct t300rs_device_entry),
+ GFP_KERNEL);
struct list_head *report_list;
int ret;