USB host library, support isochronous,bulk,interrupt and control.

Dependents:   BaseUsbHost_example BaseJpegDecode_example SimpleJpegDecode_example

Import programBaseUsbHost_example

BaseUsbHost example program

Committer:
va009039
Date:
Tue Dec 04 13:29:41 2012 +0000
Revision:
0:b7d6879637a8
Child:
3:ae77d63a1eda
first commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
va009039 0:b7d6879637a8 1 // BaseUsbHost.cpp 2012/12/4
va009039 0:b7d6879637a8 2 #include "mbed.h"
va009039 0:b7d6879637a8 3 #include "rtos.h"
va009039 0:b7d6879637a8 4 #include "BaseUsbHost.h"
va009039 0:b7d6879637a8 5 #define DEBUG
va009039 0:b7d6879637a8 6 #include "BaseUsbHostDebug.h"
va009039 0:b7d6879637a8 7 #define TEST
va009039 0:b7d6879637a8 8 #include "BaseUsbHostTest.h"
va009039 0:b7d6879637a8 9
va009039 0:b7d6879637a8 10 // bits of the USB/OTG clock control register
va009039 0:b7d6879637a8 11 #define HOST_CLK_EN (1<<0)
va009039 0:b7d6879637a8 12 #define DEV_CLK_EN (1<<1)
va009039 0:b7d6879637a8 13 #define PORTSEL_CLK_EN (1<<3)
va009039 0:b7d6879637a8 14 #define AHB_CLK_EN (1<<4)
va009039 0:b7d6879637a8 15
va009039 0:b7d6879637a8 16 // bits of the USB/OTG clock status register
va009039 0:b7d6879637a8 17 #define HOST_CLK_ON (1<<0)
va009039 0:b7d6879637a8 18 #define DEV_CLK_ON (1<<1)
va009039 0:b7d6879637a8 19 #define PORTSEL_CLK_ON (1<<3)
va009039 0:b7d6879637a8 20 #define AHB_CLK_ON (1<<4)
va009039 0:b7d6879637a8 21
va009039 0:b7d6879637a8 22 // we need host clock, OTG/portsel clock and AHB clock
va009039 0:b7d6879637a8 23 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
va009039 0:b7d6879637a8 24
va009039 0:b7d6879637a8 25 #define FI 0x2EDF /* 12000 bits per frame (-1) */
va009039 0:b7d6879637a8 26 #define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI)
va009039 0:b7d6879637a8 27
va009039 0:b7d6879637a8 28 static BaseUsbHost* pHost = NULL;
va009039 0:b7d6879637a8 29
va009039 0:b7d6879637a8 30 extern "C" void USB_IRQHandler(void) __irq;
va009039 0:b7d6879637a8 31 void USB_IRQHandler(void) __irq
va009039 0:b7d6879637a8 32 {
va009039 0:b7d6879637a8 33 if (pHost) {
va009039 0:b7d6879637a8 34 pHost->irqHandler();
va009039 0:b7d6879637a8 35 }
va009039 0:b7d6879637a8 36 }
va009039 0:b7d6879637a8 37
va009039 0:b7d6879637a8 38 inline static void* malloc_align(size_t size, size_t alignment)
va009039 0:b7d6879637a8 39 {
va009039 0:b7d6879637a8 40 void* p;
va009039 0:b7d6879637a8 41 int r = posix_memalign(&p, alignment, size);
va009039 0:b7d6879637a8 42 if (r == 0) {
va009039 0:b7d6879637a8 43 return p;
va009039 0:b7d6879637a8 44 }
va009039 0:b7d6879637a8 45 return NULL;
va009039 0:b7d6879637a8 46 }
va009039 0:b7d6879637a8 47
va009039 0:b7d6879637a8 48 BaseUsbHost::BaseUsbHost()
va009039 0:b7d6879637a8 49 {
va009039 0:b7d6879637a8 50 if (pHost) {
va009039 0:b7d6879637a8 51 TEST_ASSERT(pHost == NULL);
va009039 0:b7d6879637a8 52 return;
va009039 0:b7d6879637a8 53 }
va009039 0:b7d6879637a8 54 pHost = this;
va009039 0:b7d6879637a8 55 NVIC_DisableIRQ(USB_IRQn);
va009039 0:b7d6879637a8 56 m_pHcca = reinterpret_cast<HCCA*>(malloc_align(sizeof(HCCA), 256));
va009039 0:b7d6879637a8 57 TEST_ASSERT(m_pHcca);
va009039 0:b7d6879637a8 58 init_hw_ohci(m_pHcca);
va009039 0:b7d6879637a8 59 ResetRootHub();
va009039 0:b7d6879637a8 60 NVIC_SetPriority(USB_IRQn, 0);
va009039 0:b7d6879637a8 61 NVIC_EnableIRQ(USB_IRQn);
va009039 0:b7d6879637a8 62 }
va009039 0:b7d6879637a8 63
va009039 0:b7d6879637a8 64 void BaseUsbHost::init_hw_ohci(HCCA* pHcca)
va009039 0:b7d6879637a8 65 {
va009039 0:b7d6879637a8 66 LPC_SC->PCONP &= ~(1UL<<31); //Cut power
va009039 0:b7d6879637a8 67 wait(1);
va009039 0:b7d6879637a8 68 LPC_SC->PCONP |= (1UL<<31); // turn on power for USB
va009039 0:b7d6879637a8 69 LPC_USB->USBClkCtrl |= CLOCK_MASK; // Enable USB host clock, port selection and AHB clock
va009039 0:b7d6879637a8 70 // Wait for clocks to become available
va009039 0:b7d6879637a8 71 while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
va009039 0:b7d6879637a8 72 ;
va009039 0:b7d6879637a8 73 LPC_USB->OTGStCtrl |= 1;
va009039 0:b7d6879637a8 74 LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
va009039 0:b7d6879637a8 75
va009039 0:b7d6879637a8 76 LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));
va009039 0:b7d6879637a8 77 LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // 0x14000000
va009039 0:b7d6879637a8 78
va009039 0:b7d6879637a8 79 DBG("initialize OHCI\n");
va009039 0:b7d6879637a8 80 wait_ms(100); /* Wait 50 ms before apply reset */
va009039 0:b7d6879637a8 81 TEST_ASSERT((LPC_USB->HcRevision&0xff) == 0x10); // check revision
va009039 0:b7d6879637a8 82 LPC_USB->HcControl = 0; /* HARDWARE RESET */
va009039 0:b7d6879637a8 83 LPC_USB->HcControlHeadED = 0; /* Initialize Control list head to Zero */
va009039 0:b7d6879637a8 84 LPC_USB->HcBulkHeadED = 0; /* Initialize Bulk list head to Zero */
va009039 0:b7d6879637a8 85 /* SOFTWARE RESET */
va009039 0:b7d6879637a8 86 LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
va009039 0:b7d6879637a8 87 LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; /* Write Fm Interval and Largest Data Packet Counter */
va009039 0:b7d6879637a8 88 LPC_USB->HcPeriodicStart = FI*90/100;
va009039 0:b7d6879637a8 89 /* Put HC in operational state */
va009039 0:b7d6879637a8 90 LPC_USB->HcControl = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
va009039 0:b7d6879637a8 91 LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC; /* Set Global Power */
va009039 0:b7d6879637a8 92 TEST_ASSERT(pHcca);
va009039 0:b7d6879637a8 93 for (int i = 0; i < 32; i++) {
va009039 0:b7d6879637a8 94 pHcca->InterruptTable[i] = NULL;
va009039 0:b7d6879637a8 95 }
va009039 0:b7d6879637a8 96 LPC_USB->HcHCCA = (uint32_t)pHcca;
va009039 0:b7d6879637a8 97 LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; /* Clear Interrrupt Status */
va009039 0:b7d6879637a8 98
va009039 0:b7d6879637a8 99 //LPC_USB->HcInterruptEnable = MIE|WDH|RHSC|FNO;
va009039 0:b7d6879637a8 100 LPC_USB->HcInterruptEnable = OR_INTR_ENABLE_MIE|OR_INTR_ENABLE_WDH|OR_INTR_ENABLE_FNO;
va009039 0:b7d6879637a8 101
va009039 0:b7d6879637a8 102 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
va009039 0:b7d6879637a8 103 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
va009039 0:b7d6879637a8 104 }
va009039 0:b7d6879637a8 105
va009039 0:b7d6879637a8 106 void BaseUsbHost::ResetRootHub()
va009039 0:b7d6879637a8 107 {
va009039 0:b7d6879637a8 108 wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
va009039 0:b7d6879637a8 109 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
va009039 0:b7d6879637a8 110 DBG("Before loop\n");
va009039 0:b7d6879637a8 111 while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
va009039 0:b7d6879637a8 112 ;
va009039 0:b7d6879637a8 113 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
va009039 0:b7d6879637a8 114 DBG("After loop\n");
va009039 0:b7d6879637a8 115 wait_ms(200); /* Wait for 100 MS after port reset */
va009039 0:b7d6879637a8 116 }
va009039 0:b7d6879637a8 117
va009039 0:b7d6879637a8 118 HCTD* td_reverse(HCTD* td)
va009039 0:b7d6879637a8 119 {
va009039 0:b7d6879637a8 120 HCTD* result = NULL;
va009039 0:b7d6879637a8 121 HCTD* next;
va009039 0:b7d6879637a8 122 while(td) {
va009039 0:b7d6879637a8 123 next = reinterpret_cast<HCTD*>(td->Next);
va009039 0:b7d6879637a8 124 td->Next = reinterpret_cast<uint32_t>(result);
va009039 0:b7d6879637a8 125 result = td;
va009039 0:b7d6879637a8 126 td = next;
va009039 0:b7d6879637a8 127 }
va009039 0:b7d6879637a8 128 return result;
va009039 0:b7d6879637a8 129 }
va009039 0:b7d6879637a8 130
va009039 0:b7d6879637a8 131 void BaseUsbHost::irqHandler()
va009039 0:b7d6879637a8 132 {
va009039 0:b7d6879637a8 133 if (!(LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable)) {
va009039 0:b7d6879637a8 134 return;
va009039 0:b7d6879637a8 135 }
va009039 0:b7d6879637a8 136 m_report_irq++;
va009039 0:b7d6879637a8 137 uint32_t status = LPC_USB->HcInterruptStatus;
va009039 0:b7d6879637a8 138 if (status & OR_INTR_STATUS_FNO) {
va009039 0:b7d6879637a8 139 m_report_FNO++;
va009039 0:b7d6879637a8 140 }
va009039 0:b7d6879637a8 141 if (status & OR_INTR_STATUS_WDH) {
va009039 0:b7d6879637a8 142 uint32_t done_td = m_pHcca->DoneHead;
va009039 0:b7d6879637a8 143 TEST_ASSERT(done_td);
va009039 0:b7d6879637a8 144 m_pHcca->DoneHead = NULL; // reset
va009039 0:b7d6879637a8 145 if (done_td & 1) { // error ?
va009039 0:b7d6879637a8 146 done_td &= ~1;
va009039 0:b7d6879637a8 147 }
va009039 0:b7d6879637a8 148 HCTD* td = td_reverse(reinterpret_cast<HCTD*>(done_td));
va009039 0:b7d6879637a8 149 while(td) {
va009039 0:b7d6879637a8 150 BaseEp* ep = reinterpret_cast<BaseEp*>(td->ep);
va009039 0:b7d6879637a8 151 TEST_ASSERT(ep);
va009039 0:b7d6879637a8 152 ep->irqWdhHandler(td);
va009039 0:b7d6879637a8 153 td = reinterpret_cast<HCTD*>(td->Next);
va009039 0:b7d6879637a8 154 }
va009039 0:b7d6879637a8 155 m_report_WDH++;
va009039 0:b7d6879637a8 156 }
va009039 0:b7d6879637a8 157 LPC_USB->HcInterruptStatus = status; // Clear Interrrupt Status
va009039 0:b7d6879637a8 158 }
va009039 0:b7d6879637a8 159
va009039 0:b7d6879637a8 160 BaseEp::BaseEp(int addr, uint8_t ep, uint16_t size, int lowSpeed):m_td_queue_count(0)
va009039 0:b7d6879637a8 161 {
va009039 0:b7d6879637a8 162 DBG("%p FA=%d EN=%02x MPS=%d S=%d\n", this, addr, ep, size, lowSpeed);
va009039 0:b7d6879637a8 163 m_pED = reinterpret_cast<HCED*>(malloc_align(sizeof(HCED), 16));
va009039 0:b7d6879637a8 164 TEST_ASSERT(m_pED);
va009039 0:b7d6879637a8 165 TEST_ASSERT(size >= 8 && size <= 1023);
va009039 0:b7d6879637a8 166 TEST_ASSERT(lowSpeed == 0 || lowSpeed == 1);
va009039 0:b7d6879637a8 167 m_pED->Control = addr | /* USB address */
va009039 0:b7d6879637a8 168 ((ep & 0x7F) << 7) | /* Endpoint address */
va009039 0:b7d6879637a8 169 (ep!=0?(((ep&0x80)?2:1) << 11):0)| /* direction : Out = 1, 2 = In */
va009039 0:b7d6879637a8 170 ((lowSpeed?1:0) << 13) | /* speed full=0 low=1 */
va009039 0:b7d6879637a8 171 (size << 16); /* MaxPkt Size */
va009039 0:b7d6879637a8 172 m_pED->Next = NULL;
va009039 0:b7d6879637a8 173 }
va009039 0:b7d6879637a8 174
va009039 0:b7d6879637a8 175 int BaseEp::GetAddr()
va009039 0:b7d6879637a8 176 {
va009039 0:b7d6879637a8 177 HCED* ed = m_pED;
va009039 0:b7d6879637a8 178 TEST_ASSERT(ed);
va009039 0:b7d6879637a8 179 if (ed) {
va009039 0:b7d6879637a8 180 return ed->Control & 0x7f;
va009039 0:b7d6879637a8 181 }
va009039 0:b7d6879637a8 182 return 0;
va009039 0:b7d6879637a8 183 }
va009039 0:b7d6879637a8 184
va009039 0:b7d6879637a8 185 int BaseEp::GetLowSpeed()
va009039 0:b7d6879637a8 186 {
va009039 0:b7d6879637a8 187 HCED* ed = m_pED;
va009039 0:b7d6879637a8 188 TEST_ASSERT(ed);
va009039 0:b7d6879637a8 189 if (ed) {
va009039 0:b7d6879637a8 190 if (ed->Control & (1<<13)) {
va009039 0:b7d6879637a8 191 return 1;
va009039 0:b7d6879637a8 192 }
va009039 0:b7d6879637a8 193 }
va009039 0:b7d6879637a8 194 return 0;
va009039 0:b7d6879637a8 195 }
va009039 0:b7d6879637a8 196
va009039 0:b7d6879637a8 197 void BaseEp::update_FunctionAddress(int addr)
va009039 0:b7d6879637a8 198 {
va009039 0:b7d6879637a8 199 TEST_ASSERT(addr >= 0 && addr <= 127);
va009039 0:b7d6879637a8 200 HCED* ed = m_pED;
va009039 0:b7d6879637a8 201 TEST_ASSERT(ed);
va009039 0:b7d6879637a8 202 ed->Control &= ~0x7f;
va009039 0:b7d6879637a8 203 ed->Control |= addr;
va009039 0:b7d6879637a8 204 }
va009039 0:b7d6879637a8 205
va009039 0:b7d6879637a8 206 void BaseEp::update_MaxPacketSize(uint16_t size)
va009039 0:b7d6879637a8 207 {
va009039 0:b7d6879637a8 208 TEST_ASSERT(size >= 8 && size <= 1023);
va009039 0:b7d6879637a8 209 HCED* ed = m_pED;
va009039 0:b7d6879637a8 210 TEST_ASSERT(ed);
va009039 0:b7d6879637a8 211 ed->Control &= ~0xffff0000;
va009039 0:b7d6879637a8 212 ed->Control |= size<<16;
va009039 0:b7d6879637a8 213 }
va009039 0:b7d6879637a8 214
va009039 0:b7d6879637a8 215 HCTD* BaseEp::new_HCTD(int buf_size)
va009039 0:b7d6879637a8 216 {
va009039 0:b7d6879637a8 217 HCTD* td;
va009039 0:b7d6879637a8 218 int r = posix_memalign(reinterpret_cast<void**>(&td), 16, sizeof(HCTD) + buf_size);
va009039 0:b7d6879637a8 219 if (r == 0) {
va009039 0:b7d6879637a8 220 td->Control = TD_CC|TD_ROUNDING;
va009039 0:b7d6879637a8 221 td->Next = NULL;
va009039 0:b7d6879637a8 222 td->ep = this;
va009039 0:b7d6879637a8 223 td->buf_top = NULL;
va009039 0:b7d6879637a8 224 td->buf_size = buf_size;
va009039 0:b7d6879637a8 225 if (buf_size == 0) {
va009039 0:b7d6879637a8 226 td->CurrBufPtr = NULL;
va009039 0:b7d6879637a8 227 td->BufEnd = NULL;
va009039 0:b7d6879637a8 228 } else {
va009039 0:b7d6879637a8 229 td->CurrBufPtr = td->buf;
va009039 0:b7d6879637a8 230 td->BufEnd = const_cast<uint8_t*>(td->buf)+buf_size-1;
va009039 0:b7d6879637a8 231 }
va009039 0:b7d6879637a8 232 return td;
va009039 0:b7d6879637a8 233 }
va009039 0:b7d6879637a8 234 return NULL;
va009039 0:b7d6879637a8 235 }
va009039 0:b7d6879637a8 236
va009039 0:b7d6879637a8 237 void BaseEp::delete_HCTD(HCTD* p)
va009039 0:b7d6879637a8 238 {
va009039 0:b7d6879637a8 239 free(p);
va009039 0:b7d6879637a8 240 }
va009039 0:b7d6879637a8 241
va009039 0:b7d6879637a8 242 HCTD* BaseEp::get_queue_HCTD(uint32_t millisec)
va009039 0:b7d6879637a8 243 {
va009039 0:b7d6879637a8 244 for(int i = 0; i < 16; i++) {
va009039 0:b7d6879637a8 245 osEvent evt = m_queue.get(millisec);
va009039 0:b7d6879637a8 246 if (evt.status == osEventMessage) {
va009039 0:b7d6879637a8 247 HCTD* td = reinterpret_cast<HCTD*>(evt.value.p);
va009039 0:b7d6879637a8 248 TEST_ASSERT(td);
va009039 0:b7d6879637a8 249 if (td->Control & TD_CC) {
va009039 0:b7d6879637a8 250 m_ConditionCode = td->Control>>28;
va009039 0:b7d6879637a8 251 DBG_TD(td);
va009039 0:b7d6879637a8 252 }
va009039 0:b7d6879637a8 253 return td;
va009039 0:b7d6879637a8 254 } else if (evt.status == osOK) {
va009039 0:b7d6879637a8 255 continue;
va009039 0:b7d6879637a8 256 } else if (evt.status == osEventTimeout) {
va009039 0:b7d6879637a8 257 return NULL;
va009039 0:b7d6879637a8 258 } else {
va009039 0:b7d6879637a8 259 DBG("evt.status: %02x\n", evt.status);
va009039 0:b7d6879637a8 260 TEST_ASSERT(evt.status == osEventMessage);
va009039 0:b7d6879637a8 261 }
va009039 0:b7d6879637a8 262 }
va009039 0:b7d6879637a8 263 return NULL;
va009039 0:b7d6879637a8 264 }
va009039 0:b7d6879637a8 265
va009039 0:b7d6879637a8 266 int BaseEp::wait_queue_HCTD(HCTD* wait_td, uint32_t millisec)
va009039 0:b7d6879637a8 267 {
va009039 0:b7d6879637a8 268 for(int i = 0; i < 16; i++) {
va009039 0:b7d6879637a8 269 HCTD* td = get_queue_HCTD(millisec);
va009039 0:b7d6879637a8 270 if (td) {
va009039 0:b7d6879637a8 271 if (td->Control & TD_CC) {
va009039 0:b7d6879637a8 272 DBG_TD(td);
va009039 0:b7d6879637a8 273 delete_HCTD(td);
va009039 0:b7d6879637a8 274 return USB_ERROR;
va009039 0:b7d6879637a8 275 }
va009039 0:b7d6879637a8 276 delete_HCTD(td);
va009039 0:b7d6879637a8 277 if (td == wait_td) {
va009039 0:b7d6879637a8 278 return USB_OK;
va009039 0:b7d6879637a8 279 }
va009039 0:b7d6879637a8 280 }
va009039 0:b7d6879637a8 281 }
va009039 0:b7d6879637a8 282 return USB_ERROR;
va009039 0:b7d6879637a8 283 }