mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
HID: haptic: initialize haptic device
Add hid_haptic_init(). Parse autotrigger report to retrieve ordinals for press and release waveforms. Implement force feedback functions. Signed-off-by: Angela Czubak <aczubak@google.com> Co-developed-by: Jonathan Denose <jdenose@google.com> Signed-off-by: Jonathan Denose <jdenose@google.com> Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
This commit is contained in:
parent
7a56e7b211
commit
344ff35849
|
|
@ -5,12 +5,16 @@
|
|||
* Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hid-haptic.h"
|
||||
|
||||
void hid_haptic_feature_mapping(struct hid_device *hdev,
|
||||
struct hid_haptic_device *haptic,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
u16 usage_hid;
|
||||
|
||||
if (usage->hid == HID_HP_AUTOTRIGGER) {
|
||||
if (usage->usage_index >= field->report_count) {
|
||||
dev_err(&hdev->dev,
|
||||
|
|
@ -25,6 +29,20 @@ void hid_haptic_feature_mapping(struct hid_device *hdev,
|
|||
haptic->default_auto_trigger =
|
||||
field->value[usage->usage_index];
|
||||
haptic->auto_trigger_report = field->report;
|
||||
} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ORDINAL) {
|
||||
usage_hid = usage->hid & HID_USAGE;
|
||||
switch (field->logical) {
|
||||
case HID_HP_WAVEFORMLIST:
|
||||
if (usage_hid > haptic->max_waveform_id)
|
||||
haptic->max_waveform_id = usage_hid;
|
||||
break;
|
||||
case HID_HP_DURATIONLIST:
|
||||
if (usage_hid > haptic->max_duration_id)
|
||||
haptic->max_duration_id = usage_hid;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
|
||||
|
|
@ -70,3 +88,423 @@ int hid_haptic_input_configured(struct hid_device *hdev,
|
|||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_haptic_input_configured);
|
||||
|
||||
static void parse_auto_trigger_field(struct hid_haptic_device *haptic,
|
||||
struct hid_field *field)
|
||||
{
|
||||
int count = field->report_count;
|
||||
int n;
|
||||
u16 usage_hid;
|
||||
|
||||
for (n = 0; n < count; n++) {
|
||||
switch (field->usage[n].hid & HID_USAGE_PAGE) {
|
||||
case HID_UP_ORDINAL:
|
||||
usage_hid = field->usage[n].hid & HID_USAGE;
|
||||
switch (field->logical) {
|
||||
case HID_HP_WAVEFORMLIST:
|
||||
haptic->hid_usage_map[usage_hid] = field->value[n];
|
||||
if (field->value[n] ==
|
||||
(HID_HP_WAVEFORMPRESS & HID_USAGE)) {
|
||||
haptic->press_ordinal = usage_hid;
|
||||
} else if (field->value[n] ==
|
||||
(HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
|
||||
haptic->release_ordinal = usage_hid;
|
||||
}
|
||||
break;
|
||||
case HID_HP_DURATIONLIST:
|
||||
haptic->duration_map[usage_hid] =
|
||||
field->value[n];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case HID_UP_HAPTIC:
|
||||
switch (field->usage[n].hid) {
|
||||
case HID_HP_WAVEFORMVENDORID:
|
||||
haptic->vendor_id = field->value[n];
|
||||
break;
|
||||
case HID_HP_WAVEFORMVENDORPAGE:
|
||||
haptic->vendor_page = field->value[n];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Should not really happen */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_effect_buf(struct hid_haptic_device *haptic,
|
||||
struct ff_haptic_effect *effect,
|
||||
struct hid_haptic_effect *haptic_effect,
|
||||
int waveform_ordinal)
|
||||
{
|
||||
struct hid_report *rep = haptic->manual_trigger_report;
|
||||
struct hid_usage *usage;
|
||||
struct hid_field *field;
|
||||
s32 value;
|
||||
int i, j;
|
||||
u8 *buf = haptic_effect->report_buf;
|
||||
|
||||
mutex_lock(&haptic->manual_trigger_mutex);
|
||||
for (i = 0; i < rep->maxfield; i++) {
|
||||
field = rep->field[i];
|
||||
/* Ignore if report count is out of bounds. */
|
||||
if (field->report_count < 1)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
usage = &field->usage[j];
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_HP_INTENSITY:
|
||||
if (effect->intensity > 100) {
|
||||
value = field->logical_maximum;
|
||||
} else {
|
||||
value = field->logical_minimum +
|
||||
effect->intensity *
|
||||
(field->logical_maximum -
|
||||
field->logical_minimum) / 100;
|
||||
}
|
||||
break;
|
||||
case HID_HP_REPEATCOUNT:
|
||||
value = effect->repeat_count;
|
||||
break;
|
||||
case HID_HP_RETRIGGERPERIOD:
|
||||
value = effect->retrigger_period;
|
||||
break;
|
||||
case HID_HP_MANUALTRIGGER:
|
||||
value = waveform_ordinal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
field->value[j] = value;
|
||||
}
|
||||
}
|
||||
|
||||
hid_output_report(rep, buf);
|
||||
mutex_unlock(&haptic->manual_trigger_mutex);
|
||||
}
|
||||
|
||||
static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *effect,
|
||||
struct ff_effect *old)
|
||||
{
|
||||
struct ff_device *ff = dev->ff;
|
||||
struct hid_haptic_device *haptic = ff->private;
|
||||
int i, ordinal = 0;
|
||||
|
||||
/* If vendor range, check vendor id and page */
|
||||
if (effect->u.haptic.hid_usage >= (HID_HP_VENDORWAVEFORMMIN & HID_USAGE) &&
|
||||
effect->u.haptic.hid_usage <= (HID_HP_VENDORWAVEFORMMAX & HID_USAGE) &&
|
||||
(effect->u.haptic.vendor_id != haptic->vendor_id ||
|
||||
effect->u.haptic.vendor_waveform_page != haptic->vendor_page))
|
||||
return -EINVAL;
|
||||
|
||||
/* Check hid_usage */
|
||||
for (i = 1; i <= haptic->max_waveform_id; i++) {
|
||||
if (haptic->hid_usage_map[i] == effect->u.haptic.hid_usage) {
|
||||
ordinal = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ordinal < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Fill the buffer for the effect id */
|
||||
fill_effect_buf(haptic, &effect->u.haptic, &haptic->effect[effect->id],
|
||||
ordinal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int play_effect(struct hid_device *hdev, struct hid_haptic_device *haptic,
|
||||
struct hid_haptic_effect *effect)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hid_hw_output_report(hdev, effect->report_buf,
|
||||
haptic->manual_trigger_report_len);
|
||||
if (ret < 0) {
|
||||
ret = hid_hw_raw_request(hdev,
|
||||
haptic->manual_trigger_report->id,
|
||||
effect->report_buf,
|
||||
haptic->manual_trigger_report_len,
|
||||
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void haptic_work_handler(struct work_struct *work)
|
||||
{
|
||||
|
||||
struct hid_haptic_effect *effect = container_of(work,
|
||||
struct hid_haptic_effect,
|
||||
work);
|
||||
struct input_dev *dev = effect->input_dev;
|
||||
struct hid_device *hdev = input_get_drvdata(dev);
|
||||
struct hid_haptic_device *haptic = dev->ff->private;
|
||||
|
||||
mutex_lock(&haptic->manual_trigger_mutex);
|
||||
if (effect != &haptic->stop_effect)
|
||||
play_effect(hdev, haptic, &haptic->stop_effect);
|
||||
|
||||
play_effect(hdev, haptic, effect);
|
||||
mutex_unlock(&haptic->manual_trigger_mutex);
|
||||
|
||||
}
|
||||
|
||||
static int hid_haptic_playback(struct input_dev *dev, int effect_id, int value)
|
||||
{
|
||||
struct hid_haptic_device *haptic = dev->ff->private;
|
||||
|
||||
if (value)
|
||||
queue_work(haptic->wq, &haptic->effect[effect_id].work);
|
||||
else
|
||||
queue_work(haptic->wq, &haptic->stop_effect.work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void effect_set_default(struct ff_effect *effect)
|
||||
{
|
||||
effect->type = FF_HAPTIC;
|
||||
effect->id = -1;
|
||||
effect->u.haptic.hid_usage = HID_HP_WAVEFORMNONE & HID_USAGE;
|
||||
effect->u.haptic.intensity = 100;
|
||||
effect->u.haptic.retrigger_period = 0;
|
||||
effect->u.haptic.repeat_count = 0;
|
||||
}
|
||||
|
||||
static int hid_haptic_erase(struct input_dev *dev, int effect_id)
|
||||
{
|
||||
struct hid_haptic_device *haptic = dev->ff->private;
|
||||
struct ff_effect effect;
|
||||
int ordinal;
|
||||
|
||||
effect_set_default(&effect);
|
||||
|
||||
if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
|
||||
ordinal = haptic->release_ordinal;
|
||||
if (!ordinal)
|
||||
ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
|
||||
else
|
||||
effect.u.haptic.hid_usage = HID_HP_WAVEFORMRELEASE &
|
||||
HID_USAGE;
|
||||
fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
|
||||
ordinal);
|
||||
} else if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMPRESS & HID_USAGE)) {
|
||||
ordinal = haptic->press_ordinal;
|
||||
if (!ordinal)
|
||||
ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
|
||||
else
|
||||
effect.u.haptic.hid_usage = HID_HP_WAVEFORMPRESS &
|
||||
HID_USAGE;
|
||||
fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
|
||||
ordinal);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_haptic_destroy(struct ff_device *ff)
|
||||
{
|
||||
struct hid_haptic_device *haptic = ff->private;
|
||||
struct hid_device *hdev = haptic->hdev;
|
||||
int r;
|
||||
|
||||
if (hdev)
|
||||
put_device(&hdev->dev);
|
||||
|
||||
kfree(haptic->stop_effect.report_buf);
|
||||
haptic->stop_effect.report_buf = NULL;
|
||||
|
||||
if (haptic->effect) {
|
||||
for (r = 0; r < ff->max_effects; r++)
|
||||
kfree(haptic->effect[r].report_buf);
|
||||
kfree(haptic->effect);
|
||||
}
|
||||
haptic->effect = NULL;
|
||||
|
||||
destroy_workqueue(haptic->wq);
|
||||
haptic->wq = NULL;
|
||||
|
||||
kfree(haptic->duration_map);
|
||||
haptic->duration_map = NULL;
|
||||
|
||||
kfree(haptic->hid_usage_map);
|
||||
haptic->hid_usage_map = NULL;
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
int hid_haptic_init(struct hid_device *hdev,
|
||||
struct hid_haptic_device **haptic_ptr)
|
||||
{
|
||||
struct hid_haptic_device *haptic = *haptic_ptr;
|
||||
struct input_dev *dev = NULL;
|
||||
struct hid_input *hidinput;
|
||||
struct ff_device *ff;
|
||||
int ret = 0, r;
|
||||
struct ff_haptic_effect stop_effect = {
|
||||
.hid_usage = HID_HP_WAVEFORMSTOP & HID_USAGE,
|
||||
};
|
||||
const char *prefix = "hid-haptic";
|
||||
char *name;
|
||||
int (*flush)(struct input_dev *dev, struct file *file);
|
||||
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
||||
|
||||
haptic->hdev = hdev;
|
||||
haptic->max_waveform_id = max(2u, haptic->max_waveform_id);
|
||||
haptic->max_duration_id = max(2u, haptic->max_duration_id);
|
||||
|
||||
haptic->hid_usage_map = kcalloc(haptic->max_waveform_id + 1,
|
||||
sizeof(u16), GFP_KERNEL);
|
||||
if (!haptic->hid_usage_map) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
haptic->duration_map = kcalloc(haptic->max_duration_id + 1,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!haptic->duration_map) {
|
||||
ret = -ENOMEM;
|
||||
goto usage_map;
|
||||
}
|
||||
|
||||
if (haptic->max_waveform_id != haptic->max_duration_id)
|
||||
dev_warn(&hdev->dev,
|
||||
"Haptic duration and waveform lists have different max id (%u and %u).\n",
|
||||
haptic->max_duration_id, haptic->max_waveform_id);
|
||||
|
||||
haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMNONE] =
|
||||
HID_HP_WAVEFORMNONE & HID_USAGE;
|
||||
haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] =
|
||||
HID_HP_WAVEFORMSTOP & HID_USAGE;
|
||||
|
||||
for (r = 0; r < haptic->auto_trigger_report->maxfield; r++)
|
||||
parse_auto_trigger_field(haptic, haptic->auto_trigger_report->field[r]);
|
||||
|
||||
list_for_each_entry(hidinput, &hdev->inputs, list) {
|
||||
if (hidinput->application == HID_DG_TOUCHPAD) {
|
||||
dev = hidinput->input;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev) {
|
||||
dev_err(&hdev->dev, "Failed to find the input device\n");
|
||||
ret = -ENODEV;
|
||||
goto duration_map;
|
||||
}
|
||||
|
||||
haptic->input_dev = dev;
|
||||
haptic->manual_trigger_report_len =
|
||||
hid_report_len(haptic->manual_trigger_report);
|
||||
mutex_init(&haptic->manual_trigger_mutex);
|
||||
name = kmalloc(strlen(prefix) + strlen(hdev->name) + 2, GFP_KERNEL);
|
||||
if (name) {
|
||||
sprintf(name, "%s %s", prefix, hdev->name);
|
||||
haptic->wq = create_singlethread_workqueue(name);
|
||||
kfree(name);
|
||||
}
|
||||
if (!haptic->wq) {
|
||||
ret = -ENOMEM;
|
||||
goto duration_map;
|
||||
}
|
||||
haptic->effect = kcalloc(FF_MAX_EFFECTS,
|
||||
sizeof(struct hid_haptic_effect), GFP_KERNEL);
|
||||
if (!haptic->effect) {
|
||||
ret = -ENOMEM;
|
||||
goto output_queue;
|
||||
}
|
||||
for (r = 0; r < FF_MAX_EFFECTS; r++) {
|
||||
haptic->effect[r].report_buf =
|
||||
hid_alloc_report_buf(haptic->manual_trigger_report,
|
||||
GFP_KERNEL);
|
||||
if (!haptic->effect[r].report_buf) {
|
||||
dev_err(&hdev->dev,
|
||||
"Failed to allocate a buffer for an effect.\n");
|
||||
ret = -ENOMEM;
|
||||
goto buffer_free;
|
||||
}
|
||||
haptic->effect[r].input_dev = dev;
|
||||
INIT_WORK(&haptic->effect[r].work, haptic_work_handler);
|
||||
}
|
||||
haptic->stop_effect.report_buf =
|
||||
hid_alloc_report_buf(haptic->manual_trigger_report,
|
||||
GFP_KERNEL);
|
||||
if (!haptic->stop_effect.report_buf) {
|
||||
dev_err(&hdev->dev,
|
||||
"Failed to allocate a buffer for stop effect.\n");
|
||||
ret = -ENOMEM;
|
||||
goto buffer_free;
|
||||
}
|
||||
haptic->stop_effect.input_dev = dev;
|
||||
INIT_WORK(&haptic->stop_effect.work, haptic_work_handler);
|
||||
fill_effect_buf(haptic, &stop_effect, &haptic->stop_effect,
|
||||
HID_HAPTIC_ORDINAL_WAVEFORMSTOP);
|
||||
|
||||
input_set_capability(dev, EV_FF, FF_HAPTIC);
|
||||
|
||||
flush = dev->flush;
|
||||
event = dev->event;
|
||||
ret = input_ff_create(dev, FF_MAX_EFFECTS);
|
||||
if (ret) {
|
||||
dev_err(&hdev->dev, "Failed to create ff device.\n");
|
||||
goto stop_buffer_free;
|
||||
}
|
||||
|
||||
ff = dev->ff;
|
||||
ff->private = haptic;
|
||||
ff->upload = hid_haptic_upload_effect;
|
||||
ff->playback = hid_haptic_playback;
|
||||
ff->erase = hid_haptic_erase;
|
||||
ff->destroy = hid_haptic_destroy;
|
||||
if (!try_module_get(THIS_MODULE)) {
|
||||
dev_err(&hdev->dev, "Failed to increase module count.\n");
|
||||
goto input_free;
|
||||
}
|
||||
if (!get_device(&hdev->dev)) {
|
||||
dev_err(&hdev->dev, "Failed to get hdev device.\n");
|
||||
module_put(THIS_MODULE);
|
||||
goto input_free;
|
||||
}
|
||||
return 0;
|
||||
|
||||
input_free:
|
||||
input_ff_destroy(dev);
|
||||
/* Do not let double free happen, input_ff_destroy will call
|
||||
* hid_haptic_destroy.
|
||||
*/
|
||||
*haptic_ptr = NULL;
|
||||
/* Restore dev flush and event */
|
||||
dev->flush = flush;
|
||||
dev->event = event;
|
||||
return ret;
|
||||
stop_buffer_free:
|
||||
kfree(haptic->stop_effect.report_buf);
|
||||
haptic->stop_effect.report_buf = NULL;
|
||||
buffer_free:
|
||||
while (--r >= 0)
|
||||
kfree(haptic->effect[r].report_buf);
|
||||
kfree(haptic->effect);
|
||||
haptic->effect = NULL;
|
||||
output_queue:
|
||||
destroy_workqueue(haptic->wq);
|
||||
haptic->wq = NULL;
|
||||
duration_map:
|
||||
kfree(haptic->duration_map);
|
||||
haptic->duration_map = NULL;
|
||||
usage_map:
|
||||
kfree(haptic->hid_usage_map);
|
||||
haptic->hid_usage_map = NULL;
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_haptic_init);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ int hid_haptic_input_mapping(struct hid_device *hdev,
|
|||
int hid_haptic_input_configured(struct hid_device *hdev,
|
||||
struct hid_haptic_device *haptic,
|
||||
struct hid_input *hi);
|
||||
int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr);
|
||||
#else
|
||||
static inline
|
||||
void hid_haptic_feature_mapping(struct hid_device *hdev,
|
||||
|
|
@ -98,4 +99,8 @@ int hid_haptic_input_configured(struct hid_device *hdev,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user