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.c File Reference
#include "command.h"
#include "device.h"
#include "devnode.h"
#include "firmware.h"
#include "input.h"
#include "led.h"
#include "notify.h"
#include "profile.h"
#include "usb.h"
+ Include dependency graph for usb.c:

Go to the source code of this file.

Functions

const char * vendor_str (short vendor)
 brief . More...
 
const char * product_str (short product)
 brief . More...
 
static const devcmdget_vtable (short vendor, short product)
 brief . More...
 
static void * devmain (usbdevice *kb)
 brief . More...
 
static void * _setupusb (void *context)
 brief . More...
 
void setupusb (usbdevice *kb)
 
int revertusb (usbdevice *kb)
 
int _resetusb (usbdevice *kb, const char *file, int line)
 
int usb_tryreset (usbdevice *kb)
 
int _usbsend (usbdevice *kb, const uchar *messages, int count, const char *file, int line)
 
int _usbrecv (usbdevice *kb, const uchar *out_msg, uchar *in_msg, const char *file, int line)
 
int closeusb (usbdevice *kb)
 

Variables

pthread_mutex_t usbmutex = PTHREAD_MUTEX_INITIALIZER
 brief . More...
 
volatile int reset_stop = 0
 brief . More...
 
int features_mask = -1
 brief . More...
 
int hwload_mode
 hwload_mode is defined in device.c More...
 

Function Documentation

int _resetusb ( usbdevice kb,
const char *  file,
int  line 
)

_resetusb Reset a USB device.

First reset the device via os_resetusb() after a long delay (it may send something to the host). If this worked (retval == 0), give the device another long delay Then perform the initialization via the device specific start() function entry in kb->vtable and if this is successful also, return the result of the device depenten updatergb() with force=true.

Definition at line 436 of file usb.c.

References usbdevice::active, DELAY_LONG, os_resetusb(), and usbdevice::vtable.

436  {
437  // Perform a USB reset
438  DELAY_LONG(kb);
439  int res = os_resetusb(kb, file, line);
440  if(res)
441  return res;
442  DELAY_LONG(kb);
443  // Re-initialize the device
444  if(kb->vtable->start(kb, kb->active) != 0)
445  return -1;
446  if(kb->vtable->updatergb(kb, 1) != 0)
447  return -1;
448  return 0;
449 }
#define DELAY_LONG(kb)
The longest delay takes place where something went wrong (eg when resetting the device) ...
Definition: usb.h:186
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
char active
Definition: structures.h:231
const union devcmd * vtable
Definition: structures.h:180

+ Here is the call graph for this function:

static void* _setupusb ( void *  context)
static

_setupusb A horrible function for setting up an usb device

Parameters
contextAs _setupusb() is called as a new thread, the kb* is transferred as void*
Returns
a ptread_t* 0, here casted as void*. Retval is always null

The basic structure of the function is somewhat habituated. It is more like an assembler routine than a structured program. This is not really bad, but just getting used to.

After every action, which can be practically fault-prone, the routine goes into the same error handling: It goes via goto to one of two exit labels. The difference is whether or not an unlock has to be performed on the imutex variable. In both cases, closeusb() is called, then an unlock is performed on the dmutex.

The only case where this error handling is not performed is the correct return of the call to devmain(). Here simply the return value of devmain() is passed to the caller.

In either case, the routine terminates with a void* 0 because either devmain() has returned constant null or the routine itself returns zero.

The basic idea of this routine is the following:


First some initialization of kb standard structured and local vars is done.

  • kb is set to the pointer given from start environment
  • local vars vendor and product are set to the values from the corresponding fields of kb
  • local var vt and the kb->vtable are both set to the retval of get_vtable()
  • kb->features are set depending on the type of hardware connected:
    • set either to standard non rgb (all common flags like binding, notify, FW, hardware-loading etc) or in case of RGB-device set to standard + RGB, pollrate-change and fw-update
    • exclude all features which are disabled via feature_mask (set by daemon CLI parameters)
    • if it is a mouse, add adjust-rate
    • if it is a monochrome device, set the flag for RGB-protocol, but single color
  • the standard delay time is initialized in kb->usbdelay
  • A fixed 100ms wait is the start. Although the DELAY_LONG macro is given a parameter, it is ignored. Occasionally refactor it.
  • The first relevant point is the operating system-specific opening of the interface in os_setupusb(). As a result, some parameters should be set in kb (name, serial, fwversion, epcount = number of usb endpoints), and all endpoints should be claimed with usbclaim(). Claiming is the only point where os_setupusb() can produce an error (-1, otherwise 0).
  • The following two statements deal with possible errors when setting the kb values in the current routine: If the version or the name was not read correctly, they are set to default values:
    • serial is set to "<vendor>: <product> -NoID"
    • the name is set to "<vendor> <product>".
  • Then the user level input subsystem is activated via os_openinput(). There are two file descriptors, one for the mouse and one for the keyboard. As mentioned in structures.h, not the just opened FD numbers are stored under kb->uinput_kb or kb->uinput_mouse, but the values increased by 1! The reason is, if the open fails or not open has been done until now, that struct member is set to 0, not to -1 or other negative value. So all usage of this kb->handle must be something like "kb->handle - 1", as you can find it in the code.
  • The next action is to create a separate thread, which gets as parameter kb and starts with os_inputmain(). The thread is immediately detached so that it can return its resource completely independently if it should terminate.
  • The same happens with os_setupindicators(), which initially initializes all LED variables in kb to off and then starts the _ledthread() thread with kb as parameter and then detaches it. Here again only the generation of the thread can fail.
  • Via an entry in the vable (allocprofile, identical for all three vtable types), allocprofile() is called in profile.c. With a valid parameter kb, a usbprofile structure is allocated and stored as a kb->profile. Then initmode() is called for each of the initializable modes (MODE_COUNT, currently 6). This procedure creates the memory space for the mode information, initializes the range to 0, and then sets the light.forceupdate and dpi.forceupdate to true. This forces an update later in the initialization of the device.
    The first mode is set as the current mode and two force flags are set (this seems to be mode-intersecting flags for light and update).
    Warning
    There is no error handling for the allocprofile() and initmode() procedures. However, since they allocate storage areas, the subsequent assignments and initializations can run in a SEGV.
  • Not completely understandable is why now via the vtable the function updateindicators() is called. But this actually happens in the just started thread _ledthread(). Either the initialization is wrong und must done here with force or the overview is lost, what happens when...
    Regardless: For a mouse nothing happens here, for a keyboard updateindicators_kb() is called via the entry in kb->vtable. The first parameter is kb again, the second is constant 1 (means force = true). This causes the LED status to be sent after a 5ms delay via os_sendindicators() (ioctl with a usbdevfs_ctrltransfer).
    The notification is sent to all currently open notification channels then.
    Setupindicators() and with it updateindicators_kb() can fail.
  • From this point - if an error is detected - the error label is addressed by goto statement, which first performs an unlock on the imutex. This is interesting because the next statement is exactly this: An unlock on the imutex.
  • Via vtable the kb->start() function is called next. This is the same for a mouse and an RGB keyboard: start_dev(), for a non RGB keyboard it is start_kb_nrgb().
    First parameter is as always kb, second is 0 (makeactive = false).
    • In start_kb_nrgb() set the keyboard into a so-called software mode (NK95_HWOFF) via ioctl with usbdevfs_ctrltransfer in function _nk95cmd(), which will in turn is called via macro nk95cmd() via start_kb_nrgb().
      Then two dummy values (active and pollrate) are set in the kb structure and ready.
    • start_dev() does a bit more - because this function is for both mouse and keyboard. start_dev() calls - after setting an extended timeout parameter - _start_dev(). Both are located in device.c.
    • First, _start_dev() attempts to determine the firmware version of the device, but only if two conditions are met: hwload-mode is not null (then hw-loading is disabled) and the device has the FEAT_HWLOAD feature. Then the firmware and the poll rate are fetched via getfwversion().
      If hwload_mode is set to "load only once" (==1), then the HWLOAD feature is masked, so that no further reading can take place.
    • Now check if device needs a firmware update. If so, set it up and leave the function without error.
    • Else load the hardware profile from device if the hw-pointer is not set and hw-loading is possible and allowed.
      Return error if mode == 2 (load always) and loading got an error. Else mask the HWLOAD feature, because hwload must be 1 and the error couold be a repeated hw-reading.
      Puh, that is real Horror code. It seems to be not faulty, but completely unreadable.
    • Finally, the second parameter of _startdev() is used to check whether the device is to be activated. Depending on the parameter, the active or the idle-member in the correspondig vtable is called. These are device-dependent again:
      Device active idle
      RGB Keyboard cmd_active_kb() means: start the device with a lot of kb-specific initializers (software controlled mode) cmd_idle_kb() set the device with a lot of kb-specific initializers into the hardware controlled mode)
      non RGB Keyboard cmd_io_none() means: Do nothing cmd_io_none() means: Do nothing
      Mouse cmd_active_mouse() similar to cmd_active_kb() cmd_idle_mouse similar to cmd_idle_kb()
  • If either start() succeeded or the next following usb_tryreset(), it goes on, otherwise again a hard abort occurs.
  • Next, go to mkdevpath(). After securing the EUID (effective UID) especially for macOS, work starts really in _mkdevpath(). Create - no matter how many devices were registered - either the ckb0/ files version, pid and connected or the cmd command fifo, the first notification fifo notify0, model and serial as well as the features of the device and the pollrate.
  • If all this is done and no error has occurred, a debug info is printed ("Setup finished for ckbx") updateconnected() writes the new device into the text file under ckb0/ and devmain() is called.

devmain()'s return value is returned by _setupusb() when we terminate.

  • The remaining code lines are the two exit labels as described above

Definition at line 224 of file usb.c.

References ckb_info, closeusb(), DELAY_LONG, devmain(), devpath, dmutex, FEAT_ADJRATE, FEAT_MONOCHROME, FEAT_STD_NRGB, FEAT_STD_RGB, usbdevice::features, features_mask, get_vtable(), imutex, INDEX_OF, usbdevice::inputthread, IS_MONOCHROME, IS_MOUSE, IS_RGB, KB_NAME_LEN, keyboard, mkdevpath(), usbdevice::name, os_inputmain(), os_inputopen(), os_setupindicators(), os_setupusb(), usbdevice::product, product_str(), usbdevice::serial, SERIAL_LEN, updateconnected(), USB_DELAY_DEFAULT, usb_tryreset(), usbdevice::usbdelay, usbdevice::vendor, vendor_str(), and usbdevice::vtable.

Referenced by setupusb().

224  {
237  usbdevice* kb = context;
238  // Set standard fields
239  short vendor = kb->vendor, product = kb->product;
240  const devcmd* vt = kb->vtable = get_vtable(vendor, product);
241  kb->features = (IS_RGB(vendor, product) ? FEAT_STD_RGB : FEAT_STD_NRGB) & features_mask;
242  if(IS_MOUSE(vendor, product)) kb->features |= FEAT_ADJRATE;
243  if(IS_MONOCHROME(vendor, product)) kb->features |= FEAT_MONOCHROME;
245 
246  // Perform OS-specific setup
250  DELAY_LONG(kb);
251 
257  if(os_setupusb(kb))
258  goto fail;
259 
265  // Make up a device name and serial if they weren't assigned
266  if(!kb->serial[0])
267  snprintf(kb->serial, SERIAL_LEN, "%04x:%04x-NoID", kb->vendor, kb->product);
268  if(!kb->name[0])
269  snprintf(kb->name, KB_NAME_LEN, "%s %s", vendor_str(kb->vendor), product_str(kb->product));
270 
271  // Set up an input device for key events
279  if(os_inputopen(kb))
280  goto fail;
284  if(pthread_create(&kb->inputthread, 0, os_inputmain, kb))
285  goto fail;
286  pthread_detach(kb->inputthread);
292  if(os_setupindicators(kb))
293  goto fail;
294 
295  // Set up device
308  vt->allocprofile(kb);
319  vt->updateindicators(kb, 1);
324  pthread_mutex_unlock(imutex(kb));
358  if(vt->start(kb, 0) && usb_tryreset(kb))
359  goto fail_noinput;
365  // Make /dev path
366  if(mkdevpath(kb))
367  goto fail_noinput;
373  // Finished. Enter main loop
374  int index = INDEX_OF(kb, keyboard);
375  ckb_info("Setup finished for %s%d\n", devpath, index);
376  updateconnected();
379  return devmain(kb);
382  fail:
383  pthread_mutex_unlock(imutex(kb));
384  fail_noinput:
385  closeusb(kb);
386  pthread_mutex_unlock(dmutex(kb));
387  return 0;
388 }
#define KB_NAME_LEN
Definition: structures.h:174
static void * devmain(usbdevice *kb)
brief .
Definition: usb.c:145
Definition: command.h:73
int usb_tryreset(usbdevice *kb)
Definition: usb.c:475
char name[40+1]
Definition: structures.h:233
const char * vendor_str(short vendor)
brief .
Definition: usb.c:43
#define DELAY_LONG(kb)
The longest delay takes place where something went wrong (eg when resetting the device) ...
Definition: usb.h:186
int mkdevpath(usbdevice *kb)
Create a dev path for the keyboard at index. Returns 0 on success.
Definition: devnode.c:268
#define IS_RGB(vendor, product)
RGB vs non-RGB test (note: non-RGB Strafe is still considered "RGB" in that it shares the same protoc...
Definition: usb.h:146
pthread_t inputthread
Definition: structures.h:219
usbdevice keyboard[9]
remember all usb devices. Needed for closeusb().
Definition: device.c:10
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
int os_setupusb(usbdevice *kb)
os_setupusb OS-specific setup for a specific usb device.
Definition: usb_linux.c:548
#define IS_MONOCHROME(vendor, product)
The difference between non RGB and monochrome is, that monochrome has lights, but just in one color...
Definition: usb.h:151
#define IS_MOUSE(vendor, product)
Mouse vs keyboard test.
Definition: usb.h:163
QString devpath
Definition: kbmanager.cpp:4
int os_inputopen(usbdevice *kb)
os_inputopen
Definition: input_linux.c:55
static const devcmd * get_vtable(short vendor, short product)
brief .
Definition: usb.c:112
int features_mask
brief .
Definition: usb.c:35
#define ckb_info(fmt, args...)
Definition: includes.h:55
short product
Definition: structures.h:237
#define INDEX_OF(entry, array)
Definition: includes.h:27
#define USB_DELAY_DEFAULT
This constant is used to initialize kb->usbdelay. It is used in many places (see macros above) but of...
Definition: usb.h:192
#define FEAT_STD_RGB
Definition: structures.h:152
const char * product_str(short product)
brief .
Definition: usb.c:70
const union devcmd * vtable
Definition: structures.h:180
#define FEAT_MONOCHROME
Definition: structures.h:137
int os_setupindicators(usbdevice *kb)
Definition: input_linux.c:189
int closeusb(usbdevice *kb)
Definition: usb.c:687
char serial[34]
Definition: structures.h:235
#define imutex(kb)
Definition: device.h:22
ushort features
Definition: structures.h:229
char usbdelay
Definition: structures.h:243
#define FEAT_STD_NRGB
Definition: structures.h:153
#define dmutex(kb)
Definition: device.h:18
#define FEAT_ADJRATE
Definition: structures.h:139
short vendor
Definition: structures.h:237
void updateconnected()
Update the list of connected devices.
Definition: devnode.c:81
#define SERIAL_LEN
Definition: structures.h:175

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

int _usbrecv ( usbdevice kb,
const uchar out_msg,
uchar in_msg,
const char *  file,
int  line 
)

_usbrecv Request data from a USB device by first sending an output packet and then reading the response.

To fully understand this, you need to know about usb: All control is at the usb host (the CPU). If the device wants to communicate something to the host, it must wait for the host to ask. The usb protocol defines the cycles and periods in which actions are to be taken.

So in order to receive a data packet from the device, the host must first send a send request.
This is done by _usbrecv() in the first block by sending the MSG_SIZE large data block from out_msg via os_usbsend() as it is a machine depending implementation. The usb target device is as always determined over kb.

For os_usbsend() to know that it is a receive request, the is_recv parameter is set to true (1). With this, os_usbsend () generates a control package for the hardware, not a data packet.

If sending of the control package is not successful, a maximum of 5 times the transmission is repeated (including the first attempt). If a non-cancelable error is signaled or the drive is stopped via reset_stop, _usbrecv() immediately returns 0.

After this, the function waits for the requested response from the device using os_usbrecv ().

os_usbrecv() returns 0, -1 or something else.
Zero signals a serious error which is not treatable and _usbrecv() also returns 0.
-1 means that it is a treatable error - a timeout for example - and therefore the next transfer attempt is started after a long pause (DELAY_LONG) if not reset_stop or the wrong hwload_mode require a termination with a return value of 0.

After 5 attempts, _usbrecv () returns and returns 0 as well as an error message.

When data is received, the number of received bytes is returned. This should always be MSG_SIZE, but os_usbrecv() can also return less. It should not be more, because then there would be an unhandled buffer overflow, but it could be less. This would be signaled in os_usbrecv () with a message.

The buffers behind out_msg and in_msg are MSG_SIZE at least (currently 64 Bytes). More is ok but useless, less brings unpredictable behavior.

< Synchonization between macro and color information

Definition at line 611 of file usb.c.

References ckb_err_fn, DELAY_LONG, DELAY_MEDIUM, DELAY_SHORT, hwload_mode, mmutex, os_usbrecv(), os_usbsend(), and reset_stop.

611  {
612  // Try a maximum of 5 times
613  for (int try = 0; try < 5; try++) {
614  // Send the output message
615  pthread_mutex_lock(mmutex(kb));
616  DELAY_SHORT(kb);
617  int res = os_usbsend(kb, out_msg, 1, file, line);
618  pthread_mutex_unlock(mmutex(kb));
619  if (res == 0)
620  return 0;
621  else if (res == -1) {
622  // Retry on temporary failure
623  if (reset_stop)
624  return 0;
625  DELAY_LONG(kb);
626  continue;
627  }
628  // Wait for the response
629  DELAY_MEDIUM(kb);
630  res = os_usbrecv(kb, in_msg, file, line);
631  if(res == 0)
632  return 0;
633  else if(res != -1)
634  return res;
635  if(reset_stop || hwload_mode != 2)
636  return 0;
637  DELAY_LONG(kb);
638  }
639  // Give up
640  ckb_err_fn("Too many send/recv failures. Dropping.\n", file, line);
641  return 0;
642 }
#define mmutex(kb)
Definition: device.h:26
#define DELAY_MEDIUM(kb)
the medium delay is used after sending a command before waiting for the answer.
Definition: usb.h:182
#define DELAY_SHORT(kb)
USB delays for when the keyboards get picky about timing That was the original comment, but it is used anytime.
Definition: usb.h:178
#define DELAY_LONG(kb)
The longest delay takes place where something went wrong (eg when resetting the device) ...
Definition: usb.h:186
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
volatile int reset_stop
brief .
Definition: usb.c:25
int hwload_mode
hwload_mode is defined in device.c
Definition: device.c:7
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 ckb_err_fn(fmt, file, line, args...)
Definition: includes.h:48

+ Here is the call graph for this function:

int _usbsend ( usbdevice kb,
const uchar messages,
int  count,
const char *  file,
int  line 
)

_usbsend send a logical message completely to the given device

Todo:
A lot of different conditions are combined in this code. Don't think, it is good in every combination...

The main task of _usbsend () is to transfer the complete logical message from the buffer beginning with messages to count * MSG_SIZE.
According to usb 2.0 specification, a USB transmits a maximum of 64 byte user data packets. For the transmission of longer messages we need a segmentation. And that is exactly what happens here.

The message is given one by one to os_usbsend() in MSG_SIZE (= 64) byte large bites.

Attention
This means that the buffer given as argument must be n * MSG_SIZE Byte long.

An essential constant parameter which is relevant for os_usbsend() only is is_recv = 0, which means sending.

Now it gets a little complicated again:

  • If os_usbsend() returns 0, only zero bytes could be sent in one of the packets, or it was an error (-1 from the systemcall), but not a timeout. How many Bytes were sent in total from earlier calls does not seem to matter, _usbsend() returns a total of 0.
  • Returns os_usbsend() -1, first check if reset_stop is set globally or (incomprehensible) hwload_mode is not set to "always". In either case, _usbsend() returns 0, otherwise it is assumed to be a temporary transfer error and it simply retransmits the physical packet after a long delay.
  • If the return value of os_usbsend() was neither 0 nor -1, it specifies the numer of bytes transferred.
    Here is an information hiding conflict with os_usbsend() (at least in the Linux version):
    If os_usbsend() can not transfer the entire packet, errors are thrown and the number of bytes sent is returned. _usbsend() interprets this as well and remembers the total number of bytes transferred in the local variable total_sent. Subsequently, however, transmission is continued with the next complete MSG_SIZE block and not with the first of the possibly missing bytes.
    Todo:
    Check whether this is the same in the macOS variant. It is not dramatic, but if errors occur, it can certainly irritate the devices completely if they receive incomplete data streams. Do we have errors with the messages "Wrote YY bytes (expected 64)" in the system logs? If not, we do not need to look any further.

When the last packet is transferred, _usbsend() returns the effectively counted set of bytes (from total_sent). This at least gives the caller the opportunity to check whether something has been lost in the middle.

A bit strange is the structure of the program: Handling the count MSG_SIZE blocks to be transferred is done in the outer for (...) loop. Repeating the transfer with a treatable error is managed by the inner while(1) loop.
This must be considered when reading the code; The "break" on successful block transfer leaves the inner while, not the for (...).

< Synchonization between macro and color information

Definition at line 542 of file usb.c.

References DELAY_LONG, DELAY_SHORT, hwload_mode, mmutex, MSG_SIZE, os_usbsend(), and reset_stop.

542  {
543  int total_sent = 0;
544  for(int i = 0; i < count; i++){
545  // Send each message via the OS function
546  while(1){
547  pthread_mutex_lock(mmutex(kb));
548  DELAY_SHORT(kb);
549  int res = os_usbsend(kb, messages + i * MSG_SIZE, 0, file, line);
550  pthread_mutex_unlock(mmutex(kb));
551  if(res == 0)
552  return 0;
553  else if(res != -1){
554  total_sent += res;
555  break;
556  }
557  // Stop immediately if the program is shutting down or hardware load is set to tryonce
558  if(reset_stop || hwload_mode != 2)
559  return 0;
560  // Retry as long as the result is temporary failure
561  DELAY_LONG(kb);
562  }
563  }
564  return total_sent;
565 }
#define MSG_SIZE
Definition: structures.h:176
#define mmutex(kb)
Definition: device.h:26
#define DELAY_SHORT(kb)
USB delays for when the keyboards get picky about timing That was the original comment, but it is used anytime.
Definition: usb.h:178
#define DELAY_LONG(kb)
The longest delay takes place where something went wrong (eg when resetting the device) ...
Definition: usb.h:186
volatile int reset_stop
brief .
Definition: usb.c:25
int hwload_mode
hwload_mode is defined in device.c
Definition: device.c:7
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

+ Here is the call graph for this function:

int closeusb ( usbdevice kb)

closeusb Close a USB device and remove device entry.

An imutex lock ensures first of all, that no communication is currently running from the viewpoint of the driver to the user input device (ie the virtual driver with which characters or mouse movements are sent from the daemon to the operating system as inputs).

If the kb has an acceptable value != 0, the index of the device is looked for and with this index os_inputclose() is called. After this no more characters can be sent to the operating system.

Then the connection to the usb device is capped by os_closeusb().

Todo:
What is not yet comprehensible is the call to updateconnected() BEFORE os_closeusb(). Should that be in the other sequence? Or is updateconnected() not displaying the connected usb devices, but the representation which uinput devices are loaded? Questions about questions ...

If there is no valid handle, only updateconnected() is called. We are probably trying to disconnect a connection under construction. Not clear.

The cmd pipe as well as all open notify pipes are deleted via rmdevpath ().
This means that nothing can happen to the input path - so the device-specific imutex is unlocked again and remains unlocked.

Also the dmutex is unlocked now, but only to join the thread, which was originally taken under kb->thread (which started with _setupusb()) with pthread_join() again. Because of the closed devices that thread would have to quit sometime

See Also
the hack note with rmdevpath())

As soon as the thread is caught, the dmutex is locked again, which is what I do not understand yet: What other thread can do usb communication now?
If the vtabel exists for the given kb (why not? It seems to have race conditions here!!), via the vtable the actually device-specific, but still everywhere identical freeprofile() is called. This frees areas that are no longer needed. Then the usbdevice structure in its array is set to zero completely.

Error handling is rather unusual in closeusb(); Everything works (no matter what the called functions return), and closeusb() always returns zero (success).

Definition at line 687 of file usb.c.

References ckb_info, devpath, dmutex, usbdevice::handle, imutex, INDEX_OF, keyboard, os_closeusb(), os_inputclose(), rmdevpath(), usbdevice::thread, updateconnected(), and usbdevice::vtable.

Referenced by _setupusb(), devmain(), quitWithLock(), and usb_rm_device().

687  {
688  pthread_mutex_lock(imutex(kb));
689  if(kb->handle){
690  int index = INDEX_OF(kb, keyboard);
691  ckb_info("Disconnecting %s%d\n", devpath, index);
692  os_inputclose(kb);
693  updateconnected();
694  // Close USB device
695  os_closeusb(kb);
696  } else
697  updateconnected();
698  rmdevpath(kb);
699 
700  // Wait for thread to close
701  pthread_mutex_unlock(imutex(kb));
702  pthread_mutex_unlock(dmutex(kb));
703  pthread_join(kb->thread, 0);
704  pthread_mutex_lock(dmutex(kb));
705 
706  // Delete the profile and the control path
707  if(!kb->vtable)
708  return 0;
709  kb->vtable->freeprofile(kb);
710  memset(kb, 0, sizeof(usbdevice));
711  return 0;
712 }
usbdevice keyboard[9]
remember all usb devices. Needed for closeusb().
Definition: device.c:10
void os_closeusb(usbdevice *kb)
os_closeusb unclaim it, destroy the udev device and clear data structures at kb
Definition: usb_linux.c:448
QString devpath
Definition: kbmanager.cpp:4
#define ckb_info(fmt, args...)
Definition: includes.h:55
#define INDEX_OF(entry, array)
Definition: includes.h:27
const union devcmd * vtable
Definition: structures.h:180
#define imutex(kb)
Definition: device.h:22
int handle
Definition: structures.h:187
void os_inputclose(usbdevice *kb)
Definition: input_linux.c:76
pthread_t thread
Definition: structures.h:217
#define dmutex(kb)
Definition: device.h:18
int rmdevpath(usbdevice *kb)
Remove the dev path for the keyboard at index. Returns 0 on success.
Definition: devnode.c:275
void updateconnected()
Update the list of connected devices.
Definition: devnode.c:81

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void* devmain ( usbdevice kb)
static

devmain is called by _setupusb

Parameters
kbthe pointer to the device. Even if it has the name kb, it is valid also for a mouse (the whole driver seems to be implemented first for a keyboard).
Returns
always a nullptr

Synchronization

The syncing via mutexes is interesting:

  1. imutex (the Input mutex)
    This one is locked in setupusb(). That function does only two things: Locking the mutex and trying to start a thread at _setupusb(). _setupusb() unlocks imutex after getting some buffers and initalizing internal structures from the indicators (this function often gets problems with error messages like "unable to read indicators" or "Timeout bla blubb").
    Warning
    have a look at updateindicators() later.
    if creating the thread is not successful, the imutex remains blocked. Have a look at setupusb() later.
  2. dmutex (the Device mutex)
    This one is very interesting, because it is handled in devmain(). It seems that it is locked only in _ledthread(), which is a thread created in os_setupindicators(). os_setupindicators() again is called in _setupusb() long before calling devmain(). So this mutex is locked when we start the function as the old comment says.
    Before reading from the FIFO and direct afterwards an unlock..lock sequence is implemented here. Even if only the function readlines() should be surrounded by the unlock..lock, the variable definition of the line pointer is also included here. Not nice, but does not bother either. Probably the Unlock..lock is needed so that now another process can change the control structure linectx while we wait in readlines().
    Todo:
    Hope to find the need for dmutex usage later.
    Should this function be declared as pthread_t* function, because of the defintion of pthread-create? But void* works also...
Attention
dmutex should still be locked when this is called

First a readlines_ctx buffer structure is initialized by readlines_ctx_init().

After some setup functions, beginning in _setupusb() which has called devmain(), we read the command input-Fifo designated to that device in an endless loop. This loop has two possible exits (plus reaction to signals, not mentioned here).

If the reading via readlines() is successful (we might have read multiple lines), the interpretation is done by readcmd() iff the connection to the device is still available (checked via IS_CONNECTED(kb)). This is true if the kb-structure has a handle and an event pointer both != Null). If not, the loop is left (the first exit point).

if nothing is in the line buffer (some magic interrupt?), continue in the endless while without any reaction.

Todo:
readcmd() gets a line, not lines. Have a look on that later.
Is the condition IS_CONNECTED valid? What functions change the condititon for the macro?

If interpretation and communication with the usb device got errors, they are signalled by readcmd() (non zero retcode). In this case the usb device is closed via closeusb() and the endless loop is left (the second exit point).

After leaving the endless loop the readlines-ctx structure and its buffers are freed by readlines_ctx_free().

Definition at line 145 of file usb.c.

References closeusb(), dmutex, usbdevice::infifo, IS_CONNECTED, readcmd(), readlines(), readlines_ctx_free(), and readlines_ctx_init().

Referenced by _setupusb().

145  {
147  int kbfifo = kb->infifo - 1;
150  readlines_ctx linectx;
151  readlines_ctx_init(&linectx);
156  while(1){
163  pthread_mutex_unlock(dmutex(kb));
164  // Read from FIFO
165  const char* line;
166  int lines = readlines(kbfifo, linectx, &line);
167  pthread_mutex_lock(dmutex(kb));
168  // End thread when the handle is removed
169  if(!IS_CONNECTED(kb))
170  break;
174  if(lines){
177  if(readcmd(kb, line)){
183  // USB transfer failed; destroy device
184  closeusb(kb);
185  break;
186  }
187  }
188  }
189  pthread_mutex_unlock(dmutex(kb));
192  readlines_ctx_free(linectx);
193  return 0;
194 }
#define IS_CONNECTED(kb)
Definition: device.h:12
int readcmd(usbdevice *kb, const char *line)
Definition: command.c:68
unsigned readlines(int fd, readlines_ctx ctx, const char **input)
Definition: devnode.c:353
int infifo
Definition: structures.h:225
void readlines_ctx_init(readlines_ctx *ctx)
Definition: devnode.c:341
int closeusb(usbdevice *kb)
Definition: usb.c:687
#define dmutex(kb)
Definition: device.h:18
void readlines_ctx_free(readlines_ctx ctx)
Definition: devnode.c:348

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static const devcmd* get_vtable ( short  vendor,
short  product 
)
static

get_vtable returns the correct vtable pointer

Parameters
vendorshort usb vendor ID
productshort usb product ID
Returns
Depending on the type and model, the corresponding vtable pointer is returned (see below)

At present, we have three different vtables:

  • vtable_mouse is used for all mouse types. This may be wrong with some newer mice?
  • vtable_keyboard is used for all RGB Keyboards.
  • vtable_keyboard_nonrgb for all the rest.
Todo:
Is the last point really a good decision and always correct?

Definition at line 112 of file usb.c.

References IS_MOUSE, IS_RGB, vtable_keyboard, vtable_keyboard_nonrgb, and vtable_mouse.

Referenced by _setupusb().

112  {
113  return IS_MOUSE(vendor, product) ? &vtable_mouse : IS_RGB(vendor, product) ? &vtable_keyboard : &vtable_keyboard_nonrgb;
114 }
#define IS_RGB(vendor, product)
RGB vs non-RGB test (note: non-RGB Strafe is still considered "RGB" in that it shares the same protoc...
Definition: usb.h:146
const devcmd vtable_mouse
#define IS_MOUSE(vendor, product)
Mouse vs keyboard test.
Definition: usb.h:163
const devcmd vtable_keyboard
RGB keyboard vtable holds functions for each device type.
Definition: device_vtable.c:52
const devcmd vtable_keyboard_nonrgb
Definition: device_vtable.c:99

+ Here is the caller graph for this function:

const char* product_str ( short  product)

product_str returns a condensed view on what type of device we have.

At present, various models and their properties are known from corsair products. Some models differ in principle (mice and keyboards), others differ in the way they function (for example, RGB and non RGB), but they are very similar.

Here, only the first point is taken into consideration and we return a unified model string. If the model is not known with its number, product_str returns an empty string.

The model numbers and corresponding strings wwith the numbers in hex-string are defined in usb.h

At present, this function is used to initialize kb->name and to give information in debug strings.

Attention
The combinations below have to fit to the combinations in the macros mentioned above. So if you add a device with a new number, change both.
Todo:
There are macros defined in usb.h to detect all the combinations below. the only difference is the parameter: The macros need the kb*, product_str() needs the product ID

Definition at line 70 of file usb.c.

References P_GLAIVE, P_HARPOON, P_K63_NRGB, P_K65, P_K65_LUX, P_K65_NRGB, P_K65_RFIRE, P_K68, P_K70, P_K70_LUX, P_K70_LUX_NRGB, P_K70_NRGB, P_K70_RFIRE, P_K70_RFIRE_NRGB, P_K95, P_K95_NRGB, P_K95_PLATINUM, P_M65, P_M65_PRO, P_SABRE_L, P_SABRE_N, P_SABRE_O, P_SABRE_O2, P_SCIMITAR, P_SCIMITAR_PRO, P_STRAFE, P_STRAFE_NRGB, and P_STRAFE_NRGB_2.

Referenced by _mkdevpath(), and _setupusb().

70  {
71  if(product == P_K95 || product == P_K95_NRGB)
72  return "k95";
73  if(product == P_K95_PLATINUM)
74  return "k95p";
75  if(product == P_K70 || product == P_K70_NRGB || product == P_K70_LUX || product == P_K70_LUX_NRGB || product == P_K70_RFIRE || product == P_K70_RFIRE_NRGB)
76  return "k70";
77  if(product == P_K68)
78  return "k68";
79  if(product == P_K65 || product == P_K65_NRGB || product == P_K65_LUX || product == P_K65_RFIRE)
80  return "k65";
81  if(product == P_K63_NRGB)
82  return "k63";
83  if(product == P_STRAFE || product == P_STRAFE_NRGB || product == P_STRAFE_NRGB_2)
84  return "strafe";
85  if(product == P_M65 || product == P_M65_PRO)
86  return "m65";
87  if(product == P_SABRE_O || product == P_SABRE_L || product == P_SABRE_N || product == P_SABRE_O2)
88  return "sabre";
89  if(product == P_SCIMITAR || product == P_SCIMITAR_PRO)
90  return "scimitar";
91  if(product == P_HARPOON)
92  return "harpoon";
93  if(product == P_GLAIVE)
94  return "glaive";
95  return "";
96 }
#define P_K70_RFIRE_NRGB
Definition: usb.h:73
#define P_GLAIVE
Definition: usb.h:119
#define P_K95_PLATINUM
Definition: usb.h:81
#define P_K65_LUX
Definition: usb.h:53
#define P_K95
Definition: usb.h:77
#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 P_K70_NRGB
Definition: usb.h:65
#define P_K63_NRGB
Definition: usb.h:45
#define P_M65
Definition: usb.h:93
#define P_STRAFE
Definition: usb.h:85
#define P_SABRE_L
Definition: usb.h:101
#define P_HARPOON
Definition: usb.h:115
#define P_K68
Definition: usb.h:59
#define P_M65_PRO
Definition: usb.h:95
#define P_K70_RFIRE
Definition: usb.h:71
#define P_K65_NRGB
Definition: usb.h:51
#define P_SCIMITAR_PRO
Definition: usb.h:111
#define P_K70
Definition: usb.h:63
#define P_K95_NRGB
Definition: usb.h:79
#define P_SABRE_O2
Definition: usb.h:105
#define P_STRAFE_NRGB
Definition: usb.h:87
#define P_K70_LUX
Definition: usb.h:67
#define P_K70_LUX_NRGB
Definition: usb.h:69
#define P_SCIMITAR
Definition: usb.h:109
#define P_K65_RFIRE
Definition: usb.h:55

+ Here is the caller graph for this function:

int revertusb ( usbdevice kb)

revertusb sets a given device to inactive (hardware controlled) mode if not a fw-ugrade is indicated

First is checked, whether a firmware-upgrade is indicated for the device. If so, revertusb() returns 0.

Todo:
Why is this useful? Are there problems seen with deactivating a device with older fw-version??? Why isn't this an error indicating reason and we return success (0)?

Anyway, the following steps are similar to some other procs, dealing with low level usb handling:

  • If we do not have an RGB device, a simple setting to Hardware-mode (NK95_HWON) is sent to the device via n95cmd().
    Todo:
    The return value of nk95cmd() is ignored (but sending the ioctl may produce an error and _nk95_cmd will indicate this), instead revertusb() returns success in any case.
  • If we have an RGB device, setactive() is called with second param active = false. That function will have a look on differences between keyboards and mice.
    More precisely setactive() is just a macro to call via the kb->vtable enties either the active() or the idle() function where the vtable points to. setactive() may return error indications. If so, revertusb() returns -1, otherwise 0 in any other case.

Definition at line 417 of file usb.c.

References FEAT_RGB, HAS_FEATURES, NEEDS_FW_UPDATE, NK95_HWON, nk95cmd, and setactive.

Referenced by quitWithLock().

417  {
418  if(NEEDS_FW_UPDATE(kb))
419  return 0;
420  if(!HAS_FEATURES(kb, FEAT_RGB)){
421  nk95cmd(kb, NK95_HWON);
422  return 0;
423  }
424  if(setactive(kb, 0))
425  return -1;
426  return 0;
427 }
#define nk95cmd(kb, command)
nk95cmd() macro is used to wrap _nk95cmd() with debugging information (file and lineno). the command structure is different: Just the bits 23..16 are used as bits 7..0 for bRequest Bits 15..0 are used as wValue
Definition: usb.h:328
#define FEAT_RGB
Definition: structures.h:136
#define NK95_HWON
Hardware playback on.
Definition: usb.h:336
#define NEEDS_FW_UPDATE(kb)
Definition: structures.h:161
#define HAS_FEATURES(kb, feat)
Definition: structures.h:157
#define setactive(kb, makeactive)
setactive() calls via the corresponding kb->vtable either the active() or the idle() function...
Definition: device.h:44

+ Here is the caller graph for this function:

void setupusb ( usbdevice kb)

setupusb starts a thread with kb as parameter and _setupusb() as entrypoint.

Set up a USB device after its handle is open. Spawns a new thread _setupusb() with standard parameter kb. dmutex must be locked prior to calling this function. The function will unlock it when finished. In kb->thread the thread id is mentioned, because closeusb() needs this info for joining that thread again.

Definition at line 396 of file usb.c.

References _setupusb(), ckb_err, imutex, and usbdevice::thread.

Referenced by usbadd().

396  {
397  pthread_mutex_lock(imutex(kb));
398  if(pthread_create(&kb->thread, 0, _setupusb, kb))
399  ckb_err("Failed to create USB thread\n");
400 }
#define ckb_err(fmt, args...)
Definition: includes.h:49
static void * _setupusb(void *context)
brief .
Definition: usb.c:224
#define imutex(kb)
Definition: device.h:22
pthread_t thread
Definition: structures.h:217

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

int usb_tryreset ( usbdevice kb)

usb_tryreset does what the name means: Try to reset the usb via resetusb()

This function is called if an usb command ran into an error in case of one of the following two situations:

  • When setting up a new usb device and the start() function got an error (
    See Also
    _setupusb())
  • If upgrading to a new firmware gets an error (
    See Also
    cmd_fwupdate()).
    The previous action which got the error will NOT be re-attempted.

In an endless loop usb_tryreset() tries to reset the given usb device via the macro resetusb().
This macro calls _resetusb() with debugging information.
_resetusb() sends a command via the operating system dependent function os_resetusb() and - if successful - reinitializes the device. os_resetusb() returns -2 to indicate a broken device and all structures should be removed for it.
In that case, the loop is terminated, an error message is produced and usb_tryreset() returns -1.

In case resetusb() has success, the endless loop is left via a return 0 (success).
If the return value from resetusb() is -1, the loop is continued with the next try.

If the global variable reset_stop is set directly when the function is called or after each try, usb_tryreset() stops working and returns -1.

Todo:
Why does usb_tryreset() hide the information returned from resetusb()? Isn't it needed by the callers?

Definition at line 475 of file usb.c.

References ckb_err, ckb_info, reset_stop, and resetusb.

Referenced by _setupusb(), cmd_fwupdate(), os_sendindicators(), and os_setupusb().

475  {
476  if(reset_stop)
477  return -1;
478  ckb_info("Attempting reset...\n");
479  while(1){
480  int res = resetusb(kb);
481  if(!res){
482  ckb_info("Reset success\n");
483  return 0;
484  }
485  if(res == -2 || reset_stop)
486  break;
487  }
488  ckb_err("Reset failed. Disconnecting.\n");
489  return -1;
490 }
#define ckb_err(fmt, args...)
Definition: includes.h:49
volatile int reset_stop
brief .
Definition: usb.c:25
#define ckb_info(fmt, args...)
Definition: includes.h:55
#define resetusb(kb)
resetusb() is just a macro to call _resetusb() with debuggin constants (file, lineno) ...
Definition: usb.h:246

+ Here is the caller graph for this function:

const char* vendor_str ( short  vendor)

uncomment to see USB packets sent to the device

vendor_str returns "corsair" if the given vendor argument is equal to V_CORSAIR (0x1bc) else it returns ""

Attention
There is also a string defined V_CORSAIR_STR, which returns the device number as string in hex "1b1c".

Definition at line 43 of file usb.c.

References V_CORSAIR.

Referenced by _mkdevpath(), and _setupusb().

43  {
44  if(vendor == V_CORSAIR)
45  return "corsair";
46  return "";
47 }
#define V_CORSAIR
For the following Defines please see "Detailed Description".
Definition: usb.h:42

+ Here is the caller graph for this function:

Variable Documentation

int features_mask = -1

features_mask Mask of features to exclude from all devices

That bit mask ist set to enable all (-1). When interpreting the input parameters, some of these bits can be cleared.
At the moment binding, notifying and mouse-acceleration can be disabled via command line.
Have a look at main() in main.c for details.

Definition at line 35 of file usb.c.

Referenced by _setupusb(), and main().

int hwload_mode

hwload_mode is defined in device.c

Definition at line 7 of file device.c.

Referenced by _start_dev(), _usbrecv(), and _usbsend().

volatile int reset_stop = 0

reset_stop is boolean: Reset stopper for when the program shuts down.

Is set only by quit() to true (1) to inform several usb_* functions to end their loops and tries.

Definition at line 25 of file usb.c.

Referenced by _usbrecv(), _usbsend(), quitWithLock(), and usb_tryreset().

pthread_mutex_t usbmutex = PTHREAD_MUTEX_INITIALIZER

usbmutex is a never referenced mutex!

Todo:
We should have a look why this mutex is never used.

Definition at line 17 of file usb.c.