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
devnode.c
Go to the documentation of this file.
1 #include "device.h"
2 #include "devnode.h"
3 #include "firmware.h"
4 #include "input.h"
5 #include "led.h"
6 #include "notify.h"
7 #include "profile.h"
8 
9 // OSX doesn't like putting FIFOs in /dev for some reason
10 #ifndef OS_MAC
11 const char *const devpath = "/dev/input/ckb";
12 #else
13 const char *const devpath = "/var/run/ckb";
14 #endif
15 
16 long gid = -1;
17 #define S_GID_READ (gid >= 0 ? S_CUSTOM_R : S_READ)
18 
19 int rm_recursive(const char* path){
20  DIR* dir = opendir(path);
21  if(!dir)
22  return remove(path);
23  struct dirent* file;
24  while((file = readdir(dir)))
25  {
26  if(!strcmp(file->d_name, ".") || !strcmp(file->d_name, ".."))
27  continue;
28  char path2[FILENAME_MAX];
29  snprintf(path2, FILENAME_MAX, "%s/%s", path, file->d_name);
30  int stat = rm_recursive(path2);
31  if(stat != 0)
32  return stat;
33  }
34  closedir(dir);
35  return remove(path);
36 }
37 
56  pthread_mutex_lock(devmutex);
57  char cpath[strlen(devpath) + 12];
58  snprintf(cpath, sizeof(cpath), "%s0/connected", devpath);
59  FILE* cfile = fopen(cpath, "w");
60  if(!cfile){
61  ckb_warn("Unable to update %s: %s\n", cpath, strerror(errno));
62  pthread_mutex_unlock(devmutex);
63  return;
64  }
65  int written = 0;
66  for(int i = 1; i < DEV_MAX; i++){
67  if(IS_CONNECTED(keyboard + i)){
68  written = 1;
69  fprintf(cfile, "%s%d %s %s\n", devpath, i, keyboard[i].serial, keyboard[i].name);
70  }
71  }
72  if(!written)
73  fputc('\n', cfile);
74  fclose(cfile);
75  chmod(cpath, S_GID_READ);
76  if(gid >= 0)
77  chown(cpath, 0, gid);
78  pthread_mutex_unlock(devmutex);
79 }
80 
85 }
86 
87 int _mknotifynode(usbdevice* kb, int notify){
88  if(notify < 0 || notify >= OUTFIFO_MAX)
89  return -1;
90  if(kb->outfifo[notify] != 0)
91  return 0;
92  // Create the notification node
93  int index = INDEX_OF(kb, keyboard);
94  char outpath[strlen(devpath) + 10];
95  snprintf(outpath, sizeof(outpath), "%s%d/notify%d", devpath, index, notify);
96  if(mkfifo(outpath, S_GID_READ) != 0 || (kb->outfifo[notify] = open(outpath, O_RDWR | O_NONBLOCK) + 1) == 0){
97  // Add one to the FD because 0 is a valid descriptor, but ckb uses 0 for uninitialized devices
98  ckb_warn("Unable to create %s: %s\n", outpath, strerror(errno));
99  kb->outfifo[notify] = 0;
100  remove(outpath);
101  return -1;
102  }
103  if(gid >= 0)
104  fchown(kb->outfifo[notify] - 1, 0, gid);
105  return 0;
106 }
107 
108 int mknotifynode(usbdevice* kb, int notify){
110  int res = _mknotifynode(kb, notify);
112  return res;
113 }
114 
115 int _rmnotifynode(usbdevice* kb, int notify){
116  if(notify < 0 || notify >= OUTFIFO_MAX || !kb->outfifo[notify])
117  return -1;
118  int index = INDEX_OF(kb, keyboard);
119  char outpath[strlen(devpath) + 10];
120  snprintf(outpath, sizeof(outpath), "%s%d/notify%d", devpath, index, notify);
121  // Close FIFO
122  close(kb->outfifo[notify] - 1);
123  kb->outfifo[notify] = 0;
124  // Delete node
125  int res = remove(outpath);
126  return res;
127 }
128 
129 int rmnotifynode(usbdevice* kb, int notify){
131  int res = _rmnotifynode(kb, notify);
133  return res;
134 }
135 
136 static int _mkdevpath(usbdevice* kb){
137  int index = INDEX_OF(kb, keyboard);
138  // Create the control path
139  char path[strlen(devpath) + 2];
140  snprintf(path, sizeof(path), "%s%d", devpath, index);
141  if(rm_recursive(path) != 0 && errno != ENOENT){
142  ckb_err("Unable to delete %s: %s\n", path, strerror(errno));
143  return -1;
144  }
145  if(mkdir(path, S_READDIR) != 0){
146  ckb_err("Unable to create %s: %s\n", path, strerror(errno));
147  rm_recursive(path);
148  return -1;
149  }
150  if(gid >= 0)
151  chown(path, 0, gid);
152 
153  if(kb == keyboard + 0){
154  // Root keyboard: write a list of devices
156  // Write version number
157  char vpath[sizeof(path) + 8];
158  snprintf(vpath, sizeof(vpath), "%s/version", path);
159  FILE* vfile = fopen(vpath, "w");
160  if(vfile){
161  fprintf(vfile, "%s\n", CKB_VERSION_STR);
162  fclose(vfile);
163  chmod(vpath, S_GID_READ);
164  if(gid >= 0)
165  chown(vpath, 0, gid);
166  } else {
167  ckb_warn("Unable to create %s: %s\n", vpath, strerror(errno));
168  remove(vpath);
169  }
170  // Write PID
171  char ppath[sizeof(path) + 4];
172  snprintf(ppath, sizeof(ppath), "%s/pid", path);
173  FILE* pfile = fopen(ppath, "w");
174  if(pfile){
175  fprintf(pfile, "%u\n", getpid());
176  fclose(pfile);
177  chmod(ppath, S_READ);
178  if(gid >= 0)
179  chown(vpath, 0, gid);
180  } else {
181  ckb_warn("Unable to create %s: %s\n", ppath, strerror(errno));
182  remove(ppath);
183  }
184  } else {
185  // Create command FIFO
186  char inpath[sizeof(path) + 4];
187  snprintf(inpath, sizeof(inpath), "%s/cmd", path);
188  if(mkfifo(inpath, gid >= 0 ? S_CUSTOM : S_READWRITE) != 0
189  // Open the node in RDWR mode because RDONLY will lock the thread
190  || (kb->infifo = open(inpath, O_RDWR) + 1) == 0){
191  // Add one to the FD because 0 is a valid descriptor, but ckb uses 0 for uninitialized devices
192  ckb_err("Unable to create %s: %s\n", inpath, strerror(errno));
193  rm_recursive(path);
194  kb->infifo = 0;
195  return -1;
196  }
197  if(gid >= 0)
198  fchown(kb->infifo - 1, 0, gid);
199 
200  // Create notification FIFO
201  _mknotifynode(kb, 0);
202 
203  // Write the model and serial to files
204  char mpath[sizeof(path) + 6], spath[sizeof(path) + 7];
205  snprintf(mpath, sizeof(mpath), "%s/model", path);
206  snprintf(spath, sizeof(spath), "%s/serial", path);
207  FILE* mfile = fopen(mpath, "w");
208  if(mfile){
209  fputs(kb->name, mfile);
210  fputc('\n', mfile);
211  fclose(mfile);
212  chmod(mpath, S_GID_READ);
213  if(gid >= 0)
214  chown(mpath, 0, gid);
215  } else {
216  ckb_warn("Unable to create %s: %s\n", mpath, strerror(errno));
217  remove(mpath);
218  }
219  FILE* sfile = fopen(spath, "w");
220  if(sfile){
221  fputs(kb->serial, sfile);
222  fputc('\n', sfile);
223  fclose(sfile);
224  chmod(spath, S_GID_READ);
225  if(gid >= 0)
226  chown(spath, 0, gid);
227  } else {
228  ckb_warn("Unable to create %s: %s\n", spath, strerror(errno));
229  remove(spath);
230  }
231  // Write the keyboard's features
232  char fpath[sizeof(path) + 9];
233  snprintf(fpath, sizeof(fpath), "%s/features", path);
234  FILE* ffile = fopen(fpath, "w");
235  if(ffile){
236  fprintf(ffile, "%s %s", vendor_str(kb->vendor), product_str(kb->product));
238  fputs(" monochrome", ffile);
239  if(HAS_FEATURES(kb, FEAT_RGB))
240  fputs(" rgb", ffile);
241  if(HAS_FEATURES(kb, FEAT_POLLRATE))
242  fputs(" pollrate", ffile);
243  if(HAS_FEATURES(kb, FEAT_ADJRATE))
244  fputs(" adjrate", ffile);
245  if(HAS_FEATURES(kb, FEAT_BIND))
246  fputs(" bind", ffile);
247  if(HAS_FEATURES(kb, FEAT_NOTIFY))
248  fputs(" notify", ffile);
250  fputs(" fwversion", ffile);
251  if(HAS_FEATURES(kb, FEAT_FWUPDATE))
252  fputs(" fwupdate", ffile);
253  fputc('\n', ffile);
254  fclose(ffile);
255  chmod(fpath, S_GID_READ);
256  if(gid >= 0)
257  chown(fpath, 0, gid);
258  } else {
259  ckb_warn("Unable to create %s: %s\n", fpath, strerror(errno));
260  remove(fpath);
261  }
262  // Write firmware version and poll rate
263  mkfwnode(kb);
264  }
265  return 0;
266 }
267 
270  int res = _mkdevpath(kb);
272  return res;
273 }
274 
277  int index = INDEX_OF(kb, keyboard);
278  if(kb->infifo != 0){
279 #ifdef OS_LINUX
280  write(kb->infifo - 1, "\n", 1); // hack to prevent the FIFO thread from perma-blocking
281 #endif
282  close(kb->infifo - 1);
283  kb->infifo = 0;
284  }
285  for(int i = 0; i < OUTFIFO_MAX; i++)
286  _rmnotifynode(kb, i);
287  char path[strlen(devpath) + 2];
288  snprintf(path, sizeof(path), "%s%d", devpath, index);
289  if(rm_recursive(path) != 0 && errno != ENOENT){
290  ckb_warn("Unable to delete %s: %s\n", path, strerror(errno));
292  return -1;
293  }
294  ckb_info("Removed device path %s\n", path);
296  return 0;
297 }
298 
300  int index = INDEX_OF(kb, keyboard);
301  char fwpath[strlen(devpath) + 12];
302  snprintf(fwpath, sizeof(fwpath), "%s%d/fwversion", devpath, index);
303  FILE* fwfile = fopen(fwpath, "w");
304  if(fwfile){
305  fprintf(fwfile, "%04x", kb->fwversion);
306  fputc('\n', fwfile);
307  fclose(fwfile);
308  chmod(fwpath, S_GID_READ);
309  if(gid >= 0)
310  chown(fwpath, 0, gid);
311  } else {
312  ckb_warn("Unable to create %s: %s\n", fwpath, strerror(errno));
313  remove(fwpath);
314  return -1;
315  }
316  char ppath[strlen(devpath) + 11];
317  snprintf(ppath, sizeof(ppath), "%s%d/pollrate", devpath, index);
318  FILE* pfile = fopen(ppath, "w");
319  if(pfile){
320  fprintf(pfile, "%d ms", kb->pollrate);
321  fputc('\n', pfile);
322  fclose(pfile);
323  chmod(ppath, S_GID_READ);
324  if(gid >= 0)
325  chown(ppath, 0, gid);
326  } else {
327  ckb_warn("Unable to create %s: %s\n", fwpath, strerror(errno));
328  remove(ppath);
329  return -2;
330  }
331  return 0;
332 }
333 
334 #define MAX_BUFFER (1024 * 1024 - 1)
336  char* buffer;
339 };
340 
342  // Allocate buffers to store data
343  *ctx = calloc(1, sizeof(struct _readlines_ctx));
344  int buffersize = (*ctx)->buffersize = 4095;
345  (*ctx)->buffer = malloc(buffersize + 1);
346 }
347 
349  free(ctx->buffer);
350  free(ctx);
351 }
352 
353 unsigned readlines(int fd, readlines_ctx ctx, const char** input){
354  // Move any data left over from a previous read to the start of the buffer
355  char* buffer = ctx->buffer;
356  int buffersize = ctx->buffersize;
357  int leftover = ctx->leftover, leftoverlen = ctx->leftoverlen;
358  memcpy(buffer, buffer + leftover, leftoverlen);
359  // Read data from the file
360  ssize_t length = read(fd, buffer + leftoverlen, buffersize - leftoverlen);
361  length = (length < 0 ? 0 : length) + leftoverlen;
362  leftover = ctx->leftover = leftoverlen = ctx->leftoverlen = 0;
363  if(length <= 0){
364  *input = 0;
365  return 0;
366  }
367  // Continue buffering until all available input is read or there's no room left
368  while(length == buffersize){
369  if(buffersize == MAX_BUFFER)
370  break;
371  int oldsize = buffersize;
372  buffersize += 4096;
373  ctx->buffersize = buffersize;
374  buffer = ctx->buffer = realloc(buffer, buffersize + 1);
375  ssize_t length2 = read(fd, buffer + oldsize, buffersize - oldsize);
376  if(length2 <= 0)
377  break;
378  length += length2;
379  }
380  buffer[length] = 0;
381  // Input should be issued one line at a time and should end with a newline.
382  char* lastline = memrchr(buffer, '\n', length);
383  if(lastline == buffer + length - 1){
384  // If the buffer ends in a newline, process the whole string
385  *input = buffer;
386  return length;
387  } else if(lastline){
388  // Otherwise, chop off the last line but process everything else
389  *lastline = 0;
390  leftover = ctx->leftover = lastline + 1 - buffer;
391  leftoverlen = ctx->leftoverlen = length - leftover;
392  *input = buffer;
393  return leftover - 1;
394  } else {
395  // If a newline wasn't found at all, process the whole buffer next time
396  *input = 0;
397  if(length == MAX_BUFFER){
398  // Unless the buffer is completely full, in which case discard it
399  ckb_warn("Too much input (1MB). Dropping.\n");
400  return 0;
401  }
402  leftoverlen = ctx->leftoverlen = length;
403  return 0;
404  }
405 }
char name[40+1]
Definition: structures.h:233
#define IS_CONNECTED(kb)
Definition: device.h:12
#define S_CUSTOM
Definition: devnode.h:17
const char * vendor_str(short vendor)
brief .
Definition: usb.c:43
#define FEAT_FWVERSION
Definition: structures.h:142
unsigned readlines(int fd, readlines_ctx ctx, const char **input)
Definition: devnode.c:353
int buffersize
Definition: devnode.c:337
int mkdevpath(usbdevice *kb)
Create a dev path for the keyboard at index. Returns 0 on success.
Definition: devnode.c:268
#define ckb_err(fmt, args...)
Definition: includes.h:49
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
#define S_READ
Definition: devnode.h:15
pthread_mutex_t devmutex[9]
Mutex for handling the usbdevice structure.
Definition: device.c:12
#define FEAT_BIND
Definition: structures.h:140
char * buffer
Definition: devnode.c:336
int infifo
Definition: structures.h:225
int mkfwnode(usbdevice *kb)
Writes a keyboard's firmware version and poll rate to its device node.
Definition: devnode.c:299
int outfifo[10]
Definition: structures.h:227
long gid
Group ID for the control nodes. -1 to give read/write access to everybody.
Definition: devnode.c:16
void readlines_ctx_init(readlines_ctx *ctx)
Definition: devnode.c:341
#define ckb_warn(fmt, args...)
Definition: includes.h:52
const char *const devpath
Definition: devnode.c:11
#define ckb_info(fmt, args...)
Definition: includes.h:55
short product
Definition: structures.h:237
#define euid_guard_start
Definition: os.h:40
#define INDEX_OF(entry, array)
Definition: includes.h:27
char pollrate
Definition: structures.h:241
int leftoverlen
Definition: devnode.c:338
const char * product_str(short product)
brief .
Definition: usb.c:70
#define FEAT_MONOCHROME
Definition: structures.h:137
char serial[34]
Definition: structures.h:235
int _mknotifynode(usbdevice *kb, int notify)
Definition: devnode.c:87
#define S_GID_READ
Definition: devnode.c:17
#define HAS_FEATURES(kb, feat)
Definition: structures.h:157
int rmnotifynode(usbdevice *kb, int notify)
Removes a notification node for the specified keyboard.
Definition: devnode.c:129
int mknotifynode(usbdevice *kb, int notify)
Creates a notification node for the specified keyboard.
Definition: devnode.c:108
#define S_READDIR
Definition: devnode.h:14
#define FEAT_NOTIFY
Definition: structures.h:141
#define DEV_MAX
Definition: device.h:8
static int _mkdevpath(usbdevice *kb)
Definition: devnode.c:136
void readlines_ctx_free(readlines_ctx ctx)
Definition: devnode.c:348
#define FEAT_ADJRATE
Definition: structures.h:139
int rmdevpath(usbdevice *kb)
Remove the dev path for the keyboard at index. Returns 0 on success.
Definition: devnode.c:275
short vendor
Definition: structures.h:237
#define FEAT_FWUPDATE
Definition: structures.h:143
void _updateconnected()
_updateconnected Update the list of connected devices.
Definition: devnode.c:55
#define FEAT_POLLRATE
Definition: structures.h:138
#define S_READWRITE
Definition: devnode.h:16
#define MAX_BUFFER
Definition: devnode.c:334
int rm_recursive(const char *path)
Definition: devnode.c:19
void updateconnected()
Update the list of connected devices.
Definition: devnode.c:81
#define euid_guard_stop
Definition: os.h:41
int _rmnotifynode(usbdevice *kb, int notify)
Definition: devnode.c:115
#define OUTFIFO_MAX
Definition: structures.h:24