mirror of
https://github.com/ckb-next/ckb-next.git
synced 2026-10-10 12:27:21 -04:00
Merge branch 'fw2' into bugfix
This commit is contained in:
@@ -14,18 +14,18 @@ Hash: SHA256
|
||||
Corsair STRAFE 1.33 http://www3.corsair.com/software/HID/STRAFE.zip 0.2.2 STRAFE_app133_RC.bin a6ea43354810afab6eea1ef41537178c545acbedbd4ebe48dd9d18c61b45c9c1
|
||||
Corsair STRAFERGB 1.33 http://www3.corsair.com/software/HID/STRAFE_RGB.zip 0.2.2 STRAFE_RGB_app133_RC.bin 8526070e4f118137c0bea30805dbdbce5b26beecc3237d65f7d3c10ad7494f1d
|
||||
Corsair M65RGB 1.19 http://www3.corsair.com/software/HID/M65RGB.zip 0.2.0 M65RGB_app119.bin be214ab1f0cc1fc7f785c926c117ef270597f80d1fad9133a730022834267f4f
|
||||
Corsair SCIMITARRGB 1.06 http://www3.corsair.com/software/HID/Scimitar.zip 0.2.2 Scimitar_V106.bin fdc9bc14f6320ff7be47303cae543f1fbe406acb6590333084817da54cf3eb60
|
||||
Corsair SCIMITARRGB 2.01 http://www3.corsair.com/software/HID/Scimitar.zip 0.2.5 Scimitar_App_V201.bin f0e13a5cabdc2bfae00b51d50a369376d0cc19d8e201dcdc4d4ef1d5ac284c53
|
||||
|
||||
!END FW ENTRIES
|
||||
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v2
|
||||
|
||||
iQEcBAEBCAAGBQJWVtkQAAoJEBXtkzSNyNMJHNEIAJieZ1pwU2WJu6GD14lwZEXI
|
||||
/carx6KogdtGbFvs5aE0BnYwq6iL/Q7GvY7KpVmYorJHl9PnwcDPoLe4H3IVYTbi
|
||||
nd0dLaBSGsGFhyR2Uz4aGknEhCQH9qybqrRk6znC0nvksiH/OcvFysqG/sPr9AWN
|
||||
679064VyXwMLUlqCRJtmfIMsb+BH6O/VNsvliIfC0VKsiIyuqaQqZptgpll9lkHL
|
||||
LDBLD514Vkg8z6c/dvUOL8Ey3v4qSGvvfBS36y/qh49Q5EvI7v0xVvNOxudLZ+7k
|
||||
or1PTCFFi03qzeI77XsF7mAyKmh1S2BjkEfAmxPfpl4s/WtkO1opPAtQdprxeDw=
|
||||
=D4qQ
|
||||
iQEcBAEBCAAGBQJWzUYDAAoJEBXtkzSNyNMJBkUH/jY6AgU1b19kEs5ub572cF8H
|
||||
t/IsMdbKKkVj9WYvWXcNW3c72rEN3IzO3Aa5+k/fvgGzkuz1/zHxyWJ6u0q2uKkz
|
||||
B2X7afHgTOZ/vFiuIcJZb2dzA/yTfWk2doUl6trN+nE3u2r2v8xmgiQGH/NaRD27
|
||||
7DS2/Vn8zm6epdUxO/vwyAO3x/oeNpCseWRRMZp/LbgZF6MCcWQ2cVNqcH4Z2z7s
|
||||
nQ4987+v6EX1WHsX2fq4km77wCe35dpCdKSpO2d3xxMkK9FKrftr9f5VhkyY4Yk3
|
||||
eJlHGZ2PAHlUkRs9WVCqoUQjC3HbJbTY6pqyX4VBv87+Wem/SnqASd41lej/yxs=
|
||||
=0giP
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?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>Label</key>
|
||||
<string>com.ckb.daemon</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Applications/ckb.app/Contents/Resources/ckb-daemon</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>ProcessType</key>
|
||||
<string>Interactive</string>
|
||||
<key>LegacyTimers</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Applications/ckb.app/Contents/ckb-daemon.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
+22
-12
@@ -26,23 +26,33 @@ typedef unsigned short ushort;
|
||||
// Gets the index of an object within an array
|
||||
#define INDEX_OF(entry, array) (int)(entry - array)
|
||||
|
||||
// Compile with -DCKB_OUTPUT_TO_STDERR if you want to separate error messages from normal status updates.
|
||||
// (probably not useful because the errors don't mean much without context)
|
||||
#ifdef CKB_OUTPUT_TO_STDERR
|
||||
#define ckb_s_out stdout
|
||||
#define ckb_s_err stderr
|
||||
#else
|
||||
#define ckb_s_out stdout
|
||||
#define ckb_s_err stdout
|
||||
#endif
|
||||
|
||||
// Better __FILE__ macro
|
||||
#define __FILE_NOPATH__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
|
||||
// Output helpers
|
||||
// Use ckb_* to output info or ckb_*_fn to override the file/line numbers (useful when describing where a function was invoked from)
|
||||
#define ckb_fatal_nofile(fmt, args...) fprintf(stderr, "[F] " fmt, ## args)
|
||||
#define ckb_fatal_fn(fmt, file, line, args...) fprintf(stderr, "[F] %s (via %s:%d): " fmt, __func__, file, line, ## args)
|
||||
#define ckb_fatal(fmt, args...) fprintf(stderr, "[F] %s (%s:%d): " fmt, __func__, __FILE_NOPATH__, __LINE__, ## args)
|
||||
#define ckb_err_nofile(fmt, args...) fprintf(stderr, "[E] " fmt, ## args)
|
||||
#define ckb_err_fn(fmt, file, line, args...) fprintf(stderr, "[E] %s (via %s:%d): " fmt, __func__, file, line, ## args)
|
||||
#define ckb_err(fmt, args...) fprintf(stderr, "[E] %s (%s:%d): " fmt, __func__, __FILE_NOPATH__, __LINE__, ## args)
|
||||
#define ckb_warn_nofile(fmt, args...) printf("[W] " fmt, ## args)
|
||||
#define ckb_warn_fn(fmt, file, line, args...) printf("[W] %s (via %s:%d): " fmt, __func__, file, line, ## args)
|
||||
#define ckb_warn(fmt, args...) printf("[W] %s (%s:%d): " fmt, __func__, __FILE_NOPATH__, __LINE__, ## args)
|
||||
#define ckb_info_nofile(fmt, args...) printf("[I] " fmt, ## args)
|
||||
#define ckb_info_fn(fmt, file, line, args...) printf("[I] " fmt, ## args)
|
||||
#define ckb_info(fmt, args...) printf("[I] " fmt, ## args)
|
||||
#define ckb_fatal_nofile(fmt, args...) fprintf(ckb_s_err, "[F] " fmt, ## args)
|
||||
#define ckb_fatal_fn(fmt, file, line, args...) fprintf(ckb_s_err, "[F] %s (via %s:%d): " fmt, __func__, file, line, ## args)
|
||||
#define ckb_fatal(fmt, args...) fprintf(ckb_s_err, "[F] %s (%s:%d): " fmt, __func__, __FILE_NOPATH__, __LINE__, ## args)
|
||||
#define ckb_err_nofile(fmt, args...) fprintf(ckb_s_err, "[E] " fmt, ## args)
|
||||
#define ckb_err_fn(fmt, file, line, args...) fprintf(ckb_s_err, "[E] %s (via %s:%d): " fmt, __func__, file, line, ## args)
|
||||
#define ckb_err(fmt, args...) fprintf(ckb_s_err, "[E] %s (%s:%d): " fmt, __func__, __FILE_NOPATH__, __LINE__, ## args)
|
||||
#define ckb_warn_nofile(fmt, args...) fprintf(ckb_s_out, "[W] " fmt, ## args)
|
||||
#define ckb_warn_fn(fmt, file, line, args...) fprintf(ckb_s_out, "[W] %s (via %s:%d): " fmt, __func__, file, line, ## args)
|
||||
#define ckb_warn(fmt, args...) fprintf(ckb_s_out, "[W] %s (%s:%d): " fmt, __func__, __FILE_NOPATH__, __LINE__, ## args)
|
||||
#define ckb_info_nofile(fmt, args...) fprintf(ckb_s_out, "[I] " fmt, ## args)
|
||||
#define ckb_info_fn(fmt, file, line, args...) fprintf(ckb_s_out, "[I] " fmt, ## args)
|
||||
#define ckb_info(fmt, args...) fprintf(ckb_s_out, "[I] " fmt, ## args)
|
||||
|
||||
// Timespec utilities
|
||||
void timespec_add(struct timespec* timespec, long nanoseconds);
|
||||
|
||||
@@ -395,7 +395,12 @@ void corsair_kbcopy(unsigned char* kbinput, const unsigned char* urbinput){
|
||||
memcpy(kbinput, urbinput, N_KEYBYTES_HW);
|
||||
}
|
||||
|
||||
void corsair_mousecopy(unsigned char* kbinput, const unsigned char* urbinput){
|
||||
void corsair_mousecopy(unsigned char* kbinput, int endpoint, const unsigned char* urbinput){
|
||||
if(endpoint == 2){
|
||||
if(urbinput[0] != 3)
|
||||
return;
|
||||
urbinput++;
|
||||
}
|
||||
for(int bit = BUTTON_HID_COUNT; bit < N_BUTTONS_HW; bit++){
|
||||
int byte = bit / 8;
|
||||
uchar test = 1 << (bit % 8);
|
||||
|
||||
@@ -68,6 +68,6 @@ void hid_mouse_translate(unsigned char* kbinput, short* xaxis, short* yaxis, int
|
||||
|
||||
// Copies input from Corsair reports
|
||||
void corsair_kbcopy(unsigned char* kbinput, const unsigned char* urbinput);
|
||||
void corsair_mousecopy(unsigned char* kbinput, const unsigned char* urbinput);
|
||||
void corsair_mousecopy(unsigned char* kbinput, int endpoint, const unsigned char* urbinput);
|
||||
|
||||
#endif // KEYMAP_H
|
||||
|
||||
@@ -66,8 +66,11 @@ void localecase(char* dst, size_t length, const char* src){
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
printf(" ckb: Corsair RGB driver %s\n", CKB_VERSION_STR);
|
||||
// Set output pipes to buffer on newlines, if they weren't set that way already
|
||||
setlinebuf(stdout);
|
||||
setlinebuf(stderr);
|
||||
|
||||
printf(" ckb: Corsair RGB driver %s\n", CKB_VERSION_STR);
|
||||
// If --help occurs anywhere in the command-line, don't launch the program but instead print usage
|
||||
for(int i = 1; i < argc; i++){
|
||||
if(!strcmp(argv[i], "--help")){
|
||||
|
||||
+4
-1
@@ -50,9 +50,12 @@
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
#include <IOKit/hidsystem/IOHIDLib.h>
|
||||
#include <IOKit/hidsystem/ev_keymap.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/usb/USB.h>
|
||||
|
||||
typedef IOHIDDeviceDeviceInterface** hid_dev_t;
|
||||
typedef IOHIDDeviceDeviceInterface** hid_dev_t;
|
||||
typedef IOUSBDeviceInterface182** usb_dev_t;
|
||||
typedef IOUSBInterfaceInterface183** usb_iface_t;
|
||||
|
||||
// The OSX process needs to change its EUID to post events, so thread safety must be ensured
|
||||
// On Linux the EUID is always root
|
||||
|
||||
@@ -173,6 +173,7 @@ extern const union devcmd vtable_mouse;
|
||||
#define KB_NAME_LEN 34
|
||||
#define SERIAL_LEN 34
|
||||
#define MSG_SIZE 64
|
||||
#define IFACE_MAX 4
|
||||
typedef struct {
|
||||
// Function table (see command.h)
|
||||
const union devcmd* vtable;
|
||||
@@ -180,25 +181,37 @@ typedef struct {
|
||||
// Note that FDs have 1 added to them, because these are initialized to zero when the program starts but zero is technically a valid FD
|
||||
// So the actual value is (fd - 1)
|
||||
#ifdef OS_LINUX
|
||||
// USB device
|
||||
struct udev_device* udev;
|
||||
int handle;
|
||||
// uinput handles
|
||||
int uinput_kb, uinput_mouse;
|
||||
#else
|
||||
// USB identifier
|
||||
long location_id;
|
||||
// Device handles
|
||||
usb_dev_t handle;
|
||||
usb_iface_t ifusb[IFACE_MAX];
|
||||
hid_dev_t ifhid[IFACE_MAX];
|
||||
io_object_t rm_notify[IFACE_MAX * 2 + 1]; // one for each: handle, ifusb, ifhid
|
||||
int epcount_hid, epcount_usb;
|
||||
// Result code from the last USB transfer
|
||||
kern_return_t lastresult;
|
||||
// Input handle
|
||||
io_connect_t event;
|
||||
// Key-repeat info
|
||||
CFRunLoopRef input_loop;
|
||||
CFRunLoopTimerRef krtimer;
|
||||
struct timespec keyrepeat;
|
||||
long location_id;
|
||||
hid_dev_t handle;
|
||||
hid_dev_t handles[4];
|
||||
io_object_t rm_notify[4];
|
||||
kern_return_t lastresult;
|
||||
io_connect_t event;
|
||||
IOOptionBits modifiers;
|
||||
short lastkeypress;
|
||||
// Modifier/mouse button state
|
||||
pthread_t indicthread;
|
||||
IOOptionBits modifiers;
|
||||
uchar mousestate;
|
||||
char scroll_rate;
|
||||
pthread_t indicthread;
|
||||
#endif
|
||||
// Number of endpoints on the USB device
|
||||
int epcount;
|
||||
// Thread used for USB/devnode communication. To close: lock mutexes, set handle to zero, unlock, then wait for thread to stop
|
||||
pthread_t thread;
|
||||
// Thread for device input. Will close on its own when the device is terminated.
|
||||
|
||||
+36
-27
@@ -13,13 +13,13 @@ int os_usbsend(usbdevice* kb, const uchar* out_msg, int is_recv, const char* fil
|
||||
if(kb->fwversion >= 0x120 && !is_recv){
|
||||
struct usbdevfs_bulktransfer transfer;
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
transfer.ep = (kb->fwversion >= 0x130) ? 4 : 3;
|
||||
transfer.ep = (kb->fwversion >= 0x130 && !IS_MOUSE_DEV(kb)) ? 4 : 3;
|
||||
transfer.len = MSG_SIZE;
|
||||
transfer.timeout = 5000;
|
||||
transfer.data = (void*)out_msg;
|
||||
res = ioctl(kb->handle - 1, USBDEVFS_BULK, &transfer);
|
||||
} else {
|
||||
struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0300, 0x03, MSG_SIZE, 5000, (void*)out_msg };
|
||||
struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0200, kb->epcount - 1, MSG_SIZE, 5000, (void*)out_msg };
|
||||
res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer);
|
||||
}
|
||||
if(res <= 0){
|
||||
@@ -51,11 +51,11 @@ int os_usbrecv(usbdevice* kb, uchar* in_msg, const char* file, int line){
|
||||
transfer.data = in_msg;
|
||||
res = ioctl(kb->handle - 1, USBDEVFS_BULK, &transfer);
|
||||
} else {*/
|
||||
struct usbdevfs_ctrltransfer transfer = { 0xa1, 0x01, 0x0300, 0x03, MSG_SIZE, 5000, in_msg };
|
||||
struct usbdevfs_ctrltransfer transfer = { 0xa1, 0x01, 0x0300, kb->epcount - 1, MSG_SIZE, 5000, in_msg };
|
||||
res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer);
|
||||
//}
|
||||
if(res <= 0){
|
||||
ckb_err_fn("%s\n", file, line, res ? strerror(errno) : "No data written");
|
||||
ckb_err_fn("%s\n", file, line, res ? strerror(errno) : "No data read");
|
||||
if(res == -1 && errno == ETIMEDOUT)
|
||||
return -1;
|
||||
else
|
||||
@@ -91,8 +91,9 @@ void* os_inputmain(void* context){
|
||||
int index = INDEX_OF(kb, keyboard);
|
||||
ckb_info("Starting input thread for %s%d\n", devpath, index);
|
||||
|
||||
// Monitor input transfers on all endpoints
|
||||
const int urbcount = 3;
|
||||
// Monitor input transfers on all endpoints for non-RGB devices
|
||||
// For RGB, monitor all but the last, as it's used for input/output
|
||||
int urbcount = IS_RGB(vendor, product) ? (kb->epcount - 1) : kb->epcount;
|
||||
struct usbdevfs_urb urbs[urbcount];
|
||||
memset(urbs, 0, sizeof(urbs));
|
||||
urbs[0].buffer_length = 8;
|
||||
@@ -102,6 +103,8 @@ void* os_inputmain(void* context){
|
||||
else
|
||||
urbs[1].buffer_length = 21;
|
||||
urbs[2].buffer_length = MSG_SIZE;
|
||||
if(urbcount != 3)
|
||||
urbs[urbcount - 1].buffer_length = MSG_SIZE;
|
||||
} else {
|
||||
urbs[1].buffer_length = 4;
|
||||
urbs[2].buffer_length = 15;
|
||||
@@ -133,13 +136,16 @@ void* os_inputmain(void* context){
|
||||
// Process input (if any)
|
||||
pthread_mutex_lock(imutex(kb));
|
||||
if(IS_MOUSE(vendor, product)){
|
||||
switch(urb->endpoint){
|
||||
case 0x82:
|
||||
// RGB mouse input
|
||||
switch(urb->actual_length){
|
||||
case 8:
|
||||
case 10:
|
||||
case 11:
|
||||
// HID mouse input
|
||||
hid_mouse_translate(kb->input.keys, &kb->input.rel_x, &kb->input.rel_y, -(urb->endpoint & 0xF), urb->actual_length, urb->buffer);
|
||||
break;
|
||||
case 0x83:
|
||||
corsair_mousecopy(kb->input.keys, urb->buffer);
|
||||
case MSG_SIZE:
|
||||
// Corsair mouse input
|
||||
corsair_mousecopy(kb->input.keys, urb->endpoint & 0xF, urb->buffer);
|
||||
break;
|
||||
}
|
||||
} else if(IS_RGB(vendor, product)){
|
||||
@@ -177,9 +183,9 @@ void* os_inputmain(void* context){
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbunclaim(usbdevice* kb, int resetting, int rgb){
|
||||
int usbunclaim(usbdevice* kb, int resetting){
|
||||
int handle = kb->handle - 1;
|
||||
int count = (rgb) ? 4 : 3;
|
||||
int count = kb->epcount;
|
||||
for(int i = 0; i < count; i++)
|
||||
ioctl(handle, USBDEVFS_RELEASEINTERFACE, &i);
|
||||
// For RGB keyboards, the kernel driver should only be reconnected to interfaces 0 and 1 (HID), and only if we're not about to do a USB reset.
|
||||
@@ -189,10 +195,8 @@ int usbunclaim(usbdevice* kb, int resetting, int rgb){
|
||||
ioctl(handle, USBDEVFS_IOCTL, &ctl);
|
||||
ctl.ifno = 1;
|
||||
ioctl(handle, USBDEVFS_IOCTL, &ctl);
|
||||
// Reconnect all handles for non-RGB keyboards
|
||||
// Also reconnect iface #2 (HID) for non-RGB keyboards
|
||||
if(!HAS_FEATURES(kb, FEAT_RGB)){
|
||||
ctl.ifno = 0;
|
||||
ioctl(handle, USBDEVFS_IOCTL, &ctl);
|
||||
ctl.ifno = 2;
|
||||
ioctl(handle, USBDEVFS_IOCTL, &ctl);
|
||||
}
|
||||
@@ -202,7 +206,7 @@ int usbunclaim(usbdevice* kb, int resetting, int rgb){
|
||||
|
||||
void os_closeusb(usbdevice* kb){
|
||||
if(kb->handle){
|
||||
usbunclaim(kb, 0, HAS_FEATURES(kb, FEAT_RGB));
|
||||
usbunclaim(kb, 0);
|
||||
close(kb->handle - 1);
|
||||
}
|
||||
if(kb->udev)
|
||||
@@ -212,12 +216,8 @@ void os_closeusb(usbdevice* kb){
|
||||
kbsyspath[INDEX_OF(kb, keyboard)][0] = 0;
|
||||
}
|
||||
|
||||
int usbclaim(usbdevice* kb, int rgb){
|
||||
// 0 is for BIOS mode/non-RGB key input
|
||||
// 1 is for standard HID key input
|
||||
// 2 is for Corsair input
|
||||
// 3 is for the LED and board controller (not present on non-RGB models)
|
||||
int count = (rgb) ? 4 : 3;
|
||||
int usbclaim(usbdevice* kb){
|
||||
int count = kb->epcount;
|
||||
for(int i = 0; i < count; i++){
|
||||
struct usbdevfs_ioctl ctl = { i, USBDEVFS_DISCONNECT, 0 };
|
||||
if((ioctl(kb->handle - 1, USBDEVFS_IOCTL, &ctl) && errno != ENODATA)
|
||||
@@ -236,9 +236,9 @@ int usbclaim(usbdevice* kb, int rgb){
|
||||
}
|
||||
|
||||
int os_resetusb(usbdevice* kb, const char* file, int line){
|
||||
TEST_RESET(usbunclaim(kb, 1, HAS_FEATURES(kb, FEAT_RGB)));
|
||||
TEST_RESET(usbunclaim(kb, 1));
|
||||
TEST_RESET(ioctl(kb->handle - 1, USBDEVFS_RESET));
|
||||
TEST_RESET(usbclaim(kb, HAS_FEATURES(kb, FEAT_RGB)));
|
||||
TEST_RESET(usbclaim(kb));
|
||||
// Success!
|
||||
return 0;
|
||||
}
|
||||
@@ -282,8 +282,17 @@ int os_setupusb(usbdevice* kb){
|
||||
ckb_info("Connecting %s at %s%d\n", kb->name, devpath, index);
|
||||
|
||||
// Claim the USB interfaces
|
||||
if(usbclaim(kb, HAS_FEATURES(kb, FEAT_RGB))){
|
||||
ckb_err("Failed to claim interface: %s\n", strerror(errno));
|
||||
const char* ep_str = udev_device_get_sysattr_value(dev, "bNumInterfaces");
|
||||
kb->epcount = 0;
|
||||
if(ep_str)
|
||||
sscanf(ep_str, "%d", &kb->epcount);
|
||||
if(kb->epcount == 0){
|
||||
// This shouldn't happen, but if it does, assume EP count based on what the device is supposed to have
|
||||
kb->epcount = (HAS_FEATURES(kb, FEAT_RGB) ? 4 : 3);
|
||||
ckb_warn("Unable to read endpoint count from udev, assuming %d...\n", kb->epcount);
|
||||
}
|
||||
if(usbclaim(kb)){
|
||||
ckb_err("Failed to claim interfaces: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
+564
-208
@@ -6,16 +6,104 @@
|
||||
|
||||
#ifdef OS_MAC
|
||||
|
||||
#define INCOMPLETE (hid_dev_t)-1l
|
||||
static CFRunLoopRef mainloop = 0;
|
||||
static IONotificationPortRef notify = 0;
|
||||
|
||||
static long hidgetlong(hid_dev_t handle, CFStringRef key){
|
||||
long raw = 0;
|
||||
CFTypeRef cf;
|
||||
if((*handle)->getProperty(handle, key, &cf) != kIOReturnSuccess)
|
||||
return 0;
|
||||
if(!cf || CFGetTypeID(cf) != CFNumberGetTypeID() || !CFNumberGetValue(cf, kCFNumberLongType, &raw))
|
||||
return 0;
|
||||
return raw;
|
||||
}
|
||||
|
||||
static void hidgetstr(hid_dev_t handle, CFStringRef key, char* output, int out_len){
|
||||
CFTypeRef cf;
|
||||
if((*handle)->getProperty(handle, key, &cf) != kIOReturnSuccess){
|
||||
output[0] = 0;
|
||||
return;
|
||||
}
|
||||
if(!cf || CFGetTypeID(cf) != CFStringGetTypeID() || !CFStringGetCString(cf, output, out_len, kCFStringEncodingASCII))
|
||||
output[0] = 0;
|
||||
}
|
||||
|
||||
// profile.c
|
||||
void u16dec(ushort* in, char* out, size_t* srclen, size_t* dstlen);
|
||||
|
||||
static void usbgetstr(usb_dev_t handle, uint8 string_index, char* output, int out_len){
|
||||
// Make a temporary buffer so the request won't fail if too large
|
||||
char buffer[256] = { 0 };
|
||||
IOUSBDevRequest rq = { 0x80, 0x06, 0x0300 | string_index, 0x0409, sizeof(buffer) - 2, buffer, 0};
|
||||
kern_return_t res = (*handle)->DeviceRequest(handle, &rq);
|
||||
if(res != kIOReturnSuccess){
|
||||
output[0] = 0;
|
||||
return;
|
||||
}
|
||||
size_t inl = sizeof(buffer) / 2, outl = out_len;
|
||||
u16dec((ushort*)buffer + 1, output, &inl, &outl);
|
||||
output[out_len - 1] = 0;
|
||||
}
|
||||
|
||||
#define INCOMPLETE (usb_dev_t)-1l
|
||||
#define HAS_ALL_HANDLES(kb) ((kb)->epcount > 0 && (kb)->epcount_hid + (kb)->epcount_usb >= (kb)->epcount)
|
||||
|
||||
// Hacky way of trying something over and over again until it works. 100ms intervals, max 1s
|
||||
#define wait_loop(error, operation) do { \
|
||||
int trial = 0; \
|
||||
while(((error) = (operation)) != kIOReturnSuccess){ \
|
||||
if(++trial == 10) \
|
||||
break; \
|
||||
usleep(100000); \
|
||||
} } while(0)
|
||||
|
||||
#define IS_TEMP_FAILURE(res) ((res) == kIOUSBTransactionTimeout || (res) == kIOUSBTransactionReturned || (res) == kIOUSBPipeStalled)
|
||||
#define IS_DISCONNECT_FAILURE(res) ((res) == kIOReturnBadArgument || (res) == kIOReturnNoDevice || (res) == kIOReturnNotOpen || (res) == kIOReturnNotAttached || (res) == kIOReturnExclusiveAccess)
|
||||
|
||||
// When reading/writing USB handles we have to ensure we select the correct pipe, else it will fail
|
||||
static int get_pipe_index(usb_iface_t handle, int desired_direction){
|
||||
uchar count;
|
||||
if((*handle)->GetNumEndpoints(handle, &count) != kIOReturnSuccess)
|
||||
return 1;
|
||||
for(int i = 0; i < count; i++){
|
||||
uchar direction, number, transfertype, interval;
|
||||
ushort maxpacketsize;
|
||||
if((*handle)->GetPipeProperties(handle, i + 1, &direction, &number, &transfertype, &maxpacketsize, &interval) == kIOReturnSuccess){
|
||||
if(direction == desired_direction)
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
// Not found - default to 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
int os_usbsend(usbdevice* kb, const uchar* out_msg, int is_recv, const char* file, int line){
|
||||
// Firmware versions above 1.20 use Output instead of Feature reports for improved performance
|
||||
// It doesn't work well when receiving data, however
|
||||
IOHIDReportType type = (kb->fwversion >= 0x120 && !is_recv ? kIOHIDReportTypeOutput : kIOHIDReportTypeFeature);
|
||||
kern_return_t res = (*kb->handle)->setReport(kb->handle, type, 0, out_msg, MSG_SIZE, 5000, 0, 0, 0);
|
||||
kern_return_t res = kIOReturnSuccess;
|
||||
if(kb->fwversion < 0x120 || is_recv){
|
||||
int ep = kb->epcount;
|
||||
// For old devices, or for receiving data, use control transfers
|
||||
IOUSBDevRequestTO rq = { 0x21, 0x09, 0x0200, ep - 1, MSG_SIZE, (void*)out_msg, 0, 5000, 5000 };
|
||||
res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
|
||||
if(res == kIOReturnNotOpen){
|
||||
// Device handle not open - try to send directly to the endpoint instead
|
||||
usb_iface_t h_usb = kb->ifusb[ep - 1];
|
||||
hid_dev_t h_hid = kb->ifhid[ep - 1];
|
||||
if(h_usb)
|
||||
res = (*h_usb)->ControlRequestTO(h_usb, 0, &rq);
|
||||
else if(h_hid)
|
||||
res = (*h_hid)->setReport(h_hid, kIOHIDReportTypeFeature, 0, out_msg, MSG_SIZE, 5000, 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
// For newer devices, use interrupt transfers
|
||||
int ep = (kb->fwversion >= 0x130 && !IS_MOUSE_DEV(kb)) ? 4 : 3;
|
||||
usb_iface_t h_usb = kb->ifusb[ep - 1];
|
||||
hid_dev_t h_hid = kb->ifhid[ep - 1];
|
||||
if(h_usb)
|
||||
res = (*h_usb)->WritePipe(h_usb, get_pipe_index(h_usb, kUSBOut), (void*)out_msg, MSG_SIZE);
|
||||
else if(h_hid)
|
||||
res = (*h_hid)->setReport(h_hid, kIOHIDReportTypeOutput, 0, out_msg, MSG_SIZE, 5000, 0, 0, 0);
|
||||
}
|
||||
kb->lastresult = res;
|
||||
if(res != kIOReturnSuccess){
|
||||
ckb_err_fn("Got return value 0x%x\n", file, line, res);
|
||||
@@ -28,8 +116,19 @@ int os_usbsend(usbdevice* kb, const uchar* out_msg, int is_recv, const char* fil
|
||||
}
|
||||
|
||||
int os_usbrecv(usbdevice* kb, uchar* in_msg, const char* file, int line){
|
||||
CFIndex length = MSG_SIZE;
|
||||
kern_return_t res = (*kb->handle)->getReport(kb->handle, kIOHIDReportTypeFeature, 0, in_msg, &length, 5000, 0, 0, 0);
|
||||
int ep = kb->epcount;
|
||||
IOUSBDevRequestTO rq = { 0xa1, 0x01, 0x0200, ep - 1, MSG_SIZE, in_msg, 0, 5000, 5000 };
|
||||
kern_return_t res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
|
||||
CFIndex length = rq.wLenDone;
|
||||
if(res == kIOReturnNotOpen){
|
||||
// Device handle not open - try to send directly to the endpoint instead
|
||||
usb_iface_t h_usb = kb->ifusb[ep - 1];
|
||||
hid_dev_t h_hid = kb->ifhid[ep - 1];
|
||||
if(h_usb)
|
||||
res = (*h_usb)->ControlRequestTO(h_usb, 0, &rq);
|
||||
else if(h_hid)
|
||||
res = (*h_hid)->getReport(h_hid, kIOHIDReportTypeFeature, 0, in_msg, &length, 5000, 0, 0, 0);
|
||||
}
|
||||
kb->lastresult = res;
|
||||
if(res != kIOReturnSuccess){
|
||||
ckb_err_fn("Got return value 0x%x\n", file, line, res);
|
||||
@@ -39,38 +138,53 @@ int os_usbrecv(usbdevice* kb, uchar* in_msg, const char* file, int line){
|
||||
return 0;
|
||||
}
|
||||
if(length != MSG_SIZE)
|
||||
ckb_err_fn("Read %d bytes (expected %d)\n", file, line, (int)length, MSG_SIZE);
|
||||
ckb_err_fn("Read %ld bytes (expected %d)\n", file, line, length, MSG_SIZE);
|
||||
return length;
|
||||
}
|
||||
|
||||
int _nk95cmd(usbdevice* kb, uchar bRequest, ushort wValue, const char* file, int line){
|
||||
// TODO: stub
|
||||
IOUSBDevRequestTO rq = { 0x40, bRequest, wValue, 0, 0, 0, 0, 5000, 5000 };
|
||||
kern_return_t res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
|
||||
if(res != kIOReturnSuccess){
|
||||
ckb_err_fn("Got return value 0x%x\n", file, line, res);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_sendindicators(usbdevice* kb){
|
||||
uchar ileds = kb->ileds;
|
||||
// Get a list of LED elements from handle 0
|
||||
long ledpage = kHIDPage_LEDs;
|
||||
const void* keys[] = { CFSTR(kIOHIDElementUsagePageKey) };
|
||||
const void* values[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &ledpage) };
|
||||
CFDictionaryRef matching = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFRelease(values[0]);
|
||||
CFArrayRef leds;
|
||||
kern_return_t res = (*kb->handles[0])->copyMatchingElements(kb->handles[0], matching, &leds, 0);
|
||||
CFRelease(matching);
|
||||
if(res != kIOReturnSuccess)
|
||||
return;
|
||||
// Iterate through them and update the LEDs which have changed
|
||||
CFIndex count = CFArrayGetCount(leds);
|
||||
for(CFIndex i = 0; i < count; i++){
|
||||
IOHIDElementRef led = (void*)CFArrayGetValueAtIndex(leds, i);
|
||||
uint32_t usage = IOHIDElementGetUsage(led);
|
||||
IOHIDValueRef value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, led, 0, !!(ileds & (1 << (usage - 1))));
|
||||
(*kb->handles[0])->setValue(kb->handles[0], led, value, 5000, 0, 0, 0);
|
||||
CFRelease(value);
|
||||
IOUSBDevRequestTO rq = { 0x21, 0x09, 0x0200, 0x00, 1, &kb->ileds, 0, 500, 500 };
|
||||
kern_return_t res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
|
||||
if(res == kIOReturnNotOpen){
|
||||
// Handle not open - try to go through the HID system instead
|
||||
hid_dev_t handle = kb->ifhid[0];
|
||||
if(handle){
|
||||
uchar ileds = kb->ileds;
|
||||
// Get a list of LED elements from handle 0
|
||||
long ledpage = kHIDPage_LEDs;
|
||||
const void* keys[] = { CFSTR(kIOHIDElementUsagePageKey) };
|
||||
const void* values[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &ledpage) };
|
||||
CFDictionaryRef matching = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFRelease(values[0]);
|
||||
CFArrayRef leds;
|
||||
kern_return_t res = (*handle)->copyMatchingElements(handle, matching, &leds, 0);
|
||||
CFRelease(matching);
|
||||
if(res != kIOReturnSuccess)
|
||||
return;
|
||||
// Iterate through them and update the LEDs which have changed
|
||||
CFIndex count = CFArrayGetCount(leds);
|
||||
for(CFIndex i = 0; i < count; i++){
|
||||
IOHIDElementRef led = (void*)CFArrayGetValueAtIndex(leds, i);
|
||||
uint32_t usage = IOHIDElementGetUsage(led);
|
||||
IOHIDValueRef value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, led, 0, !!(ileds & (1 << (usage - 1))));
|
||||
(*handle)->setValue(handle, led, value, 5000, 0, 0, 0);
|
||||
CFRelease(value);
|
||||
}
|
||||
CFRelease(leds);
|
||||
}
|
||||
}
|
||||
CFRelease(leds);
|
||||
if(res != kIOReturnSuccess)
|
||||
ckb_err("Got return value 0x%x\n", res);
|
||||
}
|
||||
|
||||
int os_resetusb(usbdevice* kb, const char* file, int line){
|
||||
@@ -78,12 +192,8 @@ int os_resetusb(usbdevice* kb, const char* file, int line){
|
||||
if(IS_DISCONNECT_FAILURE(res))
|
||||
// Don't try if the keyboard was disconnected
|
||||
return -2;
|
||||
// Reset all handles
|
||||
int count = IS_RGB(kb->vendor, kb->product) ? 4 : 3;
|
||||
for(int i = 0; i < count; i++){
|
||||
if((*kb->handles[i])->setProperty(kb->handles[i], CFSTR(kIOHIDResetKey), kCFBooleanTrue) != kIOReturnSuccess)
|
||||
return -1;
|
||||
}
|
||||
// Otherwise, just wait and try again
|
||||
usleep(100000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -91,10 +201,17 @@ static void intreport(void* context, IOReturn result, void* sender, IOHIDReportT
|
||||
usbdevice* kb = context;
|
||||
pthread_mutex_lock(imutex(kb));
|
||||
if(IS_MOUSE(kb->vendor, kb->product)){
|
||||
if(length == 10)
|
||||
switch(length){
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 11:
|
||||
hid_mouse_translate(kb->input.keys, &kb->input.rel_x, &kb->input.rel_y, -2, length, data);
|
||||
else if(length == MSG_SIZE)
|
||||
corsair_mousecopy(kb->input.keys, data);
|
||||
break;
|
||||
case MSG_SIZE:
|
||||
corsair_mousecopy(kb->input.keys, kb->ifhid[3] ? 3 : 2, data);
|
||||
break;
|
||||
}
|
||||
} else if(HAS_FEATURES(kb, FEAT_RGB)){
|
||||
switch(length){
|
||||
case 8:
|
||||
@@ -132,18 +249,48 @@ static void intreport(void* context, IOReturn result, void* sender, IOHIDReportT
|
||||
pthread_mutex_unlock(imutex(kb));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
usbdevice* kb;
|
||||
uchar* buffer;
|
||||
int index;
|
||||
int pipe;
|
||||
int maxsize;
|
||||
} urbctx;
|
||||
|
||||
static void pipecomplete(void* refcon, IOReturn result, void* arg0){
|
||||
if(result != kIOReturnSuccess)
|
||||
return;
|
||||
intptr_t length = (intptr_t)arg0;
|
||||
urbctx* ctx = refcon;
|
||||
// Wrap the URB to the interrupt report handler
|
||||
usbdevice* kb = ctx->kb;
|
||||
uchar* buffer = ctx->buffer;
|
||||
intreport(kb, result, 0, 0, 0, buffer, length);
|
||||
// Re-submit the URB to read the next packet
|
||||
usb_iface_t handle = kb->ifusb[ctx->index];
|
||||
(*handle)->ReadPipeAsync(handle, ctx->pipe, buffer, ctx->maxsize, pipecomplete, ctx);
|
||||
}
|
||||
|
||||
// input_mac.c
|
||||
extern void keyretrigger(CFRunLoopTimerRef timer, void* info);
|
||||
|
||||
void* os_inputmain(void* context){
|
||||
usbdevice* kb = context;
|
||||
int index = INDEX_OF(kb, keyboard);
|
||||
// Monitor input transfers on all endpoints for non-RGB devices
|
||||
// For RGB, monitor all but the last, as it's used for input/output
|
||||
int count = IS_RGB_DEV(kb) ? (kb->epcount - 1) : kb->epcount;
|
||||
// Schedule async events for the device on this thread
|
||||
CFRunLoopRef runloop = kb->input_loop = CFRunLoopGetCurrent();
|
||||
int count = (IS_RGB(kb->vendor, kb->product)) ? 4 : 3;
|
||||
for(int i = 0; i < count; i++){
|
||||
CFTypeRef eventsource;
|
||||
kern_return_t res = (*kb->handles[i])->getAsyncEventSource(kb->handles[i], &eventsource);
|
||||
kern_return_t res;
|
||||
if(kb->ifusb[i])
|
||||
res = (*kb->ifusb[i])->CreateInterfaceAsyncEventSource(kb->ifusb[i], (CFRunLoopSourceRef*)&eventsource);
|
||||
else if(kb->ifhid[i])
|
||||
res = (*kb->ifhid[i])->getAsyncEventSource(kb->ifhid[i], &eventsource);
|
||||
else
|
||||
continue;
|
||||
if(res != kIOReturnSuccess){
|
||||
ckb_err("Failed to start input thread for %s%d: %x\n", devpath, index, res);
|
||||
return 0;
|
||||
@@ -156,14 +303,33 @@ void* os_inputmain(void* context){
|
||||
ckb_info("Starting input thread for %s%d\n", devpath, index);
|
||||
|
||||
// Start getting reports
|
||||
uint8_t* urbinput[] = { malloc(8), malloc(32), malloc(MSG_SIZE) };
|
||||
(*kb->handles[0])->setInputReportCallback(kb->handles[0], urbinput[0], 8, intreport, kb, 0);
|
||||
if(IS_RGB(kb->vendor, kb->product)){
|
||||
(*kb->handles[1])->setInputReportCallback(kb->handles[1], urbinput[1], 21, intreport, kb, 0);
|
||||
(*kb->handles[2])->setInputReportCallback(kb->handles[2], urbinput[2], MSG_SIZE, intreport, kb, 0);
|
||||
} else {
|
||||
(*kb->handles[1])->setInputReportCallback(kb->handles[1], urbinput[1], 4, intreport, kb, 0);
|
||||
(*kb->handles[2])->setInputReportCallback(kb->handles[2], urbinput[2], 15, intreport, kb, 0);
|
||||
urbctx input[IFACE_MAX];
|
||||
memset(input, 0, sizeof(input));
|
||||
for(int i = 0; i < count; i++){
|
||||
if(kb->ifusb[i]){
|
||||
ckb_info("Reading ckb%d.ifusb[%d]\n", index, i);
|
||||
usb_iface_t handle = kb->ifusb[i];
|
||||
int pipe = get_pipe_index(handle, kUSBIn);
|
||||
uchar direction, number, transfertype, interval;
|
||||
ushort maxpacketsize;
|
||||
(*handle)->GetPipeProperties(handle, pipe, &direction, &number, &transfertype, &maxpacketsize, &interval);
|
||||
if(direction != kUSBIn && direction != kUSBAnyDirn)
|
||||
continue;
|
||||
uchar* buffer = malloc(maxpacketsize);
|
||||
input[i].kb = kb;
|
||||
input[i].buffer = buffer;
|
||||
input[i].index = i;
|
||||
input[i].pipe = pipe;
|
||||
input[i].maxsize = maxpacketsize;
|
||||
(*handle)->ReadPipeAsync(handle, 1, buffer, maxpacketsize, pipecomplete, input + i);
|
||||
} else if(kb->ifhid[i]){
|
||||
ckb_info("Reading ckb%d.ifhid[%d]\n", index, i);
|
||||
hid_dev_t handle = kb->ifhid[i];
|
||||
long maxsize = hidgetlong(handle, CFSTR(kIOHIDMaxInputReportSizeKey));
|
||||
uchar* buffer = malloc(maxsize);
|
||||
input[i].buffer = buffer;
|
||||
(*handle)->setInputReportCallback(handle, buffer, maxsize, intreport, kb, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Start a timer for key repeat broadcasts
|
||||
@@ -190,154 +356,54 @@ void* os_inputmain(void* context){
|
||||
|
||||
// Clean up
|
||||
ckb_info("Stopping input thread for %s%d\n", devpath, index);
|
||||
free(urbinput[0]);
|
||||
free(urbinput[1]);
|
||||
free(urbinput[2]);
|
||||
for(int i = 0; i < count; i++)
|
||||
free(input[i].buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long usbgetlong(hid_dev_t handle, CFStringRef key){
|
||||
long raw = 0;
|
||||
CFTypeRef cf;
|
||||
if((*handle)->getProperty(handle, key, &cf) != kIOReturnSuccess)
|
||||
return 0;
|
||||
if(!cf || CFGetTypeID(cf) != CFNumberGetTypeID() || !CFNumberGetValue(cf, kCFNumberLongType, &raw))
|
||||
return 0;
|
||||
return raw;
|
||||
}
|
||||
|
||||
static void usbgetstr(hid_dev_t handle, CFStringRef key, char* output, int out_len){
|
||||
CFTypeRef cf;
|
||||
if((*handle)->getProperty(handle, key, &cf) != kIOReturnSuccess){
|
||||
output[0] = 0;
|
||||
return;
|
||||
}
|
||||
if(!cf || CFGetTypeID(cf) != CFStringGetTypeID() || !CFStringGetCString(cf, output, out_len, kCFStringEncodingASCII))
|
||||
output[0] = 0;
|
||||
}
|
||||
|
||||
int os_setupusb(usbdevice* kb){
|
||||
kb->lastkeypress = KEY_NONE;
|
||||
// Handle 3 is the control handle
|
||||
if(IS_RGB(kb->vendor, kb->product))
|
||||
kb->handle = kb->handles[3];
|
||||
else
|
||||
// Non RGB keyboards don't have one, so just use 0
|
||||
kb->handle = kb->handles[0];
|
||||
|
||||
// Get the device firmware version
|
||||
long fwversion = usbgetlong(kb->handle, CFSTR(kIOHIDVersionNumberKey));
|
||||
kb->fwversion = fwversion;
|
||||
(*kb->handle)->GetDeviceReleaseNumber(kb->handle, &kb->fwversion);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_closeusb(usbdevice* kb){
|
||||
kb->handle = 0;
|
||||
int count = (IS_RGB(kb->vendor, kb->product)) ? 4 : 3;
|
||||
// Close HID handles
|
||||
int count = kb->epcount_hid;
|
||||
for(int i = 0; i < count; i++){
|
||||
(*kb->handles[i])->close(kb->handles[i], kIOHIDOptionsTypeNone);
|
||||
(*kb->handles[i])->Release(kb->handles[i]);
|
||||
kb->handles[i] = 0;
|
||||
hid_dev_t iface = kb->ifhid[i];
|
||||
if(iface){
|
||||
(*iface)->close(iface, kIOHIDOptionsTypeNone);
|
||||
(*iface)->Release(iface);
|
||||
kb->ifhid[i] = 0;
|
||||
}
|
||||
}
|
||||
kb->epcount_hid = 0;
|
||||
// Close USB handles
|
||||
count = kb->epcount_usb;
|
||||
for(int i = 0; i < count; i++){
|
||||
usb_iface_t iface = kb->ifusb[i];
|
||||
if(iface){
|
||||
(*iface)->USBInterfaceClose(iface);
|
||||
(*iface)->Release(iface);
|
||||
kb->ifusb[i] = 0;
|
||||
}
|
||||
}
|
||||
kb->epcount_usb = 0;
|
||||
usb_dev_t iface = kb->handle;
|
||||
if(iface){
|
||||
(*iface)->USBDeviceClose(iface);
|
||||
(*iface)->Release(iface);
|
||||
kb->handle = 0;
|
||||
}
|
||||
// Close input thread
|
||||
if(kb->input_loop){
|
||||
CFRunLoopStop(kb->input_loop);
|
||||
kb->input_loop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
usbdevice* usbadd(hid_dev_t handle, io_object_t** rm_notify){
|
||||
// Get the model and serial number
|
||||
long idvendor = V_CORSAIR, idproduct = usbgetlong(handle, CFSTR(kIOHIDProductIDKey));
|
||||
// Each keyboard generates multiple match events (one for each endpoint)
|
||||
// Use the location ID key to group the handles together
|
||||
long location = usbgetlong(handle, CFSTR(kIOHIDLocationIDKey));
|
||||
|
||||
// Look for any partially-set up boards matching this device
|
||||
int index = -1;
|
||||
for(int i = 1; i < DEV_MAX; i++){
|
||||
if(pthread_mutex_trylock(devmutex + i))
|
||||
// If the mutex is locked then the device is obviously set up already, keep going
|
||||
continue;
|
||||
if(keyboard[i].handle == INCOMPLETE
|
||||
&& keyboard[i].vendor == idvendor && keyboard[i].product == idproduct
|
||||
&& keyboard[i].location_id == location){
|
||||
// Matched; continue setting up this device
|
||||
index = i;
|
||||
// Device mutex remains locked
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(devmutex + i);
|
||||
}
|
||||
// If none was found, grab the first free device
|
||||
if(index == -1){
|
||||
for(int i = 1; i < DEV_MAX; i++){
|
||||
if(pthread_mutex_trylock(devmutex + i))
|
||||
continue;
|
||||
if(!keyboard[i].handle){
|
||||
// Mark the device as in use and print out a message
|
||||
index = i;
|
||||
keyboard[i].handle = INCOMPLETE;
|
||||
keyboard[i].location_id = location;
|
||||
keyboard[i].vendor = idvendor;
|
||||
keyboard[i].product = idproduct;
|
||||
// Read the serial number and name
|
||||
usbgetstr(handle, CFSTR(kIOHIDSerialNumberKey), keyboard[i].serial, SERIAL_LEN);
|
||||
usbgetstr(handle, CFSTR(kIOHIDProductKey), keyboard[i].name, KB_NAME_LEN);
|
||||
ckb_info("Connecting %s at %s%d\n", keyboard[i].name, devpath, i);
|
||||
// Device mutex remains locked
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(devmutex + i);
|
||||
}
|
||||
}
|
||||
if(index == -1){
|
||||
ckb_err("No free devices\n");
|
||||
return 0;
|
||||
}
|
||||
usbdevice* kb = keyboard + index;
|
||||
// There's no direct way to tell which of the endpoints this is, but there's a workaround
|
||||
// Each handle has a unique maximum packet size combination, so use that to place them
|
||||
long input = usbgetlong(handle, CFSTR(kIOHIDMaxInputReportSizeKey));
|
||||
long output = usbgetlong(handle, CFSTR(kIOHIDMaxOutputReportSizeKey));
|
||||
long feature = usbgetlong(handle, CFSTR(kIOHIDMaxFeatureReportSizeKey));
|
||||
|
||||
int handle_idx;
|
||||
// Handle 0 is for BIOS mode input (RGB) or non-RGB key input
|
||||
if(((input == 8 && output == 1) // Keyboards
|
||||
|| (input == 7 && output == 0)) // Mice
|
||||
&& feature == 0)
|
||||
handle_idx = 0;
|
||||
// Handle 1 is for standard HID input (RGB) or media keys (non-RGB)
|
||||
else if(((input == 21 || input == 10) && output == 1 && feature == 1)
|
||||
|| (input == 4 && output == 0 && feature == 0))
|
||||
handle_idx = 1;
|
||||
// Handle 2 is for Corsair inputs, unused on non-RGB
|
||||
else if(((input == 64 || input == 15) && output == 0 && feature == 0)
|
||||
|| (input == 64 && output == 64 && feature == 0)) // FW >= 1.20
|
||||
handle_idx = 2;
|
||||
// Handle 3 is for controlling the device (only exists for RGB)
|
||||
else if((input == 0 && output == 0 && feature == 64)
|
||||
|| (input == 64 && output == 64 && feature == 64)) // FW >= 1.20
|
||||
handle_idx = 3;
|
||||
else {
|
||||
ckb_warn("Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If all handles have been set up, finish initializing the keyboard
|
||||
kb->handles[handle_idx] = handle;
|
||||
if(kb->handles[0] && kb->handles[1] && kb->handles[2]
|
||||
&& (kb->handles[3] || !IS_RGB(idvendor, idproduct)))
|
||||
setupusb(kb);
|
||||
else
|
||||
pthread_mutex_unlock(devmutex + index);
|
||||
*rm_notify = kb->rm_notify + handle_idx;
|
||||
return kb;
|
||||
}
|
||||
|
||||
static CFRunLoopRef mainloop = 0;
|
||||
static IONotificationPortRef notify = 0;
|
||||
|
||||
static void remove_device(void* context, io_service_t device, uint32_t message_type, void* message_argument){
|
||||
if(message_type != kIOMessageServiceIsTerminated)
|
||||
return;
|
||||
@@ -351,10 +417,43 @@ static void remove_device(void* context, io_service_t device, uint32_t message_t
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
|
||||
static int seize_wait(hid_dev_t handle){
|
||||
// HACK: We shouldn't seize the HID device until it's successfully added to the service registry.
|
||||
// Finds a USB device by location ID. Returns a new device if none was found or -1 if no devices available.
|
||||
// If successful, devmutex[index] will be locked when the function returns. Unlock it when finished.
|
||||
static int find_device(long idvendor, long idproduct, long location){
|
||||
// Look for any partially-set up boards matching this device
|
||||
for(int i = 1; i < DEV_MAX; i++){
|
||||
if(pthread_mutex_trylock(devmutex + i))
|
||||
// If the mutex is locked then the device is obviously set up already, keep going
|
||||
continue;
|
||||
if(keyboard[i].vendor == idvendor && keyboard[i].product == idproduct
|
||||
&& keyboard[i].location_id == location){
|
||||
// Matched; continue setting up this device
|
||||
// Device mutex remains locked
|
||||
return i;
|
||||
}
|
||||
pthread_mutex_unlock(devmutex + i);
|
||||
}
|
||||
// If none was found, grab the first free device
|
||||
for(int i = 1; i < DEV_MAX; i++){
|
||||
if(pthread_mutex_trylock(devmutex + i))
|
||||
continue;
|
||||
if(!keyboard[i].handle){
|
||||
// Mark the device as in use and print out a message
|
||||
keyboard[i].handle = INCOMPLETE;
|
||||
keyboard[i].location_id = location;
|
||||
keyboard[i].vendor = idvendor;
|
||||
keyboard[i].product = idproduct;
|
||||
// Device mutex remains locked
|
||||
return i;
|
||||
}
|
||||
pthread_mutex_unlock(devmutex + i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int seize_wait(long location){
|
||||
// HACK: We shouldn't seize the device until it's successfully added to the service registry.
|
||||
// Otherwise, OSX might think there's no keyboard/mouse connected.
|
||||
long location = usbgetlong(handle, CFSTR(kIOHIDLocationIDKey));
|
||||
char location_var[18], location_fixed[18];
|
||||
snprintf(location_var, sizeof(location_var), "@%lx", location);
|
||||
snprintf(location_fixed, sizeof(location_fixed), "@%08x", (int)location);
|
||||
@@ -366,9 +465,9 @@ static int seize_wait(hid_dev_t handle){
|
||||
ckb_warn("Unable to open master port: 0x%08x\n", res);
|
||||
return -1;
|
||||
}
|
||||
const int max_tries = 50; // give up after ~5s
|
||||
const int max_tries = 20; // give up after ~2s
|
||||
for(int try = 0; try < max_tries; try++){
|
||||
usleep(100);
|
||||
usleep(100000);
|
||||
// Iterate the whole IOService registry
|
||||
io_iterator_t child_iter;
|
||||
if((res = IORegistryCreateIterator(master, kIOServicePlane, kIORegistryIterateRecursively, &child_iter)) != kIOReturnSuccess)
|
||||
@@ -391,16 +490,235 @@ static int seize_wait(hid_dev_t handle){
|
||||
return -3;
|
||||
}
|
||||
|
||||
// Hacky way of trying something over and over again until it works. 500ms intervals, max 5s
|
||||
#define wait_loop(error, operation) do { \
|
||||
int trial = 0; \
|
||||
while(((error) = (operation)) != kIOReturnSuccess){ \
|
||||
if(++trial == 10) \
|
||||
break; \
|
||||
usleep(500000); \
|
||||
} } while(0)
|
||||
static usbdevice* add_usb(usb_dev_t handle, io_object_t** rm_notify){
|
||||
int iface_count = 0, iface_success = 0;
|
||||
io_iterator_t iterator = 0;
|
||||
io_service_t iface = 0;
|
||||
// Get device properties
|
||||
UInt16 idvendor, idproduct;
|
||||
UInt32 location;
|
||||
(*handle)->GetDeviceVendor(handle, &idvendor);
|
||||
(*handle)->GetDeviceProduct(handle, &idproduct);
|
||||
(*handle)->GetLocationID(handle, &location);
|
||||
// Use the location ID key to group the USB handle with the HID handles
|
||||
int index = find_device(idvendor, idproduct, location);
|
||||
if(index == -1){
|
||||
ckb_err("No free devices\n");
|
||||
return 0;
|
||||
}
|
||||
usbdevice* kb = keyboard + index;
|
||||
|
||||
static void iterate_devices(void* context, io_iterator_t iterator){
|
||||
// Set the handle for the keyboard
|
||||
if(kb->handle && kb->handle != INCOMPLETE){
|
||||
// This should never happen
|
||||
ckb_warn("Tried to set up handle for device ckb%d, but it was already set up. Skipping...\n", index);
|
||||
goto error;
|
||||
}
|
||||
kb->handle = handle;
|
||||
|
||||
// Read the serial number and name (if not done yet)
|
||||
if(!kb->serial[0] && !kb->name[0]){
|
||||
UInt8 serial_idx, product_idx;
|
||||
if((*handle)->USBGetSerialNumberStringIndex(handle, &serial_idx) == kIOReturnSuccess)
|
||||
usbgetstr(handle, serial_idx, kb->serial, SERIAL_LEN);
|
||||
if((*handle)->USBGetProductStringIndex(handle, &product_idx) == kIOReturnSuccess)
|
||||
usbgetstr(handle, product_idx, kb->name, KB_NAME_LEN);
|
||||
ckb_info("Connecting %s at %s%d\n", keyboard[index].name, devpath, index);
|
||||
}
|
||||
|
||||
// Iterate through the USB interfaces. Most of these will fail to open because they're already grabbed by the HID system.
|
||||
if(seize_wait(location))
|
||||
ckb_warn("seize_wait failed, connecting anyway...\n");
|
||||
IOUSBFindInterfaceRequest interfaceRequest;
|
||||
interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare;
|
||||
interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
|
||||
interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
|
||||
interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
|
||||
(*handle)->CreateInterfaceIterator(handle, &interfaceRequest, &iterator);
|
||||
// Count the total number of interfaces as well as the number successfully opened.
|
||||
while((iface = IOIteratorNext(iterator)) != 0){
|
||||
if(iface_count >= IFACE_MAX){
|
||||
ckb_warn("Too many interfaces. Dropping the rest.\n");
|
||||
IOObjectRelease(iface);
|
||||
break;
|
||||
}
|
||||
// Get device interface
|
||||
IOCFPlugInInterface** plugin = 0;
|
||||
SInt32 score = 0;
|
||||
kern_return_t err;
|
||||
wait_loop(err, IOCreatePlugInInterfaceForService(iface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score));
|
||||
if(err != kIOReturnSuccess){
|
||||
ckb_err("Failed to create interface plugin: %x\n", err);
|
||||
goto release;
|
||||
}
|
||||
usb_iface_t if_handle;
|
||||
wait_loop(err, (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID183), (LPVOID)&if_handle));
|
||||
if(err != kIOReturnSuccess){
|
||||
ckb_err("QueryInterface failed: %x\n", err);
|
||||
goto release;
|
||||
}
|
||||
// Plugin is no longer needed
|
||||
IODestroyPlugInInterface(plugin);
|
||||
|
||||
// Try to open the interface. If it succeeds, add it to the device's interface list.
|
||||
err = (*if_handle)->USBInterfaceOpenSeize(if_handle); // no wait_loop here because this is expected to fail
|
||||
if(err == kIOReturnSuccess){
|
||||
kb->ifusb[iface_count] = if_handle;
|
||||
iface_success++;
|
||||
// Register for removal notification
|
||||
IOServiceAddInterestNotification(notify, iface, kIOGeneralInterest, remove_device, kb, kb->rm_notify + 1 + iface_count);
|
||||
ckb_info("Adding ckb%d.ifusb[%d]\n", index, iface_count);
|
||||
} else {
|
||||
kb->ifusb[iface_count] = 0;
|
||||
(*if_handle)->Release(if_handle);
|
||||
}
|
||||
|
||||
release:
|
||||
iface_count++;
|
||||
IOObjectRelease(iface);
|
||||
}
|
||||
if(iface_count == 0){
|
||||
// This shouldn't happen, but if it does, assume EP count based on what the device is supposed to have
|
||||
iface_count = (HAS_FEATURES(kb, FEAT_RGB) ? 4 : 3);
|
||||
ckb_warn("Unable to count endpoints, assuming %d...\n", iface_count);
|
||||
}
|
||||
kb->epcount = iface_count;
|
||||
kb->epcount_usb = iface_success;
|
||||
|
||||
// If the HID handles are already opened, set up the device
|
||||
if(HAS_ALL_HANDLES(kb))
|
||||
setupusb(kb);
|
||||
else
|
||||
pthread_mutex_unlock(devmutex + index);
|
||||
*rm_notify = kb->rm_notify;
|
||||
return kb;
|
||||
|
||||
error:
|
||||
pthread_mutex_unlock(devmutex + index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iterate_devices_usb(void* context, io_iterator_t iterator){
|
||||
io_service_t device;
|
||||
euid_guard_start;
|
||||
while((device = IOIteratorNext(iterator)) != 0){
|
||||
IOCFPlugInInterface** plugin = 0;
|
||||
SInt32 score = 0;
|
||||
kern_return_t err;
|
||||
wait_loop(err, IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score));
|
||||
if(err != kIOReturnSuccess){
|
||||
ckb_err("Failed to create device plugin: %x\n", err);
|
||||
goto release;
|
||||
}
|
||||
// Get the device interface
|
||||
usb_dev_t handle;
|
||||
wait_loop(err, (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182), (LPVOID*)&handle));
|
||||
if(err != kIOReturnSuccess){
|
||||
ckb_err("QueryInterface failed: %x\n", err);
|
||||
goto release;
|
||||
}
|
||||
// Plugin is no longer needed
|
||||
IODestroyPlugInInterface(plugin);
|
||||
|
||||
err = (*handle)->USBDeviceOpenSeize(handle);
|
||||
if(err == kIOReturnExclusiveAccess){
|
||||
// We can't send control transfers but most of the other functions should work
|
||||
ckb_warn("Unable to seize USB handle, continuing anyway...\n");
|
||||
} else if(err != kIOReturnSuccess){
|
||||
ckb_err("USBDeviceOpen failed: %x\n", err);
|
||||
continue;
|
||||
}
|
||||
// Connect it
|
||||
io_object_t* rm_notify = 0;
|
||||
usbdevice* kb = add_usb(handle, &rm_notify);
|
||||
if(kb){
|
||||
// If successful, register for removal notification
|
||||
IOServiceAddInterestNotification(notify, device, kIOGeneralInterest, remove_device, kb, rm_notify);
|
||||
} else
|
||||
// Otherwise, release it now
|
||||
(*handle)->USBDeviceClose(handle);
|
||||
release:
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
euid_guard_stop;
|
||||
}
|
||||
|
||||
static usbdevice* add_hid(hid_dev_t handle, io_object_t** rm_notify){
|
||||
// Get the model and serial number
|
||||
long idvendor = hidgetlong(handle, CFSTR(kIOHIDVendorIDKey)), idproduct = hidgetlong(handle, CFSTR(kIOHIDProductIDKey));
|
||||
// Each keyboard generates multiple match events (one for each endpoint)
|
||||
// Use the location ID key to group the handles together
|
||||
long location = hidgetlong(handle, CFSTR(kIOHIDLocationIDKey));
|
||||
int index = find_device(idvendor, idproduct, location);
|
||||
if(index == -1){
|
||||
ckb_err("No free devices\n");
|
||||
return 0;
|
||||
}
|
||||
usbdevice* kb = keyboard + index;
|
||||
|
||||
// Read the serial number and name (if not done yet)
|
||||
if(!keyboard[index].serial[0] && !keyboard[index].name[0]){
|
||||
hidgetstr(handle, CFSTR(kIOHIDSerialNumberKey), keyboard[index].serial, SERIAL_LEN);
|
||||
hidgetstr(handle, CFSTR(kIOHIDProductKey), keyboard[index].name, KB_NAME_LEN);
|
||||
ckb_info("Connecting %s at %s%d\n", keyboard[index].name, devpath, index);
|
||||
}
|
||||
|
||||
// There's no direct way to tell which of the endpoints this is, but there's a workaround
|
||||
// Each handle has a unique maximum packet size combination, so use that to place them
|
||||
long input = hidgetlong(handle, CFSTR(kIOHIDMaxInputReportSizeKey));
|
||||
long output = hidgetlong(handle, CFSTR(kIOHIDMaxOutputReportSizeKey));
|
||||
long feature = hidgetlong(handle, CFSTR(kIOHIDMaxFeatureReportSizeKey));
|
||||
int handle_idx;
|
||||
// Handle 3 is for controlling the device (only exists for RGB)
|
||||
if(feature == 64)
|
||||
handle_idx = 3;
|
||||
// Handle 2 is for Corsair inputs, unused on non-RGB
|
||||
else if(feature == 0 &&
|
||||
(((input == 64 ||
|
||||
input == 15) && output == 0) ||
|
||||
(input == 64 && output == 64) || // FW >= 1.20
|
||||
(input <= 1 && output == 64))) // FW >= 2.00 (Scimitar)
|
||||
handle_idx = 2;
|
||||
// Handle 0 is for BIOS mode input (RGB) or non-RGB key input
|
||||
else if(output <= 1 && feature <= 1 &&
|
||||
(input == 8 || // Keyboards
|
||||
input == 7)) // Mice
|
||||
handle_idx = 0;
|
||||
// Handle 1 is for standard HID input (RGB) or media keys (non-RGB)
|
||||
else if(output <= 1 && feature <= 1 &&
|
||||
(input == 21 || input == 10 ||
|
||||
input == 4 ||
|
||||
input == 64)) // FW >= 2.00 (Scimitar)
|
||||
handle_idx = 1;
|
||||
else {
|
||||
ckb_warn("Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Set the handle
|
||||
if(kb->ifhid[handle_idx]){
|
||||
// This should never happen
|
||||
ckb_warn("Tried to set up ifhid[%d] for device ckb%d, but it was already set up. Skipping...\n", handle_idx, index);
|
||||
goto error;
|
||||
}
|
||||
ckb_info("Adding ckb%d.ifhid[%d]\n", index, handle_idx);
|
||||
kb->ifhid[handle_idx] = handle;
|
||||
kb->epcount_hid++;
|
||||
if(HAS_ALL_HANDLES(kb))
|
||||
// If all handles have been opened, we're ready to set up the device
|
||||
setupusb(kb);
|
||||
else
|
||||
// Otherwise, return and keep going
|
||||
pthread_mutex_unlock(devmutex + index);
|
||||
*rm_notify = kb->rm_notify + IFACE_MAX + 1 + handle_idx;
|
||||
return kb;
|
||||
|
||||
error:
|
||||
pthread_mutex_unlock(devmutex + index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iterate_devices_hid(void* context, io_iterator_t iterator){
|
||||
io_service_t device;
|
||||
euid_guard_start;
|
||||
while((device = IOIteratorNext(iterator)) != 0){
|
||||
@@ -423,23 +741,23 @@ static void iterate_devices(void* context, io_iterator_t iterator){
|
||||
// Plugin is no longer needed
|
||||
IODestroyPlugInInterface(plugin);
|
||||
// Seize the device handle
|
||||
if(seize_wait(handle))
|
||||
if(seize_wait(hidgetlong(handle, CFSTR(kIOHIDLocationIDKey))))
|
||||
ckb_warn("seize_wait failed, connecting anyway...\n");
|
||||
err = (*handle)->open(handle, kIOHIDOptionsTypeSeizeDevice);
|
||||
wait_loop(err, (*handle)->open(handle, kIOHIDOptionsTypeSeizeDevice));
|
||||
if(err != kIOReturnSuccess){
|
||||
ckb_err("Failed to seize device: %x\n", err);
|
||||
ckb_warn("Failed to open device: %x\n", err);
|
||||
goto release;
|
||||
}
|
||||
// Connect it
|
||||
io_object_t* rm_notify = 0;
|
||||
usbdevice* kb = usbadd(handle, &rm_notify);
|
||||
usbdevice* kb = add_hid(handle, &rm_notify);
|
||||
if(kb)
|
||||
// If successful, register for removal notification
|
||||
IOServiceAddInterestNotification(notify, device, kIOGeneralInterest, remove_device, kb, rm_notify);
|
||||
else
|
||||
// Otherwise, release it now
|
||||
(*handle)->close(handle, kIOHIDOptionsTypeSeizeDevice);
|
||||
release:
|
||||
release:
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
euid_guard_stop;
|
||||
@@ -453,10 +771,23 @@ int usbmain(){
|
||||
// Mice
|
||||
P_M65, P_SABRE_O, P_SABRE_L, P_SCIMITAR
|
||||
};
|
||||
// Tell IOService which type of devices we want (IOHIDDevices matching the supported vendor/products)
|
||||
CFMutableDictionaryRef match = IOServiceMatching(kIOHIDDeviceKey);
|
||||
|
||||
// Setup global variables
|
||||
notify = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
mainloop = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(mainloop, IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
|
||||
|
||||
// This gets really complicated.
|
||||
// The USB interfaces are *usually* grabbed by the HID system, which makes them inaccessible to ckb.
|
||||
// In this case, we have to get the HID handles and communicate through them.
|
||||
// But sometimes, the interfaces are not grabbed. In this case, we have to talk to the USB device directly.
|
||||
// So what do we do? Grab both HID and USB handles for all devices, try to set them up, and determine which ones to use
|
||||
// based on which interfaces were grabbed.
|
||||
|
||||
// Let's start by searching for USB devices...
|
||||
CFMutableDictionaryRef match = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
CFNumberRef cfvendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
|
||||
CFDictionarySetValue(match, CFSTR(kIOHIDVendorIDKey), cfvendor);
|
||||
CFDictionarySetValue(match, CFSTR(kUSBVendorName), cfvendor);
|
||||
CFRelease(cfvendor);
|
||||
CFMutableArrayRef cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
for(uint i = 0; i < sizeof(products) / sizeof(int); i++){
|
||||
@@ -465,20 +796,45 @@ int usbmain(){
|
||||
CFArrayAppendValue(cfproducts, cfproduct);
|
||||
CFRelease(cfproduct);
|
||||
}
|
||||
CFDictionarySetValue(match, CFSTR(kUSBProductIdsArrayName), cfproducts);
|
||||
CFRelease(cfproducts);
|
||||
|
||||
io_iterator_t iterator_usb = 0;
|
||||
IOReturn res = IOServiceAddMatchingNotification(notify, kIOMatchedNotification, match, iterate_devices_usb, 0, &iterator_usb);
|
||||
if(res != kIOReturnSuccess){
|
||||
ckb_fatal("Failed to list USB devices: %x\n", res);
|
||||
return -1;
|
||||
}
|
||||
// Iterate existing devices
|
||||
if(iterator_usb)
|
||||
iterate_devices_usb(0, iterator_usb);
|
||||
|
||||
// Now move on to HID devices
|
||||
// It is in fact necessary to recreate the matching dictionary, as the keys are different
|
||||
match = IOServiceMatching(kIOHIDDeviceKey);
|
||||
cfvendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
|
||||
CFDictionarySetValue(match, CFSTR(kIOHIDVendorIDKey), cfvendor);
|
||||
CFRelease(cfvendor);
|
||||
cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
for(uint i = 0; i < sizeof(products) / sizeof(int); i++){
|
||||
int product = products[i];
|
||||
CFNumberRef cfproduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &product);
|
||||
CFArrayAppendValue(cfproducts, cfproduct);
|
||||
CFRelease(cfproduct);
|
||||
}
|
||||
CFDictionarySetValue(match, CFSTR(kIOHIDProductIDArrayKey), cfproducts);
|
||||
CFRelease(cfproducts);
|
||||
|
||||
notify = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
CFRunLoopAddSource(mainloop = CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
|
||||
io_iterator_t iterator = 0;
|
||||
IOReturn res = IOServiceAddMatchingNotification(notify, kIOMatchedNotification, match, iterate_devices, 0, &iterator);
|
||||
io_iterator_t iterator_hid = 0;
|
||||
res = IOServiceAddMatchingNotification(notify, kIOMatchedNotification, match, iterate_devices_hid, 0, &iterator_hid);
|
||||
if(res != kIOReturnSuccess){
|
||||
ckb_fatal("Failed to list devices: %x\n", res);
|
||||
ckb_fatal("Failed to list HID devices: %x\n", res);
|
||||
return -1;
|
||||
}
|
||||
// Iterate existing devices
|
||||
if(iterator)
|
||||
iterate_devices(0, iterator);
|
||||
if(iterator_hid)
|
||||
iterate_devices_hid(0, iterator_hid);
|
||||
|
||||
// Enter loop to scan/connect new devices
|
||||
CFRunLoopRun();
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user