/* USB main task. Performs enumeration/cleanup */ void USB::Task( void ) //USB state machine { byte i; byte rcode; static byte tmpaddr; byte tmpdata; static unsigned long delay = 0; USB_DEVICE_DESCRIPTOR buf; tmpdata = getVbusState(); /* modify USB task state if Vbus changed */ switch( tmpdata ) { case SE1: //illegal state usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; break; case SE0: //disconnected if(( usb_task_state & USB_STATE_MASK ) != USB_STATE_DETACHED ) { usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; } break; case FSHOST: //attached case LSHOST: if(( usb_task_state & USB_STATE_MASK ) == USB_STATE_DETACHED ) { delay = millis() + USB_SETTLE_DELAY; usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; } break; }// switch( tmpdata //Serial.print("USB task state: "); //Serial.println( usb_task_state, HEX ); switch( usb_task_state ) { case USB_DETACHED_SUBSTATE_INITIALIZE: init(); usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; break; case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here break; case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here break; case USB_ATTACHED_SUBSTATE_SETTLE: //setlle time for just attached device if( delay < millis() ) { usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; } break; case USB_ATTACHED_SUBSTATE_RESET_DEVICE: regWr( rHCTL, bmBUSRST ); //issue bus reset usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; break; case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: if(( regRd( rHCTL ) & bmBUSRST ) == 0 ) { tmpdata = regRd( rMODE ) | bmSOFKAENAB; //start SOF generation regWr( rMODE, tmpdata ); // regWr( rMODE, bmSOFKAENAB ); usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; delay = millis() + 20; //20ms wait after reset per USB spec } break; case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order if( regRd( rHIRQ ) & bmFRAMEIRQ ) { //when first SOF received we can continue if( delay < millis() ) { //20ms passed usb_task_state = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; } } break; case USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE: // toggle( BPNT_0 ); devtable[ 0 ].epinfo->MaxPktSize = 8; //set max.packet size to min.allowed rcode = getDevDescr( 0, 0, 8, ( char* )&buf ); if( rcode == 0 ) { devtable[ 0 ].epinfo->MaxPktSize = buf.bMaxPacketSize0; usb_task_state = USB_STATE_ADDRESSING; } else { usb_error = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; usb_task_state = USB_STATE_ERROR; } break; case USB_STATE_ADDRESSING: for( i = 1; i < USB_NUMDEVICES; i++ ) { if( devtable[ i ].epinfo == NULL ) { devtable[ i ].epinfo = devtable[ 0 ].epinfo; //set correct MaxPktSize //temporary record //until plugged with real device endpoint structure rcode = setAddr( 0, 0, i ); if( rcode == 0 ) { tmpaddr = i; usb_task_state = USB_STATE_CONFIGURING; } else { usb_error = USB_STATE_ADDRESSING; //set address error usb_task_state = USB_STATE_ERROR; } break; //break if address assigned or error occured during address assignment attempt } }//for( i = 1; i < USB_NUMDEVICES; i++ if( usb_task_state == USB_STATE_ADDRESSING ) { //no vacant place in devtable usb_error = 0xfe; usb_task_state = USB_STATE_ERROR; } break; case USB_STATE_CONFIGURING: break; case USB_STATE_RUNNING: break; case USB_STATE_ERROR: break; }// switch( usb_task_state }
/* * This is broken. We need to enumerate differently. * It causes major problems with several devices if detected in an unexpected order. * * * Oleg - I wouldn't do anything before the newly connected device is considered sane. * i.e.(delays are not indicated for brevity): * 1. reset * 2. GetDevDescr(); * 3a. If ACK, continue with allocating address, addressing, etc. * 3b. Else reset again, count resets, stop at some number (5?). * 4. When max.number of resets is reached, toggle power/fail * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD() * it doesn't need to be reset again * New steps proposal: * 1: get address pool instance. exit on fail * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail. * 3: bus reset, 100ms delay * 4: set address * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail * 6: while (configurations) { * for(each configuration) { * for (each driver) { * 6a: Ask device if it likes configuration. Returns 0 on OK. * If successful, the driver configured device. * The driver now owns the endpoints, and takes over managing them. * The following will need codes: * Everything went well, instance consumed, exit with success. * Instance already in use, ignore it, try next driver. * Not a supported device, ignore it, try next driver. * Not a supported configuration for this device, ignore it, try next driver. * Could not configure device, fatal, exit with fail. * } * } * } * 7: for(each driver) { * 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID * 8: if we get here, no driver likes the device plugged in, so exit failure. * */ uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) { //uint8_t bAddress = 0; //printf("Configuring: parent = %i, port = %i\r\n", parent, port); uint8_t devConfigIndex; uint8_t rcode = 0; uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf); UsbDevice *p = NULL; EpInfo *oldep_ptr = NULL; EpInfo epInfo; epInfo.epAddr = 0; epInfo.maxPktSize = 8; epInfo.epAttribs = 0; epInfo.bmNakPower = USB_NAK_MAX_POWER; //delay(2000); AddressPool &addrPool = GetAddressPool(); // Get pointer to pseudo device with address 0 assigned p = addrPool.GetUsbDevicePtr(0); if(!p) { //printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n"); return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; } // Save old pointer to EP_RECORD of address 0 oldep_ptr = p->epinfo; // Temporary assign new pointer to epInfo to p->epinfo in order to // avoid toggle inconsistence p->epinfo = &epInfo; p->lowspeed = lowspeed; // Get device descriptor rcode = getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Restore p->epinfo p->epinfo = oldep_ptr; if(rcode) { //printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n"); return rcode; } // to-do? // Allocate new address according to device class //bAddress = addrPool.AllocAddress(parent, false, port); //if (!bAddress) // return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; uint16_t vid = udd->idVendor; uint16_t pid = udd->idProduct; uint8_t klass = udd->bDeviceClass; // Attempt to configure if VID/PID or device class matches with a driver for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { if(!devConfig[devConfigIndex]) continue; // no driver if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed if(devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass)) { rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED) break; } } if(devConfigIndex < USB_NUMDEVICES) { return rcode; } // blindly attempt to configure for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { if(!devConfig[devConfigIndex]) continue; if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed if(devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass)) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); //printf("ERROR ENUMERATING %2.2x\r\n", rcode); if(!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) { // in case of an error dev_index should be reset to 0 // in order to start from the very beginning the // next time the program gets here //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) // devConfigIndex = 0; return rcode; } } // if we get here that means that the device class is not supported by any of registered classes rcode = DefaultAddressing(parent, port, lowspeed); return rcode; }
/* * This is broken. We need to enumerate differently. * It causes major problems with several devices if detected in an unexpected order. * * * Oleg - I wouldn't do anything before the newly connected device is considered sane. * i.e.(delays are not indicated for brevity): * 1. reset * 2. GetDevDescr(); * 3a. If ACK, continue with allocating address, addressing, etc. * 3b. Else reset again, count resets, stop at some number (5?). * 4. When max.number of resets is reached, toggle power/fail * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD() * it doesn't need to be reset again * New steps proposal: * 1: get address pool instance. exit on fail * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail. * 3: bus reset, 100ms delay * 4: set address * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail * 6: while (configurations) { * for(each configuration) { * for (each driver) { * 6a: Ask device if it likes configuration. Returns 0 on OK. * If successful, the driver configured device. * The driver now owns the endpoints, and takes over managing them. * The following will need codes: * Everything went well, instance consumed, exit with success. * Instance already in use, ignore it, try next driver. * Not a supported device, ignore it, try next driver. * Not a supported configuration for this device, ignore it, try next driver. * Could not configure device, fatal, exit with fail. * } * } * } * 7: for(each driver) { * 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID * 8: if we get here, no driver likes the device plugged in, so exit failure. * */ uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) { //uint8_t bAddress = 0; //printf("Configuring: parent = %i, port = %i\r\n", parent, port); uint8_t devConfigIndex; uint8_t rcode = 0; uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; UsbDevice *p = NULL; EpInfo *oldep_ptr = NULL; EpInfo epInfo; USB_OTG_CORE_HANDLE *pdev = coreConfig; epInfo.epAddr = 0; epInfo.maxPktSize = 8; epInfo.epAttribs = 0; epInfo.bmNakPower = USB_NAK_MAX_POWER; // assume: // HC0 for control - out // HC1 for control - in //uint8_t hcnum = USBH_GetFreeChannel(pdev); //if(hcnum > 1) { // USBH_Free_Channel(pdev, epInfo.hcNumOut); // USBH_Free_Channel(pdev, epInfo.hcNumIn); //} USBH_Free_Channel(pdev, 0); USBH_Free_Channel(pdev, 1); epInfo.hcNumOut = USBH_Alloc_Channel(pdev, 0x00); // ep_addr = 0 epInfo.hcNumIn = USBH_Alloc_Channel(pdev, 0x80); USBH_Open_Channel(pdev, epInfo.hcNumOut, 0x0, (lowspeed)?bmLOWSPEED:bmFULLSPEED, EP_TYPE_CTRL, 0x8); USBH_Open_Channel(pdev, epInfo.hcNumIn, 0x0, (lowspeed)?bmLOWSPEED:bmFULLSPEED, EP_TYPE_CTRL, 0x8); printf("\nControl Pipe: out = %d (0), in = %d (1)", epInfo.hcNumOut, epInfo.hcNumIn); delay_ms(1000); AddressPool &addrPool = GetAddressPool(); // Get pointer to pseudo device with address 0 assigned p = addrPool.GetUsbDevicePtr(0); if (!p) { printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n"); return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; } // Save old pointer to EP_RECORD of address 0 // oldep_ptr = p->epinfo; // Temporary assign new pointer to epInfo to p->epinfo in order to // avoid toggle inconsistence p->epinfo = &epInfo; p->lowspeed = lowspeed; // Get device descriptor rcode = getDevDescr(0, 0, 8, (uint8_t*)buf); // 8 should be enough, sizeof (USB_DEVICE_DESCRIPTOR) printf("\nControl - Got 1st 8 bytes desc"); // Extract Max Packet Size from the device descriptor epInfo.maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0; //USB::USBH_Modify_Channel (pdev, epInfo.hcNumOut, 0, 0, 0, 0, epInfo.maxPktSize); //USB::USBH_Modify_Channel (pdev, epInfo.hcNumIn, 0, 0, 0, 0, epInfo.maxPktSize); // Restore p->epinfo // keep CtrlXfer's hcNumOut/In in p->epinfo. p->epinfo = oldep_ptr; if (rcode) { printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n"); return rcode; } // to-do? // Allocate new address according to device class //bAddress = addrPool.AllocAddress(parent, false, port); //if (!bAddress) // return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; rcode = getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); printf("\nControl - Got 2nd 18 bytes desc."); uint16_t vid = (uint16_t)((USB_DEVICE_DESCRIPTOR*)buf)->idVendor; uint16_t pid = (uint16_t)((USB_DEVICE_DESCRIPTOR*)buf)->idProduct; uint8_t klass = ((USB_DEVICE_DESCRIPTOR*)buf)->bDeviceClass; // Attempt to configure if VID/PID or device class matches with a driver for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { if (!devConfig[devConfigIndex]) continue; // no driver if (devConfig[devConfigIndex]->GetAddress()) continue; // consumed if (devConfig[devConfigIndex]->VIDPIDOK(vid, pid)) { rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); break; } else if (devConfig[devConfigIndex]->DEVCLASSOK(klass)) { rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); if (!rcode) break; } } if (devConfigIndex < USB_NUMDEVICES) { return rcode; } // blindly attempt to configure for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { if (!devConfig[devConfigIndex]) continue; if (devConfig[devConfigIndex]->GetAddress()) continue; // consumed rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); //printf("ERROR ENUMERATING %2.2x\r\n", rcode); if (!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) { // in case of an error dev_index should be reset to 0 // in order to start from the very beginning the // next time the program gets here //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) // devConfigIndex = 0; return rcode; } } // if we get here that means that the device class is not supported by any of registered classes rcode = DefaultAddressing(parent, port, lowspeed); return rcode; }