// // start // when this method is called, I have been selected as the driver for this device. // I can still return false to allow a different driver to load // bool local_IOath3kfrmwr::start(IOService *provider) { IOReturn err; const IOUSBConfigurationDescriptor *cd; // Do all the work here, on an IOKit matching thread. // 0.1 Get my USB Device DEBUG_LOG("%s(%p)::start!\n", getName(), this); pUsbDev = OSDynamicCast(IOUSBDevice, provider); if(!pUsbDev) { DEBUG_LOG("%s(%p)::start - Provider isn't a USB device!!!\n", getName(), this); return false; } // 0.2 Reset the device err = pUsbDev->ResetDevice(); if (err) { DEBUG_LOG("%s(%p)::start - failed to reset the device\n", getName(), this); pUsbDev->close(this); return false; } else IOLog("%s(%p)::start: device reset\n", getName(), this); // 0.3 Find the first config/interface int numconf = 0; if ((numconf = pUsbDev->GetNumConfigurations()) < 1) { DEBUG_LOG("%s(%p)::start - no composite configurations\n", getName(), this); return false; } else DEBUG_LOG("%s(%p)::start: num configurations %d\n", getName(), this, numconf); cd = pUsbDev->GetFullConfigurationDescriptor(0); // Set the configuration to the first config if (!cd) { DEBUG_LOG("%s(%p)::start - no config descriptor\n", getName(), this); return false; } // 1.0 Open the USB device if (!pUsbDev->open(this)) { DEBUG_LOG("%s(%p)::start - unable to open device for configuration\n", getName(), this); return false; } // 1.1 Set the configuration to the first config err = pUsbDev->SetConfiguration(this, cd->bConfigurationValue, true); if (err) { DEBUG_LOG("%s(%p)::start - unable to set the configuration\n", getName(), this); pUsbDev->close(this); return false; } DEBUG_LOG("%s(%p)::start - UPLOADED - %c\n", getName(), this,uploaded); //Uploading only one time if (!uploaded) { // 1.2 Get the status of the USB device (optional, for diag.) USBStatus status; err = pUsbDev->GetDeviceStatus(&status); if (err) { DEBUG_LOG("%s(%p)::start - unable to get device status\n", getName(), this); pUsbDev->close(this); return false; } else IOLog("%s(%p)::start: device status %d\n", getName(), this, (int)status); // 2.0 Find the interface for bulk endpoint transfers IOUSBFindInterfaceRequest request; request.bInterfaceClass = kIOUSBFindInterfaceDontCare; request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; request.bAlternateSetting = kIOUSBFindInterfaceDontCare; IOUSBInterface * intf = pUsbDev->FindNextInterface(NULL, &request); if (!intf) { DEBUG_LOG("%s(%p)::start - unable to find interface\n", getName(), this); pUsbDev->close(this); return false; } // 2.1 Open the interface if (!intf->open(this)) { DEBUG_LOG("%s(%p)::start - unable to open interface\n", getName(), this); pUsbDev->close(this); return false; } // 2.2 Get info on endpoints (optional, for diag.) int numep = intf->GetNumEndpoints(); DEBUG_LOG("%s(%p)::start: interface has %d endpoints\n", getName(), this, numep); UInt8 transferType = 0; UInt16 maxPacketSize = 0; UInt8 interval = 0; err = intf->GetEndpointProperties(0, 0x02, kUSBOut, &transferType, &maxPacketSize, &interval); if (err) { DEBUG_LOG("%s(%p)::start - failed to get endpoint 2 properties\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } else DEBUG_LOG("%s(%p)::start: EP2 %d %d %d\n", getName(), this, transferType, maxPacketSize, interval); err = intf->GetEndpointProperties(0, 0x01, kUSBIn, &transferType, &maxPacketSize, &interval); if (err) { DEBUG_LOG("%s(%p)::start - failed to get endpoint 1 properties\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } else {DEBUG_LOG("%s(%p)::start: EP1 %d %d %d\n", getName(), this, transferType, maxPacketSize, interval); } // 2.3 Get the pipe for bulk endpoint 2 Out IOUSBPipe * pipe = intf->GetPipeObj(0x02); if (!pipe) { DEBUG_LOG("%s(%p)::start - failed to find bulk out pipe\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } /* // TODO: Test the alternative way to do it: IOUSBFindEndpointRequest pipereq; pipereq.type = kUSBBulk; pipereq.direction = kUSBOut; pipereq.maxPacketSize = BULK_SIZE; pipereq.interval = 0; IOUSBPipe *pipe = intf->FindNextPipe(NULL, &pipereq); pipe = intf->FindNextPipe(pipe, &pipereq); if (!pipe) { DEBUG_LOG("%s(%p)::start - failed to find bulk out pipe 2\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } */ // 3.0 Send request to Control Endpoint to initiate the firmware transfer IOUSBDevRequest ctlreq; ctlreq.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice); ctlreq.bRequest = USB_REQ_DFU_DNLOAD; ctlreq.wValue = 0; ctlreq.wIndex = 0; ctlreq.wLength = 20; ctlreq.pData = firmware_buf; #if 0 // Trying to troubleshoot the problem after Restart (with OSBundleRequired Root) for (int irep = 0; irep < 5; irep++) { // retry on error err = pUsbDev->DeviceRequest(&ctlreq); // (synchronous, will block) if (err) DEBUG_LOG("%s(%p)::start - failed to initiate firmware transfer (%d), retrying (%d)\n", getName(), this, err, irep+1); else break; } #else err = pUsbDev->DeviceRequest(&ctlreq); // (synchronous, will block) #endif if (err) { DEBUG_LOG("%s(%p)::start - failed to initiate firmware transfer (%d)\n", getName(), this, err); intf->close(this); pUsbDev->close(this); return false; } // 3.1 Create IOMemoryDescriptor for bulk transfers char buftmp[BULK_SIZE]; IOMemoryDescriptor * membuf = IOMemoryDescriptor::withAddress(&buftmp, BULK_SIZE, kIODirectionNone); if (!membuf) { DEBUG_LOG("%s(%p)::start - failed to map memory descriptor\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } err = membuf->prepare(); if (err) { DEBUG_LOG("%s(%p)::start - failed to prepare memory descriptor\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } // 3.2 Send the rest of firmware to the bulk pipe char * buf = firmware_buf; int size = sizeof(firmware_buf); buf += 20; size -= 20; int ii = 1; while (size) { int to_send = size < BULK_SIZE ? size : BULK_SIZE; memcpy(buftmp, buf, to_send); err = pipe->Write(membuf, 10000, 10000, to_send); if (err) { DEBUG_LOG("%s(%p)::start - failed to write firmware to bulk pipe (%d)\n", getName(), this, ii); intf->close(this); pUsbDev->close(this); return false; } buf += to_send; size -= to_send; ii++; } IOLog("%s(%p)::start: firmware was sent to bulk pipe\n", getName(), this); err = membuf->complete(); if (err) { DEBUG_LOG("%s(%p)::start - failed to complete memory descriptor\n", getName(), this); intf->close(this); pUsbDev->close(this); uploaded = false; return false; } /* // TODO: Test the alternative way to do it: IOMemoryDescriptor * membuf = IOMemoryDescriptor::withAddress(&firmware_buf[20], 246804-20, kIODirectionNone); // sizeof(firmware_buf) if (!membuf) { DEBUG_LOG("%s(%p)::start - failed to map memory descriptor\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } err = membuf->prepare(); if (err) { DEBUG_LOG("%s(%p)::start - failed to prepare memory descriptor\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } //err = pipe->Write(membuf); err = pipe->Write(membuf, 10000, 10000, 246804-20, NULL); if (err) { DEBUG_LOG("%s(%p)::start - failed to write firmware to bulk pipe\n", getName(), this); intf->close(this); pUsbDev->close(this); return false; } DEBUG_LOG("%s(%p)::start: firmware was sent to bulk pipe\n", getName(), this); */ // 4.0 Get device status (it fails, but somehow is important for operational device) err = pUsbDev->GetDeviceStatus(&status); if (err) { DEBUG_LOG("%s(%p)::start - unable to get device status\n", getName(), this); intf->close(this); pUsbDev->close(this); DEBUG_LOG("%s(%p)::start - Firmware Uploaded State Changed to - %c\n", getName(), this,uploaded); // Set the Upload State uploaded = true; return false; } else {DEBUG_LOG("%s(%p)::start: device status %d\n", getName(), this, (int)status); } // Close the interface intf->close(this); DEBUG_LOG("%s(%p)::start - Firmware Uploaded State Changed to - %c\n", getName(), this,uploaded); // Set the Upload State uploaded = true; } else { IOLog("%s(%p)::Firmware already uploaded\n", getName(), this); } // Close the USB device pUsbDev->close(this); return false; // return false to allow a different driver to load }