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 |
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')
-rw-r--r-- | drivers/usb/mon/Kconfig | 22 | ||||
-rw-r--r-- | drivers/usb/mon/Makefile | 7 | ||||
-rw-r--r-- | drivers/usb/mon/mon_main.c | 377 | ||||
-rw-r--r-- | drivers/usb/mon/mon_stat.c | 74 | ||||
-rw-r--r-- | drivers/usb/mon/mon_text.c | 405 | ||||
-rw-r--r-- | drivers/usb/mon/usb_mon.h | 51 |
6 files changed, 936 insertions, 0 deletions
diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig new file mode 100644 index 000000000000..4e6152aa5f19 --- /dev/null +++ b/drivers/usb/mon/Kconfig | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # USB Monitor configuration | ||
3 | # | ||
4 | |||
5 | # In normal life, it makes little sense to have usbmon as a module, and in fact | ||
6 | # it is harmful, because there is no way to autoload the module. | ||
7 | # The 'm' option is allowed for hackers who debug the usbmon itself, | ||
8 | # and for those who have usbcore as a module. | ||
9 | config USB_MON | ||
10 | tristate "USB Monitor" | ||
11 | depends on USB | ||
12 | default y | ||
13 | help | ||
14 | If you say Y here, a component which captures the USB traffic | ||
15 | between peripheral-specific drivers and HC drivers will be built. | ||
16 | The USB_MON is similar in spirit and may be compatible with Dave | ||
17 | Harding's USBMon. | ||
18 | |||
19 | This is somewhat experimental at this time, but it should be safe, | ||
20 | as long as you aren't building this as a module and then removing it. | ||
21 | |||
22 | If unsure, say Y. Do not say M. | ||
diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile new file mode 100644 index 000000000000..3cff8d444bb1 --- /dev/null +++ b/drivers/usb/mon/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for USB Core files and filesystem | ||
3 | # | ||
4 | |||
5 | usbmon-objs := mon_main.o mon_stat.o mon_text.o | ||
6 | |||
7 | obj-$(CONFIG_USB_MON) += usbmon.o | ||
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c new file mode 100644 index 000000000000..aa9d00808e4e --- /dev/null +++ b/drivers/usb/mon/mon_main.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | * The USB Monitor, inspired by Dave Harding's USBMon. | ||
3 | * | ||
4 | * mon_main.c: Main file, module initiation and exit, registrations, etc. | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/usb.h> | ||
10 | #include <linux/debugfs.h> | ||
11 | #include <linux/smp_lock.h> | ||
12 | |||
13 | #include "usb_mon.h" | ||
14 | #include "../core/hcd.h" | ||
15 | |||
16 | static void mon_submit(struct usb_bus *ubus, struct urb *urb); | ||
17 | static void mon_complete(struct usb_bus *ubus, struct urb *urb); | ||
18 | static void mon_stop(struct mon_bus *mbus); | ||
19 | static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); | ||
20 | static void mon_bus_drop(struct kref *r); | ||
21 | static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus); | ||
22 | |||
23 | DECLARE_MUTEX(mon_lock); | ||
24 | |||
25 | static struct dentry *mon_dir; /* /dbg/usbmon */ | ||
26 | static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ | ||
27 | |||
28 | /* | ||
29 | * Link a reader into the bus. | ||
30 | * | ||
31 | * This must be called with mon_lock taken because of mbus->ref. | ||
32 | */ | ||
33 | void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) | ||
34 | { | ||
35 | unsigned long flags; | ||
36 | struct usb_bus *ubus; | ||
37 | |||
38 | spin_lock_irqsave(&mbus->lock, flags); | ||
39 | if (mbus->nreaders == 0) { | ||
40 | ubus = mbus->u_bus; | ||
41 | if (ubus->monitored) { | ||
42 | /* | ||
43 | * Something is really broken, refuse to go on and | ||
44 | * possibly corrupt ops pointers or worse. | ||
45 | */ | ||
46 | printk(KERN_ERR TAG ": bus %d is already monitored\n", | ||
47 | ubus->busnum); | ||
48 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
49 | return; | ||
50 | } | ||
51 | ubus->monitored = 1; | ||
52 | } | ||
53 | mbus->nreaders++; | ||
54 | list_add_tail(&r->r_link, &mbus->r_list); | ||
55 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
56 | |||
57 | kref_get(&mbus->ref); | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * Unlink reader from the bus. | ||
62 | * | ||
63 | * This is called with mon_lock taken, so we can decrement mbus->ref. | ||
64 | */ | ||
65 | void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) | ||
66 | { | ||
67 | unsigned long flags; | ||
68 | |||
69 | spin_lock_irqsave(&mbus->lock, flags); | ||
70 | list_del(&r->r_link); | ||
71 | --mbus->nreaders; | ||
72 | if (mbus->nreaders == 0) | ||
73 | mon_stop(mbus); | ||
74 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
75 | |||
76 | kref_put(&mbus->ref, mon_bus_drop); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | */ | ||
81 | static void mon_submit(struct usb_bus *ubus, struct urb *urb) | ||
82 | { | ||
83 | struct mon_bus *mbus; | ||
84 | unsigned long flags; | ||
85 | struct list_head *pos; | ||
86 | struct mon_reader *r; | ||
87 | |||
88 | mbus = ubus->mon_bus; | ||
89 | if (mbus == NULL) | ||
90 | goto out_unlocked; | ||
91 | |||
92 | spin_lock_irqsave(&mbus->lock, flags); | ||
93 | if (mbus->nreaders == 0) | ||
94 | goto out_locked; | ||
95 | |||
96 | list_for_each (pos, &mbus->r_list) { | ||
97 | r = list_entry(pos, struct mon_reader, r_link); | ||
98 | r->rnf_submit(r->r_data, urb); | ||
99 | } | ||
100 | |||
101 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
102 | return; | ||
103 | |||
104 | out_locked: | ||
105 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
106 | out_unlocked: | ||
107 | return; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | */ | ||
112 | static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int err) | ||
113 | { | ||
114 | struct mon_bus *mbus; | ||
115 | |||
116 | mbus = ubus->mon_bus; | ||
117 | if (mbus == NULL) | ||
118 | goto out_unlocked; | ||
119 | |||
120 | /* | ||
121 | * XXX Capture the error code and the 'E' event. | ||
122 | */ | ||
123 | |||
124 | return; | ||
125 | |||
126 | out_unlocked: | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | */ | ||
132 | static void mon_complete(struct usb_bus *ubus, struct urb *urb) | ||
133 | { | ||
134 | struct mon_bus *mbus; | ||
135 | unsigned long flags; | ||
136 | struct list_head *pos; | ||
137 | struct mon_reader *r; | ||
138 | |||
139 | mbus = ubus->mon_bus; | ||
140 | if (mbus == NULL) { | ||
141 | /* | ||
142 | * This should not happen. | ||
143 | * At this point we do not even know the bus number... | ||
144 | */ | ||
145 | printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n", | ||
146 | urb->pipe); | ||
147 | return; | ||
148 | } | ||
149 | |||
150 | spin_lock_irqsave(&mbus->lock, flags); | ||
151 | list_for_each (pos, &mbus->r_list) { | ||
152 | r = list_entry(pos, struct mon_reader, r_link); | ||
153 | r->rnf_complete(r->r_data, urb); | ||
154 | } | ||
155 | spin_unlock_irqrestore(&mbus->lock, flags); | ||
156 | } | ||
157 | |||
158 | /* int (*unlink_urb) (struct urb *urb, int status); */ | ||
159 | |||
160 | /* | ||
161 | * Stop monitoring. | ||
162 | * Obviously this must be well locked, so no need to play with mb's. | ||
163 | */ | ||
164 | static void mon_stop(struct mon_bus *mbus) | ||
165 | { | ||
166 | struct usb_bus *ubus = mbus->u_bus; | ||
167 | |||
168 | /* | ||
169 | * A stop can be called for a dissolved mon_bus in case of | ||
170 | * a reader staying across an rmmod foo_hcd. | ||
171 | */ | ||
172 | if (ubus != NULL) { | ||
173 | ubus->monitored = 0; | ||
174 | mb(); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Add a USB bus (usually by a modprobe foo-hcd) | ||
180 | * | ||
181 | * This does not return an error code because the core cannot care less | ||
182 | * if monitoring is not established. | ||
183 | */ | ||
184 | static void mon_bus_add(struct usb_bus *ubus) | ||
185 | { | ||
186 | mon_bus_init(mon_dir, ubus); | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event). | ||
191 | */ | ||
192 | static void mon_bus_remove(struct usb_bus *ubus) | ||
193 | { | ||
194 | struct mon_bus *mbus = ubus->mon_bus; | ||
195 | |||
196 | down(&mon_lock); | ||
197 | list_del(&mbus->bus_link); | ||
198 | debugfs_remove(mbus->dent_t); | ||
199 | debugfs_remove(mbus->dent_s); | ||
200 | |||
201 | mon_dissolve(mbus, ubus); | ||
202 | kref_put(&mbus->ref, mon_bus_drop); | ||
203 | up(&mon_lock); | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Ops | ||
208 | */ | ||
209 | static struct usb_mon_operations mon_ops_0 = { | ||
210 | .urb_submit = mon_submit, | ||
211 | .urb_submit_error = mon_submit_error, | ||
212 | .urb_complete = mon_complete, | ||
213 | .bus_add = mon_bus_add, | ||
214 | .bus_remove = mon_bus_remove, | ||
215 | }; | ||
216 | |||
217 | /* | ||
218 | * Tear usb_bus and mon_bus apart. | ||
219 | */ | ||
220 | static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) | ||
221 | { | ||
222 | |||
223 | /* | ||
224 | * Never happens, but... | ||
225 | */ | ||
226 | if (ubus->monitored) { | ||
227 | printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n", | ||
228 | ubus->busnum); | ||
229 | ubus->monitored = 0; | ||
230 | mb(); | ||
231 | } | ||
232 | |||
233 | ubus->mon_bus = NULL; | ||
234 | mbus->u_bus = NULL; | ||
235 | mb(); | ||
236 | // usb_bus_put(ubus); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | */ | ||
241 | static void mon_bus_drop(struct kref *r) | ||
242 | { | ||
243 | struct mon_bus *mbus = container_of(r, struct mon_bus, ref); | ||
244 | kfree(mbus); | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Initialize a bus for us: | ||
249 | * - allocate mon_bus | ||
250 | * - refcount USB bus struct | ||
251 | * - link | ||
252 | */ | ||
253 | static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) | ||
254 | { | ||
255 | struct dentry *d; | ||
256 | struct mon_bus *mbus; | ||
257 | enum { NAMESZ = 10 }; | ||
258 | char name[NAMESZ]; | ||
259 | int rc; | ||
260 | |||
261 | if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) | ||
262 | goto err_alloc; | ||
263 | memset(mbus, 0, sizeof(struct mon_bus)); | ||
264 | kref_init(&mbus->ref); | ||
265 | spin_lock_init(&mbus->lock); | ||
266 | INIT_LIST_HEAD(&mbus->r_list); | ||
267 | |||
268 | /* | ||
269 | * This usb_bus_get here is superfluous, because we receive | ||
270 | * a notification if usb_bus is about to be removed. | ||
271 | */ | ||
272 | // usb_bus_get(ubus); | ||
273 | mbus->u_bus = ubus; | ||
274 | ubus->mon_bus = mbus; | ||
275 | |||
276 | rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); | ||
277 | if (rc <= 0 || rc >= NAMESZ) | ||
278 | goto err_print_t; | ||
279 | d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text); | ||
280 | if (d == NULL) | ||
281 | goto err_create_t; | ||
282 | mbus->dent_t = d; | ||
283 | |||
284 | rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); | ||
285 | if (rc <= 0 || rc >= NAMESZ) | ||
286 | goto err_print_s; | ||
287 | d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat); | ||
288 | if (d == NULL) | ||
289 | goto err_create_s; | ||
290 | mbus->dent_s = d; | ||
291 | |||
292 | down(&mon_lock); | ||
293 | list_add_tail(&mbus->bus_link, &mon_buses); | ||
294 | up(&mon_lock); | ||
295 | return; | ||
296 | |||
297 | err_create_s: | ||
298 | err_print_s: | ||
299 | debugfs_remove(mbus->dent_t); | ||
300 | err_create_t: | ||
301 | err_print_t: | ||
302 | kfree(mbus); | ||
303 | err_alloc: | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | static int __init mon_init(void) | ||
308 | { | ||
309 | struct usb_bus *ubus; | ||
310 | struct dentry *mondir; | ||
311 | |||
312 | mondir = debugfs_create_dir("usbmon", NULL); | ||
313 | if (IS_ERR(mondir)) { | ||
314 | printk(KERN_NOTICE TAG ": debugs is not available\n"); | ||
315 | return -ENODEV; | ||
316 | } | ||
317 | if (mondir == NULL) { | ||
318 | printk(KERN_NOTICE TAG ": unable to create usbmon directory\n"); | ||
319 | return -ENODEV; | ||
320 | } | ||
321 | mon_dir = mondir; | ||
322 | |||
323 | if (usb_mon_register(&mon_ops_0) != 0) { | ||
324 | printk(KERN_NOTICE TAG ": unable to register with the core\n"); | ||
325 | debugfs_remove(mondir); | ||
326 | return -ENODEV; | ||
327 | } | ||
328 | // MOD_INC_USE_COUNT(which_module?); | ||
329 | |||
330 | down(&usb_bus_list_lock); | ||
331 | list_for_each_entry (ubus, &usb_bus_list, bus_list) { | ||
332 | mon_bus_init(mondir, ubus); | ||
333 | } | ||
334 | up(&usb_bus_list_lock); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static void __exit mon_exit(void) | ||
339 | { | ||
340 | struct mon_bus *mbus; | ||
341 | struct list_head *p; | ||
342 | |||
343 | usb_mon_deregister(); | ||
344 | |||
345 | down(&mon_lock); | ||
346 | while (!list_empty(&mon_buses)) { | ||
347 | p = mon_buses.next; | ||
348 | mbus = list_entry(p, struct mon_bus, bus_link); | ||
349 | list_del(p); | ||
350 | |||
351 | debugfs_remove(mbus->dent_t); | ||
352 | debugfs_remove(mbus->dent_s); | ||
353 | |||
354 | /* | ||
355 | * This never happens, because the open/close paths in | ||
356 | * file level maintain module use counters and so rmmod fails | ||
357 | * before reaching here. However, better be safe... | ||
358 | */ | ||
359 | if (mbus->nreaders) { | ||
360 | printk(KERN_ERR TAG | ||
361 | ": Outstanding opens (%d) on usb%d, leaking...\n", | ||
362 | mbus->nreaders, mbus->u_bus->busnum); | ||
363 | atomic_set(&mbus->ref.refcount, 2); /* Force leak */ | ||
364 | } | ||
365 | |||
366 | mon_dissolve(mbus, mbus->u_bus); | ||
367 | kref_put(&mbus->ref, mon_bus_drop); | ||
368 | } | ||
369 | up(&mon_lock); | ||
370 | |||
371 | debugfs_remove(mon_dir); | ||
372 | } | ||
373 | |||
374 | module_init(mon_init); | ||
375 | module_exit(mon_exit); | ||
376 | |||
377 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c new file mode 100644 index 000000000000..6e4b165d070a --- /dev/null +++ b/drivers/usb/mon/mon_stat.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * The USB Monitor, inspired by Dave Harding's USBMon. | ||
3 | * | ||
4 | * This is the 's' or 'stat' reader which debugs usbmon itself. | ||
5 | * Note that this code blows through locks, so make sure that | ||
6 | * /dbg/usbmon/0s is well protected from non-root users. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/usb.h> | ||
12 | #include <asm/uaccess.h> | ||
13 | |||
14 | #include "usb_mon.h" | ||
15 | |||
16 | #define STAT_BUF_SIZE 80 | ||
17 | |||
18 | struct snap { | ||
19 | int slen; | ||
20 | char str[STAT_BUF_SIZE]; | ||
21 | }; | ||
22 | |||
23 | static int mon_stat_open(struct inode *inode, struct file *file) | ||
24 | { | ||
25 | struct mon_bus *mbus; | ||
26 | struct snap *sp; | ||
27 | |||
28 | if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL) | ||
29 | return -ENOMEM; | ||
30 | |||
31 | mbus = inode->u.generic_ip; | ||
32 | |||
33 | sp->slen = snprintf(sp->str, STAT_BUF_SIZE, | ||
34 | "nreaders %d text_lost %u\n", | ||
35 | mbus->nreaders, mbus->cnt_text_lost); | ||
36 | |||
37 | file->private_data = sp; | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | static ssize_t mon_stat_read(struct file *file, char __user *buf, | ||
42 | size_t nbytes, loff_t *ppos) | ||
43 | { | ||
44 | struct snap *sp = file->private_data; | ||
45 | loff_t pos = *ppos; | ||
46 | int cnt; | ||
47 | |||
48 | if (pos < 0 || pos >= sp->slen) | ||
49 | return 0; | ||
50 | if (nbytes == 0) | ||
51 | return 0; | ||
52 | if ((cnt = sp->slen - pos) > nbytes) | ||
53 | cnt = nbytes; | ||
54 | if (copy_to_user(buf, sp->str + pos, cnt)) | ||
55 | return -EFAULT; | ||
56 | *ppos = pos + cnt; | ||
57 | return cnt; | ||
58 | } | ||
59 | |||
60 | static int mon_stat_release(struct inode *inode, struct file *file) | ||
61 | { | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | struct file_operations mon_fops_stat = { | ||
66 | .owner = THIS_MODULE, | ||
67 | .open = mon_stat_open, | ||
68 | .llseek = no_llseek, | ||
69 | .read = mon_stat_read, | ||
70 | /* .write = mon_stat_write, */ | ||
71 | /* .poll = mon_stat_poll, */ | ||
72 | /* .ioctl = mon_stat_ioctl, */ | ||
73 | .release = mon_stat_release, | ||
74 | }; | ||
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 | } | ||
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h new file mode 100644 index 000000000000..ed35c18a5c44 --- /dev/null +++ b/drivers/usb/mon/usb_mon.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * The USB Monitor, inspired by Dave Harding's USBMon. | ||
3 | */ | ||
4 | |||
5 | #ifndef __USB_MON_H | ||
6 | #define __USB_MON_H | ||
7 | |||
8 | #include <linux/list.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/kref.h> | ||
11 | /* #include <linux/usb.h> */ /* We use struct pointers only in this header */ | ||
12 | |||
13 | #define TAG "usbmon" | ||
14 | |||
15 | struct mon_bus { | ||
16 | struct list_head bus_link; | ||
17 | spinlock_t lock; | ||
18 | struct dentry *dent_s; /* Debugging file */ | ||
19 | struct dentry *dent_t; /* Text interface file */ | ||
20 | struct usb_bus *u_bus; | ||
21 | |||
22 | /* Ref */ | ||
23 | int nreaders; /* Under mon_lock AND mbus->lock */ | ||
24 | struct list_head r_list; /* Chain of readers (usually one) */ | ||
25 | struct kref ref; /* Under mon_lock */ | ||
26 | |||
27 | /* Stats */ | ||
28 | unsigned int cnt_text_lost; | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * An instance of a process which opened a file (but can fork later) | ||
33 | */ | ||
34 | struct mon_reader { | ||
35 | struct list_head r_link; | ||
36 | struct mon_bus *m_bus; | ||
37 | void *r_data; /* Use container_of instead? */ | ||
38 | |||
39 | void (*rnf_submit)(void *data, struct urb *urb); | ||
40 | void (*rnf_complete)(void *data, struct urb *urb); | ||
41 | }; | ||
42 | |||
43 | void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r); | ||
44 | void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r); | ||
45 | |||
46 | extern struct semaphore mon_lock; | ||
47 | |||
48 | extern struct file_operations mon_fops_text; | ||
49 | extern struct file_operations mon_fops_stat; | ||
50 | |||
51 | #endif /* __USB_MON_H */ | ||