I want to develop mass storage device using LPC2148.I am just referring code from Keil for USBMem. My problem is - I get Get_descriptor command from PC to which I respond with device descriptor. On receiving 8th byte it generates Reset. Then I get Set_address command. On response to it, I execute Set Address command in protocol engine. Then again I get Get_descriptor command (It should actually have index field of 18 but I receive with 64). Again after 8th byte it generates Reset and again gives Set_address command instead of Get_descriptor command for Configuration descriptor. What can be the problem?
I am just referring code from Keil for USBMem.
This one?
LPC2148 USB Mass Storage Device Example http://www.keil.com/download/docs/307.asp
> I get Get_descriptor command from PC to which I respond with device descriptor. On receiving 8th byte it generates Reset. Then I get Set_address command. On response to it, I execute Set Address command in protocol engine. Then again I get Get_descriptor command (It should actually have index field of 18 but I receive with 64).
Up to this point, it's a usual enumeration course of Windows, even though it sounds like somewhat weird. Just because Microsoft doesn't know USB well.
> Again after 8th byte it generates Reset and again gives Set_address command instead of Get_descriptor command for Configuration descriptor.
At this point, enumeration goes out of the rail.
Did you change USB_MAX_PACKET0 to 8? If so, recover it to 64.
usbcfg.h #define USB_MAX_PACKET0 64
Tsuneo
Yes. I am referring the same code. And also I have set Max packet size to 64. When the host sends Get_descriptor command for the second time, length field should be 18. But I am not getting that.
> What is meant by debug output?
A type of printf() debug Put debug string over UART. If you don't use it, it's OK.
Anyway, reverse the process order in Set_Address handler. What occurs?
I changed the sequence for setting address. But still getting Set Address command twice. When Get_descriptor command comes for 2nd time, length field should be only 18 or not?
> I changed the sequence for setting address. But still getting Set Address command twice.
Then, there are another bugs.
> When Get_descriptor command comes for 2nd time, length field should be only 18 or not?
See it using a sniffer. Windows don't always request the exact number.
KEIL original example is enumerated successfully. Compare sniffer log of yours with the original. And then, you'll find immediately the place where your code goes out of the rail.
At this stage of USB stack development, monitoring enumeration just on the MCU side isn't enough. Even when your code fails, USB engine doesn't report any error. Host puts bus reset on error, and enumeration starts over again. In this reason, monitoring bus is required.
Ok. I am trying to debug my program now. May be something is wrong with it. Will let u know as soon as I find it.
In LPC2148_USBMem example, instead of setting address of device on reception of Set_address command, they are setting it, on reception of IN packet. Is there any reason for that? I am setting it on reception of Set_address command only.
I'll show you the implementation details of Set_Address request handling, using two successful examples.
1) The first one is taken from KEIL implementation. KEIL follows traditional way, switch the device address after STATUS stage of Set_Address request - (USB_EndPoint0() - USB_EVT_SETUP - USB_REQUEST_SET_ADDRESS) At the SETUP stage handling, after parsing it as Set_Address request, - (USB_ReqSetAddress()) the device address is kept in a variable, USB_DeviceAddress. A flag on USB_DeviceAddress is enabled to show further process is required. - (USB_EndPoint0() - USB_EVT_IN) At the STATUS stage of Set_Address request, the firmware registers USB_DeviceAddress to the USB engine. SET_ADDRESS command is passed to the USB engine twice, to make it effective immediately (specific to NXP LPC family)
I suppose that KEIL has chosen this implementation for stack compatibility of other USB engine than NXP LPC family, like STM32, etc.
Excerpt from KEIL USB device code usbcore.c /* * USB Endpoint 0 Event Callback * Parameters: event * Return Value: none */ void USB_EndPoint0 (U32 event) { switch (event) { case USB_EVT_SETUP: // <----- SETUP transaction comes USB_SetupStage(); USB_DirCtrlEP(SetupPacket.bmRequestType.BM.Dir); EP0Data.Count = SetupPacket.wLength; /* Number of bytes to transfer */ switch (SetupPacket.bmRequestType.BM.Type) { case REQUEST_STANDARD: switch (SetupPacket.bRequest) { case USB_REQUEST_SET_ADDRESS: // <----- it is Set_Address request if (!USB_ReqSetAddress()) { // <----- pass it to Set_Address handler goto stall_i; } USB_StatusInStage(); break; ... ... case USB_EVT_IN : // <----- IN transaction just finishes if (SetupPacket.bmRequestType.BM.Dir == REQUEST_DEVICE_TO_HOST) { USB_DataInStage(); /* send data */ } else { // <----- this IN transaction is STATUS stage if (USB_DeviceAddress & 0x80) { // <----- process specific to STATUS stage of Set_Address USB_DeviceAddress &= 0x7F; // <----- recover original device address, masking flag bit USB_SetAddress(USB_DeviceAddress); // <----- set the address to the engine } } break; /* end case USB_EVT_IN */ ... ... /* * Set Address USB Request * Parameters: None (global SetupPacket) * Return Value: TRUE - Success, FALSE - Error */ __inline BOOL USB_ReqSetAddress (void) { switch (SetupPacket.bmRequestType.BM.Recipient) { case REQUEST_TO_DEVICE: USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L; // <----- the address is kept on a variable with flag break; default: return (__FALSE); } return (__TRUE); } usbhw.c /* * USB Set Address Function * Parameters: adr: USB Address * Return Value: None */ void USB_SetAddress (U32 adr) { // <----- set the address to the engine, twice WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Don't wait for next */ WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Setup Status Phase */ }
2) The second one comes from LPCUSB by Bertrik LPCUSB registers the device address directly to the engine at the SETUP stage of Set_Address handling before STATUS stage comes, like other request handling. This way is allowed for NXP LPC family, thanking to the feature specific to LPC USB engine. In this case, SET ADDRESS command to the engine is just once.
Excerpt from LPCUSB lpcusb.svn.sourceforge.net/.../usbstdreq.c /** Local function to handle a standard device request @param [in] pSetup The setup packet @param [in,out] *piLen Pointer to data length @param [in,out] ppbData Data buffer. @return TRUE if the request was handled successfully */ static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, U8 **ppbData) { U8 *pbData = *ppbData; switch (pSetup->bRequest) { case REQ_GET_STATUS: // bit 0: self-powered // bit 1: remote wakeup = not supported pbData[0] = 0; pbData[1] = 0; *piLen = 2; break; case REQ_SET_ADDRESS: // <------- Set_Address request handler USBHwSetAddress(pSetup->wValue); // <------- Set address to the engine before STATUS stage break; // <------- like handling of other requests case REQ_GET_DESCRIPTOR: DBG("D%x", pSetup->wValue); return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData); lpcusb.svn.sourceforge.net/.../usbhw_lpc.c /** Sets the USB address. @param [in] bAddr Device address to set */ void USBHwSetAddress(U8 bAddr) { USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr); // <------- set the address to the engine, once }
Both ways work. But which one is better for LPC family? I take LPCUSB way. The reason is two-fold, 1) It gives simpler code. 2) If we would take the traditional coding like KEIL, there is a chance that we miss the next request from host when the USB interrupt is disturbed by another higher-priority interrupts. The USB engine of NXP LPC family is designed to prevent this accident. There is no reason to stick to the traditional coding, wasting up NXP effort ;-)
Actually, this can be a silly question, but in Keil program, why they don't face problem of interrupt within interrupt? When initially I get Get_descriptor command, if i set TxPLen=18, then I don't get Reset generated by host after reception of 8th byte because when reset occurs I am in ISR only, busy writing 9th to 18th bytes. To avoid it when I get Get_descriptor command for 1st time, I set TxPLen=08 only. Can there be any better solution or is there any mistake from my side in understanding the program?
Actually, this can be a silly question, but in Keil program, why they don't face problem of interrupt within interrupt?
Because nested interrupts are disabled by default. This is not a silly question; it just shows that you need to read somewhat more. Try an ARM (7) architecture guide.
"Because nested interrupts are disabled by default."
They're not disabled; standard ARM(7) architecture doesn't have direct support per se. You have to insert code to provide the facility.
> When initially I get Get_descriptor command, if i set TxPLen=18, then I don't get Reset generated by host after reception of 8th byte because when reset occurs I am in ISR only, busy writing 9th to 18th bytes.
1) How many bytes does the host request in the wLength field of the SETUP data of the Get_Descriptor request ? 2) How many bytes do you return in the bMaxPacketSize0 field of the device descriptor ? Post your device descriptor.
> How many bytes does the host request in the wLength field of the SETUP data of the Get_Descriptor request? 0x40. > How many bytes do you return in the bMaxPacketSize0 field of the device descriptor ? 0x40. Here is my device descriptor -
const unsigned char USB_DeviceDescriptor[] = { 0x12, /* bLength */ 0x01, /* bDescriptorType */ 0x00,0x02, /* 2.0 */ /* bcdUSB */ 0x00, /* bDeviceClass */ 0x00, /* bDeviceSubClass */ 0x00, /* bDeviceProtocol */ 0x40, /* bMaxPacketSize0 */ 0x51,0xc2, /* idVendor */ 0x03,0x13, /* idProduct */ 0x00,0x02, /* 2.0 */ /* bcdDevice */ 0x00, /* iManufacturer */ 0x00, /* iProduct */ 0x00, /* iSerialNumber */ 0x01, /* bNumConfigurations */ };
Your descriptor is fine.
Did you set up this bMaxPacketSize0 (0x40) to the USB engine?
usbhw.c void USB_Reset (void) { EP_INDEX = 0; // <------ setup bMaxPacketSize0 to EP0 IN/OUT of USB engine MAXPACKET_SIZE = USB_MAX_PACKET0; EP_INDEX = 1; MAXPACKET_SIZE = USB_MAX_PACKET0; while ((DEV_INT_STAT & EP_RLZED_INT) == 0);
Maybe, you have trouble on the handling of USB protocol engine commands.
While your firmware is writing to the descriptor to the default IN EP, host never reads it, because the data is not validated yet. When your firmware puts Validate Buffer command, the data is passed to the endpoint. As your writing process to the endpoint is disturbed, you miss some sequence to the USB protocol engine.
I recommend you to examine the successful implementations, like KEIL and LPCUSB, for the usage details of protocol engine commands. Compare these routines with your code.
KEIL - usbhw.c: USB_Reset(), USB_ReadEP(), USB_WriteEP(), USB_ISR()
The USB engine (SIE) of LPC family is a little difficult for USB starters to write low-level USB stack. You have to synchronize to the USB protocol engine everywhere, not just fiddle the USB registers.
Isn't it better to start with easier USB engine? Like SiLabs C8051F32x/34x, Atmel AT90USB, ST Micro STM32, etc. But I don't recommend EZ-USB for USB study, because it's too easy :-)
Tsuneo,
Sorry....Out of topic:
You should write a book about USB specifically in MCUs.
I know there are some good books about General USB, like: "USB Complete". But I think that your knowledge and excellent communication skills could produce a really good book.
Didn't you think about it?
Tsuneo, I agree! Bradford
http://www.keil.com/forum/docs/thread16320.asp
I recommend you to examine the successful implementations. KEIL - usbhw.c: USB_Reset(), USB_ReadEP(), USB_WriteEP(), USB_ISR() Actually i have referred them and understood them. I checked my routines too. They are same only. That's why i am getting confused because I am not getting where the problem is. I will let you know as soon as I get anything.
For last two weeks, stuck at the same position. I think the problem occurs during handling Set_address command only. Tried by implementing all ways for setting address. But still trying to find the bug.
Is it your custom board, or one of dev board made by some manufacturer?
Run above KEIL example on your board first, to rule out any hardware problem of the board. KEIL example is written for their MCB2140 board. You may apply minor changes to the example code, because of the hardware difference.
MCB2140 schematic http://www.keil.com/mcb2140/mcb2140-schematics.pdf
The check points are,
1) Crystal frequency and PLL setting MCB2140 mounts 12MHz crystal. For other frequency, modify PLL setting. usbhw.c USB_Init() OTG_CLK_CTRL
2) CONNECT control and VBUS detection pins MCB2140 apply a fixed D+ pull-up resistor (R35, 1.5k) without CONNECT pin (P0.31) control. VBUS detection on P0.23 is optional (J15) If your board has these feature, modify PINSEL setting. usbhw.c USB_Init() PINSEL3/4
With these short modifications, KEIL example should work on your board. If it doesn't work, check your hardware.
Anyway, having a working example on your board will encourage you so much ;-) And then, it also works as a good reference for your original USB stack.
Got finally!!! Thanks for your constant support! By implementing Bettrick's method, I got the Get_descriptor command with length field 18. Challenge - Still not getting Get_descriptor for configuration descriptor. Can it be problem of Vendor id or product id? I am not using Keil board. It's board designed by local company.
Actually I am using LPC2148 CPU module designed by local company. Can I use vendor id and product id by Keil?
> Can I use vendor id and product id by Keil?
Unless the Vendor/Product ID (VID/PID) conflict with other devices connected to your PC, you can use any VID/PID on your PC for development. Unique official VID/PID is required when you release your product, so that it doesn't conflict with others on your customer's PC. I often use VID = 0xFFF0 - 0xFFFF for development. USB.org has not assigned these range of VID yet, then it should not conflict with existing USB devices.
This is given in USB in a nutshell. Normally when something is wrong with a descriptor or how it is being sent, the host will attempt to read it three times with long pauses in between requests. After the third attempt, the host gives up reporting an error with your device. I am getting Get_descriptor 3 times and then nothing happens. But if I can send 8 bytes successfully then I think 18 bytes also would have gone succesfully. And I have already shown my device descriptor before. And t seems to be fine. What else can be the problem?
Still not getting Get_descriptor command for configuration descriptor. But getting it for device descriptor three times. What can be the problem?
Most of points have been discussed already. I don't think of any more. If you would have a hardware analyzer, you could get more detailed sign to focus on the firmware bug...
Do you have any account on a server to share your code? Post your code to the sharing server so that we can look into it. If it can, I'll run your code on my LPC2148 board (IAR or Olimex), and monitor it with a hardware analyzer.
If it can, I'll run your code on my LPC2148 board (IAR or Olimex), and monitor it with a hardware analyzer.
you have a good soul!
I have sent my code on your yahoo account which I got from LPC2000 group. Problem still continues......
> I have sent my code on your yahoo account which I got from LPC2000 group.
I don't see the yahoo mail at all. The account is held just to post to LPC2000. Until you noticed to me on above post, I wasn't aware of your mail ;-) Fortunately, yahoo server still kept your mail. Now, your code is running on my Olimex LPC2148 board. The outline of the trace on a hardware analyzer is as follows
- Bus Reset - Get_Descriptor( Device ) --- incomplete - Bus Reset - Set_Address --- success - Get_Descriptor( Device ) --- incomplete - Get_Descriptor( Device ) --- incomplete - Get_Descriptor( Device ) --- incomplete
All of Get_Descriptor fails on its STATUS stage. Host puts ZLP (Zero-Length Packet) to the default OUT endpoint, but the device is NAKing until time out.
This means the default OUT endpoint is still occupied by the SETUP packet. The endpoint buffer is not cleared correctly, after read out on the SETUP handler.
In USB_ReadEP(), you try to send "Select Endpoint" and "Clear Buffer" command to the device protocol engine using wr_cmd_H(). But these commands don't take any data to write on the data phase. Just optional read. Then, I add new wr_cmd() and modified USB_ReadEP() as follows.
// <------- add a subroutine to write a command to the protocol engine without any data void wr_cmd( unsigned long send_cmd ) { USBDevIntClr=0x10; //clear cmd_code_empty USBCmdCode=send_cmd|usbcmd; //configure device while(!(USBDevIntSt & 0x10)); //wait cmd_code_empty USBDevIntClr=0x10; //clear cmd_code_empty interrupt bit } unsigned long USB_ReadEP (unsigned long EPNum, unsigned char *pData) { unsigned long cnt, n, phy_num; USBCtrl = 0x00000001|EPNum<<2; do { cnt = USBRxPLen; }while ((cnt & PKT_RDY) == 0); cnt &= PKT_LNGTH_MASK; for (n = 0; n < (cnt + 3) / 4; n++) { *((__packed unsigned long *)pData) = USBRxData; pData += 4; } USBCtrl = 0; phy_num = EPAdr(EPNum); // wr_cmd_H(phy_num,(phy_num|0x00000200)); // <------ these commands are wrong // wr_cmd_H(0xF2,0x00F20200); // <------ replaced with right ones wr_cmd( 0x00000500 | phy_num ); // select endpoint command wr_cmd( 0x00F20500 ); // clear buffer command return (cnt); }
Also, to unload ZLP of the STATUS stage from the default OUT endpoint, this temporary fix was added.
void USB_ISR (void) __irq { ... ... if (val & 0x04) //STP user manual page number 229 { USBDevIntClr = dev_stat; Ep0_setup(); continue; } //**************** modified from here else { // temporary fix for status stage USB_ReadEP(0x00, EP0Data.pData); // unload ZLP } //**************** modified to here ... ...
Now, the trace was improved one step further.
- Bus Reset - Get_Descriptor( Device ) --- success - Bus Reset - Set_Address --- success - Get_Descriptor( Device ) --- success - Get_Descriptor( Config ) --- incomplete
Get_Descriptor( Config ) is disturbed by "while(1);", inserted by you for debug ;-)
Not just USB_ReadEP(). You are using wr_cmd_H() wrongly everywhere on your code. Check them all.
Thank you very much! I made those changes in my code and it worked properly. You have helped us a lot in our project and we will require your valuable guidance in further development.
We successfully sent our configuration descriptor. Now when our board is connected, it shows mass storage device found. But still does not support that device. I am getting Set_cofiguration command but what am I supposed to do in its response? I am executing configure command in protocol engine in its response. As mine is mass storage device, can you please tell me what events are expected further? What is sequence of next requests from host?
> What is sequence of next requests from host?
For device firmware, you should not expect requests in sequence. Host puts any request as it likes. Device responds to it and does the requested job, if the device supports it. Otherwise, the device returns STALL.
> As mine is mass storage device, can you please tell me what events are expected further.
Ummm MSC (Mass Storage Class), It's really tough for starter, because you have to implement two layers of protocol. I recommend HID for the starting project, which is simpler.
But if you dare to try MSC, Read out the MSC specs I refer in this post. Also, Jan Axelson's book, USB Mass Storage ( http://www.lvr.com/usbms.htm ) is your friend. Trace a successful implementation, like LPCUSB lpcusb.svn.sourceforge.net/.../msc_bot.c lpcusb.svn.sourceforge.net/.../msc_scsi.c And then, attack to the implementation.
For MSC Bulk-Only Transport (BOT) implementation, Firstly, you have to implement the handlers for required class-specific request(s) at least, in addition to the standard device requests. Secondly, MSC-BOT carries SCSI command over bulk IN/OUT endpoints with command wrapper packets. You have to unwrap/wrap SCSI command and status, and transfer requested data or media sector (cluster) data Thirdly, SCSI command parser upon above MSC-BOT protocol, which handles the media actually.
1. Class specific request
these USB specs are the must.
Mass Storage Class Specification Overview 1.3 www.usb.org/.../usb-msc-overview-1.3b.pdf
Mass Storage Bulk Only 1.0 www.usb.org/.../usbmassbulk_10.pdf
In "3 Functional Characteristics (usbmassbulk_10.pdf p7)" section, you'll find these class-specific requests. - 3.1 Bulk-Only Mass Storage Reset (class-specific request) - 3.2 Get Max LUN (class-specific request)
"Bulk-Only Mass Storage Reset" is required, but "Get Max LUN" is optional for single-LUN device. If you don't implement "Get Max LUN", return STALL to this request.
Other than these class-specific requests, these standard requests are used for error recovery. - Get_Status( ENDPOINT ) (usb_20.pdf 9.4.5 Get Status) - Clear_Feater( ENDPOINT_HALT ) (usb_20.pdf 9.4.1 Clear Feature)
USB 2.0 spec www.usb.org/.../usb_20_122909-2.zip
2. Command/Data/Status Protocol over bulk IN/OUT endpoints
1) Command transport SCSI command comes to bulk OUT EP included in Command Block Wrapper (CBW) (usbmassbulk_10.pdf 5.1 Command Block Wrapper (CBW)) Check the integrity of the CBW following to "6.2 Valid and Meaningful CBW" section. If it isn't valid, return STALL to bulk IN EP, following "6.6.1 CBW Not Valid" For valid CBW, pass the SCSI command to the parser. Depending on the SCSI command, MSC-BOT handler moves to one of Data In/ Data Out / Status transport stage.
2) Data IN/Out transport Most of SCSI command exchange data on this stage. For example, Inquiry returns fixed inquiry response (a block of data) to bulk IN EP. Read(10)/Write(10) exchange block data (sector - 512 bytes or cluster - 4K bytes) over bulk IN/OUT EP. But some SCSI commands, like Test Unit Ready, don't have this stage.
3) Status transport All of SCSI commands conclude the session in this stage. Firmware passes back Status report (the result of the SCSI command), included in Command Status Wrapper (CSW), to the bulk IN EP.
3. SCSI command over above MSC protocol
The required SCSI commands are listed up in "4.2 Command Set Information" (PDT=00h) section of this spec.
MSC Compliance Test Specification www.usb.org/.../MSC-compliance-0_9a.pdf
required SCSI commands - Inquiry - spc3r23.pdf(p142) - Request Sense - spc3r23.pdf(p221) - Test Unit Ready - spc3r23.pdf(p232) - Read(10) - sbc2r16.pdf(p47) - Read Capacity(10) - sbc2r16.pdf(p54) - Write(10) - sbc2r16.pdf(p78)
Implement them all following the SCSI spec. As of these SCSI specs, the original t10.org has closed the links just for their members. Search "sbc2r16.pdf" on Goooogle, and you'll find them floating over the internet. This is just a brief outline of MSC-BOT and SCSI implementation. You'll learn a lot from a running example, the specs and the reference book.
Good luck.
Implementing class requests for mass storage class. Got GET MAX LUN request. Replied it with value zero. using bus hound software. so my device replies with 1 byte that is 0. but host does not respond. why is this so?
View all questions in Keil forum