162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * The USB Monitor, inspired by Dave Harding's USBMon.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This is a text format reader.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/list.h>
1062306a36Sopenharmony_ci#include <linux/usb.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/sched/signal.h>
1362306a36Sopenharmony_ci#include <linux/time.h>
1462306a36Sopenharmony_ci#include <linux/ktime.h>
1562306a36Sopenharmony_ci#include <linux/export.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/debugfs.h>
1862306a36Sopenharmony_ci#include <linux/scatterlist.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "usb_mon.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * No, we do not want arbitrarily long data strings.
2562306a36Sopenharmony_ci * Use the binary interface if you want to capture bulk data!
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#define DATA_MAX  32
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Defined by USB 2.0 clause 9.3, table 9.2.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define SETUP_MAX  8
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * This limit exists to prevent OOMs when the user process stops reading.
3662306a36Sopenharmony_ci * If usbmon were available to unprivileged processes, it might be open
3762306a36Sopenharmony_ci * to a local DoS. But we have to keep to root in order to prevent
3862306a36Sopenharmony_ci * password sniffing from HID devices.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci#define EVENT_MAX  (4*PAGE_SIZE / sizeof(struct mon_event_text))
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Potentially unlimited number; we limit it for similar allocations.
4462306a36Sopenharmony_ci * The usbfs limits this to 128, but we're not quite as generous.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci#define ISODESC_MAX   5
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define PRINTF_DFL  250   /* with 5 ISOs segs */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct mon_iso_desc {
5162306a36Sopenharmony_ci	int status;
5262306a36Sopenharmony_ci	unsigned int offset;
5362306a36Sopenharmony_ci	unsigned int length;	/* Unsigned here, signed in URB. Historic. */
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct mon_event_text {
5762306a36Sopenharmony_ci	struct list_head e_link;
5862306a36Sopenharmony_ci	int type;		/* submit, complete, etc. */
5962306a36Sopenharmony_ci	unsigned long id;	/* From pointer, most of the time */
6062306a36Sopenharmony_ci	unsigned int tstamp;
6162306a36Sopenharmony_ci	int busnum;
6262306a36Sopenharmony_ci	char devnum;
6362306a36Sopenharmony_ci	char epnum;
6462306a36Sopenharmony_ci	char is_in;
6562306a36Sopenharmony_ci	char xfertype;
6662306a36Sopenharmony_ci	int length;		/* Depends on type: xfer length or act length */
6762306a36Sopenharmony_ci	int status;
6862306a36Sopenharmony_ci	int interval;
6962306a36Sopenharmony_ci	int start_frame;
7062306a36Sopenharmony_ci	int error_count;
7162306a36Sopenharmony_ci	char setup_flag;
7262306a36Sopenharmony_ci	char data_flag;
7362306a36Sopenharmony_ci	int numdesc;		/* Full number */
7462306a36Sopenharmony_ci	struct mon_iso_desc isodesc[ISODESC_MAX];
7562306a36Sopenharmony_ci	unsigned char setup[SETUP_MAX];
7662306a36Sopenharmony_ci	unsigned char data[DATA_MAX];
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define SLAB_NAME_SZ  30
8062306a36Sopenharmony_cistruct mon_reader_text {
8162306a36Sopenharmony_ci	struct kmem_cache *e_slab;
8262306a36Sopenharmony_ci	int nevents;
8362306a36Sopenharmony_ci	struct list_head e_list;
8462306a36Sopenharmony_ci	struct mon_reader r;	/* In C, parent class can be placed anywhere */
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	wait_queue_head_t wait;
8762306a36Sopenharmony_ci	int printf_size;
8862306a36Sopenharmony_ci	size_t printf_offset;
8962306a36Sopenharmony_ci	size_t printf_togo;
9062306a36Sopenharmony_ci	char *printf_buf;
9162306a36Sopenharmony_ci	struct mutex printf_lock;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	char slab_name[SLAB_NAME_SZ];
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic struct dentry *mon_dir;		/* Usually /sys/kernel/debug/usbmon */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void mon_text_ctor(void *);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistruct mon_text_ptr {
10162306a36Sopenharmony_ci	int cnt, limit;
10262306a36Sopenharmony_ci	char *pbuf;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic struct mon_event_text *
10662306a36Sopenharmony_ci    mon_text_read_wait(struct mon_reader_text *rp, struct file *file);
10762306a36Sopenharmony_cistatic void mon_text_read_head_t(struct mon_reader_text *rp,
10862306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
10962306a36Sopenharmony_cistatic void mon_text_read_head_u(struct mon_reader_text *rp,
11062306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
11162306a36Sopenharmony_cistatic void mon_text_read_statset(struct mon_reader_text *rp,
11262306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
11362306a36Sopenharmony_cistatic void mon_text_read_intstat(struct mon_reader_text *rp,
11462306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
11562306a36Sopenharmony_cistatic void mon_text_read_isostat(struct mon_reader_text *rp,
11662306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
11762306a36Sopenharmony_cistatic void mon_text_read_isodesc(struct mon_reader_text *rp,
11862306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep);
11962306a36Sopenharmony_cistatic void mon_text_read_data(struct mon_reader_text *rp,
12062306a36Sopenharmony_ci    struct mon_text_ptr *p, const struct mon_event_text *ep);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/*
12362306a36Sopenharmony_ci * mon_text_submit
12462306a36Sopenharmony_ci * mon_text_complete
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * May be called from an interrupt.
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * This is called with the whole mon_bus locked, so no additional lock.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic inline char mon_text_get_setup(struct mon_event_text *ep,
13262306a36Sopenharmony_ci    struct urb *urb, char ev_type, struct mon_bus *mbus)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (ep->xfertype != USB_ENDPOINT_XFER_CONTROL || ev_type != 'S')
13662306a36Sopenharmony_ci		return '-';
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (urb->setup_packet == NULL)
13962306a36Sopenharmony_ci		return 'Z';	/* '0' would be not as pretty. */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	memcpy(ep->setup, urb->setup_packet, SETUP_MAX);
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
14662306a36Sopenharmony_ci    int len, char ev_type, struct mon_bus *mbus)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	void *src;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (len <= 0)
15162306a36Sopenharmony_ci		return 'L';
15262306a36Sopenharmony_ci	if (len >= DATA_MAX)
15362306a36Sopenharmony_ci		len = DATA_MAX;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (ep->is_in) {
15662306a36Sopenharmony_ci		if (ev_type != 'C')
15762306a36Sopenharmony_ci			return '<';
15862306a36Sopenharmony_ci	} else {
15962306a36Sopenharmony_ci		if (ev_type != 'S')
16062306a36Sopenharmony_ci			return '>';
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (urb->num_sgs == 0) {
16462306a36Sopenharmony_ci		src = urb->transfer_buffer;
16562306a36Sopenharmony_ci		if (src == NULL)
16662306a36Sopenharmony_ci			return 'Z';	/* '0' would be not as pretty. */
16762306a36Sopenharmony_ci	} else {
16862306a36Sopenharmony_ci		struct scatterlist *sg = urb->sg;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		if (PageHighMem(sg_page(sg)))
17162306a36Sopenharmony_ci			return 'D';
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		/* For the text interface we copy only the first sg buffer */
17462306a36Sopenharmony_ci		len = min_t(int, sg->length, len);
17562306a36Sopenharmony_ci		src = sg_virt(sg);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	memcpy(ep->data, src, len);
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic inline unsigned int mon_get_timestamp(void)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct timespec64 now;
18562306a36Sopenharmony_ci	unsigned int stamp;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ktime_get_ts64(&now);
18862306a36Sopenharmony_ci	stamp = now.tv_sec & 0xFFF;  /* 2^32 = 4294967296. Limit to 4096s. */
18962306a36Sopenharmony_ci	stamp = stamp * USEC_PER_SEC + now.tv_nsec / NSEC_PER_USEC;
19062306a36Sopenharmony_ci	return stamp;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
19462306a36Sopenharmony_ci    char ev_type, int status)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct mon_event_text *ep;
19762306a36Sopenharmony_ci	unsigned int stamp;
19862306a36Sopenharmony_ci	struct usb_iso_packet_descriptor *fp;
19962306a36Sopenharmony_ci	struct mon_iso_desc *dp;
20062306a36Sopenharmony_ci	int i, ndesc;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	stamp = mon_get_timestamp();
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (rp->nevents >= EVENT_MAX ||
20562306a36Sopenharmony_ci	    (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) {
20662306a36Sopenharmony_ci		rp->r.m_bus->cnt_text_lost++;
20762306a36Sopenharmony_ci		return;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ep->type = ev_type;
21162306a36Sopenharmony_ci	ep->id = (unsigned long) urb;
21262306a36Sopenharmony_ci	ep->busnum = urb->dev->bus->busnum;
21362306a36Sopenharmony_ci	ep->devnum = urb->dev->devnum;
21462306a36Sopenharmony_ci	ep->epnum = usb_endpoint_num(&urb->ep->desc);
21562306a36Sopenharmony_ci	ep->xfertype = usb_endpoint_type(&urb->ep->desc);
21662306a36Sopenharmony_ci	ep->is_in = usb_urb_dir_in(urb);
21762306a36Sopenharmony_ci	ep->tstamp = stamp;
21862306a36Sopenharmony_ci	ep->length = (ev_type == 'S') ?
21962306a36Sopenharmony_ci	    urb->transfer_buffer_length : urb->actual_length;
22062306a36Sopenharmony_ci	/* Collecting status makes debugging sense for submits, too */
22162306a36Sopenharmony_ci	ep->status = status;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
22462306a36Sopenharmony_ci		ep->interval = urb->interval;
22562306a36Sopenharmony_ci	} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
22662306a36Sopenharmony_ci		ep->interval = urb->interval;
22762306a36Sopenharmony_ci		ep->start_frame = urb->start_frame;
22862306a36Sopenharmony_ci		ep->error_count = urb->error_count;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	ep->numdesc = urb->number_of_packets;
23162306a36Sopenharmony_ci	if (ep->xfertype == USB_ENDPOINT_XFER_ISOC &&
23262306a36Sopenharmony_ci			urb->number_of_packets > 0) {
23362306a36Sopenharmony_ci		if ((ndesc = urb->number_of_packets) > ISODESC_MAX)
23462306a36Sopenharmony_ci			ndesc = ISODESC_MAX;
23562306a36Sopenharmony_ci		fp = urb->iso_frame_desc;
23662306a36Sopenharmony_ci		dp = ep->isodesc;
23762306a36Sopenharmony_ci		for (i = 0; i < ndesc; i++) {
23862306a36Sopenharmony_ci			dp->status = fp->status;
23962306a36Sopenharmony_ci			dp->offset = fp->offset;
24062306a36Sopenharmony_ci			dp->length = (ev_type == 'S') ?
24162306a36Sopenharmony_ci			    fp->length : fp->actual_length;
24262306a36Sopenharmony_ci			fp++;
24362306a36Sopenharmony_ci			dp++;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci		/* Wasteful, but simple to understand: ISO 'C' is sparse. */
24662306a36Sopenharmony_ci		if (ev_type == 'C')
24762306a36Sopenharmony_ci			ep->length = urb->transfer_buffer_length;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
25162306a36Sopenharmony_ci	ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
25262306a36Sopenharmony_ci			rp->r.m_bus);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	rp->nevents++;
25562306a36Sopenharmony_ci	list_add_tail(&ep->e_link, &rp->e_list);
25662306a36Sopenharmony_ci	wake_up(&rp->wait);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void mon_text_submit(void *data, struct urb *urb)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct mon_reader_text *rp = data;
26262306a36Sopenharmony_ci	mon_text_event(rp, urb, 'S', -EINPROGRESS);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void mon_text_complete(void *data, struct urb *urb, int status)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct mon_reader_text *rp = data;
26862306a36Sopenharmony_ci	mon_text_event(rp, urb, 'C', status);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void mon_text_error(void *data, struct urb *urb, int error)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct mon_reader_text *rp = data;
27462306a36Sopenharmony_ci	struct mon_event_text *ep;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (rp->nevents >= EVENT_MAX ||
27762306a36Sopenharmony_ci	    (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) {
27862306a36Sopenharmony_ci		rp->r.m_bus->cnt_text_lost++;
27962306a36Sopenharmony_ci		return;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ep->type = 'E';
28362306a36Sopenharmony_ci	ep->id = (unsigned long) urb;
28462306a36Sopenharmony_ci	ep->busnum = urb->dev->bus->busnum;
28562306a36Sopenharmony_ci	ep->devnum = urb->dev->devnum;
28662306a36Sopenharmony_ci	ep->epnum = usb_endpoint_num(&urb->ep->desc);
28762306a36Sopenharmony_ci	ep->xfertype = usb_endpoint_type(&urb->ep->desc);
28862306a36Sopenharmony_ci	ep->is_in = usb_urb_dir_in(urb);
28962306a36Sopenharmony_ci	ep->tstamp = mon_get_timestamp();
29062306a36Sopenharmony_ci	ep->length = 0;
29162306a36Sopenharmony_ci	ep->status = error;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	ep->setup_flag = '-';
29462306a36Sopenharmony_ci	ep->data_flag = 'E';
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	rp->nevents++;
29762306a36Sopenharmony_ci	list_add_tail(&ep->e_link, &rp->e_list);
29862306a36Sopenharmony_ci	wake_up(&rp->wait);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/*
30262306a36Sopenharmony_ci * Fetch next event from the circular buffer.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistatic struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
30562306a36Sopenharmony_ci    struct mon_bus *mbus)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct list_head *p;
30862306a36Sopenharmony_ci	unsigned long flags;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	spin_lock_irqsave(&mbus->lock, flags);
31162306a36Sopenharmony_ci	if (list_empty(&rp->e_list)) {
31262306a36Sopenharmony_ci		spin_unlock_irqrestore(&mbus->lock, flags);
31362306a36Sopenharmony_ci		return NULL;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	p = rp->e_list.next;
31662306a36Sopenharmony_ci	list_del(p);
31762306a36Sopenharmony_ci	--rp->nevents;
31862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mbus->lock, flags);
31962306a36Sopenharmony_ci	return list_entry(p, struct mon_event_text, e_link);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/*
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_cistatic int mon_text_open(struct inode *inode, struct file *file)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct mon_bus *mbus;
32762306a36Sopenharmony_ci	struct mon_reader_text *rp;
32862306a36Sopenharmony_ci	int rc;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mutex_lock(&mon_lock);
33162306a36Sopenharmony_ci	mbus = inode->i_private;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
33462306a36Sopenharmony_ci	if (rp == NULL) {
33562306a36Sopenharmony_ci		rc = -ENOMEM;
33662306a36Sopenharmony_ci		goto err_alloc;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci	INIT_LIST_HEAD(&rp->e_list);
33962306a36Sopenharmony_ci	init_waitqueue_head(&rp->wait);
34062306a36Sopenharmony_ci	mutex_init(&rp->printf_lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	rp->printf_size = PRINTF_DFL;
34362306a36Sopenharmony_ci	rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
34462306a36Sopenharmony_ci	if (rp->printf_buf == NULL) {
34562306a36Sopenharmony_ci		rc = -ENOMEM;
34662306a36Sopenharmony_ci		goto err_alloc_pr;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	rp->r.m_bus = mbus;
35062306a36Sopenharmony_ci	rp->r.r_data = rp;
35162306a36Sopenharmony_ci	rp->r.rnf_submit = mon_text_submit;
35262306a36Sopenharmony_ci	rp->r.rnf_error = mon_text_error;
35362306a36Sopenharmony_ci	rp->r.rnf_complete = mon_text_complete;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp);
35662306a36Sopenharmony_ci	rp->e_slab = kmem_cache_create(rp->slab_name,
35762306a36Sopenharmony_ci	    sizeof(struct mon_event_text), sizeof(long), 0,
35862306a36Sopenharmony_ci	    mon_text_ctor);
35962306a36Sopenharmony_ci	if (rp->e_slab == NULL) {
36062306a36Sopenharmony_ci		rc = -ENOMEM;
36162306a36Sopenharmony_ci		goto err_slab;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	mon_reader_add(mbus, &rp->r);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	file->private_data = rp;
36762306a36Sopenharmony_ci	mutex_unlock(&mon_lock);
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci// err_busy:
37162306a36Sopenharmony_ci//	kmem_cache_destroy(rp->e_slab);
37262306a36Sopenharmony_cierr_slab:
37362306a36Sopenharmony_ci	kfree(rp->printf_buf);
37462306a36Sopenharmony_cierr_alloc_pr:
37562306a36Sopenharmony_ci	kfree(rp);
37662306a36Sopenharmony_cierr_alloc:
37762306a36Sopenharmony_ci	mutex_unlock(&mon_lock);
37862306a36Sopenharmony_ci	return rc;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
38262306a36Sopenharmony_ci    char __user * const buf, const size_t nbytes)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	const size_t togo = min(nbytes, rp->printf_togo);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo))
38762306a36Sopenharmony_ci		return -EFAULT;
38862306a36Sopenharmony_ci	rp->printf_togo -= togo;
38962306a36Sopenharmony_ci	rp->printf_offset += togo;
39062306a36Sopenharmony_ci	return togo;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/* ppos is not advanced since the llseek operation is not permitted. */
39462306a36Sopenharmony_cistatic ssize_t mon_text_read_t(struct file *file, char __user *buf,
39562306a36Sopenharmony_ci    size_t nbytes, loff_t *ppos)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct mon_reader_text *rp = file->private_data;
39862306a36Sopenharmony_ci	struct mon_event_text *ep;
39962306a36Sopenharmony_ci	struct mon_text_ptr ptr;
40062306a36Sopenharmony_ci	ssize_t ret;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	mutex_lock(&rp->printf_lock);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (rp->printf_togo == 0) {
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		ep = mon_text_read_wait(rp, file);
40762306a36Sopenharmony_ci		if (IS_ERR(ep)) {
40862306a36Sopenharmony_ci			mutex_unlock(&rp->printf_lock);
40962306a36Sopenharmony_ci			return PTR_ERR(ep);
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci		ptr.cnt = 0;
41262306a36Sopenharmony_ci		ptr.pbuf = rp->printf_buf;
41362306a36Sopenharmony_ci		ptr.limit = rp->printf_size;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		mon_text_read_head_t(rp, &ptr, ep);
41662306a36Sopenharmony_ci		mon_text_read_statset(rp, &ptr, ep);
41762306a36Sopenharmony_ci		ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
41862306a36Sopenharmony_ci		    " %d", ep->length);
41962306a36Sopenharmony_ci		mon_text_read_data(rp, &ptr, ep);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		rp->printf_togo = ptr.cnt;
42262306a36Sopenharmony_ci		rp->printf_offset = 0;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		kmem_cache_free(rp->e_slab, ep);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ret = mon_text_copy_to_user(rp, buf, nbytes);
42862306a36Sopenharmony_ci	mutex_unlock(&rp->printf_lock);
42962306a36Sopenharmony_ci	return ret;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/* ppos is not advanced since the llseek operation is not permitted. */
43362306a36Sopenharmony_cistatic ssize_t mon_text_read_u(struct file *file, char __user *buf,
43462306a36Sopenharmony_ci    size_t nbytes, loff_t *ppos)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct mon_reader_text *rp = file->private_data;
43762306a36Sopenharmony_ci	struct mon_event_text *ep;
43862306a36Sopenharmony_ci	struct mon_text_ptr ptr;
43962306a36Sopenharmony_ci	ssize_t ret;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	mutex_lock(&rp->printf_lock);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (rp->printf_togo == 0) {
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ep = mon_text_read_wait(rp, file);
44662306a36Sopenharmony_ci		if (IS_ERR(ep)) {
44762306a36Sopenharmony_ci			mutex_unlock(&rp->printf_lock);
44862306a36Sopenharmony_ci			return PTR_ERR(ep);
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci		ptr.cnt = 0;
45162306a36Sopenharmony_ci		ptr.pbuf = rp->printf_buf;
45262306a36Sopenharmony_ci		ptr.limit = rp->printf_size;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		mon_text_read_head_u(rp, &ptr, ep);
45562306a36Sopenharmony_ci		if (ep->type == 'E') {
45662306a36Sopenharmony_ci			mon_text_read_statset(rp, &ptr, ep);
45762306a36Sopenharmony_ci		} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
45862306a36Sopenharmony_ci			mon_text_read_isostat(rp, &ptr, ep);
45962306a36Sopenharmony_ci			mon_text_read_isodesc(rp, &ptr, ep);
46062306a36Sopenharmony_ci		} else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
46162306a36Sopenharmony_ci			mon_text_read_intstat(rp, &ptr, ep);
46262306a36Sopenharmony_ci		} else {
46362306a36Sopenharmony_ci			mon_text_read_statset(rp, &ptr, ep);
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci		ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
46662306a36Sopenharmony_ci		    " %d", ep->length);
46762306a36Sopenharmony_ci		mon_text_read_data(rp, &ptr, ep);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		rp->printf_togo = ptr.cnt;
47062306a36Sopenharmony_ci		rp->printf_offset = 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		kmem_cache_free(rp->e_slab, ep);
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = mon_text_copy_to_user(rp, buf, nbytes);
47662306a36Sopenharmony_ci	mutex_unlock(&rp->printf_lock);
47762306a36Sopenharmony_ci	return ret;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
48162306a36Sopenharmony_ci    struct file *file)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct mon_bus *mbus = rp->r.m_bus;
48462306a36Sopenharmony_ci	DECLARE_WAITQUEUE(waita, current);
48562306a36Sopenharmony_ci	struct mon_event_text *ep;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	add_wait_queue(&rp->wait, &waita);
48862306a36Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
48962306a36Sopenharmony_ci	while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
49062306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
49162306a36Sopenharmony_ci			set_current_state(TASK_RUNNING);
49262306a36Sopenharmony_ci			remove_wait_queue(&rp->wait, &waita);
49362306a36Sopenharmony_ci			return ERR_PTR(-EWOULDBLOCK);
49462306a36Sopenharmony_ci		}
49562306a36Sopenharmony_ci		/*
49662306a36Sopenharmony_ci		 * We do not count nwaiters, because ->release is supposed
49762306a36Sopenharmony_ci		 * to be called when all openers are gone only.
49862306a36Sopenharmony_ci		 */
49962306a36Sopenharmony_ci		schedule();
50062306a36Sopenharmony_ci		if (signal_pending(current)) {
50162306a36Sopenharmony_ci			remove_wait_queue(&rp->wait, &waita);
50262306a36Sopenharmony_ci			return ERR_PTR(-EINTR);
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
50762306a36Sopenharmony_ci	remove_wait_queue(&rp->wait, &waita);
50862306a36Sopenharmony_ci	return ep;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic void mon_text_read_head_t(struct mon_reader_text *rp,
51262306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	char udir, utype;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	udir = (ep->is_in ? 'i' : 'o');
51762306a36Sopenharmony_ci	switch (ep->xfertype) {
51862306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:	utype = 'Z'; break;
51962306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:	utype = 'I'; break;
52062306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:	utype = 'C'; break;
52162306a36Sopenharmony_ci	default: /* PIPE_BULK */  utype = 'B';
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
52462306a36Sopenharmony_ci	    "%lx %u %c %c%c:%03u:%02u",
52562306a36Sopenharmony_ci	    ep->id, ep->tstamp, ep->type,
52662306a36Sopenharmony_ci	    utype, udir, ep->devnum, ep->epnum);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void mon_text_read_head_u(struct mon_reader_text *rp,
53062306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	char udir, utype;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	udir = (ep->is_in ? 'i' : 'o');
53562306a36Sopenharmony_ci	switch (ep->xfertype) {
53662306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:	utype = 'Z'; break;
53762306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:	utype = 'I'; break;
53862306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:	utype = 'C'; break;
53962306a36Sopenharmony_ci	default: /* PIPE_BULK */  utype = 'B';
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
54262306a36Sopenharmony_ci	    "%lx %u %c %c%c:%d:%03u:%u",
54362306a36Sopenharmony_ci	    ep->id, ep->tstamp, ep->type,
54462306a36Sopenharmony_ci	    utype, udir, ep->busnum, ep->devnum, ep->epnum);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void mon_text_read_statset(struct mon_reader_text *rp,
54862306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (ep->setup_flag == 0) {   /* Setup packet is present and captured */
55262306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
55362306a36Sopenharmony_ci		    " s %02x %02x %04x %04x %04x",
55462306a36Sopenharmony_ci		    ep->setup[0],
55562306a36Sopenharmony_ci		    ep->setup[1],
55662306a36Sopenharmony_ci		    (ep->setup[3] << 8) | ep->setup[2],
55762306a36Sopenharmony_ci		    (ep->setup[5] << 8) | ep->setup[4],
55862306a36Sopenharmony_ci		    (ep->setup[7] << 8) | ep->setup[6]);
55962306a36Sopenharmony_ci	} else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
56062306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
56162306a36Sopenharmony_ci		    " %c __ __ ____ ____ ____", ep->setup_flag);
56262306a36Sopenharmony_ci	} else {                     /* No setup for this kind of URB */
56362306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
56462306a36Sopenharmony_ci		    " %d", ep->status);
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void mon_text_read_intstat(struct mon_reader_text *rp,
56962306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
57262306a36Sopenharmony_ci	    " %d:%d", ep->status, ep->interval);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic void mon_text_read_isostat(struct mon_reader_text *rp,
57662306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	if (ep->type == 'S') {
57962306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
58062306a36Sopenharmony_ci		    " %d:%d:%d", ep->status, ep->interval, ep->start_frame);
58162306a36Sopenharmony_ci	} else {
58262306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
58362306a36Sopenharmony_ci		    " %d:%d:%d:%d",
58462306a36Sopenharmony_ci		    ep->status, ep->interval, ep->start_frame, ep->error_count);
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic void mon_text_read_isodesc(struct mon_reader_text *rp,
58962306a36Sopenharmony_ci	struct mon_text_ptr *p, const struct mon_event_text *ep)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	int ndesc;	/* Display this many */
59262306a36Sopenharmony_ci	int i;
59362306a36Sopenharmony_ci	const struct mon_iso_desc *dp;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
59662306a36Sopenharmony_ci	    " %d", ep->numdesc);
59762306a36Sopenharmony_ci	ndesc = ep->numdesc;
59862306a36Sopenharmony_ci	if (ndesc > ISODESC_MAX)
59962306a36Sopenharmony_ci		ndesc = ISODESC_MAX;
60062306a36Sopenharmony_ci	if (ndesc < 0)
60162306a36Sopenharmony_ci		ndesc = 0;
60262306a36Sopenharmony_ci	dp = ep->isodesc;
60362306a36Sopenharmony_ci	for (i = 0; i < ndesc; i++) {
60462306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
60562306a36Sopenharmony_ci		    " %d:%u:%u", dp->status, dp->offset, dp->length);
60662306a36Sopenharmony_ci		dp++;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void mon_text_read_data(struct mon_reader_text *rp,
61162306a36Sopenharmony_ci    struct mon_text_ptr *p, const struct mon_event_text *ep)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	int data_len, i;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if ((data_len = ep->length) > 0) {
61662306a36Sopenharmony_ci		if (ep->data_flag == 0) {
61762306a36Sopenharmony_ci			p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
61862306a36Sopenharmony_ci			    " =");
61962306a36Sopenharmony_ci			if (data_len >= DATA_MAX)
62062306a36Sopenharmony_ci				data_len = DATA_MAX;
62162306a36Sopenharmony_ci			for (i = 0; i < data_len; i++) {
62262306a36Sopenharmony_ci				if (i % 4 == 0) {
62362306a36Sopenharmony_ci					p->cnt += scnprintf(p->pbuf + p->cnt,
62462306a36Sopenharmony_ci					    p->limit - p->cnt,
62562306a36Sopenharmony_ci					    " ");
62662306a36Sopenharmony_ci				}
62762306a36Sopenharmony_ci				p->cnt += scnprintf(p->pbuf + p->cnt,
62862306a36Sopenharmony_ci				    p->limit - p->cnt,
62962306a36Sopenharmony_ci				    "%02x", ep->data[i]);
63062306a36Sopenharmony_ci			}
63162306a36Sopenharmony_ci			p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
63262306a36Sopenharmony_ci			    "\n");
63362306a36Sopenharmony_ci		} else {
63462306a36Sopenharmony_ci			p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
63562306a36Sopenharmony_ci			    " %c\n", ep->data_flag);
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci	} else {
63862306a36Sopenharmony_ci		p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int mon_text_release(struct inode *inode, struct file *file)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct mon_reader_text *rp = file->private_data;
64562306a36Sopenharmony_ci	struct mon_bus *mbus;
64662306a36Sopenharmony_ci	/* unsigned long flags; */
64762306a36Sopenharmony_ci	struct list_head *p;
64862306a36Sopenharmony_ci	struct mon_event_text *ep;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	mutex_lock(&mon_lock);
65162306a36Sopenharmony_ci	mbus = inode->i_private;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (mbus->nreaders <= 0) {
65462306a36Sopenharmony_ci		printk(KERN_ERR TAG ": consistency error on close\n");
65562306a36Sopenharmony_ci		mutex_unlock(&mon_lock);
65662306a36Sopenharmony_ci		return 0;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci	mon_reader_del(mbus, &rp->r);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/*
66162306a36Sopenharmony_ci	 * In theory, e_list is protected by mbus->lock. However,
66262306a36Sopenharmony_ci	 * after mon_reader_del has finished, the following is the case:
66362306a36Sopenharmony_ci	 *  - we are not on reader list anymore, so new events won't be added;
66462306a36Sopenharmony_ci	 *  - whole mbus may be dropped if it was orphaned.
66562306a36Sopenharmony_ci	 * So, we better not touch mbus.
66662306a36Sopenharmony_ci	 */
66762306a36Sopenharmony_ci	/* spin_lock_irqsave(&mbus->lock, flags); */
66862306a36Sopenharmony_ci	while (!list_empty(&rp->e_list)) {
66962306a36Sopenharmony_ci		p = rp->e_list.next;
67062306a36Sopenharmony_ci		ep = list_entry(p, struct mon_event_text, e_link);
67162306a36Sopenharmony_ci		list_del(p);
67262306a36Sopenharmony_ci		--rp->nevents;
67362306a36Sopenharmony_ci		kmem_cache_free(rp->e_slab, ep);
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci	/* spin_unlock_irqrestore(&mbus->lock, flags); */
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	kmem_cache_destroy(rp->e_slab);
67862306a36Sopenharmony_ci	kfree(rp->printf_buf);
67962306a36Sopenharmony_ci	kfree(rp);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	mutex_unlock(&mon_lock);
68262306a36Sopenharmony_ci	return 0;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic const struct file_operations mon_fops_text_t = {
68662306a36Sopenharmony_ci	.owner =	THIS_MODULE,
68762306a36Sopenharmony_ci	.open =		mon_text_open,
68862306a36Sopenharmony_ci	.llseek =	no_llseek,
68962306a36Sopenharmony_ci	.read =		mon_text_read_t,
69062306a36Sopenharmony_ci	.release =	mon_text_release,
69162306a36Sopenharmony_ci};
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic const struct file_operations mon_fops_text_u = {
69462306a36Sopenharmony_ci	.owner =	THIS_MODULE,
69562306a36Sopenharmony_ci	.open =		mon_text_open,
69662306a36Sopenharmony_ci	.llseek =	no_llseek,
69762306a36Sopenharmony_ci	.read =		mon_text_read_u,
69862306a36Sopenharmony_ci	.release =	mon_text_release,
69962306a36Sopenharmony_ci};
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ciint mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	enum { NAMESZ = 10 };
70462306a36Sopenharmony_ci	char name[NAMESZ];
70562306a36Sopenharmony_ci	int busnum = ubus? ubus->busnum: 0;
70662306a36Sopenharmony_ci	int rc;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (mon_dir == NULL)
70962306a36Sopenharmony_ci		return 0;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (ubus != NULL) {
71262306a36Sopenharmony_ci		rc = snprintf(name, NAMESZ, "%dt", busnum);
71362306a36Sopenharmony_ci		if (rc <= 0 || rc >= NAMESZ)
71462306a36Sopenharmony_ci			goto err_print_t;
71562306a36Sopenharmony_ci		mbus->dent_t = debugfs_create_file(name, 0600, mon_dir, mbus,
71662306a36Sopenharmony_ci							     &mon_fops_text_t);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	rc = snprintf(name, NAMESZ, "%du", busnum);
72062306a36Sopenharmony_ci	if (rc <= 0 || rc >= NAMESZ)
72162306a36Sopenharmony_ci		goto err_print_u;
72262306a36Sopenharmony_ci	mbus->dent_u = debugfs_create_file(name, 0600, mon_dir, mbus,
72362306a36Sopenharmony_ci					   &mon_fops_text_u);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	rc = snprintf(name, NAMESZ, "%ds", busnum);
72662306a36Sopenharmony_ci	if (rc <= 0 || rc >= NAMESZ)
72762306a36Sopenharmony_ci		goto err_print_s;
72862306a36Sopenharmony_ci	mbus->dent_s = debugfs_create_file(name, 0600, mon_dir, mbus,
72962306a36Sopenharmony_ci					   &mon_fops_stat);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return 1;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cierr_print_s:
73462306a36Sopenharmony_ci	debugfs_remove(mbus->dent_u);
73562306a36Sopenharmony_ci	mbus->dent_u = NULL;
73662306a36Sopenharmony_cierr_print_u:
73762306a36Sopenharmony_ci	if (ubus != NULL) {
73862306a36Sopenharmony_ci		debugfs_remove(mbus->dent_t);
73962306a36Sopenharmony_ci		mbus->dent_t = NULL;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_cierr_print_t:
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_civoid mon_text_del(struct mon_bus *mbus)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	debugfs_remove(mbus->dent_u);
74862306a36Sopenharmony_ci	debugfs_remove(mbus->dent_t);
74962306a36Sopenharmony_ci	debugfs_remove(mbus->dent_s);
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/*
75362306a36Sopenharmony_ci * Slab interface: constructor.
75462306a36Sopenharmony_ci */
75562306a36Sopenharmony_cistatic void mon_text_ctor(void *mem)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	/*
75862306a36Sopenharmony_ci	 * Nothing to initialize. No, really!
75962306a36Sopenharmony_ci	 * So, we fill it with garbage to emulate a reused object.
76062306a36Sopenharmony_ci	 */
76162306a36Sopenharmony_ci	memset(mem, 0xe5, sizeof(struct mon_event_text));
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ciint __init mon_text_init(void)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	mon_dir = debugfs_create_dir("usbmon", usb_debug_root);
76762306a36Sopenharmony_ci	return 0;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_civoid mon_text_exit(void)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	debugfs_remove(mon_dir);
77362306a36Sopenharmony_ci}
774