1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * uvc_debugfs.c -- USB Video Class driver - Debugging support |
4 | * |
5 | * Copyright (C) 2011 |
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/debugfs.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/usb.h> |
13 | |
14 | #include "uvcvideo.h" |
15 | |
16 | /* ----------------------------------------------------------------------------- |
17 | * Statistics |
18 | */ |
19 | |
20 | #define UVC_DEBUGFS_BUF_SIZE 1024 |
21 | |
22 | struct uvc_debugfs_buffer { |
23 | size_t count; |
24 | char data[UVC_DEBUGFS_BUF_SIZE]; |
25 | }; |
26 | |
27 | static int uvc_debugfs_stats_open(struct inode *inode, struct file *file) |
28 | { |
29 | struct uvc_streaming *stream = inode->i_private; |
30 | struct uvc_debugfs_buffer *buf; |
31 | |
32 | buf = kmalloc(size: sizeof(*buf), GFP_KERNEL); |
33 | if (buf == NULL) |
34 | return -ENOMEM; |
35 | |
36 | buf->count = uvc_video_stats_dump(stream, buf: buf->data, size: sizeof(buf->data)); |
37 | |
38 | file->private_data = buf; |
39 | return 0; |
40 | } |
41 | |
42 | static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf, |
43 | size_t nbytes, loff_t *ppos) |
44 | { |
45 | struct uvc_debugfs_buffer *buf = file->private_data; |
46 | |
47 | return simple_read_from_buffer(to: user_buf, count: nbytes, ppos, from: buf->data, |
48 | available: buf->count); |
49 | } |
50 | |
51 | static int uvc_debugfs_stats_release(struct inode *inode, struct file *file) |
52 | { |
53 | kfree(objp: file->private_data); |
54 | file->private_data = NULL; |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static const struct file_operations uvc_debugfs_stats_fops = { |
60 | .owner = THIS_MODULE, |
61 | .open = uvc_debugfs_stats_open, |
62 | .llseek = no_llseek, |
63 | .read = uvc_debugfs_stats_read, |
64 | .release = uvc_debugfs_stats_release, |
65 | }; |
66 | |
67 | /* ----------------------------------------------------------------------------- |
68 | * Global and stream initialization/cleanup |
69 | */ |
70 | |
71 | static struct dentry *uvc_debugfs_root_dir; |
72 | |
73 | void uvc_debugfs_init_stream(struct uvc_streaming *stream) |
74 | { |
75 | struct usb_device *udev = stream->dev->udev; |
76 | char dir_name[33]; |
77 | |
78 | if (uvc_debugfs_root_dir == NULL) |
79 | return; |
80 | |
81 | snprintf(buf: dir_name, size: sizeof(dir_name), fmt: "%u-%u-%u" , udev->bus->busnum, |
82 | udev->devnum, stream->intfnum); |
83 | |
84 | stream->debugfs_dir = debugfs_create_dir(name: dir_name, |
85 | parent: uvc_debugfs_root_dir); |
86 | |
87 | debugfs_create_file(name: "stats" , mode: 0444, parent: stream->debugfs_dir, data: stream, |
88 | fops: &uvc_debugfs_stats_fops); |
89 | } |
90 | |
91 | void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream) |
92 | { |
93 | debugfs_remove_recursive(dentry: stream->debugfs_dir); |
94 | stream->debugfs_dir = NULL; |
95 | } |
96 | |
97 | void uvc_debugfs_init(void) |
98 | { |
99 | uvc_debugfs_root_dir = debugfs_create_dir(name: "uvcvideo" , parent: usb_debug_root); |
100 | } |
101 | |
102 | void uvc_debugfs_cleanup(void) |
103 | { |
104 | debugfs_remove_recursive(dentry: uvc_debugfs_root_dir); |
105 | } |
106 | |