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