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 | ||
