Skip to content
Snippets Groups Projects
usb_storage.c 38.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • {
    	memset(&srb->cmd[0], 0, 12);
    	srb->cmd[0] = SCSI_WRITE10;
    
    	srb->cmd[1] = srb->lun << 5;
    
    	srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff;
    	srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff;
    	srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff;
    	srb->cmd[5] = ((unsigned char) (start)) & 0xff;
    	srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff;
    	srb->cmd[8] = (unsigned char) blocks & 0xff;
    	srb->cmdlen = 12;
    
    	debug("write10: start %lx blocks %x\n", start, blocks);
    
    	return ss->transport(srb, ss);
    }
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #ifdef CONFIG_USB_BIN_FIXUP
    /*
     * Some USB storage devices queried for SCSI identification data respond with
     * binary strings, which if output to the console freeze the terminal. The
     * workaround is to modify the vendor and product strings read from such
     * device with proper values (as reported by 'usb info').
     *
     * Vendor and product length limits are taken from the definition of
     * block_dev_desc_t in include/part.h.
     */
    static void usb_bin_fixup(struct usb_device_descriptor descriptor,
    				unsigned char vendor[],
    				unsigned char product[]) {
    	const unsigned char max_vendor_len = 40;
    	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);
    
    unsigned long usb_stor_read(int device, lbaint_t blknr,
    
    			    lbaint_t blkcnt, void *buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	lbaint_t start, blks;
    	uintptr_t 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 */
    
    	debug("\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;
    	}
    
    	ss = (struct us_data *)dev->privptr;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	usb_disable_asynch(1); /* asynch transfer not allowed */
    
    	srb->lun = usb_dev_desc[device].lun;
    	buf_addr = (unsigned long)buffer;
    	start = blknr;
    	blks = blkcnt;
    
    
    	debug("\nusb_read: dev %d startblk " LBAF ", blccnt " LBAF
    	      " 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_XFER_BLK)
    			smallblks = USB_MAX_XFER_BLK;
    
    		else
    			smallblks = (unsigned short) blks;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    retry_it:
    
    		if (smallblks == USB_MAX_XFER_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, ss, start, smallblks)) {
    
    			usb_request_sense(srb, ss);
    
    			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);
    
    	debug("usb_read: end startblk " LBAF
    	      ", 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_XFER_BLK)
    
    	return blkcnt;
    
    unsigned long usb_stor_write(int device, lbaint_t blknr,
    
    				lbaint_t blkcnt, const void *buffer)
    
    	lbaint_t start, blks;
    	uintptr_t 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 */
    
    	debug("\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;
    	}
    
    	ss = (struct us_data *)dev->privptr;
    
    
    	usb_disable_asynch(1); /* asynch transfer not allowed */
    
    	srb->lun = usb_dev_desc[device].lun;
    	buf_addr = (unsigned long)buffer;
    	start = blknr;
    	blks = blkcnt;
    
    
    	debug("\nusb_write: dev %d startblk " LBAF ", blccnt " LBAF
    	      " 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_XFER_BLK)
    			smallblks = USB_MAX_XFER_BLK;
    
    		else
    			smallblks = (unsigned short) blks;
    retry_it:
    
    		if (smallblks == USB_MAX_XFER_BLK)
    
    			usb_show_progress();
    		srb->datalen = usb_dev_desc[device].blksz * smallblks;
    		srb->pdata = (unsigned char *)buf_addr;
    
    		if (usb_write_10(srb, ss, start, smallblks)) {
    
    			debug("Write ERROR\n");
    
    			usb_request_sense(srb, ss);
    
    			if (retry--)
    				goto retry_it;
    			blkcnt -= blks;
    			break;
    		}
    		start += smallblks;
    		blks -= smallblks;
    		buf_addr += srb->datalen;
    	} while (blks != 0);
    
    	debug("usb_write: end startblk " LBAF ", blccnt %x buffer %lx\n",
    	      start, smallblks, buf_addr);
    
    
    	usb_disable_asynch(0); /* asynch transfer allowed */
    
    	if (blkcnt >= USB_MAX_XFER_BLK)
    
    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;
    
    	struct usb_endpoint_descriptor *ep_desc;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	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 */
    
    	debug("iVendor %X iProduct %X\n", dev->descriptor.idVendor,
    
    			dev->descriptor.idProduct);
    
    	if ((dev->descriptor.idVendor) == 0x066b &&
    	    (dev->descriptor.idProduct) == 0x0103) {
    
    		debug("patched for E-USB\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		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 */
    
    	debug("\n\nUSB Mass Storage device detected\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* 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 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	switch (ss->protocol) {
    	case US_PR_CB:
    
    		debug("Control/Bulk\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		ss->transport = usb_stor_CB_transport;
    		ss->transport_reset = usb_stor_CB_reset;
    		break;
    
    	case US_PR_CBI:
    
    		debug("Control/Bulk/Interrupt\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		ss->transport = usb_stor_CB_transport;
    		ss->transport_reset = usb_stor_CB_reset;
    		break;
    
    		debug("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++) {
    
    		ep_desc = &iface->ep_desc[i];
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* is it an BULK endpoint? */
    
    		if ((ep_desc->bmAttributes &
    
    		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
    
    			if (ep_desc->bEndpointAddress & USB_DIR_IN)
    				ss->ep_in = ep_desc->bEndpointAddress &
    						USB_ENDPOINT_NUMBER_MASK;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			else
    
    				ss->ep_out =
    
    					ep_desc->bEndpointAddress &
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					USB_ENDPOINT_NUMBER_MASK;
    		}
    
    		/* is it an interrupt endpoint? */
    
    		if ((ep_desc->bmAttributes &
    		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
    			ss->ep_int = ep_desc->bEndpointAddress &
    						USB_ENDPOINT_NUMBER_MASK;
    			ss->irqinterval = ep_desc->bInterval;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    	}
    
    	debug("Endpoints In %d Out %d Int %d\n",
    	      ss->ep_in, ss->ep_out, ss->ep_int);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* 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)) {
    
    		debug("Problems with device\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		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;
    
    	ALLOC_CACHE_ALIGN_BUFFER(unsigned long, cap, 2);
    	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, usb_stor_buf, 36);
    
    	unsigned long *capacity, *blksz;
    
    	ccb *pccb = &usb_ccb;
    
    	pccb->pdata = usb_stor_buf;
    
    	dev_desc->target = dev->devnum;
    	pccb->lun = dev_desc->lun;
    
    	debug(" 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], (const void *) &usb_stor_buf[8], 8);
    	memcpy(&dev_desc->product[0], (const void *) &usb_stor_buf[16], 16);
    	memcpy(&dev_desc->revision[0], (const void *) &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 */
    
    	debug("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
    	}
    
    	debug("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];
    
    	debug("Capacity = 0x%lx, blocksz = 0x%lx\n", *capacity, *blksz);
    
    	dev_desc->lba = *capacity;
    	dev_desc->blksz = *blksz;
    
    	dev_desc->log2blksz = LOG2(dev_desc->blksz);
    
    	dev_desc->type = perq;
    
    	debug(" address %d\n", dev_desc->target);
    	debug("partype: %d\n", dev_desc->part_type);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	init_part(dev_desc);
    
    
    	debug("partype: %d\n", dev_desc->part_type);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return 1;
    }