patch-2.3.99-pre3 linux/drivers/usb/usb-ohci.c

Next file: linux/drivers/usb/usb-ohci.h
Previous file: linux/drivers/usb/uhci.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
@@ -262,8 +262,14 @@
 	urb_print (urb, "SUB", usb_pipein (pipe));
 #endif
 	
+	/* a request to the virtual root hub */
 	if (usb_pipedevice (pipe) == ohci->rh.devnum) 
-		return rh_submit_urb (urb); /* a request to the virtual root hub */
+		return rh_submit_urb (urb);
+	
+	/* when controller's hung, permit only hub cleanup attempts
+	 * such as powering down ports */
+	if (ohci->disabled)
+		return -ESHUTDOWN;
 
 	/* every endpoint has a ed, locate and fill it */
 	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
@@ -333,10 +339,11 @@
 								(le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
 	}	
 	
-	td_submit_urb (urb); /* fill the TDs and link it to the ed */
-						
 	if (ed->state != ED_OPER)  /* link the ed into a chain if is not already */
 		ep_link (ohci, ed);
+	
+	td_submit_urb (urb); /* fill the TDs and link it to the ed */
+
 	spin_unlock_irqrestore (&usb_ed_lock, flags);
 	
 	urb->status = USB_ST_URB_PENDING; 
@@ -1122,8 +1129,8 @@
 	0x00,       /*  __u16 bcdDevice; */
  	0x00,
 	0x00,       /*  __u8  iManufacturer; */
-	0x00,       /*  __u8  iProduct; */
-	0x00,       /*  __u8  iSerialNumber; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
 	0x01        /*  __u8  bNumConfigurations; */
 };
 
@@ -1222,13 +1229,16 @@
 
 	urb_t * urb = (urb_t *) ptr;
 	ohci_t * ohci = urb->dev->bus->hcpriv;
+
+	if (ohci->disabled)
+	    return;
 	
 	if(ohci->rh.send) { 
 		len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
 		if (len > 0) {
 			urb->actual_length = len;
 #ifdef DEBUG
-			urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+			urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
 #endif
 			if (urb->complete) urb->complete (urb);
 		}
@@ -1379,27 +1389,49 @@
 					len = min (leni, min (sizeof (root_hub_config_des), wLength));
 					data_buf = root_hub_config_des; OK(len);
 				case (0x03): /* string descriptors */
+					len = usb_root_hub_string (wValue & 0xff,
+						(int) ohci->regs, "OHCI",
+						data, wLength);
+					if (len > 0) {
+						data_buf = data;
+						OK (min (leni, len));
+					}
+					// else fallthrough
 				default: 
 					status = TD_CC_STALL;
 			}
 			break;
 		
 		case RH_GET_DESCRIPTOR | RH_CLASS:
-			*(__u8 *)  (data_buf+1) = 0x29;
-			put_unaligned(cpu_to_le32 (readl (&ohci->regs->roothub.a)),
-				      (__u32 *) (data_buf + 2));
-	 		*(__u8 *)  data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */
-				 
-			len = min (leni, min(*(__u8 *) data_buf, wLength));
-			*(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */
-			if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */
-				*(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff; 
-				*(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16; 
-			} else {
-				put_unaligned(cpu_to_le32 (readl(&ohci->regs->roothub.b)),
-					      (__u32 *) (data_buf + 7));
+		    {
+			    __u32 temp = readl (&ohci->regs->roothub.a);
+
+			    data_buf [0] = 9;		// min length;
+			    data_buf [1] = 0x29;
+			    data_buf [2] = temp & RH_A_NDP;
+			    data_buf [3] = 0;
+			    if (temp & RH_A_PSM) 	/* per-port power switching? */
+				data_buf [3] |= 0x1;
+			    if (temp & RH_A_NOCP)	/* no overcurrent reporting? */
+				data_buf [3] |= 0x10;
+			    else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */
+				data_buf [3] |= 0x8;
+
+			    datab [1] = 0;
+			    data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+			    temp = readl (&ohci->regs->roothub.b);
+			    data_buf [7] = temp & RH_B_DR;
+			    if (data_buf [2] < 7) {
+				data_buf [8] = 0xff;
+			    } else {
+				data_buf [0] += 2;
+				data_buf [8] = (temp & RH_B_DR) >> 8;
+				data_buf [10] = data_buf [9] = 0xff;
+			    }
+				
+			    len = min (leni, min (data_buf [0], wLength));
+			    OK (len);
 			}
-			OK (len); 
  
 		case RH_GET_CONFIGURATION: 	*(__u8 *) data_buf = 0x01; OK (1);
 
@@ -1413,7 +1445,8 @@
 	dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) ));
 
 	len = min(len, leni);
-	memcpy (data, data_buf, len);
+	if (data != data_buf)
+	    memcpy (data, data_buf, len);
   	urb->actual_length = len;
 	urb->status = cc_to_error [status];
 	
@@ -1472,6 +1505,7 @@
 		}	
 		udelay (1);
 	}	 
+	ohci->disabled = 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1502,7 +1536,7 @@
 	writel (0x628, &ohci->regs->lsthresh);
 
 	/* Choose the interrupts we care about now, others later on demand */
-	mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO;
+	mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
 	
 	writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */
 	writel (mask, &ohci->regs->intrenable);
@@ -1550,6 +1584,11 @@
 
 	dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
 	
+	if (ints & OHCI_INTR_UE) {
+		ohci->disabled++;
+		err ("OHCI Unrecoverable Error, controller disabled");
+	}
+  
 	if (ints & OHCI_INTR_WDH) {
 		writel (OHCI_INTR_WDH, &regs->intrdisable);	
 		dl_done_list (ohci, dl_reverse_done_list (ohci));
@@ -1650,16 +1689,24 @@
 static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base)
 {
 	ohci_t * ohci;
-	dbg("USB HC found: irq= %d membase= %lx", irq, (unsigned long) mem_base);
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
+		(unsigned long)	mem_base, bufp);
     
 	ohci = hc_alloc_ohci (mem_base);
 	if (!ohci) {
 		return -ENOMEM;
 	}
-	
+
 	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
 	list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
-	
+
 	hc_reset (ohci);
 	writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
 	wait_ms (10);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)