USB Mass Storage Read10(SCSI)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
MrLolthe1st
Member
Member
Posts: 90
Joined: Sat Sep 24, 2016 12:06 am

USB Mass Storage Read10(SCSI)

Post by MrLolthe1st »

Hi everyone!
Today i'm tried to write up mass storage driver. I'm already have working UHCI driver and almost working EHCI(only at QEMU).
I'm write function to make endpoint calls:

Code: Select all

static void EhciDevIntr(UsbDevice *dev, UsbTransfer *t)
{
	EhciController *hc = (EhciController *)dev->hc;

	// Determine transfer properties
	uint speed = dev->speed;
	uint addr = dev->addr;
	uint maxSize = dev->maxPacketSize;
	uint endp = t->endp->desc->addr & 0xf;
	
	// Create queue of transfer descriptors
	EhciTD *td = EhciAllocTD(hc);
	if (!td)
	{
		t->success = false;
		t->complete = true;
		return;
	}

	EhciTD *head = td;
	EhciTD *prev = 0;

	// Data in/out packets
	uint toggle = t->endp->toggle;
	uint packetType = USB_PACKET_IN;
	if (t->endp->desc->addr&0x80!=0)
		packetType = USB_PACKET_OUT;
	kprintf("$#%x#$", packetType);
	uint packetSize = t->len;

	EhciInitTD(td, prev, toggle, packetType, packetSize, t->data);

	// Initialize queue head
	EhciQH *qh = EhciAllocQH(hc);
	EhciInitQH(qh, t, head, dev->parent, true, speed, addr, endp, maxSize);
	//printQh(qh);
	// Schedule queue
	EhciInsertPeriodicQH(hc->periodicQH, qh);
	EhciWaitForQH(hc, qh);
}
I'm have 100% working UsbDevRequest function to make devctrl requests(With it i'm reciving info, endpoints desc and etc).
So, firstly i'm reseting MassStorage and reciving lun count:

Code: Select all

	UsbDevRequest(dev, RT_HOST_TO_DEV | RT_CLASS | RT_INTF, 0xff, 0, dev->intfDesc->intfIndex, 0, 0);
	u8 lunCnt = 0;
	UsbDevRequest(dev, 0b10100001, 0xfe, 0, dev->intfDesc->intfIndex, 1, &lunCnt);
Firstly, i'm dissapointed with recieved lun count: 0.
Next i'm getting two endpoints to in and out packets.

Code: Select all

	UsbEndpoint * endpointIn = malloc(sizeof(UsbEndpoint));
	endpointIn->toggle = 1;
	if(dev->intfDesc->endpoints->addr & 0x80)
		endpointIn->desc = dev->intfDesc->endpoints;
	else
		endpointIn->desc = dev->intfDesc->endpoints->next;
	UsbEndpoint * endpointOut = malloc(sizeof(UsbEndpoint));
	endpointOut->toggle = 0;
	if (dev->intfDesc->endpoints->addr & 0x80)
		endpointOut->desc = dev->intfDesc->endpoints->next;
	else
		endpointOut->desc = dev->intfDesc->endpoints;
I'm creating my own UsbTransfer structure and filling up Read10 request, filling UsbTransfer structure to make Read10 request and making it:

Code: Select all

// Prepare transfer
	UsbTransfer *t = malloc(sizeof(UsbTransfer));
	
	Read10 ro;
	memset(&ro, 0, sizeof(Read10));
	ro.signature = 0x43425355;
	ro.tag =0x21;
	ro.dataLength = 0x200;
	ro.flags = 0x80;
	ro.lun = 0;
	ro.cmdLength = 10;
	ro.opcode = 0x28;
	ro.LBA = 0;
	ro.cnt =1<<8;
	t->endp = endpointOut;
	t->req = 0;
	t->data = &ro;
	t->len = 0x1F;
	t->complete = false;
	t->success = false;
	dev->hcIntr(dev, t);
hcIntr - points to EhciDevIntr.
Next i'm read data:

Code: Select all

	t->endp = endpointIn;
	t->req = 0;
	t->data = buf;
	t->len = 0x200;
	t->complete = false;
	t->success = false;
	dev->hcIntr(dev, t);
And nothing changed. What i'm doing wrong?
With best regards,
Aleksandr
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: USB Mass Storage Read10(SCSI)

Post by BenLunt »

The LUN count is zero based. A return value of zero is for one LUN. Read Section 3.2 of usbmassbulk_10.pdf.

Depending on the type of interface, BBB or UASP, you can have numerous endpoints in both directions.

BBB or Bulk/Bulk/Bulk, aka Bulk Only Protocol, should only have one of each, an in and an out. The interface sub-class will have a value of 0x06.

UASP (USB Attached SCSI), will have four pipes, having pipe descriptors for all four.

As for reading from the drive, there are numerous things you must do first. For example, most MSD devices will assume the first data transfer is the IDENTIFY command. You must do this first for most devices.
You then might need to check the LUN by doing a TEST UNIT READY command. Some devices want this command too.

You then need to get the status of the device, as well as get the capacity of the device. (Note, some devices return an "off by one" count of sectors).

Some devices, though just a few, will not even let you read a single sector until you do one or more of these others first.

Manufacturers, when creating a cheap device, will test only with Windows and if it works, they put a pass on it and run with it. Windows does a certain sequence of events, so if this sequence is not followed, the device may fail. Happens when money is involved.

My book explains all of this and how to read and write to a MSD (Mass Storage Device) either as a BBB or a UASP device.

Ben
- http://www.fysnet.net/the_universal_serial_bus.htm
Post Reply