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