openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

259 lines
7.0 KiB

#include "TinyGPUDriver.h"
#include "TinyGPUDriverUserClient.h"
#include <AudioDriverKit/AudioDriverKit.h>
#include <DriverKit/IOUserServer.h>
#include <DriverKit/IOLib.h>
#include <DriverKit/OSString.h>
#include <DriverKit/IOMemoryMap.h>
#include <DriverKit/IODMACommand.h>
#include <DriverKit/IODispatchQueue.h>
#include <PCIDriverKit/PCIDriverKit.h>
#include <DriverKit/OSAction.h>
struct TinyGPUDriver_IVars
{
IOPCIDevice *pci = nullptr;
};
bool TinyGPUDriver::init()
{
os_log(OS_LOG_DEFAULT, "tinygpu: init");
auto answer = super::init();
if (!answer) {
return false;
}
ivars = new TinyGPUDriver_IVars();
if (ivars == nullptr) {
return false;
}
return true;
}
void TinyGPUDriver::free()
{
if (ivars != nullptr) {
}
IOSafeDeleteNULL(ivars, TinyGPUDriver_IVars, 1);
super::free();
}
kern_return_t TinyGPUDriver::Start_Impl(IOService* in_provider)
{
IOServiceName service_name;
os_log(OS_LOG_DEFAULT, "tinygpu: on gpu detected");
kern_return_t err = Start(in_provider, SUPERDISPATCH);
if (err) return err;
ivars->pci = OSDynamicCast(IOPCIDevice, in_provider);
if (!ivars->pci) return kIOReturnNoDevice;
err = ivars->pci->Open(this, 0);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: Open() failed 0x%08x", err);
ivars->pci = nullptr;
return err;
}
uint16_t ven = 0, dev = 0;
ivars->pci->ConfigurationRead16(kIOPCIConfigurationOffsetVendorID, &ven);
ivars->pci->ConfigurationRead16(kIOPCIConfigurationOffsetDeviceID, &dev);
os_log(OS_LOG_DEFAULT, "tinygpu: opened device ven=0x%04x dev=0x%04x", ven, dev);
#if 0
uint32_t off = 0x100;
while (off) {
uint32_t hdr = 0, next = 0, cap_id = 0;
ivars->pci->ConfigurationRead32(off, &hdr);
cap_id = hdr & 0xFFFFu;
next = (hdr >> 20) & 0xFFCu;
os_log(OS_LOG_DEFAULT, "tinygpu: cap: %u", cap_id);
if (cap_id == 0x15) {
uint32_t cap = 0, ctrl = 0;
ivars->pci->ConfigurationRead32(off+0x4, &cap);
ivars->pci->ConfigurationRead32(off+0x8, &ctrl);
uint32_t new_bar_size = 31 - __builtin_clz(cap >> 4);
uint32_t new_ctrl = (ctrl & ~0x1f00) | (new_bar_size << 8);
ivars->pci->ConfigurationWrite32(off+0x8, new_ctrl);
os_log(OS_LOG_DEFAULT, "tinygpu: rebar: cap=%u ctrl=%u new_bar_size=%u new_ctrl=%u", cap, ctrl, new_bar_size, new_ctrl);
ivars->pci->Reset(0);
break;
}
off = next;
}
ivars->pci->Reset(kIOPCIDeviceResetTypeHotReset);
#endif
uint16_t commandRegister;
ivars->pci->ConfigurationRead16(kIOPCIConfigurationOffsetCommand, &commandRegister);
commandRegister |= (kIOPCICommandIOSpace | kIOPCICommandBusMaster | kIOPCICommandMemorySpace);
ivars->pci->ConfigurationWrite16(kIOPCIConfigurationOffsetCommand, commandRegister);
memcpy((void*)service_name, (void*)"tinygpu\0", 8);
SetName(service_name);
os_log(OS_LOG_DEFAULT, "tinygpu: will register service %s", service_name);
RegisterService();
os_log(OS_LOG_DEFAULT, "tinygpu: service started %s", service_name);
return 0;
}
kern_return_t TinyGPUDriver::Stop_Impl(IOService* in_provider)
{
ivars->pci->Close(this, 0);
return 0;
}
kern_return_t TinyGPUDriver::NewUserClient_Impl(uint32_t in_type, IOUserClient** out_user_client)
{
kern_return_t err = 0;
IOService* user_client_service = nullptr;
err = Create(this, "TinyGPUDriverUserClientProperties", &user_client_service);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to create NewUserClient");
goto error;
}
*out_user_client = OSDynamicCast(IOUserClient, user_client_service);
os_log(OS_LOG_DEFAULT, "tinygpu: NewUserClient created");
error:
return err;
}
kern_return_t TinyGPUDriver::MapBar(uint32_t bar, IOMemoryDescriptor** memory)
{
kern_return_t err = 0;
uint8_t barMemoryIndex, barMemoryType;
uint64_t barMemorySize;
err = ivars->pci->GetBARInfo(bar, &barMemoryIndex, &barMemorySize, &barMemoryType);
if (err) return err;
os_log(OS_LOG_DEFAULT, "tinygpu: requested bar mapping %d, %d", bar, (uint32_t)barMemoryIndex);
err = ivars->pci->_CopyDeviceMemoryWithIndex(barMemoryIndex, memory, this);
return err;
}
kern_return_t TinyGPUDriver::CreateDMA(size_t size, TinyGPUCreateDMAResp* dmaDesc)
{
kern_return_t err = 0;
IOMemoryMap* memoryMap = nullptr;
IOBufferMemoryDescriptor* sharedBuf = nullptr;
IODMACommand* dmaCmd = nullptr;
uint64_t flags = kIOMemoryDirectionInOut;
uint32_t segCount = 32;
IOAddressSegment segments[32];
IODMACommandSpecification dmaSpec = {
.options = 0,
.maxAddressBits = 40,
};
err = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, size, IOVMPageSize, &sharedBuf);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to alloc user buffer, err=%d", err);
goto error;
}
err = IODMACommand::Create(ivars->pci, kIODMACommandCreateNoOptions, &dmaSpec, &dmaCmd);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to create dma command, err=%d", err);
goto error;
}
err = dmaCmd->PrepareForDMA(kIODMACommandPrepareForDMANoOptions, sharedBuf, 0, size,
&flags, &segCount, segments);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to prepare for dma, err=%d", err);
goto error;
}
// pass addresses to userland
{
// debug
for (int i = 0; i < segCount; i++) {
os_log(OS_LOG_DEFAULT, "tinygpu: new dma mapping (sz=0x%zx) %d 0x%llx 0x%llx", size, i, segments[i].address, segments[i].length);
}
err = sharedBuf->CreateMapping(0, 0, 0, IOVMPageSize, IOVMPageSize, &memoryMap); // one page should be fine
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to map memory, err=%d", err);
goto error;
}
// Send back gpu addresses
uint64_t* addr = (uint64_t*)memoryMap->GetAddress();
for (int i = 0; i < segCount; i++) {
addr[i * 2] = segments[i].address;
addr[i * 2 + 1] = segments[i].length;
}
addr[segCount * 2] = 0;
addr[segCount * 2 + 1] = 0;
// free memoryMap
memoryMap->release();
memoryMap = nullptr;
}
dmaDesc->sharedBuf = sharedBuf;
dmaDesc->dmaCmd = dmaCmd;
return 0;
error:
if (memoryMap) {
memoryMap->release();
memoryMap = nullptr;
}
if (dmaCmd) {
dmaCmd->CompleteDMA(kIODMACommandCompleteDMANoOptions);
dmaCmd->release();
dmaCmd = nullptr;
}
if (sharedBuf) {
sharedBuf->release();
sharedBuf = nullptr;
}
return err;
}
kern_return_t TinyGPUDriver::CfgRead(uint32_t off, uint32_t size, uint32_t* outVal)
{
if (!ivars->pci || !outVal) return kIOReturnNotReady;
if (size == 1) {
uint8_t v8 = 0;
ivars->pci->ConfigurationRead8(off, &v8);
*outVal = v8;
} else if (size == 2) {
uint16_t v16 = 0;
ivars->pci->ConfigurationRead16(off, &v16);
*outVal = v16;
} else if (size == 4) {
uint32_t v32 = 0;
ivars->pci->ConfigurationRead32(off, &v32);
*outVal = v32;
}
return 0;
}
kern_return_t TinyGPUDriver::CfgWrite(uint32_t off, uint32_t size, uint32_t val)
{
if (!ivars->pci) return kIOReturnNotReady;
if (size == 1) ivars->pci->ConfigurationWrite8 (off, (uint8_t)val);
else if (size == 2) ivars->pci->ConfigurationWrite16(off, (uint16_t)val);
else if (size == 4) ivars->pci->ConfigurationWrite32(off, (uint32_t)val);
return 0;
}
kern_return_t TinyGPUDriver::ResetDevice()
{
if (!ivars->pci) return kIOReturnNotReady;
ivars->pci->Reset(kIOPCIDeviceResetTypeFunctionReset);
return 0;
}