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 binary format reader. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2006 Paolo Abeni (paolo.abeni@email.it) 862306a36Sopenharmony_ci * Copyright (C) 2006,2007 Pete Zaitcev (zaitcev@redhat.com) 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/sched/signal.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/cdev.h> 1662306a36Sopenharmony_ci#include <linux/export.h> 1762306a36Sopenharmony_ci#include <linux/usb.h> 1862306a36Sopenharmony_ci#include <linux/poll.h> 1962306a36Sopenharmony_ci#include <linux/compat.h> 2062306a36Sopenharmony_ci#include <linux/mm.h> 2162306a36Sopenharmony_ci#include <linux/scatterlist.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/time64.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "usb_mon.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Defined by USB 2.0 clause 9.3, table 9.2. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define SETUP_LEN 8 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* ioctl macros */ 3562306a36Sopenharmony_ci#define MON_IOC_MAGIC 0x92 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1) 3862306a36Sopenharmony_ci/* #2 used to be MON_IOCX_URB, removed before it got into Linus tree */ 3962306a36Sopenharmony_ci#define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats) 4062306a36Sopenharmony_ci#define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4) 4162306a36Sopenharmony_ci#define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5) 4262306a36Sopenharmony_ci#define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) 4362306a36Sopenharmony_ci#define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) 4462306a36Sopenharmony_ci#define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) 4562306a36Sopenharmony_ci/* #9 was MON_IOCT_SETAPI */ 4662306a36Sopenharmony_ci#define MON_IOCX_GETX _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 4962306a36Sopenharmony_ci#define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32) 5062306a36Sopenharmony_ci#define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32) 5162306a36Sopenharmony_ci#define MON_IOCX_GETX32 _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get32) 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Some architectures have enormous basic pages (16KB for ia64, 64KB for ppc). 5662306a36Sopenharmony_ci * But it's all right. Just use a simple way to make sure the chunk is never 5762306a36Sopenharmony_ci * smaller than a page. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * N.B. An application does not know our chunk size. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Woops, get_zeroed_page() returns a single page. I guess we're stuck with 6262306a36Sopenharmony_ci * page-sized chunks for the time being. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci#define CHUNK_SIZE PAGE_SIZE 6562306a36Sopenharmony_ci#define CHUNK_ALIGN(x) (((x)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1)) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * The magic limit was calculated so that it allows the monitoring 6962306a36Sopenharmony_ci * application to pick data once in two ticks. This way, another application, 7062306a36Sopenharmony_ci * which presumably drives the bus, gets to hog CPU, yet we collect our data. 7162306a36Sopenharmony_ci * If HZ is 100, a 480 mbit/s bus drives 614 KB every jiffy. USB has an 7262306a36Sopenharmony_ci * enormous overhead built into the bus protocol, so we need about 1000 KB. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * This is still too much for most cases, where we just snoop a few 7562306a36Sopenharmony_ci * descriptor fetches for enumeration. So, the default is a "reasonable" 7662306a36Sopenharmony_ci * amount for systems with HZ=250 and incomplete bus saturation. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * XXX What about multi-megabyte URBs which take minutes to transfer? 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci#define BUFF_MAX CHUNK_ALIGN(1200*1024) 8162306a36Sopenharmony_ci#define BUFF_DFL CHUNK_ALIGN(300*1024) 8262306a36Sopenharmony_ci#define BUFF_MIN CHUNK_ALIGN(8*1024) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * The per-event API header (2 per URB). 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * This structure is seen in userland as defined by the documentation. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistruct mon_bin_hdr { 9062306a36Sopenharmony_ci u64 id; /* URB ID - from submission to callback */ 9162306a36Sopenharmony_ci unsigned char type; /* Same as in text API; extensible. */ 9262306a36Sopenharmony_ci unsigned char xfer_type; /* ISO, Intr, Control, Bulk */ 9362306a36Sopenharmony_ci unsigned char epnum; /* Endpoint number and transfer direction */ 9462306a36Sopenharmony_ci unsigned char devnum; /* Device address */ 9562306a36Sopenharmony_ci unsigned short busnum; /* Bus number */ 9662306a36Sopenharmony_ci char flag_setup; 9762306a36Sopenharmony_ci char flag_data; 9862306a36Sopenharmony_ci s64 ts_sec; /* ktime_get_real_ts64 */ 9962306a36Sopenharmony_ci s32 ts_usec; /* ktime_get_real_ts64 */ 10062306a36Sopenharmony_ci int status; 10162306a36Sopenharmony_ci unsigned int len_urb; /* Length of data (submitted or actual) */ 10262306a36Sopenharmony_ci unsigned int len_cap; /* Delivered length */ 10362306a36Sopenharmony_ci union { 10462306a36Sopenharmony_ci unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ 10562306a36Sopenharmony_ci struct iso_rec { 10662306a36Sopenharmony_ci int error_count; 10762306a36Sopenharmony_ci int numdesc; 10862306a36Sopenharmony_ci } iso; 10962306a36Sopenharmony_ci } s; 11062306a36Sopenharmony_ci int interval; 11162306a36Sopenharmony_ci int start_frame; 11262306a36Sopenharmony_ci unsigned int xfer_flags; 11362306a36Sopenharmony_ci unsigned int ndesc; /* Actual number of ISO descriptors */ 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * ISO vector, packed into the head of data stream. 11862306a36Sopenharmony_ci * This has to take 16 bytes to make sure that the end of buffer 11962306a36Sopenharmony_ci * wrap is not happening in the middle of a descriptor. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistruct mon_bin_isodesc { 12262306a36Sopenharmony_ci int iso_status; 12362306a36Sopenharmony_ci unsigned int iso_off; 12462306a36Sopenharmony_ci unsigned int iso_len; 12562306a36Sopenharmony_ci u32 _pad; 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* per file statistic */ 12962306a36Sopenharmony_cistruct mon_bin_stats { 13062306a36Sopenharmony_ci u32 queued; 13162306a36Sopenharmony_ci u32 dropped; 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct mon_bin_get { 13562306a36Sopenharmony_ci struct mon_bin_hdr __user *hdr; /* Can be 48 bytes or 64. */ 13662306a36Sopenharmony_ci void __user *data; 13762306a36Sopenharmony_ci size_t alloc; /* Length of data (can be zero) */ 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct mon_bin_mfetch { 14162306a36Sopenharmony_ci u32 __user *offvec; /* Vector of events fetched */ 14262306a36Sopenharmony_ci u32 nfetch; /* Number of events to fetch (out: fetched) */ 14362306a36Sopenharmony_ci u32 nflush; /* Number of events to flush */ 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 14762306a36Sopenharmony_cistruct mon_bin_get32 { 14862306a36Sopenharmony_ci u32 hdr32; 14962306a36Sopenharmony_ci u32 data32; 15062306a36Sopenharmony_ci u32 alloc32; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct mon_bin_mfetch32 { 15462306a36Sopenharmony_ci u32 offvec32; 15562306a36Sopenharmony_ci u32 nfetch32; 15662306a36Sopenharmony_ci u32 nflush32; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci#endif 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* Having these two values same prevents wrapping of the mon_bin_hdr */ 16162306a36Sopenharmony_ci#define PKT_ALIGN 64 16262306a36Sopenharmony_ci#define PKT_SIZE 64 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define PKT_SZ_API0 48 /* API 0 (2.6.20) size */ 16562306a36Sopenharmony_ci#define PKT_SZ_API1 64 /* API 1 size: extra fields */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define ISODESC_MAX 128 /* Same number as usbfs allows, 2048 bytes. */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* max number of USB bus supported */ 17062306a36Sopenharmony_ci#define MON_BIN_MAX_MINOR 128 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* 17362306a36Sopenharmony_ci * The buffer: map of used pages. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistruct mon_pgmap { 17662306a36Sopenharmony_ci struct page *pg; 17762306a36Sopenharmony_ci unsigned char *ptr; /* XXX just use page_to_virt everywhere? */ 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * This gets associated with an open file struct. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistruct mon_reader_bin { 18462306a36Sopenharmony_ci /* The buffer: one per open. */ 18562306a36Sopenharmony_ci spinlock_t b_lock; /* Protect b_cnt, b_in */ 18662306a36Sopenharmony_ci unsigned int b_size; /* Current size of the buffer - bytes */ 18762306a36Sopenharmony_ci unsigned int b_cnt; /* Bytes used */ 18862306a36Sopenharmony_ci unsigned int b_in, b_out; /* Offsets into buffer - bytes */ 18962306a36Sopenharmony_ci unsigned int b_read; /* Amount of read data in curr. pkt. */ 19062306a36Sopenharmony_ci struct mon_pgmap *b_vec; /* The map array */ 19162306a36Sopenharmony_ci wait_queue_head_t b_wait; /* Wait for data here */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci struct mutex fetch_lock; /* Protect b_read, b_out */ 19462306a36Sopenharmony_ci int mmap_active; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* A list of these is needed for "bus 0". Some time later. */ 19762306a36Sopenharmony_ci struct mon_reader r; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Stats */ 20062306a36Sopenharmony_ci unsigned int cnt_lost; 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp, 20462306a36Sopenharmony_ci unsigned int offset) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return (struct mon_bin_hdr *) 20762306a36Sopenharmony_ci (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#define MON_RING_EMPTY(rp) ((rp)->b_cnt == 0) 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic unsigned char xfer_to_pipe[4] = { 21362306a36Sopenharmony_ci PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct class mon_bin_class = { 21762306a36Sopenharmony_ci .name = "usbmon", 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic dev_t mon_bin_dev0; 22162306a36Sopenharmony_cistatic struct cdev mon_bin_cdev; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void mon_buff_area_fill(const struct mon_reader_bin *rp, 22462306a36Sopenharmony_ci unsigned int offset, unsigned int size); 22562306a36Sopenharmony_cistatic int mon_bin_wait_event(struct file *file, struct mon_reader_bin *rp); 22662306a36Sopenharmony_cistatic int mon_alloc_buff(struct mon_pgmap *map, int npages); 22762306a36Sopenharmony_cistatic void mon_free_buff(struct mon_pgmap *map, int npages); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * This is a "chunked memcpy". It does not manipulate any counters. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, 23362306a36Sopenharmony_ci unsigned int off, const unsigned char *from, unsigned int length) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci unsigned int step_len; 23662306a36Sopenharmony_ci unsigned char *buf; 23762306a36Sopenharmony_ci unsigned int in_page; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci while (length) { 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * Determine step_len. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci step_len = length; 24462306a36Sopenharmony_ci in_page = CHUNK_SIZE - (off & (CHUNK_SIZE-1)); 24562306a36Sopenharmony_ci if (in_page < step_len) 24662306a36Sopenharmony_ci step_len = in_page; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Copy data and advance pointers. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci buf = this->b_vec[off / CHUNK_SIZE].ptr + off % CHUNK_SIZE; 25262306a36Sopenharmony_ci memcpy(buf, from, step_len); 25362306a36Sopenharmony_ci if ((off += step_len) >= this->b_size) off = 0; 25462306a36Sopenharmony_ci from += step_len; 25562306a36Sopenharmony_ci length -= step_len; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return off; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * This is a little worse than the above because it's "chunked copy_to_user". 26262306a36Sopenharmony_ci * The return value is an error code, not an offset. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic int copy_from_buf(const struct mon_reader_bin *this, unsigned int off, 26562306a36Sopenharmony_ci char __user *to, int length) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci unsigned int step_len; 26862306a36Sopenharmony_ci unsigned char *buf; 26962306a36Sopenharmony_ci unsigned int in_page; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci while (length) { 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * Determine step_len. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci step_len = length; 27662306a36Sopenharmony_ci in_page = CHUNK_SIZE - (off & (CHUNK_SIZE-1)); 27762306a36Sopenharmony_ci if (in_page < step_len) 27862306a36Sopenharmony_ci step_len = in_page; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * Copy data and advance pointers. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci buf = this->b_vec[off / CHUNK_SIZE].ptr + off % CHUNK_SIZE; 28462306a36Sopenharmony_ci if (copy_to_user(to, buf, step_len)) 28562306a36Sopenharmony_ci return -EINVAL; 28662306a36Sopenharmony_ci if ((off += step_len) >= this->b_size) off = 0; 28762306a36Sopenharmony_ci to += step_len; 28862306a36Sopenharmony_ci length -= step_len; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/* 29462306a36Sopenharmony_ci * Allocate an (aligned) area in the buffer. 29562306a36Sopenharmony_ci * This is called under b_lock. 29662306a36Sopenharmony_ci * Returns ~0 on failure. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_cistatic unsigned int mon_buff_area_alloc(struct mon_reader_bin *rp, 29962306a36Sopenharmony_ci unsigned int size) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci unsigned int offset; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 30462306a36Sopenharmony_ci if (rp->b_cnt + size > rp->b_size) 30562306a36Sopenharmony_ci return ~0; 30662306a36Sopenharmony_ci offset = rp->b_in; 30762306a36Sopenharmony_ci rp->b_cnt += size; 30862306a36Sopenharmony_ci if ((rp->b_in += size) >= rp->b_size) 30962306a36Sopenharmony_ci rp->b_in -= rp->b_size; 31062306a36Sopenharmony_ci return offset; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* 31462306a36Sopenharmony_ci * This is the same thing as mon_buff_area_alloc, only it does not allow 31562306a36Sopenharmony_ci * buffers to wrap. This is needed by applications which pass references 31662306a36Sopenharmony_ci * into mmap-ed buffers up their stacks (libpcap can do that). 31762306a36Sopenharmony_ci * 31862306a36Sopenharmony_ci * Currently, we always have the header stuck with the data, although 31962306a36Sopenharmony_ci * it is not strictly speaking necessary. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * When a buffer would wrap, we place a filler packet to mark the space. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_cistatic unsigned int mon_buff_area_alloc_contiguous(struct mon_reader_bin *rp, 32462306a36Sopenharmony_ci unsigned int size) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci unsigned int offset; 32762306a36Sopenharmony_ci unsigned int fill_size; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 33062306a36Sopenharmony_ci if (rp->b_cnt + size > rp->b_size) 33162306a36Sopenharmony_ci return ~0; 33262306a36Sopenharmony_ci if (rp->b_in + size > rp->b_size) { 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * This would wrap. Find if we still have space after 33562306a36Sopenharmony_ci * skipping to the end of the buffer. If we do, place 33662306a36Sopenharmony_ci * a filler packet and allocate a new packet. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci fill_size = rp->b_size - rp->b_in; 33962306a36Sopenharmony_ci if (rp->b_cnt + size + fill_size > rp->b_size) 34062306a36Sopenharmony_ci return ~0; 34162306a36Sopenharmony_ci mon_buff_area_fill(rp, rp->b_in, fill_size); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci offset = 0; 34462306a36Sopenharmony_ci rp->b_in = size; 34562306a36Sopenharmony_ci rp->b_cnt += size + fill_size; 34662306a36Sopenharmony_ci } else if (rp->b_in + size == rp->b_size) { 34762306a36Sopenharmony_ci offset = rp->b_in; 34862306a36Sopenharmony_ci rp->b_in = 0; 34962306a36Sopenharmony_ci rp->b_cnt += size; 35062306a36Sopenharmony_ci } else { 35162306a36Sopenharmony_ci offset = rp->b_in; 35262306a36Sopenharmony_ci rp->b_in += size; 35362306a36Sopenharmony_ci rp->b_cnt += size; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci return offset; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/* 35962306a36Sopenharmony_ci * Return a few (kilo-)bytes to the head of the buffer. 36062306a36Sopenharmony_ci * This is used if a data fetch fails. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_cistatic void mon_buff_area_shrink(struct mon_reader_bin *rp, unsigned int size) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* size &= ~(PKT_ALIGN-1); -- we're called with aligned size */ 36662306a36Sopenharmony_ci rp->b_cnt -= size; 36762306a36Sopenharmony_ci if (rp->b_in < size) 36862306a36Sopenharmony_ci rp->b_in += rp->b_size; 36962306a36Sopenharmony_ci rp->b_in -= size; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* 37362306a36Sopenharmony_ci * This has to be called under both b_lock and fetch_lock, because 37462306a36Sopenharmony_ci * it accesses both b_cnt and b_out. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_cistatic void mon_buff_area_free(struct mon_reader_bin *rp, unsigned int size) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 38062306a36Sopenharmony_ci rp->b_cnt -= size; 38162306a36Sopenharmony_ci if ((rp->b_out += size) >= rp->b_size) 38262306a36Sopenharmony_ci rp->b_out -= rp->b_size; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void mon_buff_area_fill(const struct mon_reader_bin *rp, 38662306a36Sopenharmony_ci unsigned int offset, unsigned int size) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct mon_bin_hdr *ep; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, offset); 39162306a36Sopenharmony_ci memset(ep, 0, PKT_SIZE); 39262306a36Sopenharmony_ci ep->type = '@'; 39362306a36Sopenharmony_ci ep->len_cap = size - PKT_SIZE; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic inline char mon_bin_get_setup(unsigned char *setupb, 39762306a36Sopenharmony_ci const struct urb *urb, char ev_type) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (urb->setup_packet == NULL) 40162306a36Sopenharmony_ci return 'Z'; 40262306a36Sopenharmony_ci memcpy(setupb, urb->setup_packet, SETUP_LEN); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic unsigned int mon_bin_get_data(const struct mon_reader_bin *rp, 40762306a36Sopenharmony_ci unsigned int offset, struct urb *urb, unsigned int length, 40862306a36Sopenharmony_ci char *flag) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int i; 41162306a36Sopenharmony_ci struct scatterlist *sg; 41262306a36Sopenharmony_ci unsigned int this_len; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci *flag = 0; 41562306a36Sopenharmony_ci if (urb->num_sgs == 0) { 41662306a36Sopenharmony_ci if (urb->transfer_buffer == NULL) { 41762306a36Sopenharmony_ci *flag = 'Z'; 41862306a36Sopenharmony_ci return length; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); 42162306a36Sopenharmony_ci length = 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci } else { 42462306a36Sopenharmony_ci /* If IOMMU coalescing occurred, we cannot trust sg_page */ 42562306a36Sopenharmony_ci if (urb->transfer_flags & URB_DMA_SG_COMBINED) { 42662306a36Sopenharmony_ci *flag = 'D'; 42762306a36Sopenharmony_ci return length; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Copy up to the first non-addressable segment */ 43162306a36Sopenharmony_ci for_each_sg(urb->sg, sg, urb->num_sgs, i) { 43262306a36Sopenharmony_ci if (length == 0 || PageHighMem(sg_page(sg))) 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci this_len = min_t(unsigned int, sg->length, length); 43562306a36Sopenharmony_ci offset = mon_copy_to_buff(rp, offset, sg_virt(sg), 43662306a36Sopenharmony_ci this_len); 43762306a36Sopenharmony_ci length -= this_len; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci if (i == 0) 44062306a36Sopenharmony_ci *flag = 'D'; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return length; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * This is the look-ahead pass in case of 'C Zi', when actual_length cannot 44862306a36Sopenharmony_ci * be used to determine the length of the whole contiguous buffer. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_cistatic unsigned int mon_bin_collate_isodesc(const struct mon_reader_bin *rp, 45162306a36Sopenharmony_ci struct urb *urb, unsigned int ndesc) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct usb_iso_packet_descriptor *fp; 45462306a36Sopenharmony_ci unsigned int length; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci length = 0; 45762306a36Sopenharmony_ci fp = urb->iso_frame_desc; 45862306a36Sopenharmony_ci while (ndesc-- != 0) { 45962306a36Sopenharmony_ci if (fp->actual_length != 0) { 46062306a36Sopenharmony_ci if (fp->offset + fp->actual_length > length) 46162306a36Sopenharmony_ci length = fp->offset + fp->actual_length; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci fp++; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci return length; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic void mon_bin_get_isodesc(const struct mon_reader_bin *rp, 46962306a36Sopenharmony_ci unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct mon_bin_isodesc *dp; 47262306a36Sopenharmony_ci struct usb_iso_packet_descriptor *fp; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci fp = urb->iso_frame_desc; 47562306a36Sopenharmony_ci while (ndesc-- != 0) { 47662306a36Sopenharmony_ci dp = (struct mon_bin_isodesc *) 47762306a36Sopenharmony_ci (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE); 47862306a36Sopenharmony_ci dp->iso_status = fp->status; 47962306a36Sopenharmony_ci dp->iso_off = fp->offset; 48062306a36Sopenharmony_ci dp->iso_len = (ev_type == 'S') ? fp->length : fp->actual_length; 48162306a36Sopenharmony_ci dp->_pad = 0; 48262306a36Sopenharmony_ci if ((offset += sizeof(struct mon_bin_isodesc)) >= rp->b_size) 48362306a36Sopenharmony_ci offset = 0; 48462306a36Sopenharmony_ci fp++; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, 48962306a36Sopenharmony_ci char ev_type, int status) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci const struct usb_endpoint_descriptor *epd = &urb->ep->desc; 49262306a36Sopenharmony_ci struct timespec64 ts; 49362306a36Sopenharmony_ci unsigned long flags; 49462306a36Sopenharmony_ci unsigned int urb_length; 49562306a36Sopenharmony_ci unsigned int offset; 49662306a36Sopenharmony_ci unsigned int length; 49762306a36Sopenharmony_ci unsigned int delta; 49862306a36Sopenharmony_ci unsigned int ndesc, lendesc; 49962306a36Sopenharmony_ci unsigned char dir; 50062306a36Sopenharmony_ci struct mon_bin_hdr *ep; 50162306a36Sopenharmony_ci char data_tag = 0; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ktime_get_real_ts64(&ts); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * Find the maximum allowable length, then allocate space. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci urb_length = (ev_type == 'S') ? 51162306a36Sopenharmony_ci urb->transfer_buffer_length : urb->actual_length; 51262306a36Sopenharmony_ci length = urb_length; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (usb_endpoint_xfer_isoc(epd)) { 51562306a36Sopenharmony_ci if (urb->number_of_packets < 0) { 51662306a36Sopenharmony_ci ndesc = 0; 51762306a36Sopenharmony_ci } else if (urb->number_of_packets >= ISODESC_MAX) { 51862306a36Sopenharmony_ci ndesc = ISODESC_MAX; 51962306a36Sopenharmony_ci } else { 52062306a36Sopenharmony_ci ndesc = urb->number_of_packets; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci if (ev_type == 'C' && usb_urb_dir_in(urb)) 52362306a36Sopenharmony_ci length = mon_bin_collate_isodesc(rp, urb, ndesc); 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci ndesc = 0; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci lendesc = ndesc*sizeof(struct mon_bin_isodesc); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* not an issue unless there's a subtle bug in a HCD somewhere */ 53062306a36Sopenharmony_ci if (length >= urb->transfer_buffer_length) 53162306a36Sopenharmony_ci length = urb->transfer_buffer_length; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (length >= rp->b_size/5) 53462306a36Sopenharmony_ci length = rp->b_size/5; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (usb_urb_dir_in(urb)) { 53762306a36Sopenharmony_ci if (ev_type == 'S') { 53862306a36Sopenharmony_ci length = 0; 53962306a36Sopenharmony_ci data_tag = '<'; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci /* Cannot rely on endpoint number in case of control ep.0 */ 54262306a36Sopenharmony_ci dir = USB_DIR_IN; 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci if (ev_type == 'C') { 54562306a36Sopenharmony_ci length = 0; 54662306a36Sopenharmony_ci data_tag = '>'; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci dir = 0; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (rp->mmap_active) { 55262306a36Sopenharmony_ci offset = mon_buff_area_alloc_contiguous(rp, 55362306a36Sopenharmony_ci length + PKT_SIZE + lendesc); 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci offset = mon_buff_area_alloc(rp, length + PKT_SIZE + lendesc); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci if (offset == ~0) { 55862306a36Sopenharmony_ci rp->cnt_lost++; 55962306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 56062306a36Sopenharmony_ci return; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, offset); 56462306a36Sopenharmony_ci if ((offset += PKT_SIZE) >= rp->b_size) offset = 0; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * Fill the allocated area. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci memset(ep, 0, PKT_SIZE); 57062306a36Sopenharmony_ci ep->type = ev_type; 57162306a36Sopenharmony_ci ep->xfer_type = xfer_to_pipe[usb_endpoint_type(epd)]; 57262306a36Sopenharmony_ci ep->epnum = dir | usb_endpoint_num(epd); 57362306a36Sopenharmony_ci ep->devnum = urb->dev->devnum; 57462306a36Sopenharmony_ci ep->busnum = urb->dev->bus->busnum; 57562306a36Sopenharmony_ci ep->id = (unsigned long) urb; 57662306a36Sopenharmony_ci ep->ts_sec = ts.tv_sec; 57762306a36Sopenharmony_ci ep->ts_usec = ts.tv_nsec / NSEC_PER_USEC; 57862306a36Sopenharmony_ci ep->status = status; 57962306a36Sopenharmony_ci ep->len_urb = urb_length; 58062306a36Sopenharmony_ci ep->len_cap = length + lendesc; 58162306a36Sopenharmony_ci ep->xfer_flags = urb->transfer_flags; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (usb_endpoint_xfer_int(epd)) { 58462306a36Sopenharmony_ci ep->interval = urb->interval; 58562306a36Sopenharmony_ci } else if (usb_endpoint_xfer_isoc(epd)) { 58662306a36Sopenharmony_ci ep->interval = urb->interval; 58762306a36Sopenharmony_ci ep->start_frame = urb->start_frame; 58862306a36Sopenharmony_ci ep->s.iso.error_count = urb->error_count; 58962306a36Sopenharmony_ci ep->s.iso.numdesc = urb->number_of_packets; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (usb_endpoint_xfer_control(epd) && ev_type == 'S') { 59362306a36Sopenharmony_ci ep->flag_setup = mon_bin_get_setup(ep->s.setup, urb, ev_type); 59462306a36Sopenharmony_ci } else { 59562306a36Sopenharmony_ci ep->flag_setup = '-'; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (ndesc != 0) { 59962306a36Sopenharmony_ci ep->ndesc = ndesc; 60062306a36Sopenharmony_ci mon_bin_get_isodesc(rp, offset, urb, ev_type, ndesc); 60162306a36Sopenharmony_ci if ((offset += lendesc) >= rp->b_size) 60262306a36Sopenharmony_ci offset -= rp->b_size; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (length != 0) { 60662306a36Sopenharmony_ci length = mon_bin_get_data(rp, offset, urb, length, 60762306a36Sopenharmony_ci &ep->flag_data); 60862306a36Sopenharmony_ci if (length > 0) { 60962306a36Sopenharmony_ci delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 61062306a36Sopenharmony_ci ep->len_cap -= length; 61162306a36Sopenharmony_ci delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 61262306a36Sopenharmony_ci mon_buff_area_shrink(rp, delta); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci } else { 61562306a36Sopenharmony_ci ep->flag_data = data_tag; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci wake_up(&rp->b_wait); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void mon_bin_submit(void *data, struct urb *urb) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct mon_reader_bin *rp = data; 62662306a36Sopenharmony_ci mon_bin_event(rp, urb, 'S', -EINPROGRESS); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void mon_bin_complete(void *data, struct urb *urb, int status) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct mon_reader_bin *rp = data; 63262306a36Sopenharmony_ci mon_bin_event(rp, urb, 'C', status); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void mon_bin_error(void *data, struct urb *urb, int error) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct mon_reader_bin *rp = data; 63862306a36Sopenharmony_ci struct timespec64 ts; 63962306a36Sopenharmony_ci unsigned long flags; 64062306a36Sopenharmony_ci unsigned int offset; 64162306a36Sopenharmony_ci struct mon_bin_hdr *ep; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ktime_get_real_ts64(&ts); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci offset = mon_buff_area_alloc(rp, PKT_SIZE); 64862306a36Sopenharmony_ci if (offset == ~0) { 64962306a36Sopenharmony_ci /* Not incrementing cnt_lost. Just because. */ 65062306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, offset); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci memset(ep, 0, PKT_SIZE); 65762306a36Sopenharmony_ci ep->type = 'E'; 65862306a36Sopenharmony_ci ep->xfer_type = xfer_to_pipe[usb_endpoint_type(&urb->ep->desc)]; 65962306a36Sopenharmony_ci ep->epnum = usb_urb_dir_in(urb) ? USB_DIR_IN : 0; 66062306a36Sopenharmony_ci ep->epnum |= usb_endpoint_num(&urb->ep->desc); 66162306a36Sopenharmony_ci ep->devnum = urb->dev->devnum; 66262306a36Sopenharmony_ci ep->busnum = urb->dev->bus->busnum; 66362306a36Sopenharmony_ci ep->id = (unsigned long) urb; 66462306a36Sopenharmony_ci ep->ts_sec = ts.tv_sec; 66562306a36Sopenharmony_ci ep->ts_usec = ts.tv_nsec / NSEC_PER_USEC; 66662306a36Sopenharmony_ci ep->status = error; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ep->flag_setup = '-'; 66962306a36Sopenharmony_ci ep->flag_data = 'E'; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci wake_up(&rp->b_wait); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic int mon_bin_open(struct inode *inode, struct file *file) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct mon_bus *mbus; 67962306a36Sopenharmony_ci struct mon_reader_bin *rp; 68062306a36Sopenharmony_ci size_t size; 68162306a36Sopenharmony_ci int rc; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mutex_lock(&mon_lock); 68462306a36Sopenharmony_ci mbus = mon_bus_lookup(iminor(inode)); 68562306a36Sopenharmony_ci if (mbus == NULL) { 68662306a36Sopenharmony_ci mutex_unlock(&mon_lock); 68762306a36Sopenharmony_ci return -ENODEV; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci if (mbus != &mon_bus0 && mbus->u_bus == NULL) { 69062306a36Sopenharmony_ci printk(KERN_ERR TAG ": consistency error on open\n"); 69162306a36Sopenharmony_ci mutex_unlock(&mon_lock); 69262306a36Sopenharmony_ci return -ENODEV; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci rp = kzalloc(sizeof(struct mon_reader_bin), GFP_KERNEL); 69662306a36Sopenharmony_ci if (rp == NULL) { 69762306a36Sopenharmony_ci rc = -ENOMEM; 69862306a36Sopenharmony_ci goto err_alloc; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci spin_lock_init(&rp->b_lock); 70162306a36Sopenharmony_ci init_waitqueue_head(&rp->b_wait); 70262306a36Sopenharmony_ci mutex_init(&rp->fetch_lock); 70362306a36Sopenharmony_ci rp->b_size = BUFF_DFL; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci size = sizeof(struct mon_pgmap) * (rp->b_size/CHUNK_SIZE); 70662306a36Sopenharmony_ci if ((rp->b_vec = kzalloc(size, GFP_KERNEL)) == NULL) { 70762306a36Sopenharmony_ci rc = -ENOMEM; 70862306a36Sopenharmony_ci goto err_allocvec; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if ((rc = mon_alloc_buff(rp->b_vec, rp->b_size/CHUNK_SIZE)) < 0) 71262306a36Sopenharmony_ci goto err_allocbuff; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci rp->r.m_bus = mbus; 71562306a36Sopenharmony_ci rp->r.r_data = rp; 71662306a36Sopenharmony_ci rp->r.rnf_submit = mon_bin_submit; 71762306a36Sopenharmony_ci rp->r.rnf_error = mon_bin_error; 71862306a36Sopenharmony_ci rp->r.rnf_complete = mon_bin_complete; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci mon_reader_add(mbus, &rp->r); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci file->private_data = rp; 72362306a36Sopenharmony_ci mutex_unlock(&mon_lock); 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cierr_allocbuff: 72762306a36Sopenharmony_ci kfree(rp->b_vec); 72862306a36Sopenharmony_cierr_allocvec: 72962306a36Sopenharmony_ci kfree(rp); 73062306a36Sopenharmony_cierr_alloc: 73162306a36Sopenharmony_ci mutex_unlock(&mon_lock); 73262306a36Sopenharmony_ci return rc; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/* 73662306a36Sopenharmony_ci * Extract an event from buffer and copy it to user space. 73762306a36Sopenharmony_ci * Wait if there is no event ready. 73862306a36Sopenharmony_ci * Returns zero or error. 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_cistatic int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, 74162306a36Sopenharmony_ci struct mon_bin_hdr __user *hdr, unsigned int hdrbytes, 74262306a36Sopenharmony_ci void __user *data, unsigned int nbytes) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci unsigned long flags; 74562306a36Sopenharmony_ci struct mon_bin_hdr *ep; 74662306a36Sopenharmony_ci size_t step_len; 74762306a36Sopenharmony_ci unsigned int offset; 74862306a36Sopenharmony_ci int rc; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if ((rc = mon_bin_wait_event(file, rp)) < 0) { 75362306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 75462306a36Sopenharmony_ci return rc; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, rp->b_out); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (copy_to_user(hdr, ep, hdrbytes)) { 76062306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 76162306a36Sopenharmony_ci return -EFAULT; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci step_len = min(ep->len_cap, nbytes); 76562306a36Sopenharmony_ci if ((offset = rp->b_out + PKT_SIZE) >= rp->b_size) offset = 0; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (copy_from_buf(rp, offset, data, step_len)) { 76862306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 76962306a36Sopenharmony_ci return -EFAULT; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 77362306a36Sopenharmony_ci mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); 77462306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 77562306a36Sopenharmony_ci rp->b_read = 0; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int mon_bin_release(struct inode *inode, struct file *file) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct mon_reader_bin *rp = file->private_data; 78462306a36Sopenharmony_ci struct mon_bus* mbus = rp->r.m_bus; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci mutex_lock(&mon_lock); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (mbus->nreaders <= 0) { 78962306a36Sopenharmony_ci printk(KERN_ERR TAG ": consistency error on close\n"); 79062306a36Sopenharmony_ci mutex_unlock(&mon_lock); 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci mon_reader_del(mbus, &rp->r); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); 79662306a36Sopenharmony_ci kfree(rp->b_vec); 79762306a36Sopenharmony_ci kfree(rp); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci mutex_unlock(&mon_lock); 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic ssize_t mon_bin_read(struct file *file, char __user *buf, 80462306a36Sopenharmony_ci size_t nbytes, loff_t *ppos) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct mon_reader_bin *rp = file->private_data; 80762306a36Sopenharmony_ci unsigned int hdrbytes = PKT_SZ_API0; 80862306a36Sopenharmony_ci unsigned long flags; 80962306a36Sopenharmony_ci struct mon_bin_hdr *ep; 81062306a36Sopenharmony_ci unsigned int offset; 81162306a36Sopenharmony_ci size_t step_len; 81262306a36Sopenharmony_ci char *ptr; 81362306a36Sopenharmony_ci ssize_t done = 0; 81462306a36Sopenharmony_ci int rc; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if ((rc = mon_bin_wait_event(file, rp)) < 0) { 81962306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 82062306a36Sopenharmony_ci return rc; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, rp->b_out); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (rp->b_read < hdrbytes) { 82662306a36Sopenharmony_ci step_len = min(nbytes, (size_t)(hdrbytes - rp->b_read)); 82762306a36Sopenharmony_ci ptr = ((char *)ep) + rp->b_read; 82862306a36Sopenharmony_ci if (step_len && copy_to_user(buf, ptr, step_len)) { 82962306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 83062306a36Sopenharmony_ci return -EFAULT; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci nbytes -= step_len; 83362306a36Sopenharmony_ci buf += step_len; 83462306a36Sopenharmony_ci rp->b_read += step_len; 83562306a36Sopenharmony_ci done += step_len; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (rp->b_read >= hdrbytes) { 83962306a36Sopenharmony_ci step_len = ep->len_cap; 84062306a36Sopenharmony_ci step_len -= rp->b_read - hdrbytes; 84162306a36Sopenharmony_ci if (step_len > nbytes) 84262306a36Sopenharmony_ci step_len = nbytes; 84362306a36Sopenharmony_ci offset = rp->b_out + PKT_SIZE; 84462306a36Sopenharmony_ci offset += rp->b_read - hdrbytes; 84562306a36Sopenharmony_ci if (offset >= rp->b_size) 84662306a36Sopenharmony_ci offset -= rp->b_size; 84762306a36Sopenharmony_ci if (copy_from_buf(rp, offset, buf, step_len)) { 84862306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 84962306a36Sopenharmony_ci return -EFAULT; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci nbytes -= step_len; 85262306a36Sopenharmony_ci buf += step_len; 85362306a36Sopenharmony_ci rp->b_read += step_len; 85462306a36Sopenharmony_ci done += step_len; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* 85862306a36Sopenharmony_ci * Check if whole packet was read, and if so, jump to the next one. 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_ci if (rp->b_read >= hdrbytes + ep->len_cap) { 86162306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 86262306a36Sopenharmony_ci mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); 86362306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 86462306a36Sopenharmony_ci rp->b_read = 0; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 86862306a36Sopenharmony_ci return done; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/* 87262306a36Sopenharmony_ci * Remove at most nevents from chunked buffer. 87362306a36Sopenharmony_ci * Returns the number of removed events. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_cistatic int mon_bin_flush(struct mon_reader_bin *rp, unsigned nevents) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci unsigned long flags; 87862306a36Sopenharmony_ci struct mon_bin_hdr *ep; 87962306a36Sopenharmony_ci int i; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 88262306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 88362306a36Sopenharmony_ci for (i = 0; i < nevents; ++i) { 88462306a36Sopenharmony_ci if (MON_RING_EMPTY(rp)) 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, rp->b_out); 88862306a36Sopenharmony_ci mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 89162306a36Sopenharmony_ci rp->b_read = 0; 89262306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 89362306a36Sopenharmony_ci return i; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci/* 89762306a36Sopenharmony_ci * Fetch at most max event offsets into the buffer and put them into vec. 89862306a36Sopenharmony_ci * The events are usually freed later with mon_bin_flush. 89962306a36Sopenharmony_ci * Return the effective number of events fetched. 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_cistatic int mon_bin_fetch(struct file *file, struct mon_reader_bin *rp, 90262306a36Sopenharmony_ci u32 __user *vec, unsigned int max) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci unsigned int cur_out; 90562306a36Sopenharmony_ci unsigned int bytes, avail; 90662306a36Sopenharmony_ci unsigned int size; 90762306a36Sopenharmony_ci unsigned int nevents; 90862306a36Sopenharmony_ci struct mon_bin_hdr *ep; 90962306a36Sopenharmony_ci unsigned long flags; 91062306a36Sopenharmony_ci int rc; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if ((rc = mon_bin_wait_event(file, rp)) < 0) { 91562306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 91662306a36Sopenharmony_ci return rc; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 92062306a36Sopenharmony_ci avail = rp->b_cnt; 92162306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci cur_out = rp->b_out; 92462306a36Sopenharmony_ci nevents = 0; 92562306a36Sopenharmony_ci bytes = 0; 92662306a36Sopenharmony_ci while (bytes < avail) { 92762306a36Sopenharmony_ci if (nevents >= max) 92862306a36Sopenharmony_ci break; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, cur_out); 93162306a36Sopenharmony_ci if (put_user(cur_out, &vec[nevents])) { 93262306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 93362306a36Sopenharmony_ci return -EFAULT; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci nevents++; 93762306a36Sopenharmony_ci size = ep->len_cap + PKT_SIZE; 93862306a36Sopenharmony_ci size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 93962306a36Sopenharmony_ci if ((cur_out += size) >= rp->b_size) 94062306a36Sopenharmony_ci cur_out -= rp->b_size; 94162306a36Sopenharmony_ci bytes += size; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 94562306a36Sopenharmony_ci return nevents; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* 94962306a36Sopenharmony_ci * Count events. This is almost the same as the above mon_bin_fetch, 95062306a36Sopenharmony_ci * only we do not store offsets into user vector, and we have no limit. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_cistatic int mon_bin_queued(struct mon_reader_bin *rp) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci unsigned int cur_out; 95562306a36Sopenharmony_ci unsigned int bytes, avail; 95662306a36Sopenharmony_ci unsigned int size; 95762306a36Sopenharmony_ci unsigned int nevents; 95862306a36Sopenharmony_ci struct mon_bin_hdr *ep; 95962306a36Sopenharmony_ci unsigned long flags; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 96462306a36Sopenharmony_ci avail = rp->b_cnt; 96562306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci cur_out = rp->b_out; 96862306a36Sopenharmony_ci nevents = 0; 96962306a36Sopenharmony_ci bytes = 0; 97062306a36Sopenharmony_ci while (bytes < avail) { 97162306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, cur_out); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci nevents++; 97462306a36Sopenharmony_ci size = ep->len_cap + PKT_SIZE; 97562306a36Sopenharmony_ci size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); 97662306a36Sopenharmony_ci if ((cur_out += size) >= rp->b_size) 97762306a36Sopenharmony_ci cur_out -= rp->b_size; 97862306a36Sopenharmony_ci bytes += size; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 98262306a36Sopenharmony_ci return nevents; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci/* 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_cistatic long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct mon_reader_bin *rp = file->private_data; 99062306a36Sopenharmony_ci // struct mon_bus* mbus = rp->r.m_bus; 99162306a36Sopenharmony_ci int ret = 0; 99262306a36Sopenharmony_ci struct mon_bin_hdr *ep; 99362306a36Sopenharmony_ci unsigned long flags; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci switch (cmd) { 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci case MON_IOCQ_URB_LEN: 99862306a36Sopenharmony_ci /* 99962306a36Sopenharmony_ci * N.B. This only returns the size of data, without the header. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 100262306a36Sopenharmony_ci if (!MON_RING_EMPTY(rp)) { 100362306a36Sopenharmony_ci ep = MON_OFF2HDR(rp, rp->b_out); 100462306a36Sopenharmony_ci ret = ep->len_cap; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 100762306a36Sopenharmony_ci break; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci case MON_IOCQ_RING_SIZE: 101062306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 101162306a36Sopenharmony_ci ret = rp->b_size; 101262306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci case MON_IOCT_RING_SIZE: 101662306a36Sopenharmony_ci /* 101762306a36Sopenharmony_ci * Changing the buffer size will flush it's contents; the new 101862306a36Sopenharmony_ci * buffer is allocated before releasing the old one to be sure 101962306a36Sopenharmony_ci * the device will stay functional also in case of memory 102062306a36Sopenharmony_ci * pressure. 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci { 102362306a36Sopenharmony_ci int size; 102462306a36Sopenharmony_ci struct mon_pgmap *vec; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (arg < BUFF_MIN || arg > BUFF_MAX) 102762306a36Sopenharmony_ci return -EINVAL; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci size = CHUNK_ALIGN(arg); 103062306a36Sopenharmony_ci vec = kcalloc(size / CHUNK_SIZE, sizeof(struct mon_pgmap), 103162306a36Sopenharmony_ci GFP_KERNEL); 103262306a36Sopenharmony_ci if (vec == NULL) { 103362306a36Sopenharmony_ci ret = -ENOMEM; 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci ret = mon_alloc_buff(vec, size/CHUNK_SIZE); 103862306a36Sopenharmony_ci if (ret < 0) { 103962306a36Sopenharmony_ci kfree(vec); 104062306a36Sopenharmony_ci break; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci mutex_lock(&rp->fetch_lock); 104462306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 104562306a36Sopenharmony_ci if (rp->mmap_active) { 104662306a36Sopenharmony_ci mon_free_buff(vec, size/CHUNK_SIZE); 104762306a36Sopenharmony_ci kfree(vec); 104862306a36Sopenharmony_ci ret = -EBUSY; 104962306a36Sopenharmony_ci } else { 105062306a36Sopenharmony_ci mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); 105162306a36Sopenharmony_ci kfree(rp->b_vec); 105262306a36Sopenharmony_ci rp->b_vec = vec; 105362306a36Sopenharmony_ci rp->b_size = size; 105462306a36Sopenharmony_ci rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0; 105562306a36Sopenharmony_ci rp->cnt_lost = 0; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 105862306a36Sopenharmony_ci mutex_unlock(&rp->fetch_lock); 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci case MON_IOCH_MFLUSH: 106362306a36Sopenharmony_ci ret = mon_bin_flush(rp, arg); 106462306a36Sopenharmony_ci break; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci case MON_IOCX_GET: 106762306a36Sopenharmony_ci case MON_IOCX_GETX: 106862306a36Sopenharmony_ci { 106962306a36Sopenharmony_ci struct mon_bin_get getb; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (copy_from_user(&getb, (void __user *)arg, 107262306a36Sopenharmony_ci sizeof(struct mon_bin_get))) 107362306a36Sopenharmony_ci return -EFAULT; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (getb.alloc > 0x10000000) /* Want to cast to u32 */ 107662306a36Sopenharmony_ci return -EINVAL; 107762306a36Sopenharmony_ci ret = mon_bin_get_event(file, rp, getb.hdr, 107862306a36Sopenharmony_ci (cmd == MON_IOCX_GET)? PKT_SZ_API0: PKT_SZ_API1, 107962306a36Sopenharmony_ci getb.data, (unsigned int)getb.alloc); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci case MON_IOCX_MFETCH: 108462306a36Sopenharmony_ci { 108562306a36Sopenharmony_ci struct mon_bin_mfetch mfetch; 108662306a36Sopenharmony_ci struct mon_bin_mfetch __user *uptr; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci uptr = (struct mon_bin_mfetch __user *)arg; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (copy_from_user(&mfetch, uptr, sizeof(mfetch))) 109162306a36Sopenharmony_ci return -EFAULT; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (mfetch.nflush) { 109462306a36Sopenharmony_ci ret = mon_bin_flush(rp, mfetch.nflush); 109562306a36Sopenharmony_ci if (ret < 0) 109662306a36Sopenharmony_ci return ret; 109762306a36Sopenharmony_ci if (put_user(ret, &uptr->nflush)) 109862306a36Sopenharmony_ci return -EFAULT; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci ret = mon_bin_fetch(file, rp, mfetch.offvec, mfetch.nfetch); 110162306a36Sopenharmony_ci if (ret < 0) 110262306a36Sopenharmony_ci return ret; 110362306a36Sopenharmony_ci if (put_user(ret, &uptr->nfetch)) 110462306a36Sopenharmony_ci return -EFAULT; 110562306a36Sopenharmony_ci ret = 0; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci case MON_IOCG_STATS: { 111062306a36Sopenharmony_ci struct mon_bin_stats __user *sp; 111162306a36Sopenharmony_ci unsigned int nevents; 111262306a36Sopenharmony_ci unsigned int ndropped; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 111562306a36Sopenharmony_ci ndropped = rp->cnt_lost; 111662306a36Sopenharmony_ci rp->cnt_lost = 0; 111762306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 111862306a36Sopenharmony_ci nevents = mon_bin_queued(rp); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci sp = (struct mon_bin_stats __user *)arg; 112162306a36Sopenharmony_ci if (put_user(ndropped, &sp->dropped)) 112262306a36Sopenharmony_ci return -EFAULT; 112362306a36Sopenharmony_ci if (put_user(nevents, &sp->queued)) 112462306a36Sopenharmony_ci return -EFAULT; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci break; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci default: 113062306a36Sopenharmony_ci return -ENOTTY; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci return ret; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 113762306a36Sopenharmony_cistatic long mon_bin_compat_ioctl(struct file *file, 113862306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci struct mon_reader_bin *rp = file->private_data; 114162306a36Sopenharmony_ci int ret; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci switch (cmd) { 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci case MON_IOCX_GET32: 114662306a36Sopenharmony_ci case MON_IOCX_GETX32: 114762306a36Sopenharmony_ci { 114862306a36Sopenharmony_ci struct mon_bin_get32 getb; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (copy_from_user(&getb, (void __user *)arg, 115162306a36Sopenharmony_ci sizeof(struct mon_bin_get32))) 115262306a36Sopenharmony_ci return -EFAULT; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ret = mon_bin_get_event(file, rp, compat_ptr(getb.hdr32), 115562306a36Sopenharmony_ci (cmd == MON_IOCX_GET32)? PKT_SZ_API0: PKT_SZ_API1, 115662306a36Sopenharmony_ci compat_ptr(getb.data32), getb.alloc32); 115762306a36Sopenharmony_ci if (ret < 0) 115862306a36Sopenharmony_ci return ret; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci return 0; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci case MON_IOCX_MFETCH32: 116362306a36Sopenharmony_ci { 116462306a36Sopenharmony_ci struct mon_bin_mfetch32 mfetch; 116562306a36Sopenharmony_ci struct mon_bin_mfetch32 __user *uptr; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci uptr = (struct mon_bin_mfetch32 __user *) compat_ptr(arg); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (copy_from_user(&mfetch, uptr, sizeof(mfetch))) 117062306a36Sopenharmony_ci return -EFAULT; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (mfetch.nflush32) { 117362306a36Sopenharmony_ci ret = mon_bin_flush(rp, mfetch.nflush32); 117462306a36Sopenharmony_ci if (ret < 0) 117562306a36Sopenharmony_ci return ret; 117662306a36Sopenharmony_ci if (put_user(ret, &uptr->nflush32)) 117762306a36Sopenharmony_ci return -EFAULT; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci ret = mon_bin_fetch(file, rp, compat_ptr(mfetch.offvec32), 118062306a36Sopenharmony_ci mfetch.nfetch32); 118162306a36Sopenharmony_ci if (ret < 0) 118262306a36Sopenharmony_ci return ret; 118362306a36Sopenharmony_ci if (put_user(ret, &uptr->nfetch32)) 118462306a36Sopenharmony_ci return -EFAULT; 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci case MON_IOCG_STATS: 118962306a36Sopenharmony_ci return mon_bin_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci case MON_IOCQ_URB_LEN: 119262306a36Sopenharmony_ci case MON_IOCQ_RING_SIZE: 119362306a36Sopenharmony_ci case MON_IOCT_RING_SIZE: 119462306a36Sopenharmony_ci case MON_IOCH_MFLUSH: 119562306a36Sopenharmony_ci return mon_bin_ioctl(file, cmd, arg); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci default: 119862306a36Sopenharmony_ci ; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci return -ENOTTY; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci#endif /* CONFIG_COMPAT */ 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic __poll_t 120562306a36Sopenharmony_cimon_bin_poll(struct file *file, struct poll_table_struct *wait) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci struct mon_reader_bin *rp = file->private_data; 120862306a36Sopenharmony_ci __poll_t mask = 0; 120962306a36Sopenharmony_ci unsigned long flags; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (file->f_mode & FMODE_READ) 121262306a36Sopenharmony_ci poll_wait(file, &rp->b_wait, wait); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 121562306a36Sopenharmony_ci if (!MON_RING_EMPTY(rp)) 121662306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; /* readable */ 121762306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 121862306a36Sopenharmony_ci return mask; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci/* 122262306a36Sopenharmony_ci * open and close: just keep track of how many times the device is 122362306a36Sopenharmony_ci * mapped, to use the proper memory allocation function. 122462306a36Sopenharmony_ci */ 122562306a36Sopenharmony_cistatic void mon_bin_vma_open(struct vm_area_struct *vma) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci struct mon_reader_bin *rp = vma->vm_private_data; 122862306a36Sopenharmony_ci unsigned long flags; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 123162306a36Sopenharmony_ci rp->mmap_active++; 123262306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic void mon_bin_vma_close(struct vm_area_struct *vma) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci unsigned long flags; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci struct mon_reader_bin *rp = vma->vm_private_data; 124062306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 124162306a36Sopenharmony_ci rp->mmap_active--; 124262306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci/* 124662306a36Sopenharmony_ci * Map ring pages to user space. 124762306a36Sopenharmony_ci */ 124862306a36Sopenharmony_cistatic vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct mon_reader_bin *rp = vmf->vma->vm_private_data; 125162306a36Sopenharmony_ci unsigned long offset, chunk_idx; 125262306a36Sopenharmony_ci struct page *pageptr; 125362306a36Sopenharmony_ci unsigned long flags; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 125662306a36Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 125762306a36Sopenharmony_ci if (offset >= rp->b_size) { 125862306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 125962306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci chunk_idx = offset / CHUNK_SIZE; 126262306a36Sopenharmony_ci pageptr = rp->b_vec[chunk_idx].pg; 126362306a36Sopenharmony_ci get_page(pageptr); 126462306a36Sopenharmony_ci vmf->page = pageptr; 126562306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 126662306a36Sopenharmony_ci return 0; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic const struct vm_operations_struct mon_bin_vm_ops = { 127062306a36Sopenharmony_ci .open = mon_bin_vma_open, 127162306a36Sopenharmony_ci .close = mon_bin_vma_close, 127262306a36Sopenharmony_ci .fault = mon_bin_vma_fault, 127362306a36Sopenharmony_ci}; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci /* don't do anything here: "fault" will set up page table entries */ 127862306a36Sopenharmony_ci vma->vm_ops = &mon_bin_vm_ops; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (vma->vm_flags & VM_WRITE) 128162306a36Sopenharmony_ci return -EPERM; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci vm_flags_mod(vma, VM_DONTEXPAND | VM_DONTDUMP, VM_MAYWRITE); 128462306a36Sopenharmony_ci vma->vm_private_data = filp->private_data; 128562306a36Sopenharmony_ci mon_bin_vma_open(vma); 128662306a36Sopenharmony_ci return 0; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic const struct file_operations mon_fops_binary = { 129062306a36Sopenharmony_ci .owner = THIS_MODULE, 129162306a36Sopenharmony_ci .open = mon_bin_open, 129262306a36Sopenharmony_ci .llseek = no_llseek, 129362306a36Sopenharmony_ci .read = mon_bin_read, 129462306a36Sopenharmony_ci /* .write = mon_text_write, */ 129562306a36Sopenharmony_ci .poll = mon_bin_poll, 129662306a36Sopenharmony_ci .unlocked_ioctl = mon_bin_ioctl, 129762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 129862306a36Sopenharmony_ci .compat_ioctl = mon_bin_compat_ioctl, 129962306a36Sopenharmony_ci#endif 130062306a36Sopenharmony_ci .release = mon_bin_release, 130162306a36Sopenharmony_ci .mmap = mon_bin_mmap, 130262306a36Sopenharmony_ci}; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic int mon_bin_wait_event(struct file *file, struct mon_reader_bin *rp) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci DECLARE_WAITQUEUE(waita, current); 130762306a36Sopenharmony_ci unsigned long flags; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci add_wait_queue(&rp->b_wait, &waita); 131062306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 131362306a36Sopenharmony_ci while (MON_RING_EMPTY(rp)) { 131462306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 131762306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 131862306a36Sopenharmony_ci remove_wait_queue(&rp->b_wait, &waita); 131962306a36Sopenharmony_ci return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci schedule(); 132262306a36Sopenharmony_ci if (signal_pending(current)) { 132362306a36Sopenharmony_ci remove_wait_queue(&rp->b_wait, &waita); 132462306a36Sopenharmony_ci return -EINTR; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci spin_lock_irqsave(&rp->b_lock, flags); 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci spin_unlock_irqrestore(&rp->b_lock, flags); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 133362306a36Sopenharmony_ci remove_wait_queue(&rp->b_wait, &waita); 133462306a36Sopenharmony_ci return 0; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic int mon_alloc_buff(struct mon_pgmap *map, int npages) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci int n; 134062306a36Sopenharmony_ci unsigned long vaddr; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci for (n = 0; n < npages; n++) { 134362306a36Sopenharmony_ci vaddr = get_zeroed_page(GFP_KERNEL); 134462306a36Sopenharmony_ci if (vaddr == 0) { 134562306a36Sopenharmony_ci while (n-- != 0) 134662306a36Sopenharmony_ci free_page((unsigned long) map[n].ptr); 134762306a36Sopenharmony_ci return -ENOMEM; 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci map[n].ptr = (unsigned char *) vaddr; 135062306a36Sopenharmony_ci map[n].pg = virt_to_page((void *) vaddr); 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci return 0; 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic void mon_free_buff(struct mon_pgmap *map, int npages) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci int n; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci for (n = 0; n < npages; n++) 136062306a36Sopenharmony_ci free_page((unsigned long) map[n].ptr); 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ciint mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci struct device *dev; 136662306a36Sopenharmony_ci unsigned minor = ubus? ubus->busnum: 0; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci if (minor >= MON_BIN_MAX_MINOR) 136962306a36Sopenharmony_ci return 0; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci dev = device_create(&mon_bin_class, ubus ? ubus->controller : NULL, 137262306a36Sopenharmony_ci MKDEV(MAJOR(mon_bin_dev0), minor), NULL, 137362306a36Sopenharmony_ci "usbmon%d", minor); 137462306a36Sopenharmony_ci if (IS_ERR(dev)) 137562306a36Sopenharmony_ci return 0; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci mbus->classdev = dev; 137862306a36Sopenharmony_ci return 1; 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_civoid mon_bin_del(struct mon_bus *mbus) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci device_destroy(&mon_bin_class, mbus->classdev->devt); 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ciint __init mon_bin_init(void) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci int rc; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci rc = class_register(&mon_bin_class); 139162306a36Sopenharmony_ci if (rc) 139262306a36Sopenharmony_ci goto err_class; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon"); 139562306a36Sopenharmony_ci if (rc < 0) 139662306a36Sopenharmony_ci goto err_dev; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci cdev_init(&mon_bin_cdev, &mon_fops_binary); 139962306a36Sopenharmony_ci mon_bin_cdev.owner = THIS_MODULE; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci rc = cdev_add(&mon_bin_cdev, mon_bin_dev0, MON_BIN_MAX_MINOR); 140262306a36Sopenharmony_ci if (rc < 0) 140362306a36Sopenharmony_ci goto err_add; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci return 0; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cierr_add: 140862306a36Sopenharmony_ci unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); 140962306a36Sopenharmony_cierr_dev: 141062306a36Sopenharmony_ci class_unregister(&mon_bin_class); 141162306a36Sopenharmony_cierr_class: 141262306a36Sopenharmony_ci return rc; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_civoid mon_bin_exit(void) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci cdev_del(&mon_bin_cdev); 141862306a36Sopenharmony_ci unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); 141962306a36Sopenharmony_ci class_unregister(&mon_bin_class); 142062306a36Sopenharmony_ci} 1421