Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 305026
b: refs/heads/master
c: a7197c2
h: refs/heads/master
v: v3
  • Loading branch information
Henrik Rydberg authored and Jiri Kosina committed May 1, 2012
1 parent 0ce7312 commit ee163ac
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 33 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2a039bf5a6caa6b41c3839c1e3a19b20fb38270b
refs/heads/master: a7197c2e4120ce40e7e3f5580336b9a1dc791220
112 changes: 90 additions & 22 deletions trunk/drivers/hid/hid-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,12 +546,11 @@ static void hid_free_report(struct hid_report *report)
}

/*
* Free a device structure, all reports, and all fields.
* Close report. This function returns the device
* state to the point prior to hid_open_report().
*/

static void hid_device_release(struct device *dev)
static void hid_close_report(struct hid_device *device)
{
struct hid_device *device = container_of(dev, struct hid_device, dev);
unsigned i, j;

for (i = 0; i < HID_REPORT_TYPES; i++) {
Expand All @@ -562,11 +561,34 @@ static void hid_device_release(struct device *dev)
if (report)
hid_free_report(report);
}
memset(report_enum, 0, sizeof(*report_enum));
INIT_LIST_HEAD(&report_enum->report_list);
}

kfree(device->rdesc);
device->rdesc = NULL;
device->rsize = 0;

kfree(device->collection);
kfree(device);
device->collection = NULL;
device->collection_size = 0;
device->maxcollection = 0;
device->maxapplication = 0;

device->status &= ~HID_STAT_PARSED;
}

/*
* Free a device structure, all reports, and all fields.
*/

static void hid_device_release(struct device *dev)
{
struct hid_device *hid = container_of(dev, struct hid_device, dev);

hid_close_report(hid);
kfree(hid->dev_rdesc);
kfree(hid);
}

/*
Expand Down Expand Up @@ -643,15 +665,37 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
* @start: report start
* @size: report size
*
* Allocate the device report as read by the bus driver. This function should
* only be called from parse() in ll drivers.
*/
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size)
{
hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL);
if (!hid->dev_rdesc)
return -ENOMEM;
hid->dev_rsize = size;
return 0;
}
EXPORT_SYMBOL_GPL(hid_parse_report);

/**
* hid_open_report - open a driver-specific device report
*
* @device: hid device
*
* Parse a report description into a hid_device structure. Reports are
* enumerated, fields are attached to these reports.
* 0 returned on success, otherwise nonzero error value.
*
* This function (or the equivalent hid_parse() macro) should only be
* called from probe() in drivers, before starting the device.
*/
int hid_parse_report(struct hid_device *device, __u8 *start,
unsigned size)
int hid_open_report(struct hid_device *device)
{
struct hid_parser *parser;
struct hid_item item;
unsigned int size;
__u8 *start;
__u8 *end;
int ret;
static int (*dispatch_type[])(struct hid_parser *parser,
Expand All @@ -662,6 +706,14 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
hid_parser_reserved
};

if (WARN_ON(device->status & HID_STAT_PARSED))
return -EBUSY;

start = device->dev_rdesc;
if (WARN_ON(!start))
return -ENODEV;
size = device->dev_rsize;

if (device->driver->report_fixup)
start = device->driver->report_fixup(device, start, &size);

Expand All @@ -679,6 +731,15 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
parser->device = device;

end = start + size;

device->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS,
sizeof(struct hid_collection), GFP_KERNEL);
if (!device->collection) {
ret = -ENOMEM;
goto err;
}
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;

ret = -EINVAL;
while ((start = fetch_item(start, end, &item)) != NULL) {

Expand All @@ -704,16 +765,18 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
goto err;
}
vfree(parser);
device->status |= HID_STAT_PARSED;
return 0;
}
}

hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
err:
vfree(parser);
hid_close_report(device);
return ret;
}
EXPORT_SYMBOL_GPL(hid_parse_report);
EXPORT_SYMBOL_GPL(hid_open_report);

/*
* Convert a signed n-bit integer to signed 32-bit integer. Common
Expand Down Expand Up @@ -1718,12 +1781,14 @@ static int hid_device_probe(struct device *dev)
if (hdrv->probe) {
ret = hdrv->probe(hdev, id);
} else { /* default probe */
ret = hid_parse(hdev);
ret = hid_open_report(hdev);
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
if (ret)
if (ret) {
hid_close_report(hdev);
hdev->driver = NULL;
}
}
unlock:
up(&hdev->driver_lock);
Expand All @@ -1744,6 +1809,7 @@ static int hid_device_remove(struct device *dev)
hdrv->remove(hdev);
else /* default remove */
hid_hw_stop(hdev);
hid_close_report(hdev);
hdev->driver = NULL;
}

Expand Down Expand Up @@ -2075,6 +2141,16 @@ int hid_add_device(struct hid_device *hdev)
&& (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE)))
return -ENODEV;

/*
* Read the device report descriptor once and use as template
* for the driver-specific modifications.
*/
ret = hdev->ll_driver->parse(hdev);
if (ret)
return ret;
if (!hdev->dev_rdesc)
return -ENODEV;

/* XXX hack, any other cleaner solution after the driver core
* is converted to allow more than 20 bytes as the device name? */
dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
Expand Down Expand Up @@ -2103,7 +2179,6 @@ EXPORT_SYMBOL_GPL(hid_add_device);
struct hid_device *hid_allocate_device(void)
{
struct hid_device *hdev;
unsigned int i;
int ret = -ENOMEM;

hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
Expand All @@ -2114,23 +2189,13 @@ struct hid_device *hid_allocate_device(void)
hdev->dev.release = hid_device_release;
hdev->dev.bus = &hid_bus_type;

hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS,
sizeof(struct hid_collection), GFP_KERNEL);
if (hdev->collection == NULL)
goto err;
hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS;

for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&hdev->report_enum[i].report_list);
hid_close_report(hdev);

init_waitqueue_head(&hdev->debug_wait);
INIT_LIST_HEAD(&hdev->debug_list);
sema_init(&hdev->driver_lock, 1);

return hdev;
err:
put_device(&hdev->dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(hid_allocate_device);

Expand All @@ -2141,6 +2206,9 @@ static void hid_remove_device(struct hid_device *hdev)
hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED;
}
kfree(hdev->dev_rdesc);
hdev->dev_rdesc = NULL;
hdev->dev_rsize = 0;
}

/**
Expand Down
14 changes: 4 additions & 10 deletions trunk/include/linux/hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ struct hid_driver;
struct hid_ll_driver;

struct hid_device { /* device report descriptor */
__u8 *dev_rdesc;
unsigned dev_rsize;
__u8 *rdesc;
unsigned rsize;
struct hid_collection *collection; /* List of HID collections */
Expand Down Expand Up @@ -735,6 +737,7 @@ void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
int hid_open_report(struct hid_device *device);
int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
void hid_disconnect(struct hid_device *hid);
Expand Down Expand Up @@ -805,16 +808,7 @@ static inline void hid_map_usage_clear(struct hid_input *hidinput,
*/
static inline int __must_check hid_parse(struct hid_device *hdev)
{
int ret;

if (hdev->status & HID_STAT_PARSED)
return 0;

ret = hdev->ll_driver->parse(hdev);
if (!ret)
hdev->status |= HID_STAT_PARSED;

return ret;
return hid_open_report(hdev);
}

/**
Expand Down

0 comments on commit ee163ac

Please sign in to comment.