* [PATCH] HID: hid-sensor-hub: Processing for duplicate physical ids
From: Srinivas Pandruvada @ 2014-01-30 16:40 UTC (permalink / raw)
To: jkosina; +Cc: jic23, linux-input, Srinivas Pandruvada, Archana Patni
In HID sensor hub, HID physical ids are used to represent different
sensors. For example physical id of 0x73 in usage page = 0x20, represents
an accelerometer. The HID sensor hub driver uses this physical ids to
create platform devices using MFD. There is 1:1 correspondence between
an phy id and a client driver.
But in some cases these physical ids are reused. There is a phy id 0xe1,
which specifies a custom sensor, which can exist multiple times to represent
various custom sensors. In this case there can be multiple instances
of client MFD drivers, processing specific custom sensor. In this
case when client driver looks for report id or a field index, it
should still get the report id specific to its own type. This is
also true for reports, they should be directed towards correct instance.
This change introduce a way to parse and tie physical devices to their
correct instance.
Summary of changes:
- To get physical ids, use collections. If a collection of type=physical
exist then use usage id as in the name of platform device name
- As part of the platform data, we assign a hdsev instance, which has
start and end of collection indexes. Using these indexes attributes
can be tied to correct MFD client instances
- When a report is received, call callback with correct hsdev instance.
In this way using its private data stored as part of its registry, it
can distinguish different sensors even when they have same physical and
logical ids.
This patch is co-authored with Archana Patni<archna.patni@intel.com>.
Reported-by: Archana Patni <archana.patni@intel.com>
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Archana Patni <archana.patni@intel.com>
---
drivers/hid/hid-sensor-hub.c | 182 ++++++++++++++++++++++-------------------
include/linux/hid-sensor-hub.h | 6 +-
2 files changed, 102 insertions(+), 86 deletions(-)
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index ad2b869..80be0c0 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -56,9 +56,9 @@ struct sensor_hub_pending {
* @dyn_callback_lock: spin lock to protect callback list
* @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance.
* @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached).
+ * @ref_cnt: Number of MFD clients have opened this device
*/
struct sensor_hub_data {
- struct hid_sensor_hub_device *hsdev;
struct mutex mutex;
spinlock_t lock;
struct sensor_hub_pending pending;
@@ -67,6 +67,7 @@ struct sensor_hub_data {
struct mfd_cell *hid_sensor_hub_client_devs;
int hid_sensor_client_cnt;
unsigned long quirks;
+ int ref_cnt;
};
/**
@@ -79,6 +80,7 @@ struct sensor_hub_data {
struct hid_sensor_hub_callbacks_list {
struct list_head list;
u32 usage_id;
+ struct hid_sensor_hub_device *hsdev;
struct hid_sensor_hub_callbacks *usage_callback;
void *priv;
};
@@ -97,20 +99,18 @@ static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
return NULL;
}
-static int sensor_hub_get_physical_device_count(
- struct hid_report_enum *report_enum)
+static int sensor_hub_get_physical_device_count(struct hid_device *hdev)
{
- struct hid_report *report;
- struct hid_field *field;
- int cnt = 0;
+ int i;
+ int count = 0;
- list_for_each_entry(report, &report_enum->report_list, list) {
- field = report->field[0];
- if (report->maxfield && field && field->physical)
- cnt++;
+ for (i = 0; i < hdev->maxcollection; ++i) {
+ struct hid_collection *collection = &hdev->collection[i];
+ if (collection->type == HID_COLLECTION_PHYSICAL)
+ ++count;
}
- return cnt;
+ return count;
}
static void sensor_hub_fill_attr_info(
@@ -128,15 +128,23 @@ static void sensor_hub_fill_attr_info(
static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
struct hid_device *hdev,
- u32 usage_id, void **priv)
+ u32 usage_id,
+ int collection_index,
+ struct hid_sensor_hub_device **hsdev,
+ void **priv)
{
struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
spin_lock(&pdata->dyn_callback_lock);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
- if (callback->usage_id == usage_id) {
+ if (callback->usage_id == usage_id &&
+ (collection_index >=
+ callback->hsdev->start_collection_index) &&
+ (collection_index <
+ callback->hsdev->end_collection_index)) {
*priv = callback->priv;
+ *hsdev = callback->hsdev;
spin_unlock(&pdata->dyn_callback_lock);
return callback->usage_callback;
}
@@ -163,6 +171,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
spin_unlock(&pdata->dyn_callback_lock);
return -ENOMEM;
}
+ callback->hsdev = hsdev;
callback->usage_callback = usage_callback;
callback->usage_id = usage_id;
callback->priv = NULL;
@@ -320,8 +329,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
struct hid_sensor_hub_attribute_info *info)
{
int ret = -1;
- int i, j;
- int collection_index = -1;
+ int i;
struct hid_report *report;
struct hid_field *field;
struct hid_report_enum *report_enum;
@@ -335,44 +343,31 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
info->units = -1;
info->unit_expo = -1;
- for (i = 0; i < hdev->maxcollection; ++i) {
- struct hid_collection *collection = &hdev->collection[i];
- if (usage_id == collection->usage) {
- collection_index = i;
- break;
- }
- }
- if (collection_index == -1)
- goto err_ret;
-
report_enum = &hdev->report_enum[type];
list_for_each_entry(report, &report_enum->report_list, list) {
for (i = 0; i < report->maxfield; ++i) {
field = report->field[i];
- if (field->physical == usage_id &&
- field->logical == attr_usage_id) {
- sensor_hub_fill_attr_info(info, i, report->id,
- field);
- ret = 0;
- } else {
- for (j = 0; j < field->maxusage; ++j) {
- if (field->usage[j].hid ==
- attr_usage_id &&
- field->usage[j].collection_index ==
- collection_index) {
- sensor_hub_fill_attr_info(info,
- i, report->id, field);
- ret = 0;
- break;
- }
+ if (field->maxusage) {
+ if (field->physical == usage_id &&
+ (field->logical == attr_usage_id ||
+ field->usage[0].hid ==
+ attr_usage_id) &&
+ (field->usage[0].collection_index >=
+ hsdev->start_collection_index) &&
+ (field->usage[0].collection_index <
+ hsdev->end_collection_index)) {
+
+ sensor_hub_fill_attr_info(info, i,
+ report->id,
+ field);
+ ret = 0;
+ break;
}
}
- if (ret == 0)
- break;
}
+
}
-err_ret:
return ret;
}
EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info);
@@ -388,7 +383,7 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->suspend)
callback->usage_callback->suspend(
- pdata->hsdev, callback->priv);
+ callback->hsdev, callback->priv);
}
spin_unlock(&pdata->dyn_callback_lock);
@@ -405,7 +400,7 @@ static int sensor_hub_resume(struct hid_device *hdev)
list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->resume)
callback->usage_callback->resume(
- pdata->hsdev, callback->priv);
+ callback->hsdev, callback->priv);
}
spin_unlock(&pdata->dyn_callback_lock);
@@ -432,6 +427,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
struct hid_sensor_hub_callbacks *callback = NULL;
struct hid_collection *collection = NULL;
void *priv = NULL;
+ struct hid_sensor_hub_device *hsdev = NULL;
hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
report->id, size, report->type);
@@ -466,23 +462,26 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
report->field[i]->usage->collection_index];
hid_dbg(hdev, "collection->usage %x\n",
collection->usage);
- callback = sensor_hub_get_callback(pdata->hsdev->hdev,
- report->field[i]->physical,
- &priv);
+
+ callback = sensor_hub_get_callback(hdev,
+ report->field[i]->physical,
+ report->field[i]->usage[0].collection_index,
+ &hsdev, &priv);
+
if (callback && callback->capture_sample) {
if (report->field[i]->logical)
- callback->capture_sample(pdata->hsdev,
+ callback->capture_sample(hsdev,
report->field[i]->logical, sz, ptr,
callback->pdev);
else
- callback->capture_sample(pdata->hsdev,
+ callback->capture_sample(hsdev,
report->field[i]->usage->hid, sz, ptr,
callback->pdev);
}
ptr += sz;
}
if (callback && collection && callback->send_event)
- callback->send_event(pdata->hsdev, collection->usage,
+ callback->send_event(hsdev, collection->usage,
callback->pdev);
spin_unlock_irqrestore(&pdata->lock, flags);
@@ -495,7 +494,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
mutex_lock(&data->mutex);
- if (!hsdev->ref_cnt) {
+ if (!data->ref_cnt) {
ret = hid_hw_open(hsdev->hdev);
if (ret) {
hid_err(hsdev->hdev, "failed to open hid device\n");
@@ -503,7 +502,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
return ret;
}
}
- hsdev->ref_cnt++;
+ data->ref_cnt++;
mutex_unlock(&data->mutex);
return ret;
@@ -515,8 +514,8 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev)
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
mutex_lock(&data->mutex);
- hsdev->ref_cnt--;
- if (!hsdev->ref_cnt)
+ data->ref_cnt--;
+ if (!data->ref_cnt)
hid_hw_close(hsdev->hdev);
mutex_unlock(&data->mutex);
}
@@ -563,26 +562,19 @@ static int sensor_hub_probe(struct hid_device *hdev,
struct sensor_hub_data *sd;
int i;
char *name;
- struct hid_report *report;
- struct hid_report_enum *report_enum;
- struct hid_field *field;
int dev_cnt;
+ struct hid_sensor_hub_device *hsdev;
+ struct hid_sensor_hub_device *last_hsdev = NULL;
sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
if (!sd) {
hid_err(hdev, "cannot allocate Sensor data\n");
return -ENOMEM;
}
- sd->hsdev = devm_kzalloc(&hdev->dev, sizeof(*sd->hsdev), GFP_KERNEL);
- if (!sd->hsdev) {
- hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
- return -ENOMEM;
- }
+
hid_set_drvdata(hdev, sd);
sd->quirks = id->driver_data;
- sd->hsdev->hdev = hdev;
- sd->hsdev->vendor_id = hdev->vendor;
- sd->hsdev->product_id = hdev->product;
+
spin_lock_init(&sd->lock);
spin_lock_init(&sd->dyn_callback_lock);
mutex_init(&sd->mutex);
@@ -600,9 +592,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
}
INIT_LIST_HEAD(&sd->dyn_callback_list);
sd->hid_sensor_client_cnt = 0;
- report_enum = &hdev->report_enum[HID_INPUT_REPORT];
- dev_cnt = sensor_hub_get_physical_device_count(report_enum);
+ dev_cnt = sensor_hub_get_physical_device_count(hdev);
if (dev_cnt > HID_MAX_PHY_DEVICES) {
hid_err(hdev, "Invalid Physical device count\n");
ret = -EINVAL;
@@ -616,42 +607,63 @@ static int sensor_hub_probe(struct hid_device *hdev,
ret = -ENOMEM;
goto err_stop_hw;
}
- list_for_each_entry(report, &report_enum->report_list, list) {
- hid_dbg(hdev, "Report id:%x\n", report->id);
- field = report->field[0];
- if (report->maxfield && field &&
- field->physical) {
+
+ for (i = 0; i < hdev->maxcollection; ++i) {
+ struct hid_collection *collection = &hdev->collection[i];
+
+ if (collection->type == HID_COLLECTION_PHYSICAL) {
+
+ hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
+ if (!hsdev) {
+ hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
+ ret = -ENOMEM;
+ goto err_no_mem;
+ }
+ hsdev->hdev = hdev;
+ hsdev->vendor_id = hdev->vendor;
+ hsdev->product_id = hdev->product;
+ hsdev->start_collection_index = i;
+ if (last_hsdev)
+ last_hsdev->end_collection_index = i;
+ last_hsdev = hsdev;
name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x",
- field->physical);
+ collection->usage);
if (name == NULL) {
hid_err(hdev, "Failed MFD device name\n");
ret = -ENOMEM;
- goto err_free_names;
+ goto err_no_mem;
}
sd->hid_sensor_hub_client_devs[
- sd->hid_sensor_client_cnt].id = PLATFORM_DEVID_AUTO;
+ sd->hid_sensor_client_cnt].id =
+ PLATFORM_DEVID_AUTO;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].name = name;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].platform_data =
- sd->hsdev;
+ hsdev;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].pdata_size =
- sizeof(*sd->hsdev);
- hid_dbg(hdev, "Adding %s:%p\n", name, sd);
+ sizeof(*hsdev);
+ hid_dbg(hdev, "Adding %s:%d\n", name,
+ hsdev->start_collection_index);
sd->hid_sensor_client_cnt++;
}
}
+ if (last_hsdev)
+ last_hsdev->end_collection_index = i;
+
ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,
sd->hid_sensor_client_cnt, NULL, 0, NULL);
if (ret < 0)
- goto err_free_names;
+ goto err_no_mem;
return ret;
-err_free_names:
- for (i = 0; i < sd->hid_sensor_client_cnt ; ++i)
+err_no_mem:
+ for (i = 0; i < sd->hid_sensor_client_cnt; ++i) {
kfree(sd->hid_sensor_hub_client_devs[i].name);
+ kfree(sd->hid_sensor_hub_client_devs[i].platform_data);
+ }
kfree(sd->hid_sensor_hub_client_devs);
err_stop_hw:
hid_hw_stop(hdev);
@@ -673,8 +685,10 @@ static void sensor_hub_remove(struct hid_device *hdev)
complete(&data->pending.ready);
spin_unlock_irqrestore(&data->lock, flags);
mfd_remove_devices(&hdev->dev);
- for (i = 0; i < data->hid_sensor_client_cnt ; ++i)
+ for (i = 0; i < data->hid_sensor_client_cnt; ++i) {
kfree(data->hid_sensor_hub_client_devs[i].name);
+ kfree(data->hid_sensor_hub_client_devs[i].platform_data);
+ }
kfree(data->hid_sensor_hub_client_devs);
hid_set_drvdata(hdev, NULL);
mutex_destroy(&data->mutex);
diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h
index 205eba0..b70cfd7 100644
--- a/include/linux/hid-sensor-hub.h
+++ b/include/linux/hid-sensor-hub.h
@@ -51,13 +51,15 @@ struct hid_sensor_hub_attribute_info {
* @hdev: Stores the hid instance.
* @vendor_id: Vendor id of hub device.
* @product_id: Product id of hub device.
- * @ref_cnt: Number of MFD clients have opened this device
+ * @start_collection_index: Starting index for a phy type collection
+ * @end_collection_index: Last index for a phy type collection
*/
struct hid_sensor_hub_device {
struct hid_device *hdev;
u32 vendor_id;
u32 product_id;
- int ref_cnt;
+ int start_collection_index;
+ int end_collection_index;
};
/**
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 0/6] HID: sony: Add full Dualshock 4 support in Bluetooth mode
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
This updated set of patches adds NULL pointer checking for the ll_driver
functions and makes the battery calculation code more robust.
The last patch from the first set that checked for duplicate controllers is
on-hold for now as without a guaranteed way to retrieve the MAC address
of a Bluetooth device from a module it has the potential to break.
^ permalink raw reply
* [PATCH v2 1/6] HID: sony: Use low-level transport driver functions
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Franz Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>
From: Franz Frank Praznik <frank.praznik@oh.rr.com>
Change the dualshock4_state_worker function to use the low-level output_report
function.
Remove sony_set_output_report since it is no longer used.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 58 ++++++++++++++++----------------------------------
1 file changed, 18 insertions(+), 40 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2bd3f13..7a6d7c9 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -502,7 +502,6 @@ struct sony_sc {
spinlock_t lock;
struct hid_device *hdev;
struct led_classdev *leds[MAX_LEDS];
- struct hid_report *output_report;
unsigned long quirks;
struct work_struct state_worker;
struct power_supply battery;
@@ -1053,21 +1052,26 @@ static void dualshock4_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
struct hid_device *hdev = sc->hdev;
- struct hid_report *report = sc->output_report;
- __s32 *value = report->field[0]->value;
+ int offset;
+
+ __u8 buf[32] = { 0 };
- value[0] = 0x03;
+ buf[0] = 0x05;
+ buf[1] = 0x03;
+ offset = 4;
#ifdef CONFIG_SONY_FF
- value[3] = sc->right;
- value[4] = sc->left;
+ buf[offset++] = sc->right;
+ buf[offset++] = sc->left;
+#else
+ offset += 2;
#endif
- value[5] = sc->led_state[0];
- value[6] = sc->led_state[1];
- value[7] = sc->led_state[2];
+ buf[offset++] = sc->led_state[0];
+ buf[offset++] = sc->led_state[1];
+ buf[offset++] = sc->led_state[2];
- hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+ hdev->ll_driver->output_report(hdev, buf, sizeof(buf));
}
#ifdef CONFIG_SONY_FF
@@ -1200,33 +1204,6 @@ static void sony_battery_remove(struct sony_sc *sc)
sc->battery.name = NULL;
}
-static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
-{
- struct list_head *head, *list;
- struct hid_report *report;
- struct hid_device *hdev = sc->hdev;
-
- list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
-
- list_for_each(head, list) {
- report = list_entry(head, struct hid_report, list);
-
- if (report->id == req_id) {
- if (report->size < req_size) {
- hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n",
- req_id, report->size, req_size);
- return -EINVAL;
- }
- sc->output_report = report;
- return 0;
- }
- }
-
- hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
-
- return -EINVAL;
-}
-
static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
int w, int h)
{
@@ -1291,10 +1268,11 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
ret = sixaxis_set_operational_bt(hdev);
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
- /* Report 5 (31 bytes) is used to send data to the controller via USB */
- ret = sony_set_output_report(sc, 0x05, 248);
- if (ret < 0)
+ if (hdev->ll_driver->output_report == NULL) {
+ hid_err(hdev, "NULL output_report handler\n");
+ ret = -EINVAL;
goto err_stop;
+ }
/* The Dualshock 4 touchpad supports 2 touches and has a
* resolution of 1920x940.
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 3/6] HID: sony: Add Bluetooth output report formatting
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>
Add formatting and reporting for Dualshock 4 output reports on Bluetooth.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 52162a9..4d12b4e 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1268,11 +1268,18 @@ static void dualshock4_state_worker(struct work_struct *work)
struct hid_device *hdev = sc->hdev;
int offset;
- __u8 buf[32] = { 0 };
+ __u8 buf[78] = { 0 };
- buf[0] = 0x05;
- buf[1] = 0x03;
- offset = 4;
+ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+ buf[0] = 0x05;
+ buf[1] = 0x03;
+ offset = 4;
+ } else {
+ buf[0] = 0x11;
+ buf[1] = 0xB0;
+ buf[3] = 0x0F;
+ offset = 6;
+ }
#ifdef CONFIG_SONY_FF
buf[offset++] = sc->right;
@@ -1285,7 +1292,11 @@ static void dualshock4_state_worker(struct work_struct *work)
buf[offset++] = sc->led_state[1];
buf[offset++] = sc->led_state[2];
- hdev->ll_driver->output_report(hdev, buf, sizeof(buf));
+ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
+ hdev->ll_driver->output_report(hdev, buf, 32);
+ else
+ hdev->ll_driver->raw_request(hdev, 0x11, buf, 78,
+ HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
#ifdef CONFIG_SONY_FF
@@ -1482,10 +1493,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
ret = sixaxis_set_operational_bt(hdev);
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
- if (hdev->ll_driver->output_report == NULL) {
+ if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) &&
+ hdev->ll_driver->output_report == NULL) {
hid_err(hdev, "NULL output_report handler\n");
ret = -EINVAL;
goto err_stop;
+ } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) &&
+ hdev->ll_driver->raw_request == NULL) {
+ hid_err(hdev, "NULL raw_request handler\n");
+ ret = -EINVAL;
+ goto err_stop;
}
/* The Dualshock 4 touchpad supports 2 touches and has a
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 2/6] HID: sony: Add modified Dualshock 4 Bluetooth HID descriptor
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>
By default, the Dualshock 4 sends controller data via report 1. Once an output
report is received the controller changes from sending data in report 1 to
sending data in report 17, which is unmapped in the default descriptor. The
mappings have to be moved to report 17 to let the HID driver properly process
the incoming reports.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 214 insertions(+)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 7a6d7c9..52162a9 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -336,6 +336,216 @@ static u8 dualshock4_usb_rdesc[] = {
0xC0 /* End Collection */
};
+/* The default behavior of the Dualshock 4 is to send reports using report
+ * type 1 when running over Bluetooth. However, as soon as it receives a
+ * report of type 17 to set the LEDs or rumble it starts returning it's state
+ * in report 17 instead of 1. Since report 17 is undefined in the default HID
+ * descriptor the button and axis definitions must be moved to report 17 or
+ * the HID layer won't process the received input once a report is sent.
+ */
+static u8 dualshock4_bt_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x05, /* Usage (Gamepad), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x01, /* Report ID (1), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x0A, /* Report Count (9), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x04, 0xFF, /* Usage Page (FF04h), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x09, 0x24, /* Usage (24h), */
+ 0x95, 0x24, /* Report Count (36), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xA3, /* Report ID (163), */
+ 0x09, 0x25, /* Usage (25h), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x05, /* Report ID (5), */
+ 0x09, 0x26, /* Usage (26h), */
+ 0x95, 0x28, /* Report Count (40), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x06, /* Report ID (6), */
+ 0x09, 0x27, /* Usage (27h), */
+ 0x95, 0x34, /* Report Count (52), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x07, /* Report ID (7), */
+ 0x09, 0x28, /* Usage (28h), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x08, /* Report ID (8), */
+ 0x09, 0x29, /* Usage (29h), */
+ 0x95, 0x2F, /* Report Count (47), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x06, 0x03, 0xFF, /* Usage Page (FF03h), */
+ 0x85, 0x03, /* Report ID (3), */
+ 0x09, 0x21, /* Usage (21h), */
+ 0x95, 0x26, /* Report Count (38), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x04, /* Report ID (4), */
+ 0x09, 0x22, /* Usage (22h), */
+ 0x95, 0x2E, /* Report Count (46), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xF0, /* Report ID (240), */
+ 0x09, 0x47, /* Usage (47h), */
+ 0x95, 0x3F, /* Report Count (63), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xF1, /* Report ID (241), */
+ 0x09, 0x48, /* Usage (48h), */
+ 0x95, 0x3F, /* Report Count (63), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xF2, /* Report ID (242), */
+ 0x09, 0x49, /* Usage (49h), */
+ 0x95, 0x0F, /* Report Count (15), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x11, /* Report ID (17), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x20, /* Usage (20h), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x09, 0x32, /* Usage (Z), */
+ 0x09, 0x35, /* Usage (Rz), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x39, /* Usage (Hat Switch), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x07, /* Logical Maximum (7), */
+ 0x75, 0x04, /* Report Size (4), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x42, /* Input (Variable, Null State), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x0E, /* Usage Maximum (0Eh), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x0E, /* Report Count (14), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x06, /* Report Size (6), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x33, /* Usage (Rx), */
+ 0x09, 0x34, /* Usage (Ry), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x20, /* Usage (20h), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x19, 0x40, /* Usage Minimum (40h), */
+ 0x29, 0x42, /* Usage Maximum (42h), */
+ 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
+ 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x19, 0x43, /* Usage Minimum (43h), */
+ 0x29, 0x45, /* Usage Maximum (45h), */
+ 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */
+ 0x26, 0x00, 0x40, /* Logical Maximum (16384), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x20, /* Usage (20h), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x31, /* Report Count (51), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x21, /* Usage (21h), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x4D, /* Report Count (77), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x12, /* Report ID (18), */
+ 0x09, 0x22, /* Usage (22h), */
+ 0x95, 0x8D, /* Report Count (141), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x23, /* Usage (23h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x13, /* Report ID (19), */
+ 0x09, 0x24, /* Usage (24h), */
+ 0x95, 0xCD, /* Report Count (205), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x25, /* Usage (25h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x14, /* Report ID (20), */
+ 0x09, 0x26, /* Usage (26h), */
+ 0x96, 0x0D, 0x01, /* Report Count (269), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x27, /* Usage (27h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x15, /* Report ID (21), */
+ 0x09, 0x28, /* Usage (28h), */
+ 0x96, 0x4D, 0x01, /* Report Count (333), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x29, /* Usage (29h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x16, /* Report ID (22), */
+ 0x09, 0x2A, /* Usage (2Ah), */
+ 0x96, 0x8D, 0x01, /* Report Count (397), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x2B, /* Usage (2Bh), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x17, /* Report ID (23), */
+ 0x09, 0x2C, /* Usage (2Ch), */
+ 0x96, 0xCD, 0x01, /* Report Count (461), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x2D, /* Usage (2Dh), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x18, /* Report ID (24), */
+ 0x09, 0x2E, /* Usage (2Eh), */
+ 0x96, 0x0D, 0x02, /* Report Count (525), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x2F, /* Usage (2Fh), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x19, /* Report ID (25), */
+ 0x09, 0x30, /* Usage (30h), */
+ 0x96, 0x22, 0x02, /* Report Count (546), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (31h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x06, 0x80, 0xFF, /* Usage Page (FF80h), */
+ 0x85, 0x82, /* Report ID (130), */
+ 0x09, 0x22, /* Usage (22h), */
+ 0x95, 0x3F, /* Report Count (63), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x83, /* Report ID (131), */
+ 0x09, 0x23, /* Usage (23h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x84, /* Report ID (132), */
+ 0x09, 0x24, /* Usage (24h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x90, /* Report ID (144), */
+ 0x09, 0x30, /* Usage (30h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x91, /* Report ID (145), */
+ 0x09, 0x31, /* Usage (31h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x92, /* Report ID (146), */
+ 0x09, 0x32, /* Usage (32h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x93, /* Report ID (147), */
+ 0x09, 0x33, /* Usage (33h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xA0, /* Report ID (160), */
+ 0x09, 0x40, /* Usage (40h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xA4, /* Report ID (164), */
+ 0x09, 0x44, /* Usage (44h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0 /* End Collection */
+};
+
static __u8 ps3remote_rdesc[] = {
0x05, 0x01, /* GUsagePage Generic Desktop */
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
@@ -591,6 +801,10 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
rdesc = dualshock4_usb_rdesc;
*rsize = sizeof(dualshock4_usb_rdesc);
+ } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) {
+ hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
+ rdesc = dualshock4_bt_rdesc;
+ *rsize = sizeof(dualshock4_bt_rdesc);
}
/* The HID descriptor exposed over BT has a trailing zero byte */
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 4/6] HID: sony: Add Dualshock 4 Bluetooth battery and touchpad parsing
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>
Add offsets for retrieving the battery and touchpad data in Dualshock 4
Bluetooth reports.
Adjust battery capacity calculations when in wireless mode.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 688da52..9aa20ff 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -861,25 +861,34 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
unsigned long flags;
- int n, offset = 35;
+ int n, offset;
__u8 cable_state, battery_capacity, battery_charging;
+ /* Battery and touchpad data starts at byte 30 in the USB report and
+ * 32 in Bluetooth report.
+ */
+ offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32;
+
/* The lower 4 bits of byte 30 contain the battery level
* and the 5th bit contains the USB cable state.
*/
- cable_state = (rd[30] >> 4) & 0x01;
- battery_capacity = rd[30] & 0x0F;
+ cable_state = (rd[offset] >> 4) & 0x01;
+ battery_capacity = rd[offset] & 0x0F;
- /* On USB the Dualshock 4 battery level goes from 0 to 11.
- * A battery level of 11 means fully charged.
+ /* When a USB power source is connected the battery level ranges from
+ * 0 to 10, and when running on battery power it ranges from 0 to 9.
+ * A battery level above 10 when plugged in means charge completed.
*/
- if (cable_state && battery_capacity == 11)
+ if (!cable_state || battery_capacity > 10)
battery_charging = 0;
else
battery_charging = 1;
+ if (!cable_state)
+ battery_capacity++;
if (battery_capacity > 10)
- battery_capacity--;
+ battery_capacity = 10;
+
battery_capacity *= 10;
spin_lock_irqsave(&sc->lock, flags);
@@ -888,7 +897,10 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
sc->battery_charging = battery_charging;
spin_unlock_irqrestore(&sc->lock, flags);
- /* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB.
+ offset += 5;
+
+ /* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB
+ * and 37 on Bluetooth.
* The first 7 bits of the first byte is a counter and bit 8 is a touch
* indicator that is 0 when pressed and 1 when not pressed.
* The next 3 bytes are two 12 bit touch coordinates, X and Y.
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 6/6] HID: sony: Add conditionals to enable all features in Bluetooth mode
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>
Add the necessary conditionals to enable battery reporting, rumble, LED
settings and touchpad parsing.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index eee56d7..d920362 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -44,8 +44,10 @@
#define DUALSHOCK4_CONTROLLER_USB BIT(5)
#define DUALSHOCK4_CONTROLLER_BT BIT(6)
-#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
-#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_USB)
+#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | \
+ DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_BT)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | \
+ DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_BT)
#define MAX_LEDS 4
@@ -939,8 +941,9 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
swap(rd[47], rd[48]);
sixaxis_parse_report(sc, rd, size);
- } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
- size == 64) {
+ } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
+ size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
+ && rd[0] == 0x11 && size == 78)) {
dualshock4_parse_report(sc, rd, size);
}
@@ -1079,7 +1082,8 @@ static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
buzz_set_leds(hdev, leds);
} else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) ||
- (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB)) {
+ (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+ (drv_data->quirks & DUALSHOCK4_CONTROLLER_BT)) {
for (n = 0; n < count; n++)
drv_data->led_state[n] = leds[n];
schedule_work(&drv_data->state_worker);
@@ -1184,7 +1188,8 @@ static int sony_leds_init(struct hid_device *hdev)
/* Validate expected report characteristics. */
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
- } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) {
+ } else if ((drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+ (drv_data->quirks & DUALSHOCK4_CONTROLLER_BT)) {
drv_data->led_count = 3;
max_brightness = 255;
use_colors = 1;
@@ -1509,7 +1514,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
ret = sixaxis_set_operational_bt(hdev);
- else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+ else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+ (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) &&
hdev->ll_driver->output_report == NULL) {
hid_err(hdev, "NULL output_report handler\n");
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 5/6] HID: sony: Set initial battery level to 100% to avoid false low battery warnings
From: Frank Praznik @ 2014-01-30 17:24 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1391102648-19381-1-git-send-email-frank.praznik@oh.rr.com>
Set initial battery level to 100% to avoid false low battery warnings if a the
battery level is polled before a report containing the battery information is
received.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 9aa20ff..5564446 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1404,6 +1404,11 @@ static int sony_battery_probe(struct sony_sc *sc)
struct hid_device *hdev = sc->hdev;
int ret;
+ /* Set the default battery level to 100% to avoid low battery warnings
+ * if the battery is polled before the first device report is received.
+ */
+ sc->battery_capacity = 100;
+
power_id = (unsigned long)atomic_inc_return(&power_id_seq);
sc->battery.properties = sony_battery_props;
--
1.8.5.3
^ permalink raw reply related
* [PATCH 1/4] Input: wacom: Use full 32-bit HID Usage value in switch statement
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
Cc: Jason Gerecke
A HID Usage is a 32-bit value: an upper 16-bit "page" and a lower
16-bit ID. While the two halves are normally reported seperately,
only the combination uniquely idenfifes a particular HID Usage.
The existing code performs the comparison in two steps, first
performing a switch on the ID and then verifying the page within
each case. While this works fine, it is very akward to handle
two Usages that share a single ID, such as HID_USAGE_PRESSURE
and HID_USAGE_X because the case statement can only have a
single identifier.
To work around this, we now check the full 32-bit HID Usage
directly rather than first checking the ID and then the page.
This allows the switch statement to have distinct cases for
e.g. HID_USAGE_PRESSURE and HID_USAGE_X.
Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
drivers/input/tablet/wacom_sys.c | 237 ++++++++++++++++++---------------------
1 file changed, 109 insertions(+), 128 deletions(-)
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index b16ebef..5be6177 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -22,23 +22,17 @@
#define HID_USAGE_PAGE_DIGITIZER 0x0d
#define HID_USAGE_PAGE_DESKTOP 0x01
#define HID_USAGE 0x09
-#define HID_USAGE_X 0x30
-#define HID_USAGE_Y 0x31
-#define HID_USAGE_X_TILT 0x3d
-#define HID_USAGE_Y_TILT 0x3e
-#define HID_USAGE_FINGER 0x22
-#define HID_USAGE_STYLUS 0x20
-#define HID_USAGE_CONTACTMAX 0x55
+#define HID_USAGE_X ((HID_USAGE_PAGE_DESKTOP << 16) | 0x30)
+#define HID_USAGE_Y ((HID_USAGE_PAGE_DESKTOP << 16) | 0x31)
+#define HID_USAGE_X_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d)
+#define HID_USAGE_Y_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e)
+#define HID_USAGE_FINGER ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22)
+#define HID_USAGE_STYLUS ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x20)
+#define HID_USAGE_CONTACTMAX ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x55)
#define HID_COLLECTION 0xa1
#define HID_COLLECTION_LOGICAL 0x02
#define HID_COLLECTION_END 0xc0
-enum {
- WCM_UNDEFINED = 0,
- WCM_DESKTOP,
- WCM_DIGITIZER,
-};
-
struct hid_descriptor {
struct usb_descriptor_header header;
__le16 bcdHID;
@@ -305,7 +299,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
char limit = 0;
/* result has to be defined as int for some devices */
int result = 0, touch_max = 0;
- int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;
+ int i = 0, page = 0, finger = 0, pen = 0;
unsigned char *report;
report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
@@ -332,134 +326,121 @@ static int wacom_parse_hid(struct usb_interface *intf,
switch (report[i]) {
case HID_USAGE_PAGE:
- switch (report[i + 1]) {
- case HID_USAGE_PAGE_DIGITIZER:
- usage = WCM_DIGITIZER;
- i++;
- break;
-
- case HID_USAGE_PAGE_DESKTOP:
- usage = WCM_DESKTOP;
- i++;
- break;
- }
+ page = report[i + 1];
+ i++;
break;
case HID_USAGE:
- switch (report[i + 1]) {
+ switch (page << 16 | report[i + 1]) {
case HID_USAGE_X:
- if (usage == WCM_DESKTOP) {
- if (finger) {
- features->device_type = BTN_TOOL_FINGER;
- /* touch device at least supports one touch point */
- touch_max = 1;
- switch (features->type) {
- case TABLETPC2FG:
- features->pktlen = WACOM_PKGLEN_TPC2FG;
- break;
-
- case MTSCREEN:
- case WACOM_24HDT:
- features->pktlen = WACOM_PKGLEN_MTOUCH;
- break;
-
- case MTTPC:
- features->pktlen = WACOM_PKGLEN_MTTPC;
- break;
-
- case BAMBOO_PT:
- features->pktlen = WACOM_PKGLEN_BBTOUCH;
- break;
-
- default:
- features->pktlen = WACOM_PKGLEN_GRAPHIRE;
- break;
- }
-
- switch (features->type) {
- case BAMBOO_PT:
- features->x_phy =
- get_unaligned_le16(&report[i + 5]);
- features->x_max =
- get_unaligned_le16(&report[i + 8]);
- i += 15;
- break;
-
- case WACOM_24HDT:
- features->x_max =
- get_unaligned_le16(&report[i + 3]);
- features->x_phy =
- get_unaligned_le16(&report[i + 8]);
- features->unit = report[i - 1];
- features->unitExpo = report[i - 3];
- i += 12;
- break;
-
- default:
- features->x_max =
- get_unaligned_le16(&report[i + 3]);
- features->x_phy =
- get_unaligned_le16(&report[i + 6]);
- features->unit = report[i + 9];
- features->unitExpo = report[i + 11];
- i += 12;
- break;
- }
- } else if (pen) {
- /* penabled only accepts exact bytes of data */
- if (features->type >= TABLETPC)
- features->pktlen = WACOM_PKGLEN_GRAPHIRE;
- features->device_type = BTN_TOOL_PEN;
+ if (finger) {
+ features->device_type = BTN_TOOL_FINGER;
+ /* touch device at least supports one touch point */
+ touch_max = 1;
+ switch (features->type) {
+ case TABLETPC2FG:
+ features->pktlen = WACOM_PKGLEN_TPC2FG;
+ break;
+
+ case MTSCREEN:
+ case WACOM_24HDT:
+ features->pktlen = WACOM_PKGLEN_MTOUCH;
+ break;
+
+ case MTTPC:
+ features->pktlen = WACOM_PKGLEN_MTTPC;
+ break;
+
+ case BAMBOO_PT:
+ features->pktlen = WACOM_PKGLEN_BBTOUCH;
+ break;
+
+ default:
+ features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+ break;
+ }
+
+ switch (features->type) {
+ case BAMBOO_PT:
+ features->x_phy =
+ get_unaligned_le16(&report[i + 5]);
+ features->x_max =
+ get_unaligned_le16(&report[i + 8]);
+ i += 15;
+ break;
+
+ case WACOM_24HDT:
features->x_max =
get_unaligned_le16(&report[i + 3]);
- i += 4;
+ features->x_phy =
+ get_unaligned_le16(&report[i + 8]);
+ features->unit = report[i - 1];
+ features->unitExpo = report[i - 3];
+ i += 12;
+ break;
+
+ default:
+ features->x_max =
+ get_unaligned_le16(&report[i + 3]);
+ features->x_phy =
+ get_unaligned_le16(&report[i + 6]);
+ features->unit = report[i + 9];
+ features->unitExpo = report[i + 11];
+ i += 12;
+ break;
}
+ } else if (pen) {
+ /* penabled only accepts exact bytes of data */
+ if (features->type >= TABLETPC)
+ features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+ features->device_type = BTN_TOOL_PEN;
+ features->x_max =
+ get_unaligned_le16(&report[i + 3]);
+ i += 4;
}
break;
case HID_USAGE_Y:
- if (usage == WCM_DESKTOP) {
- if (finger) {
- switch (features->type) {
- case TABLETPC2FG:
- case MTSCREEN:
- case MTTPC:
- features->y_max =
- get_unaligned_le16(&report[i + 3]);
- features->y_phy =
- get_unaligned_le16(&report[i + 6]);
- i += 7;
- break;
-
- case WACOM_24HDT:
- features->y_max =
- get_unaligned_le16(&report[i + 3]);
- features->y_phy =
- get_unaligned_le16(&report[i - 2]);
- i += 7;
- break;
-
- case BAMBOO_PT:
- features->y_phy =
- get_unaligned_le16(&report[i + 3]);
- features->y_max =
- get_unaligned_le16(&report[i + 6]);
- i += 12;
- break;
-
- default:
- features->y_max =
- features->x_max;
- features->y_phy =
- get_unaligned_le16(&report[i + 3]);
- i += 4;
- break;
- }
- } else if (pen) {
+ if (finger) {
+ switch (features->type) {
+ case TABLETPC2FG:
+ case MTSCREEN:
+ case MTTPC:
+ features->y_max =
+ get_unaligned_le16(&report[i + 3]);
+ features->y_phy =
+ get_unaligned_le16(&report[i + 6]);
+ i += 7;
+ break;
+
+ case WACOM_24HDT:
+ features->y_max =
+ get_unaligned_le16(&report[i + 3]);
+ features->y_phy =
+ get_unaligned_le16(&report[i - 2]);
+ i += 7;
+ break;
+
+ case BAMBOO_PT:
+ features->y_phy =
+ get_unaligned_le16(&report[i + 3]);
+ features->y_max =
+ get_unaligned_le16(&report[i + 6]);
+ i += 12;
+ break;
+
+ default:
features->y_max =
+ features->x_max;
+ features->y_phy =
get_unaligned_le16(&report[i + 3]);
i += 4;
+ break;
}
+ } else if (pen) {
+ features->y_max =
+ get_unaligned_le16(&report[i + 3]);
+ i += 4;
}
break;
@@ -489,7 +470,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
case HID_COLLECTION_END:
/* reset UsagePage and Finger */
- finger = usage = 0;
+ finger = page = 0;
break;
case HID_COLLECTION:
--
1.8.5.3
^ permalink raw reply related
* [PATCH 2/4] Input: wacom: Override 'pressure_max' with value from HID_USAGE_PRESSURE
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
Cc: Jason Gerecke
In-Reply-To: <1391107728-1306-1-git-send-email-killertofu@gmail.com>
The 0xEC sensor is used in multiple tablet PCs and curiously has
versions that report 256 levels of pressure (Samsung Slate 7)
as well as versions that report 1024 levels (Lenovo Thinkpad Yoga).
To allow both versions to work properly, we allow the value of
HID_USAGE_PRESSURE reported to override pressure_max.
Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
drivers/input/tablet/wacom_sys.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 5be6177..611fc39 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -24,6 +24,7 @@
#define HID_USAGE 0x09
#define HID_USAGE_X ((HID_USAGE_PAGE_DESKTOP << 16) | 0x30)
#define HID_USAGE_Y ((HID_USAGE_PAGE_DESKTOP << 16) | 0x31)
+#define HID_USAGE_PRESSURE ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x30)
#define HID_USAGE_X_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d)
#define HID_USAGE_Y_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e)
#define HID_USAGE_FINGER ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22)
@@ -465,6 +466,14 @@ static int wacom_parse_hid(struct usb_interface *intf,
wacom_retrieve_report_data(intf, features);
i++;
break;
+
+ case HID_USAGE_PRESSURE:
+ if (pen) {
+ features->pressure_max =
+ get_unaligned_le16(&report[i + 3]);
+ i += 4;
+ }
+ break;
}
break;
--
1.8.5.3
^ permalink raw reply related
* [PATCH 3/4] Input: wacom: References to 'wacom->data' should use 'unsigned char*'
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
Cc: Jason Gerecke
In-Reply-To: <1391107728-1306-1-git-send-email-killertofu@gmail.com>
'wacom->data' contains raw binary data and can lead to unexpected
behavior if a byte under examination happens to have its MSB set.
Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
drivers/input/tablet/wacom_wac.c | 27 +++++++++------------------
1 file changed, 9 insertions(+), 18 deletions(-)
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 05f371d..9ff5613 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -178,10 +178,9 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
static int wacom_dtu_irq(struct wacom_wac *wacom)
{
- struct wacom_features *features = &wacom->features;
- char *data = wacom->data;
+ unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
- int prox = data[1] & 0x20, pressure;
+ int prox = data[1] & 0x20;
dev_dbg(input->dev.parent,
"%s: received report #%d", __func__, data[0]);
@@ -198,10 +197,7 @@ static int wacom_dtu_irq(struct wacom_wac *wacom)
input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
- pressure = ((data[7] & 0x01) << 8) | data[6];
- if (pressure < 0)
- pressure = features->pressure_max + pressure + 1;
- input_report_abs(input, ABS_PRESSURE, pressure);
+ input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
input_report_key(input, BTN_TOUCH, data[1] & 0x05);
if (!prox) /* out-prox */
wacom->id[0] = 0;
@@ -906,7 +902,7 @@ static int int_dist(int x1, int y1, int x2, int y2)
static int wacom_24hdt_irq(struct wacom_wac *wacom)
{
struct input_dev *input = wacom->input;
- char *data = wacom->data;
+ unsigned char *data = wacom->data;
int i;
int current_num_contacts = data[61];
int contacts_to_send = 0;
@@ -959,7 +955,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
static int wacom_mt_touch(struct wacom_wac *wacom)
{
struct input_dev *input = wacom->input;
- char *data = wacom->data;
+ unsigned char *data = wacom->data;
int i;
int current_num_contacts = data[2];
int contacts_to_send = 0;
@@ -1038,7 +1034,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
{
- char *data = wacom->data;
+ unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
bool prox;
int x = 0, y = 0;
@@ -1074,10 +1070,8 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
static int wacom_tpc_pen(struct wacom_wac *wacom)
{
- struct wacom_features *features = &wacom->features;
- char *data = wacom->data;
+ unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
- int pressure;
bool prox = data[1] & 0x20;
if (!wacom->shared->stylus_in_proximity) /* first in prox */
@@ -1093,10 +1087,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
- pressure = ((data[7] & 0x01) << 8) | data[6];
- if (pressure < 0)
- pressure = features->pressure_max + pressure + 1;
- input_report_abs(input, ABS_PRESSURE, pressure);
+ input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
input_report_key(input, BTN_TOUCH, data[1] & 0x05);
input_report_key(input, wacom->tool[0], prox);
return 1;
@@ -1107,7 +1098,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
{
- char *data = wacom->data;
+ unsigned char *data = wacom->data;
dev_dbg(wacom->input->dev.parent,
"%s: received report #%d\n", __func__, data[0]);
--
1.8.5.3
^ permalink raw reply related
* [PATCH 4/4] Input: wacom: Handle 1024 pressure levels in wacom_tpc_pen
From: Jason Gerecke @ 2014-01-30 18:48 UTC (permalink / raw)
To: linux-input, linuxwacom-devel, pinglinux, skomra, dmitry.torokhov
Cc: Jason Gerecke
In-Reply-To: <1391107728-1306-1-git-send-email-killertofu@gmail.com>
Some tablet PC sensors (e.g. the 0xEC found in the Thinkpad
Yoga) report more than 256 pressure levels and will experience
wraparound unless the full range is read.
Signed-off-by: Jason Gerecke <killertofu@gmail.com>
---
drivers/input/tablet/wacom_wac.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 9ff5613..ae1437b 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1087,7 +1087,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom)
input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
- input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
+ input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x03) << 8) | data[6]);
input_report_key(input, BTN_TOUCH, data[1] & 0x05);
input_report_key(input, wacom->tool[0], prox);
return 1;
--
1.8.5.3
^ permalink raw reply related
* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Benjamin Tissoires @ 2014-01-30 21:44 UTC (permalink / raw)
To: Stephan Mueller
Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <2981174.bDZe6OjLCj@myon.chronox.de>
Hi Stephan,
thanks for the traces.
Well, the device definitively presents an Output and an Input report
on the report ID 2. That means that the windows driver can send and
read configuration to the trackpoint through this report.
I think our best chance here is to capture the initialization protocol
from a Windows virtual machine, and then mimic the behavior under
Linux.
Cheers,
Benjamin
On Wed, Jan 29, 2014 at 10:48 PM, Stephan Mueller <smueller@chronox.de> wrote:
> Am Mittwoch, 29. Januar 2014, 10:07:40 schrieb Benjamin Tissoires:
>
> Hi Benjamin,
>
>> On Wed, Jan 29, 2014 at 9:59 AM, Stephan Mueller <smueller@chronox.de>
> wrote:
>> > Am Mittwoch, 29. Januar 2014, 09:53:03 schrieb Benjamin Tissoires:
>> >
>> > Hi Benjamin,
>> >
>> >>On Fri, Jan 24, 2014 at 11:51 PM, Stephan Mueller <smueller@chronox.de>
>> >>
>> > wrote:
>> >>> Am Samstag, 25. Januar 2014, 12:17:13 schrieb Mattia Dongili:
>> >>>
>> >>> Hi Mattia,
>> >>>
>> >>>> I'd try with the input subsystem and the synaptics_usb driver first
>> >>>> but it's just a wild guess. Your kernel log should give you more
>> >>>> hints about which driver is bound to the device and the sysfs tree
>> >>>> under
>> >>>> /sys/class/input/event*/device/* has all the capabilities and
>> >>>> identifiers.
>> >>>
>> >>> The following did not help:
>> >>>
>> >>> modprobe synaptics_usb
>> >>> cd /sys/bus/usb/drivers/usbhid
>> >>> echo -n "1-1.3:1.0" > unbind
>> >>> #now the mouse is without driver, does not move, and
>> >>> #/sys/class/input/event2/device/device is without driver
>> >>> cd /sys/bus/usb/drivers/synaptics_usb
>> >>> echo -n "1-1.3:1.0" > bind
>> >>> #error: no such device, mouse does not work, nothing in dmesg
>> >>> cd /sys/bus/usb/drivers/usbhid
>> >>> echo -n "1-1.3:1.0" > bind
>> >>> #mouse works again without middle button
>> >>
>> >>Hi Stephan,
>> >>
>> >>in this case, you definitively want to talk to HID (and input) folks.
>> >>Adding Jiri, the HID maintainer in the discussion.
>> >>
>> >>Your mouse does not seem to be handled properly by the hid subsystem
>> >>and needs quirks, or fix.
>> >>
>> >>Can you send us some hid-recorder[1] traces of your device? We should
>> >>then be able to check what's wrong and hopefully fix the problem.
>> >>
>> > Thanks a lot for the helping hand. I will try your suggestion tonight
>> > and report back.
>> >
>> > But please allow me to point out that I have doubts that HID or input is
>> > at fault, because when sniffing on the USB bus with usbmon, I do *not*
>> > see any information transported when pressing the middle button.
>> > Therefore, I would suspect it is rather the base USB driver that somehow
>> > needs a quirk to access the mouse properly.
>>
>> Oh, then in this case it may be that your device needs to be put in a
>> special mode, and the report descriptors will show us some hints on
>> how to do it (maybe).
>> I strongly doubt that USB is in fault here. I can not see any reasons
>> why the USB or underlying driver would select which packets are
>> transmitted.
>>
>> What you can also do is setup a windows virtual machine, assign the
>> usb device to it, and sniff through usbmon or wireshark what packets
>> are emitted from/to the mouse. Then, we will duplicate this behavior
>> in the hid driver, and you would be good to go. Still, having the
>> reports descriptors (which are provided by hid-recorder, or in
>> /sys/kernel/debug/hid/DEVICE/rdesc, or in lsusb -vv when the usbhid
>> driver is not bound) would help us to understand the mouse firmware.
>>
>> Cheers,
>> Benjamin
>
> The device is 26E1:C1A0; when doing a cat
> /sys/kernel/debug/hid/0003\:26E1\:C1A0.0003/events, I see the mouse movements
> and the left and right mouse button, but not the middle one.
>
> # cat /sys/kernel/debug/hid/0003\:26E1\:C1A0.0003/rdesc
> 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03
> 81 02 95 05 81 01 05 01 15 81 25 7f 75 08 95 03 09 30 09 31 09 38 81 06 c0 c0
> 06 a0 ff 09 01 a1 01 85 02 09 02 a1 00 06 a1 ff 09 01 15 80 25 7f 35 00 45 ff
> 75 08 95 04 81 02 09 11 15 80 25 7f 35 00 45 ff 75 08 95 04 91 02 c0 c0
>
> INPUT(1)[INPUT]
> Field(0)
> Physical(GenericDesktop.Pointer)
> Application(GenericDesktop.Mouse)
> Usage(3)
> Button.0001
> Button.0002
> Button.0003
> Logical Minimum(0)
> Logical Maximum(1)
> Report Size(1)
> Report Count(3)
> Report Offset(0)
> Flags( Variable Absolute )
> Field(1)
> Physical(GenericDesktop.Pointer)
> Application(GenericDesktop.Mouse)
> Usage(3)
> GenericDesktop.X
> GenericDesktop.Y
> GenericDesktop.Wheel
> Logical Minimum(-127)
> Logical Maximum(127)
> Report Size(8)
> Report Count(3)
> Report Offset(8)
> Flags( Variable Relative )
> INPUT(2)[INPUT]
> Field(0)
> Physical(ffa0.0002)
> Application(ffa0.0001)
> Usage(4)
> ffa1.0001
> ffa1.0001
> ffa1.0001
> ffa1.0001
> Logical Minimum(-128)
> Logical Maximum(127)
> Physical Minimum(0)
> Physical Maximum(255)
> Report Size(8)
> Report Count(4)
> Report Offset(0)
> Flags( Variable Absolute )
> OUTPUT(2)[OUTPUT]
> Field(0)
> Physical(ffa0.0002)
> Application(ffa0.0001)
> Usage(4)
> ffa1.0011
> ffa1.0011
> ffa1.0011
> ffa1.0011
> Logical Minimum(-128)
> Logical Maximum(127)
> Physical Minimum(0)
> Physical Maximum(255)
> Report Size(8)
> Report Count(4)
> Report Offset(0)
> Flags( Variable Absolute )
>
> Button.0001 ---> Key.LeftBtn
> Button.0002 ---> Key.RightBtn
> Button.0003 ---> Key.MiddleBtn
> GenericDesktop.X ---> Relative.X
> GenericDesktop.Y ---> Relative.Y
> GenericDesktop.Wheel ---> Relative.Wheel
> ffa1.0001 ---> Absolute.Misc
> ffa1.0001 ---> Absolute.?
> ffa1.0001 ---> Absolute.?
> ffa1.0001 ---> Absolute.?
> ffa1.0011 ---> Sync.Report
> ffa1.0011 ---> Sync.Report
> ffa1.0011 ---> Sync.Report
> ffa1.0011 ---> Sync.Report
>
> Here is the hid-recorder output -- the mouse clicks are at the bottom. The
> middle button did not generate anything:
>
> hid-recorder /dev/hidraw0
> R: 102 05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01
> 95 03 81 02 95 05 81 01 05 01 15 81 25 7f 75 08 95 03 09 30 09 31 09 38 81 06
> c0 c0 06 a0 ff 09 01 a1 01 85 02 09 02 a1 00 06 a1 ff 09 01 15 80 25 7f 35 00
> 45 ff 75 08 95 04 81 02 09 11 15 80 25 7f 35 00 45 ff 75 08 95 04 91 02 c0 c0
> N: Crucialtek co.,LTD Optical Track Pad
> P: usb-0000:00:1a.0-1.3/input0
> I: 3 26e1 c1a0
> E: 0.000000 5 01 00 00 01 00
> E: 0.007998 5 01 00 00 01 00
> E: 0.031999 5 01 00 01 00 00
> E: 0.160003 5 01 00 01 fe 00
> E: 0.167999 5 01 00 01 ff 00
> E: 0.175997 5 01 00 01 fe 00
> E: 0.191999 5 01 00 01 fe 00
> E: 0.199948 5 01 00 01 fd 00
> E: 0.215935 5 01 00 01 fe 00
> E: 0.223858 5 01 00 01 fe 00
> E: 0.231940 5 01 00 00 ff 00
> E: 0.247945 5 01 00 00 ff 00
> E: 0.295953 5 01 00 00 ff 00
> E: 0.319953 5 01 00 01 fe 00
> E: 0.335936 5 01 00 02 fe 00
> E: 0.343944 5 01 00 01 fe 00
> E: 0.359938 5 01 00 01 ff 00
> E: 0.383963 5 01 00 ff 00 00
> E: 2.407955 5 01 02 00 00 00
> E: 2.623954 5 01 00 00 00 00
> E: 5.024015 5 01 01 00 00 00
> E: 5.320041 5 01 00 00 00 00
>
>
> Ciao
> Stephan
> --
> | Cui bono? |
^ permalink raw reply
* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Stephan Mueller @ 2014-01-30 22:07 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <CAN+gG=F_L1UdSqpi2d--JeerADT66s2d04t8hv+ZoVSL6dQgbg@mail.gmail.com>
Am Donnerstag, 30. Januar 2014, 16:44:02 schrieb Benjamin Tissoires:
Hi Benjamin,
>Hi Stephan,
>
>thanks for the traces.
>Well, the device definitively presents an Output and an Input report
>on the report ID 2. That means that the windows driver can send and
>read configuration to the trackpoint through this report.
>
>I think our best chance here is to capture the initialization protocol
>from a Windows virtual machine, and then mimic the behavior under
>Linux.
Thanks.
Can you please help me how to do that? I install Windows in a KVM with
the mouse device as a USB-passthrough device -- shall I detach the Linux
USB driver?
Now, shall I use hid-recorder on that device or rather usbmon while
Windows is booting?
Thanks
Stephan
^ permalink raw reply
* Is the hid-multitouch default driver for multitouch screen ?
From: Yufeng Shen @ 2014-01-30 22:52 UTC (permalink / raw)
To: linux-input
Hi,
I was under the impression that in recent kernel (I am testing on 3.8)
hid-multitouch
is the default driver for touchscreen. And I found one case that there is this
RNDPLUS PulseIR Touchscreen pid/vid 2512:5004 defaults to generic-usb driver.
If you search
"HID-Multitouch Kernel Driver Installation for Android OS IDSPulse"
there is a setup document shows one need to add its pid/vid to
hid_have_special_driver[]
list so it will pick up hid-multitouch. Do we still need to do that
in recent kernel ?
Thanks,
Yufeng Shen
^ permalink raw reply
* Re: Is the hid-multitouch default driver for multitouch screen ?
From: Benjamin Tissoires @ 2014-01-30 23:47 UTC (permalink / raw)
To: Yufeng Shen; +Cc: linux-input
In-Reply-To: <CAPDwgkMf2j9kmDa1Q7LgMjtqGXfqEDMv7JNC+eO6j3Vk9DPysQ@mail.gmail.com>
Hi Yufeng,
On Thu, Jan 30, 2014 at 5:52 PM, Yufeng Shen <miletus@chromium.org> wrote:
> Hi,
>
> I was under the impression that in recent kernel (I am testing on 3.8)
> hid-multitouch
> is the default driver for touchscreen.
yes, and no. hid-multitouch is the default for Win 7 / Win 8 certified
touchscreens, which tends to be a lot.
For instance, here is a list of device I know of:
http://lii-enac.fr/en/architecture/linux-input/multitouch-devices.html
So given that on x86 most manufacturers wants the Microsoft
certification, their devices use hid-multitouch. However, on the
Android side, or the embedded world more generally, people use their
own protocol and communication bus and they have to write their own
driver.
Side note, I think they should consider switching to HID over i2c,
they will not need to write any driver this way. But that's just my
opinion.
> And I found one case that there is this
> RNDPLUS PulseIR Touchscreen pid/vid 2512:5004 defaults to generic-usb driver.
Hmm... the RNDPLUS 5003 (as written in the table I mentioned above)
uses hid-multitouch. And given the fact that you refer to generic-usb
driver, I think (but I may be wrong) that last time you checked this
device, it was on a kernel before 3.2. Now it should be handled by
hid-multitouch. Or it is just a single touch touchscreen.
>
> If you search
> "HID-Multitouch Kernel Driver Installation for Android OS IDSPulse"
> there is a setup document shows one need to add its pid/vid to
> hid_have_special_driver[]
> list so it will pick up hid-multitouch. Do we still need to do that
> in recent kernel ?
No.
- Starting from 3.2 multitouch devices where rejected by hid-core and
had to be added manually to hid-multiouch
- in 3.4, there were a non generic attempt to automatically bind them
to hid-multitouch (attempt which was doing its job though)
- starting from 3.5, there is a much better generic solution and
multitouch screens are bound to hid-multitouch, and sensors to
hid-sensorhub for instance.
So to sum up, no, multitouch screen do not need to be added to
hid_have_special_driver[] except some rare cases which are already in
the kernel.
Cheers,
Benjamin
>
>
> Thanks,
>
> Yufeng Shen
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH RFC] Input: Add Microchip AR1021 i2c touchscreen
From: Dmitry Torokhov @ 2014-01-31 1:01 UTC (permalink / raw)
To: Christian Gmeiner; +Cc: linux-input
In-Reply-To: <1391074185-26973-1-git-send-email-christian.gmeiner@gmail.com>
Hi Christian,
On Thu, Jan 30, 2014 at 10:29:45AM +0100, Christian Gmeiner wrote:
> This driver is quite simple and only supports the Touch
> Reporting Protocol.
Pretty clean and neat, just a few comments.
>
> Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
> ---
> drivers/input/touchscreen/Kconfig | 12 ++
> drivers/input/touchscreen/Makefile | 1 +
> drivers/input/touchscreen/ar1021_i2c.c | 201 ++++++++++++++++++++++++++++++++
> 3 files changed, 214 insertions(+)
> create mode 100644 drivers/input/touchscreen/ar1021_i2c.c
>
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 07e9e82..15cc9a1 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI
> To compile this driver as a module, choose M here: the
> module will be called ad7879-spi.
>
> +config TOUCHSCREEN_AR1021_I2C
> + tristate "Microchip AR1021 i2c touchscreen"
> + depends on I2C && OF
> + help
> + Say Y here if you have the Microchip AR1021 touchscreen controller
> + chip in your system.
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called microchip_ar1021_i2c.
s/microchip_ar1021_i2c/ar1021_i2c
> +
> config TOUCHSCREEN_ATMEL_MXT
> tristate "Atmel mXT I2C Touchscreen"
> depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 62801f2..efaa328 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
> obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
> obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
> obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
> +obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
> obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
> obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
> obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
> diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
> new file mode 100644
> index 0000000..c77ce05
> --- /dev/null
> +++ b/drivers/input/touchscreen/ar1021_i2c.c
> @@ -0,0 +1,201 @@
> +/*
> + * Microchip AR1021 driver for I2C
> + *
> + * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
> + *
> + * License: GPL as published by the FSF.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/of.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/gpio.h>
> +#include <asm/unaligned.h>
> +
> +#define AR1021_TOCUH_PKG_SIZE 5
> +
> +struct ar1021_i2c {
> + struct i2c_client *client;
> + struct input_dev *input;
> + u8 data[AR1021_TOCUH_PKG_SIZE];
> +};
> +
> +static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
> +{
> + struct ar1021_i2c *ar1021 = dev_id;
> + struct input_dev *input = ar1021->input;
> + u8 *data = ar1021->data;
> + unsigned int x, y, button;
> + int error;
> +
> + error = i2c_master_recv(ar1021->client,
> + ar1021->data, sizeof(ar1021->data));
> + if (error < 0)
> + goto out;
> +
> + button = !(data[0] & BIT(0));
> + x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f);
> + y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f);
> +
> + input_report_key(input, BTN_TOUCH, button);
> + input_report_abs(input, ABS_X, x);
> + input_report_abs(input, ABS_Y, y);
> + input_sync(input);
> +
> +out:
> + return IRQ_HANDLED;
> +}
> +
> +static int ar1021_i2c_open(struct input_dev *dev)
> +{
> + struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
> + struct i2c_client *client = wac_i2c->client;
> +
> + enable_irq(client->irq);
> +
> + return 0;
> +}
> +
> +static void ar1021_i2c_close(struct input_dev *dev)
> +{
> + struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
> + struct i2c_client *client = wac_i2c->client;
> +
> + disable_irq(client->irq);
> +}
> +
> +static int ar1021_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct ar1021_i2c *ar1021;
> + struct input_dev *input;
> + int error;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> + dev_err(&client->dev, "i2c_check_functionality error\n");
> + return -EIO;
> + }
> +
> + ar1021 = devm_kzalloc(client->dev, sizeof(*ar1021), GFP_KERNEL);
> + input = input_allocate_device();
Use devm_input_allocate_device() and later devm_request_threaded_irq()
as well.
> + if (!ar1021 || !input) {
> + error = -ENOMEM;
> + goto err_free_mem;
> + }
> +
> + ar1021->client = client;
> + ar1021->input = input;
> +
> + input->name = "ar1021 I2C Touchscreen";
> + input->id.bustype = BUS_I2C;
> + input->dev.parent = &client->dev;
> + input->open = ar1021_i2c_open;
> + input->close = ar1021_i2c_close;
> +
> + input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +
> + __set_bit(BTN_TOOL_PEN, input->keybit);
> +
> + input_set_abs_params(input, ABS_X, 0, 4095, 0, 0);
> + input_set_abs_params(input, ABS_Y, 0, 4095, 0, 0);
> +
> + input_set_drvdata(input, ar1021);
> +
> + error = request_threaded_irq(client->irq, NULL, ar1021_i2c_irq,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "ar1021_i2c", ar1021);
> + if (error) {
> + dev_err(&client->dev,
> + "Failed to enable IRQ, error: %d\n", error);
> + goto err_free_mem;
> + }
> +
> + /* Disable the IRQ, we'll enable it in wac_i2c_open() */
No, not in wac_i2c_open ;) It looks like you might want to update
copyright notice to mentioned what driver you used as a base...
> + disable_irq(client->irq);
> +
> + error = input_register_device(ar1021->input);
> + if (error) {
> + dev_err(&client->dev,
> + "Failed to register input device, error: %d\n", error);
> + goto err_free_irq;
> + }
> +
> + i2c_set_clientdata(client, ar1021);
> + return 0;
> +
> +err_free_irq:
> + free_irq(client->irq, ar1021);
> +err_free_mem:
> + input_free_device(input);
> +
> + return error;
> +}
> +
> +static int ar1021_i2c_remove(struct i2c_client *client)
> +{
> + struct ar1021_i2c *ar1021 = i2c_get_clientdata(client);
> +
> + free_irq(client->irq, ar1021);
> + input_unregister_device(ar1021->input);
> +
> + return 0;
> +}
If you use devm throughout you won't need ar1021_i2c_remove method at
all.
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ar1021_i2c_suspend(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> +
> + disable_irq(client->irq);
> +
> + return 0;
> +}
> +
> +static int ar1021_i2c_resume(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> +
> + enable_irq(client->irq);
You do not want to enable IRQ if there are no users (nobody opened
device).
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
> +
> +static const struct i2c_device_id ar1021_i2c_id[] = {
> + { "MICROCHIP_AR1021_I2C", 0 },
> + { },
> +};
> +MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id ar1021_i2c_of_match[] = {
> + { .compatible = "mc,ar1021-i2c", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
> +#endif
> +
> +static struct i2c_driver ar1021_i2c_driver = {
> + .driver = {
> + .name = "ar1021_i2c",
> + .owner = THIS_MODULE,
> + .pm = &ar1021_i2c_pm,
> + .of_match_table = of_match_ptr(ar1021_i2c_of_match),
> + },
> +
> + .probe = ar1021_i2c_probe,
> + .remove = ar1021_i2c_remove,
> + .id_table = ar1021_i2c_id,
> +};
> +module_i2c_driver(ar1021_i2c_driver);
> +
> +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
> +MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ar1021_i2c");
Why platform if it is I2C driver? This MODULE_ALIAS is not needed at
all.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Benjamin Tissoires @ 2014-01-31 3:50 UTC (permalink / raw)
To: Stephan Mueller
Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <2193004.SWLhWZ2AvA@tauon>
On Thu, Jan 30, 2014 at 5:07 PM, Stephan Mueller <smueller@chronox.de> wrote:
> Am Donnerstag, 30. Januar 2014, 16:44:02 schrieb Benjamin Tissoires:
>
> Hi Benjamin,
>
>>Hi Stephan,
>>
>>thanks for the traces.
>>Well, the device definitively presents an Output and an Input report
>>on the report ID 2. That means that the windows driver can send and
>>read configuration to the trackpoint through this report.
>>
>>I think our best chance here is to capture the initialization protocol
>>from a Windows virtual machine, and then mimic the behavior under
>>Linux.
>
> Thanks.
>
> Can you please help me how to do that? I install Windows in a KVM with
> the mouse device as a USB-passthrough device -- shall I detach the Linux
> USB driver?
No, you don't have to detach the Linux USB driver (it's done for you
by KVM IIRC).
>
> Now, shall I use hid-recorder on that device or rather usbmon while
> Windows is booting?
usbmon is the tool you need. hid-recorder will not work because the
hid subsystem will be disconnected as the device is used by Windows.
You can even use wireshark now. It gives you a nice interface and
interpretation of the USB packets :)
Cheers,
Benjamin
^ permalink raw reply
* Re: Sony Vaio Duo 11: getting middle mouse button to work
From: Stephan Mueller @ 2014-01-31 4:31 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Mattia Dongili, platform-driver-x86, Jiri Kosina, linux-input
In-Reply-To: <CAN+gG=HAXE4LT_tN+3tE8-T_L5g+2HAE35CN+z8KCEQcmJ=JWw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1431 bytes --]
Am Donnerstag, 30. Januar 2014, 22:50:25 schrieb Benjamin Tissoires:
Hi Benjamin,
> > Can you please help me how to do that? I install Windows in a KVM with
> > the mouse device as a USB-passthrough device -- shall I detach the Linux
> > USB driver?
>
> No, you don't have to detach the Linux USB driver (it's done for you
> by KVM IIRC).
Correct, libvirtd is the one that detaches the driver when I start the VM.
>
> > Now, shall I use hid-recorder on that device or rather usbmon while
> > Windows is booting?
>
> usbmon is the tool you need. hid-recorder will not work because the
> hid subsystem will be disconnected as the device is used by Windows.
>
> You can even use wireshark now. It gives you a nice interface and
> interpretation of the USB packets :)
Please see attached for the USB sniffing on the USB bus with the Crucialtec
touch pad when Windows starts up and shuts down. To drive the device, Windows
uses the Crucialtec driver according to its device settings display.
The attached log contains the log information for the mouse (device 001:005)
and the USB bridge (device 001:002). I cut out the listing for the touch pad
which is also attached to that bridge.
The strange thing, however, is the following: the mouse works in Windows when
I do not sniff on the USB bus using usbmon. But as soon as I start sniffing,
it does not work any more.
Is that log helpful?
Ciao
Stephan
--
| Cui bono? |
[-- Attachment #2: sony-vaio-duo-11-usbsniff.bz2 --]
[-- Type: application/x-bzip, Size: 1338 bytes --]
^ permalink raw reply
* Re: [PATCH RFC] Input: Add Microchip AR1021 i2c touchscreen
From: Christian Gmeiner @ 2014-01-31 11:40 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <20140131010131.GA29566@core.coreip.homeip.net>
Hi Dmitry.
> On Thu, Jan 30, 2014 at 10:29:45AM +0100, Christian Gmeiner wrote:
>> This driver is quite simple and only supports the Touch
>> Reporting Protocol.
>
> Pretty clean and neat, just a few comments.
>
Thanks for the review.
>>
>> Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
>> ---
>> drivers/input/touchscreen/Kconfig | 12 ++
>> drivers/input/touchscreen/Makefile | 1 +
>> drivers/input/touchscreen/ar1021_i2c.c | 201 ++++++++++++++++++++++++++++++++
>> 3 files changed, 214 insertions(+)
>> create mode 100644 drivers/input/touchscreen/ar1021_i2c.c
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 07e9e82..15cc9a1 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI
>> To compile this driver as a module, choose M here: the
>> module will be called ad7879-spi.
>>
>> +config TOUCHSCREEN_AR1021_I2C
>> + tristate "Microchip AR1021 i2c touchscreen"
>> + depends on I2C && OF
>> + help
>> + Say Y here if you have the Microchip AR1021 touchscreen controller
>> + chip in your system.
>> +
>> + If unsure, say N.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called microchip_ar1021_i2c.
>
> s/microchip_ar1021_i2c/ar1021_i2c
>
ups
>> +
>> config TOUCHSCREEN_ATMEL_MXT
>> tristate "Atmel mXT I2C Touchscreen"
>> depends on I2C
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index 62801f2..efaa328 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
>> obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
>> obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
>> obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
>> +obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
>> obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
>> obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
>> obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
>> diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
>> new file mode 100644
>> index 0000000..c77ce05
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/ar1021_i2c.c
>> @@ -0,0 +1,201 @@
>> +/*
>> + * Microchip AR1021 driver for I2C
>> + *
>> + * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
>> + *
>> + * License: GPL as published by the FSF.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/input.h>
>> +#include <linux/of.h>
>> +#include <linux/i2c.h>
>> +#include <linux/slab.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/gpio.h>
>> +#include <asm/unaligned.h>
>> +
>> +#define AR1021_TOCUH_PKG_SIZE 5
>> +
>> +struct ar1021_i2c {
>> + struct i2c_client *client;
>> + struct input_dev *input;
>> + u8 data[AR1021_TOCUH_PKG_SIZE];
>> +};
>> +
>> +static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
>> +{
>> + struct ar1021_i2c *ar1021 = dev_id;
>> + struct input_dev *input = ar1021->input;
>> + u8 *data = ar1021->data;
>> + unsigned int x, y, button;
>> + int error;
>> +
>> + error = i2c_master_recv(ar1021->client,
>> + ar1021->data, sizeof(ar1021->data));
>> + if (error < 0)
>> + goto out;
>> +
>> + button = !(data[0] & BIT(0));
>> + x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f);
>> + y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f);
>> +
>> + input_report_key(input, BTN_TOUCH, button);
>> + input_report_abs(input, ABS_X, x);
>> + input_report_abs(input, ABS_Y, y);
>> + input_sync(input);
>> +
>> +out:
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int ar1021_i2c_open(struct input_dev *dev)
>> +{
>> + struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
>> + struct i2c_client *client = wac_i2c->client;
>> +
>> + enable_irq(client->irq);
>> +
>> + return 0;
>> +}
>> +
>> +static void ar1021_i2c_close(struct input_dev *dev)
>> +{
>> + struct ar1021_i2c *wac_i2c = input_get_drvdata(dev);
>> + struct i2c_client *client = wac_i2c->client;
>> +
>> + disable_irq(client->irq);
>> +}
>> +
>> +static int ar1021_i2c_probe(struct i2c_client *client,
>> + const struct i2c_device_id *id)
>> +{
>> + struct ar1021_i2c *ar1021;
>> + struct input_dev *input;
>> + int error;
>> +
>> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
>> + dev_err(&client->dev, "i2c_check_functionality error\n");
>> + return -EIO;
>> + }
>> +
>> + ar1021 = devm_kzalloc(client->dev, sizeof(*ar1021), GFP_KERNEL);
>> + input = input_allocate_device();
>
> Use devm_input_allocate_device() and later devm_request_threaded_irq()
> as well.
>
Thats a very good idea...
>> + if (!ar1021 || !input) {
>> + error = -ENOMEM;
>> + goto err_free_mem;
>> + }
>> +
>> + ar1021->client = client;
>> + ar1021->input = input;
>> +
>> + input->name = "ar1021 I2C Touchscreen";
>> + input->id.bustype = BUS_I2C;
>> + input->dev.parent = &client->dev;
>> + input->open = ar1021_i2c_open;
>> + input->close = ar1021_i2c_close;
>> +
>> + input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
>> +
>> + __set_bit(BTN_TOOL_PEN, input->keybit);
>> +
>> + input_set_abs_params(input, ABS_X, 0, 4095, 0, 0);
>> + input_set_abs_params(input, ABS_Y, 0, 4095, 0, 0);
>> +
>> + input_set_drvdata(input, ar1021);
>> +
>> + error = request_threaded_irq(client->irq, NULL, ar1021_i2c_irq,
>> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>> + "ar1021_i2c", ar1021);
>> + if (error) {
>> + dev_err(&client->dev,
>> + "Failed to enable IRQ, error: %d\n", error);
>> + goto err_free_mem;
>> + }
>> +
>> + /* Disable the IRQ, we'll enable it in wac_i2c_open() */
>
> No, not in wac_i2c_open ;) It looks like you might want to update
> copyright notice to mentioned what driver you used as a base...
>
Will do :)
>> + disable_irq(client->irq);
>> +
>> + error = input_register_device(ar1021->input);
>> + if (error) {
>> + dev_err(&client->dev,
>> + "Failed to register input device, error: %d\n", error);
>> + goto err_free_irq;
>> + }
>> +
>> + i2c_set_clientdata(client, ar1021);
Do I need the i2c_set_clientdata(..) call at all?
>> + return 0;
>> +
>> +err_free_irq:
>> + free_irq(client->irq, ar1021);
>> +err_free_mem:
>> + input_free_device(input);
>> +
>> + return error;
>> +}
>> +
>> +static int ar1021_i2c_remove(struct i2c_client *client)
>> +{
>> + struct ar1021_i2c *ar1021 = i2c_get_clientdata(client);
>> +
>> + free_irq(client->irq, ar1021);
>> + input_unregister_device(ar1021->input);
>> +
>> + return 0;
>> +}
>
> If you use devm throughout you won't need ar1021_i2c_remove method at
> all.
>
Correct.. will do it in the final patch.
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int ar1021_i2c_suspend(struct device *dev)
>> +{
>> + struct i2c_client *client = to_i2c_client(dev);
>> +
>> + disable_irq(client->irq);
>> +
>> + return 0;
>> +}
>> +
>> +static int ar1021_i2c_resume(struct device *dev)
>> +{
>> + struct i2c_client *client = to_i2c_client(dev);
>> +
>> + enable_irq(client->irq);
>
> You do not want to enable IRQ if there are no users (nobody opened
> device).
>
Okay.. but then I also do not need the disable_irq(..) call in
ar1021_i2c_suspend
and can totally remove the PM stuff - or?
>> +
>> + return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
>> +
>> +static const struct i2c_device_id ar1021_i2c_id[] = {
>> + { "MICROCHIP_AR1021_I2C", 0 },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
>> +
>> +#ifdef CONFIG_OF
>> +static struct of_device_id ar1021_i2c_of_match[] = {
>> + { .compatible = "mc,ar1021-i2c", },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
>> +#endif
>> +
>> +static struct i2c_driver ar1021_i2c_driver = {
>> + .driver = {
>> + .name = "ar1021_i2c",
>> + .owner = THIS_MODULE,
>> + .pm = &ar1021_i2c_pm,
>> + .of_match_table = of_match_ptr(ar1021_i2c_of_match),
>> + },
>> +
>> + .probe = ar1021_i2c_probe,
>> + .remove = ar1021_i2c_remove,
>> + .id_table = ar1021_i2c_id,
>> +};
>> +module_i2c_driver(ar1021_i2c_driver);
>> +
>> +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
>> +MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:ar1021_i2c");
>
> Why platform if it is I2C driver? This MODULE_ALIAS is not needed at
> all.
Ok
Thanks
--
Christian Gmeiner, MSc
^ permalink raw reply
* [PATCH 0/7] Input: xpad: Fix wireless controller connection and leds
From: Greg Kroah-Hartman @ 2014-01-31 13:03 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input
Here's a series of patches from Pierre-Loup and I that rework the xpad
driver to fix the issue where when a wireless xpad controller is plugged
in, 4 joystick devices are created, no matter how many are really
attached to the system. Pierre-Loup's patches fix this by dynamically
creating the devices only when they are found by the wireless
controller.
Bonus is that the LEDs now work correctly with this series too, when
mixing wireless and wired, we can properly identify which device is
which on the X-LED. We also now name the LED device based on the
joystick id, not on an atomic number that keeps incrementing, never
decrementing when removed from the system.
Note, patch 4/7 is a crazy hack to get the minor number of the joystick
id that we have registered with the system. Pierre-Loup came up with
this, and I really can't figure out any other way to do it, the joydev
layer doesn't even know this information, otherwise I would have added
it to that layer so the xpad driver could call it. Am I missing
something here that we could do instead?
The first patch in the series fixes a really annoying bug when plugging
in one of these controllers to a USB 3 system. The URB type is
incorrect so the xhci driver complains about it, rightly so. I think
the original code was incorrect, but left it alone incase there is
really a crazy device with a bulk endpoint instead of interrupt.
Also, even with this series applied, the xpad driver needs a bunch of
work. The LED device seems pointless as it doesn't actually work, and
given that the number/name of the LED device was impossible to actually
map to the proper xpad device, there's no way userspace code could ever
actually use it. I think it should be removed entirely. There's also
some other USB things that should be cleaned up, the bulk urb doesn't
need to be always running (with lots of disconnect/connect cycles you
can get a warning about it running when trying to submit it again), and
the urb callbacks seem a bit strange. I'll work on those issues next...
This series was based on a larger patch from Pierre-Loup that I broke up
into pieces, and added some of my own where needed to resolve other
issues.
^ permalink raw reply
* [PATCH 2/7] Input: xpad: set the LEDs properly on XBox Wireless controllers
From: Greg Kroah-Hartman @ 2014-01-31 13:03 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, Pierre-Loup A. Griffais, Greg Kroah-Hartman
In-Reply-To: <1391173414-6199-1-git-send-email-gregkh@linuxfoundation.org>
From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
Add the logic to set the LEDs on XBox Wireless controllers. Command
sequence found by sniffing the Windows data stream when plugging the
device in.
Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/input/joystick/xpad.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 517829f6a58b..aabff9140aaa 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -715,15 +715,37 @@ struct xpad_led {
static void xpad_send_led_command(struct usb_xpad *xpad, int command)
{
- if (command >= 0 && command < 14) {
- mutex_lock(&xpad->odata_mutex);
+ if (command > 15)
+ return;
+
+ mutex_lock(&xpad->odata_mutex);
+
+ switch (xpad->xtype) {
+ case XTYPE_XBOX360:
xpad->odata[0] = 0x01;
xpad->odata[1] = 0x03;
xpad->odata[2] = command;
xpad->irq_out->transfer_buffer_length = 3;
- usb_submit_urb(xpad->irq_out, GFP_KERNEL);
- mutex_unlock(&xpad->odata_mutex);
+ break;
+ case XTYPE_XBOX360W:
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x00;
+ xpad->odata[2] = 0x08;
+ xpad->odata[3] = 0x40 + (command % 0x0e);
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ break;
}
+
+ usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ mutex_unlock(&xpad->odata_mutex);
}
static void xpad_led_set(struct led_classdev *led_cdev,
@@ -743,7 +765,7 @@ static int xpad_led_probe(struct usb_xpad *xpad)
struct led_classdev *led_cdev;
int error;
- if (xpad->xtype != XTYPE_XBOX360)
+ if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W)
return 0;
xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL);
--
1.8.5.3
^ permalink raw reply related
* [PATCH 3/7] Input: xpad: move the input device creation to a new function
From: Greg Kroah-Hartman @ 2014-01-31 13:03 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, Pierre-Loup A. Griffais, Greg Kroah-Hartman
In-Reply-To: <1391173414-6199-1-git-send-email-gregkh@linuxfoundation.org>
From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
To allow us to later create / destroy the input device from the urb
callback, we need to initialize the input device from a separate
function. So pull that logic out now to make later patches more
"obvious" as to what they do.
Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/input/joystick/xpad.c | 171 ++++++++++++++++++++++++------------------
1 file changed, 97 insertions(+), 74 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index aabff9140aaa..5b5a84dae54a 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -293,6 +293,7 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */
+ const char *name; /* name of the device */
};
/*
@@ -858,70 +859,21 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
}
}
-static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+static int xpad_init_input(struct usb_xpad *xpad)
{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct usb_xpad *xpad;
struct input_dev *input_dev;
- struct usb_endpoint_descriptor *ep_irq_in;
int i, error;
- for (i = 0; xpad_device[i].idVendor; i++) {
- if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
- (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
- break;
- }
-
- xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
input_dev = input_allocate_device();
- if (!xpad || !input_dev) {
- error = -ENOMEM;
- goto fail1;
- }
-
- xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
- GFP_KERNEL, &xpad->idata_dma);
- if (!xpad->idata) {
- error = -ENOMEM;
- goto fail1;
- }
-
- xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
- if (!xpad->irq_in) {
- error = -ENOMEM;
- goto fail2;
- }
-
- xpad->udev = udev;
- xpad->intf = intf;
- xpad->mapping = xpad_device[i].mapping;
- xpad->xtype = xpad_device[i].xtype;
-
- if (xpad->xtype == XTYPE_UNKNOWN) {
- if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
- if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
- xpad->xtype = XTYPE_XBOX360W;
- else
- xpad->xtype = XTYPE_XBOX360;
- } else
- xpad->xtype = XTYPE_XBOX;
-
- if (dpad_to_buttons)
- xpad->mapping |= MAP_DPAD_TO_BUTTONS;
- if (triggers_to_buttons)
- xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS;
- if (sticks_to_null)
- xpad->mapping |= MAP_STICKS_TO_NULL;
- }
+ if (!input_dev)
+ return -ENOMEM;
xpad->dev = input_dev;
- usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
- strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
- input_dev->name = xpad_device[i].name;
+ input_dev->name = xpad->name;
input_dev->phys = xpad->phys;
- usb_to_input_id(udev, &input_dev->id);
- input_dev->dev.parent = &intf->dev;
+ usb_to_input_id(xpad->udev, &input_dev->id);
+ input_dev->dev.parent = &xpad->intf->dev;
input_set_drvdata(input_dev, xpad);
@@ -966,17 +918,92 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
}
- error = xpad_init_output(intf, xpad);
- if (error)
- goto fail3;
-
error = xpad_init_ff(xpad);
if (error)
- goto fail4;
+ goto fail_init_ff;
error = xpad_led_probe(xpad);
if (error)
- goto fail5;
+ goto fail_init_led;
+
+ error = input_register_device(xpad->dev);
+ if (error)
+ goto fail_input_register;
+
+ return 0;
+
+fail_input_register:
+ xpad_led_disconnect(xpad);
+
+fail_init_led:
+ input_ff_destroy(input_dev);
+
+fail_init_ff:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_xpad *xpad;
+ struct usb_endpoint_descriptor *ep_irq_in;
+ int i, error;
+
+ for (i = 0; xpad_device[i].idVendor; i++) {
+ if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
+ (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
+ break;
+ }
+
+ xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
+ if (!xpad) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
+ strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
+
+ xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
+ GFP_KERNEL, &xpad->idata_dma);
+ if (!xpad->idata) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!xpad->irq_in) {
+ error = -ENOMEM;
+ goto fail2;
+ }
+
+ xpad->udev = udev;
+ xpad->intf = intf;
+ xpad->mapping = xpad_device[i].mapping;
+ xpad->xtype = xpad_device[i].xtype;
+ xpad->name = xpad_device[i].name;
+
+ if (xpad->xtype == XTYPE_UNKNOWN) {
+ if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+ if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
+ xpad->xtype = XTYPE_XBOX360W;
+ else
+ xpad->xtype = XTYPE_XBOX360;
+ } else
+ xpad->xtype = XTYPE_XBOX;
+
+ if (dpad_to_buttons)
+ xpad->mapping |= MAP_DPAD_TO_BUTTONS;
+ if (triggers_to_buttons)
+ xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS;
+ if (sticks_to_null)
+ xpad->mapping |= MAP_STICKS_TO_NULL;
+ }
+
+ error = xpad_init_output(intf, xpad);
+ if (error)
+ goto fail3;
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(xpad->irq_in, udev,
@@ -986,10 +1013,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->irq_in->transfer_dma = xpad->idata_dma;
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- error = input_register_device(xpad->dev);
- if (error)
- goto fail6;
-
usb_set_intfdata(intf, xpad);
if (xpad->xtype == XTYPE_XBOX360W) {
@@ -1000,7 +1023,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
if (!xpad->bulk_out) {
error = -ENOMEM;
- goto fail7;
+ goto fail4;
}
xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
@@ -1048,24 +1071,24 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
*/
xpad->irq_in->dev = xpad->udev;
error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
- if (error)
+ if (error) {
+ usb_kill_urb(xpad->irq_in);
goto fail9;
+ }
}
+ xpad->pad_present = 1;
+ error = xpad_init_input(xpad);
+ if (error)
+ goto fail9;
return 0;
fail9: kfree(xpad->bdata);
fail8: usb_free_urb(xpad->bulk_out);
- fail7: input_unregister_device(input_dev);
- input_dev = NULL;
- fail6: xpad_led_disconnect(xpad);
- fail5: if (input_dev)
- input_ff_destroy(input_dev);
fail4: xpad_deinit_output(xpad);
fail3: usb_free_urb(xpad->irq_in);
fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
- fail1: input_free_device(input_dev);
- kfree(xpad);
+ fail1: kfree(xpad);
return error;
}
--
1.8.5.3
^ permalink raw reply related
* [PATCH 4/7] Input: xpad: Set the correct LED number
From: Greg Kroah-Hartman @ 2014-01-31 13:03 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, Pierre-Loup A. Griffais, Greg Kroah-Hartman
In-Reply-To: <1391173414-6199-1-git-send-email-gregkh@linuxfoundation.org>
From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
The LED number should not just be incremented every time, that doesn't
work, set the number based on the number it actually is.
Note, the joydev subsystem doesn't allow us to easily find out the minor
number, so we have to walk all devices in order to find a joystick
device by looking at the name of the device. Odds are, this isn't the
best way to do it, but I don't know of any other way at the moment.
Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/input/joystick/xpad.c | 26 +++++++++++++++++++++-----
1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 5b5a84dae54a..7997ae89a877 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -293,6 +293,7 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */
+ int joydev_id; /* the minor of the device */
const char *name; /* name of the device */
};
@@ -789,11 +790,6 @@ static int xpad_led_probe(struct usb_xpad *xpad)
return error;
}
- /*
- * Light up the segment corresponding to controller number
- */
- xpad_send_led_command(xpad, (led_no % 4) + 2);
-
return 0;
}
@@ -809,6 +805,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
#else
static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
static void xpad_led_disconnect(struct usb_xpad *xpad) { }
+static void xpad_send_led_command(struct usb_xpad *xpad, int command) { }
#endif
@@ -859,9 +856,17 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
}
}
+static int xpad_find_joydev(struct device *dev, void *data)
+{
+ if (strstr(dev_name(dev), "js"))
+ return 1;
+ return 0;
+}
+
static int xpad_init_input(struct usb_xpad *xpad)
{
struct input_dev *input_dev;
+ struct device *joydev_dev;
int i, error;
input_dev = input_allocate_device();
@@ -930,6 +935,17 @@ static int xpad_init_input(struct usb_xpad *xpad)
if (error)
goto fail_input_register;
+ joydev_dev = device_find_child(&xpad->dev->dev, NULL,
+ xpad_find_joydev);
+ if (joydev_dev) {
+ dev_dbg(&xpad->dev->dev, "Found xpad with minor %i\n",
+ MINOR(joydev_dev->devt));
+ xpad->joydev_id = MINOR(joydev_dev->devt);
+
+ /* Light up the segment corresponding to controller number */
+ xpad_send_led_command(xpad, (xpad->joydev_id % 4) + 2);
+ }
+
return 0;
fail_input_register:
--
1.8.5.3
^ permalink raw reply related
* [PATCH 5/7] Input: xpad: disconnect all Wireless controllers at init
From: Greg Kroah-Hartman @ 2014-01-31 13:03 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, Pierre-Loup A. Griffais, Greg Kroah-Hartman
In-Reply-To: <1391173414-6199-1-git-send-email-gregkh@linuxfoundation.org>
From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
We initializing the driver/device, we really don't know how many
controllers are connected. So send a "disconnect all" command to the
base-station, and let the user pair the controllers in the order in
which they want them assigned.
Note, this means we now do not "preallocate" all 4 devices when a single
wireless base station is seen, but require the device to be properly
connected to the base station before that can happen. The allocation of
the device happens in the next patch, not here, so in a way, this patch
breaks all wireless devices...
Because we want to talk to the device, we have to set up the output urbs
no matter if we have CONFIG_JOYSTICK_XPAD_FF or
CONFIG_JOYSTICK_XPAD_LEDS enabled not, so take out the code that allows
those variables and code to be disabled (it's so small it should never
matter...)
Signed-off-by: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/input/joystick/xpad.c | 46 ++++++++++++++++++++++++++++++++-----------
1 file changed, 34 insertions(+), 12 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 7997ae89a877..7a07b95790d7 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -278,12 +278,10 @@ struct usb_xpad {
struct urb *bulk_out;
unsigned char *bdata;
-#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
struct urb *irq_out; /* urb for interrupt out report */
unsigned char *odata; /* output data */
dma_addr_t odata_dma;
struct mutex odata_mutex;
-#endif
#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
struct xpad_led *led;
@@ -537,7 +535,6 @@ static void xpad_bulk_out(struct urb *urb)
}
}
-#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
static void xpad_irq_out(struct urb *urb)
{
struct usb_xpad *xpad = urb->context;
@@ -623,11 +620,6 @@ static void xpad_deinit_output(struct usb_xpad *xpad)
xpad->odata, xpad->odata_dma);
}
}
-#else
-static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; }
-static void xpad_deinit_output(struct usb_xpad *xpad) {}
-static void xpad_stop_output(struct usb_xpad *xpad) {}
-#endif
#ifdef CONFIG_JOYSTICK_XPAD_FF
static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
@@ -1091,11 +1083,41 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
usb_kill_urb(xpad->irq_in);
goto fail9;
}
+
+ /*
+ * We don't know how to check the controller state at driver
+ * load, so just disconnect them all, requiring the user to
+ * repair the device in the order they want them used. Good
+ * point is that we don't connect devices in "random" order,
+ * but the extra step seems a bit harsh as other operating
+ * systems don't require this at the moment.
+ *
+ * Power-off packet information came from an OS-X
+ * reverse-engineered driver located at:
+ * http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver#toc1
+ */
+ mutex_lock(&xpad->odata_mutex);
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x00;
+ xpad->odata[2] = 0x08;
+ xpad->odata[3] = 0xC0;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ mutex_unlock(&xpad->odata_mutex);
+ } else {
+ xpad->pad_present = 1;
+ error = xpad_init_input(xpad);
+ if (error)
+ goto fail9;
}
- xpad->pad_present = 1;
- error = xpad_init_input(xpad);
- if (error)
- goto fail9;
return 0;
--
1.8.5.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox