如何使用 IOUSBHostDevice / IOUSBHostInterface 的 deviceRequest?
How to use deviceRequest of IOUSBHostDevice / IOUSBHostInterface?
我目前正在试验使用 Apple 的 I/O 工具包来开发内核模块。我正在尝试使用 IOUSBHostDevice 为 USB 无线网卡 (TP-Link WN722N-v1) 创建设备驱动程序。
我设法加载了 kext,它用卡正确探测,但我需要将固件发送到卡,这是我试图在 probe() 中做的。
我不完全了解一切如何进行,但似乎我需要向配置端点 0 发送请求,但我尝试过的方法导致内核崩溃,而我的其他示例使用的是过时的DeviceRequest 与新的 deviceRequest 没有可交换的参数。
我有一个从 github 项目 "BrcmPatchRAM" 编写固件的参考,但所有分支都使用旧的 IOUSBInterface 而不是 IOUSBHostInterface。
我在这个网站上搜索过类似的问题,但发现 none 在这件事上很有帮助,我还搜索了 Apple 开发者网站文档,它也引用了 DeviceRequest 的旧方法。
这是重写这个的正确方法,例如:
IOReturn USBInterfaceShim::hciCommand(void* command, UInt16 length)
{
IOUSBDevRequest request =
{
.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBDevice),
.bRequest = 0,
.wValue = 0,
.wIndex = 0,
.wLength = length,
.pData = command
};
return m_pInterface->DeviceRequest(&request);
}
使用此函数的一些其他实现在加载驱动程序时给我一个错误,并告诉我我在调用 prepare 之前发送请求,但我确实在发送请求之前调用了 prepare。
下面是我的一段乱码:
<pre>IOReturn AirPort_Atheros9271::pipeCommand(UInt8 requestType, UInt8 command, UInt16 address, IOMemoryDescriptor *buffer)
{
DeviceRequest request;
request.bmRequestType = requestType;
request.bRequest = command;
request.wValue = address;
request.wIndex = 0;
request.wLength = buffer->getLength();
uint32_t bytesTransferred;
return fInterface->deviceRequest(request, buffer, bytesTransferred, kUSBHostStandardRequestCompletionTimeout);
}
IOReturn AirPort_Atheros9271::pipeCommand(UInt8 requestType, UInt8 command, UInt16 address, void *buffer, UInt16 length)
{
DeviceRequest request;
request.bmRequestType = requestType;
request.bRequest = command;
request.wValue = address;
request.wIndex = 0;
request.wLength = length;
uint32_t bytesTransferred;
return fInterface->deviceRequest(request, buffer, bytesTransferred, kUSBHostStandardRequestCompletionTimeout);
}
bool TL_WN722N::performUpgrade()
{
IOLog("TL_WN722N::[%04x:%04x]: Performing firmware upgrade.\n", fVendorId, fProductId);
OSData *firmwareData = OSData::withBytes(ar9271_fw, ar9271_fw_len);
IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress((void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(), kIODirectionIn);
bool success = true;
IOLog("TL_WN722N::[%04x:%04x]: I have firmwareData and created the buffer\n", fVendorId, fProductId);
IOLockLock(fCompletionLock);
IOReturn result;
if (buffer!=NULL)
{
IOLog("TL_WN722N::[%04x:%04x]: Buffer is not null, now calling prepare()\n", fVendorId, fProductId);
if ((result = buffer->prepare(kIODirectionNone)) == kIOReturnSuccess)
{
IOLog("TL_WN722N::[%04x:%04x]: prepare() called and pass! Piping I - writing firmware\n", fVendorId, fProductId);
IOLog("TL_WN722N::[%04x:%04x]: Resultado de prepare: Result: %d\n", fVendorId, fProductId,result);
IOLog("TL_WN722N::[%04x:%04x]: About to excecute pipeCommand\n", fVendorId, fProductId);
result = pipeCommand(0x40, FIRMWARE_DOWNLOAD, AR9271_FIRMWARE >> 8, buffer); //TODO: EXPLOTION LINE
if (result>0) {
IOLog("TL_WN722N::[%04x:%04x]: Resultado de pipeCommand: %d\n", fVendorId, fProductId,result);
}
if (result != kIOReturnSuccess)
IOLog("TL_WN722N::[%04x:%04x]: Unable to write the firmware (0x%08x).\n", fVendorId, fProductId, result);
else
{
if ((result = pipeCommand(0x40, FIRMWARE_DOWNLOAD_COMP, AR9271_FIRMWARE_TEXT >> 8, NULL, 0)) != kIOReturnSuccess)
IOLog("TL_WN722N::[%04x:%04x]: Unable to write the firmware complete sequence (0x%08x).\n", fVendorId, fProductId, result);
else
{
IOLog("TL_WN722N::[%04x:%04x]: Success in writing the firmware sequence.\n", fVendorId, fProductId);
success = true;
}
}
}
else
IOLog("TL_WN722N::[%04x:%04x]: Failed to prepare write memory buffer (0x%08x).\n", fVendorId, fProductId, result);
if ((result = buffer->complete()) != kIOReturnSuccess)
IOLog("TL_WN722N::[%04x:%04x]: Failed to complete write memory buffer (0x%08x).\n", fVendorId, fProductId, result);
}
else
IOLog("TL_WN722N::[%04x:%04x]: Unable to allocate write memory buffer.\n", fVendorId, fProductId);
IOLockUnlock(fCompletionLock);
OSSafeReleaseNULL(buffer);
OSSafeReleaseNULL(firmwareData);
return success;
}</pre>
这里整理几篇:
Using some other implementation of this function gives me an error when loading the driver and tells me that I am sending the request before calling prepare, but I did called prepare before sending the request.
这给了我们一个很大的线索,表明您的问题出在内存缓冲区上。那么让我们看看创建和 prepare()
发生在哪里:
IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress(
(void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(),
kIODirectionIn);
// ^^^^^^^^^^^^^^^^
因此您正在创建输入(读取)内存描述符。
if ((result = buffer->prepare(kIODirectionNone)) == kIOReturnSuccess)
kIODirectionNone
只是使用创建方向,所以你准备从设备读取数据。
然后,I/O:
result = pipeCommand(0x40, FIRMWARE_DOWNLOAD, AR9271_FIRMWARE >> 8, buffer); //TODO: EXPLOTION LINE
// --------------------------^^^^
这意味着您的 bmRequestType
是 0x40
,因此没有设置 0x80
位。这意味着它是主机到设备,即 output/write.
所以你的方向不匹配:你正在准备一些内存用于从 USB 设备读取,然后尝试使用它写入设备。这行不通。
补充几点:
OSData
并不是 I/O 缓冲区分配的理想候选者。如果要为 I/O 分配系统内存,请使用 IOBufferMemoryDescriptor
。如果 ar9271_fw
是静态分配的数组,您也可以使用 IOMemoryDescriptor::withAddress()
简单地包装它 - 这避免了 OSData::withBytes()
执行的复制。
- 您正在执行 I/O,同时持有
IOLockLock(fCompletionLock);
锁。即使在启动异步 I/O 时,这也不是一个好主意,但看起来您正在使用 deviceRequest
的阻塞版本?这几乎肯定不是您想要的:函数调用实际上可能需要 秒 才能完成或失败。你不应该持有锁那么久。
我目前正在试验使用 Apple 的 I/O 工具包来开发内核模块。我正在尝试使用 IOUSBHostDevice 为 USB 无线网卡 (TP-Link WN722N-v1) 创建设备驱动程序。 我设法加载了 kext,它用卡正确探测,但我需要将固件发送到卡,这是我试图在 probe() 中做的。 我不完全了解一切如何进行,但似乎我需要向配置端点 0 发送请求,但我尝试过的方法导致内核崩溃,而我的其他示例使用的是过时的DeviceRequest 与新的 deviceRequest 没有可交换的参数。 我有一个从 github 项目 "BrcmPatchRAM" 编写固件的参考,但所有分支都使用旧的 IOUSBInterface 而不是 IOUSBHostInterface。 我在这个网站上搜索过类似的问题,但发现 none 在这件事上很有帮助,我还搜索了 Apple 开发者网站文档,它也引用了 DeviceRequest 的旧方法。 这是重写这个的正确方法,例如:
IOReturn USBInterfaceShim::hciCommand(void* command, UInt16 length) { IOUSBDevRequest request = { .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBDevice), .bRequest = 0, .wValue = 0, .wIndex = 0, .wLength = length, .pData = command }; return m_pInterface->DeviceRequest(&request); }
使用此函数的一些其他实现在加载驱动程序时给我一个错误,并告诉我我在调用 prepare 之前发送请求,但我确实在发送请求之前调用了 prepare。
下面是我的一段乱码:
<pre>IOReturn AirPort_Atheros9271::pipeCommand(UInt8 requestType, UInt8 command, UInt16 address, IOMemoryDescriptor *buffer)
{
DeviceRequest request;
request.bmRequestType = requestType;
request.bRequest = command;
request.wValue = address;
request.wIndex = 0;
request.wLength = buffer->getLength();
uint32_t bytesTransferred;
return fInterface->deviceRequest(request, buffer, bytesTransferred, kUSBHostStandardRequestCompletionTimeout);
}
IOReturn AirPort_Atheros9271::pipeCommand(UInt8 requestType, UInt8 command, UInt16 address, void *buffer, UInt16 length)
{
DeviceRequest request;
request.bmRequestType = requestType;
request.bRequest = command;
request.wValue = address;
request.wIndex = 0;
request.wLength = length;
uint32_t bytesTransferred;
return fInterface->deviceRequest(request, buffer, bytesTransferred, kUSBHostStandardRequestCompletionTimeout);
}
bool TL_WN722N::performUpgrade()
{
IOLog("TL_WN722N::[%04x:%04x]: Performing firmware upgrade.\n", fVendorId, fProductId);
OSData *firmwareData = OSData::withBytes(ar9271_fw, ar9271_fw_len);
IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress((void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(), kIODirectionIn);
bool success = true;
IOLog("TL_WN722N::[%04x:%04x]: I have firmwareData and created the buffer\n", fVendorId, fProductId);
IOLockLock(fCompletionLock);
IOReturn result;
if (buffer!=NULL)
{
IOLog("TL_WN722N::[%04x:%04x]: Buffer is not null, now calling prepare()\n", fVendorId, fProductId);
if ((result = buffer->prepare(kIODirectionNone)) == kIOReturnSuccess)
{
IOLog("TL_WN722N::[%04x:%04x]: prepare() called and pass! Piping I - writing firmware\n", fVendorId, fProductId);
IOLog("TL_WN722N::[%04x:%04x]: Resultado de prepare: Result: %d\n", fVendorId, fProductId,result);
IOLog("TL_WN722N::[%04x:%04x]: About to excecute pipeCommand\n", fVendorId, fProductId);
result = pipeCommand(0x40, FIRMWARE_DOWNLOAD, AR9271_FIRMWARE >> 8, buffer); //TODO: EXPLOTION LINE
if (result>0) {
IOLog("TL_WN722N::[%04x:%04x]: Resultado de pipeCommand: %d\n", fVendorId, fProductId,result);
}
if (result != kIOReturnSuccess)
IOLog("TL_WN722N::[%04x:%04x]: Unable to write the firmware (0x%08x).\n", fVendorId, fProductId, result);
else
{
if ((result = pipeCommand(0x40, FIRMWARE_DOWNLOAD_COMP, AR9271_FIRMWARE_TEXT >> 8, NULL, 0)) != kIOReturnSuccess)
IOLog("TL_WN722N::[%04x:%04x]: Unable to write the firmware complete sequence (0x%08x).\n", fVendorId, fProductId, result);
else
{
IOLog("TL_WN722N::[%04x:%04x]: Success in writing the firmware sequence.\n", fVendorId, fProductId);
success = true;
}
}
}
else
IOLog("TL_WN722N::[%04x:%04x]: Failed to prepare write memory buffer (0x%08x).\n", fVendorId, fProductId, result);
if ((result = buffer->complete()) != kIOReturnSuccess)
IOLog("TL_WN722N::[%04x:%04x]: Failed to complete write memory buffer (0x%08x).\n", fVendorId, fProductId, result);
}
else
IOLog("TL_WN722N::[%04x:%04x]: Unable to allocate write memory buffer.\n", fVendorId, fProductId);
IOLockUnlock(fCompletionLock);
OSSafeReleaseNULL(buffer);
OSSafeReleaseNULL(firmwareData);
return success;
}</pre>
这里整理几篇:
Using some other implementation of this function gives me an error when loading the driver and tells me that I am sending the request before calling prepare, but I did called prepare before sending the request.
这给了我们一个很大的线索,表明您的问题出在内存缓冲区上。那么让我们看看创建和 prepare()
发生在哪里:
IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress(
(void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(),
kIODirectionIn);
// ^^^^^^^^^^^^^^^^
因此您正在创建输入(读取)内存描述符。
if ((result = buffer->prepare(kIODirectionNone)) == kIOReturnSuccess)
kIODirectionNone
只是使用创建方向,所以你准备从设备读取数据。
然后,I/O:
result = pipeCommand(0x40, FIRMWARE_DOWNLOAD, AR9271_FIRMWARE >> 8, buffer); //TODO: EXPLOTION LINE
// --------------------------^^^^
这意味着您的 bmRequestType
是 0x40
,因此没有设置 0x80
位。这意味着它是主机到设备,即 output/write.
所以你的方向不匹配:你正在准备一些内存用于从 USB 设备读取,然后尝试使用它写入设备。这行不通。
补充几点:
OSData
并不是 I/O 缓冲区分配的理想候选者。如果要为 I/O 分配系统内存,请使用IOBufferMemoryDescriptor
。如果ar9271_fw
是静态分配的数组,您也可以使用IOMemoryDescriptor::withAddress()
简单地包装它 - 这避免了OSData::withBytes()
执行的复制。- 您正在执行 I/O,同时持有
IOLockLock(fCompletionLock);
锁。即使在启动异步 I/O 时,这也不是一个好主意,但看起来您正在使用deviceRequest
的阻塞版本?这几乎肯定不是您想要的:函数调用实际上可能需要 秒 才能完成或失败。你不应该持有锁那么久。