diff options
Diffstat (limited to 'drivers/hid/hid-debug.c')
-rw-r--r-- | drivers/hid/hid-debug.c | 239 |
1 files changed, 220 insertions, 19 deletions
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 067e173aa3e4..a331a1821e85 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c | |||
@@ -28,6 +28,10 @@ | |||
28 | 28 | ||
29 | #include <linux/debugfs.h> | 29 | #include <linux/debugfs.h> |
30 | #include <linux/seq_file.h> | 30 | #include <linux/seq_file.h> |
31 | #include <linux/sched.h> | ||
32 | #include <linux/uaccess.h> | ||
33 | #include <linux/poll.h> | ||
34 | |||
31 | #include <linux/hid.h> | 35 | #include <linux/hid.h> |
32 | #include <linux/hid-debug.h> | 36 | #include <linux/hid-debug.h> |
33 | 37 | ||
@@ -335,49 +339,86 @@ static const struct hid_usage_entry hid_usage_table[] = { | |||
335 | { 0, 0, NULL } | 339 | { 0, 0, NULL } |
336 | }; | 340 | }; |
337 | 341 | ||
338 | static void resolv_usage_page(unsigned page, struct seq_file *f) { | 342 | /* Either output directly into simple seq_file, or (if f == NULL) |
343 | * allocate a separate buffer that will then be passed to the 'events' | ||
344 | * ringbuffer. | ||
345 | * | ||
346 | * This is because these functions can be called both for "one-shot" | ||
347 | * "rdesc" while resolving, or for blocking "events". | ||
348 | * | ||
349 | * This holds both for resolv_usage_page() and hid_resolv_usage(). | ||
350 | */ | ||
351 | static char *resolv_usage_page(unsigned page, struct seq_file *f) { | ||
339 | const struct hid_usage_entry *p; | 352 | const struct hid_usage_entry *p; |
353 | char *buf = NULL; | ||
354 | |||
355 | if (!f) { | ||
356 | buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); | ||
357 | if (!buf) | ||
358 | return ERR_PTR(-ENOMEM); | ||
359 | } | ||
340 | 360 | ||
341 | for (p = hid_usage_table; p->description; p++) | 361 | for (p = hid_usage_table; p->description; p++) |
342 | if (p->page == page) { | 362 | if (p->page == page) { |
343 | if (!f) | 363 | if (!f) { |
344 | printk("%s", p->description); | 364 | snprintf(buf, HID_DEBUG_BUFSIZE, "%s", |
345 | else | 365 | p->description); |
366 | return buf; | ||
367 | } | ||
368 | else { | ||
346 | seq_printf(f, "%s", p->description); | 369 | seq_printf(f, "%s", p->description); |
347 | return; | 370 | return NULL; |
371 | } | ||
348 | } | 372 | } |
349 | if (!f) | 373 | if (!f) |
350 | printk("%04x", page); | 374 | snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page); |
351 | else | 375 | else |
352 | seq_printf(f, "%04x", page); | 376 | seq_printf(f, "%04x", page); |
377 | return buf; | ||
353 | } | 378 | } |
354 | 379 | ||
355 | void hid_resolv_usage(unsigned usage, struct seq_file *f) { | 380 | char *hid_resolv_usage(unsigned usage, struct seq_file *f) { |
356 | const struct hid_usage_entry *p; | 381 | const struct hid_usage_entry *p; |
382 | char *buf = NULL; | ||
383 | int len = 0; | ||
357 | 384 | ||
358 | resolv_usage_page(usage >> 16, f); | 385 | buf = resolv_usage_page(usage >> 16, f); |
359 | if (!f) | 386 | if (IS_ERR(buf)) { |
360 | printk("."); | 387 | printk(KERN_ERR "error allocating HID debug buffer\n"); |
361 | else | 388 | return NULL; |
389 | } | ||
390 | |||
391 | |||
392 | if (!f) { | ||
393 | len = strlen(buf); | ||
394 | snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), "."); | ||
395 | len++; | ||
396 | } | ||
397 | else { | ||
362 | seq_printf(f, "."); | 398 | seq_printf(f, "."); |
399 | } | ||
363 | for (p = hid_usage_table; p->description; p++) | 400 | for (p = hid_usage_table; p->description; p++) |
364 | if (p->page == (usage >> 16)) { | 401 | if (p->page == (usage >> 16)) { |
365 | for(++p; p->description && p->usage != 0; p++) | 402 | for(++p; p->description && p->usage != 0; p++) |
366 | if (p->usage == (usage & 0xffff)) { | 403 | if (p->usage == (usage & 0xffff)) { |
367 | if (!f) | 404 | if (!f) |
368 | printk("%s", p->description); | 405 | snprintf(buf + len, |
406 | max(0,HID_DEBUG_BUFSIZE - len - 1), | ||
407 | "%s", p->description); | ||
369 | else | 408 | else |
370 | seq_printf(f, | 409 | seq_printf(f, |
371 | "%s", | 410 | "%s", |
372 | p->description); | 411 | p->description); |
373 | return; | 412 | return buf; |
374 | } | 413 | } |
375 | break; | 414 | break; |
376 | } | 415 | } |
377 | if (!f) | 416 | if (!f) |
378 | printk("%04x", usage & 0xffff); | 417 | snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1), |
418 | "%04x", usage & 0xffff); | ||
379 | else | 419 | else |
380 | seq_printf(f, "%04x", usage & 0xffff); | 420 | seq_printf(f, "%04x", usage & 0xffff); |
421 | return buf; | ||
381 | } | 422 | } |
382 | EXPORT_SYMBOL_GPL(hid_resolv_usage); | 423 | EXPORT_SYMBOL_GPL(hid_resolv_usage); |
383 | 424 | ||
@@ -508,13 +549,37 @@ void hid_dump_device(struct hid_device *device, struct seq_file *f) | |||
508 | } | 549 | } |
509 | EXPORT_SYMBOL_GPL(hid_dump_device); | 550 | EXPORT_SYMBOL_GPL(hid_dump_device); |
510 | 551 | ||
511 | void hid_dump_input(struct hid_usage *usage, __s32 value) { | 552 | /* enqueue string to 'events' ring buffer */ |
512 | if (hid_debug < 2) | 553 | void hid_debug_event(struct hid_device *hdev, char *buf) |
554 | { | ||
555 | int i; | ||
556 | struct hid_debug_list *list; | ||
557 | |||
558 | list_for_each_entry(list, &hdev->debug_list, node) { | ||
559 | for (i = 0; i <= strlen(buf); i++) | ||
560 | list->hid_debug_buf[(list->tail + i) % (HID_DEBUG_BUFSIZE - 1)] = | ||
561 | buf[i]; | ||
562 | list->tail = (list->tail + i) % (HID_DEBUG_BUFSIZE - 1); | ||
563 | } | ||
564 | } | ||
565 | EXPORT_SYMBOL_GPL(hid_debug_event); | ||
566 | |||
567 | void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) | ||
568 | { | ||
569 | char *buf; | ||
570 | int len; | ||
571 | |||
572 | buf = hid_resolv_usage(usage->hid, NULL); | ||
573 | if (!buf) | ||
513 | return; | 574 | return; |
575 | len = strlen(buf); | ||
576 | snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value); | ||
577 | |||
578 | hid_debug_event(hdev, buf); | ||
579 | |||
580 | kfree(buf); | ||
581 | wake_up_interruptible(&hdev->debug_wait); | ||
514 | 582 | ||
515 | printk(KERN_DEBUG "hid-debug: input "); | ||
516 | hid_resolv_usage(usage->hid, NULL); | ||
517 | printk(" = %d\n", value); | ||
518 | } | 583 | } |
519 | EXPORT_SYMBOL_GPL(hid_dump_input); | 584 | EXPORT_SYMBOL_GPL(hid_dump_input); |
520 | 585 | ||
@@ -808,6 +873,7 @@ void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) | |||
808 | 873 | ||
809 | } | 874 | } |
810 | 875 | ||
876 | |||
811 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) | 877 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) |
812 | { | 878 | { |
813 | struct hid_device *hdev = f->private; | 879 | struct hid_device *hdev = f->private; |
@@ -831,6 +897,126 @@ static int hid_debug_rdesc_open(struct inode *inode, struct file *file) | |||
831 | return single_open(file, hid_debug_rdesc_show, inode->i_private); | 897 | return single_open(file, hid_debug_rdesc_show, inode->i_private); |
832 | } | 898 | } |
833 | 899 | ||
900 | static int hid_debug_events_open(struct inode *inode, struct file *file) | ||
901 | { | ||
902 | int err = 0; | ||
903 | struct hid_debug_list *list; | ||
904 | |||
905 | if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) { | ||
906 | err = -ENOMEM; | ||
907 | goto out; | ||
908 | } | ||
909 | |||
910 | if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { | ||
911 | err = -ENOMEM; | ||
912 | goto out; | ||
913 | } | ||
914 | list->hdev = (struct hid_device *) inode->i_private; | ||
915 | file->private_data = list; | ||
916 | mutex_init(&list->read_mutex); | ||
917 | |||
918 | list_add_tail(&list->node, &list->hdev->debug_list); | ||
919 | |||
920 | out: | ||
921 | return err; | ||
922 | } | ||
923 | |||
924 | static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, | ||
925 | size_t count, loff_t *ppos) | ||
926 | { | ||
927 | struct hid_debug_list *list = file->private_data; | ||
928 | int ret = 0, len; | ||
929 | DECLARE_WAITQUEUE(wait, current); | ||
930 | |||
931 | while (ret == 0) { | ||
932 | mutex_lock(&list->read_mutex); | ||
933 | if (list->head == list->tail) { | ||
934 | add_wait_queue(&list->hdev->debug_wait, &wait); | ||
935 | set_current_state(TASK_INTERRUPTIBLE); | ||
936 | |||
937 | while (list->head == list->tail) { | ||
938 | if (file->f_flags & O_NONBLOCK) { | ||
939 | ret = -EAGAIN; | ||
940 | break; | ||
941 | } | ||
942 | if (signal_pending(current)) { | ||
943 | ret = -ERESTARTSYS; | ||
944 | break; | ||
945 | } | ||
946 | |||
947 | if (!list->hdev || !list->hdev->debug) { | ||
948 | ret = -EIO; | ||
949 | break; | ||
950 | } | ||
951 | |||
952 | /* allow O_NONBLOCK from other threads */ | ||
953 | mutex_unlock(&list->read_mutex); | ||
954 | schedule(); | ||
955 | mutex_lock(&list->read_mutex); | ||
956 | set_current_state(TASK_INTERRUPTIBLE); | ||
957 | } | ||
958 | |||
959 | set_current_state(TASK_RUNNING); | ||
960 | remove_wait_queue(&list->hdev->debug_wait, &wait); | ||
961 | } | ||
962 | |||
963 | if (ret) | ||
964 | goto out; | ||
965 | |||
966 | /* pass the ringbuffer contents to userspace */ | ||
967 | copy_rest: | ||
968 | if (list->tail == list->head) | ||
969 | goto out; | ||
970 | if (list->tail > list->head) { | ||
971 | len = list->tail - list->head; | ||
972 | |||
973 | if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { | ||
974 | ret = -EFAULT; | ||
975 | goto out; | ||
976 | } | ||
977 | ret += len; | ||
978 | list->head += len; | ||
979 | } else { | ||
980 | len = HID_DEBUG_BUFSIZE - list->head; | ||
981 | |||
982 | if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { | ||
983 | ret = -EFAULT; | ||
984 | goto out; | ||
985 | } | ||
986 | list->head = 0; | ||
987 | ret += len; | ||
988 | goto copy_rest; | ||
989 | } | ||
990 | |||
991 | } | ||
992 | out: | ||
993 | mutex_unlock(&list->read_mutex); | ||
994 | return ret; | ||
995 | } | ||
996 | |||
997 | static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait) | ||
998 | { | ||
999 | struct hid_debug_list *list = file->private_data; | ||
1000 | |||
1001 | poll_wait(file, &list->hdev->debug_wait, wait); | ||
1002 | if (list->head != list->tail) | ||
1003 | return POLLIN | POLLRDNORM; | ||
1004 | if (!list->hdev->debug) | ||
1005 | return POLLERR | POLLHUP; | ||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | static int hid_debug_events_release(struct inode *inode, struct file *file) | ||
1010 | { | ||
1011 | struct hid_debug_list *list = file->private_data; | ||
1012 | |||
1013 | list_del(&list->node); | ||
1014 | kfree(list->hid_debug_buf); | ||
1015 | kfree(list); | ||
1016 | |||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
834 | static const struct file_operations hid_debug_rdesc_fops = { | 1020 | static const struct file_operations hid_debug_rdesc_fops = { |
835 | .open = hid_debug_rdesc_open, | 1021 | .open = hid_debug_rdesc_open, |
836 | .read = seq_read, | 1022 | .read = seq_read, |
@@ -838,16 +1024,31 @@ static const struct file_operations hid_debug_rdesc_fops = { | |||
838 | .release = single_release, | 1024 | .release = single_release, |
839 | }; | 1025 | }; |
840 | 1026 | ||
1027 | static const struct file_operations hid_debug_events_fops = { | ||
1028 | .owner = THIS_MODULE, | ||
1029 | .open = hid_debug_events_open, | ||
1030 | .read = hid_debug_events_read, | ||
1031 | .poll = hid_debug_events_poll, | ||
1032 | .release = hid_debug_events_release, | ||
1033 | }; | ||
1034 | |||
1035 | |||
841 | void hid_debug_register(struct hid_device *hdev, const char *name) | 1036 | void hid_debug_register(struct hid_device *hdev, const char *name) |
842 | { | 1037 | { |
843 | hdev->debug_dir = debugfs_create_dir(name, hid_debug_root); | 1038 | hdev->debug_dir = debugfs_create_dir(name, hid_debug_root); |
844 | hdev->debug_rdesc = debugfs_create_file("rdesc", 0400, | 1039 | hdev->debug_rdesc = debugfs_create_file("rdesc", 0400, |
845 | hdev->debug_dir, hdev, &hid_debug_rdesc_fops); | 1040 | hdev->debug_dir, hdev, &hid_debug_rdesc_fops); |
1041 | hdev->debug_events = debugfs_create_file("events", 0400, | ||
1042 | hdev->debug_dir, hdev, &hid_debug_events_fops); | ||
1043 | hdev->debug = 1; | ||
846 | } | 1044 | } |
847 | 1045 | ||
848 | void hid_debug_unregister(struct hid_device *hdev) | 1046 | void hid_debug_unregister(struct hid_device *hdev) |
849 | { | 1047 | { |
1048 | hdev->debug = 0; | ||
1049 | wake_up_interruptible(&hdev->debug_wait); | ||
850 | debugfs_remove(hdev->debug_rdesc); | 1050 | debugfs_remove(hdev->debug_rdesc); |
1051 | debugfs_remove(hdev->debug_events); | ||
851 | debugfs_remove(hdev->debug_dir); | 1052 | debugfs_remove(hdev->debug_dir); |
852 | } | 1053 | } |
853 | 1054 | ||