ckb-next
v0.2.8 at branch master
ckb-next driver for corsair devices
|
#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"
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 devcmd * | get_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... | |
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.
|
static |
_setupusb A horrible function for setting up an usb device
context | As _setupusb() is called as a new thread, the kb* is transferred as void* |
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->handle - 1"
, as you can find it in the code.usbdevfs_ctrltransfer
). usbdevfs_ctrltransfer
in function _nk95cmd(), which will in turn is called via macro nk95cmd() via start_kb_nrgb(). 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() |
devmain()'s return value is returned by _setupusb() when we terminate.
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().
_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.
_usbsend send a logical message completely to the given device
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.
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:
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.
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().
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
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().
|
static |
devmain is called by _setupusb
kb | the 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). |
The syncing via mutexes is interesting:
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"). 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.
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().
|
static |
get_vtable returns the correct vtable pointer
vendor | short usb vendor ID |
product | short usb product ID |
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.Definition at line 112 of file usb.c.
References IS_MOUSE, IS_RGB, vtable_keyboard, vtable_keyboard_nonrgb, and vtable_mouse.
Referenced by _setupusb().
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.
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().
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.
Anyway, the following steps are similar to some other procs, dealing with low level usb handling:
Definition at line 417 of file usb.c.
References FEAT_RGB, HAS_FEATURES, NEEDS_FW_UPDATE, NK95_HWON, nk95cmd, and setactive.
Referenced by quitWithLock().
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().
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:
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.
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().
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 ""
Definition at line 43 of file usb.c.
References V_CORSAIR.
Referenced by _mkdevpath(), and _setupusb().
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().