Beispiel #1
0
int os_inputopen(usbdevice* kb){
    // Open master port (if not done yet)
    static mach_port_t master = 0;
    kern_return_t res;
    if(!master && (res = IOMasterPort(bootstrap_port, &master)) != KERN_SUCCESS){
        master = 0;
        ckb_err("Unable to open master port: 0x%08x\n", res);
        return -1;
    }
    // Open an HID service
    io_iterator_t iter;
    if((res = IOServiceGetMatchingServices(master, IOServiceMatching(kIOHIDSystemClass), &iter)) != KERN_SUCCESS){
        ckb_err("Unable to get input service iterator: 0x%08x\n", res);
        return -2;
    }
    io_service_t service = IOIteratorNext(iter);
    if((res = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &kb->event)) != KERN_SUCCESS){
        IOObjectRelease(iter);
        ckb_err("Unable to open IO service: 0x%08x\n", res);
        kb->event = 0;
        return -3;
    }

    IOObjectRelease(iter);
    clearkeys(kb);
    return 0;
}
Beispiel #2
0
int loadrgb_mouse(usbdevice* kb, lighting* light, int mode){
    uchar data_pkt[MSG_SIZE] = { 0x0e, 0x13, 0x10, 1, 0 };
    uchar in_pkt[MSG_SIZE] = { 0 };
    // Zone 1
    if(!usbrecv(kb, data_pkt, in_pkt))
        return -1;
    if(memcmp(in_pkt, data_pkt, 4)){
        ckb_err("Bad input header\n");
        return -2;
    }
    // Copy data
    light->r[LED_MOUSE] = in_pkt[4];
    light->g[LED_MOUSE] = in_pkt[5];
    light->b[LED_MOUSE] = in_pkt[6];

    // Zone 2
    data_pkt[2]++;
    if(!usbrecv(kb, data_pkt, in_pkt))
        return -1;
    if(memcmp(in_pkt, data_pkt, 4)){
        ckb_err("Bad input header\n");
        return -2;
    }
    // Copy data
    light->r[LED_MOUSE + 1] = in_pkt[4];
    light->g[LED_MOUSE + 1] = in_pkt[5];
    light->b[LED_MOUSE + 1] = in_pkt[6];

    // TODO: zone 4 for Sabre?
    return 0;
}
Beispiel #3
0
// Opens HID service. Returns kIOReturnSuccess on success.
static int open_iohid(io_connect_t* connection){
    io_iterator_t iter;
    io_service_t service;
    // Open master port (if not done yet)
    static mach_port_t master = 0;
    kern_return_t res;
    if(!master && (res = IOMasterPort(bootstrap_port, &master)) != kIOReturnSuccess){
        master = 0;
        ckb_err("Unable to open master port: 0x%08x\n", res);
        goto failure;
    }
    // Open the HID service
    if((res = IOServiceGetMatchingServices(master, IOServiceMatching(kIOHIDSystemClass), &iter)) != kIOReturnSuccess)
        goto failure;
    service = IOIteratorNext(iter);
    if(!service){
        res = kIOReturnNotOpen;
        goto failure_release_iter;
    }
    if((res = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, connection)) != kIOReturnSuccess){
        *connection = 0;
        goto failure_release_iter;
    }
    // Finished; release objects and return success
    IOObjectRelease(service);
    failure_release_iter:
    IOObjectRelease(iter);
    failure:
    return res;
}
Beispiel #4
0
int loadrgb_mouse(usbdevice* kb, lighting* light, int mode){
    (void)mode;

    uchar data_pkt[MSG_SIZE] = { 0x0e, 0x13, 0x10, 1, 0 };
    uchar in_pkt[MSG_SIZE] = { 0 };
    // Load each RGB zone
    int zonecount = IS_SCIMITAR(kb) ? 4 : IS_SABRE(kb) ? 3 : 2;
    for(int i = 0; i < zonecount; i++){
        if(!usbrecv(kb, data_pkt, in_pkt))
            return -1;
        if(memcmp(in_pkt, data_pkt, 4)){
            ckb_err("Bad input header\n");
            return -2;
        }
        // Copy data
        int led = LED_MOUSE + i;
        if(led >= LED_DPI)
            led++;          // Skip DPI light
        light->r[led] = in_pkt[4];
        light->g[led] = in_pkt[5];
        light->b[led] = in_pkt[6];
        // Set packet for next zone
        data_pkt[2]++;
    }
    return 0;
}
Beispiel #5
0
int usbadd(struct udev_device* dev, short vendor, short product){
    const char* path = udev_device_get_devnode(dev);
    const char* syspath = udev_device_get_syspath(dev);
    if(!path || !syspath || path[0] == 0 || syspath[0] == 0){
        ckb_err("Failed to get device path\n");
        return -1;
    }
    // Find a free USB slot
    for(int index = 1; index < DEV_MAX; index++){
        usbdevice* kb = keyboard + index;
        if(pthread_mutex_trylock(dmutex(kb))){
            // If the mutex is locked then the device is obviously in use, so keep going
            if(!strcmp(syspath, kbsyspath[index])){
                // Make sure this existing keyboard doesn't have the same syspath (this shouldn't happen)
                return 0;
            }
            continue;
        }
        if(!IS_CONNECTED(kb)){
            // Open the sysfs device
            kb->handle = open(path, O_RDWR);
            if(kb->handle <= 0){
                ckb_err("Failed to open USB device: %s\n", strerror(errno));
                kb->handle = 0;
                pthread_mutex_unlock(dmutex(kb));
                return -1;
            } else {
                // Set up device
                kb->udev = dev;
                kb->vendor = vendor;
                kb->product = product;
                strncpy(kbsyspath[index], syspath, FILENAME_MAX);
                // Mutex remains locked
                setupusb(kb);
                return 0;
            }
        }
        pthread_mutex_unlock(dmutex(kb));
    }
    ckb_err("No free devices\n");
    return -1;
}
Beispiel #6
0
static void iterate_devices(void* context, io_iterator_t iterator){
    io_service_t device;
    while((device = IOIteratorNext(iterator)) != 0){
        // Get the plugin interface for the device
        IOCFPlugInInterface** plugin;
        SInt32 score;
        kern_return_t err = IOCreatePlugInInterfaceForService(device, kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &plugin, &score);
        if(err != kIOReturnSuccess){
            ckb_err("Failed to create device plugin: %x\n", err);
            continue;
        }
        // Get the device interface
        hid_dev_t handle;
        err = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOHIDDeviceDeviceInterfaceID), (LPVOID*)&handle);
        if(err != kIOReturnSuccess){
            ckb_err("QueryInterface failed: %x\n", err);
            continue;
        }
        // Plugin is no longer needed
        IODestroyPlugInInterface(plugin);
        // Seize the device handle
        euid_guard_start;
        err = (*handle)->open(handle, kIOHIDOptionsTypeSeizeDevice);
        euid_guard_stop;
        if(err != kIOReturnSuccess){
            ckb_err("Failed to seize device: %x\n", err);
            continue;
        }
        // Connect it
        io_object_t* rm_notify = 0;
        usbdevice* kb = usbadd(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, kIOHIDOptionsTypeNone);
            remove_device(0, device, kIOMessageServiceIsTerminated, 0);
        }
    }
}
Beispiel #7
0
// Xorg has buggy handling of combined keyboard + mouse devices, so instead we should create two separate devices:
// One for keyboard events, one for mouse.
int uinputopen(struct uinput_user_dev* indev, int mouse){
    int fd = open("/dev/uinput", O_RDWR);
    if(fd < 0){
        // If that didn't work, try /dev/input/uinput instead
        fd = open("/dev/input/uinput", O_RDWR);
        if(fd < 0){
            ckb_err("Failed to open uinput: %s\n", strerror(errno));
            return 0;
        }
    }
    // Enable all keys and mouse buttons
    ioctl(fd, UI_SET_EVBIT, EV_KEY);
    for(int i = 0; i < KEY_CNT; i++)
        ioctl(fd, UI_SET_KEYBIT, i);
    if(mouse){
        // Enable mouse axes
        ioctl(fd, UI_SET_EVBIT, EV_REL);
        for(int i = 0; i < REL_CNT; i++)
            ioctl(fd, UI_SET_RELBIT, i);
    } else {
        // Enable LEDs
        ioctl(fd, UI_SET_EVBIT, EV_LED);
        for(int i = 0; i < LED_CNT; i++)
            ioctl(fd, UI_SET_LEDBIT, i);
        // Eanble autorepeat
        ioctl(fd, UI_SET_EVBIT, EV_REP);
    }
    // Enable sychronization
    ioctl(fd, UI_SET_EVBIT, EV_SYN);
    // Create the device
    if(write(fd, indev, sizeof(*indev)) <= 0)
        ckb_warn("uinput write failed: %s\n", strerror(errno));
    if(ioctl(fd, UI_DEV_CREATE)){
        ckb_err("Failed to create uinput device: %s\n", strerror(errno));
        close(fd);
        return 0;
    }
    return fd + 1;
}
Beispiel #8
0
void* os_inputmain(void* context){
    usbdevice* kb = context;
    int index = INDEX_OF(kb, keyboard);
    // Schedule async events for the device on this thread
    CFRunLoopRef runloop = 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);
        if(res != kIOReturnSuccess){
            ckb_err("Failed to start input thread for %s%d: %x\n", devpath, index, res);
            return 0;
        }
        if(CFGetTypeID(eventsource) == CFRunLoopSourceGetTypeID())
            CFRunLoopAddSource(runloop, (CFRunLoopSourceRef)eventsource, kCFRunLoopDefaultMode);
        else if(CFGetTypeID(eventsource) == CFRunLoopTimerGetTypeID())
            CFRunLoopAddTimer(runloop, (CFRunLoopTimerRef)eventsource, kCFRunLoopDefaultMode);
    }
    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);
    }

    // Run the run loop for up to 2ms at a time, then check for key repeats
    while(1){
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, false);
        pthread_mutex_lock(imutex(kb));
        if(!IS_CONNECTED(kb)){
            // Make sure the device hasn't disconnected
            pthread_mutex_unlock(imutex(kb));
            break;
        }
        keyretrigger(kb);
        pthread_mutex_unlock(imutex(kb));
    }

    // Clean up
    ckb_info("Stopping input thread for %s%d\n", devpath, index);
    free(urbinput[0]);
    free(urbinput[1]);
    free(urbinput[2]);
    return 0;
}
Beispiel #9
0
int os_inputopen(usbdevice* kb){
    // The IO service isn't always ready at startup, so if it's not, wait until it is
    IOReturn res;
    while((res = open_iohid(&kb->event)) != kIOReturnSuccess){
        if(res != kIOReturnNotOpen){
            // If this is a more serious error, at least print a warning
            ckb_err("Unable to open HID system: 0x%08x\n", res);
            sleep(1);
            continue;
        }
        usleep(10000);
    }
    clearkeys(kb);
    return 0;
}
Beispiel #10
0
int usb_tryreset(usbdevice* kb){
    if(reset_stop)
        return -1;
    ckb_info("Attempting reset...\n");
    while(1){
        int res = resetusb(kb);
        if(!res){
            ckb_info("Reset success\n");
            return 0;
        }
        if(res == -2 || reset_stop)
            break;
    }
    ckb_err("Reset failed. Disconnecting.\n");
    return -1;
}
Beispiel #11
0
int os_setupusb(usbdevice* kb){
    // Copy device description and serial
    struct udev_device* dev = kb->udev;
    const char* name = udev_device_get_sysattr_value(dev, "product");
    if(name)
        strncpy(kb->name, name, KB_NAME_LEN);
    const char* serial = udev_device_get_sysattr_value(dev, "serial");
    if(serial)
        strncpy(kb->serial, serial, SERIAL_LEN);
    // Copy firmware version (needed to determine USB protocol)
    const char* firmware = udev_device_get_sysattr_value(dev, "bcdDevice");
    if(firmware)
        sscanf(firmware, "%hx", &kb->fwversion);
    else
        kb->fwversion = 0;
    ckb_info("Connecting %s (S/N: %s)\n", kb->name, kb->serial);

    // Claim the USB interfaces
    if(usbclaim(kb, HAS_FEATURES(kb, FEAT_RGB))){
        ckb_err("Failed to claim interface: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}
Beispiel #12
0
int os_setupusb(usbdevice* kb){
    // Copy device description and serial
    struct udev_device* dev = kb->udev;
    const char* name = udev_device_get_sysattr_value(dev, "product");
    if(name)
        strncpy(kb->name, name, KB_NAME_LEN);
    strtrim(kb->name);
    const char* serial = udev_device_get_sysattr_value(dev, "serial");
    if(serial)
        strncpy(kb->serial, serial, SERIAL_LEN);
    strtrim(kb->serial);
    // Copy firmware version (needed to determine USB protocol)
    const char* firmware = udev_device_get_sysattr_value(dev, "bcdDevice");
    if(firmware)
        sscanf(firmware, "%hx", &kb->fwversion);
    else
        kb->fwversion = 0;
    int index = INDEX_OF(kb, keyboard);
    ckb_info("Connecting %s at %s%d\n", kb->name, devpath, index);

    // Claim the USB interfaces
    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;
}
Beispiel #13
0
void setupusb(usbdevice* kb){
    pthread_mutex_lock(imutex(kb));
    if(pthread_create(&kb->thread, 0, _setupusb, kb))
        ckb_err("Failed to create USB thread\n");
}
Beispiel #14
0
void os_sendindicators(usbdevice* kb){
    struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0200, 0x00, 1, 500, &kb->ileds };
    int res = ioctl(kb->handle - 1, USBDEVFS_CONTROL, &transfer);
    if(res <= 0)
        ckb_err("%s\n", res ? strerror(errno) : "No data written");
}
Beispiel #15
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 (S/N: %s)\n", keyboard[i].name, keyboard[i].serial);
                // 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;
}