Hi,
This is the code snippet in my driver for an usb uart device. I am trying to call standard cdc-acm command to set the Line Coding in the device, but fails with this error:
"USBSendSetLineCoding - Failed : 0xe0005000, bytes transferred: 0"
I guess the USB device is returning this error due to incorrect buffer or format. There is no proper documentation on how to use IOMemoryDescriptor when the data has to be passed down in a buffer to the usb stack. (IOUSBHostInterface->DeviceRequest())
Can anyone please point out what is wrong with this code and suggest a right method?
void MyDriver::USBSendSetLineCoding(uint32_t BaudRate, uint8_t StopBits, uint8_t TX_Parity, uint8_t CharLength)
{
kern_return_t ret = kIOReturnSuccess;
LineCoding *lineParms;
uint16_t lcLen = sizeof(LineCoding)-1;
lineParms = (LineCoding *)IOMalloc(lcLen);
if (!lineParms)
{
MyDebugLog("USBSendSetLineCoding - allocate lineParms failed");
return;
}
bzero(lineParms, lcLen);
lineParms->bCharFormat = StopBits - 2;
lineParms->bParityType = TX_Parity - 1;
lineParms->bDataBits = CharLength;
OSSwapBigToHostInt32(BaudRate);
lineParms->dwDTERate = BaudRate;
IOBufferMemoryDescriptor* bufferDescriptor = nullptr;
_controlInterface->CreateIOBuffer(kIOMemoryDirectionOut, lcLen, &bufferDescriptor);
IOMemoryMap *map = nullptr;
bufferDescriptor->CreateMapping(kIOMemoryMapReadOnly, 0, 0, 0, 0, &map);
if(map == nullptr)
{
MyDebugLog("USBSendSetLineCoding - Failed to map memory in CreateMapping\n");
IOFree(lineParms, lcLen);
bufferDescriptor->release();
return;
}
uint64_t ptr = map->GetAddress();
if(!ptr)
{
MyDebugLog("USBSendSetLineCoding - Failed to get Memory Address\n");
IOFree(lineParms, lcLen);
bufferDescriptor->release();
map->release();
return;
}
memcpy(&ptr, lineParms, lcLen);
uint8_t bmRequestType = kIOUSBDeviceRequestDirectionOut | kIOUSBDeviceRequestTypeClass | kIOUSBDeviceRequestRecipientInterface;
uint16_t wValue = 0;
uint16_t wIndex = _bControlInterfaceNumber;
uint16_t bytesTransferred = 0;
ret = _controlInterface->DeviceRequest(bmRequestType, kUSBSET_LINE_CODING, wValue, wIndex, lcLen, bufferDescriptor, &bytesTransferred, 1000);
IOFree(lineParms, lcLen);
map->release();
bufferDescriptor->release();
if (ret != kIOReturnSuccess) {
MyDebugLog("USBSendSetLineCoding - Failed : 0x%x, bytes transferred: %d\n", ret, bytesTransferred);
return;
}
return;
}
I am able to call DeviceRequest() successfully on the same interface for any other setting that requires no data buffer, such as,
"ret = _controlInterface->DeviceRequest(bmRequestType, kUSBSEND_BREAK, wValue, wIndex, 0, NULL, &bytesTransferred, 1000);"
So I think the "bufferDescriptor" is not properly created or the data is not copied correctly in this function for the failure.
"ret = _controlInterface->DeviceRequest(bmRequestType, kUSBSET_LINE_CODING, wValue, wIndex, lcLen, bufferDescriptor, &bytesTransferred, 1000);"
Any help is very much appreciated. Thanks in advance.
Can anyone please point out what is wrong with this code and suggest a right method?
I see a few different issues:
(1) You don't need to do any "extra" work to get access to your buffer, as the IOBufferMemoryDescriptor is basically "an IOMemoryDescriptor that makes it easy to read/write the memory". GetAddressRange actually returns a pointer inside your address space.
(2) You'll be cutting this code out, but for future reference, you should have passed "0" instead of "kIOMemoryMapReadOnly" here.
bufferDescriptor->CreateMapping(kIOMemoryMapReadOnly, 0, 0, 0, 0, &map);
The default behavior here is read/write, which kIOMemoryMapReadOnly is then modifying. I'm actually not sure what the result of your call was, as I would have expected a read-only map to mean you'd crash at memcpy. You didn’t, so I'm not sure what the actual result was, but I suspect you sent NULL data out.
(3) I'm not sure what your final goal here is, but you should not be creating and destroying the buffers returned by CreateIOBuffer. These are relatively "heavy" objects that should be reused.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware