18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * The USB Monitor, inspired by Dave Harding's USBMon.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This is a text format reader.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/list.h>
108c2ecf20Sopenharmony_ci#include <linux/usb.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
138c2ecf20Sopenharmony_ci#include <linux/time.h>
148c2ecf20Sopenharmony_ci#include <linux/ktime.h>
158c2ecf20Sopenharmony_ci#include <linux/export.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
188c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "usb_mon.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * No, we do not want arbitrarily long data strings.
258c2ecf20Sopenharmony_ci * Use the binary interface if you want to capture bulk data!
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#define DATA_MAX  32
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Defined by USB 2.0 clause 9.3, table 9.2.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci#define SETUP_MAX  8
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * This limit exists to prevent OOMs when the user process stops reading.
368c2ecf20Sopenharmony_ci * If usbmon were available to unprivileged processes, it might be open
378c2ecf20Sopenharmony_ci * to a local DoS. But we have to keep to root in order to prevent
388c2ecf20Sopenharmony_ci * password sniffing from HID devices.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci#define EVENT_MAX  (4*PAGE_SIZE / sizeof(struct mon_event_text))
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Potentially unlimited number; we limit it for similar allocations.
448c2ecf20Sopenharmony_ci * The usbfs limits this to 128, but we're not quite as generous.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci#define ISODESC_MAX   5
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define PRINTF_DFL  250   /* with 5 ISOs segs */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct mon_iso_desc {
518c2ecf20Sopenharmony_ci	int status;
528c2ecf20Sopenharmony_ci	unsigned int offset;
538c2ecf20Sopenharmony_ci	unsigned int length;	/* Unsigned here, signed in URB. Historic. */
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct mon_event_text {
578c2ecf20Sopenharmony_ci	struct list_head e_link;
588c2ecf20Sopenharmony_ci	int type;		/* submit, complete, etc. */
598c2ecf20Sopenharmony_ci	unsigned long id;	/* From pointer, most of the time */
608c2ecf20Sopenharmony_ci	unsigned int tstamp;
618c2ecf20Sopenharmony_ci	int busnum;
628c2ecf20Sopenharmony_ci	char devnum;
638c2ecf20Sopenharmony_ci	char epnum;
648c2ecf20Sopenharmony_ci	char is_in;
658c2ecf20Sopenharmony_ci	char xfertype;
668c2ecf20Sopenharmony_ci	int length;		/* Depends on type: xfer length or act length */
678c2ecf20Sopenharmony_ci	int status;
688c2ecf20Sopenharmony_ci	int interval;
698c2ecf20Sopenharmony_ci	int start_frame;
708c2ecf20Sopenharmony_ci	int error_count;
718c2ecf20Sopenharmony_ci	char setup_flag;
728c2ecf20Sopenharmony_ci	char data_flag;
738c2ecf20Sopenharmony_ci	int numdesc;		/* Full number */
748c2ecf20Sopenharmony_ci	struct mon_iso_desc isodesc[ISODESC_MAX];
758c2ecf20Sopenharmony_ci	unsigned char setup[SETUP_MAX];
768c2ecf20Sopenharmony_ci	unsigned char data[DATA_MAX];
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define SLAB_NAME_SZ  30
808c2ecf20Sopenharmony_cistruct mon_reader_text {
818c2ecf20Sopenharmony_ci	struct kmem_cache *e_slab;
828c2ecf20Sopenharmony_ci	int nevents;
838c2ecf20Sopenharmony_ci	struct list_head e_list;
848c2ecf20Sopenharmony_ci	struct mon_reader r;	/* In C, parent class can be placed anywhere */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	wait_queue_head_t wait;
878c2ecf20Sopenharmony_ci	int printf_size;
888c2ecf20Sopenharmony_ci	size_t printf_offset;
898c2ecf20Sopenharmony_ci	size_t printf_togo;
908c2ecf20Sopenharmony_ci	char *printf_buf;
918c2ecf20Sopenharmony_ci	struct mutex printf_lock;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	char slab_name[SLAB_NAME_SZ];
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic struct dentry *mon_dir;		/* Usually /sys/kernel/debug/usbmon */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void mon_text_ctor(void *);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistruct mon_text_ptr {
1018c2ecf20Sopenharmony_ci	int cnt, limit;
1028c2ecf20Sopenharmony_ci	char *pbuf;
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic struct mon_event_text *
1068c2ecf20Sopenharmony_ci    mon_text_read_wait(struct mon_reader_text *rp, struct file *file);
1078c2ecf20Sopenharmony_cistatic void mon_text_read_head_t(struct mon_reader_text *rp,
1088c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
1098c2ecf20Sopenharmony_cistatic void mon_text_read_head_u(struct mon_reader_text *rp,
1108c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
1118c2ecf20Sopenharmony_cistatic void mon_text_read_statset(struct mon_reader_text *rp,
1128c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
1138c2ecf20Sopenharmony_cistatic void mon_text_read_intstat(struct mon_reader_text *rp,
1148c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
1158c2ecf20Sopenharmony_cistatic void mon_text_read_isostat(struct mon_reader_text *rp,
1168c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
1178c2ecf20Sopenharmony_cistatic void mon_text_read_isodesc(struct mon_reader_text *rp,
1188c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
1198c2ecf20Sopenharmony_cistatic void mon_text_read_data(struct mon_reader_text *rp,
1208c2ecf20Sopenharmony_ci    struct mon_text_ptr *p, const struct mon_event_text *ep);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/*
1238c2ecf20Sopenharmony_ci * mon_text_submit
1248c2ecf20Sopenharmony_ci * mon_text_complete
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * May be called from an interrupt.
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * This is called with the whole mon_bus locked, so no additional lock.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline char mon_text_get_setup(struct mon_event_text *ep,
1328c2ecf20Sopenharmony_ci    struct urb *urb, char ev_type, struct mon_bus *mbus)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (ep->xfertype != USB_ENDPOINT_XFER_CONTROL || ev_type != 'S')
1368c2ecf20Sopenharmony_ci		return '-';
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (urb->setup_packet == NULL)
1398c2ecf20Sopenharmony_ci		return 'Z';	/* '0' would be not as pretty. */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	memcpy(ep->setup, urb->setup_packet, SETUP_MAX);
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
1468c2ecf20Sopenharmony_ci    int len, char ev_type, struct mon_bus *mbus)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	void *src;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (len <= 0)
1518c2ecf20Sopenharmony_ci		return 'L';
1528c2ecf20Sopenharmony_ci	if (len >= DATA_MAX)
1538c2ecf20Sopenharmony_ci		len = DATA_MAX;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (ep->is_in) {
1568c2ecf20Sopenharmony_ci		if (ev_type != 'C')
1578c2ecf20Sopenharmony_ci			return '<';
1588c2ecf20Sopenharmony_ci	} else {
1598c2ecf20Sopenharmony_ci		if (ev_type != 'S')
1608c2ecf20Sopenharmony_ci			return '>';
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (urb->num_sgs == 0) {
1648c2ecf20Sopenharmony_ci		src = urb->transfer_buffer;
1658c2ecf20Sopenharmony_ci		if (src == NULL)
1668c2ecf20Sopenharmony_ci			return 'Z';	/* '0' would be not as pretty. */
1678c2ecf20Sopenharmony_ci	} else {
1688c2ecf20Sopenharmony_ci		struct scatterlist *sg = urb->sg;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		if (PageHighMem(sg_page(sg)))
1718c2ecf20Sopenharmony_ci			return 'D';
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		/* For the text interface we copy only the first sg buffer */
1748c2ecf20Sopenharmony_ci		len = min_t(int, sg->length, len);
1758c2ecf20Sopenharmony_ci		src = sg_virt(sg);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	memcpy(ep->data, src, len);
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic inline unsigned int mon_get_timestamp(void)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct timespec64 now;
1858c2ecf20Sopenharmony_ci	unsigned int stamp;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ktime_get_ts64(&now);
1888c2ecf20Sopenharmony_ci	stamp = now.tv_sec & 0xFFF;  /* 2^32 = 4294967296. Limit to 4096s. */
1898c2ecf20Sopenharmony_ci	stamp = stamp * USEC_PER_SEC + now.tv_nsec / NSEC_PER_USEC;
1908c2ecf20Sopenharmony_ci	return stamp;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
1948c2ecf20Sopenharmony_ci    char ev_type, int status)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct mon_event_text *ep;
1978c2ecf20Sopenharmony_ci	unsigned int stamp;
1988c2ecf20Sopenharmony_ci	struct usb_iso_packet_descriptor *fp;
1998c2ecf20Sopenharmony_ci	struct mon_iso_desc *dp;
2008c2ecf20Sopenharmony_ci	int i, ndesc;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	stamp = mon_get_timestamp();
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (rp->nevents >= EVENT_MAX ||
2058c2ecf20Sopenharmony_ci	    (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) {
2068c2ecf20Sopenharmony_ci		rp->r.m_bus->cnt_text_lost++;
2078c2ecf20Sopenharmony_ci		return;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	ep->type = ev_type;
2118c2ecf20Sopenharmony_ci	ep->id = (unsigned long) urb;
2128c2ecf20Sopenharmony_ci	ep->busnum = urb->dev->bus->busnum;
2138c2ecf20Sopenharmony_ci	ep->devnum = urb->dev->devnum;
2148c2ecf20Sopenharmony_ci	ep->epnum = usb_endpoint_num(&urb->ep->desc);
2158c2ecf20Sopenharmony_ci	ep->xfertype = usb_endpoint_type(&urb->ep->desc);
2168c2ecf20Sopenharmony_ci	ep->is_in = usb_urb_dir_in(urb);
2178c2ecf20Sopenharmony_ci	ep->tstamp = stamp;
2188c2ecf20Sopenharmony_ci	ep->length = (ev_type == 'S') ?
2198c2ecf20Sopenharmony_ci	    urb->transfer_buffer_length : urb->actual_length;
2208c2ecf20Sopenharmony_ci	/* Collecting status makes debugging sense for submits, too */
2218c2ecf20Sopenharmony_ci	ep->status = status;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
2248c2ecf20Sopenharmony_ci		ep->interval = urb->interval;
2258c2ecf20Sopenharmony_ci	} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
2268c2ecf20Sopenharmony_ci		ep->interval = urb->interval;
2278c2ecf20Sopenharmony_ci		ep->start_frame = urb->start_frame;
2288c2ecf20Sopenharmony_ci		ep->error_count = urb->error_count;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	ep->numdesc = urb->number_of_packets;
2318c2ecf20Sopenharmony_ci	if (ep->xfertype == USB_ENDPOINT_XFER_ISOC &&
2328c2ecf20Sopenharmony_ci			urb->number_of_packets > 0) {
2338c2ecf20Sopenharmony_ci		if ((ndesc = urb->number_of_packets) > ISODESC_MAX)
2348c2ecf20Sopenharmony_ci			ndesc = ISODESC_MAX;
2358c2ecf20Sopenharmony_ci		fp = urb->iso_frame_desc;
2368c2ecf20Sopenharmony_ci		dp = ep->isodesc;
2378c2ecf20Sopenharmony_ci		for (i = 0; i < ndesc; i++) {
2388c2ecf20Sopenharmony_ci			dp->status = fp->status;
2398c2ecf20Sopenharmony_ci			dp->offset = fp->offset;
2408c2ecf20Sopenharmony_ci			dp->length = (ev_type == 'S') ?
2418c2ecf20Sopenharmony_ci			    fp->length : fp->actual_length;
2428c2ecf20Sopenharmony_ci			fp++;
2438c2ecf20Sopenharmony_ci			dp++;
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci		/* Wasteful, but simple to understand: ISO 'C' is sparse. */
2468c2ecf20Sopenharmony_ci		if (ev_type == 'C')
2478c2ecf20Sopenharmony_ci			ep->length = urb->transfer_buffer_length;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
2518c2ecf20Sopenharmony_ci	ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
2528c2ecf20Sopenharmony_ci			rp->r.m_bus);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	rp->nevents++;
2558c2ecf20Sopenharmony_ci	list_add_tail(&ep->e_link, &rp->e_list);
2568c2ecf20Sopenharmony_ci	wake_up(&rp->wait);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void mon_text_submit(void *data, struct urb *urb)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct mon_reader_text *rp = data;
2628c2ecf20Sopenharmony_ci	mon_text_event(rp, urb, 'S', -EINPROGRESS);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void mon_text_complete(void *data, struct urb *urb, int status)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct mon_reader_text *rp = data;
2688c2ecf20Sopenharmony_ci	mon_text_event(rp, urb, 'C', status);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic void mon_text_error(void *data, struct urb *urb, int error)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct mon_reader_text *rp = data;
2748c2ecf20Sopenharmony_ci	struct mon_event_text *ep;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (rp->nevents >= EVENT_MAX ||
2778c2ecf20Sopenharmony_ci	    (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) {
2788c2ecf20Sopenharmony_ci		rp->r.m_bus->cnt_text_lost++;
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ep->type = 'E';
2838c2ecf20Sopenharmony_ci	ep->id = (unsigned long) urb;
2848c2ecf20Sopenharmony_ci	ep->busnum = urb->dev->bus->busnum;
2858c2ecf20Sopenharmony_ci	ep->devnum = urb->dev->devnum;
2868c2ecf20Sopenharmony_ci	ep->epnum = usb_endpoint_num(&urb->ep->desc);
2878c2ecf20Sopenharmony_ci	ep->xfertype = usb_endpoint_type(&urb->ep->desc);
2888c2ecf20Sopenharmony_ci	ep->is_in = usb_urb_dir_in(urb);
2898c2ecf20Sopenharmony_ci	ep->tstamp = mon_get_timestamp();
2908c2ecf20Sopenharmony_ci	ep->length = 0;
2918c2ecf20Sopenharmony_ci	ep->status = error;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	ep->setup_flag = '-';
2948c2ecf20Sopenharmony_ci	ep->data_flag = 'E';
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	rp->nevents++;
2978c2ecf20Sopenharmony_ci	list_add_tail(&ep->e_link, &rp->e_list);
2988c2ecf20Sopenharmony_ci	wake_up(&rp->wait);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/*
3028c2ecf20Sopenharmony_ci * Fetch next event from the circular buffer.
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_cistatic struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
3058c2ecf20Sopenharmony_ci    struct mon_bus *mbus)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct list_head *p;
3088c2ecf20Sopenharmony_ci	unsigned long flags;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mbus->lock, flags);
3118c2ecf20Sopenharmony_ci	if (list_empty(&rp->e_list)) {
3128c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mbus->lock, flags);
3138c2ecf20Sopenharmony_ci		return NULL;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	p = rp->e_list.next;
3168c2ecf20Sopenharmony_ci	list_del(p);
3178c2ecf20Sopenharmony_ci	--rp->nevents;
3188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mbus->lock, flags);
3198c2ecf20Sopenharmony_ci	return list_entry(p, struct mon_event_text, e_link);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/*
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cistatic int mon_text_open(struct inode *inode, struct file *file)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct mon_bus *mbus;
3278c2ecf20Sopenharmony_ci	struct mon_reader_text *rp;
3288c2ecf20Sopenharmony_ci	int rc;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	mutex_lock(&mon_lock);
3318c2ecf20Sopenharmony_ci	mbus = inode->i_private;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
3348c2ecf20Sopenharmony_ci	if (rp == NULL) {
3358c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3368c2ecf20Sopenharmony_ci		goto err_alloc;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rp->e_list);
3398c2ecf20Sopenharmony_ci	init_waitqueue_head(&rp->wait);
3408c2ecf20Sopenharmony_ci	mutex_init(&rp->printf_lock);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	rp->printf_size = PRINTF_DFL;
3438c2ecf20Sopenharmony_ci	rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
3448c2ecf20Sopenharmony_ci	if (rp->printf_buf == NULL) {
3458c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3468c2ecf20Sopenharmony_ci		goto err_alloc_pr;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	rp->r.m_bus = mbus;
3508c2ecf20Sopenharmony_ci	rp->r.r_data = rp;
3518c2ecf20Sopenharmony_ci	rp->r.rnf_submit = mon_text_submit;
3528c2ecf20Sopenharmony_ci	rp->r.rnf_error = mon_text_error;
3538c2ecf20Sopenharmony_ci	rp->r.rnf_complete = mon_text_complete;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp);
3568c2ecf20Sopenharmony_ci	rp->e_slab = kmem_cache_create(rp->slab_name,
3578c2ecf20Sopenharmony_ci	    sizeof(struct mon_event_text), sizeof(long), 0,
3588c2ecf20Sopenharmony_ci	    mon_text_ctor);
3598c2ecf20Sopenharmony_ci	if (rp->e_slab == NULL) {
3608c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3618c2ecf20Sopenharmony_ci		goto err_slab;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	mon_reader_add(mbus, &rp->r);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	file->private_data = rp;
3678c2ecf20Sopenharmony_ci	mutex_unlock(&mon_lock);
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci// err_busy:
3718c2ecf20Sopenharmony_ci//	kmem_cache_destroy(rp->e_slab);
3728c2ecf20Sopenharmony_cierr_slab:
3738c2ecf20Sopenharmony_ci	kfree(rp->printf_buf);
3748c2ecf20Sopenharmony_cierr_alloc_pr:
3758c2ecf20Sopenharmony_ci	kfree(rp);
3768c2ecf20Sopenharmony_cierr_alloc:
3778c2ecf20Sopenharmony_ci	mutex_unlock(&mon_lock);
3788c2ecf20Sopenharmony_ci	return rc;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
3828c2ecf20Sopenharmony_ci    char __user * const buf, const size_t nbytes)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	const size_t togo = min(nbytes, rp->printf_togo);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo))
3878c2ecf20Sopenharmony_ci		return -EFAULT;
3888c2ecf20Sopenharmony_ci	rp->printf_togo -= togo;
3898c2ecf20Sopenharmony_ci	rp->printf_offset += togo;
3908c2ecf20Sopenharmony_ci	return togo;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci/* ppos is not advanced since the llseek operation is not permitted. */
3948c2ecf20Sopenharmony_cistatic ssize_t mon_text_read_t(struct file *file, char __user *buf,
3958c2ecf20Sopenharmony_ci    size_t nbytes, loff_t *ppos)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct mon_reader_text *rp = file->private_data;
3988c2ecf20Sopenharmony_ci	struct mon_event_text *ep;
3998c2ecf20Sopenharmony_ci	struct mon_text_ptr ptr;
4008c2ecf20Sopenharmony_ci	ssize_t ret;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	mutex_lock(&rp->printf_lock);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (rp->printf_togo == 0) {
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		ep = mon_text_read_wait(rp, file);
4078c2ecf20Sopenharmony_ci		if (IS_ERR(ep)) {
4088c2ecf20Sopenharmony_ci			mutex_unlock(&rp->printf_lock);
4098c2ecf20Sopenharmony_ci			return PTR_ERR(ep);
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci		ptr.cnt = 0;
4128c2ecf20Sopenharmony_ci		ptr.pbuf = rp->printf_buf;
4138c2ecf20Sopenharmony_ci		ptr.limit = rp->printf_size;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		mon_text_read_head_t(rp, &ptr, ep);
4168c2ecf20Sopenharmony_ci		mon_text_read_statset(rp, &ptr, ep);
4178c2ecf20Sopenharmony_ci		ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
4188c2ecf20Sopenharmony_ci		    " %d", ep->length);
4198c2ecf20Sopenharmony_ci		mon_text_read_data(rp, &ptr, ep);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		rp->printf_togo = ptr.cnt;
4228c2ecf20Sopenharmony_ci		rp->printf_offset = 0;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		kmem_cache_free(rp->e_slab, ep);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	ret = mon_text_copy_to_user(rp, buf, nbytes);
4288c2ecf20Sopenharmony_ci	mutex_unlock(&rp->printf_lock);
4298c2ecf20Sopenharmony_ci	return ret;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/* ppos is not advanced since the llseek operation is not permitted. */
4338c2ecf20Sopenharmony_cistatic ssize_t mon_text_read_u(struct file *file, char __user *buf,
4348c2ecf20Sopenharmony_ci    size_t nbytes, loff_t *ppos)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct mon_reader_text *rp = file->private_data;
4378c2ecf20Sopenharmony_ci	struct mon_event_text *ep;
4388c2ecf20Sopenharmony_ci	struct mon_text_ptr ptr;
4398c2ecf20Sopenharmony_ci	ssize_t ret;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	mutex_lock(&rp->printf_lock);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (rp->printf_togo == 0) {
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		ep = mon_text_read_wait(rp, file);
4468c2ecf20Sopenharmony_ci		if (IS_ERR(ep)) {
4478c2ecf20Sopenharmony_ci			mutex_unlock(&rp->printf_lock);
4488c2ecf20Sopenharmony_ci			return PTR_ERR(ep);
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci		ptr.cnt = 0;
4518c2ecf20Sopenharmony_ci		ptr.pbuf = rp->printf_buf;
4528c2ecf20Sopenharmony_ci		ptr.limit = rp->printf_size;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		mon_text_read_head_u(rp, &ptr, ep);
4558c2ecf20Sopenharmony_ci		if (ep->type == 'E') {
4568c2ecf20Sopenharmony_ci			mon_text_read_statset(rp, &ptr, ep);
4578c2ecf20Sopenharmony_ci		} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
4588c2ecf20Sopenharmony_ci			mon_text_read_isostat(rp, &ptr, ep);
4598c2ecf20Sopenharmony_ci			mon_text_read_isodesc(rp, &ptr, ep);
4608c2ecf20Sopenharmony_ci		} else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
4618c2ecf20Sopenharmony_ci			mon_text_read_intstat(rp, &ptr, ep);
4628c2ecf20Sopenharmony_ci		} else {
4638c2ecf20Sopenharmony_ci			mon_text_read_statset(rp, &ptr, ep);
4648c2ecf20Sopenharmony_ci		}
4658c2ecf20Sopenharmony_ci		ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
4668c2ecf20Sopenharmony_ci		    " %d", ep->length);
4678c2ecf20Sopenharmony_ci		mon_text_read_data(rp, &ptr, ep);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		rp->printf_togo = ptr.cnt;
4708c2ecf20Sopenharmony_ci		rp->printf_offset = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		kmem_cache_free(rp->e_slab, ep);
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	ret = mon_text_copy_to_user(rp, buf, nbytes);
4768c2ecf20Sopenharmony_ci	mutex_unlock(&rp->printf_lock);
4778c2ecf20Sopenharmony_ci	return ret;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
4818c2ecf20Sopenharmony_ci    struct file *file)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct mon_bus *mbus = rp->r.m_bus;
4848c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(waita, current);
4858c2ecf20Sopenharmony_ci	struct mon_event_text *ep;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	add_wait_queue(&rp->wait, &waita);
4888c2ecf20Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
4898c2ecf20Sopenharmony_ci	while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
4908c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
4918c2ecf20Sopenharmony_ci			set_current_state(TASK_RUNNING);
4928c2ecf20Sopenharmony_ci			remove_wait_queue(&rp->wait, &waita);
4938c2ecf20Sopenharmony_ci			return ERR_PTR(-EWOULDBLOCK);
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci		/*
4968c2ecf20Sopenharmony_ci		 * We do not count nwaiters, because ->release is supposed
4978c2ecf20Sopenharmony_ci		 * to be called when all openers are gone only.
4988c2ecf20Sopenharmony_ci		 */
4998c2ecf20Sopenharmony_ci		schedule();
5008c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
5018c2ecf20Sopenharmony_ci			remove_wait_queue(&rp->wait, &waita);
5028c2ecf20Sopenharmony_ci			return ERR_PTR(-EINTR);
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
5078c2ecf20Sopenharmony_ci	remove_wait_queue(&rp->wait, &waita);
5088c2ecf20Sopenharmony_ci	return ep;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic void mon_text_read_head_t(struct mon_reader_text *rp,
5128c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	char udir, utype;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	udir = (ep->is_in ? 'i' : 'o');
5178c2ecf20Sopenharmony_ci	switch (ep->xfertype) {
5188c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:	utype = 'Z'; break;
5198c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:	utype = 'I'; break;
5208c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:	utype = 'C'; break;
5218c2ecf20Sopenharmony_ci	default: /* PIPE_BULK */  utype = 'B';
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5248c2ecf20Sopenharmony_ci	    "%lx %u %c %c%c:%03u:%02u",
5258c2ecf20Sopenharmony_ci	    ep->id, ep->tstamp, ep->type,
5268c2ecf20Sopenharmony_ci	    utype, udir, ep->devnum, ep->epnum);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic void mon_text_read_head_u(struct mon_reader_text *rp,
5308c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	char udir, utype;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	udir = (ep->is_in ? 'i' : 'o');
5358c2ecf20Sopenharmony_ci	switch (ep->xfertype) {
5368c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:	utype = 'Z'; break;
5378c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:	utype = 'I'; break;
5388c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:	utype = 'C'; break;
5398c2ecf20Sopenharmony_ci	default: /* PIPE_BULK */  utype = 'B';
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5428c2ecf20Sopenharmony_ci	    "%lx %u %c %c%c:%d:%03u:%u",
5438c2ecf20Sopenharmony_ci	    ep->id, ep->tstamp, ep->type,
5448c2ecf20Sopenharmony_ci	    utype, udir, ep->busnum, ep->devnum, ep->epnum);
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic void mon_text_read_statset(struct mon_reader_text *rp,
5488c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if (ep->setup_flag == 0) {   /* Setup packet is present and captured */
5528c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5538c2ecf20Sopenharmony_ci		    " s %02x %02x %04x %04x %04x",
5548c2ecf20Sopenharmony_ci		    ep->setup[0],
5558c2ecf20Sopenharmony_ci		    ep->setup[1],
5568c2ecf20Sopenharmony_ci		    (ep->setup[3] << 8) | ep->setup[2],
5578c2ecf20Sopenharmony_ci		    (ep->setup[5] << 8) | ep->setup[4],
5588c2ecf20Sopenharmony_ci		    (ep->setup[7] << 8) | ep->setup[6]);
5598c2ecf20Sopenharmony_ci	} else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
5608c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5618c2ecf20Sopenharmony_ci		    " %c __ __ ____ ____ ____", ep->setup_flag);
5628c2ecf20Sopenharmony_ci	} else {                     /* No setup for this kind of URB */
5638c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5648c2ecf20Sopenharmony_ci		    " %d", ep->status);
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic void mon_text_read_intstat(struct mon_reader_text *rp,
5698c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5728c2ecf20Sopenharmony_ci	    " %d:%d", ep->status, ep->interval);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic void mon_text_read_isostat(struct mon_reader_text *rp,
5768c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	if (ep->type == 'S') {
5798c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5808c2ecf20Sopenharmony_ci		    " %d:%d:%d", ep->status, ep->interval, ep->start_frame);
5818c2ecf20Sopenharmony_ci	} else {
5828c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5838c2ecf20Sopenharmony_ci		    " %d:%d:%d:%d",
5848c2ecf20Sopenharmony_ci		    ep->status, ep->interval, ep->start_frame, ep->error_count);
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic void mon_text_read_isodesc(struct mon_reader_text *rp,
5898c2ecf20Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	int ndesc;	/* Display this many */
5928c2ecf20Sopenharmony_ci	int i;
5938c2ecf20Sopenharmony_ci	const struct mon_iso_desc *dp;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
5968c2ecf20Sopenharmony_ci	    " %d", ep->numdesc);
5978c2ecf20Sopenharmony_ci	ndesc = ep->numdesc;
5988c2ecf20Sopenharmony_ci	if (ndesc > ISODESC_MAX)
5998c2ecf20Sopenharmony_ci		ndesc = ISODESC_MAX;
6008c2ecf20Sopenharmony_ci	if (ndesc < 0)
6018c2ecf20Sopenharmony_ci		ndesc = 0;
6028c2ecf20Sopenharmony_ci	dp = ep->isodesc;
6038c2ecf20Sopenharmony_ci	for (i = 0; i < ndesc; i++) {
6048c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
6058c2ecf20Sopenharmony_ci		    " %d:%u:%u", dp->status, dp->offset, dp->length);
6068c2ecf20Sopenharmony_ci		dp++;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic void mon_text_read_data(struct mon_reader_text *rp,
6118c2ecf20Sopenharmony_ci    struct mon_text_ptr *p, const struct mon_event_text *ep)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	int data_len, i;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if ((data_len = ep->length) > 0) {
6168c2ecf20Sopenharmony_ci		if (ep->data_flag == 0) {
6178c2ecf20Sopenharmony_ci			p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
6188c2ecf20Sopenharmony_ci			    " =");
6198c2ecf20Sopenharmony_ci			if (data_len >= DATA_MAX)
6208c2ecf20Sopenharmony_ci				data_len = DATA_MAX;
6218c2ecf20Sopenharmony_ci			for (i = 0; i < data_len; i++) {
6228c2ecf20Sopenharmony_ci				if (i % 4 == 0) {
6238c2ecf20Sopenharmony_ci					p->cnt += scnprintf(p->pbuf + p->cnt,
6248c2ecf20Sopenharmony_ci					    p->limit - p->cnt,
6258c2ecf20Sopenharmony_ci					    " ");
6268c2ecf20Sopenharmony_ci				}
6278c2ecf20Sopenharmony_ci				p->cnt += scnprintf(p->pbuf + p->cnt,
6288c2ecf20Sopenharmony_ci				    p->limit - p->cnt,
6298c2ecf20Sopenharmony_ci				    "%02x", ep->data[i]);
6308c2ecf20Sopenharmony_ci			}
6318c2ecf20Sopenharmony_ci			p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
6328c2ecf20Sopenharmony_ci			    "\n");
6338c2ecf20Sopenharmony_ci		} else {
6348c2ecf20Sopenharmony_ci			p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
6358c2ecf20Sopenharmony_ci			    " %c\n", ep->data_flag);
6368c2ecf20Sopenharmony_ci		}
6378c2ecf20Sopenharmony_ci	} else {
6388c2ecf20Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic int mon_text_release(struct inode *inode, struct file *file)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct mon_reader_text *rp = file->private_data;
6458c2ecf20Sopenharmony_ci	struct mon_bus *mbus;
6468c2ecf20Sopenharmony_ci	/* unsigned long flags; */
6478c2ecf20Sopenharmony_ci	struct list_head *p;
6488c2ecf20Sopenharmony_ci	struct mon_event_text *ep;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	mutex_lock(&mon_lock);
6518c2ecf20Sopenharmony_ci	mbus = inode->i_private;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (mbus->nreaders <= 0) {
6548c2ecf20Sopenharmony_ci		printk(KERN_ERR TAG ": consistency error on close\n");
6558c2ecf20Sopenharmony_ci		mutex_unlock(&mon_lock);
6568c2ecf20Sopenharmony_ci		return 0;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci	mon_reader_del(mbus, &rp->r);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/*
6618c2ecf20Sopenharmony_ci	 * In theory, e_list is protected by mbus->lock. However,
6628c2ecf20Sopenharmony_ci	 * after mon_reader_del has finished, the following is the case:
6638c2ecf20Sopenharmony_ci	 *  - we are not on reader list anymore, so new events won't be added;
6648c2ecf20Sopenharmony_ci	 *  - whole mbus may be dropped if it was orphaned.
6658c2ecf20Sopenharmony_ci	 * So, we better not touch mbus.
6668c2ecf20Sopenharmony_ci	 */
6678c2ecf20Sopenharmony_ci	/* spin_lock_irqsave(&mbus->lock, flags); */
6688c2ecf20Sopenharmony_ci	while (!list_empty(&rp->e_list)) {
6698c2ecf20Sopenharmony_ci		p = rp->e_list.next;
6708c2ecf20Sopenharmony_ci		ep = list_entry(p, struct mon_event_text, e_link);
6718c2ecf20Sopenharmony_ci		list_del(p);
6728c2ecf20Sopenharmony_ci		--rp->nevents;
6738c2ecf20Sopenharmony_ci		kmem_cache_free(rp->e_slab, ep);
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci	/* spin_unlock_irqrestore(&mbus->lock, flags); */
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	kmem_cache_destroy(rp->e_slab);
6788c2ecf20Sopenharmony_ci	kfree(rp->printf_buf);
6798c2ecf20Sopenharmony_ci	kfree(rp);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	mutex_unlock(&mon_lock);
6828c2ecf20Sopenharmony_ci	return 0;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic const struct file_operations mon_fops_text_t = {
6868c2ecf20Sopenharmony_ci	.owner =	THIS_MODULE,
6878c2ecf20Sopenharmony_ci	.open =		mon_text_open,
6888c2ecf20Sopenharmony_ci	.llseek =	no_llseek,
6898c2ecf20Sopenharmony_ci	.read =		mon_text_read_t,
6908c2ecf20Sopenharmony_ci	.release =	mon_text_release,
6918c2ecf20Sopenharmony_ci};
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic const struct file_operations mon_fops_text_u = {
6948c2ecf20Sopenharmony_ci	.owner =	THIS_MODULE,
6958c2ecf20Sopenharmony_ci	.open =		mon_text_open,
6968c2ecf20Sopenharmony_ci	.llseek =	no_llseek,
6978c2ecf20Sopenharmony_ci	.read =		mon_text_read_u,
6988c2ecf20Sopenharmony_ci	.release =	mon_text_release,
6998c2ecf20Sopenharmony_ci};
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ciint mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	enum { NAMESZ = 10 };
7048c2ecf20Sopenharmony_ci	char name[NAMESZ];
7058c2ecf20Sopenharmony_ci	int busnum = ubus? ubus->busnum: 0;
7068c2ecf20Sopenharmony_ci	int rc;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	if (mon_dir == NULL)
7098c2ecf20Sopenharmony_ci		return 0;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (ubus != NULL) {
7128c2ecf20Sopenharmony_ci		rc = snprintf(name, NAMESZ, "%dt", busnum);
7138c2ecf20Sopenharmony_ci		if (rc <= 0 || rc >= NAMESZ)
7148c2ecf20Sopenharmony_ci			goto err_print_t;
7158c2ecf20Sopenharmony_ci		mbus->dent_t = debugfs_create_file(name, 0600, mon_dir, mbus,
7168c2ecf20Sopenharmony_ci							     &mon_fops_text_t);
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	rc = snprintf(name, NAMESZ, "%du", busnum);
7208c2ecf20Sopenharmony_ci	if (rc <= 0 || rc >= NAMESZ)
7218c2ecf20Sopenharmony_ci		goto err_print_u;
7228c2ecf20Sopenharmony_ci	mbus->dent_u = debugfs_create_file(name, 0600, mon_dir, mbus,
7238c2ecf20Sopenharmony_ci					   &mon_fops_text_u);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	rc = snprintf(name, NAMESZ, "%ds", busnum);
7268c2ecf20Sopenharmony_ci	if (rc <= 0 || rc >= NAMESZ)
7278c2ecf20Sopenharmony_ci		goto err_print_s;
7288c2ecf20Sopenharmony_ci	mbus->dent_s = debugfs_create_file(name, 0600, mon_dir, mbus,
7298c2ecf20Sopenharmony_ci					   &mon_fops_stat);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return 1;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cierr_print_s:
7348c2ecf20Sopenharmony_ci	debugfs_remove(mbus->dent_u);
7358c2ecf20Sopenharmony_ci	mbus->dent_u = NULL;
7368c2ecf20Sopenharmony_cierr_print_u:
7378c2ecf20Sopenharmony_ci	if (ubus != NULL) {
7388c2ecf20Sopenharmony_ci		debugfs_remove(mbus->dent_t);
7398c2ecf20Sopenharmony_ci		mbus->dent_t = NULL;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_cierr_print_t:
7428c2ecf20Sopenharmony_ci	return 0;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_civoid mon_text_del(struct mon_bus *mbus)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	debugfs_remove(mbus->dent_u);
7488c2ecf20Sopenharmony_ci	debugfs_remove(mbus->dent_t);
7498c2ecf20Sopenharmony_ci	debugfs_remove(mbus->dent_s);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci/*
7538c2ecf20Sopenharmony_ci * Slab interface: constructor.
7548c2ecf20Sopenharmony_ci */
7558c2ecf20Sopenharmony_cistatic void mon_text_ctor(void *mem)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	/*
7588c2ecf20Sopenharmony_ci	 * Nothing to initialize. No, really!
7598c2ecf20Sopenharmony_ci	 * So, we fill it with garbage to emulate a reused object.
7608c2ecf20Sopenharmony_ci	 */
7618c2ecf20Sopenharmony_ci	memset(mem, 0xe5, sizeof(struct mon_event_text));
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ciint __init mon_text_init(void)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	mon_dir = debugfs_create_dir("usbmon", usb_debug_root);
7678c2ecf20Sopenharmony_ci	return 0;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_civoid mon_text_exit(void)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	debugfs_remove(mon_dir);
7738c2ecf20Sopenharmony_ci}
774