]>
Commit | Line | Data |
---|---|---|
c7de829c WD |
1 | /* |
2 | * (C) Copyright 2001 | |
3 | * Denis Peter, MPL AG Switzerland | |
4 | * | |
5 | * See file CREDITS for list of people who contributed to this | |
6 | * project. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation; either version 2 of | |
11 | * the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | * MA 02111-1307 USA | |
22 | * | |
23 | * Note: Part of this code has been derived from linux | |
24 | * | |
25 | */ | |
26 | ||
27 | /********************************************************************** | |
28 | * How it works: | |
29 | * ------------- | |
30 | * The framelist / Transfer descriptor / Queue Heads are similar like | |
31 | * in the linux usb_uhci.c. | |
32 | * | |
33 | * During initialization, the following skeleton is allocated in init_skel: | |
34 | * | |
35 | * framespecific | common chain | |
36 | * | |
37 | * framelist[] | |
38 | * [ 0 ]-----> TD ---------\ | |
39 | * [ 1 ]-----> TD ----------> TD ------> QH -------> QH -------> QH ---> NULL | |
40 | * ... TD ---------/ | |
41 | * [1023]-----> TD --------/ | |
42 | * | |
43 | * ^^ ^^ ^^ ^^ ^^ | |
44 | * 7 TDs for 1 TD for Start of Start of End Chain | |
45 | * INT (2-128ms) 1ms-INT CTRL Chain BULK Chain | |
46 | * | |
47 | * | |
48 | * Since this is a bootloader, the isochronous transfer descriptor have been removed. | |
49 | * | |
50 | * Interrupt Transfers. | |
51 | * -------------------- | |
52 | * For Interupt transfers USB_MAX_TEMP_INT_TD Transfer descriptor are available. They | |
53 | * will be inserted after the appropriate (depending the interval setting) skeleton TD. | |
54 | * If an interrupt has been detected the dev->irqhandler is called. The status and number | |
55 | * of transfered bytes is stored in dev->irq_status resp. dev->irq_act_len. If the | |
56 | * dev->irqhandler returns 0, the interrupt TD is removed and disabled. If an 1 is returned, | |
57 | * the interrupt TD will be reactivated. | |
58 | * | |
59 | * Control Transfers | |
60 | * ----------------- | |
61 | * Control Transfers are issued by filling the tmp_td with the appropriate data and connect | |
62 | * them to the qh_cntrl queue header. Before other control/bulk transfers can be issued, | |
63 | * the programm has to wait for completion. This does not allows asynchronous data transfer. | |
64 | * | |
65 | * Bulk Transfers | |
66 | * -------------- | |
67 | * Bulk Transfers are issued by filling the tmp_td with the appropriate data and connect | |
68 | * them to the qh_bulk queue header. Before other control/bulk transfers can be issued, | |
69 | * the programm has to wait for completion. This does not allows asynchronous data transfer. | |
70 | * | |
71 | * | |
72 | */ | |
73 | ||
74 | #include <common.h> | |
75 | #include <pci.h> | |
76 | ||
77 | #ifdef CONFIG_USB_UHCI | |
78 | ||
79 | #include <usb.h> | |
80 | #include "usb_uhci.h" | |
81 | ||
82 | #define USB_MAX_TEMP_TD 128 /* number of temporary TDs for bulk and control transfers */ | |
83 | #define USB_MAX_TEMP_INT_TD 32 /* number of temporary TDs for Interrupt transfers */ | |
84 | ||
85 | ||
8bde7f77 | 86 | /*#define USB_UHCI_DEBUG */ |
c7de829c WD |
87 | |
88 | #ifdef USB_UHCI_DEBUG | |
89 | #define USB_UHCI_PRINTF(fmt,args...) printf (fmt ,##args) | |
90 | #else | |
91 | #define USB_UHCI_PRINTF(fmt,args...) | |
92 | #endif | |
93 | ||
94 | ||
95 | static int irqvec = -1; /* irq vector, if -1 uhci is stopped / reseted */ | |
96 | unsigned int usb_base_addr; /* base address */ | |
97 | ||
98 | static uhci_td_t td_int[8]; /* Interrupt Transfer descriptors */ | |
99 | static uhci_qh_t qh_cntrl; /* control Queue Head */ | |
100 | static uhci_qh_t qh_bulk; /* bulk Queue Head */ | |
101 | static uhci_qh_t qh_end; /* end Queue Head */ | |
102 | static uhci_td_t td_last; /* last TD (linked with end chain) */ | |
103 | ||
104 | /* temporary tds */ | |
105 | static uhci_td_t tmp_td[USB_MAX_TEMP_TD]; /* temporary bulk/control td's */ | |
106 | static uhci_td_t tmp_int_td[USB_MAX_TEMP_INT_TD]; /* temporary interrupt td's */ | |
107 | ||
108 | static unsigned long framelist[1024] __attribute__ ((aligned (0x1000))); /* frame list */ | |
109 | ||
110 | static struct virt_root_hub rh; /* struct for root hub */ | |
111 | ||
112 | /********************************************************************** | |
113 | * some forward decleration | |
114 | */ | |
115 | int uhci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, | |
116 | void *buffer, int transfer_len,struct devrequest *setup); | |
117 | ||
118 | /* fill a td with the approproiate data. Link, status, info and buffer | |
119 | * are used by the USB controller itselfes, dev is used to identify the | |
120 | * "connected" device | |
121 | */ | |
122 | void usb_fill_td(uhci_td_t* td,unsigned long link,unsigned long status, | |
123 | unsigned long info, unsigned long buffer, unsigned long dev) | |
124 | { | |
125 | td->link=swap_32(link); | |
126 | td->status=swap_32(status); | |
127 | td->info=swap_32(info); | |
128 | td->buffer=swap_32(buffer); | |
129 | td->dev_ptr=dev; | |
130 | } | |
131 | ||
132 | /* fill a qh with the approproiate data. Head and element are used by the USB controller | |
133 | * itselfes. As soon as a valid dev_ptr is filled, a td chain is connected to the qh. | |
134 | * Please note, that after completion of the td chain, the entry element is removed / | |
135 | * marked invalid by the USB controller. | |
136 | */ | |
137 | void usb_fill_qh(uhci_qh_t* qh,unsigned long head,unsigned long element) | |
138 | { | |
139 | qh->head=swap_32(head); | |
140 | qh->element=swap_32(element); | |
141 | qh->dev_ptr=0L; | |
142 | } | |
143 | ||
144 | /* get the status of a td->status | |
145 | */ | |
146 | unsigned long usb_uhci_td_stat(unsigned long status) | |
147 | { | |
148 | unsigned long result=0; | |
149 | result |= (status & TD_CTRL_NAK) ? USB_ST_NAK_REC : 0; | |
150 | result |= (status & TD_CTRL_STALLED) ? USB_ST_STALLED : 0; | |
151 | result |= (status & TD_CTRL_DBUFERR) ? USB_ST_BUF_ERR : 0; | |
152 | result |= (status & TD_CTRL_BABBLE) ? USB_ST_BABBLE_DET : 0; | |
153 | result |= (status & TD_CTRL_CRCTIMEO) ? USB_ST_CRC_ERR : 0; | |
154 | result |= (status & TD_CTRL_BITSTUFF) ? USB_ST_BIT_ERR : 0; | |
155 | result |= (status & TD_CTRL_ACTIVE) ? USB_ST_NOT_PROC : 0; | |
156 | return result; | |
157 | } | |
158 | ||
159 | /* get the status and the transfered len of a td chain. | |
160 | * called from the completion handler | |
161 | */ | |
162 | int usb_get_td_status(uhci_td_t *td,struct usb_device *dev) | |
163 | { | |
164 | unsigned long temp,info; | |
165 | unsigned long stat; | |
166 | uhci_td_t *mytd=td; | |
167 | ||
168 | if(dev->devnum==rh.devnum) | |
169 | return 0; | |
170 | dev->act_len=0; | |
171 | stat=0; | |
172 | do { | |
173 | temp=swap_32((unsigned long)mytd->status); | |
174 | stat=usb_uhci_td_stat(temp); | |
175 | info=swap_32((unsigned long)mytd->info); | |
176 | if(((info & 0xff)!= USB_PID_SETUP) && | |
177 | (((info >> 21) & 0x7ff)!= 0x7ff) && | |
178 | (temp & 0x7FF)!=0x7ff) | |
179 | { /* if not setup and not null data pack */ | |
180 | dev->act_len+=(temp & 0x7FF) + 1; /* the transfered len is act_len + 1 */ | |
181 | } | |
182 | if(stat) { /* status no ok */ | |
183 | dev->status=stat; | |
184 | return -1; | |
185 | } | |
186 | temp=swap_32((unsigned long)mytd->link); | |
187 | mytd=(uhci_td_t *)(temp & 0xfffffff0); | |
188 | }while((temp & 0x1)==0); /* process all TDs */ | |
189 | dev->status=stat; | |
190 | return 0; /* Ok */ | |
191 | } | |
192 | ||
193 | ||
194 | /*------------------------------------------------------------------- | |
195 | * LOW LEVEL STUFF | |
196 | * assembles QHs und TDs for control, bulk and iso | |
197 | *-------------------------------------------------------------------*/ | |
198 | ||
199 | /* Submits a control message. That is a Setup, Data and Status transfer. | |
200 | * Routine does not wait for completion. | |
201 | */ | |
202 | int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, | |
203 | int transfer_len,struct devrequest *setup) | |
204 | { | |
205 | unsigned long destination, status; | |
206 | int maxsze = usb_maxpacket(dev, pipe); | |
207 | unsigned long dataptr; | |
208 | int len; | |
209 | int pktsze; | |
210 | int i=0; | |
211 | ||
212 | if (!maxsze) { | |
213 | USB_UHCI_PRINTF("uhci_submit_control_urb: pipesize for pipe %lx is zero\n", pipe); | |
214 | return -1; | |
215 | } | |
216 | if(((pipe>>8)&0x7f)==rh.devnum) { | |
217 | /* this is the root hub -> redirect it */ | |
218 | return uhci_submit_rh_msg(dev,pipe,buffer,transfer_len,setup); | |
219 | } | |
220 | USB_UHCI_PRINTF("uhci_submit_control start len %x, maxsize %x\n",transfer_len,maxsze); | |
221 | /* The "pipe" thing contains the destination in bits 8--18 */ | |
222 | destination = (pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* Setup stage */ | |
223 | /* 3 errors */ | |
224 | status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); | |
225 | /* (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD); */ | |
226 | /* Build the TD for the control request, try forever, 8 bytes of data */ | |
227 | usb_fill_td(&tmp_td[i],UHCI_PTR_TERM ,status, destination | (7 << 21),(unsigned long)setup,(unsigned long)dev); | |
228 | #if 0 | |
229 | { | |
230 | char *sp=(char *)setup; | |
231 | printf("SETUP to pipe %lx: %x %x %x %x %x %x %x %x\n", pipe, | |
232 | sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); | |
233 | } | |
234 | #endif | |
235 | dataptr = (unsigned long)buffer; | |
236 | len=transfer_len; | |
237 | ||
238 | /* If direction is "send", change the frame from SETUP (0x2D) | |
239 | to OUT (0xE1). Else change it from SETUP to IN (0x69). */ | |
240 | destination = (pipe & PIPE_DEVEP_MASK) | ((pipe & USB_DIR_IN)==0 ? USB_PID_OUT : USB_PID_IN); | |
241 | while (len > 0) { | |
242 | /* data stage */ | |
243 | pktsze = len; | |
244 | i++; | |
245 | if (pktsze > maxsze) | |
246 | pktsze = maxsze; | |
247 | destination ^= 1 << TD_TOKEN_TOGGLE; /* toggle DATA0/1 */ | |
248 | usb_fill_td(&tmp_td[i],UHCI_PTR_TERM, status, destination | ((pktsze - 1) << 21),dataptr,(unsigned long)dev); /* Status, pktsze bytes of data */ | |
249 | tmp_td[i-1].link=swap_32((unsigned long)&tmp_td[i]); | |
250 | ||
251 | dataptr += pktsze; | |
252 | len -= pktsze; | |
253 | } | |
254 | ||
255 | /* Build the final TD for control status */ | |
256 | /* It's only IN if the pipe is out AND we aren't expecting data */ | |
257 | ||
258 | destination &= ~UHCI_PID; | |
259 | if (((pipe & USB_DIR_IN)==0) || (transfer_len == 0)) | |
260 | destination |= USB_PID_IN; | |
261 | else | |
262 | destination |= USB_PID_OUT; | |
263 | destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ | |
264 | i++; | |
265 | status &=~TD_CTRL_SPD; | |
266 | /* no limit on errors on final packet , 0 bytes of data */ | |
267 | usb_fill_td(&tmp_td[i],UHCI_PTR_TERM, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),0,(unsigned long)dev); | |
268 | tmp_td[i-1].link=swap_32((unsigned long)&tmp_td[i]); /* queue status td */ | |
269 | /* usb_show_td(i+1);*/ | |
270 | USB_UHCI_PRINTF("uhci_submit_control end (%d tmp_tds used)\n",i); | |
271 | /* first mark the control QH element terminated */ | |
272 | qh_cntrl.element=0xffffffffL; | |
273 | /* set qh active */ | |
274 | qh_cntrl.dev_ptr=(unsigned long)dev; | |
275 | /* fill in tmp_td_chain */ | |
276 | qh_cntrl.element=swap_32((unsigned long)&tmp_td[0]); | |
277 | return 0; | |
278 | } | |
279 | ||
280 | /*------------------------------------------------------------------- | |
281 | * Prepare TDs for bulk transfers. | |
282 | */ | |
283 | int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len) | |
284 | { | |
285 | unsigned long destination, status,info; | |
286 | unsigned long dataptr; | |
287 | int maxsze = usb_maxpacket(dev, pipe); | |
288 | int len; | |
289 | int i=0; | |
290 | ||
291 | if(transfer_len < 0) { | |
292 | printf("Negative transfer length in submit_bulk\n"); | |
293 | return -1; | |
294 | } | |
295 | if (!maxsze) | |
296 | return -1; | |
297 | /* The "pipe" thing contains the destination in bits 8--18. */ | |
298 | destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); | |
299 | /* 3 errors */ | |
300 | status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); | |
301 | /* ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); */ | |
302 | /* Build the TDs for the bulk request */ | |
303 | len = transfer_len; | |
304 | dataptr = (unsigned long)buffer; | |
305 | do { | |
306 | int pktsze = len; | |
307 | if (pktsze > maxsze) | |
308 | pktsze = maxsze; | |
309 | /* pktsze bytes of data */ | |
310 | info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) | | |
311 | (usb_gettoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); | |
312 | ||
313 | if((len-pktsze)==0) | |
314 | status |= TD_CTRL_IOC; /* last one generates INT */ | |
315 | ||
316 | usb_fill_td(&tmp_td[i],UHCI_PTR_TERM, status, info,dataptr,(unsigned long)dev); /* Status, pktsze bytes of data */ | |
317 | if(i>0) | |
318 | tmp_td[i-1].link=swap_32((unsigned long)&tmp_td[i]); | |
319 | i++; | |
320 | dataptr += pktsze; | |
321 | len -= pktsze; | |
322 | usb_dotoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); | |
323 | } while (len > 0); | |
324 | /* first mark the bulk QH element terminated */ | |
325 | qh_bulk.element=0xffffffffL; | |
326 | /* set qh active */ | |
327 | qh_bulk.dev_ptr=(unsigned long)dev; | |
328 | /* fill in tmp_td_chain */ | |
329 | qh_bulk.element=swap_32((unsigned long)&tmp_td[0]); | |
330 | return 0; | |
331 | } | |
332 | ||
333 | ||
334 | /* search a free interrupt td | |
335 | */ | |
336 | uhci_td_t *uhci_alloc_int_td(void) | |
337 | { | |
338 | int i; | |
339 | for(i=0;i<USB_MAX_TEMP_INT_TD;i++) { | |
340 | if(tmp_int_td[i].dev_ptr==0) /* no device assigned -> free TD */ | |
341 | return &tmp_int_td[i]; | |
342 | } | |
343 | return NULL; | |
344 | } | |
345 | ||
346 | #if 0 | |
347 | void uhci_show_temp_int_td(void) | |
348 | { | |
349 | int i; | |
350 | for(i=0;i<USB_MAX_TEMP_INT_TD;i++) { | |
351 | if((tmp_int_td[i].dev_ptr&0x01)!=0x1L) /* no device assigned -> free TD */ | |
352 | printf("temp_td %d is assigned to dev %lx\n",i,tmp_int_td[i].dev_ptr); | |
353 | } | |
354 | printf("all others temp_tds are free\n"); | |
355 | } | |
356 | #endif | |
357 | /*------------------------------------------------------------------- | |
358 | * submits USB interrupt (ie. polling ;-) | |
359 | */ | |
360 | int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len, int interval) | |
361 | { | |
362 | int nint, n; | |
363 | unsigned long status, destination; | |
364 | unsigned long info,tmp; | |
365 | uhci_td_t *mytd; | |
366 | if (interval < 0 || interval >= 256) | |
367 | return -1; | |
368 | ||
369 | if (interval == 0) | |
370 | nint = 0; | |
371 | else { | |
372 | for (nint = 0, n = 1; nint <= 8; nint++, n += n) /* round interval down to 2^n */ | |
373 | { | |
374 | if(interval < n) { | |
375 | interval = n / 2; | |
376 | break; | |
377 | } | |
378 | } | |
379 | nint--; | |
380 | } | |
381 | ||
382 | USB_UHCI_PRINTF("Rounded interval to %i, chain %i\n", interval, nint); | |
383 | mytd=uhci_alloc_int_td(); | |
384 | if(mytd==NULL) { | |
385 | printf("No free INT TDs found\n"); | |
386 | return -1; | |
387 | } | |
388 | status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | (3 << 27); | |
389 | /* (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); | |
390 | */ | |
391 | ||
392 | destination =(pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe) | (((transfer_len - 1) & 0x7ff) << 21); | |
393 | ||
394 | info = destination | (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); | |
395 | tmp = swap_32(td_int[nint].link); | |
396 | usb_fill_td(mytd,tmp,status, info,(unsigned long)buffer,(unsigned long)dev); | |
397 | /* Link it */ | |
398 | tmp = swap_32((unsigned long)mytd); | |
399 | td_int[nint].link=tmp; | |
400 | ||
401 | usb_dotoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
406 | /********************************************************************** | |
407 | * Low Level functions | |
408 | */ | |
409 | ||
410 | ||
411 | void reset_hc(void) | |
412 | { | |
413 | ||
414 | /* Global reset for 100ms */ | |
415 | out16r( usb_base_addr + USBPORTSC1,0x0204); | |
416 | out16r( usb_base_addr + USBPORTSC2,0x0204); | |
417 | out16r( usb_base_addr + USBCMD,USBCMD_GRESET | USBCMD_RS); | |
418 | /* Turn off all interrupts */ | |
419 | out16r(usb_base_addr + USBINTR,0); | |
420 | wait_ms(50); | |
421 | out16r( usb_base_addr + USBCMD,0); | |
422 | wait_ms(10); | |
423 | } | |
424 | ||
425 | void start_hc(void) | |
426 | { | |
427 | int timeout = 1000; | |
428 | ||
429 | while(in16r(usb_base_addr + USBCMD) & USBCMD_HCRESET) { | |
430 | if (!--timeout) { | |
431 | printf("USBCMD_HCRESET timed out!\n"); | |
432 | break; | |
433 | } | |
434 | } | |
435 | /* Turn on all interrupts */ | |
436 | out16r(usb_base_addr + USBINTR,USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP); | |
437 | /* Start at frame 0 */ | |
438 | out16r(usb_base_addr + USBFRNUM,0); | |
439 | /* set Framebuffer base address */ | |
440 | out32r(usb_base_addr+USBFLBASEADD,(unsigned long)&framelist); | |
441 | /* Run and mark it configured with a 64-byte max packet */ | |
442 | out16r(usb_base_addr + USBCMD,USBCMD_RS | USBCMD_CF | USBCMD_MAXP); | |
443 | } | |
444 | ||
445 | /* Initialize the skeleton | |
446 | */ | |
447 | void usb_init_skel(void) | |
448 | { | |
449 | unsigned long temp; | |
450 | int n; | |
451 | ||
452 | for(n=0;n<USB_MAX_TEMP_INT_TD;n++) | |
453 | tmp_int_td[n].dev_ptr=0L; /* no devices connected */ | |
454 | /* last td */ | |
455 | usb_fill_td(&td_last,UHCI_PTR_TERM,TD_CTRL_IOC ,0,0,0L); | |
456 | /* usb_fill_td(&td_last,UHCI_PTR_TERM,0,0,0); */ | |
457 | /* End Queue Header */ | |
458 | usb_fill_qh(&qh_end,UHCI_PTR_TERM,(unsigned long)&td_last); | |
459 | /* Bulk Queue Header */ | |
460 | temp=(unsigned long)&qh_end; | |
461 | usb_fill_qh(&qh_bulk,temp | UHCI_PTR_QH,UHCI_PTR_TERM); | |
462 | /* Control Queue Header */ | |
463 | temp=(unsigned long)&qh_bulk; | |
464 | usb_fill_qh(&qh_cntrl, temp | UHCI_PTR_QH,UHCI_PTR_TERM); | |
465 | /* 1ms Interrupt td */ | |
466 | temp=(unsigned long)&qh_cntrl; | |
467 | usb_fill_td(&td_int[0],temp | UHCI_PTR_QH,0,0,0,0L); | |
468 | temp=(unsigned long)&td_int[0]; | |
469 | for(n=1; n<8; n++) | |
470 | usb_fill_td(&td_int[n],temp,0,0,0,0L); | |
471 | for (n = 0; n < 1024; n++) { | |
472 | /* link all framelist pointers to one of the interrupts */ | |
473 | int m, o; | |
474 | if ((n&127)==127) | |
475 | framelist[n]= swap_32((unsigned long)&td_int[0]); | |
476 | else | |
477 | for (o = 1, m = 2; m <= 128; o++, m += m) | |
478 | if ((n & (m - 1)) == ((m - 1) / 2)) | |
479 | framelist[n]= swap_32((unsigned long)&td_int[o]); | |
480 | } | |
481 | } | |
482 | ||
483 | /* check the common skeleton for completed transfers, and update the status | |
484 | * of the "connected" device. Called from the IRQ routine. | |
485 | */ | |
486 | void usb_check_skel(void) | |
487 | { | |
488 | struct usb_device *dev; | |
489 | /* start with the control qh */ | |
490 | if(qh_cntrl.dev_ptr!=0) /* it's a device assigned check if this caused IRQ */ | |
491 | { | |
492 | dev=(struct usb_device *)qh_cntrl.dev_ptr; | |
493 | usb_get_td_status(&tmp_td[0],dev); /* update status */ | |
494 | if(!(dev->status & USB_ST_NOT_PROC)) { /* is not active anymore, disconnect devices */ | |
495 | qh_cntrl.dev_ptr=0; | |
496 | } | |
497 | } | |
498 | /* now process the bulk */ | |
499 | if(qh_bulk.dev_ptr!=0) /* it's a device assigned check if this caused IRQ */ | |
500 | { | |
501 | dev=(struct usb_device *)qh_bulk.dev_ptr; | |
502 | usb_get_td_status(&tmp_td[0],dev); /* update status */ | |
503 | if(!(dev->status & USB_ST_NOT_PROC)) { /* is not active anymore, disconnect devices */ | |
504 | qh_bulk.dev_ptr=0; | |
505 | } | |
506 | } | |
507 | } | |
508 | ||
509 | /* check the interrupt chain, ubdate the status of the appropriate device, | |
510 | * call the appropriate irqhandler and reactivate the TD if the irqhandler | |
511 | * returns with 1 | |
512 | */ | |
513 | void usb_check_int_chain(void) | |
514 | { | |
515 | int i,res; | |
516 | unsigned long link,status; | |
517 | struct usb_device *dev; | |
518 | uhci_td_t *td,*prevtd; | |
519 | ||
520 | for(i=0;i<8;i++) { | |
521 | prevtd=&td_int[i]; /* the first previous td is the skeleton td */ | |
522 | link=swap_32(td_int[i].link) & 0xfffffff0; /* next in chain */ | |
523 | td=(uhci_td_t *)link; /* assign it */ | |
524 | /* all interrupt TDs are finally linked to the td_int[0]. | |
525 | * so we process all until we find the td_int[0]. | |
526 | * if int0 chain points to a QH, we're also done | |
527 | */ | |
528 | while(((i>0) && (link != (unsigned long)&td_int[0])) || | |
529 | ((i==0) && !(swap_32(td->link) & UHCI_PTR_QH))) | |
530 | { | |
531 | /* check if a device is assigned with this td */ | |
532 | status=swap_32(td->status); | |
533 | if((td->dev_ptr!=0L) && !(status & TD_CTRL_ACTIVE)) { | |
534 | /* td is not active and a device is assigned -> call irqhandler */ | |
535 | dev=(struct usb_device *)td->dev_ptr; | |
536 | dev->irq_act_len=((status & 0x7FF)==0x7FF) ? 0 : (status & 0x7FF) + 1; /* transfered length */ | |
537 | dev->irq_status=usb_uhci_td_stat(status); /* get status */ | |
538 | res=dev->irq_handle(dev); /* call irqhandler */ | |
539 | if(res==1) { | |
540 | /* reactivate */ | |
541 | status|=TD_CTRL_ACTIVE; | |
542 | td->status=swap_32(status); | |
543 | prevtd=td; /* previous td = this td */ | |
544 | } | |
545 | else { | |
546 | prevtd->link=td->link; /* link previous td directly to the nex td -> unlinked */ | |
547 | /* remove device pointer */ | |
548 | td->dev_ptr=0L; | |
549 | } | |
550 | } /* if we call the irq handler */ | |
551 | link=swap_32(td->link) & 0xfffffff0; /* next in chain */ | |
552 | td=(uhci_td_t *)link; /* assign it */ | |
553 | } /* process all td in this int chain */ | |
554 | } /* next interrupt chain */ | |
555 | } | |
556 | ||
557 | ||
558 | /* usb interrupt service routine. | |
559 | */ | |
560 | void handle_usb_interrupt(void) | |
561 | { | |
562 | unsigned short status; | |
563 | ||
564 | /* | |
565 | * Read the interrupt status, and write it back to clear the | |
566 | * interrupt cause | |
567 | */ | |
568 | ||
569 | status = in16r(usb_base_addr + USBSTS); | |
570 | ||
571 | if (!status) /* shared interrupt, not mine */ | |
572 | return; | |
573 | if (status != 1) { | |
574 | /* remove host controller halted state */ | |
575 | if ((status&0x20) && ((in16r(usb_base_addr+USBCMD) && USBCMD_RS)==0)) { | |
576 | out16r(usb_base_addr + USBCMD, USBCMD_RS | in16r(usb_base_addr + USBCMD)); | |
577 | } | |
578 | } | |
579 | usb_check_int_chain(); /* call interrupt handlers for int tds */ | |
580 | usb_check_skel(); /* call completion handler for common transfer routines */ | |
581 | out16r(usb_base_addr+USBSTS,status); | |
582 | } | |
583 | ||
584 | ||
585 | /* init uhci | |
586 | */ | |
587 | int usb_lowlevel_init(void) | |
588 | { | |
589 | unsigned char temp; | |
590 | int busdevfunc; | |
591 | /* | |
592 | * HJF - configure IRQ and base from variables optionally. | |
593 | */ | |
594 | char *s; | |
595 | ||
596 | ||
597 | busdevfunc=pci_find_device(USB_UHCI_VEND_ID,USB_UHCI_DEV_ID,0); /* get PCI Device ID */ | |
598 | if(busdevfunc==-1) { | |
599 | printf("Error USB UHCI (%04X,%04X) not found\n",USB_UHCI_VEND_ID,USB_UHCI_DEV_ID); | |
600 | return -1; | |
601 | } | |
8bde7f77 | 602 | |
c7de829c WD |
603 | #if 1 |
604 | s = getenv("usb_irq"); | |
605 | if (s) | |
606 | { | |
607 | temp = atoi(s); | |
608 | pci_write_config_byte(busdevfunc, PCI_INTERRUPT_LINE, temp); | |
609 | } | |
610 | else | |
611 | #endif | |
612 | pci_read_config_byte(busdevfunc,PCI_INTERRUPT_LINE,&temp); | |
613 | ||
614 | s = getenv("usb_base"); | |
615 | if (s) | |
616 | { | |
617 | unsigned long temp2; | |
618 | temp2 = atoi(s); | |
619 | pci_write_config_dword(busdevfunc, PCI_BASE_ADDRESS_4, temp2|0x01); | |
620 | } | |
621 | ||
622 | irqvec = temp; | |
623 | irq_free_handler(irqvec); | |
624 | USB_UHCI_PRINTF("Interrupt Line = %d\n",irqvec); | |
625 | pci_read_config_byte(busdevfunc,PCI_INTERRUPT_PIN,&temp); | |
626 | USB_UHCI_PRINTF("Interrupt Pin = %ld\n",temp); | |
627 | pci_read_config_dword(busdevfunc,PCI_BASE_ADDRESS_4,&usb_base_addr); | |
628 | USB_UHCI_PRINTF("IO Base Address = 0x%lx\n",usb_base_addr); | |
629 | usb_base_addr&=0xFFFFFFF0; | |
630 | usb_base_addr+=CFG_ISA_IO_BASE_ADDRESS; | |
631 | rh.devnum = 0; | |
632 | usb_init_skel(); | |
633 | reset_hc(); | |
634 | start_hc(); | |
635 | irq_install_handler(irqvec, (interrupt_handler_t *)handle_usb_interrupt, NULL); | |
636 | irq_install_handler(0, (interrupt_handler_t *)handle_usb_interrupt, NULL); | |
637 | ||
638 | return 0; | |
639 | } | |
640 | ||
641 | /* stop uhci | |
642 | */ | |
643 | int usb_lowlevel_stop(void) | |
644 | { | |
645 | if(irqvec==-1) | |
646 | return 1; | |
647 | irq_free_handler(irqvec); | |
648 | irq_free_handler(0); | |
649 | reset_hc(); | |
650 | irqvec=-1; | |
651 | return 0; | |
652 | } | |
653 | ||
654 | /******************************************************************************************* | |
655 | * Virtual Root Hub | |
656 | * Since the uhci does not have a real HUB, we simulate one ;-) | |
657 | */ | |
658 | #undef USB_RH_DEBUG | |
659 | ||
660 | #ifdef USB_RH_DEBUG | |
661 | #define USB_RH_PRINTF(fmt,args...) printf (fmt ,##args) | |
662 | static void usb_display_wValue(unsigned short wValue,unsigned short wIndex); | |
663 | static void usb_display_Req(unsigned short req); | |
664 | #else | |
665 | #define USB_RH_PRINTF(fmt,args...) | |
666 | static void usb_display_wValue(unsigned short wValue,unsigned short wIndex) {} | |
667 | static void usb_display_Req(unsigned short req) {} | |
668 | #endif | |
669 | ||
670 | static unsigned char root_hub_dev_des[] = | |
671 | { | |
672 | 0x12, /* __u8 bLength; */ | |
673 | 0x01, /* __u8 bDescriptorType; Device */ | |
674 | 0x00, /* __u16 bcdUSB; v1.0 */ | |
675 | 0x01, | |
676 | 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ | |
677 | 0x00, /* __u8 bDeviceSubClass; */ | |
678 | 0x00, /* __u8 bDeviceProtocol; */ | |
679 | 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ | |
680 | 0x00, /* __u16 idVendor; */ | |
681 | 0x00, | |
682 | 0x00, /* __u16 idProduct; */ | |
683 | 0x00, | |
684 | 0x00, /* __u16 bcdDevice; */ | |
685 | 0x00, | |
686 | 0x01, /* __u8 iManufacturer; */ | |
687 | 0x00, /* __u8 iProduct; */ | |
688 | 0x00, /* __u8 iSerialNumber; */ | |
689 | 0x01 /* __u8 bNumConfigurations; */ | |
690 | }; | |
691 | ||
692 | ||
693 | /* Configuration descriptor */ | |
694 | static unsigned char root_hub_config_des[] = | |
695 | { | |
696 | 0x09, /* __u8 bLength; */ | |
697 | 0x02, /* __u8 bDescriptorType; Configuration */ | |
698 | 0x19, /* __u16 wTotalLength; */ | |
699 | 0x00, | |
700 | 0x01, /* __u8 bNumInterfaces; */ | |
701 | 0x01, /* __u8 bConfigurationValue; */ | |
702 | 0x00, /* __u8 iConfiguration; */ | |
703 | 0x40, /* __u8 bmAttributes; | |
704 | Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ | |
705 | 0x00, /* __u8 MaxPower; */ | |
706 | ||
707 | /* interface */ | |
708 | 0x09, /* __u8 if_bLength; */ | |
709 | 0x04, /* __u8 if_bDescriptorType; Interface */ | |
710 | 0x00, /* __u8 if_bInterfaceNumber; */ | |
711 | 0x00, /* __u8 if_bAlternateSetting; */ | |
712 | 0x01, /* __u8 if_bNumEndpoints; */ | |
713 | 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ | |
714 | 0x00, /* __u8 if_bInterfaceSubClass; */ | |
715 | 0x00, /* __u8 if_bInterfaceProtocol; */ | |
716 | 0x00, /* __u8 if_iInterface; */ | |
717 | ||
718 | /* endpoint */ | |
719 | 0x07, /* __u8 ep_bLength; */ | |
720 | 0x05, /* __u8 ep_bDescriptorType; Endpoint */ | |
721 | 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ | |
722 | 0x03, /* __u8 ep_bmAttributes; Interrupt */ | |
723 | 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ | |
724 | 0x00, | |
725 | 0xff /* __u8 ep_bInterval; 255 ms */ | |
726 | }; | |
727 | ||
728 | ||
729 | static unsigned char root_hub_hub_des[] = | |
730 | { | |
731 | 0x09, /* __u8 bLength; */ | |
732 | 0x29, /* __u8 bDescriptorType; Hub-descriptor */ | |
733 | 0x02, /* __u8 bNbrPorts; */ | |
734 | 0x00, /* __u16 wHubCharacteristics; */ | |
735 | 0x00, | |
736 | 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ | |
737 | 0x00, /* __u8 bHubContrCurrent; 0 mA */ | |
738 | 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ | |
739 | 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ | |
740 | }; | |
741 | ||
742 | static unsigned char root_hub_str_index0[] = | |
743 | { | |
744 | 0x04, /* __u8 bLength; */ | |
745 | 0x03, /* __u8 bDescriptorType; String-descriptor */ | |
746 | 0x09, /* __u8 lang ID */ | |
747 | 0x04, /* __u8 lang ID */ | |
748 | }; | |
749 | ||
750 | static unsigned char root_hub_str_index1[] = | |
751 | { | |
752 | 28, /* __u8 bLength; */ | |
753 | 0x03, /* __u8 bDescriptorType; String-descriptor */ | |
754 | 'U', /* __u8 Unicode */ | |
755 | 0, /* __u8 Unicode */ | |
756 | 'H', /* __u8 Unicode */ | |
757 | 0, /* __u8 Unicode */ | |
758 | 'C', /* __u8 Unicode */ | |
759 | 0, /* __u8 Unicode */ | |
760 | 'I', /* __u8 Unicode */ | |
761 | 0, /* __u8 Unicode */ | |
762 | ' ', /* __u8 Unicode */ | |
763 | 0, /* __u8 Unicode */ | |
764 | 'R', /* __u8 Unicode */ | |
765 | 0, /* __u8 Unicode */ | |
766 | 'o', /* __u8 Unicode */ | |
767 | 0, /* __u8 Unicode */ | |
768 | 'o', /* __u8 Unicode */ | |
769 | 0, /* __u8 Unicode */ | |
770 | 't', /* __u8 Unicode */ | |
771 | 0, /* __u8 Unicode */ | |
772 | ' ', /* __u8 Unicode */ | |
773 | 0, /* __u8 Unicode */ | |
774 | 'H', /* __u8 Unicode */ | |
775 | 0, /* __u8 Unicode */ | |
776 | 'u', /* __u8 Unicode */ | |
777 | 0, /* __u8 Unicode */ | |
778 | 'b', /* __u8 Unicode */ | |
779 | 0, /* __u8 Unicode */ | |
780 | }; | |
781 | ||
782 | ||
783 | /* | |
784 | * Root Hub Control Pipe (interrupt Pipes are not supported) | |
785 | */ | |
786 | ||
787 | ||
788 | int uhci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len,struct devrequest *cmd) | |
789 | { | |
790 | void *data = buffer; | |
791 | int leni = transfer_len; | |
792 | int len = 0; | |
793 | int status = 0; | |
794 | int stat = 0; | |
795 | int i; | |
796 | ||
797 | unsigned short cstatus; | |
798 | ||
799 | unsigned short bmRType_bReq; | |
800 | unsigned short wValue; | |
801 | unsigned short wIndex; | |
802 | unsigned short wLength; | |
803 | ||
804 | if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { | |
805 | printf("Root-Hub submit IRQ: NOT implemented\n"); | |
806 | #if 0 | |
807 | uhci->rh.urb = urb; | |
808 | uhci->rh.send = 1; | |
809 | uhci->rh.interval = urb->interval; | |
810 | rh_init_int_timer (urb); | |
811 | #endif | |
812 | return 0; | |
813 | } | |
814 | bmRType_bReq = cmd->requesttype | cmd->request << 8; | |
815 | wValue = swap_16(cmd->value); | |
816 | wIndex = swap_16(cmd->index); | |
817 | wLength = swap_16(cmd->length); | |
818 | usb_display_Req(bmRType_bReq); | |
819 | for (i = 0; i < 8; i++) | |
820 | rh.c_p_r[i] = 0; | |
821 | USB_RH_PRINTF("Root-Hub: adr: %2x cmd(%1x): %02x%02x %04x %04x %04x\n", | |
822 | dev->devnum, 8, cmd->requesttype,cmd->request, wValue, wIndex, wLength); | |
823 | ||
824 | switch (bmRType_bReq) { | |
825 | /* Request Destination: | |
826 | without flags: Device, | |
827 | RH_INTERFACE: interface, | |
828 | RH_ENDPOINT: endpoint, | |
829 | RH_CLASS means HUB here, | |
830 | RH_OTHER | RH_CLASS almost ever means HUB_PORT here | |
831 | */ | |
832 | ||
833 | case RH_GET_STATUS: | |
834 | *(unsigned short *) data = swap_16(1); | |
835 | len=2; | |
836 | break; | |
837 | case RH_GET_STATUS | RH_INTERFACE: | |
838 | *(unsigned short *) data = swap_16(0); | |
839 | len=2; | |
840 | break; | |
841 | case RH_GET_STATUS | RH_ENDPOINT: | |
842 | *(unsigned short *) data = swap_16(0); | |
843 | len=2; | |
844 | break; | |
845 | case RH_GET_STATUS | RH_CLASS: | |
846 | *(unsigned long *) data = swap_32(0); | |
847 | len=4; | |
848 | break; /* hub power ** */ | |
849 | case RH_GET_STATUS | RH_OTHER | RH_CLASS: | |
850 | ||
851 | status = in16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1)); | |
852 | cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | | |
853 | ((status & USBPORTSC_PEC) >> (3 - 1)) | | |
854 | (rh.c_p_r[wIndex - 1] << (0 + 4)); | |
855 | status = (status & USBPORTSC_CCS) | | |
856 | ((status & USBPORTSC_PE) >> (2 - 1)) | | |
857 | ((status & USBPORTSC_SUSP) >> (12 - 2)) | | |
858 | ((status & USBPORTSC_PR) >> (9 - 4)) | | |
859 | (1 << 8) | /* power on ** */ | |
860 | ((status & USBPORTSC_LSDA) << (-8 + 9)); | |
861 | ||
862 | *(unsigned short *) data = swap_16(status); | |
863 | *(unsigned short *) (data + 2) = swap_16(cstatus); | |
864 | len=4; | |
865 | break; | |
866 | case RH_CLEAR_FEATURE | RH_ENDPOINT: | |
867 | switch (wValue) { | |
868 | case (RH_ENDPOINT_STALL): | |
869 | len=0; | |
870 | break; | |
871 | } | |
872 | break; | |
873 | ||
874 | case RH_CLEAR_FEATURE | RH_CLASS: | |
875 | switch (wValue) { | |
876 | case (RH_C_HUB_OVER_CURRENT): | |
877 | len=0; /* hub power over current ** */ | |
878 | break; | |
879 | } | |
880 | break; | |
881 | ||
882 | case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: | |
883 | usb_display_wValue(wValue,wIndex); | |
884 | switch (wValue) { | |
885 | case (RH_PORT_ENABLE): | |
886 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
887 | status = (status & 0xfff5) & ~USBPORTSC_PE; | |
888 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
889 | len=0; | |
890 | break; | |
891 | case (RH_PORT_SUSPEND): | |
892 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
893 | status = (status & 0xfff5) & ~USBPORTSC_SUSP; | |
894 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
895 | len=0; | |
896 | break; | |
897 | case (RH_PORT_POWER): | |
898 | len=0; /* port power ** */ | |
899 | break; | |
900 | case (RH_C_PORT_CONNECTION): | |
901 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
902 | status = (status & 0xfff5) | USBPORTSC_CSC; | |
903 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
904 | len=0; | |
905 | break; | |
906 | case (RH_C_PORT_ENABLE): | |
907 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
908 | status = (status & 0xfff5) | USBPORTSC_PEC; | |
909 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
910 | len=0; | |
911 | break; | |
912 | case (RH_C_PORT_SUSPEND): | |
913 | /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ | |
914 | len=0; | |
915 | break; | |
916 | case (RH_C_PORT_OVER_CURRENT): | |
917 | len=0; | |
918 | break; | |
919 | case (RH_C_PORT_RESET): | |
920 | rh.c_p_r[wIndex - 1] = 0; | |
921 | len=0; | |
922 | break; | |
923 | } | |
924 | break; | |
925 | case RH_SET_FEATURE | RH_OTHER | RH_CLASS: | |
926 | usb_display_wValue(wValue,wIndex); | |
927 | switch (wValue) { | |
928 | case (RH_PORT_SUSPEND): | |
929 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
930 | status = (status & 0xfff5) | USBPORTSC_SUSP; | |
931 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
932 | len=0; | |
933 | break; | |
934 | case (RH_PORT_RESET): | |
935 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
936 | status = (status & 0xfff5) | USBPORTSC_PR; | |
937 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
938 | wait_ms(10); | |
939 | status = (status & 0xfff5) & ~USBPORTSC_PR; | |
940 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
941 | udelay(10); | |
942 | status = (status & 0xfff5) | USBPORTSC_PE; | |
943 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
944 | wait_ms(10); | |
945 | status = (status & 0xfff5) | 0xa; | |
946 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
947 | len=0; | |
948 | break; | |
949 | case (RH_PORT_POWER): | |
950 | len=0; /* port power ** */ | |
951 | break; | |
952 | case (RH_PORT_ENABLE): | |
953 | status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); | |
954 | status = (status & 0xfff5) | USBPORTSC_PE; | |
955 | out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); | |
956 | len=0; | |
957 | break; | |
958 | } | |
959 | break; | |
960 | ||
961 | case RH_SET_ADDRESS: | |
962 | rh.devnum = wValue; | |
963 | len=0; | |
964 | break; | |
965 | case RH_GET_DESCRIPTOR: | |
966 | switch ((wValue & 0xff00) >> 8) { | |
967 | case (0x01): /* device descriptor */ | |
968 | i=sizeof(root_hub_config_des); | |
969 | status=i > wLength ? wLength : i; | |
970 | len = leni > status ? status : leni; | |
971 | memcpy (data, root_hub_dev_des, len); | |
972 | break; | |
973 | case (0x02): /* configuration descriptor */ | |
974 | i=sizeof(root_hub_config_des); | |
975 | status=i > wLength ? wLength : i; | |
976 | len = leni > status ? status : leni; | |
977 | memcpy (data, root_hub_config_des, len); | |
978 | break; | |
979 | case (0x03): /*string descriptors */ | |
980 | if(wValue==0x0300) { | |
981 | i=sizeof(root_hub_str_index0); | |
982 | status = i > wLength ? wLength : i; | |
983 | len = leni > status ? status : leni; | |
984 | memcpy (data, root_hub_str_index0, len); | |
985 | break; | |
986 | } | |
987 | if(wValue==0x0301) { | |
988 | i=sizeof(root_hub_str_index1); | |
989 | status = i > wLength ? wLength : i; | |
990 | len = leni > status ? status : leni; | |
991 | memcpy (data, root_hub_str_index1, len); | |
992 | break; | |
993 | } | |
994 | stat = USB_ST_STALLED; | |
995 | } | |
996 | break; | |
997 | ||
998 | case RH_GET_DESCRIPTOR | RH_CLASS: | |
999 | root_hub_hub_des[2] = 2; | |
1000 | i=sizeof(root_hub_hub_des); | |
1001 | status= i > wLength ? wLength : i; | |
1002 | len = leni > status ? status : leni; | |
1003 | memcpy (data, root_hub_hub_des, len); | |
1004 | break; | |
1005 | case RH_GET_CONFIGURATION: | |
1006 | *(unsigned char *) data = 0x01; | |
1007 | len = 1; | |
1008 | break; | |
1009 | case RH_SET_CONFIGURATION: | |
1010 | len=0; | |
1011 | break; | |
1012 | default: | |
1013 | stat = USB_ST_STALLED; | |
1014 | } | |
1015 | USB_RH_PRINTF("Root-Hub stat %lx port1: %x port2: %x\n\n",stat, | |
1016 | in16r(usb_base_addr + USBPORTSC1), in16r(usb_base_addr + USBPORTSC2)); | |
1017 | dev->act_len=len; | |
1018 | dev->status=stat; | |
1019 | return stat; | |
1020 | ||
1021 | } | |
1022 | ||
1023 | /******************************************************************************** | |
1024 | * Some Debug Routines | |
1025 | */ | |
1026 | ||
1027 | #ifdef USB_RH_DEBUG | |
1028 | ||
1029 | static void usb_display_Req(unsigned short req) | |
1030 | { | |
1031 | USB_RH_PRINTF("- Root-Hub Request: "); | |
1032 | switch (req) { | |
1033 | case RH_GET_STATUS: | |
1034 | USB_RH_PRINTF("Get Status "); | |
1035 | break; | |
1036 | case RH_GET_STATUS | RH_INTERFACE: | |
1037 | USB_RH_PRINTF("Get Status Interface "); | |
1038 | break; | |
1039 | case RH_GET_STATUS | RH_ENDPOINT: | |
1040 | USB_RH_PRINTF("Get Status Endpoint "); | |
1041 | break; | |
1042 | case RH_GET_STATUS | RH_CLASS: | |
1043 | USB_RH_PRINTF("Get Status Class"); | |
1044 | break; /* hub power ** */ | |
1045 | case RH_GET_STATUS | RH_OTHER | RH_CLASS: | |
1046 | USB_RH_PRINTF("Get Status Class Others"); | |
1047 | break; | |
1048 | case RH_CLEAR_FEATURE | RH_ENDPOINT: | |
1049 | USB_RH_PRINTF("Clear Feature Endpoint "); | |
1050 | break; | |
1051 | case RH_CLEAR_FEATURE | RH_CLASS: | |
1052 | USB_RH_PRINTF("Clear Feature Class "); | |
1053 | break; | |
1054 | case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: | |
1055 | USB_RH_PRINTF("Clear Feature Other Class "); | |
1056 | break; | |
1057 | case RH_SET_FEATURE | RH_OTHER | RH_CLASS: | |
1058 | USB_RH_PRINTF("Set Feature Other Class "); | |
1059 | break; | |
1060 | case RH_SET_ADDRESS: | |
1061 | USB_RH_PRINTF("Set Address "); | |
1062 | break; | |
1063 | case RH_GET_DESCRIPTOR: | |
1064 | USB_RH_PRINTF("Get Descriptor "); | |
1065 | break; | |
1066 | case RH_GET_DESCRIPTOR | RH_CLASS: | |
1067 | USB_RH_PRINTF("Get Descriptor Class "); | |
1068 | break; | |
1069 | case RH_GET_CONFIGURATION: | |
1070 | USB_RH_PRINTF("Get Configuration "); | |
1071 | break; | |
1072 | case RH_SET_CONFIGURATION: | |
1073 | USB_RH_PRINTF("Get Configuration "); | |
1074 | break; | |
1075 | default: | |
1076 | USB_RH_PRINTF("****UNKNOWN**** 0x%04X ",req); | |
1077 | } | |
1078 | USB_RH_PRINTF("\n"); | |
1079 | ||
1080 | } | |
1081 | ||
1082 | static void usb_display_wValue(unsigned short wValue,unsigned short wIndex) | |
1083 | { | |
1084 | switch (wValue) { | |
1085 | case (RH_PORT_ENABLE): | |
1086 | USB_RH_PRINTF("Root-Hub: Enable Port %d\n",wIndex); | |
1087 | break; | |
1088 | case (RH_PORT_SUSPEND): | |
1089 | USB_RH_PRINTF("Root-Hub: Suspend Port %d\n",wIndex); | |
1090 | break; | |
1091 | case (RH_PORT_POWER): | |
1092 | USB_RH_PRINTF("Root-Hub: Port Power %d\n",wIndex); | |
1093 | break; | |
1094 | case (RH_C_PORT_CONNECTION): | |
1095 | USB_RH_PRINTF("Root-Hub: C Port Connection Port %d\n",wIndex); | |
1096 | break; | |
1097 | case (RH_C_PORT_ENABLE): | |
1098 | USB_RH_PRINTF("Root-Hub: C Port Enable Port %d\n",wIndex); | |
1099 | break; | |
1100 | case (RH_C_PORT_SUSPEND): | |
1101 | USB_RH_PRINTF("Root-Hub: C Port Suspend Port %d\n",wIndex); | |
1102 | break; | |
1103 | case (RH_C_PORT_OVER_CURRENT): | |
1104 | USB_RH_PRINTF("Root-Hub: C Port Over Current Port %d\n",wIndex); | |
1105 | break; | |
1106 | case (RH_C_PORT_RESET): | |
1107 | USB_RH_PRINTF("Root-Hub: C Port reset Port %d\n",wIndex); | |
1108 | break; | |
1109 | default: | |
1110 | USB_RH_PRINTF("Root-Hub: unknown %x %x\n",wValue,wIndex); | |
1111 | break; | |
1112 | } | |
1113 | } | |
1114 | ||
1115 | #endif | |
1116 | ||
1117 | ||
c7de829c WD |
1118 | #ifdef USB_UHCI_DEBUG |
1119 | ||
1120 | static int usb_display_td(uhci_td_t *td) | |
1121 | { | |
1122 | unsigned long tmp; | |
1123 | int valid; | |
1124 | ||
1125 | printf("TD at %p:\n",td); | |
1126 | ||
1127 | tmp=swap_32(td->link); | |
1128 | printf("Link points to 0x%08lX, %s first, %s, %s\n",tmp&0xfffffff0, | |
1129 | ((tmp & 0x4)==0x4) ? "Depth" : "Breath", | |
1130 | ((tmp & 0x2)==0x2) ? "QH" : "TD", | |
1131 | ((tmp & 0x1)==0x1) ? "invalid" : "valid"); | |
1132 | valid=((tmp & 0x1)==0x0); | |
1133 | tmp=swap_32(td->status); | |
1134 | printf(" %s %ld Errors %s %s %s \n %s %s %s %s %s %s\n Len 0x%lX\n", | |
1135 | (((tmp>>29)&0x1)==0x1) ? "SPD Enable" : "SPD Disable", | |
1136 | ((tmp>>28)&0x3), | |
1137 | (((tmp>>26)&0x1)==0x1) ? "Low Speed" : "Full Speed", | |
1138 | (((tmp>>25)&0x1)==0x1) ? "ISO " : "", | |
1139 | (((tmp>>24)&0x1)==0x1) ? "IOC " : "", | |
1140 | (((tmp>>23)&0x1)==0x1) ? "Active " : "Inactive ", | |
1141 | (((tmp>>22)&0x1)==0x1) ? "Stalled" : "", | |
1142 | (((tmp>>21)&0x1)==0x1) ? "Data Buffer Error" : "", | |
1143 | (((tmp>>20)&0x1)==0x1) ? "Babble" : "", | |
1144 | (((tmp>>19)&0x1)==0x1) ? "NAK" : "", | |
1145 | (((tmp>>18)&0x1)==0x1) ? "Bitstuff Error" : "", | |
1146 | (tmp&0x7ff)); | |
1147 | tmp=swap_32(td->info); | |
1148 | printf(" MaxLen 0x%lX\n",((tmp>>21)&0x7FF)); | |
1149 | printf(" %s Endpoint 0x%lX Dev Addr 0x%lX PID 0x%lX\n",((tmp>>19)&0x1)==0x1 ? "TOGGLE" : "", | |
1150 | ((tmp>>15)&0xF),((tmp>>8)&0x7F),tmp&0xFF); | |
1151 | tmp=swap_32(td->buffer); | |
1152 | printf(" Buffer 0x%08lX\n",tmp); | |
1153 | printf(" DEV %08lX\n",td->dev_ptr); | |
1154 | return valid; | |
1155 | } | |
1156 | ||
1157 | ||
1158 | void usb_show_td(int max) | |
1159 | { | |
1160 | int i; | |
1161 | if(max>0) { | |
1162 | for(i=0;i<max;i++) { | |
1163 | usb_display_td(&tmp_td[i]); | |
1164 | } | |
1165 | } | |
1166 | else { | |
1167 | i=0; | |
1168 | do { | |
1169 | printf("tmp_td[%d]\n",i); | |
1170 | }while(usb_display_td(&tmp_td[i++])); | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | ||
1175 | #endif | |
1176 | #endif /* CONFIG_USB_UHCI */ | |
1177 | ||
1178 | /* EOF */ |