Skip to content
Snippets Groups Projects
usb_storage.c 39.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	const unsigned char max_product_len = 20;
    	if (descriptor.idVendor == 0x0424 && descriptor.idProduct == 0x223a) {
    
    		strncpy((char *)vendor, "SMSC", max_vendor_len);
    		strncpy((char *)product, "Flash Media Cntrller",
    			max_product_len);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define USB_MAX_READ_BLK 20
    
    
    unsigned long usb_stor_read(int device, unsigned long blknr,
    			    unsigned long blkcnt, void *buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	unsigned long start, blks, buf_addr;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	unsigned short smallblks;
    	struct usb_device *dev;
    
    	int retry, i;
    
    	ccb *srb = &usb_ccb;
    
    	if (blkcnt == 0)
    		return 0;
    
    	device &= 0xff;
    
    	/* Setup  device */
    	USB_STOR_PRINTF("\nusb_read: dev %d \n", device);
    	dev = NULL;
    	for (i = 0; i < USB_MAX_DEVICE; i++) {
    		dev = usb_get_dev_index(i);
    		if (dev == NULL)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			return 0;
    
    		if (dev->devnum == usb_dev_desc[device].target)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			break;
    	}
    
    	usb_disable_asynch(1); /* asynch transfer not allowed */
    
    	srb->lun = usb_dev_desc[device].lun;
    	buf_addr = (unsigned long)buffer;
    	start = blknr;
    	blks = blkcnt;
    	if (usb_test_unit_ready(srb, (struct us_data *)dev->privptr)) {
    		printf("Device NOT ready\n   Request Sense returned %02X %02X"
    		       " %02X\n", srb->sense_buf[2], srb->sense_buf[12],
    		       srb->sense_buf[13]);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return 0;
    	}
    
    
    	USB_STOR_PRINTF("\nusb_read: dev %d startblk %lx, blccnt %lx"
    			" buffer %lx\n", device, start, blks, buf_addr);
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	do {
    
    		/* XXX need some comment here */
    		retry = 2;
    		srb->pdata = (unsigned char *)buf_addr;
    		if (blks > USB_MAX_READ_BLK)
    			smallblks = USB_MAX_READ_BLK;
    		else
    			smallblks = (unsigned short) blks;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    retry_it:
    
    		if (smallblks == USB_MAX_READ_BLK)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			usb_show_progress();
    
    		srb->datalen = usb_dev_desc[device].blksz * smallblks;
    		srb->pdata = (unsigned char *)buf_addr;
    		if (usb_read_10(srb, (struct us_data *)dev->privptr, start,
    		    smallblks)) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			USB_STOR_PRINTF("Read ERROR\n");
    
    			usb_request_sense(srb, (struct us_data *)dev->privptr);
    			if (retry--)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				goto retry_it;
    
    			blkcnt -= blks;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			break;
    		}
    
    		start += smallblks;
    		blks -= smallblks;
    		buf_addr += srb->datalen;
    	} while (blks != 0);
    
    	USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\n",
    			start, smallblks, buf_addr);
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	usb_disable_asynch(0); /* asynch transfer allowed */
    
    	if (blkcnt >= USB_MAX_READ_BLK)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		printf("\n");
    
    	return blkcnt;
    
    #define USB_MAX_WRITE_BLK 20
    
    unsigned long usb_stor_write(int device, unsigned long blknr,
    				unsigned long blkcnt, const void *buffer)
    {
    	unsigned long start, blks, buf_addr;
    	unsigned short smallblks;
    	struct usb_device *dev;
    	int retry, i;
    	ccb *srb = &usb_ccb;
    
    	if (blkcnt == 0)
    		return 0;
    
    	device &= 0xff;
    	/* Setup  device */
    	USB_STOR_PRINTF("\nusb_write: dev %d \n", device);
    	dev = NULL;
    	for (i = 0; i < USB_MAX_DEVICE; i++) {
    		dev = usb_get_dev_index(i);
    		if (dev == NULL)
    			return 0;
    		if (dev->devnum == usb_dev_desc[device].target)
    			break;
    	}
    
    	usb_disable_asynch(1); /* asynch transfer not allowed */
    
    	srb->lun = usb_dev_desc[device].lun;
    	buf_addr = (unsigned long)buffer;
    	start = blknr;
    	blks = blkcnt;
    	if (usb_test_unit_ready(srb, (struct us_data *)dev->privptr)) {
    		printf("Device NOT ready\n   Request Sense returned %02X %02X"
    		       " %02X\n", srb->sense_buf[2], srb->sense_buf[12],
    			srb->sense_buf[13]);
    		return 0;
    	}
    
    	USB_STOR_PRINTF("\nusb_write: dev %d startblk %lx, blccnt %lx"
    			" buffer %lx\n", device, start, blks, buf_addr);
    
    	do {
    		/* If write fails retry for max retry count else
    		 * return with number of blocks written successfully.
    		 */
    		retry = 2;
    		srb->pdata = (unsigned char *)buf_addr;
    		if (blks > USB_MAX_WRITE_BLK)
    			smallblks = USB_MAX_WRITE_BLK;
    		else
    			smallblks = (unsigned short) blks;
    retry_it:
    		if (smallblks == USB_MAX_WRITE_BLK)
    			usb_show_progress();
    		srb->datalen = usb_dev_desc[device].blksz * smallblks;
    		srb->pdata = (unsigned char *)buf_addr;
    		if (usb_write_10(srb, (struct us_data *)dev->privptr, start,
    		    smallblks)) {
    			USB_STOR_PRINTF("Write ERROR\n");
    			usb_request_sense(srb, (struct us_data *)dev->privptr);
    			if (retry--)
    				goto retry_it;
    			blkcnt -= blks;
    			break;
    		}
    		start += smallblks;
    		blks -= smallblks;
    		buf_addr += srb->datalen;
    	} while (blks != 0);
    
    	USB_STOR_PRINTF("usb_write: end startblk %lx, blccnt %x buffer %lx\n",
    			start, smallblks, buf_addr);
    
    	usb_disable_asynch(0); /* asynch transfer allowed */
    	if (blkcnt >= USB_MAX_WRITE_BLK)
    		printf("\n");
    	return blkcnt;
    
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    /* Probe to see if a new device is actually a Storage device */
    
    int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,
    		      struct us_data *ss)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	struct usb_interface *iface;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int i;
    	unsigned int flags = 0;
    
    	int protocol = 0;
    	int subclass = 0;
    
    	/* let's examine the device now */
    	iface = &dev->config.if_desc[ifnum];
    
    #if 0
    	/* this is the place to patch some storage devices */
    
    	USB_STOR_PRINTF("iVendor %X iProduct %X\n", dev->descriptor.idVendor,
    			dev->descriptor.idProduct);
    
    	if ((dev->descriptor.idVendor) == 0x066b &&
    	    (dev->descriptor.idProduct) == 0x0103) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		USB_STOR_PRINTF("patched for E-USB\n");
    		protocol = US_PR_CB;
    		subclass = US_SC_UFI;	    /* an assumption */
    	}
    #endif
    
    	if (dev->descriptor.bDeviceClass != 0 ||
    
    			iface->desc.bInterfaceClass != USB_CLASS_MASS_STORAGE ||
    			iface->desc.bInterfaceSubClass < US_SC_MIN ||
    			iface->desc.bInterfaceSubClass > US_SC_MAX) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* if it's not a mass storage, we go no further */
    		return 0;
    	}
    
    
    	memset(ss, 0, sizeof(struct us_data));
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* At this point, we know we've got a live one */
    	USB_STOR_PRINTF("\n\nUSB Mass Storage device detected\n");
    
    	/* Initialize the us_data structure with some useful info */
    	ss->flags = flags;
    	ss->ifnum = ifnum;
    	ss->pusb_dev = dev;
    	ss->attention_done = 0;
    
    	/* If the device has subclass and protocol, then use that.  Otherwise,
    	 * take data from the specific interface.
    	 */
    	if (subclass) {
    		ss->subclass = subclass;
    		ss->protocol = protocol;
    	} else {
    
    		ss->subclass = iface->desc.bInterfaceSubClass;
    		ss->protocol = iface->desc.bInterfaceProtocol;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	/* set the handler pointers based on the protocol */
    	USB_STOR_PRINTF("Transport: ");
    	switch (ss->protocol) {
    	case US_PR_CB:
    		USB_STOR_PRINTF("Control/Bulk\n");
    		ss->transport = usb_stor_CB_transport;
    		ss->transport_reset = usb_stor_CB_reset;
    		break;
    
    	case US_PR_CBI:
    		USB_STOR_PRINTF("Control/Bulk/Interrupt\n");
    		ss->transport = usb_stor_CB_transport;
    		ss->transport_reset = usb_stor_CB_reset;
    		break;
    
    	case US_PR_BULK:
    		USB_STOR_PRINTF("Bulk/Bulk/Bulk\n");
    		ss->transport = usb_stor_BBB_transport;
    		ss->transport_reset = usb_stor_BBB_reset;
    		break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	default:
    
    		printf("USB Storage Transport unknown / not yet implemented\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return 0;
    		break;
    	}
    
    	/*
    	 * We are expecting a minimum of 2 endpoints - in and out (bulk).
    	 * An optional interrupt is OK (necessary for CBI protocol).
    	 * We will ignore any others.
    	 */
    
    	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* is it an BULK endpoint? */
    
    		if ((iface->ep_desc[i].bmAttributes &
    		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
    				ss->ep_in = iface->ep_desc[i].bEndpointAddress &
    					USB_ENDPOINT_NUMBER_MASK;
    			else
    
    				ss->ep_out =
    					iface->ep_desc[i].bEndpointAddress &
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					USB_ENDPOINT_NUMBER_MASK;
    		}
    
    		/* is it an interrupt endpoint? */
    
    		if ((iface->ep_desc[i].bmAttributes &
    		    USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			ss->ep_int = iface->ep_desc[i].bEndpointAddress &
    				USB_ENDPOINT_NUMBER_MASK;
    			ss->irqinterval = iface->ep_desc[i].bInterval;
    		}
    	}
    	USB_STOR_PRINTF("Endpoints In %d Out %d Int %d\n",
    		  ss->ep_in, ss->ep_out, ss->ep_int);
    
    	/* Do some basic sanity checks, and bail if we find a problem */
    
    	if (usb_set_interface(dev, iface->desc.bInterfaceNumber, 0) ||
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	    !ss->ep_in || !ss->ep_out ||
    	    (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
    		USB_STOR_PRINTF("Problems with device\n");
    		return 0;
    	}
    	/* set class specific stuff */
    
    	/* We only handle certain protocols.  Currently, these are
    	 * the only ones.
    
    	 * The SFF8070 accepts the requests used in u-boot
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	 */
    
    	if (ss->subclass != US_SC_UFI && ss->subclass != US_SC_SCSI &&
    	    ss->subclass != US_SC_8070) {
    
    		printf("Sorry, protocol %d not yet supported.\n", ss->subclass);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return 0;
    	}
    
    	if (ss->ep_int) {
    		/* we had found an interrupt endpoint, prepare irq pipe
    		 * set up the IRQ pipe and handler
    		 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255;
    		ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
    		ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe);
    
    		dev->irq_handle = usb_stor_irq;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	dev->privptr = (void *)ss;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return 1;
    }
    
    
    int usb_stor_get_info(struct usb_device *dev, struct us_data *ss,
    		      block_dev_desc_t *dev_desc)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	unsigned char perq, modi;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	unsigned long cap[2];
    
    	unsigned long *capacity, *blksz;
    
    	ccb *pccb = &usb_ccb;
    
    	/* for some reasons a couple of devices would not survive this reset */
    	if (
    	    /* Sony USM256E */
    	    (dev->descriptor.idVendor == 0x054c &&
    	     dev->descriptor.idProduct == 0x019e)
    	    ||
    	    /* USB007 Mini-USB2 Flash Drive */
    	    (dev->descriptor.idVendor == 0x066f &&
    	     dev->descriptor.idProduct == 0x2010)
    
    	    ||
    	    /* SanDisk Corporation Cruzer Micro 20044318410546613953 */
    	    (dev->descriptor.idVendor == 0x0781 &&
    	     dev->descriptor.idProduct == 0x5151)
    
    	    ||
    	    /*
    	     * SanDisk Corporation U3 Cruzer Micro 1/4GB
    	     * Flash Drive 000016244373FFB4
    	     */
    	    (dev->descriptor.idVendor == 0x0781 &&
    	     dev->descriptor.idProduct == 0x5406)
    
    	    )
    		USB_STOR_PRINTF("usb_stor_get_info: skipping RESET..\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	else
    
    		ss->transport_reset(ss);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	pccb->pdata = usb_stor_buf;
    
    	dev_desc->target = dev->devnum;
    	pccb->lun = dev_desc->lun;
    
    	USB_STOR_PRINTF(" address %d\n", dev_desc->target);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if (usb_inquiry(pccb, ss))
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return -1;
    
    	perq = usb_stor_buf[0];
    	modi = usb_stor_buf[1];
    
    
    	if ((perq & 0x1f) == 0x1f) {
    		/* skip unknown devices */
    		return 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	if ((modi&0x80) == 0x80) {
    		/* drive is removable */
    
    		dev_desc->removable = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    	memcpy(&dev_desc->vendor[0], &usb_stor_buf[8], 8);
    	memcpy(&dev_desc->product[0], &usb_stor_buf[16], 16);
    	memcpy(&dev_desc->revision[0], &usb_stor_buf[32], 4);
    
    	dev_desc->vendor[8] = 0;
    	dev_desc->product[16] = 0;
    	dev_desc->revision[4] = 0;
    
    #ifdef CONFIG_USB_BIN_FIXUP
    
    	usb_bin_fixup(dev->descriptor, (uchar *)dev_desc->vendor,
    		      (uchar *)dev_desc->product);
    
    #endif /* CONFIG_USB_BIN_FIXUP */
    
    	USB_STOR_PRINTF("ISO Vers %X, Response Data %X\n", usb_stor_buf[2],
    			usb_stor_buf[3]);
    	if (usb_test_unit_ready(pccb, ss)) {
    		printf("Device NOT ready\n"
    		       "   Request Sense returned %02X %02X %02X\n",
    		       pccb->sense_buf[2], pccb->sense_buf[12],
    		       pccb->sense_buf[13]);
    		if (dev_desc->removable == 1) {
    
    			dev_desc->type = perq;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			return 1;
    		}
    
    		return 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	pccb->pdata = (unsigned char *)&cap[0];
    
    	memset(pccb->pdata, 0, 8);
    	if (usb_read_capacity(pccb, ss) != 0) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		printf("READ_CAP ERROR\n");
    
    		cap[0] = 2880;
    		cap[1] = 0x200;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\n", cap[0],
    			cap[1]);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #if 0
    
    	if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */
    		cap[0] >>= 16;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    	cap[0] = cpu_to_be32(cap[0]);
    	cap[1] = cpu_to_be32(cap[1]);
    
    
    	/* this assumes bigendian! */
    
    	cap[0] += 1;
    	capacity = &cap[0];
    	blksz = &cap[1];
    
    	USB_STOR_PRINTF("Capacity = 0x%lx, blocksz = 0x%lx\n",
    			*capacity, *blksz);
    
    	dev_desc->lba = *capacity;
    	dev_desc->blksz = *blksz;
    	dev_desc->type = perq;
    
    	USB_STOR_PRINTF(" address %d\n", dev_desc->target);
    	USB_STOR_PRINTF("partype: %d\n", dev_desc->part_type);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	init_part(dev_desc);
    
    
    	USB_STOR_PRINTF("partype: %d\n", dev_desc->part_type);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return 1;
    }