162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *      uvc_debugfs.c --  USB Video Class driver - Debugging support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *      Copyright (C) 2011
662306a36Sopenharmony_ci *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/debugfs.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/usb.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "uvcvideo.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
1762306a36Sopenharmony_ci * Statistics
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define UVC_DEBUGFS_BUF_SIZE	1024
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct uvc_debugfs_buffer {
2362306a36Sopenharmony_ci	size_t count;
2462306a36Sopenharmony_ci	char data[UVC_DEBUGFS_BUF_SIZE];
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int uvc_debugfs_stats_open(struct inode *inode, struct file *file)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct uvc_streaming *stream = inode->i_private;
3062306a36Sopenharmony_ci	struct uvc_debugfs_buffer *buf;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
3362306a36Sopenharmony_ci	if (buf == NULL)
3462306a36Sopenharmony_ci		return -ENOMEM;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data));
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	file->private_data = buf;
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf,
4362306a36Sopenharmony_ci				      size_t nbytes, loff_t *ppos)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct uvc_debugfs_buffer *buf = file->private_data;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data,
4862306a36Sopenharmony_ci				       buf->count);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int uvc_debugfs_stats_release(struct inode *inode, struct file *file)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	kfree(file->private_data);
5462306a36Sopenharmony_ci	file->private_data = NULL;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return 0;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const struct file_operations uvc_debugfs_stats_fops = {
6062306a36Sopenharmony_ci	.owner = THIS_MODULE,
6162306a36Sopenharmony_ci	.open = uvc_debugfs_stats_open,
6262306a36Sopenharmony_ci	.llseek = no_llseek,
6362306a36Sopenharmony_ci	.read = uvc_debugfs_stats_read,
6462306a36Sopenharmony_ci	.release = uvc_debugfs_stats_release,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
6862306a36Sopenharmony_ci * Global and stream initialization/cleanup
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct dentry *uvc_debugfs_root_dir;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_civoid uvc_debugfs_init_stream(struct uvc_streaming *stream)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct usb_device *udev = stream->dev->udev;
7662306a36Sopenharmony_ci	char dir_name[33];
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (uvc_debugfs_root_dir == NULL)
7962306a36Sopenharmony_ci		return;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	snprintf(dir_name, sizeof(dir_name), "%u-%u-%u", udev->bus->busnum,
8262306a36Sopenharmony_ci		 udev->devnum, stream->intfnum);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	stream->debugfs_dir = debugfs_create_dir(dir_name,
8562306a36Sopenharmony_ci						 uvc_debugfs_root_dir);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	debugfs_create_file("stats", 0444, stream->debugfs_dir, stream,
8862306a36Sopenharmony_ci			    &uvc_debugfs_stats_fops);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_civoid uvc_debugfs_cleanup_stream(struct uvc_streaming *stream)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	debugfs_remove_recursive(stream->debugfs_dir);
9462306a36Sopenharmony_ci	stream->debugfs_dir = NULL;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_civoid uvc_debugfs_init(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	uvc_debugfs_root_dir = debugfs_create_dir("uvcvideo", usb_debug_root);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid uvc_debugfs_cleanup(void)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	debugfs_remove_recursive(uvc_debugfs_root_dir);
10562306a36Sopenharmony_ci}
106