File: /usr/src/linux/drivers/usb/uhci-debug.h

1     /*
2      * UHCI-specific debugging code. Invaluable when something
3      * goes wrong, but don't get in my face.
4      *
5      * Kernel visible pointers are surrounded in []'s and bus
6      * visible pointers are surrounded in ()'s
7      *
8      * (C) Copyright 1999 Linus Torvalds
9      * (C) Copyright 1999-2001 Johannes Erdfelt
10      */
11     
12     #include <linux/config.h>
13     #include <linux/kernel.h>
14     #include <linux/proc_fs.h>
15     #include <asm/io.h>
16     
17     #include "uhci.h"
18     
19     /* Handle REALLY large printk's so we don't overflow buffers */
20     static void inline lprintk(char *buf)
21     {
22     	char *p;
23     
24     	/* Just write one line at a time */
25     	while (buf) {
26     		p = strchr(buf, '\n');
27     		if (p)
28     			*p = 0;
29     		printk("%s\n", buf);
30     		buf = p;
31     		if (buf)
32     			buf++;
33     	}
34     }
35     
36     static int inline uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td)
37     {
38     	int i;
39     
40     	for (i = 0; i < UHCI_NUM_SKELTD; i++)
41     		if (td == uhci->skeltd[i])
42     			return 1;
43     
44     	return 0;
45     }
46     
47     static int inline uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
48     {
49     	int i;
50     
51     	for (i = 0; i < UHCI_NUM_SKELQH; i++)
52     		if (qh == uhci->skelqh[i])
53     			return 1;
54     
55     	return 0;
56     }
57     
58     static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
59     {
60     	char *out = buf;
61     	char *spid;
62     
63     	/* Try to make sure there's enough memory */
64     	if (len < 160)
65     		return 0;
66     
67     	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, td->link);
68     	out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
69     		((td->status >> 27) & 3),
70     		(td->status & TD_CTRL_SPD) ?      "SPD " : "",
71     		(td->status & TD_CTRL_LS) ?       "LS " : "",
72     		(td->status & TD_CTRL_IOC) ?      "IOC " : "",
73     		(td->status & TD_CTRL_ACTIVE) ?   "Active " : "",
74     		(td->status & TD_CTRL_STALLED) ?  "Stalled " : "",
75     		(td->status & TD_CTRL_DBUFERR) ?  "DataBufErr " : "",
76     		(td->status & TD_CTRL_BABBLE) ?   "Babble " : "",
77     		(td->status & TD_CTRL_NAK) ?      "NAK " : "",
78     		(td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
79     		(td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
80     		td->status & 0x7ff);
81     
82     	switch (td->info & 0xff) {
83     		case USB_PID_SETUP:
84     			spid = "SETUP";
85     			break;
86     		case USB_PID_OUT:
87     			spid = "OUT";
88     			break;
89     		case USB_PID_IN:
90     			spid = "IN";
91     			break;
92     		default:
93     			spid = "?";
94     			break;
95     	}
96     
97     	out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
98     		td->info >> 21,
99     		((td->info >> 19) & 1),
100     		(td->info >> 15) & 15,
101     		(td->info >> 8) & 127,
102     		(td->info & 0xff),
103     		spid);
104     	out += sprintf(out, "(buf=%08x)\n", td->buffer);
105     
106     	return out - buf;
107     }
108     
109     static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
110     {
111     	char *out = buf;
112     
113     	/* Try to make sure there's enough memory */
114     	if (len < 80)
115     		return 0;
116     
117     	out += sprintf(out, "  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
118     		port,
119     		status,
120     		(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
121     		(status & USBPORTSC_PR) ?   "PortReset " : "",
122     		(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
123     		(status & USBPORTSC_RD) ?   "ResumeDetect " : "",
124     		(status & USBPORTSC_PEC) ?  "EnableChange " : "",
125     		(status & USBPORTSC_PE) ?   "PortEnabled " : "",
126     		(status & USBPORTSC_CSC) ?  "ConnectChange " : "",
127     		(status & USBPORTSC_CCS) ?  "PortConnected " : "");
128     
129     	return out - buf;
130     }
131     
132     static int uhci_show_status(struct uhci *uhci, char *buf, int len)
133     {
134     	char *out = buf;
135     	unsigned int io_addr = uhci->io_addr;
136     	unsigned short usbcmd, usbstat, usbint, usbfrnum;
137     	unsigned int flbaseadd;
138     	unsigned char sof;
139     	unsigned short portsc1, portsc2;
140     
141     	/* Try to make sure there's enough memory */
142     	if (len < 80 * 6)
143     		return 0;
144     
145     	usbcmd    = inw(io_addr + 0);
146     	usbstat   = inw(io_addr + 2);
147     	usbint    = inw(io_addr + 4);
148     	usbfrnum  = inw(io_addr + 6);
149     	flbaseadd = inl(io_addr + 8);
150     	sof       = inb(io_addr + 12);
151     	portsc1   = inw(io_addr + 16);
152     	portsc2   = inw(io_addr + 18);
153     
154     	out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
155     		usbcmd,
156     		(usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
157     		(usbcmd & USBCMD_CF) ?      "CF " : "",
158     		(usbcmd & USBCMD_SWDBG) ?   "SWDBG " : "",
159     		(usbcmd & USBCMD_FGR) ?     "FGR " : "",
160     		(usbcmd & USBCMD_EGSM) ?    "EGSM " : "",
161     		(usbcmd & USBCMD_GRESET) ?  "GRESET " : "",
162     		(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
163     		(usbcmd & USBCMD_RS) ?      "RS " : "");
164     
165     	out += sprintf(out, "  usbstat   =     %04x   %s%s%s%s%s%s\n",
166     		usbstat,
167     		(usbstat & USBSTS_HCH) ?    "HCHalted " : "",
168     		(usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
169     		(usbstat & USBSTS_HSE) ?    "HostSystemError " : "",
170     		(usbstat & USBSTS_RD) ?     "ResumeDetect " : "",
171     		(usbstat & USBSTS_ERROR) ?  "USBError " : "",
172     		(usbstat & USBSTS_USBINT) ? "USBINT " : "");
173     
174     	out += sprintf(out, "  usbint    =     %04x\n", usbint);
175     	out += sprintf(out, "  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
176     		0xfff & (4*(unsigned int)usbfrnum));
177     	out += sprintf(out, "  flbaseadd = %08x\n", flbaseadd);
178     	out += sprintf(out, "  sof       =       %02x\n", sof);
179     	out += uhci_show_sc(1, portsc1, out, len - (out - buf));
180     	out += uhci_show_sc(2, portsc2, out, len - (out - buf));
181     
182     	return out - buf;
183     }
184     
185     static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
186     {
187     	char *out = buf;
188     	struct urb_priv *urbp;
189     	struct list_head *head, *tmp;
190     	struct uhci_td *td;
191     	int i = 0, checked = 0, prevactive = 0;
192     
193     	/* Try to make sure there's enough memory */
194     	if (len < 80 * 6)
195     		return 0;
196     
197     	out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
198     			qh, qh->link, qh->element);
199     
200     	if (qh->element & UHCI_PTR_QH)
201     		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
202     
203     	if (qh->element & UHCI_PTR_DEPTH)
204     		out += sprintf(out, "%*s  Depth traverse\n", space, "");
205     
206     	if (qh->element & 8)
207     		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
208     
209     	if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
210     		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
211     
212     	if (!qh->urbp) {
213     		out += sprintf(out, "%*s  urbp == NULL\n", space, "");
214     		goto out;
215     	}
216     
217     	urbp = qh->urbp;
218     
219     	head = &urbp->td_list;
220     	tmp = head->next;
221     
222     	td = list_entry(tmp, struct uhci_td, list);
223     
224     	if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS))
225     		out += sprintf(out, "%*s Element != First TD\n", space, "");
226     
227     	while (tmp != head) {
228     		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
229     
230     		tmp = tmp->next;
231     
232     		out += sprintf(out, "%*s%d: ", space + 2, "", i++);
233     		out += uhci_show_td(td, out, len - (out - buf), 0);
234     
235     		if (i > 10 && !checked && prevactive && tmp != head &&
236     		    debug <= 2) {
237     			struct list_head *ntmp = tmp;
238     			struct uhci_td *ntd = td;
239     			int active = 1, ni = i;
240     
241     			checked = 1;
242     
243     			while (ntmp != head && ntmp->next != head && active) {
244     				ntd = list_entry(ntmp, struct uhci_td, list);
245     
246     				ntmp = ntmp->next;
247     
248     				active = ntd->status & TD_CTRL_ACTIVE;
249     
250     				ni++;
251     			}
252     
253     			if (active && ni > i) {
254     				out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
255     				tmp = ntmp;
256     				td = ntd;
257     				i = ni;
258     			}
259     		}
260     
261     		prevactive = td->status & TD_CTRL_ACTIVE;
262     	}
263     
264     	if (list_empty(&urbp->queue_list) || urbp->queued)
265     		goto out;
266     
267     	out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
268     
269     	head = &urbp->queue_list;
270     	tmp = head->next;
271     
272     	while (tmp != head) {
273     		struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
274     						queue_list);
275     		tmp = tmp->next;
276     
277     		out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
278     	}
279     
280     out:
281     	return out - buf;
282     }
283     
284     static const char *td_names[] = {"skel_int1_td", "skel_int2_td",
285     				 "skel_int4_td", "skel_int8_td",
286     				 "skel_int16_td", "skel_int32_td",
287     				 "skel_int64_td", "skel_int128_td",
288     				 "skel_int256_td", "skel_term_td" };
289     static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh",
290     				  "skel_bulk_qh", "skel_term_qh" };
291     
292     #define show_frame_num()	\
293     	if (!shown) {		\
294     	  shown = 1;		\
295     	  out += sprintf(out, "- Frame %d\n", i); \
296     	}
297     
298     #define show_td_name()		\
299     	if (!shown) {		\
300     	  shown = 1;		\
301     	  out += sprintf(out, "- %s\n", td_names[i]); \
302     	}
303     
304     #define show_qh_name()		\
305     	if (!shown) {		\
306     	  shown = 1;		\
307     	  out += sprintf(out, "- %s\n", qh_names[i]); \
308     	}
309     
310     static int uhci_sprint_schedule(struct uhci *uhci, char *buf, int len)
311     {
312     	char *out = buf;
313     	int i;
314     	struct uhci_qh *qh;
315     	struct uhci_td *td;
316     	struct list_head *tmp, *head;
317     
318     	out += sprintf(out, "HC status\n");
319     	out += uhci_show_status(uhci, out, len - (out - buf));
320     
321     	out += sprintf(out, "Frame List\n");
322     	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
323     		int shown = 0;
324     		td = uhci->fl->frame_cpu[i];
325     		if (!td)
326     			continue;
327     
328     		if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
329     			show_frame_num();
330     			out += sprintf(out, "    frame list does not match td->dma_handle!\n");
331     		}
332     		if (uhci_is_skeleton_td(uhci, td))
333     			continue;
334     		show_frame_num();
335     
336     		head = &td->fl_list;
337     		tmp = head;
338     		do {
339     			td = list_entry(tmp, struct uhci_td, fl_list);
340     			tmp = tmp->next;
341     			out += uhci_show_td(td, out, len - (out - buf), 4);
342     		} while (tmp != head);
343     	}
344     
345     	out += sprintf(out, "Skeleton TD's\n");
346     	for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) {
347     		int shown = 0;
348     
349     		td = uhci->skeltd[i];
350     
351     		if (debug > 1) {
352     			show_td_name();
353     			out += uhci_show_td(td, out, len - (out - buf), 4);
354     		}
355     
356     		if (list_empty(&td->fl_list)) {
357     			/* TD 0 is the int1 TD and links to control_ls_qh */
358     			if (!i) {
359     				if (td->link !=
360     				    (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) {
361     					show_td_name();
362     					out += sprintf(out, "    skeleton TD not linked to ls_control QH!\n");
363     				}
364     			} else if (i < 9) {
365     				if (td->link != uhci->skeltd[i - 1]->dma_handle) {
366     					show_td_name();
367     					out += sprintf(out, "    skeleton TD not linked to next skeleton TD!\n");
368     				}
369     			} else {
370     				show_td_name();
371     
372     				if (td->link != td->dma_handle)
373     					out += sprintf(out, "    skel_term_td does not link to self\n");
374     
375     				out += uhci_show_td(td, out, len - (out - buf), 4);
376     			}
377     
378     			continue;
379     		}
380     
381     		show_td_name();
382     
383     		head = &td->fl_list;
384     		tmp = head->next;
385     
386     		while (tmp != head) {
387     			td = list_entry(tmp, struct uhci_td, fl_list);
388     
389     			tmp = tmp->next;
390     
391     			out += uhci_show_td(td, out, len - (out - buf), 4);
392     		}
393     
394     		if (!i) {
395     			if (td->link !=
396     			    (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH))
397     				out += sprintf(out, "    last TD not linked to ls_control QH!\n");
398     		} else if (i < 9) {
399     			if (td->link != uhci->skeltd[i - 1]->dma_handle)
400     				out += sprintf(out, "    last TD not linked to next skeleton!\n");
401     		}
402     	}
403     
404     	out += sprintf(out, "Skeleton QH's\n");
405     
406     	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
407     		int shown = 0;
408     
409     		qh = uhci->skelqh[i];
410     
411     		if (debug > 1) {
412     			show_qh_name();
413     			out += uhci_show_qh(qh, out, len - (out - buf), 4);
414     		}
415     
416     		/* QH 3 is the Terminating QH, it's different */
417     		if (i == 3) {
418     			if (qh->link != UHCI_PTR_TERM) {
419     				show_qh_name();
420     				out += sprintf(out, "    bandwidth reclamation on!\n");
421     			}
422     
423     			if (qh->element != uhci->skel_term_td->dma_handle) {
424     				show_qh_name();
425     				out += sprintf(out, "    skel_term_qh element is not set to skel_term_td\n");
426     			}
427     		}
428     
429     		if (list_empty(&qh->list)) {
430     			if (i < 3) {
431     				if (qh->link !=
432     				    (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) {
433     					show_qh_name();
434     					out += sprintf(out, "    skeleton QH not linked to next skeleton QH!\n");
435     				}
436     			}
437     
438     			continue;
439     		}
440     
441     		show_qh_name();
442     
443     		head = &qh->list;
444     		tmp = head->next;
445     
446     		while (tmp != head) {
447     			qh = list_entry(tmp, struct uhci_qh, list);
448     
449     			tmp = tmp->next;
450     
451     			out += uhci_show_qh(qh, out, len - (out - buf), 4);
452     		}
453     
454     		if (i < 3) {
455     			if (qh->link !=
456     			    (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH))
457     				out += sprintf(out, "    last QH not linked to next skeleton!\n");
458     		}
459     	}
460     
461     	return out - buf;
462     }
463     
464     #ifdef CONFIG_PROC_FS
465     #define MAX_OUTPUT	(PAGE_SIZE * 8)
466     
467     static struct proc_dir_entry *uhci_proc_root = NULL;
468     
469     struct uhci_proc {
470     	int size;
471     	char *data;
472     	struct uhci *uhci;
473     };
474     
475     static int uhci_proc_open(struct inode *inode, struct file *file)
476     {
477     	const struct proc_dir_entry *dp = inode->u.generic_ip;
478     	struct uhci *uhci = dp->data;
479     	struct uhci_proc *up;
480     	unsigned long flags;
481     	int ret = -ENOMEM;
482     
483     	lock_kernel();
484     	up = kmalloc(sizeof(*up), GFP_KERNEL);
485     	if (!up)
486     		goto out;
487     
488     	up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
489     	if (!up->data) {
490     		kfree(up);
491     		goto out;
492     	}
493     
494     	spin_lock_irqsave(&uhci->frame_list_lock, flags);
495     	up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
496     	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
497     
498     	file->private_data = up;
499     
500     	ret = 0;
501     out:
502     	unlock_kernel();
503     	return ret;
504     }
505     
506     static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence)
507     {
508     	struct uhci_proc *up = file->private_data;
509     	loff_t new;
510     
511     	switch (whence) {
512     	case 0:
513     		new = off;
514     		break;
515     	case 1:
516     		new = file->f_pos + off;
517     		break;
518     	case 2:
519     	default:
520     		return -EINVAL;
521     	}
522     	if (new < 0 || new > up->size)
523     		return -EINVAL;
524     	return (file->f_pos = new);
525     }
526     
527     static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes,
528     			loff_t *ppos)
529     {
530     	struct uhci_proc *up = file->private_data;
531     	unsigned int pos;
532     	unsigned int size;
533     
534     	pos = *ppos;
535     	size = up->size;
536     	if (pos >= size)
537     		return 0;
538     	if (nbytes >= size)
539     		nbytes = size;
540     	if (pos + nbytes > size)
541     		nbytes = size - pos;
542     
543     	if (!access_ok(VERIFY_WRITE, buf, nbytes))
544     		return -EINVAL;
545     
546     	copy_to_user(buf, up->data + pos, nbytes);
547     
548     	*ppos += nbytes;
549     
550     	return nbytes;
551     }
552     
553     static int uhci_proc_release(struct inode *inode, struct file *file)
554     {
555     	struct uhci_proc *up = file->private_data;
556     
557     	kfree(up->data);
558     	kfree(up);
559     
560     	return 0;
561     }
562     
563     static struct file_operations uhci_proc_operations = {
564     	open:		uhci_proc_open,
565     	llseek:		uhci_proc_lseek,
566     	read:		uhci_proc_read,
567     //	write:		uhci_proc_write,
568     	release:	uhci_proc_release,
569     };
570     #endif
571     
572