diff --git a/CMakeLists.txt b/CMakeLists.txt index db4e52835..54710abe5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,7 @@ add_subdirectory(src/libobjcdarwin) add_subdirectory(src/libncurses) add_subdirectory(src/CoreSecurity) add_subdirectory(src/CoreServices) +add_subdirectory(src/IOKit) add_subdirectory(src/thin) add_dependencies(dyld${SUFFIX} mach-o) diff --git a/src/IOKit/CMakeLists.txt b/src/IOKit/CMakeLists.txt new file mode 100644 index 000000000..93c9e8e51 --- /dev/null +++ b/src/IOKit/CMakeLists.txt @@ -0,0 +1,34 @@ +project(IOKit) + +cmake_minimum_required(VERSION 2.4.0) +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) + +#if (NOT "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" MATCHES ".*clang") +# message(FATAL_ERROR "Clang is the only supported compiler.") +#endif (NOT "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" MATCHES ".*clang") + +#configure_file(config.h.in config.h) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks") + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../util) + +set(IOKit_SRCS + bsd.cpp + port.c + service.mm + io_object.cpp + io_device_iterator.cpp + io_device.cpp + io_device_usb.cpp +) + +add_library(IOKit SHARED ${IOKit_SRCS}) +target_link_libraries(IOKit -lgnustep-corebase util -ludev) + +install(TARGETS IOKit DESTINATION "lib${SUFFIX}/darling") + diff --git a/src/IOKit/bsd.cpp b/src/IOKit/bsd.cpp new file mode 100644 index 000000000..cba185001 --- /dev/null +++ b/src/IOKit/bsd.cpp @@ -0,0 +1,54 @@ +#include "bsd.h" +#include "service.h" +#include +#include +#include +#include +#include "constants.h" +#include + +std::string DarlingTranslateBSDName(const char* name); +bool DarlingFindBSDName(const char* name, char* path); + +/* +CFMutableDictionaryRef IOBSDNameMatching(void* iokitPort, unsigned int options, const char* bsdName) +{ + // Search /sys/class/* for a device called bsdName + bsdName = DarlingTranslateBSDName(bsdName); + + char path[PATH_MAX]; + + if (DarlingFindBSDName(bsdName, path)) + return IOServiceSysPath(path); + else + return NULL; +} +*/ + + +std::string DarlingTranslateBSDName(const char* name) +{ + int num; + char linuxName[100]; + + // Macbooks have en0 = eth0, en1 = wlan0 + // We don't handle that yet + if (sscanf(name, "en%d", &num) == 1) + { + sprintf(linuxName, "eth%d", num); + return linuxName; + } + if (sscanf(name, "lo%d", &num) == 1) + { + return "lo"; + } + if (sscanf(name, "stf%d", &num) == 1) + { + sprintf(linuxName, "sit%d", num); + return linuxName; + } + + return name; +} + + diff --git a/src/IOKit/bsd.h b/src/IOKit/bsd.h new file mode 100644 index 000000000..a208f1278 --- /dev/null +++ b/src/IOKit/bsd.h @@ -0,0 +1,13 @@ +#ifndef IOKIT_BSD_H +#define IOKIT_BSD_H +#include +#include + +extern "C" +CFMutableDictionaryRef IOBSDNameMatching(void* iokitPort, unsigned int options, const char* bsdName); + +std::string DarlingTranslateBSDName(const char* name); +bool DarlingFindBSDName(const char* name, char* path); + +#endif + diff --git a/src/IOKit/cfutil.h b/src/IOKit/cfutil.h new file mode 100644 index 000000000..5e5e2ee61 --- /dev/null +++ b/src/IOKit/cfutil.h @@ -0,0 +1,13 @@ +#ifndef IOKIT_CFUTIL_H +#define IOKIT_CFUTIL_H + +#define strCFEqual(cfs, cstr) (CFStringCompare(cfs, CFSTR(cstr), CFStringCompareFlags(0)) == 0) + +inline CFStringRef strToCF(const char* val, CFAllocatorRef all = kCFAllocatorDefault) +{ + if (!val) + return nullptr; + return CFStringCreateWithCString(kCFAllocatorDefault, val, kCFStringEncodingUTF8); +} + +#endif diff --git a/src/IOKit/constants.h b/src/IOKit/constants.h new file mode 100644 index 000000000..991b1e7e3 --- /dev/null +++ b/src/IOKit/constants.h @@ -0,0 +1,41 @@ +#ifndef IOKIT_CONSTANTS_H +#define IOKIT_CONSTANTS_H + +#define kIOMasterPortDefault NULL + +#define kIOProviderClassKey "IOProviderClass" +#define kIOBSDNameKey "BSD Name" +#define kIONameMatchKey "IONameMatch" +#define kIOPropertyMatchKey "IOPropertyMatch" +#define kIOPathMatchKey "IOPathMatch" +#define kIOLocationMatchKey "IOLocationMatch" +#define kIOParentMatchKey "IOParentMatch" +#define kIOResourceMatchKey "IOResourceMatch" +#define kIOMatchedServiceCountKey "IOMatchedServiceCountMatch" +#define kIONameMatchedKey "IONameMatched" +#define kIOMatchCategoryKey "IOMatchCategory" +#define kIODefaultMatchCategoryKey "IODefaultMatchCategory" + +// ethernet +#define kIOEthernetAddressSize 6 +#define kIOEthernetInterfaceClass "IOEthernetInterface" + +// serial +#define kIOSerialBSDServiceValue "IOSerialBSDClient" +#define kIOSerialBSDAllTypes "IOSerialStream" +#define kIOSerialBSDModemType "IOModemSerialStream" +#define kIOSerialBSDRS232Type "IORS232SerialStream" + +#define kIOTTYDeviceKey "IOTTYDevice" +#define kIOTTYBaseNameKey "IOTTYBaseName" +#define kIOTTYSuffixKey "IOTTYSuffix" +#define kIOCalloutDeviceKey "IOCalloutDevice" +#define kIODialinDeviceKey "IODialinDevice" + +// USB +#define kIOUSBDeviceClassName "IOUSBDevice" +#define kIOUSBInterfaceClassName "IOUSBInterface" +#define kIOHIDDeviceKey "IOHIDDevice" + +#endif + diff --git a/src/IOKit/io_device.cpp b/src/IOKit/io_device.cpp new file mode 100644 index 000000000..b5f7958f8 --- /dev/null +++ b/src/IOKit/io_device.cpp @@ -0,0 +1,98 @@ +#include "io_device.h" +#include +#include "cfutil.h" + +io_device::io_device(struct udev_device* dev) + : m_device(dev) +{ + m_properties = udev_device_get_properties_list_entry(m_device); + m_sysattrs = udev_device_get_sysattr_list_entry(m_device); +} + +io_device::~io_device() +{ + udev_device_unref(m_device); +} + +const char* io_device::property(const char* name) +{ + struct udev_list_entry* p = udev_list_entry_get_by_name(m_properties, name); + if (p != nullptr) + return udev_list_entry_get_value(p); + else + return nullptr; +} + +const char* io_device::sysattr(const char* name) +{ + struct udev_list_entry* p = udev_list_entry_get_by_name(m_sysattrs, name); + if (p != nullptr) + return udev_list_entry_get_value(p); + else + return nullptr; +} + +CFStringRef io_device::sysattrStr(const char* name, CFAllocatorRef allocator) +{ + return strToCF(sysattr(name), allocator); +} + +CFNumberRef io_device::sysattrNum(const char* name, CFAllocatorRef allocator) +{ + const char* value = sysattr(name); + if (!value) + return nullptr; + + char* end; + long lvalue = strtol(value, &end, 16); + + if (end == value) + return nullptr; + + return CFNumberCreate(allocator, kCFNumberLongType, &lvalue); +} + +CFTypeRef io_device::retrieve(const property_mapping* mapping, CFAllocatorRef allocator) +{ + const char* value = nullptr; + if (mapping->linuxProperty) + value = property(mapping->linuxProperty); + else if (mapping->linuxSysAttr) + value = sysattr(mapping->linuxSysAttr); + if (!value) + return nullptr; + + if (mapping->dataType == property_mapping::String) + return strToCF(value, allocator); + else + { + int base = (mapping->dataType == property_mapping::Number10) ? 10 : 16; + char* end; + long lvalue = strtol(value, &end, base); + + if (end == value) + return nullptr; + + return CFNumberCreate(allocator, kCFNumberLongType, &lvalue); + } +} + +void io_device::properties(CFMutableDictionaryRef dict, CFAllocatorRef allocator) +{ +} + +CFTypeRef io_device::property(CFStringRef name, CFAllocatorRef allocator) +{ + return nullptr; +} + +const property_mapping* property_mapping::find(const property_mapping* map, size_t count, CFStringRef what) +{ + for (size_t i = 0; i < count; i++) + { + if (CFStringCompare(map[i].appleName, what, CFStringCompareFlags(0)) == 0) + return map + i; + } + return nullptr; +} + diff --git a/src/IOKit/io_device.h b/src/IOKit/io_device.h new file mode 100644 index 000000000..8e4d4dabf --- /dev/null +++ b/src/IOKit/io_device.h @@ -0,0 +1,41 @@ +#ifndef IOKIT_IODEVICE_H +#define IOKIT_IODEVICE_H +#include +#include +#include +#include "io_object.h" + +struct property_mapping; + +class io_device : public io_object +{ +public: + io_device(struct udev_device* device); + virtual ~io_device(); + virtual void properties(CFMutableDictionaryRef dict, CFAllocatorRef allocator); + virtual CFTypeRef property(CFStringRef name, CFAllocatorRef allocator); +protected: + const char* property(const char* name); + const char* sysattr(const char* name); + CFStringRef sysattrStr(const char* name, CFAllocatorRef allocator); + CFNumberRef sysattrNum(const char* name, CFAllocatorRef allocator); + CFTypeRef retrieve(const property_mapping* mapping, CFAllocatorRef allocator); +protected: + struct udev_device* m_device; + struct udev_list_entry *m_properties, *m_sysattrs; +}; + +struct property_mapping +{ + CFStringRef appleName; + const char* linuxProperty; + const char* linuxSysAttr; + + enum DataType { String, Number10, Number16 }; + DataType dataType; + + static const property_mapping* find(const property_mapping* map, size_t count, CFStringRef what); +}; + +#endif + diff --git a/src/IOKit/io_device_iterator.cpp b/src/IOKit/io_device_iterator.cpp new file mode 100644 index 000000000..4cbc01588 --- /dev/null +++ b/src/IOKit/io_device_iterator.cpp @@ -0,0 +1,28 @@ +#include "io_device_iterator.h" +#include "io_device.h" +#include + +io_device_iterator::io_device_iterator(udev_enumerate* uenum) + : m_uenum(uenum) +{ + m_udev = udev_new(); + m_next = udev_enumerate_get_list_entry(uenum); +} + +io_device_iterator::~io_device_iterator() +{ + udev_enumerate_unref(m_uenum); + udev_unref(m_udev); +} + +io_object_t io_device_iterator::next() +{ + if (!m_next) + return nullptr; + else + { + udev_device* dev = udev_device_new_from_syspath(m_udev, udev_list_entry_get_name(m_next)); + m_next = udev_list_entry_get_next(m_next); + return new io_device(dev); + } +} diff --git a/src/IOKit/io_device_iterator.h b/src/IOKit/io_device_iterator.h new file mode 100644 index 000000000..c879fac23 --- /dev/null +++ b/src/IOKit/io_device_iterator.h @@ -0,0 +1,19 @@ +#ifndef IOKIT_IODEVICEITERATOR_H +#define IOKIT_IODEVICEITERATOR_H +#include "io_iterator.h" + +class io_device_iterator : public io_iterator +{ +public: + io_device_iterator(struct udev_enumerate* uenum); + virtual ~io_device_iterator(); + virtual io_object_t next() override; +private: + struct udev* m_udev; + struct udev_enumerate* m_uenum; + struct udev_list_entry* m_next; +}; + +typedef io_device_iterator* io_device_iterator_t; + +#endif diff --git a/src/IOKit/io_device_usb.cpp b/src/IOKit/io_device_usb.cpp new file mode 100644 index 000000000..5ab90a104 --- /dev/null +++ b/src/IOKit/io_device_usb.cpp @@ -0,0 +1,29 @@ +#include "io_device_usb.h" + +// https://developer.apple.com/library/mac/#documentation/IOKit/Reference/USBSpec_header_reference/Reference/reference.html +static const property_mapping usb_properties[] = { + { CFSTR("Serial Number"), nullptr, "serial", property_mapping::String }, + { CFSTR("USB Product Name"), nullptr, "product", property_mapping::String }, + { CFSTR("USB Vendor Name"), nullptr, "manufacturer", property_mapping::String }, + { CFSTR("VendorID"), nullptr, "idVendor", property_mapping::Number16 }, + { CFSTR("ProductID"), nullptr, "idProduct", property_mapping::Number16 }, + { CFSTR("bInterfaceClass"), nullptr, "bInterfaceClass", property_mapping::Number16 }, + { CFSTR("bDeviceClass"), nullptr, "bDeviceClass", property_mapping::Number16 }, +}; + + +CFTypeRef io_device_usb::property(CFStringRef name, CFAllocatorRef allocator) +{ + const property_mapping* prop = property_mapping::find(usb_properties, sizeof(usb_properties)/sizeof(usb_properties[0]), name); + + if (!prop) + return nullptr; + else + return retrieve(prop, allocator); +} + +void io_device_usb::properties(CFMutableDictionaryRef dict, CFAllocatorRef allocator) +{ + // walk dict, call retrieve() + +} diff --git a/src/IOKit/io_device_usb.h b/src/IOKit/io_device_usb.h new file mode 100644 index 000000000..bba3bd0e7 --- /dev/null +++ b/src/IOKit/io_device_usb.h @@ -0,0 +1,16 @@ +#ifndef IOKIT_IODEVICEUSB_H +#define IOKIT_IODEVICEUSB_H +#include "io_device.h" + +class io_device_usb : public io_device +{ +public: + // error: inheriting constructors are not supported + // using io_device::io_device; + + io_device_usb(struct udev_device* device) : io_device(device) {} + virtual CFTypeRef property(CFStringRef name, CFAllocatorRef allocator) override; + virtual void properties(CFMutableDictionaryRef dict, CFAllocatorRef allocator) override; +}; + +#endif diff --git a/src/IOKit/io_iterator.h b/src/IOKit/io_iterator.h new file mode 100644 index 000000000..a8bcc44c6 --- /dev/null +++ b/src/IOKit/io_iterator.h @@ -0,0 +1,13 @@ +#ifndef IOKIT_IOITERATOR_H +#define IOKIT_IOITERATOR_H +#include "io_object.h" + +class io_iterator : public io_object +{ +public: + virtual io_object_t next() = 0; +}; + +typedef io_iterator* io_iterator_t; + +#endif diff --git a/src/IOKit/io_object.cpp b/src/IOKit/io_object.cpp new file mode 100644 index 000000000..d0606e970 --- /dev/null +++ b/src/IOKit/io_object.cpp @@ -0,0 +1,6 @@ +#include "io_object.h" + +io_object::~io_object() +{ +} + diff --git a/src/IOKit/io_object.h b/src/IOKit/io_object.h new file mode 100644 index 000000000..3c006d887 --- /dev/null +++ b/src/IOKit/io_object.h @@ -0,0 +1,15 @@ +#ifndef IOKIT_IOOBJECT_H +#define IOKIT_IOOBJECT_H + +class io_object +{ +public: + virtual ~io_object(); +}; + +typedef io_object* io_object_t; + +extern "C" int IOObjectRelease(io_object_t obj); + +#endif + diff --git a/src/IOKit/port.c b/src/IOKit/port.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/IOKit/port.h b/src/IOKit/port.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/IOKit/service.h b/src/IOKit/service.h new file mode 100644 index 000000000..6afe8f005 --- /dev/null +++ b/src/IOKit/service.h @@ -0,0 +1,25 @@ +#ifndef IOKIT_SERVICE_H +#define IOKIT_SERVICE_H +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS +#endif + +#include "io_object.h" +#include "io_iterator.h" +#include + +// This seems to give all children (e.g. all USB devices) +extern "C" CFMutableDictionaryRef IOServiceMatching(const char* service); +// This seems to give the root device (e.g. the USB driver) +extern "C" CFMutableDictionaryRef IOServiceNameMatching(const char* name); +extern "C" CFMutableDictionaryRef IOBSDNameMatching(void* iokitPort, unsigned int options, const char* bsdName); + +extern "C" int IOServiceGetMatchingServices(void* port, CFDictionaryRef rules, io_iterator_t* iter); +extern "C" int IOIteratorIsValid(io_iterator_t iter); +extern "C" io_object_t IOIteratorNext(io_iterator_t iter); +extern "C" int IOObjectRelease(io_object_t obj); + +extern "C" int IORegistryEntryCreateCFProperties(io_object_t obj, CFMutableDictionaryRef* props, CFAllocatorRef allocator, int opts); + +#endif + diff --git a/src/IOKit/service.mm b/src/IOKit/service.mm new file mode 100644 index 000000000..8519ee9dd --- /dev/null +++ b/src/IOKit/service.mm @@ -0,0 +1,190 @@ +#include "service.h" +#include "constants.h" +#include "bsd.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "../util/stlutils.h" +#include "../util/log.h" +#include "io_device.h" +#include "io_device_iterator.h" +#include "cfutil.h" + +static void filterByBSDName(struct udev_enumerate* uenum, CFStringRef str); +static void filterByClass(struct udev_enumerate* uenum, CFStringRef str); +static void filterByDriver(struct udev_enumerate* uenum, CFStringRef str); +static void filterByProperties(struct udev_enumerate* uenum, CFDictionaryRef dict); + +io_object_t IOIteratorNext(io_iterator_t iter) +{ + return iter->next(); +} + +static CFMutableDictionaryRef createMatchingDictionary(CFStringRef key, CFStringRef value) +{ + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(dict, key, value); + return dict; +} + +CFMutableDictionaryRef IOServiceMatching(const char* service) +{ + static CFStringRef str = CFSTR(kIOProviderClassKey); + return createMatchingDictionary(str, strToCF(service)); +} + +CFMutableDictionaryRef IOServiceNameMatching(const char* name) +{ + static CFStringRef str = CFSTR(kIONameMatchKey); + return createMatchingDictionary(str, strToCF(name)); +} + +CFMutableDictionaryRef IOBSDNameMatching(void* iokitPort, unsigned int options, const char* bsdName) +{ + static CFStringRef str = CFSTR(kIOBSDNameKey); + return createMatchingDictionary(str, strToCF(bsdName)); +} + +int IOServiceGetMatchingServices(void* port, CFDictionaryRef rules, io_device_iterator_t* iter) +{ + size_t size = CFDictionaryGetCount(rules); + CFStringRef* keys = new CFStringRef[size]; + CFDictionaryGetKeysAndValues(rules, (const void **) keys, nullptr); + struct udev* udev; + struct udev_enumerate* uenum; + struct udev_list_entry* list; + const void* value; + + udev = udev_new(); + uenum = udev_enumerate_new(udev); + + if ((value = CFDictionaryGetValue(rules, CFSTR(kIOProviderClassKey)))) + filterByClass(uenum, (CFStringRef) value); + + if ((value = CFDictionaryGetValue(rules, CFSTR(kIOBSDNameKey)))) + filterByBSDName(uenum, (CFStringRef) value); + + if ((value = CFDictionaryGetValue(rules, CFSTR(kIOPropertyMatchKey)))) + filterByProperties(uenum, (CFDictionaryRef) value); + + delete [] keys; + udev_unref(udev); + + udev_enumerate_scan_devices(uenum); + *iter = new io_device_iterator(uenum); + + return 0; +} + +int IOIteratorIsValid(io_iterator_t iter) +{ + return iter != nullptr; +} + +int IOObjectRelease(io_object_t obj) +{ + delete obj; + return 0; +} + + +int IORegistryEntryCreateCFProperties(io_object_t obj, CFMutableDictionaryRef* props, CFAllocatorRef allocator, int opts) +{ + io_device* dev = dynamic_cast(obj); + + if (!dev) + { + *props = nullptr; + return -4; + } + else + { + *props = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + dev->properties(*props, allocator); + return 0; + } +} + +void filterByClass(struct udev_enumerate* uenum, CFStringRef str) +{ + if (strCFEqual(str, kIOEthernetInterfaceClass)) + udev_enumerate_add_match_subsystem(uenum, "net"); + else if (strCFEqual(str, kIOSerialBSDServiceValue)) + udev_enumerate_add_match_subsystem(uenum, "tty"); + else if (strCFEqual(str, kIOUSBDeviceClassName)) + udev_enumerate_add_match_subsystem(uenum, "usb"); + else if (strCFEqual(str, kIOHIDDeviceKey)) + udev_enumerate_add_match_subsystem(uenum, "hidraw"); +} + +void filterByBSDName(struct udev_enumerate* uenum, CFStringRef str) +{ + std::string translated = DarlingTranslateBSDName([(NSString*) str UTF8String]); + udev_enumerate_add_match_sysname(uenum, translated.c_str()); +} + +void filterByDriver(struct udev_enumerate* uenum, CFStringRef str) +{ + const char* driver = nullptr; + + if (strCFEqual(str, "AppleUSBEHCI")) + driver = "ehci_hcd"; + else if (strCFEqual(str, "AppleUSBOHCI")) + driver = "ohci_hcd"; + else if (strCFEqual(str, "AppleUSBUHCI")) + driver = "uhci_hcd"; + else if (strCFEqual(str, "AppleUSBCDC")) + driver = "cdc_acm"; + else if (strCFEqual(str, "processor")) + driver = "processor"; + else + driver = [(NSString*) str UTF8String]; + + udev_enumerate_add_match_property(uenum, "driver", driver); +} + +void filterByProperties(struct udev_enumerate* uenum, CFDictionaryRef dict) +{ + size_t size = CFDictionaryGetCount(dict); + CFStringRef* keys = new CFStringRef[size]; + CFDictionaryGetKeysAndValues(dict, (const void **) keys, nullptr); + + for (size_t i = 0; i < size; i++) + { + const void* value = CFDictionaryGetValue(dict, keys[i]); + const char* svalue = nullptr; + const char* key = nullptr; + CFStringRef str = keys[i]; + + if (strCFEqual(str, "VendorID")) + { + key = "ID_VENDOR_ID"; + svalue = [(NSString*)value UTF8String]; + } + else if (strCFEqual(str, "ProductID")) + { + key = "ID_MODEL_ID"; + svalue = [(NSString*)value UTF8String]; + } + else if (strCFEqual(str, "IOPrimaryInterface")) + { + key = "INTERFACE"; + svalue = "eth0"; + } + + if (key != nullptr) + udev_enumerate_add_match_property(uenum, key, svalue); + else + LOG << "Ignoring property " << [(NSString*) str UTF8String] << " in IOKit filterByProperties\n"; + } + + delete [] keys; +} + + +