diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/mon/mon_text.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/mon/mon_text.c')
-rw-r--r-- | drivers/usb/mon/mon_text.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c new file mode 100644 index 000000000000..755a4570477f --- /dev/null +++ b/drivers/usb/mon/mon_text.c | |||
@@ -0,0 +1,405 @@ | |||
1 | /* | ||
2 | * The USB Monitor, inspired by Dave Harding's USBMon. | ||
3 | * | ||
4 | * This is a text format reader. | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/usb.h> | ||
10 | #include <linux/time.h> | ||
11 | #include <asm/uaccess.h> | ||
12 | |||
13 | #include "usb_mon.h" | ||
14 | |||
15 | /* | ||
16 | * No, we do not want arbitrarily long data strings. | ||
17 | * Use the binary interface if you want to capture bulk data! | ||
18 | */ | ||
19 | #define DATA_MAX 32 | ||
20 | |||
21 | /* | ||
22 | * This limit exists to prevent OOMs when the user process stops reading. | ||
23 | */ | ||
24 | #define EVENT_MAX 25 | ||
25 | |||
26 | #define PRINTF_DFL 120 | ||
27 | |||
28 | struct mon_event_text { | ||
29 | struct list_head e_link; | ||
30 | int type; /* submit, complete, etc. */ | ||
31 | unsigned int pipe; /* Pipe */ | ||
32 | unsigned long id; /* From pointer, most of the time */ | ||
33 | unsigned int tstamp; | ||
34 | int length; /* Depends on type: xfer length or act length */ | ||
35 | int status; | ||
36 | char data_flag; | ||
37 | unsigned char data[DATA_MAX]; | ||
38 | }; | ||
39 | |||
40 | #define SLAB_NAME_SZ 30 | ||
41 | struct mon_reader_text { | ||
42 | kmem_cache_t *e_slab; | ||
43 | int nevents; | ||
44 | struct list_head e_list; | ||
45 | struct mon_reader r; /* In C, parent class can be placed anywhere */ | ||
46 | |||
47 | wait_queue_head_t wait; | ||
48 | int printf_size; | ||
49 | char *printf_buf; | ||
50 | struct semaphore printf_lock; | ||
51 | |||
52 | char slab_name[SLAB_NAME_SZ]; | ||
53 | }; | ||
54 | |||
55 | static void mon_text_ctor(void *, kmem_cache_t *, unsigned long); | ||
56 | static void mon_text_dtor(void *, kmem_cache_t *, unsigned long); | ||
57 | |||
58 | /* | ||
59 | * mon_text_submit | ||
60 | * mon_text_complete | ||
61 | * | ||
62 | * May be called from an interrupt. | ||
63 | * | ||
64 | * This is called with the whole mon_bus locked, so no additional lock. | ||
65 | */ | ||
66 | |||
67 | static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, | ||
68 | int len, char ev_type) | ||
69 | { | ||
70 | int pipe = urb->pipe; | ||
71 | unsigned char *data; | ||
72 | |||
73 | /* | ||
74 | * The check to see if it's safe to poke at data has an enormous | ||
75 | * number of corner cases, but it seems that the following is | ||
76 | * more or less safe. | ||
77 | * | ||
78 | * We do not even try to look transfer_buffer, because it can | ||
79 | * contain non-NULL garbage in case the upper level promised to | ||
80 | * set DMA for the HCD. | ||
81 | */ | ||
82 | if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) | ||
83 | return 'D'; | ||
84 | |||
85 | if (len <= 0) | ||
86 | return 'L'; | ||
87 | |||
88 | if ((data = urb->transfer_buffer) == NULL) | ||
89 | return 'Z'; /* '0' would be not as pretty. */ | ||
90 | |||
91 | /* | ||
92 | * Bulk is easy to shortcut reliably. | ||
93 | * XXX Control needs setup packet taken. | ||
94 | * XXX Other pipe types need consideration. Currently, we overdo it | ||
95 | * and collect garbage for them: better more than less. | ||
96 | */ | ||
97 | if (usb_pipebulk(pipe) || usb_pipecontrol(pipe)) { | ||
98 | if (usb_pipein(pipe)) { | ||
99 | if (ev_type == 'S') | ||
100 | return '<'; | ||
101 | } else { | ||
102 | if (ev_type == 'C') | ||
103 | return '>'; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | if (len >= DATA_MAX) | ||
108 | len = DATA_MAX; | ||
109 | memcpy(ep->data, urb->transfer_buffer, len); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static inline unsigned int mon_get_timestamp(void) | ||
114 | { | ||
115 | struct timeval tval; | ||
116 | unsigned int stamp; | ||
117 | |||
118 | do_gettimeofday(&tval); | ||
119 | stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */ | ||
120 | stamp = stamp * 1000000 + tval.tv_usec; | ||
121 | return stamp; | ||
122 | } | ||
123 | |||
124 | static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, | ||
125 | char ev_type) | ||
126 | { | ||
127 | struct mon_event_text *ep; | ||
128 | unsigned int stamp; | ||
129 | |||
130 | stamp = mon_get_timestamp(); | ||
131 | |||
132 | if (rp->nevents >= EVENT_MAX || | ||
133 | (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) { | ||
134 | rp->r.m_bus->cnt_text_lost++; | ||
135 | return; | ||
136 | } | ||
137 | |||
138 | ep->type = ev_type; | ||
139 | ep->pipe = urb->pipe; | ||
140 | ep->id = (unsigned long) urb; | ||
141 | ep->tstamp = stamp; | ||
142 | ep->length = (ev_type == 'S') ? | ||
143 | urb->transfer_buffer_length : urb->actual_length; | ||
144 | /* Collecting status makes debugging sense for submits, too */ | ||
145 | ep->status = urb->status; | ||
146 | |||
147 | ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type); | ||
148 | |||
149 | rp->nevents++; | ||
150 | list_add_tail(&ep->e_link, &rp->e_list); | ||
151 | wake_up(&rp->wait); | ||
152 | } | ||
153 | |||
154 | static void mon_text_submit(void *data, struct urb *urb) | ||
155 | { | ||
156 | struct mon_reader_text *rp = data; | ||
157 | mon_text_event(rp, urb, 'S'); | ||
158 | } | ||
159 | |||
160 | static void mon_text_complete(void *data, struct urb *urb) | ||
161 | { | ||
162 | struct mon_reader_text *rp = data; | ||
163 | mon_text_event(rp, urb, 'C'); | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * Fetch next event from the circular buffer. | ||
168 | */ | ||
169 | static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, | ||
170 | struct mon_bus *mbus) | ||
171 | { | ||
172 | struct list_head *p; | ||
173 | unsigned long flags; | ||
174 | |||
175 | spin_lock_irqsave(&mbus->lock, flags); | ||
176 | if (list_empty(&rp->e_list)) { | ||
177 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
178 | return NULL; | ||
179 | } | ||
180 | p = rp->e_list.next; | ||
181 | list_del(p); | ||
182 | --rp->nevents; | ||
183 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
184 | return list_entry(p, struct mon_event_text, e_link); | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | */ | ||
189 | static int mon_text_open(struct inode *inode, struct file *file) | ||
190 | { | ||
191 | struct mon_bus *mbus; | ||
192 | struct usb_bus *ubus; | ||
193 | struct mon_reader_text *rp; | ||
194 | int rc; | ||
195 | |||
196 | down(&mon_lock); | ||
197 | mbus = inode->u.generic_ip; | ||
198 | ubus = mbus->u_bus; | ||
199 | |||
200 | rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL); | ||
201 | if (rp == NULL) { | ||
202 | rc = -ENOMEM; | ||
203 | goto err_alloc; | ||
204 | } | ||
205 | memset(rp, 0, sizeof(struct mon_reader_text)); | ||
206 | INIT_LIST_HEAD(&rp->e_list); | ||
207 | init_waitqueue_head(&rp->wait); | ||
208 | init_MUTEX(&rp->printf_lock); | ||
209 | |||
210 | rp->printf_size = PRINTF_DFL; | ||
211 | rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); | ||
212 | if (rp->printf_buf == NULL) { | ||
213 | rc = -ENOMEM; | ||
214 | goto err_alloc_pr; | ||
215 | } | ||
216 | |||
217 | rp->r.m_bus = mbus; | ||
218 | rp->r.r_data = rp; | ||
219 | rp->r.rnf_submit = mon_text_submit; | ||
220 | rp->r.rnf_complete = mon_text_complete; | ||
221 | |||
222 | snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum, | ||
223 | (long)rp); | ||
224 | rp->e_slab = kmem_cache_create(rp->slab_name, | ||
225 | sizeof(struct mon_event_text), sizeof(long), 0, | ||
226 | mon_text_ctor, mon_text_dtor); | ||
227 | if (rp->e_slab == NULL) { | ||
228 | rc = -ENOMEM; | ||
229 | goto err_slab; | ||
230 | } | ||
231 | |||
232 | mon_reader_add(mbus, &rp->r); | ||
233 | |||
234 | file->private_data = rp; | ||
235 | up(&mon_lock); | ||
236 | return 0; | ||
237 | |||
238 | // err_busy: | ||
239 | // kmem_cache_destroy(rp->e_slab); | ||
240 | err_slab: | ||
241 | kfree(rp->printf_buf); | ||
242 | err_alloc_pr: | ||
243 | kfree(rp); | ||
244 | err_alloc: | ||
245 | up(&mon_lock); | ||
246 | return rc; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * For simplicity, we read one record in one system call and throw out | ||
251 | * what does not fit. This means that the following does not work: | ||
252 | * dd if=/dbg/usbmon/0t bs=10 | ||
253 | * Also, we do not allow seeks and do not bother advancing the offset. | ||
254 | */ | ||
255 | static ssize_t mon_text_read(struct file *file, char __user *buf, | ||
256 | size_t nbytes, loff_t *ppos) | ||
257 | { | ||
258 | struct mon_reader_text *rp = file->private_data; | ||
259 | struct mon_bus *mbus = rp->r.m_bus; | ||
260 | DECLARE_WAITQUEUE(waita, current); | ||
261 | struct mon_event_text *ep; | ||
262 | int cnt, limit; | ||
263 | char *pbuf; | ||
264 | char udir, utype; | ||
265 | int data_len, i; | ||
266 | |||
267 | add_wait_queue(&rp->wait, &waita); | ||
268 | set_current_state(TASK_INTERRUPTIBLE); | ||
269 | while ((ep = mon_text_fetch(rp, mbus)) == NULL) { | ||
270 | if (file->f_flags & O_NONBLOCK) { | ||
271 | set_current_state(TASK_RUNNING); | ||
272 | remove_wait_queue(&rp->wait, &waita); | ||
273 | return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ | ||
274 | } | ||
275 | /* | ||
276 | * We do not count nwaiters, because ->release is supposed | ||
277 | * to be called when all openers are gone only. | ||
278 | */ | ||
279 | schedule(); | ||
280 | if (signal_pending(current)) { | ||
281 | remove_wait_queue(&rp->wait, &waita); | ||
282 | return -EINTR; | ||
283 | } | ||
284 | set_current_state(TASK_INTERRUPTIBLE); | ||
285 | } | ||
286 | set_current_state(TASK_RUNNING); | ||
287 | remove_wait_queue(&rp->wait, &waita); | ||
288 | |||
289 | down(&rp->printf_lock); | ||
290 | cnt = 0; | ||
291 | pbuf = rp->printf_buf; | ||
292 | limit = rp->printf_size; | ||
293 | |||
294 | udir = usb_pipein(ep->pipe) ? 'i' : 'o'; | ||
295 | switch (usb_pipetype(ep->pipe)) { | ||
296 | case PIPE_ISOCHRONOUS: utype = 'Z'; break; | ||
297 | case PIPE_INTERRUPT: utype = 'I'; break; | ||
298 | case PIPE_CONTROL: utype = 'C'; break; | ||
299 | default: /* PIPE_BULK */ utype = 'B'; | ||
300 | } | ||
301 | cnt += snprintf(pbuf + cnt, limit - cnt, | ||
302 | "%lx %u %c %c%c:%03u:%02u %d %d", | ||
303 | ep->id, ep->tstamp, ep->type, | ||
304 | utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe), | ||
305 | ep->status, ep->length); | ||
306 | |||
307 | if ((data_len = ep->length) > 0) { | ||
308 | if (ep->data_flag == 0) { | ||
309 | cnt += snprintf(pbuf + cnt, limit - cnt, " ="); | ||
310 | if (data_len >= DATA_MAX) | ||
311 | data_len = DATA_MAX; | ||
312 | for (i = 0; i < data_len; i++) { | ||
313 | if (i % 4 == 0) { | ||
314 | cnt += snprintf(pbuf + cnt, limit - cnt, | ||
315 | " "); | ||
316 | } | ||
317 | cnt += snprintf(pbuf + cnt, limit - cnt, | ||
318 | "%02x", ep->data[i]); | ||
319 | } | ||
320 | cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); | ||
321 | } else { | ||
322 | cnt += snprintf(pbuf + cnt, limit - cnt, | ||
323 | " %c\n", ep->data_flag); | ||
324 | } | ||
325 | } else { | ||
326 | cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); | ||
327 | } | ||
328 | |||
329 | if (copy_to_user(buf, rp->printf_buf, cnt)) | ||
330 | cnt = -EFAULT; | ||
331 | up(&rp->printf_lock); | ||
332 | kmem_cache_free(rp->e_slab, ep); | ||
333 | return cnt; | ||
334 | } | ||
335 | |||
336 | static int mon_text_release(struct inode *inode, struct file *file) | ||
337 | { | ||
338 | struct mon_reader_text *rp = file->private_data; | ||
339 | struct mon_bus *mbus; | ||
340 | /* unsigned long flags; */ | ||
341 | struct list_head *p; | ||
342 | struct mon_event_text *ep; | ||
343 | |||
344 | down(&mon_lock); | ||
345 | mbus = inode->u.generic_ip; | ||
346 | |||
347 | if (mbus->nreaders <= 0) { | ||
348 | printk(KERN_ERR TAG ": consistency error on close\n"); | ||
349 | up(&mon_lock); | ||
350 | return 0; | ||
351 | } | ||
352 | mon_reader_del(mbus, &rp->r); | ||
353 | |||
354 | /* | ||
355 | * In theory, e_list is protected by mbus->lock. However, | ||
356 | * after mon_reader_del has finished, the following is the case: | ||
357 | * - we are not on reader list anymore, so new events won't be added; | ||
358 | * - whole mbus may be dropped if it was orphaned. | ||
359 | * So, we better not touch mbus. | ||
360 | */ | ||
361 | /* spin_lock_irqsave(&mbus->lock, flags); */ | ||
362 | while (!list_empty(&rp->e_list)) { | ||
363 | p = rp->e_list.next; | ||
364 | ep = list_entry(p, struct mon_event_text, e_link); | ||
365 | list_del(p); | ||
366 | --rp->nevents; | ||
367 | kmem_cache_free(rp->e_slab, ep); | ||
368 | } | ||
369 | /* spin_unlock_irqrestore(&mbus->lock, flags); */ | ||
370 | |||
371 | kmem_cache_destroy(rp->e_slab); | ||
372 | kfree(rp->printf_buf); | ||
373 | kfree(rp); | ||
374 | |||
375 | up(&mon_lock); | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | struct file_operations mon_fops_text = { | ||
380 | .owner = THIS_MODULE, | ||
381 | .open = mon_text_open, | ||
382 | .llseek = no_llseek, | ||
383 | .read = mon_text_read, | ||
384 | /* .write = mon_text_write, */ | ||
385 | /* .poll = mon_text_poll, */ | ||
386 | /* .ioctl = mon_text_ioctl, */ | ||
387 | .release = mon_text_release, | ||
388 | }; | ||
389 | |||
390 | /* | ||
391 | * Slab interface: constructor. | ||
392 | */ | ||
393 | static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags) | ||
394 | { | ||
395 | /* | ||
396 | * Nothing to initialize. No, really! | ||
397 | * So, we fill it with garbage to emulate a reused object. | ||
398 | */ | ||
399 | memset(mem, 0xe5, sizeof(struct mon_event_text)); | ||
400 | } | ||
401 | |||
402 | static void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags) | ||
403 | { | ||
404 | ; | ||
405 | } | ||