Как следует реализовать "NewUserClient"
Я пытаюсь взаимодействовать с декстом из приложения. Я могу найти сервис, используяIOServiceOpen
и мне звонят NewUserClient
моего декста (я вижу type
переданный параметр выводится в журнал). После этого я немного заблудился. Читая здесь о NewUserClient, я вижу, что следует использовать Create
для создания нового объекта Service.
Обсуждение часть здесь говорит ключи вpropertiesKey
словарь описать новую услугу.
Если этот словарь быть помещен в файл plist для расширения системы как запись верхнего уровня, или если словарь должен быть помещен с ключом в IOKitPersonalities
?
Могу я оставить IOServiceDEXTEntitlements
ключ с пустым значением, чтобы не накладывать никаких ограничений на права для приложения, которое подключается к расширению системы?
Мой список выглядит так (с MyUserClientProperties
ключ / dict в двух местах).
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MyUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>MyUserClient</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
</dict>
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>MyUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>MyUserClient</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
</dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOProviderClass</key>
<string>IOUSBHostInterface</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOUserServerName</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>bConfigurationValue</key>
<integer>0x1</integer>
<key>bInterfaceNumber</key>
<integer>0x0</integer>
<key>idVendor</key>
<integer>0x123</integer>
<key>idProduct</key>
<integer>0x08</integer>
</dict>
</dict>
<key>OSBundleUsageDescription</key>
<string>Example user space USB driver</string>
</dict>
</plist>
Мне нужно пройти SUPERDISPATCH
как последний аргумент Create
?
Из главы 5 "Программирование ядра OSX и iOS" на стр. 81:
Изобретательность дизайна набора I/O Kit состоит в том, что пользовательские клиентские объекты сами являются объектом драйвера: класс IOUserClient наследуется от IOService, и, как и в случае с любым другим экземпляром IOService, каждый пользовательский клиент имеет класс провайдера, который для пользовательского клиента является экземпляр драйвера, которым управляет приложение.
Хотя приведенное выше может быть правильным только для kext (?), Я бы предположил, что все работает таким же образом для dext,
Из документации Create: используйте ключ kIOUserClassKey, чтобы указать имя настраиваемого подкласса IOService, экземпляр которого вы хотите создать в системе.
Почему другой IOService
класс нужно создать? Какова цель этого занятия? Это поставщик моего класса, который наследуется отIOUserClient
? Если да, то как я могу создать экземпляр моего драйвера (тот, который реализуетNewUserClient
) провайдер?
Из документации Create: используйтеkIOClassKey
указать название кастома IOUserClient
подкласс для возврата клиентам вашего сервиса.
Тип класса, который будет создан и назначен третьему аргументу Create
? Если да, то это тот, который я должен назначитьIOUserClient*
указатель на, который передается NewUserClient
?
kern_return_t IMPL(MyUserUSBInterfaceDriver, NewUserClient) {
os_log(OS_LOG_DEFAULT, "%{public}d:", type);
IOPropertyName propertiesKey = "MyUserClientProperties";
IOService* client;
auto ret = Create(this, propertiesKey, &client, SUPERDISPATCH);
// Need to do more things here...
return ret;
}
Что бы я ни пытался, я всегда получаю утверждение, но я не могу понять, что его вызывает.
3 com.apple.DriverKit 0x0000000102f2b24b __assert_rtn + 102
4 com.apple.DriverKit 0x0000000102f2c20a IOService::Create_Impl(IOService*, char const*, IOService**) (.cold.2) + 35
5 com.apple.DriverKit 0x0000000102f1766b IOService::Create_Impl(IOService*, char const*, IOService**) + 91
6 com.apple.DriverKit 0x0000000102f2668f IOService::Create_Invoke(IORPC, OSMetaClassBase*, int (*)(OSMetaClassBase*, IOService*, char const*, IOService**)) + 135
7 com.apple.DriverKit 0x0000000102f276d7 IOService::Create(IOService*, char const*, IOService**, int (*)(OSMetaClassBase*, IORPC)) + 267
8 sc.example.MyUserUSBInterfaceDriver 0x0000000102ee0c89 MyUserUSBInterfaceDriver::NewUserClient_Impl(unsigned int, IOUserClient**) + 313 (MyUserUSBInterfaceDriver.cpp:155)
1 ответ
Несмотря на то, что презентация WWDC на DriverKit пыталась притвориться иначе, взгляд DriverKit на мир сильно отличается от взгляда ядра, и вам нужно знать некоторые детали реализации, потому что абстракция очень нечеткая.
Как вы, наверное, уже заметили, то, что выглядит как IOService
объект в вашем драйвере DriverKit на самом деле IOUserService
объект в представлении ядра (и пользовательского пространства) реестра ввода-вывода. Пробел устраняется с помощью механизма IPC DriverKit.
Для создания нового пользовательского клиента вам нужен экземпляр (ядра) IOUserClient
подкласс, который поддерживается вашим (dext) IOUserClient
подкласс. Класс ядра для этого на самом делеIOUserUserClient
. (Да, действительно.) Как вы обнаружили, в документации не совсем ясно, как это сделать. Я счел полезным взглянуть на то, что доступно с точки зрения исходного кода - со стороны ядра, вызывающейNewUserClient
реализуется в IOUserServer::serviceNewUserClient()
функции здесь.
Вы сразу заметите, что если IOServiceDEXTEntitlements
свойство отсутствует, это не помешает выполнению кода:
prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey);
ok = checkEntitlements(entitlements, prop, NULL, NULL);
И в checkEntitlements
:
if (!prop) {
return true;
}
Это отличная новость, поскольку это означает, что нам не нужно беспокоиться об этом ни на минуту, и мы можем просто оставить это без внимания.
Далее выясняется, что propertiesKey
относится к собственности у поставщикаIOUserService
объект ядра. Вы не можете установить эти свойства из кода декста, поэтому единственный способ предоставить их - из соответствующего словаря личности IOKit.
Вы можете назвать это свойство как хотите, но:
- Его значением должен быть словарь.
- Он должен содержать
"IOClass"
пара ключ-значение, определяющая класс ядра для создания экземпляра в виде строки - в вашем случае"IOUserUserClient"
- Он должен содержать
"IOUserClass"
пара ключ-значение. Это указывает класс dext для создания экземпляра снова в виде строки. В вашем случае это выглядит какMyUserClient
.
Собираем вместе:
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>MyUserClientProperties</key>
<dict>
<key>IOUserClass</key>
<string>MyUserClient</string>
<key>IOClass</key>
<string>IOUserUserClient</string>
</dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
…
</dict>
</dict>
…
Затем из вашего NewUserClient
функция, вызов:
IOService* client = nullptr;
kern_return_t ret = this->Create(this, "MyUserClient", &client);
Я не верю SUPERDISPATCH
здесь нужен, поскольку вы, вероятно, не отменяете Create
в вашем классе, поэтому ваша супер-реализация все равно наследуется.
Затем выполните проверку ошибок, любую другую инициализацию, подготовку и т. Д., Которая может вам понадобиться, и, наконец:
*userClient = client;
return kIOReturnSuccess;