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