ckb-next  v0.2.8 at branch master
ckb-next driver for corsair devices
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
usb_mac.c
Go to the documentation of this file.
1 #include "device.h"
2 #include "devnode.h"
3 #include "input.h"
4 #include "notify.h"
5 #include "usb.h"
6 
7 #ifdef OS_MAC
8 
9 static CFRunLoopRef mainloop = 0;
10 static IONotificationPortRef notify = 0;
11 
12 // Pointer to the mouse event tap. This tap lets the daemon re-insert modifier keys into
13 // the event stream using CoreGraphics. This is necessary because mouse events are processed
14 // before IOHID events according to this document: https://github.com/tekezo/Karabiner-Elements/blob/master/DEVELOPMENT.md
15 //
16 // relevant section quoted below:
17 // The modifier flag events are handled in the following sequence in macOS 10.12.
18 //
19 // Receive HID reports from device.
20 // - Treat reports in the keyboard device driver.
21 // - Treat flags in accessibility functions. (eg. sticky keys, zoom)
22 // - Treat flags in mouse events.
23 // - Treat flags in IOHIDSystem.
24 // - Treat flags in Coregraphics.
25 //
26 // Thus, IOHIDPostEvent will be ignored in accessibility functions and mouse events.
27 // - endquote
28 //
29 static CFMachPortRef mouse_event_tap;
30 static char current_ckb_pid;
31 static char mouse_event_tap_pid;
32 
33 static long hidgetlong(hid_dev_t handle, CFStringRef key){
34  long raw = 0;
35  CFTypeRef cf;
36  if((*handle)->getProperty(handle, key, &cf) != kIOReturnSuccess)
37  return 0;
38  if(!cf || CFGetTypeID(cf) != CFNumberGetTypeID() || !CFNumberGetValue(cf, kCFNumberLongType, &raw))
39  return 0;
40  return raw;
41 }
42 
43 static void hidgetstr(hid_dev_t handle, CFStringRef key, char* output, int out_len){
44  CFTypeRef cf;
45  if((*handle)->getProperty(handle, key, &cf) != kIOReturnSuccess){
46  output[0] = 0;
47  return;
48  }
49  if(!cf || CFGetTypeID(cf) != CFStringGetTypeID() || !CFStringGetCString(cf, output, out_len, kCFStringEncodingASCII))
50  output[0] = 0;
51 }
52 
53 // profile.c
54 void u16dec(ushort* in, char* out, size_t* srclen, size_t* dstlen);
55 
56 static void usbgetstr(usb_dev_t handle, uint8 string_index, char* output, int out_len){
57  // Make a temporary buffer so the request won't fail if too large
58  char buffer[256] = { 0 };
59  IOUSBDevRequest rq = { 0x80, 0x06, 0x0300 | string_index, 0x0409, sizeof(buffer) - 2, buffer, 0};
60  kern_return_t res = (*handle)->DeviceRequest(handle, &rq);
61  if(res != kIOReturnSuccess){
62  output[0] = 0;
63  return;
64  }
65  size_t inl = sizeof(buffer) / 2, outl = out_len;
66  u16dec((ushort*)buffer + 1, output, &inl, &outl);
67  output[out_len - 1] = 0;
68 }
69 
70 #define INCOMPLETE (usb_dev_t)-1l
71 #define HAS_ALL_HANDLES(kb) ((kb)->epcount > 0 && (kb)->epcount_hid + (kb)->epcount_usb >= (kb)->epcount)
72 
73 // Hacky way of trying something over and over again until it works. 100ms intervals, max 1s
74 #define wait_loop(error, operation) do { \
75  int trial = 0; \
76  while(((error) = (operation)) != kIOReturnSuccess){ \
77  if(++trial == 10) \
78  break; \
79  clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = 100000000}, NULL); \
80  } } while(0)
81 
82 #define IS_TEMP_FAILURE(res) ((res) == kIOUSBTransactionTimeout || (res) == kIOUSBTransactionReturned || (res) == kIOUSBPipeStalled)
83 #define IS_DISCONNECT_FAILURE(res) ((res) == kIOReturnBadArgument || (res) == kIOReturnNoDevice || (res) == kIOReturnNotOpen || (res) == kIOReturnNotAttached || (res) == kIOReturnExclusiveAccess)
84 
85 // When reading/writing USB handles we have to ensure we select the correct pipe, else it will fail
86 static int get_pipe_index(usb_iface_t handle, int desired_direction){
87  uchar count;
88  if((*handle)->GetNumEndpoints(handle, &count) != kIOReturnSuccess)
89  return 1;
90  for(int i = 0; i < count; i++){
91  uchar direction, number, transfertype, interval;
92  ushort maxpacketsize;
93  if((*handle)->GetPipeProperties(handle, i + 1, &direction, &number, &transfertype, &maxpacketsize, &interval) == kIOReturnSuccess){
94  if(direction == desired_direction)
95  return i + 1;
96  }
97  }
98  // Not found - default to 1
99  return 1;
100 }
101 
102 int os_usbsend(usbdevice* kb, const uchar* out_msg, int is_recv, const char* file, int line){
103  kern_return_t res = kIOReturnSuccess;
106  if((kb->fwversion < 0x120 && !IS_V2_OVERRIDE(kb)) || is_recv){
107  int ep = kb->epcount;
108  // For old devices, or for receiving data, use control transfers
109  IOUSBDevRequestTO rq = { 0x21, 0x09, 0x0200, ep - 1, MSG_SIZE, (void*)out_msg, 0, 5000, 5000 };
110  res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
111  if(res == kIOReturnNotOpen){
112  // Device handle not open - try to send directly to the endpoint instead
113  usb_iface_t h_usb = kb->ifusb[ep - 1];
114  hid_dev_t h_hid = kb->ifhid[ep - 1];
115  if(h_usb)
116  res = (*h_usb)->ControlRequestTO(h_usb, 0, &rq);
117  else if(h_hid)
118  res = (*h_hid)->setReport(h_hid, kIOHIDReportTypeFeature, 0, out_msg, MSG_SIZE, 5000, 0, 0, 0);
119  }
120  } else {
121  // For newer devices, use interrupt transfers
122  // macOS sees 4 endpoints (including ep0) for FW 3.XX
123  int ep = (kb->fwversion >= 0x130 && (kb->fwversion < 0x200 || kb->fwversion >= 0x300)) ? 4 : 3;
124  usb_iface_t h_usb = kb->ifusb[ep - 1];
125  hid_dev_t h_hid = kb->ifhid[ep - 1];
126  if(h_usb)
127  res = (*h_usb)->WritePipe(h_usb, get_pipe_index(h_usb, kUSBOut), (void*)out_msg, MSG_SIZE);
128  else if(h_hid)
129  res = (*h_hid)->setReport(h_hid, kIOHIDReportTypeOutput, 0, out_msg, MSG_SIZE, 5000, 0, 0, 0);
130  }
131  kb->lastresult = res;
132  if(res != kIOReturnSuccess){
133  ckb_err_fn("Got return value 0x%x\n", file, line, res);
134  if(IS_TEMP_FAILURE(res))
135  return -1;
136  else
137  return 0;
138  }
139  return MSG_SIZE;
140 }
141 
142 int os_usbrecv(usbdevice* kb, uchar* in_msg, const char* file, int line){
143  int ep = kb->epcount;
144  IOUSBDevRequestTO rq = { 0xa1, 0x01, 0x0200, ep - 1, MSG_SIZE, in_msg, 0, 5000, 5000 };
145  kern_return_t res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
146  CFIndex length = rq.wLenDone;
147  if(res == kIOReturnNotOpen){
148  // Device handle not open - try to send directly to the endpoint instead
149  usb_iface_t h_usb = kb->ifusb[ep - 1];
150  hid_dev_t h_hid = kb->ifhid[ep - 1];
151  if(h_usb)
152  res = (*h_usb)->ControlRequestTO(h_usb, 0, &rq);
153  else if(h_hid)
154  res = (*h_hid)->getReport(h_hid, kIOHIDReportTypeFeature, 0, in_msg, &length, 5000, 0, 0, 0);
155  }
156  kb->lastresult = res;
157  if(res != kIOReturnSuccess){
158  ckb_err_fn("Got return value 0x%x\n", file, line, res);
159  if(IS_TEMP_FAILURE(res))
160  return -1;
161  else
162  return 0;
163  }
164  if(length != MSG_SIZE)
165  ckb_err_fn("Read %ld bytes (expected %d)\n", file, line, length, MSG_SIZE);
166  return length;
167 }
168 
169 int _nk95cmd(usbdevice* kb, uchar bRequest, ushort wValue, const char* file, int line){
170  IOUSBDevRequestTO rq = { 0x40, bRequest, wValue, 0, 0, 0, 0, 5000, 5000 };
171  kern_return_t res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
172  if(res != kIOReturnSuccess){
173  ckb_err_fn("Got return value 0x%x\n", file, line, res);
174  return 1;
175  }
176  return 0;
177 }
178 
179 void os_sendindicators(usbdevice* kb){
180  void *ileds;
181  ushort leds;
182  if(kb->fwversion >= 0x300) {
183  leds = (kb->ileds << 8) | 0x0001;
184  ileds = &leds;
185  }
186  else {
187  ileds = &kb->ileds;
188  }
189  IOUSBDevRequestTO rq = { 0x21, 0x09, 0x0200, 0x00, (kb->fwversion >= 0x300 ? 2 : 1), ileds, 0, 500, 500 };
190  kern_return_t res = (*kb->handle)->DeviceRequestTO(kb->handle, &rq);
191  if(res == kIOReturnNotOpen){
192  // Handle not open - try to go through the HID system instead
193  hid_dev_t handle = kb->ifhid[0];
194  if(handle){
195  uchar ileds = kb->ileds;
196  // Get a list of LED elements from handle 0
197  long ledpage = kHIDPage_LEDs;
198  const void* keys[] = { CFSTR(kIOHIDElementUsagePageKey) };
199  const void* values[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &ledpage) };
200  CFDictionaryRef matching = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
201  CFRelease(values[0]);
202  CFArrayRef leds;
203  kern_return_t res = (*handle)->copyMatchingElements(handle, matching, &leds, 0);
204  CFRelease(matching);
205  if(res != kIOReturnSuccess)
206  return;
207  // Iterate through them and update the LEDs which have changed
208  CFIndex count = CFArrayGetCount(leds);
209  for(CFIndex i = 0; i < count; i++){
210  IOHIDElementRef led = (void*)CFArrayGetValueAtIndex(leds, i);
211  uint32_t usage = IOHIDElementGetUsage(led);
212  IOHIDValueRef value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, led, 0, !!(ileds & (1 << (usage - 1))));
213  (*handle)->setValue(handle, led, value, 5000, 0, 0, 0);
214  CFRelease(value);
215  }
216  CFRelease(leds);
217  }
218  }
219  if(res != kIOReturnSuccess)
220  ckb_err("Got return value 0x%x\n", res);
221 }
222 
223 int os_resetusb(usbdevice* kb, const char* file, int line){
224  kern_return_t res = kb->lastresult;
225  if(IS_DISCONNECT_FAILURE(res))
226  // Don't try if the keyboard was disconnected
227  return -2;
228  // Otherwise, just wait and try again
229  clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = 100000000}, NULL);
230  return 0;
231 }
232 
233 static void intreport(void* context, IOReturn result, void* sender, IOHIDReportType reporttype, uint32_t reportid, uint8_t* data, CFIndex length){
234  usbdevice* kb = context;
235  pthread_mutex_lock(imutex(kb));
236  if(IS_MOUSE(kb->vendor, kb->product)){
237  switch(length){
238  case 7:
239  case 8:
240  case 10:
241  case 11:
242  hid_mouse_translate(kb->input.keys, &kb->input.rel_x, &kb->input.rel_y, -2, length, data, kb->fwversion);
243  break;
244  case MSG_SIZE:
245  corsair_mousecopy(kb->input.keys, kb->epcount >= 4 ? -3 : -2, data);
246  break;
247  }
248  } else if(HAS_FEATURES(kb, FEAT_RGB)){
249  switch(length){
250  case 8:
251  // RGB EP 1: 6KRO (BIOS mode) input
252  hid_kb_translate(kb->input.keys, -1, length, data);
253  break;
254  case 21:
255  case 5:
256  // RGB EP 2: NKRO (non-BIOS) input. Accept only if keyboard is inactive
257  if(!kb->active)
258  hid_kb_translate(kb->input.keys, -2, length, data);
259  break;
260  case MSG_SIZE:
261  // RGB EP 3: Corsair input
262  corsair_kbcopy(kb->input.keys, kb->epcount >= 4 ? -3 : -2, data);
263  break;
264  }
265  } else {
266  switch(length){
267  case 8:
268  // Non-RGB EP 1: 6KRO input
269  hid_kb_translate(kb->input.keys, 1, length, data);
270  break;
271  case 4:
272  // Non-RGB EP 2: media keys
273  hid_kb_translate(kb->input.keys, 2, length, data);
274  break;
275  case 15:
276  // Non-RGB EP 3: NKRO input
277  hid_kb_translate(kb->input.keys, 3, length, data);
278  break;
279  }
280  }
281  inputupdate(kb);
282  pthread_mutex_unlock(imutex(kb));
283 }
284 
285 typedef struct {
286  usbdevice* kb;
287  uchar* buffer;
288  int index;
289  int pipe;
290  int maxsize;
291 } urbctx;
292 
293 static void pipecomplete(void* refcon, IOReturn result, void* arg0){
294  if(result != kIOReturnSuccess)
295  return;
296  intptr_t length = (intptr_t)arg0;
297  urbctx* ctx = refcon;
298  // Wrap the URB to the interrupt report handler
299  usbdevice* kb = ctx->kb;
300  uchar* buffer = ctx->buffer;
301  intreport(kb, result, 0, 0, 0, buffer, length);
302  // Re-submit the URB to read the next packet
303  usb_iface_t handle = kb->ifusb[ctx->index];
304  (*handle)->ReadPipeAsync(handle, ctx->pipe, buffer, ctx->maxsize, pipecomplete, ctx);
305 }
306 
307 // Callback for adding modifier keys to mouse events. Every time a mouse event happens on the system
308 // this callback will be called and the modifier keys from the keyboard will be added to the mouse event.
309 CGEventRef mouse_event_modifier_callback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
310  if (type == kCGEventTapDisabledByTimeout) {
311  CGEventTapEnable(mouse_event_tap, true);
312  return event;
313  }
314 
315  usbdevice *kb = NULL;
316  if (event) {
317  // Grab the existing flags from the keyboard, useful for not losing the modifiers when using
318  // the device keyoard when a corsair keyboard is plugged in.
319  CGEventFlags existingFlags = CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState);
320  for(int i = 0; i < DEV_MAX; i++){
321  if(IS_CONNECTED(keyboard + i)){
322  kb = keyboard + i;
323  // Only care about the active keyboard for grabbing the modifier keys.
324  // Once found, can move on.
325  if (!IS_MOUSE_DEV(kb) && kb->active == 1) {
326  pthread_mutex_lock(imutex(kb));
327  CGEventSetFlags(event, (kCGEventFlagMaskNonCoalesced | kb->modifiers | existingFlags));
328  pthread_mutex_unlock(imutex(kb));
329  break;
330  }
331  }
332  }
333  }
334  return event;
335 }
336 
337 // Get the current ckb.app instance pid if it's open.
338 void update_ckb_pid() {
339  char str[10] = {0};
340  FILE *fp = fopen("/tmp/ckb", "r");
341  if (fp) {
342  fgets(str, sizeof(str), fp);
343  fclose(fp);
344  }
345  current_ckb_pid = *str;
346 }
347 
348 // Register to watch all mouse events, so the daemon can add the modifier keys
349 // back into those events. Called with the same runloop that is used to listen
350 // for input.
351 void register_mouse_event_tap(CFRunLoopTimerRef timer, void* info) {
352  CFRunLoopRef run_loop = info;
353  update_ckb_pid();
354 
355  if (current_ckb_pid) {
356  if (mouse_event_tap && current_ckb_pid != mouse_event_tap_pid) {
357  // If the ckb pid has changed, invalidate the event.
358  CFMachPortInvalidate(mouse_event_tap);
359  mouse_event_tap = NULL;
360  }
361  if (!mouse_event_tap) {
362  // Set mask to catch all mouse events, as the modifier keys
363  // can change the look of the pointer.
364  CGEventMask mask = CGEventMaskBit(kCGEventLeftMouseDown) |
365  CGEventMaskBit(kCGEventLeftMouseUp) |
366  CGEventMaskBit(kCGEventRightMouseDown) |
367  CGEventMaskBit(kCGEventRightMouseUp) |
368  CGEventMaskBit(kCGEventMouseMoved) |
369  CGEventMaskBit(kCGEventLeftMouseDragged) |
370  CGEventMaskBit(kCGEventRightMouseDragged) |
371  CGEventMaskBit(kCGEventScrollWheel) |
372  CGEventMaskBit(kCGEventTabletPointer) |
373  CGEventMaskBit(kCGEventTabletProximity) |
374  CGEventMaskBit(kCGEventOtherMouseDown) |
375  CGEventMaskBit(kCGEventOtherMouseUp) |
376  CGEventMaskBit(kCGEventOtherMouseDragged);
377 
378  // Create the tap. This is what will specify what to call (mouse_event_modifier_callback)
379  // whenever a mouse event happens.
380  mouse_event_tap = CGEventTapCreate(kCGHIDEventTap,
381  kCGHeadInsertEventTap,
382  kCGEventTapOptionDefault,
383  mask,
384  mouse_event_modifier_callback,
385  NULL);
386 
387  if (mouse_event_tap) {
388  // Add the tap to the runloop.
389  CFRunLoopSourceRef run_loop_source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouse_event_tap, 0);
390  if (run_loop_source) {
391  ckb_info("Registering EventTap for modifier keys.\n");
392  CFRunLoopAddSource(run_loop, run_loop_source, kCFRunLoopCommonModes);
393  CGEventTapEnable(mouse_event_tap, true);
394  CFRelease(run_loop_source);
395  }
396  memcpy(&mouse_event_tap_pid, &current_ckb_pid, sizeof mouse_event_tap_pid);
397  } else {
398  ckb_info("EventTap not available");
399  }
400  }
401  }
402 }
403 
404 // input_mac.c
405 extern void keyretrigger(CFRunLoopTimerRef timer, void* info);
406 
407 void* os_inputmain(void* context){
408  usbdevice* kb = context;
409  int index = INDEX_OF(kb, keyboard);
410  // Monitor input transfers on all endpoints for non-RGB devices
411  // For RGB, monitor all but the last, as it's used for input/output
412  int count = IS_RGB_DEV(kb) ? (kb->epcount - 1) : kb->epcount;
413  // Schedule async events for the device on this thread
414  CFRunLoopRef runloop = kb->input_loop = CFRunLoopGetCurrent();
415  for(int i = 0; i < count; i++){
416  CFTypeRef eventsource;
417  kern_return_t res;
418  if(kb->ifusb[i])
419  res = (*kb->ifusb[i])->CreateInterfaceAsyncEventSource(kb->ifusb[i], (CFRunLoopSourceRef*)&eventsource);
420  else if(kb->ifhid[i])
421  res = (*kb->ifhid[i])->getAsyncEventSource(kb->ifhid[i], &eventsource);
422  else
423  continue;
424  if(res != kIOReturnSuccess){
425  ckb_err("Failed to start input thread for %s%d: %x\n", devpath, index, res);
426  return 0;
427  }
428  if(CFGetTypeID(eventsource) == CFRunLoopSourceGetTypeID())
429  CFRunLoopAddSource(runloop, (CFRunLoopSourceRef)eventsource, kCFRunLoopDefaultMode);
430  else if(CFGetTypeID(eventsource) == CFRunLoopTimerGetTypeID())
431  CFRunLoopAddTimer(runloop, (CFRunLoopTimerRef)eventsource, kCFRunLoopDefaultMode);
432  }
433  ckb_info("Starting input thread for %s%d\n", devpath, index);
434 
435  // Start getting reports
436  urbctx input[IFACE_MAX];
437  memset(input, 0, sizeof(input));
438  for(int i = 0; i < count; i++){
439  if(kb->ifusb[i]){
440  usb_iface_t handle = kb->ifusb[i];
441  int pipe = get_pipe_index(handle, kUSBIn);
442  uchar direction, number, transfertype, interval;
443  ushort maxpacketsize;
444  (*handle)->GetPipeProperties(handle, pipe, &direction, &number, &transfertype, &maxpacketsize, &interval);
445  if(direction != kUSBIn && direction != kUSBAnyDirn)
446  continue;
447  uchar* buffer = malloc(maxpacketsize);
448  input[i].kb = kb;
449  input[i].buffer = buffer;
450  input[i].index = i;
451  input[i].pipe = pipe;
452  input[i].maxsize = maxpacketsize;
453  (*handle)->ReadPipeAsync(handle, 1, buffer, maxpacketsize, pipecomplete, input + i);
454  } else if(kb->ifhid[i]){
455  hid_dev_t handle = kb->ifhid[i];
456  long maxsize = hidgetlong(handle, CFSTR(kIOHIDMaxInputReportSizeKey));
457  uchar* buffer = malloc(maxsize);
458  input[i].buffer = buffer;
459  (*handle)->setInputReportCallback(handle, buffer, maxsize, intreport, kb, 0);
460  }
461  }
462 
463  // Start a timer for key repeat broadcasts
464  CFRunLoopTimerContext krctx = { 0, kb, NULL, NULL, NULL };
465  CFRunLoopTimerRef krtimer = kb->krtimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
466  CFAbsoluteTimeGetCurrent() + 0.001, 0.001, // Set it to run every 1ms
467  0, 0,
468  keyretrigger, &krctx);
469  CFRunLoopTimerSetTolerance(krtimer, 0.015); // Set a maximum tolerance of 15ms
470  // We don't actually add the timer to the run loop yet. There's no need to run the function until a key is actually pressed,
471  // so the timer is added and removed dynamically.
472 
473  // Start the run loop
474  while(1){
475  CFRunLoopRun();
476  // If we get here, the device should be disconnected
477  pthread_mutex_lock(imutex(kb));
478  if(!IS_CONNECTED(kb)){
479  pthread_mutex_unlock(imutex(kb));
480  break;
481  }
482  pthread_mutex_unlock(imutex(kb));
483  }
484 
485  // Clean up
486  ckb_info("Stopping input thread for %s%d\n", devpath, index);
487  for(int i = 0; i < count; i++)
488  free(input[i].buffer);
489  return 0;
490 }
491 
492 int os_setupusb(usbdevice* kb){
493  kb->lastkeypress = KEY_NONE;
494  // Get the device firmware version
495  (*kb->handle)->GetDeviceReleaseNumber(kb->handle, &kb->fwversion);
496  return 0;
497 }
498 
499 void os_closeusb(usbdevice* kb){
500  // Close HID handles
501  int count = kb->epcount_hid;
502  for(int i = 0; i < count; i++){
503  hid_dev_t iface = kb->ifhid[i];
504  if(iface){
505  (*iface)->close(iface, kIOHIDOptionsTypeNone);
506  (*iface)->Release(iface);
507  kb->ifhid[i] = 0;
508  }
509  }
510  kb->epcount_hid = 0;
511  // Close USB handles
512  count = kb->epcount_usb;
513  for(int i = 0; i < count; i++){
514  usb_iface_t iface = kb->ifusb[i];
515  if(iface){
516  (*iface)->USBInterfaceClose(iface);
517  (*iface)->Release(iface);
518  kb->ifusb[i] = 0;
519  }
520  }
521  kb->epcount_usb = 0;
522  usb_dev_t iface = kb->handle;
523  if(iface){
524  (*iface)->USBDeviceClose(iface);
525  (*iface)->Release(iface);
526  kb->handle = 0;
527  }
528  // Close input thread
529  if(kb->input_loop){
530  CFRunLoopStop(kb->input_loop);
531  kb->input_loop = 0;
532  }
533 }
534 
535 static void remove_device(void* context, io_service_t device, uint32_t message_type, void* message_argument){
536  if(message_type != kIOMessageServiceIsTerminated)
537  return;
538  usbdevice* kb = context;
539  if(kb){
540  // If the handle is connected to a device, close it
541  pthread_mutex_lock(dmutex(kb));
542  closeusb(kb);
543  pthread_mutex_unlock(dmutex(kb));
544  }
545  IOObjectRelease(device);
546 }
547 
548 // Finds a USB device by location ID. Returns a new device if none was found or -1 if no devices available.
549 // If successful, devmutex[index] will be locked when the function returns. Unlock it when finished.
550 static int find_device(uint16_t idvendor, uint16_t idproduct, uint32_t location, int handle_idx){
551  // Look for any partially-set up boards matching this device
552  for(int i = 1; i < DEV_MAX; i++){
553  if(pthread_mutex_trylock(devmutex + i))
554  // If the mutex is locked then the device is obviously set up already, keep going
555  continue;
556  if(keyboard[i].vendor == idvendor && keyboard[i].product == idproduct){
557  for(int iface = 0; iface <= IFACE_MAX; iface++){
558  if(keyboard[i].location_id[iface] == location){
559  // Matched; continue setting up this device
560  keyboard[i].location_id[handle_idx] = location;
561  // Device mutex remains locked
562  return i;
563  }
564  }
565  }
566  pthread_mutex_unlock(devmutex + i);
567  }
568  // If none was found, grab the first free device
569  for(int i = 1; i < DEV_MAX; i++){
570  if(pthread_mutex_trylock(devmutex + i))
571  continue;
572  if(!keyboard[i].handle){
573  // Mark the device as in use and print out a message
574  keyboard[i].handle = INCOMPLETE;
575  keyboard[i].location_id[handle_idx] = location;
576  keyboard[i].vendor = idvendor;
577  keyboard[i].product = idproduct;
578  // Device mutex remains locked
579  return i;
580  }
581  pthread_mutex_unlock(devmutex + i);
582  }
583  return -1;
584 }
585 
586 static int seize_wait(long location){
587  // HACK: We shouldn't seize the device until it's successfully added to the service registry.
588  // Otherwise, OSX might think there's no keyboard/mouse connected.
589  char location_var[18], location_fixed[18];
590  snprintf(location_var, sizeof(location_var), "@%lx", location);
591  snprintf(location_fixed, sizeof(location_fixed), "@%08x", (int)location);
592  // Open master port (if not done yet)
593  static mach_port_t master = 0;
594  kern_return_t res;
595  if(!master && (res = IOMasterPort(bootstrap_port, &master)) != kIOReturnSuccess){
596  master = 0;
597  ckb_warn("Unable to open master port: 0x%08x\n", res);
598  return -1;
599  }
600  const int max_tries = 20; // give up after ~2s
601  for(int try = 0; try < max_tries; try++){
602  clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = 100000000}, NULL);
603  // Iterate the whole IOService registry
604  io_iterator_t child_iter;
605  if((res = IORegistryCreateIterator(master, kIOServicePlane, kIORegistryIterateRecursively, &child_iter)) != kIOReturnSuccess)
606  return -2;
607 
608  io_registry_entry_t child_service;
609  while((child_service = IOIteratorNext(child_iter)) != 0){
610  io_string_t path;
611  IORegistryEntryGetPath(child_service, kIOServicePlane, path);
612  IOObjectRelease(child_service);
613  // Look for an entry that matches the location of the device and says "HID". If found, we can proceed with adding the device
614  if((strstr(path, location_var) || strstr(path, location_fixed)) && strstr(path, "HID")){
615  IOObjectRelease(child_iter);
616  return 0;
617  }
618  }
619  IOObjectRelease(child_iter);
620  }
621  // Timed out
622  return -3;
623 }
624 
625 static usbdevice* add_usb(usb_dev_t handle, io_object_t** rm_notify){
626  int iface_count = 0, iface_success = 0;
627  io_iterator_t iterator = 0;
628  io_service_t iface = 0;
629  // Get device properties
630  UInt16 idvendor, idproduct;
631  UInt32 location;
632  (*handle)->GetDeviceVendor(handle, &idvendor);
633  (*handle)->GetDeviceProduct(handle, &idproduct);
634  (*handle)->GetLocationID(handle, &location);
635  // Use the location ID key to group the USB handle with the HID handles
636  int index = find_device(idvendor, idproduct, location, 0);
637  if(index == -1){
638  ckb_err("No free devices\n");
639  return 0;
640  }
641  usbdevice* kb = keyboard + index;
642 
643  // Set the handle for the keyboard
644  if(kb->handle && kb->handle != INCOMPLETE){
645  // This should never happen
646  ckb_warn("Tried to set up handle for device ckb%d, but it was already set up. Skipping...\n", index);
647  goto error;
648  }
649  kb->handle = handle;
650 
651  // Read the serial number and name (if not done yet)
652  if(!kb->serial[0] && !kb->name[0]){
653  UInt8 serial_idx, product_idx;
654  if((*handle)->USBGetSerialNumberStringIndex(handle, &serial_idx) == kIOReturnSuccess)
655  usbgetstr(handle, serial_idx, kb->serial, SERIAL_LEN);
656  if((*handle)->USBGetProductStringIndex(handle, &product_idx) == kIOReturnSuccess)
657  usbgetstr(handle, product_idx, kb->name, KB_NAME_LEN);
658  ckb_info("Connecting %s at %s%d\n", keyboard[index].name, devpath, index);
659  }
660 
661  // Iterate through the USB interfaces. Most of these will fail to open because they're already grabbed by the HID system.
662  if(seize_wait(location))
663  ckb_warn("seize_wait failed, connecting anyway...\n");
664  IOUSBFindInterfaceRequest interfaceRequest;
665  interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare;
666  interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
667  interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
668  interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
669  (*handle)->CreateInterfaceIterator(handle, &interfaceRequest, &iterator);
670  // Count the total number of interfaces as well as the number successfully opened.
671  while((iface = IOIteratorNext(iterator)) != 0){
672  if(iface_count >= IFACE_MAX){
673  ckb_warn("Too many interfaces. Dropping the rest.\n");
674  IOObjectRelease(iface);
675  break;
676  }
677  // Get device interface
678  IOCFPlugInInterface** plugin = 0;
679  SInt32 score = 0;
680  kern_return_t err;
681  wait_loop(err, IOCreatePlugInInterfaceForService(iface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score));
682  if(err != kIOReturnSuccess){
683  ckb_err("Failed to create interface plugin: %x\n", err);
684  goto release;
685  }
686  usb_iface_t if_handle;
687  wait_loop(err, (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID183), (LPVOID)&if_handle));
688  if(err != kIOReturnSuccess){
689  ckb_err("QueryInterface failed: %x\n", err);
690  goto release;
691  }
692  // Plugin is no longer needed
693  IODestroyPlugInInterface(plugin);
694 
695  // Get location ID in case it's different from the main USB
696  (*if_handle)->GetLocationID(if_handle, kb->location_id + iface_count + 1);
697  // Try to open the interface. If it succeeds, add it to the device's interface list.
698  err = (*if_handle)->USBInterfaceOpenSeize(if_handle); // no wait_loop here because this is expected to fail
699  if(err == kIOReturnSuccess){
700  kb->ifusb[iface_count] = if_handle;
701  iface_success++;
702  // Register for removal notification
703  IOServiceAddInterestNotification(notify, iface, kIOGeneralInterest, remove_device, kb, kb->rm_notify + 1 + iface_count);
704  } else {
705  kb->ifusb[iface_count] = 0;
706  (*if_handle)->Release(if_handle);
707  }
708 
709 release:
710  iface_count++;
711  IOObjectRelease(iface);
712  }
713  if(iface_count == 0){
714  // This shouldn't happen, but if it does, assume EP count based on what the device is supposed to have
715  iface_count = (HAS_FEATURES(kb, FEAT_RGB) ? 4 : 3);
716  ckb_warn("Unable to count endpoints, assuming %d...\n", iface_count);
717  }
718  kb->epcount = iface_count;
719  kb->epcount_usb = iface_success;
720 
721  // If the HID handles are already opened, set up the device
722  if(HAS_ALL_HANDLES(kb))
723  setupusb(kb);
724  else
725  pthread_mutex_unlock(devmutex + index);
726  *rm_notify = kb->rm_notify;
727  return kb;
728 
729 error:
730  pthread_mutex_unlock(devmutex + index);
731  return 0;
732 }
733 
734 static void iterate_devices_usb(void* context, io_iterator_t iterator){
735  io_service_t device;
737  while((device = IOIteratorNext(iterator)) != 0){
738  IOCFPlugInInterface** plugin = 0;
739  SInt32 score = 0;
740  kern_return_t err;
741  wait_loop(err, IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score));
742  if(err != kIOReturnSuccess){
743  ckb_err("Failed to create device plugin: %x\n", err);
744  goto release;
745  }
746  // Get the device interface
747  usb_dev_t handle;
748  wait_loop(err, (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182), (LPVOID*)&handle));
749  if(err != kIOReturnSuccess){
750  ckb_err("QueryInterface failed: %x\n", err);
751  goto release;
752  }
753  // Plugin is no longer needed
754  IODestroyPlugInInterface(plugin);
755 
756  err = (*handle)->USBDeviceOpenSeize(handle);
757  if(err == kIOReturnExclusiveAccess){
758  // We can't send control transfers but most of the other functions should work
759  ckb_warn("Unable to seize USB handle, continuing anyway...\n");
760  } else if(err != kIOReturnSuccess){
761  ckb_err("USBDeviceOpen failed: %x\n", err);
762  continue;
763  }
764  // Connect it
765  io_object_t* rm_notify = 0;
766  usbdevice* kb = add_usb(handle, &rm_notify);
767  if(kb){
768  // If successful, register for removal notification
769  IOServiceAddInterestNotification(notify, device, kIOGeneralInterest, remove_device, kb, rm_notify);
770  } else
771  // Otherwise, release it now
772  (*handle)->USBDeviceClose(handle);
773 release:
774  IOObjectRelease(device);
775  }
777 }
778 
779 static usbdevice* add_hid(hid_dev_t handle, io_object_t** rm_notify){
780  // Each keyboard generates multiple match events (one for each endpoint)
781  // There's no direct way to tell which of the endpoints this is, but there's a workaround
782  // Each handle has a unique maximum packet size combination, so use that to place them
783  long input = hidgetlong(handle, CFSTR(kIOHIDMaxInputReportSizeKey));
784  long output = hidgetlong(handle, CFSTR(kIOHIDMaxOutputReportSizeKey));
785  long feature = hidgetlong(handle, CFSTR(kIOHIDMaxFeatureReportSizeKey));
786  long fwversion = hidgetlong(handle, CFSTR(kIOHIDVersionNumberKey));
787  int handle_idx;
788 
789  // Handle 3 is for controlling the device (only exists for RGB)
790  if(feature == 64)
791  handle_idx = 3;
792  // Handle 2 is for Corsair inputs, unused on non-RGB
793  else if(feature == 0 &&
794  (((input == 64 ||
795  input == 15) && output == 0) ||
796  (input == 64 && output == 64) || // FW >= 1.20
797  (input <= 1 && output == 64))) // FW >= 2.00 (Scimitar)
798  handle_idx = 2;
799  // Handle 0 is for BIOS mode input (RGB) or non-RGB key input
800  else if((output <= 1 && feature <= 1 &&
801  (input == 8 || // Keyboards
802  input == 7)) || // Mice
803  (fwversion >= 0x300 && // FWv3 hack
804  input == 64 &&
805  output <= 2 &&
806  feature == 1))
807  handle_idx = 0;
808  // Handle 1 is for standard HID input (RGB) or media keys (non-RGB)
809  else if(output <= 1 && feature <= 1 &&
810  (input == 21 || input == 10 ||
811  input == 4 ||
812  input == 64)) // FW >= 2.00 (Scimitar)
813  handle_idx = 1;
814  else {
815  ckb_warn("Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature);
816  return 0;
817  }
818 
819  // Get the model and serial number
820  uint16_t idvendor = hidgetlong(handle, CFSTR(kIOHIDVendorIDKey)), idproduct = hidgetlong(handle, CFSTR(kIOHIDProductIDKey));
821  // Use the location ID key to group the handles together
822  uint32_t location = hidgetlong(handle, CFSTR(kIOHIDLocationIDKey));
823  int index = find_device(idvendor, idproduct, location, handle_idx + 1);
824  if(index == -1){
825  ckb_err("No free devices\n");
826  return 0;
827  }
828  usbdevice* kb = keyboard + index;
829 
830  // Read the serial number and name (if not done yet)
831  if(!keyboard[index].serial[0] && !keyboard[index].name[0]){
832  hidgetstr(handle, CFSTR(kIOHIDSerialNumberKey), keyboard[index].serial, SERIAL_LEN);
833  hidgetstr(handle, CFSTR(kIOHIDProductKey), keyboard[index].name, KB_NAME_LEN);
834  ckb_info("Connecting %s at %s%d\n", keyboard[index].name, devpath, index);
835  }
836 
837 
838  // Set the handle
839  if(kb->ifhid[handle_idx]){
840  // This should never happen
841  ckb_warn("Tried to set up ifhid[%d] for device ckb%d, but it was already set up. Skipping...\n", handle_idx, index);
842  goto error;
843  }
844  kb->ifhid[handle_idx] = handle;
845  kb->epcount_hid++;
846  if(HAS_ALL_HANDLES(kb))
847  // If all handles have been opened, we're ready to set up the device
848  setupusb(kb);
849  else
850  // Otherwise, return and keep going
851  pthread_mutex_unlock(devmutex + index);
852  *rm_notify = kb->rm_notify + IFACE_MAX + 1 + handle_idx;
853  return kb;
854 
855 error:
856  pthread_mutex_unlock(devmutex + index);
857  return 0;
858 }
859 
860 static void iterate_devices_hid(void* context, io_iterator_t iterator){
861  io_service_t device;
863  while((device = IOIteratorNext(iterator)) != 0){
864  // Get the plugin interface for the device
865  IOCFPlugInInterface** plugin = 0;
866  SInt32 score = 0;
867  kern_return_t err;
868  wait_loop(err, IOCreatePlugInInterfaceForService(device, kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &plugin, &score));
869  if(err != kIOReturnSuccess){
870  ckb_err("Failed to create device plugin: %x\n", err);
871  goto release;
872  }
873  // Get the device interface
874  hid_dev_t handle;
875  wait_loop(err, (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOHIDDeviceDeviceInterfaceID), (LPVOID*)&handle));
876  if(err != kIOReturnSuccess){
877  ckb_err("QueryInterface failed: %x\n", err);
878  goto release;
879  }
880  // Plugin is no longer needed
881  IODestroyPlugInInterface(plugin);
882  // Seize the device handle
883  if(seize_wait(hidgetlong(handle, CFSTR(kIOHIDLocationIDKey))))
884  ckb_warn("seize_wait failed, connecting anyway...\n");
885  wait_loop(err, (*handle)->open(handle, kIOHIDOptionsTypeSeizeDevice));
886  if(err != kIOReturnSuccess){
887  ckb_warn("Failed to open device: %x\n", err);
888  goto release;
889  }
890  // Connect it
891  io_object_t* rm_notify = 0;
892  usbdevice* kb = add_hid(handle, &rm_notify);
893  if(kb)
894  // If successful, register for removal notification
895  IOServiceAddInterestNotification(notify, device, kIOGeneralInterest, remove_device, kb, rm_notify);
896  else
897  // Otherwise, release it now
898  (*handle)->close(handle, kIOHIDOptionsTypeSeizeDevice);
899 release:
900  IOObjectRelease(device);
901  }
903 }
904 
905 int usbmain(){
906  int vendor = V_CORSAIR;
907  int products[] = {
908  // Keyboards
910  // Mice
912  };
913 
914  // Setup global variables
915  notify = IONotificationPortCreate(kIOMasterPortDefault);
916  mainloop = CFRunLoopGetCurrent();
917  CFRunLoopAddSource(mainloop, IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
918 
919  // This gets really complicated.
920  // The USB interfaces are *usually* grabbed by the HID system, which makes them inaccessible to ckb.
921  // In this case, we have to get the HID handles and communicate through them.
922  // But sometimes, the interfaces are not grabbed. In this case, we have to talk to the USB device directly.
923  // 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
924  // based on which interfaces were grabbed.
925 
926  // Let's start by searching for USB devices...
927  CFMutableDictionaryRef match = IOServiceMatching(kIOUSBDeviceClassName);
928  CFNumberRef cfvendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
929  CFDictionarySetValue(match, CFSTR(kUSBVendorName), cfvendor);
930  CFRelease(cfvendor);
931  CFMutableArrayRef cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
932  for(uint i = 0; i < sizeof(products) / sizeof(int); i++){
933  int product = products[i];
934  CFNumberRef cfproduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &product);
935  CFArrayAppendValue(cfproducts, cfproduct);
936  CFRelease(cfproduct);
937  }
938  CFDictionarySetValue(match, CFSTR(kUSBProductIdsArrayName), cfproducts);
939  CFRelease(cfproducts);
940 
941  io_iterator_t iterator_usb = 0;
942  IOReturn res = IOServiceAddMatchingNotification(notify, kIOMatchedNotification, match, iterate_devices_usb, 0, &iterator_usb);
943  if(res != kIOReturnSuccess){
944  ckb_fatal("Failed to list USB devices: %x\n", res);
945  return -1;
946  }
947  // Iterate existing devices
948  if(iterator_usb)
949  iterate_devices_usb(0, iterator_usb);
950 
951  // Now move on to HID devices
952  // It is in fact necessary to recreate the matching dictionary, as the keys are different
953  match = IOServiceMatching(kIOHIDDeviceKey);
954  cfvendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
955  CFDictionarySetValue(match, CFSTR(kIOHIDVendorIDKey), cfvendor);
956  CFRelease(cfvendor);
957  cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
958  for(uint i = 0; i < sizeof(products) / sizeof(int); i++){
959  int product = products[i];
960  CFNumberRef cfproduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &product);
961  CFArrayAppendValue(cfproducts, cfproduct);
962  CFRelease(cfproduct);
963  }
964  CFDictionarySetValue(match, CFSTR(kIOHIDProductIDArrayKey), cfproducts);
965  CFRelease(cfproducts);
966 
967  io_iterator_t iterator_hid = 0;
968  res = IOServiceAddMatchingNotification(notify, kIOMatchedNotification, match, iterate_devices_hid, 0, &iterator_hid);
969  if(res != kIOReturnSuccess){
970  ckb_fatal("Failed to list HID devices: %x\n", res);
971  return -1;
972  }
973  // Iterate existing devices
974  if(iterator_hid)
975  iterate_devices_hid(0, iterator_hid);
976 
977  // Create a timer for register mouse event tap
978  CFRunLoopTimerContext rmectx = { 0, mainloop, NULL, NULL, NULL };
979  CFRunLoopTimerRef rmetimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
980  CFAbsoluteTimeGetCurrent() + 5, 5, // Set it to run every 5s
981  0, 0,
982  register_mouse_event_tap, &rmectx);
983  CFRunLoopAddTimer(mainloop, (CFRunLoopTimerRef)rmetimer, kCFRunLoopCommonModes);
984 
985  // Enter loop to scan/connect new devices
986  CFRunLoopRun();
987  return 0;
988 }
989 
990 void usbkill(){
991  CFRunLoopStop(mainloop);
992 }
993 
994 #endif
#define P_K70_RFIRE_NRGB
Definition: usb.h:73
#define KB_NAME_LEN
Definition: structures.h:174
#define MSG_SIZE
Definition: structures.h:176
#define P_GLAIVE
Definition: usb.h:119
void setupusb(usbdevice *kb)
Definition: usb.c:396
#define P_K95_PLATINUM
Definition: usb.h:81
#define P_K65_LUX
Definition: usb.h:53
char name[40+1]
Definition: structures.h:233
#define P_K95
Definition: usb.h:77
double maxsize
Definition: main.c:38
#define IS_CONNECTED(kb)
Definition: device.h:12
usbinput input
Definition: structures.h:245
int usbmain()
Start the USB main loop. Returns program exit code when finished.
Definition: usb_linux.c:793
#define P_STRAFE_NRGB_2
Definition: usb.h:89
#define P_SABRE_N
Definition: usb.h:103
#define P_SABRE_O
Definition: usb.h:99
#define P_K65
Definition: usb.h:49
#define ckb_err(fmt, args...)
Definition: includes.h:49
#define P_K70_NRGB
Definition: usb.h:65
ushort fwversion
Definition: structures.h:239
#define FEAT_RGB
Definition: structures.h:136
usbdevice keyboard[9]
remember all usb devices. Needed for closeusb().
Definition: device.c:10
int os_resetusb(usbdevice *kb, const char *file, int line)
os_resetusb is the os specific implementation for resetting usb
Definition: usb_linux.c:510
void * os_inputmain(void *context)
os_inputmain is run in a separate thread and will be detached from the main thread, so it needs to clean up its own resources.
Definition: usb_linux.c:248
void os_closeusb(usbdevice *kb)
os_closeusb unclaim it, destroy the udev device and clear data structures at kb
Definition: usb_linux.c:448
pthread_mutex_t devmutex[9]
Mutex for handling the usbdevice structure.
Definition: device.c:12
#define P_K63_NRGB
Definition: usb.h:45
#define P_M65
Definition: usb.h:93
int os_usbrecv(usbdevice *kb, uchar *in_msg, const char *file, int line)
os_usbrecv receives a max MSGSIZE long buffer from usb device
Definition: usb_linux.c:130
Definition: keymap.h:49
int os_setupusb(usbdevice *kb)
os_setupusb OS-specific setup for a specific usb device.
Definition: usb_linux.c:548
uchar ileds
Definition: structures.h:247
char active
Definition: structures.h:231
#define P_STRAFE
Definition: usb.h:85
#define P_SABRE_L
Definition: usb.h:101
#define IFACE_MAX
Definition: structures.h:177
int epcount
Definition: structures.h:215
#define P_HARPOON
Definition: usb.h:115
void inputupdate(usbdevice *kb)
Definition: input.c:241
#define ckb_fatal(fmt, args...)
Definition: includes.h:46
#define IS_MOUSE(vendor, product)
Mouse vs keyboard test.
Definition: usb.h:163
unsigned char uchar
Definition: includes.h:24
QString devpath
Definition: kbmanager.cpp:4
#define KEY_NONE
Definition: keymap.h:7
#define P_K68
Definition: usb.h:59
short rel_x
Definition: structures.h:132
#define P_M65_PRO
Definition: usb.h:95
#define P_K70_RFIRE
Definition: usb.h:71
#define ckb_warn(fmt, args...)
Definition: includes.h:52
void corsair_mousecopy(unsigned char *kbinput, int endpoint, const unsigned char *urbinput)
Definition: keymap.c:429
void hid_mouse_translate(unsigned char *kbinput, short *xaxis, short *yaxis, int endpoint, int length, const unsigned char *urbinput, ushort fwversion)
Definition: keymap.c:391
#define ckb_info(fmt, args...)
Definition: includes.h:55
#define P_K65_NRGB
Definition: usb.h:51
#define P_SCIMITAR_PRO
Definition: usb.h:111
short product
Definition: structures.h:237
#define euid_guard_start
Definition: os.h:40
#define INDEX_OF(entry, array)
Definition: includes.h:27
#define P_K70
Definition: usb.h:63
short rel_y
Definition: structures.h:132
unsigned short ushort
Definition: includes.h:25
#define P_K95_NRGB
Definition: usb.h:79
#define P_SABRE_O2
Definition: usb.h:105
int closeusb(usbdevice *kb)
Definition: usb.c:687
char serial[34]
Definition: structures.h:235
#define IS_RGB_DEV(kb)
For calling with a usbdevice*, vendor and product are extracted and IS_RGB() is returned.
Definition: usb.h:154
#define imutex(kb)
Definition: device.h:22
void hid_kb_translate(unsigned char *kbinput, int endpoint, int length, const unsigned char *urbinput)
Definition: keymap.c:246
#define P_STRAFE_NRGB
Definition: usb.h:87
int handle
Definition: structures.h:187
#define IS_MOUSE_DEV(kb)
For calling with a usbdevice*, vendor and product are extracted and IS_MOUSE() is returned...
Definition: usb.h:166
#define HAS_FEATURES(kb, feat)
Definition: structures.h:157
Definitions for using USB interface.
#define P_K70_LUX
Definition: usb.h:67
void os_sendindicators(usbdevice *kb)
os_sendindicators update the indicators for the special keys (Numlock, Capslock and what else...
Definition: usb_linux.c:214
void corsair_kbcopy(unsigned char *kbinput, int endpoint, const unsigned char *urbinput)
Definition: keymap.c:420
#define V_CORSAIR
For the following Defines please see "Detailed Description".
Definition: usb.h:42
int _nk95cmd(usbdevice *kb, uchar bRequest, ushort wValue, const char *file, int line)
_nk95cmd If we control a non RGB keyboard, set the keyboard via ioctl with usbdevfs_ctrltransfer ...
Definition: usb_linux.c:189
#define P_K70_LUX_NRGB
Definition: usb.h:69
#define DEV_MAX
Definition: device.h:8
#define P_SCIMITAR
Definition: usb.h:109
uchar keys[((((152+22+12)+25)+7)/8)]
Definition: structures.h:130
void usbkill()
Stop the USB system.
Definition: usb_linux.c:853
int os_usbsend(usbdevice *kb, const uchar *out_msg, int is_recv, const char *file, int line)
os_usbsend sends a data packet (MSG_SIZE = 64) Bytes long
Definition: usb_linux.c:68
#define dmutex(kb)
Definition: device.h:18
void u16dec(ushort *in, char *out, size_t *srclen, size_t *dstlen)
Definition: profile.c:105
short vendor
Definition: structures.h:237
#define ckb_err_fn(fmt, file, line, args...)
Definition: includes.h:48
#define IS_V2_OVERRIDE(kb)
Used when a device has a firmware with a low version number that uses the new protocol.
Definition: usb.h:172
#define euid_guard_stop
Definition: os.h:41
#define SERIAL_LEN
Definition: structures.h:175
#define P_K65_RFIRE
Definition: usb.h:55