2009-06-07 14:17:16

by Bastien Nocera

[permalink] [raw]
Subject: Apple Bluetooth devices: Battery level?

Heya,

I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
anyone had information on how to get the battery level from the device
(the keyboard apparently also allows that):
http://support.apple.com/kb/TA27118?viewlocale=en_US

Anyone with an idea? An unparsed HID event? Does it need "poking"?

Cheers



2009-06-16 13:37:08

by Marcel Holtmann

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

Hi Jiri,

> > Sorry, I posted a parsed version of the report descriptor previously
> > though I failed to cross post:
> >
> > Collection page=Generic_Desktop usage=Mouse
> > Input id=2 size=1 count=1 page=Button usage=Button_1 Variable, logical range 0..1
> > Input id=2 size=1 count=1 page=Button usage=Button_2 Variable, logical range 0..1
> > Input id=2 size=1 count=1 page=Button usage=Button_3 Variable, logical range 0..1
> > Input id=2 size=1 count=1 page=Button usage=Button_4 Variable, logical range 0..1
> > Input id=2 size=4 count=1 page=0x0000 usage=0x0000 Const Variable, logical range 0..1
> > Collection page=Generic_Desktop usage=Pointer
> > Input id=2 size=8 count=1 page=Generic_Desktop usage=X Variable Relative, logical range -127..127
> > Input id=2 size=8 count=1 page=Generic_Desktop usage=Y Variable Relative, logical range -127..127
> > Input id=2 size=8 count=1 page=Consumer usage=AC_Pan Variable Relative, logical range -127..127
> > Input id=2 size=8 count=1 page=Generic_Desktop usage=Wheel Variable Relative, logical range -127..127
> > End collection
> > Input id=2 size=8 count=1 page=0x00ff usage=0x00c0 Variable, logical range -127..127
> > Feature id=71 size=8 count=1 page=Device_Controls usage=Battery_Strength Variable NoPref Volatile, logical range 0..100
> > End collection
> > As you can see, there is a Feature report relating to the Battery
> > Strength, but I do not know what to do with that. I suppose that it is
> > to be polled in order to return a percentage value..
>
> Yes, it has to be polled. But at least the initial value should be
> retrieved during the very initialization of the device -- both USB and
> Bluetooth implementations this in usbhid_init_reports()/hidp_start() --
> they query INPUT and FEATURE reports during device initialization.

since the descriptors are the same, can we not do this inside the HID
core with a specific quirk setting. And maybe even a timer that polls it
every 30 minutes or so.

Regards

Marcel



2009-06-16 09:59:57

by Jiri Kosina

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Sat, 13 Jun 2009, Iain Hibbert wrote:

> Sorry, I posted a parsed version of the report descriptor previously
> though I failed to cross post:
>
> Collection page=Generic_Desktop usage=Mouse
> Input id=2 size=1 count=1 page=Button usage=Button_1 Variable, logical range 0..1
> Input id=2 size=1 count=1 page=Button usage=Button_2 Variable, logical range 0..1
> Input id=2 size=1 count=1 page=Button usage=Button_3 Variable, logical range 0..1
> Input id=2 size=1 count=1 page=Button usage=Button_4 Variable, logical range 0..1
> Input id=2 size=4 count=1 page=0x0000 usage=0x0000 Const Variable, logical range 0..1
> Collection page=Generic_Desktop usage=Pointer
> Input id=2 size=8 count=1 page=Generic_Desktop usage=X Variable Relative, logical range -127..127
> Input id=2 size=8 count=1 page=Generic_Desktop usage=Y Variable Relative, logical range -127..127
> Input id=2 size=8 count=1 page=Consumer usage=AC_Pan Variable Relative, logical range -127..127
> Input id=2 size=8 count=1 page=Generic_Desktop usage=Wheel Variable Relative, logical range -127..127
> End collection
> Input id=2 size=8 count=1 page=0x00ff usage=0x00c0 Variable, logical range -127..127
> Feature id=71 size=8 count=1 page=Device_Controls usage=Battery_Strength Variable NoPref Volatile, logical range 0..100
> End collection
> As you can see, there is a Feature report relating to the Battery
> Strength, but I do not know what to do with that. I suppose that it is
> to be polled in order to return a percentage value..

Yes, it has to be polled. But at least the initial value should be
retrieved during the very initialization of the device -- both USB and
Bluetooth implementations this in usbhid_init_reports()/hidp_start() --
they query INPUT and FEATURE reports during device initialization.

--
Jiri Kosina
SUSE Labs

2009-06-13 13:16:15

by Iain Hibbert

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Fri, 12 Jun 2009, Jiri Kosina wrote:

> It doesn't seem to be sending the battery level in a standard-compliant
> way, at least according to these reports.

but I wonder what is the standard-compliant way?

> Could you also please post the report descriptor data? They should appear
> in a hid debugging mode once the mouse is connected.

Sorry, I posted a parsed version of the report descriptor previously
though I failed to cross post:

Collection page=Generic_Desktop usage=Mouse
Input id=2 size=1 count=1 page=Button usage=Button_1 Variable, logical range 0..1
Input id=2 size=1 count=1 page=Button usage=Button_2 Variable, logical range 0..1
Input id=2 size=1 count=1 page=Button usage=Button_3 Variable, logical range 0..1
Input id=2 size=1 count=1 page=Button usage=Button_4 Variable, logical range 0..1
Input id=2 size=4 count=1 page=0x0000 usage=0x0000 Const Variable, logical range 0..1
Collection page=Generic_Desktop usage=Pointer
Input id=2 size=8 count=1 page=Generic_Desktop usage=X Variable Relative, logical range -127..127
Input id=2 size=8 count=1 page=Generic_Desktop usage=Y Variable Relative, logical range -127..127
Input id=2 size=8 count=1 page=Consumer usage=AC_Pan Variable Relative, logical range -127..127
Input id=2 size=8 count=1 page=Generic_Desktop usage=Wheel Variable Relative, logical range -127..127
End collection
Input id=2 size=8 count=1 page=0x00ff usage=0x00c0 Variable, logical range -127..127
Feature id=71 size=8 count=1 page=Device_Controls usage=Battery_Strength Variable NoPref Volatile, logical range 0..100
End collection

As you can see, there is a Feature report relating to the Battery
Strength, but I do not know what to do with that. I suppose that it is to
be polled in order to return a percentage value..

> Does 00ff.00c0 usage change it's value if you put different batteries into
> the mouse?

The mighty mouse has a single hard shell and detects the difference
between left click and right click by a capacitance detector underneath
the finger positions. This usage contains bits indicating where the mouse
is being touched though I guess it is just for testing as the OS does not
really need to know (it generates normal button clicks).

There is one more data point that may be interesting. The mighty mouse
(and an old Apple keyboard I have) spits out an input report that is not
mentioned in the descriptor with id=48 just before the battery fails. I
captured it once and it contained a single byte with value 0x01. I have no
idea what that report is for but as I use NiMH batteries which maintain
voltage until very near the end, it could be a charge level warning.

regards,
iain

2009-06-12 13:31:20

by Jiri Kosina

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Wed, 10 Jun 2009, Bastien Nocera wrote:

> > Could you please obtain HID debug dump (CONFIG_HID_DEBUG + modprobe
> > hid module with 'debug=2') and look at this usage both in the report
> > descriptor itself, and also check whether it is present in the
> > received reports?
> This is the debug output:
> http://pastebin.com/m256a4187
> Doesn't look very useful, and I have no idea what the 00ff.00c0 part
> does.

It doesn't seem to be sending the battery level in a standard-compliant
way, at least according to these reports.

Could you also please post the report descriptor data? They should appear
in a hid debugging mode once the mouse is connected.

Does 00ff.00c0 usage change it's value if you put different batteries into
the mouse?

If this is not successful, looking at what the driver in other operating
system does on the transport level might be an option.

--
Jiri Kosina
SUSE Labs

2009-06-12 13:27:06

by Jiri Kosina

[permalink] [raw]
Subject: [PATCH 1/2] HID: use debugfs for report dumping descriptor

It is a little bit inconvenient for people who have some non-standard
HID hardware (usually violating the HID specification) to have to
recompile kernel with CONFIG_HID_DEBUG to be able to see kernel's perspective
of the HID report descriptor and observe the parsed events. Plus the messages
are then mixed up inconveniently with the rest of the dmesg stuff.

This patch implements /sys/kernel/debug/hid/<device>/rdesc file, which
represents the kernel's view of report descriptor (both the raw report
descriptor data and parsed contents).

With all the device-specific debug data being available through debugfs, there
is no need for keeping CONFIG_HID_DEBUG, as the 'debug' parameter to the
hid module will now only output only driver-specific debugging options, which has
absolutely minimal memory footprint, just a few error messages and one global
flag (hid_debug).

We use the current set of output formatting functions. The ones that need to be
used both for one-shot rdesc seq_file and also for continuous flow of data
(individual reports, as being sent by the device) distinguish according to the
passed seq_file parameter, and if it is NULL, it still output to kernel ringbuffer,
otherwise the corresponding seq_file is used for output.

The format of the output is preserved.

Signed-off-by: Jiri Kosina <[email protected]>
---
drivers/hid/Kconfig | 15 ---
drivers/hid/Makefile | 5 +-
drivers/hid/hid-core.c | 13 ++-
drivers/hid/hid-debug.c | 227 ++++++++++++++++++++++++++++-------------
drivers/hid/hid-input.c | 13 +--
drivers/hid/usbhid/hid-core.c | 8 +--
include/linux/hid-debug.h | 32 ++++---
include/linux/hid.h | 4 +
8 files changed, 195 insertions(+), 122 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7e67dcb..05950a7 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -31,21 +31,6 @@ config HID

If unsure, say Y.

-config HID_DEBUG
- bool "HID debugging support"
- default y
- depends on HID
- ---help---
- This option lets the HID layer output diagnostics about its internal
- state, resolve HID usages, dump HID fields, etc. Individual HID drivers
- use this debugging facility to output information about individual HID
- devices, etc.
-
- This feature is useful for those who are either debugging the HID parser
- or any HID hardware device.
-
- If unsure, say Y.
-
config HIDRAW
bool "/dev/hidraw raw HID device support"
depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 1f7cb0f..cf3687d 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -3,9 +3,12 @@
#
hid-objs := hid-core.o hid-input.o

+ifdef CONFIG_DEBUG_FS
+ hid-objs += hid-debug.o
+endif
+
obj-$(CONFIG_HID) += hid.o

-hid-$(CONFIG_HID_DEBUG) += hid-debug.o
hid-$(CONFIG_HIDRAW) += hidraw.o

hid-logitech-objs := hid-lg.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 8551693..d4317db 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -44,12 +44,10 @@
#define DRIVER_DESC "HID core driver"
#define DRIVER_LICENSE "GPL"

-#ifdef CONFIG_HID_DEBUG
int hid_debug = 0;
module_param_named(debug, hid_debug, int, 0600);
MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)");
EXPORT_SYMBOL_GPL(hid_debug);
-#endif

/*
* Register a new report for a device.
@@ -987,7 +985,6 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)

if (offset >= field->report_count) {
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
- hid_dump_field(field, 8);
return -1;
}
if (field->logical_minimum < 0) {
@@ -1721,6 +1718,8 @@ int hid_add_device(struct hid_device *hdev)
if (!ret)
hdev->status |= HID_STAT_ADDED;

+ hid_debug_register(hdev, dev_name(&hdev->dev));
+
return ret;
}
EXPORT_SYMBOL_GPL(hid_add_device);
@@ -1768,6 +1767,7 @@ static void hid_remove_device(struct hid_device *hdev)
{
if (hdev->status & HID_STAT_ADDED) {
device_del(&hdev->dev);
+ hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED;
}
}
@@ -1843,6 +1843,10 @@ static int __init hid_init(void)
{
int ret;

+ if (hid_debug)
+ printk(KERN_WARNING "HID: hid_debug parameter has been deprecated. "
+ "Debugging data are now provided via debugfs\n");
+
ret = bus_register(&hid_bus_type);
if (ret) {
printk(KERN_ERR "HID: can't register hid bus\n");
@@ -1853,6 +1857,8 @@ static int __init hid_init(void)
if (ret)
goto err_bus;

+ hid_debug_init();
+
return 0;
err_bus:
bus_unregister(&hid_bus_type);
@@ -1862,6 +1868,7 @@ err:

static void __exit hid_exit(void)
{
+ hid_debug_exit();
hidraw_exit();
bus_unregister(&hid_bus_type);
}
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 47ac1a7..067e173 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -1,9 +1,9 @@
/*
* (c) 1999 Andreas Gal <[email protected]>
* (c) 2000-2001 Vojtech Pavlik <[email protected]>
- * (c) 2007 Jiri Kosina
+ * (c) 2007-2009 Jiri Kosina
*
- * Some debug stuff for the HID parser.
+ * HID debugging support
*/

/*
@@ -26,9 +26,13 @@
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/

+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/hid.h>
#include <linux/hid-debug.h>

+static struct dentry *hid_debug_root;
+
struct hid_usage_entry {
unsigned page;
unsigned usage;
@@ -331,72 +335,83 @@ static const struct hid_usage_entry hid_usage_table[] = {
{ 0, 0, NULL }
};

-static void resolv_usage_page(unsigned page) {
+static void resolv_usage_page(unsigned page, struct seq_file *f) {
const struct hid_usage_entry *p;

for (p = hid_usage_table; p->description; p++)
if (p->page == page) {
- printk("%s", p->description);
+ if (!f)
+ printk("%s", p->description);
+ else
+ seq_printf(f, "%s", p->description);
return;
}
- printk("%04x", page);
+ if (!f)
+ printk("%04x", page);
+ else
+ seq_printf(f, "%04x", page);
}

-void hid_resolv_usage(unsigned usage) {
+void hid_resolv_usage(unsigned usage, struct seq_file *f) {
const struct hid_usage_entry *p;

- if (!hid_debug)
- return;
-
- resolv_usage_page(usage >> 16);
- printk(".");
+ resolv_usage_page(usage >> 16, f);
+ if (!f)
+ printk(".");
+ else
+ seq_printf(f, ".");
for (p = hid_usage_table; p->description; p++)
if (p->page == (usage >> 16)) {
for(++p; p->description && p->usage != 0; p++)
if (p->usage == (usage & 0xffff)) {
- printk("%s", p->description);
+ if (!f)
+ printk("%s", p->description);
+ else
+ seq_printf(f,
+ "%s",
+ p->description);
return;
}
break;
}
- printk("%04x", usage & 0xffff);
+ if (!f)
+ printk("%04x", usage & 0xffff);
+ else
+ seq_printf(f, "%04x", usage & 0xffff);
}
EXPORT_SYMBOL_GPL(hid_resolv_usage);

-static void tab(int n) {
- printk(KERN_DEBUG "%*s", n, "");
+static void tab(int n, struct seq_file *f) {
+ seq_printf(f, "%*s", n, "");
}

-void hid_dump_field(struct hid_field *field, int n) {
+void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) {
int j;

- if (!hid_debug)
- return;
-
if (field->physical) {
- tab(n);
- printk("Physical(");
- hid_resolv_usage(field->physical); printk(")\n");
+ tab(n, f);
+ seq_printf(f, "Physical(");
+ hid_resolv_usage(field->physical, f); seq_printf(f, ")\n");
}
if (field->logical) {
- tab(n);
- printk("Logical(");
- hid_resolv_usage(field->logical); printk(")\n");
+ tab(n, f);
+ seq_printf(f, "Logical(");
+ hid_resolv_usage(field->logical, f); seq_printf(f, ")\n");
}
- tab(n); printk("Usage(%d)\n", field->maxusage);
+ tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage);
for (j = 0; j < field->maxusage; j++) {
- tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n");
+ tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n");
}
if (field->logical_minimum != field->logical_maximum) {
- tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum);
- tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum);
+ tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum);
+ tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum);
}
if (field->physical_minimum != field->physical_maximum) {
- tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum);
- tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum);
+ tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum);
+ tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum);
}
if (field->unit_exponent) {
- tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
+ tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent);
}
if (field->unit) {
static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
@@ -417,77 +432,75 @@ void hid_dump_field(struct hid_field *field, int n) {
data >>= 4;

if(sys > 4) {
- tab(n); printk("Unit(Invalid)\n");
+ tab(n, f); seq_printf(f, "Unit(Invalid)\n");
}
else {
int earlier_unit = 0;

- tab(n); printk("Unit(%s : ", systems[sys]);
+ tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]);

for (i=1 ; i<sizeof(__u32)*2 ; i++) {
char nibble = data & 0xf;
data >>= 4;
if (nibble != 0) {
if(earlier_unit++ > 0)
- printk("*");
- printk("%s", units[sys][i]);
+ seq_printf(f, "*");
+ seq_printf(f, "%s", units[sys][i]);
if(nibble != 1) {
/* This is a _signed_ nibble(!) */

int val = nibble & 0x7;
if(nibble & 0x08)
val = -((0x7 & ~val) +1);
- printk("^%d", val);
+ seq_printf(f, "^%d", val);
}
}
}
- printk(")\n");
+ seq_printf(f, ")\n");
}
}
- tab(n); printk("Report Size(%u)\n", field->report_size);
- tab(n); printk("Report Count(%u)\n", field->report_count);
- tab(n); printk("Report Offset(%u)\n", field->report_offset);
+ tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size);
+ tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count);
+ tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset);

- tab(n); printk("Flags( ");
+ tab(n, f); seq_printf(f, "Flags( ");
j = field->flags;
- printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
- printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
- printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
- printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
- printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
- printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
- printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
- printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
- printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
- printk(")\n");
+ seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
+ seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
+ seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
+ seq_printf(f, ")\n");
}
EXPORT_SYMBOL_GPL(hid_dump_field);

-void hid_dump_device(struct hid_device *device) {
+void hid_dump_device(struct hid_device *device, struct seq_file *f)
+{
struct hid_report_enum *report_enum;
struct hid_report *report;
struct list_head *list;
unsigned i,k;
static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};

- if (!hid_debug)
- return;
-
for (i = 0; i < HID_REPORT_TYPES; i++) {
report_enum = device->report_enum + i;
list = report_enum->report_list.next;
while (list != &report_enum->report_list) {
report = (struct hid_report *) list;
- tab(2);
- printk("%s", table[i]);
+ tab(2, f);
+ seq_printf(f, "%s", table[i]);
if (report->id)
- printk("(%d)", report->id);
- printk("[%s]", table[report->type]);
- printk("\n");
+ seq_printf(f, "(%d)", report->id);
+ seq_printf(f, "[%s]", table[report->type]);
+ seq_printf(f, "\n");
for (k = 0; k < report->maxfield; k++) {
- tab(4);
- printk("Field(%d)\n", k);
- hid_dump_field(report->field[k], 6);
+ tab(4, f);
+ seq_printf(f, "Field(%d)\n", k);
+ hid_dump_field(report->field[k], 6, f);
}
list = list->next;
}
@@ -500,7 +513,7 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) {
return;

printk(KERN_DEBUG "hid-debug: input ");
- hid_resolv_usage(usage->hid);
+ hid_resolv_usage(usage->hid, NULL);
printk(" = %d\n", value);
}
EXPORT_SYMBOL_GPL(hid_dump_input);
@@ -767,12 +780,84 @@ static const char **names[EV_MAX + 1] = {
[EV_SND] = sounds, [EV_REP] = repeats,
};

-void hid_resolv_event(__u8 type, __u16 code) {
-
- if (!hid_debug)
- return;
+void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) {

- printk("%s.%s", events[type] ? events[type] : "?",
+ seq_printf(f, "%s.%s", events[type] ? events[type] : "?",
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
}
-EXPORT_SYMBOL_GPL(hid_resolv_event);
+
+void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
+{
+ int i, j, k;
+ struct hid_report *report;
+ struct hid_usage *usage;
+
+ for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+ list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
+ for (i = 0; i < report->maxfield; i++) {
+ for ( j = 0; j < report->field[i]->maxusage; j++) {
+ usage = report->field[i]->usage + j;
+ hid_resolv_usage(usage->hid, f);
+ seq_printf(f, " ---> ");
+ hid_resolv_event(usage->type, usage->code, f);
+ seq_printf(f, "\n");
+ }
+ }
+ }
+ }
+
+}
+
+static int hid_debug_rdesc_show(struct seq_file *f, void *p)
+{
+ struct hid_device *hdev = f->private;
+ int i;
+
+ /* dump HID report descriptor */
+ for (i = 0; i < hdev->rsize; i++)
+ seq_printf(f, "%02x ", hdev->rdesc[i]);
+ seq_printf(f, "\n\n");
+
+ /* dump parsed data and input mappings */
+ hid_dump_device(hdev, f);
+ seq_printf(f, "\n");
+ hid_dump_input_mapping(hdev, f);
+
+ return 0;
+}
+
+static int hid_debug_rdesc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hid_debug_rdesc_show, inode->i_private);
+}
+
+static const struct file_operations hid_debug_rdesc_fops = {
+ .open = hid_debug_rdesc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void hid_debug_register(struct hid_device *hdev, const char *name)
+{
+ hdev->debug_dir = debugfs_create_dir(name, hid_debug_root);
+ hdev->debug_rdesc = debugfs_create_file("rdesc", 0400,
+ hdev->debug_dir, hdev, &hid_debug_rdesc_fops);
+}
+
+void hid_debug_unregister(struct hid_device *hdev)
+{
+ debugfs_remove(hdev->debug_rdesc);
+ debugfs_remove(hdev->debug_dir);
+}
+
+void hid_debug_init(void)
+{
+ hid_debug_root = debugfs_create_dir("hid", NULL);
+}
+
+void hid_debug_exit(void)
+{
+ debugfs_remove_recursive(hid_debug_root);
+}
+
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 7f183b7..5862b0f 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -159,17 +159,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel

field->hidinput = hidinput;

- dbg_hid("Mapping: ");
- hid_resolv_usage(usage->hid);
- dbg_hid_line(" ---> ");
-
if (field->flags & HID_MAIN_ITEM_CONSTANT)
goto ignore;

/* only LED usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT &&
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
- dbg_hid_line(" [non-LED output field] ");
goto ignore;
}

@@ -561,15 +556,9 @@ mapped:
set_bit(MSC_SCAN, input->mscbit);
}

- hid_resolv_event(usage->type, usage->code);
-
- dbg_hid_line("\n");
-
- return;
-
ignore:
- dbg_hid_line("IGNORED\n");
return;
+
}

void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index ac8049b..708aa52 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -4,8 +4,8 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <[email protected]>
* Copyright (c) 2005 Michael Haboustak <[email protected]> for Concept2, Inc
- * Copyright (c) 2006-2008 Jiri Kosina
* Copyright (c) 2007-2008 Oliver Neukum
+ * Copyright (c) 2006-2009 Jiri Kosina
*/

/*
@@ -886,11 +886,6 @@ static int usbhid_parse(struct hid_device *hid)
goto err;
}

- dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
- for (n = 0; n < rsize; n++)
- dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
- dbg_hid_line("\n");
-
ret = hid_parse_report(hid, rdesc, rsize);
kfree(rdesc);
if (ret) {
@@ -1005,7 +1000,6 @@ static int usbhid_start(struct hid_device *hid)
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);

usbhid_init_reports(hid);
- hid_dump_device(hid);

set_bit(HID_STARTED, &usbhid->iofl);

diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h
index 50d568e..516e12c 100644
--- a/include/linux/hid-debug.h
+++ b/include/linux/hid-debug.h
@@ -2,7 +2,7 @@
#define __HID_DEBUG_H

/*
- * Copyright (c) 2007 Jiri Kosina
+ * Copyright (c) 2007-2009 Jiri Kosina
*/

/*
@@ -22,24 +22,30 @@
*
*/

-#ifdef CONFIG_HID_DEBUG
+#ifdef CONFIG_DEBUG_FS

void hid_dump_input(struct hid_usage *, __s32);
-void hid_dump_device(struct hid_device *);
-void hid_dump_field(struct hid_field *, int);
-void hid_resolv_usage(unsigned);
-void hid_resolv_event(__u8, __u16);
+void hid_dump_device(struct hid_device *, struct seq_file *);
+void hid_dump_field(struct hid_field *, int, struct seq_file *);
+void hid_resolv_usage(unsigned, struct seq_file *);
+void hid_debug_register(struct hid_device *, const char *);
+void hid_debug_unregister(struct hid_device *);
+void hid_debug_init(void);
+void hid_debug_exit(void);

#else

-#define hid_dump_input(a,b) do { } while (0)
-#define hid_dump_device(c) do { } while (0)
-#define hid_dump_field(a,b) do { } while (0)
-#define hid_resolv_usage(a) do { } while (0)
-#define hid_resolv_event(a,b) do { } while (0)
-
-#endif /* CONFIG_HID_DEBUG */
+#define hid_dump_input(a,b) do { } while (0)
+#define hid_dump_device(c) do { } while (0)
+#define hid_dump_field(a,b) do { } while (0)
+#define hid_resolv_usage(a) do { } while (0)
+#define hid_resolv_event(a,b) do { } while (0)
+#define hid_debug_register(a, b) do { } while (0)
+#define hid_debug_unregister(a) do { } while (0)
+#define hid_debug_init() do { } while (0)
+#define hid_debug_exit() do { } while (0)

+#endif

#endif

diff --git a/include/linux/hid.h b/include/linux/hid.h
index a72876e..da09ab1 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -451,6 +451,10 @@ struct hid_device { /* device report descriptor */
char phys[64]; /* Device physical location */
char uniq[64]; /* Device unique identifier (serial #) */

+ /* debugfs */
+ struct dentry *debug_dir;
+ struct dentry *debug_rdesc;
+
void *driver_data;

/* temporary hid_ff handling (until moved to the drivers) */
--
1.5.6

2009-06-12 13:27:03

by Jiri Kosina

[permalink] [raw]
Subject: [PATCH 2/2] HID: use debugfs for events/reports dumping

This is a followup patch to the one implemeting rdesc representation in debugfs
rather than being dependent on compile-time CONFIG_HID_DEBUG setting.

The API of the appropriate formatting functions is slightly modified -- if
they are passed seq_file pointer, the one-shot output for 'rdesc' file mode
is used, and therefore the message is formatted into the corresponding seq_file
immediately.

Otherwise the called function allocated a new buffer, formats the text into the
buffer and returns the pointer to it, so that it can be queued into the ring-buffer
of the processess blocked waiting on input on 'events' file in debugfs.

'debug' parameter to the 'hid' module is now used solely for the prupose of inetrnal
driver state debugging (parser, transport, etc).

Signed-off-by: Jiri Kosina <[email protected]>
---
drivers/hid/hid-core.c | 42 ++++++--
drivers/hid/hid-debug.c | 239 +++++++++++++++++++++++++++++++++++++++++----
include/linux/hid-debug.h | 18 +++-
include/linux/hid.h | 26 ++----
4 files changed, 276 insertions(+), 49 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d4317db..449bd74 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -46,7 +46,7 @@

int hid_debug = 0;
module_param_named(debug, hid_debug, int, 0600);
-MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)");
+MODULE_PARM_DESC(debug, "toggle HID debugging messages");
EXPORT_SYMBOL_GPL(hid_debug);

/*
@@ -859,7 +859,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
struct hid_driver *hdrv = hid->driver;
int ret;

- hid_dump_input(usage, value);
+ hid_dump_input(hid, usage, value);

if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
ret = hdrv->event(hid, field, usage, value);
@@ -981,7 +981,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
{
unsigned size = field->report_size;

- hid_dump_input(field->usage + offset, value);
+ hid_dump_input(field->report->device, field->usage + offset, value);

if (offset >= field->report_count) {
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
@@ -1075,6 +1075,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_driver *hdrv = hid->driver;
struct hid_report *report;
+ char *buf;
unsigned int i;
int ret;

@@ -1086,18 +1087,36 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
return -1;
}

- dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+ buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE,
+ interrupt ? GFP_ATOMIC : GFP_KERNEL);
+
+ if (!buf) {
+ report = hid_get_report(report_enum, data);
+ goto nomem;
+ }
+
+ snprintf(buf, HID_DEBUG_BUFSIZE - 1,
+ "\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+ hid_debug_event(hid, buf);

report = hid_get_report(report_enum, data);
if (!report)
return -1;

/* dump the report */
- dbg_hid("report %d (size %u) = ", report->id, size);
- for (i = 0; i < size; i++)
- dbg_hid_line(" %02x", data[i]);
- dbg_hid_line("\n");
+ snprintf(buf, HID_DEBUG_BUFSIZE - 1,
+ "report %d (size %u) = ", report->id, size);
+ hid_debug_event(hid, buf);
+ for (i = 0; i < size; i++) {
+ snprintf(buf, HID_DEBUG_BUFSIZE - 1,
+ " %02x", data[i]);
+ hid_debug_event(hid, buf);
+ }
+ hid_debug_event(hid, "\n");
+
+ kfree(buf);

+nomem:
if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
ret = hdrv->raw_event(hid, report, data, size);
if (ret != 0)
@@ -1756,6 +1775,9 @@ struct hid_device *hid_allocate_device(void)
for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&hdev->report_enum[i].report_list);

+ init_waitqueue_head(&hdev->debug_wait);
+ INIT_LIST_HEAD(&hdev->debug_list);
+
return hdev;
err:
put_device(&hdev->dev);
@@ -1844,8 +1866,8 @@ static int __init hid_init(void)
int ret;

if (hid_debug)
- printk(KERN_WARNING "HID: hid_debug parameter has been deprecated. "
- "Debugging data are now provided via debugfs\n");
+ printk(KERN_WARNING "HID: hid_debug is now used solely for parser and driver debugging.\n"
+ "HID: debugfs is now used for inspecting the device (report descriptor, reports)\n");

ret = bus_register(&hid_bus_type);
if (ret) {
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 067e173..a331a18 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -28,6 +28,10 @@

#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+
#include <linux/hid.h>
#include <linux/hid-debug.h>

@@ -335,49 +339,86 @@ static const struct hid_usage_entry hid_usage_table[] = {
{ 0, 0, NULL }
};

-static void resolv_usage_page(unsigned page, struct seq_file *f) {
+/* Either output directly into simple seq_file, or (if f == NULL)
+ * allocate a separate buffer that will then be passed to the 'events'
+ * ringbuffer.
+ *
+ * This is because these functions can be called both for "one-shot"
+ * "rdesc" while resolving, or for blocking "events".
+ *
+ * This holds both for resolv_usage_page() and hid_resolv_usage().
+ */
+static char *resolv_usage_page(unsigned page, struct seq_file *f) {
const struct hid_usage_entry *p;
+ char *buf = NULL;
+
+ if (!f) {
+ buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+ }

for (p = hid_usage_table; p->description; p++)
if (p->page == page) {
- if (!f)
- printk("%s", p->description);
- else
+ if (!f) {
+ snprintf(buf, HID_DEBUG_BUFSIZE, "%s",
+ p->description);
+ return buf;
+ }
+ else {
seq_printf(f, "%s", p->description);
- return;
+ return NULL;
+ }
}
if (!f)
- printk("%04x", page);
+ snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page);
else
seq_printf(f, "%04x", page);
+ return buf;
}

-void hid_resolv_usage(unsigned usage, struct seq_file *f) {
+char *hid_resolv_usage(unsigned usage, struct seq_file *f) {
const struct hid_usage_entry *p;
+ char *buf = NULL;
+ int len = 0;

- resolv_usage_page(usage >> 16, f);
- if (!f)
- printk(".");
- else
+ buf = resolv_usage_page(usage >> 16, f);
+ if (IS_ERR(buf)) {
+ printk(KERN_ERR "error allocating HID debug buffer\n");
+ return NULL;
+ }
+
+
+ if (!f) {
+ len = strlen(buf);
+ snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), ".");
+ len++;
+ }
+ else {
seq_printf(f, ".");
+ }
for (p = hid_usage_table; p->description; p++)
if (p->page == (usage >> 16)) {
for(++p; p->description && p->usage != 0; p++)
if (p->usage == (usage & 0xffff)) {
if (!f)
- printk("%s", p->description);
+ snprintf(buf + len,
+ max(0,HID_DEBUG_BUFSIZE - len - 1),
+ "%s", p->description);
else
seq_printf(f,
"%s",
p->description);
- return;
+ return buf;
}
break;
}
if (!f)
- printk("%04x", usage & 0xffff);
+ snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1),
+ "%04x", usage & 0xffff);
else
seq_printf(f, "%04x", usage & 0xffff);
+ return buf;
}
EXPORT_SYMBOL_GPL(hid_resolv_usage);

@@ -508,13 +549,37 @@ void hid_dump_device(struct hid_device *device, struct seq_file *f)
}
EXPORT_SYMBOL_GPL(hid_dump_device);

-void hid_dump_input(struct hid_usage *usage, __s32 value) {
- if (hid_debug < 2)
+/* enqueue string to 'events' ring buffer */
+void hid_debug_event(struct hid_device *hdev, char *buf)
+{
+ int i;
+ struct hid_debug_list *list;
+
+ list_for_each_entry(list, &hdev->debug_list, node) {
+ for (i = 0; i <= strlen(buf); i++)
+ list->hid_debug_buf[(list->tail + i) % (HID_DEBUG_BUFSIZE - 1)] =
+ buf[i];
+ list->tail = (list->tail + i) % (HID_DEBUG_BUFSIZE - 1);
+ }
+}
+EXPORT_SYMBOL_GPL(hid_debug_event);
+
+void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value)
+{
+ char *buf;
+ int len;
+
+ buf = hid_resolv_usage(usage->hid, NULL);
+ if (!buf)
return;
+ len = strlen(buf);
+ snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value);
+
+ hid_debug_event(hdev, buf);
+
+ kfree(buf);
+ wake_up_interruptible(&hdev->debug_wait);

- printk(KERN_DEBUG "hid-debug: input ");
- hid_resolv_usage(usage->hid, NULL);
- printk(" = %d\n", value);
}
EXPORT_SYMBOL_GPL(hid_dump_input);

@@ -808,6 +873,7 @@ void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)

}

+
static int hid_debug_rdesc_show(struct seq_file *f, void *p)
{
struct hid_device *hdev = f->private;
@@ -831,6 +897,126 @@ static int hid_debug_rdesc_open(struct inode *inode, struct file *file)
return single_open(file, hid_debug_rdesc_show, inode->i_private);
}

+static int hid_debug_events_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+ struct hid_debug_list *list;
+
+ if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto out;
+ }
+ list->hdev = (struct hid_device *) inode->i_private;
+ file->private_data = list;
+ mutex_init(&list->read_mutex);
+
+ list_add_tail(&list->node, &list->hdev->debug_list);
+
+out:
+ return err;
+}
+
+static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct hid_debug_list *list = file->private_data;
+ int ret = 0, len;
+ DECLARE_WAITQUEUE(wait, current);
+
+ while (ret == 0) {
+ mutex_lock(&list->read_mutex);
+ if (list->head == list->tail) {
+ add_wait_queue(&list->hdev->debug_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list->head == list->tail) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ if (!list->hdev || !list->hdev->debug) {
+ ret = -EIO;
+ break;
+ }
+
+ /* allow O_NONBLOCK from other threads */
+ mutex_unlock(&list->read_mutex);
+ schedule();
+ mutex_lock(&list->read_mutex);
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&list->hdev->debug_wait, &wait);
+ }
+
+ if (ret)
+ goto out;
+
+ /* pass the ringbuffer contents to userspace */
+copy_rest:
+ if (list->tail == list->head)
+ goto out;
+ if (list->tail > list->head) {
+ len = list->tail - list->head;
+
+ if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret += len;
+ list->head += len;
+ } else {
+ len = HID_DEBUG_BUFSIZE - list->head;
+
+ if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ list->head = 0;
+ ret += len;
+ goto copy_rest;
+ }
+
+ }
+out:
+ mutex_unlock(&list->read_mutex);
+ return ret;
+}
+
+static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait)
+{
+ struct hid_debug_list *list = file->private_data;
+
+ poll_wait(file, &list->hdev->debug_wait, wait);
+ if (list->head != list->tail)
+ return POLLIN | POLLRDNORM;
+ if (!list->hdev->debug)
+ return POLLERR | POLLHUP;
+ return 0;
+}
+
+static int hid_debug_events_release(struct inode *inode, struct file *file)
+{
+ struct hid_debug_list *list = file->private_data;
+
+ list_del(&list->node);
+ kfree(list->hid_debug_buf);
+ kfree(list);
+
+ return 0;
+}
+
static const struct file_operations hid_debug_rdesc_fops = {
.open = hid_debug_rdesc_open,
.read = seq_read,
@@ -838,16 +1024,31 @@ static const struct file_operations hid_debug_rdesc_fops = {
.release = single_release,
};

+static const struct file_operations hid_debug_events_fops = {
+ .owner = THIS_MODULE,
+ .open = hid_debug_events_open,
+ .read = hid_debug_events_read,
+ .poll = hid_debug_events_poll,
+ .release = hid_debug_events_release,
+};
+
+
void hid_debug_register(struct hid_device *hdev, const char *name)
{
hdev->debug_dir = debugfs_create_dir(name, hid_debug_root);
hdev->debug_rdesc = debugfs_create_file("rdesc", 0400,
hdev->debug_dir, hdev, &hid_debug_rdesc_fops);
+ hdev->debug_events = debugfs_create_file("events", 0400,
+ hdev->debug_dir, hdev, &hid_debug_events_fops);
+ hdev->debug = 1;
}

void hid_debug_unregister(struct hid_device *hdev)
{
+ hdev->debug = 0;
+ wake_up_interruptible(&hdev->debug_wait);
debugfs_remove(hdev->debug_rdesc);
+ debugfs_remove(hdev->debug_events);
debugfs_remove(hdev->debug_dir);
}

diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h
index 516e12c..ec08ac1 100644
--- a/include/linux/hid-debug.h
+++ b/include/linux/hid-debug.h
@@ -24,14 +24,27 @@

#ifdef CONFIG_DEBUG_FS

-void hid_dump_input(struct hid_usage *, __s32);
+void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
void hid_dump_device(struct hid_device *, struct seq_file *);
void hid_dump_field(struct hid_field *, int, struct seq_file *);
-void hid_resolv_usage(unsigned, struct seq_file *);
+char *hid_resolv_usage(unsigned, struct seq_file *);
void hid_debug_register(struct hid_device *, const char *);
void hid_debug_unregister(struct hid_device *);
void hid_debug_init(void);
void hid_debug_exit(void);
+void hid_debug_event(struct hid_device *, char *);
+
+#define HID_DEBUG_BUFSIZE 512
+
+struct hid_debug_list {
+ char *hid_debug_buf;
+ int head;
+ int tail;
+ struct fasync_struct *fasync;
+ struct hid_device *hdev;
+ struct list_head node;
+ struct mutex read_mutex;
+};

#else

@@ -44,6 +57,7 @@ void hid_debug_exit(void);
#define hid_debug_unregister(a) do { } while (0)
#define hid_debug_init() do { } while (0)
#define hid_debug_exit() do { } while (0)
+#define hid_debug_event(a,b) do { } while (0)

#endif

diff --git a/include/linux/hid.h b/include/linux/hid.h
index da09ab1..60fa529 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -451,10 +451,6 @@ struct hid_device { /* device report descriptor */
char phys[64]; /* Device physical location */
char uniq[64]; /* Device unique identifier (serial #) */

- /* debugfs */
- struct dentry *debug_dir;
- struct dentry *debug_rdesc;
-
void *driver_data;

/* temporary hid_ff handling (until moved to the drivers) */
@@ -468,6 +464,14 @@ struct hid_device { /* device report descriptor */

/* handler for raw output data, used by hidraw */
int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
+
+ /* debugging support via debugfs */
+ unsigned short debug;
+ struct dentry *debug_dir;
+ struct dentry *debug_rdesc;
+ struct dentry *debug_events;
+ struct list_head debug_list;
+ wait_queue_head_t debug_wait;
};

static inline void *hid_get_drvdata(struct hid_device *hdev)
@@ -625,9 +629,7 @@ struct hid_ll_driver {

/* HID core API */

-#ifdef CONFIG_HID_DEBUG
extern int hid_debug;
-#endif

extern int hid_add_device(struct hid_device *);
extern void hid_destroy_device(struct hid_device *);
@@ -783,21 +785,9 @@ int hid_pidff_init(struct hid_device *hid);
#define hid_pidff_init NULL
#endif

-#ifdef CONFIG_HID_DEBUG
#define dbg_hid(format, arg...) if (hid_debug) \
printk(KERN_DEBUG "%s: " format ,\
__FILE__ , ## arg)
-#define dbg_hid_line(format, arg...) if (hid_debug) \
- printk(format, ## arg)
-#else
-static inline int __attribute__((format(printf, 1, 2)))
-dbg_hid(const char *fmt, ...)
-{
- return 0;
-}
-#define dbg_hid_line dbg_hid
-#endif /* HID_DEBUG */
-
#define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \
__FILE__ , ## arg)
#endif /* HID_FF */
--
1.5.6

2009-06-12 13:25:10

by Jiri Kosina

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Wed, 10 Jun 2009, Marcel Holtmann wrote:

> there is no real downside in having this all times available since you
> have to store that binary record somewhere anyway during runtime. We
> should fix that and put it into debugfs, but nobody bothered so far
> sending a patch for it.

JFYI, I have just pushed super-preliminary version of the code that moves
the whole HID-device debugging (report descriptor and report/event
dumping) into debugfs. It's 'debugfs' branch of my HID tree, for those who
are interested.
Please bear in mind that this is more or less a snapshot of the current
code (which is working, though), not production quality yet. Hopefully
they will get properly polished for 2.6.31.

I will post the patches as a followup to this mail, if anyone is
interested.

Comments and patches welcome, as usual.

--
Jiri Kosina
SUSE Labs

2009-06-10 18:19:17

by Iain Hibbert

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Wed, 10 Jun 2009, Bastien Nocera wrote:

> Doesn't look very useful, and I have no idea what the 00ff.00c0 part
> does.

I found this:

http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-258.1/IOHIDFamily/AppleHIDUsageTables.h

which labels it as "Reserved MouseData" and it seems to be related to the
capacitance detectors. Run "hcidump -X" and place your fingers on the
mouse top without actually clicking then move the mouse slightly. You will
see 0x00 (no touching), 0x01 (left finger), 0x02 (right finger) and 0x03
(both fingers). I didn't see any other values.

I think the battery strength is found by polling with the feature report
id#71 but I don't know how you do that, libusb might be able to do it?

as I mentioned before, you will also get an input report id#42 sent before
the mouse dies (but that is not listed in the HID descriptor)

iain

2009-06-10 14:25:43

by Bastien Nocera

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Wed, 2009-06-10 at 10:25 +0200, Jiri Kosina wrote:
> On Wed, 10 Jun 2009, Bastien Nocera wrote:
<snip>
> > - enabling CONFIG_HID_DEBUG requires rebuilding the kernel
>
> CONFIG_HID_DEBUG has 'default y' for quite some time, but the defconfigs
> haven't unfortunately been updated yet.
>
> > - most distributions build hid into the kernel
>
> hid.debug=2 on the kernel commandline option does the trick.

Or "echo 2 > /sys/modules/hid/parameters/debug" at run-time.

Seems CONFIG_HID_DEBUG is enabled by default in the builds I use as
well, so I'll quit complaining about that :)

Cheers


2009-06-10 14:06:09

by Bastien Nocera

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Mon, 2009-06-08 at 15:33 +0200, Jiri Kosina wrote:
> On Sun, 7 Jun 2009, Bastien Nocera wrote:
>
> > I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> > anyone had information on how to get the battery level from the device
> > (the keyboard apparently also allows that):
> > http://support.apple.com/kb/TA27118?viewlocale=en_US
> >
> > Anyone with an idea? An unparsed HID event? Does it need "poking"?
>
> If the mouse is standard-compliant, it should be sending the battery level
> strength in usage 0x20 of device controls page (0x06).
>
> Could you please obtain HID debug dump (CONFIG_HID_DEBUG + modprobe hid
> module with 'debug=2') and look at this usage both in the report
> descriptor itself, and also check whether it is present in the received
> reports?

This is the debug output:
http://pastebin.com/m256a4187

Doesn't look very useful, and I have no idea what the 00ff.00c0 part
does.

Cheers


2009-06-10 08:25:24

by Bastien Nocera

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Wed, 2009-06-10 at 11:14 +0400, Dmitriy Geels wrote:
> Hi!
>
> Kernel rebuilding is pretty easy, for ubuntu/debian it would be:
<snip>

FWIW, I contributed drivers to those two lists, so I do know how to
build my own kernels...

> 2009/6/10 Bastien Nocera <[email protected]>:
> > I found that for pretty much all the input problems, I'd need to do
> > something like that. Problem is:
> > - enabling CONFIG_HID_DEBUG requires rebuilding the kernel
> > - most distributions build hid into the kernel
> >
> > Wouldn't there be a way to enable the debug at run-time without
> > impacting too much on performance or binary size?
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>


2009-06-10 08:25:17

by Jiri Kosina

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Wed, 10 Jun 2009, Bastien Nocera wrote:

> > > I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> > > anyone had information on how to get the battery level from the device
> > > (the keyboard apparently also allows that):
> > > http://support.apple.com/kb/TA27118?viewlocale=en_US
> > > Anyone with an idea? An unparsed HID event? Does it need "poking"?
> > If the mouse is standard-compliant, it should be sending the battery level
> > strength in usage 0x20 of device controls page (0x06).
> > Could you please obtain HID debug dump (CONFIG_HID_DEBUG + modprobe hid
> > module with 'debug=2')
> I found that for pretty much all the input problems, I'd need to do
> something like that. Problem is:
> - enabling CONFIG_HID_DEBUG requires rebuilding the kernel

CONFIG_HID_DEBUG has 'default y' for quite some time, but the defconfigs
haven't unfortunately been updated yet.

> - most distributions build hid into the kernel

hid.debug=2 on the kernel commandline option does the trick.

> Wouldn't there be a way to enable the debug at run-time without
> impacting too much on performance or binary size?

Absolutely. I have a migration of all this stuff to debugfs on my TODO
list.

Thanks,

--
Jiri Kosina
SUSE Labs

2009-06-10 07:24:43

by Marcel Holtmann

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

Hi Dimitry,

first of all, no top-posting. I really mean it.

> Kernel rebuilding is pretty easy, for ubuntu/debian it would be:
> - sudo apt-get install linux-source build-essential
> - tar xf /usr/src/linux-source*
> - cd linux-source-`uname -r`
> - cp /boot/config-`uname -r` .config
> - make menuconfig (find and enable HID_DEBUG)
> - make bzImage modules deb-pkg
> Then after some time you will get deb package, which you need to
> install and then run:
> - sudo update-initramfs -c -k 'new kernel version'
> - sudo update-grub
>
> Then you can reboot into new kernel and use "echo 2 >
> /sys/modules/hid/parameters/debug" instead of debug module parameter.

that should be changeable on the fly and we should just export the HID
descriptor for users. Putting the burden to boot into a different kernel
is painful and we don't want that. Hell, I don't want to boot a new
kernel to just get the HID descriptor.

Regards

Marcel



2009-06-10 07:14:57

by Dmitriy Geels

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

Hi!

Kernel rebuilding is pretty easy, for ubuntu/debian it would be:
- sudo apt-get install linux-source build-essential
- tar xf /usr/src/linux-source*
- cd linux-source-`uname -r`
- cp /boot/config-`uname -r` .config
- make menuconfig (find and enable HID_DEBUG)
- make bzImage modules deb-pkg
Then after some time you will get deb package, which you need to
install and then run:
- sudo update-initramfs -c -k 'new kernel version'
- sudo update-grub

Then you can reboot into new kernel and use "echo 2 >
/sys/modules/hid/parameters/debug" instead of debug module parameter.

2009/6/10 Bastien Nocera <[email protected]>:
> I found that for pretty much all the input problems, I'd need to do
> something like that. Problem is:
> - enabling CONFIG_HID_DEBUG requires rebuilding the kernel
> - most distributions build hid into the kernel
>
> Wouldn't there be a way to enable the debug at run-time without
> impacting too much on performance or binary size?

2009-06-10 07:03:06

by Marcel Holtmann

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

Hi Bastien,

> > > I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> > > anyone had information on how to get the battery level from the device
> > > (the keyboard apparently also allows that):
> > > http://support.apple.com/kb/TA27118?viewlocale=en_US
> > >
> > > Anyone with an idea? An unparsed HID event? Does it need "poking"?
> >
> > If the mouse is standard-compliant, it should be sending the battery level
> > strength in usage 0x20 of device controls page (0x06).
> >
> > Could you please obtain HID debug dump (CONFIG_HID_DEBUG + modprobe hid
> > module with 'debug=2')
>
> I found that for pretty much all the input problems, I'd need to do
> something like that. Problem is:
> - enabling CONFIG_HID_DEBUG requires rebuilding the kernel
> - most distributions build hid into the kernel
>
> Wouldn't there be a way to enable the debug at run-time without
> impacting too much on performance or binary size?

there is no real downside in having this all times available since you
have to store that binary record somewhere anyway during runtime. We
should fix that and put it into debugfs, but nobody bothered so far
sending a patch for it. If we worry about code size then the decoding
should of the record should be moved into userspace, but in the end it
really doesn't matter that much.

Regards

Marcel



2009-06-09 23:22:18

by Bastien Nocera

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Mon, 2009-06-08 at 15:33 +0200, Jiri Kosina wrote:
> On Sun, 7 Jun 2009, Bastien Nocera wrote:
>
> > I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> > anyone had information on how to get the battery level from the device
> > (the keyboard apparently also allows that):
> > http://support.apple.com/kb/TA27118?viewlocale=en_US
> >
> > Anyone with an idea? An unparsed HID event? Does it need "poking"?
>
> If the mouse is standard-compliant, it should be sending the battery level
> strength in usage 0x20 of device controls page (0x06).
>
> Could you please obtain HID debug dump (CONFIG_HID_DEBUG + modprobe hid
> module with 'debug=2')

I found that for pretty much all the input problems, I'd need to do
something like that. Problem is:
- enabling CONFIG_HID_DEBUG requires rebuilding the kernel
- most distributions build hid into the kernel

Wouldn't there be a way to enable the debug at run-time without
impacting too much on performance or binary size?

> and look at this usage both in the report
> descriptor itself, and also check whether it is present in the received
> reports?

I'll look at it when I get an occasion to rebuild a kernel (which
thankfully isn't very often these days).

Cheers


2009-06-08 13:33:34

by Jiri Kosina

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Sun, 7 Jun 2009, Bastien Nocera wrote:

> I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> anyone had information on how to get the battery level from the device
> (the keyboard apparently also allows that):
> http://support.apple.com/kb/TA27118?viewlocale=en_US
>
> Anyone with an idea? An unparsed HID event? Does it need "poking"?

If the mouse is standard-compliant, it should be sending the battery level
strength in usage 0x20 of device controls page (0x06).

Could you please obtain HID debug dump (CONFIG_HID_DEBUG + modprobe hid
module with 'debug=2') and look at this usage both in the report
descriptor itself, and also check whether it is present in the received
reports?

Thanks,

--
Jiri Kosina
SUSE Labs


2009-06-07 21:15:42

by David Sainty

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

Iain Hibbert wrote:
> On Sun, 7 Jun 2009, Marcel Holtmann wrote:
>
>
>> Hi Bastien,
>>
>>
>>> I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
>>> anyone had information on how to get the battery level from the device
>>> (the keyboard apparently also allows that):
>>> http://support.apple.com/kb/TA27118?viewlocale=en_US
>>>
>>> Anyone with an idea? An unparsed HID event? Does it need "poking"?
>>>
>> I think that Apple ones used an actually HID HUT conform descriptor for
>> that. I did reverser engineered that a long time ago, but then never got
>> around to do anything with it. Can't find my notes right now. Just run
>> hcidump and eventually you will see the report.
>>
>
> I have a mighty mouse and while I don't use BlueZ or Linux I would be
> interested in the details of this.. On my (NetBSD) system, the HID
> descriptor parses as:
>
> Collection page=Generic_Desktop usage=Mouse
> Input id=2 size=1 count=1 page=Button usage=Button_1 Variable, logical range 0..1
> Input id=2 size=1 count=1 page=Button usage=Button_2 Variable, logical range 0..1
> Input id=2 size=1 count=1 page=Button usage=Button_3 Variable, logical range 0..1
> Input id=2 size=1 count=1 page=Button usage=Button_4 Variable, logical range 0..1
> Input id=2 size=4 count=1 page=0x0000 usage=0x0000 Const Variable, logical range 0..1
> Collection page=Generic_Desktop usage=Pointer
> Input id=2 size=8 count=1 page=Generic_Desktop usage=X Variable Relative, logical range -127..127
> Input id=2 size=8 count=1 page=Generic_Desktop usage=Y Variable Relative, logical range -127..127
> Input id=2 size=8 count=1 page=Consumer usage=AC_Pan Variable Relative, logical range -127..127
> Input id=2 size=8 count=1 page=Generic_Desktop usage=Wheel Variable Relative, logical range -127..127
> End collection
> Input id=2 size=8 count=1 page=0x00ff usage=0x00c0 Variable, logical range -127..127
> Feature id=71 size=8 count=1 page=Device_Controls usage=Battery_Strength Variable NoPref Volatile, logical range 0..100
> End collection
>
> But I'm not really sure what the Device_Controls feature is supposed to
> be. Is it sent from the device, or sent to the device? I've never had one
> sent from the device in any case that I know of, and I'm not sure what the
> Vendor_Defined (page 0x00ff) value (usage 0x00c0) is for either, it seems
> to be zero.
>
> What I know does happen is that a report with id=48 containing a single
> byte with value=0x01 is sent two or three times just before the battery
> gives out. That also happens with an older Apple keyboard except that the
> descriptor does not contain any Vendor or Feature items. I did capture the
> report once but I don't have it now. (I think it was an input report)
>

Features are bidirectional (speaking from USB experience, not
bluetooth). Presumably it has a battery level value between 0% and 100%
on input. You'd expect it to be just an input though (unless you can
charge it by slowly setting it to bigger and bigger values :)

Hard to guess what it does with the output value of the feature. Maybe
it's just in the feature report to get it out of the input report, which
gets sent rather often (I.e. as a power saving measure).


2009-06-07 19:34:18

by Iain Hibbert

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Sun, 7 Jun 2009, Marcel Holtmann wrote:

> Hi Bastien,
>
> > I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> > anyone had information on how to get the battery level from the device
> > (the keyboard apparently also allows that):
> > http://support.apple.com/kb/TA27118?viewlocale=en_US
> >
> > Anyone with an idea? An unparsed HID event? Does it need "poking"?
>
> I think that Apple ones used an actually HID HUT conform descriptor for
> that. I did reverser engineered that a long time ago, but then never got
> around to do anything with it. Can't find my notes right now. Just run
> hcidump and eventually you will see the report.

I have a mighty mouse and while I don't use BlueZ or Linux I would be
interested in the details of this.. On my (NetBSD) system, the HID
descriptor parses as:

Collection page=Generic_Desktop usage=Mouse
Input id=2 size=1 count=1 page=Button usage=Button_1 Variable, logical range 0..1
Input id=2 size=1 count=1 page=Button usage=Button_2 Variable, logical range 0..1
Input id=2 size=1 count=1 page=Button usage=Button_3 Variable, logical range 0..1
Input id=2 size=1 count=1 page=Button usage=Button_4 Variable, logical range 0..1
Input id=2 size=4 count=1 page=0x0000 usage=0x0000 Const Variable, logical range 0..1
Collection page=Generic_Desktop usage=Pointer
Input id=2 size=8 count=1 page=Generic_Desktop usage=X Variable Relative, logical range -127..127
Input id=2 size=8 count=1 page=Generic_Desktop usage=Y Variable Relative, logical range -127..127
Input id=2 size=8 count=1 page=Consumer usage=AC_Pan Variable Relative, logical range -127..127
Input id=2 size=8 count=1 page=Generic_Desktop usage=Wheel Variable Relative, logical range -127..127
End collection
Input id=2 size=8 count=1 page=0x00ff usage=0x00c0 Variable, logical range -127..127
Feature id=71 size=8 count=1 page=Device_Controls usage=Battery_Strength Variable NoPref Volatile, logical range 0..100
End collection

But I'm not really sure what the Device_Controls feature is supposed to
be. Is it sent from the device, or sent to the device? I've never had one
sent from the device in any case that I know of, and I'm not sure what the
Vendor_Defined (page 0x00ff) value (usage 0x00c0) is for either, it seems
to be zero.

What I know does happen is that a report with id=48 containing a single
byte with value=0x01 is sent two or three times just before the battery
gives out. That also happens with an older Apple keyboard except that the
descriptor does not contain any Vendor or Feature items. I did capture the
report once but I don't have it now. (I think it was an input report)

iain

2009-06-07 15:52:13

by Marcel Holtmann

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

Hi Bastien,

> I just got an Apple Bluetooth Mighty Mouse, and was wondering whether
> anyone had information on how to get the battery level from the device
> (the keyboard apparently also allows that):
> http://support.apple.com/kb/TA27118?viewlocale=en_US
>
> Anyone with an idea? An unparsed HID event? Does it need "poking"?

I think that Apple ones used an actually HID HUT conform descriptor for
that. I did reverser engineered that a long time ago, but then never got
around to do anything with it. Can't find my notes right now. Just run
hcidump and eventually you will see the report.

Regards

Marcel



2010-02-12 15:41:35

by Bastien Nocera

[permalink] [raw]
Subject: Re: Apple Bluetooth devices: Battery level?

On Tue, 2009-06-16 at 11:59 +0200, Jiri Kosina wrote:
> On Sat, 13 Jun 2009, Iain Hibbert wrote:
>
> > Sorry, I posted a parsed version of the report descriptor previously
> > though I failed to cross post:
> >
> > Collection page=Generic_Desktop usage=Mouse
> > Input id=2 size=1 count=1 page=Button usage=Button_1 Variable, logical range 0..1
> > Input id=2 size=1 count=1 page=Button usage=Button_2 Variable, logical range 0..1
> > Input id=2 size=1 count=1 page=Button usage=Button_3 Variable, logical range 0..1
> > Input id=2 size=1 count=1 page=Button usage=Button_4 Variable, logical range 0..1
> > Input id=2 size=4 count=1 page=0x0000 usage=0x0000 Const Variable, logical range 0..1
> > Collection page=Generic_Desktop usage=Pointer
> > Input id=2 size=8 count=1 page=Generic_Desktop usage=X Variable Relative, logical range -127..127
> > Input id=2 size=8 count=1 page=Generic_Desktop usage=Y Variable Relative, logical range -127..127
> > Input id=2 size=8 count=1 page=Consumer usage=AC_Pan Variable Relative, logical range -127..127
> > Input id=2 size=8 count=1 page=Generic_Desktop usage=Wheel Variable Relative, logical range -127..127
> > End collection
> > Input id=2 size=8 count=1 page=0x00ff usage=0x00c0 Variable, logical range -127..127
> > Feature id=71 size=8 count=1 page=Device_Controls usage=Battery_Strength Variable NoPref Volatile, logical range 0..100
> > End collection
> > As you can see, there is a Feature report relating to the Battery
> > Strength, but I do not know what to do with that. I suppose that it is
> > to be polled in order to return a percentage value..
>
> Yes, it has to be polled. But at least the initial value should be
> retrieved during the very initialization of the device -- both USB and
> Bluetooth implementations this in usbhid_init_reports()/hidp_start() --
> they query INPUT and FEATURE reports during device initialization.

Any hints/example code on how to do that? Or is this something you'd
want to work on?

Cheers