moberg.c 13.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
    moberg.c --  interface to moberg I/O system

    Copyright (C) 2019 Anders Blomdell <anders.blomdell@gmail.com>

    This file is part of Moberg.

    Moberg is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
21
22
23
24
25
26
27
28
29
30
31
32
#define _POSIX_C_SOURCE  200809L
#define _GNU_SOURCE               /* scandirat */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <basedir.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
33
#include <errno.h>
Anders Blomdell's avatar
Anders Blomdell committed
34
35
#include <moberg.h>
#include <moberg_config.h>
36
#include <moberg_inline.h>
Anders Blomdell's avatar
Anders Blomdell committed
37
#include <moberg_module.h>
38
#include <moberg_parser.h>
39

Anders Blomdell's avatar
Anders Blomdell committed
40
struct moberg {
41
42
  int should_free;
  int open_channels;
Anders Blomdell's avatar
Anders Blomdell committed
43
  struct moberg_config *config;
Anders Blomdell's avatar
Anders Blomdell committed
44
45
46
47
  struct channel_list {
    int capacity;
    struct moberg_channel **value;
  } analog_in, analog_out, digital_in, digital_out, encoder_in;
Anders Blomdell's avatar
Anders Blomdell committed
48
49
50
51
52
  struct deferred_action {
    struct deferred_action *next;
    int (*action)(void *param);
    void *param;
  } *deferred_action;
53

Anders Blomdell's avatar
Anders Blomdell committed
54
};
55

Anders Blomdell's avatar
Anders Blomdell committed
56
static void run_deferred_actions(struct moberg *moberg)
57
{
Anders Blomdell's avatar
Anders Blomdell committed
58
59
60
  while (moberg->deferred_action) {
    struct deferred_action *deferred = moberg->deferred_action;
    moberg->deferred_action = deferred->next;
61
62
63
64
65
    deferred->action(deferred->param);
    free(deferred);
  }
}

Anders Blomdell's avatar
Anders Blomdell committed
66
67
68
69
70
71
72
static int channel_list_set(struct channel_list *list,
                            int index,
                            struct moberg_channel *value)
{
  if (list->capacity <= index) {
    int capacity;
    for (capacity = 2 ; capacity <= index ; capacity *= 2);
73
    void *new = realloc(list->value, capacity * sizeof(*list->value));
Anders Blomdell's avatar
Anders Blomdell committed
74
75
76
77
    if (!new) {
      goto err;
    }
    void *p = new + list->capacity * sizeof(*list->value);
78
    memset(p, 0, (capacity - list->capacity) * sizeof(*list->value));
Anders Blomdell's avatar
Anders Blomdell committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    list->value = new;
    list->capacity = capacity;
  }

  if (0 <= index && index < list->capacity) {
    list->value[index] = value;
    return 1;
  }
err:
  return 0;
}
                                   
static int channel_list_get(struct channel_list *list,
                            int index,
                            struct moberg_channel **value)
{
  if (0 <= index && index < list->capacity) {
    *value = list->value[index];
    return 1;
  }
  return 0;
}

static void channel_list_free(struct channel_list *list)
{
  for (int i = 0 ; i < list->capacity ; i++) {
    if (list->value[i]) {
      list->value[i]->down(list->value[i]);
    }
  }
  free(list->value);
}

Anders Blomdell's avatar
Anders Blomdell committed
112
113
114
115
static void parse_config_at(
  struct moberg *moberg,
  int dirfd,
  const char *pathname)
116
117
118
119
{
  if (dirfd >= 0) {
    int fd = openat(dirfd, pathname, O_RDONLY);
    if (fd >= 0) {
Anders Blomdell's avatar
Anders Blomdell committed
120
121
122
123
124
125
126
      struct stat statbuf;
      if (fstat(fd, &statbuf) == 0) {
        char *buf = malloc(statbuf.st_size + 1);
        if (buf) {
          if (read(fd, buf, statbuf.st_size) == statbuf.st_size) {
            buf[statbuf.st_size] = 0;
          }
Anders Blomdell's avatar
Anders Blomdell committed
127
          struct moberg_config *config = moberg_parse(moberg, buf);
Anders Blomdell's avatar
Anders Blomdell committed
128
          if (config) {
Anders Blomdell's avatar
Anders Blomdell committed
129
130
131
132
133
134
            if (! moberg->config) {
              moberg->config = config;
            } else {
              moberg_config_join(moberg->config, config);
              moberg_config_free(config);
            }
Anders Blomdell's avatar
Anders Blomdell committed
135
136
137
138
          }
          free(buf);
        }
      }
139
140
141
142
143
      close(fd);
    }
  }
}

Anders Blomdell's avatar
Anders Blomdell committed
144
145
static int conf_filter(
  const struct dirent *entry)
146
147
148
149
150
151
152
153
154
{
  char *dot = strrchr(entry->d_name, '.');
  if (dot != NULL && strcmp(dot, ".conf") == 0) {
    return  1;
  } else {
    return 0;
  }
}

Anders Blomdell's avatar
Anders Blomdell committed
155
156
157
static void parse_config_dir_at(
  struct moberg *config,
  int dirfd)
158
159
160
161
162
163
{
  if (dirfd >= 0) {
    struct dirent **entry = NULL;
    int n = scandirat(dirfd, ".", &entry, conf_filter, alphasort);
    for (int i = 0 ; i < n ; i++) {
      parse_config_at(config, dirfd, entry[i]->d_name);
Anders Blomdell's avatar
Anders Blomdell committed
164
      free(entry[i]);
165
166
167
168
169
170
    }
    free(entry);
  }
  
}

171
172
173
174
175
static struct moberg_status install_channel(
  struct moberg *moberg,
  int index,
  struct moberg_device* device,
  struct moberg_channel *channel)
Anders Blomdell's avatar
Anders Blomdell committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
{
  if (channel) {
    struct moberg_channel *old = NULL;
    switch (channel->kind) {
      case chan_ANALOGIN:
        channel_list_get(&moberg->analog_in, index, &old);
        break;
      case chan_ANALOGOUT:
        channel_list_get(&moberg->analog_out, index, &old);
        break;
      case chan_DIGITALIN:
        channel_list_get(&moberg->digital_in, index, &old);
        break;
      case chan_DIGITALOUT:
        channel_list_get(&moberg->digital_out, index, &old);
        break;
      case chan_ENCODERIN:
        channel_list_get(&moberg->encoder_in, index, &old);
        break;
    }
    if (old) {
      old->down(old);
    }
    channel->up(channel);
    /* TODO: Clean up old channel */
    switch (channel->kind) {
      case chan_ANALOGIN:
        if (! channel_list_set(&moberg->analog_in, index, channel)) {
          goto err;
        }
        break;
      case chan_ANALOGOUT:
        if (! channel_list_set(&moberg->analog_out, index, channel)) {
          goto err;
        }
        break;
      case chan_DIGITALIN:
        if (! channel_list_set(&moberg->digital_in, index, channel)) {
          goto err;
        }
        break;
      case chan_DIGITALOUT:
        if (! channel_list_set(&moberg->digital_out, index, channel)) {
          goto err;
        }
        break;
      case chan_ENCODERIN:
        if (! channel_list_set(&moberg->encoder_in, index, channel)) {
          goto err;
        }
        break;
    }
  }
229
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
230
err:
231
  return MOBERG_ERRNO(ENOMEM);
Anders Blomdell's avatar
Anders Blomdell committed
232
233
}

234
235
static int install_config(struct moberg *moberg)
{
Anders Blomdell's avatar
Anders Blomdell committed
236
237
238
  struct moberg_channel_install install = {
    .context=moberg,
    .channel=install_channel
239
  };
Anders Blomdell's avatar
Anders Blomdell committed
240
241
242
243
244
245
  if (! moberg->config) {
    fprintf(stderr, "No moberg configuration found\n");
    return 0;
  } else {
    return moberg_config_install_channels(moberg->config, &install);
  }
246
247
}

248
249
250
251
252
int moberg_OK(struct moberg_status status)
{
  return status.result == 0;
}

Anders Blomdell's avatar
Anders Blomdell committed
253
struct moberg *moberg_new()
254
{
Anders Blomdell's avatar
Anders Blomdell committed
255
  struct moberg *result = malloc(sizeof(*result));
Anders Blomdell's avatar
Anders Blomdell committed
256
257
  if (! result) {
    fprintf(stderr, "Failed to allocate moberg struct\n");
258
259
    goto err;
  }
260
261
  memset(result, 0, sizeof(*result));

Anders Blomdell's avatar
Anders Blomdell committed
262
263
264
265
266
267
268
269
270
271
272
  /* Parse default configuration(s) */
  const char * const *config_paths = xdgSearchableConfigDirectories(NULL);
  const char * const *path;
  for (path = config_paths ; *path ; path++) {
    int dirfd1 = open(*path, O_DIRECTORY);
    if (dirfd >= 0) {
      parse_config_at(result, dirfd1, "moberg.conf");
      int dirfd2 = openat(dirfd1, "moberg.d", O_DIRECTORY);
      if (dirfd2 >= 0) { 
        parse_config_dir_at(result, dirfd2);
        close(dirfd2);
273
      }
Anders Blomdell's avatar
Anders Blomdell committed
274
      close(dirfd1);
275
    }
Anders Blomdell's avatar
Anders Blomdell committed
276
    free((char*)*path);
277
  }
Anders Blomdell's avatar
Anders Blomdell committed
278
279
280
281
  free((const char **)config_paths);
  
  /* TODO: Read & parse environment overrides */

282
  install_config(result);
Anders Blomdell's avatar
Anders Blomdell committed
283
  run_deferred_actions(result);
284
  
285
err:
286
287
  return result;
}
Anders Blomdell's avatar
Anders Blomdell committed
288

289
static void free_if_unused(struct moberg *moberg)
Anders Blomdell's avatar
Anders Blomdell committed
290
{
291
  if (moberg->should_free && moberg->open_channels == 0) {
Anders Blomdell's avatar
Anders Blomdell committed
292
    moberg_config_free(moberg->config);
Anders Blomdell's avatar
Anders Blomdell committed
293
294
295
296
297
    channel_list_free(&moberg->analog_in);
    channel_list_free(&moberg->analog_out);
    channel_list_free(&moberg->digital_in);
    channel_list_free(&moberg->digital_out);
    channel_list_free(&moberg->encoder_in);
Anders Blomdell's avatar
Anders Blomdell committed
298
    run_deferred_actions(moberg);
Anders Blomdell's avatar
Anders Blomdell committed
299
300
    free(moberg);
  }
Anders Blomdell's avatar
Anders Blomdell committed
301
302
}

303
304
305
306
307
308
309
310
311
void moberg_free(struct moberg *moberg)
{
  if (moberg) {
    moberg->should_free = 1;
    free_if_unused(moberg);
  }
}


Anders Blomdell's avatar
Anders Blomdell committed
312
313
/* Input/output */

314
315
316
317
struct moberg_status moberg_analog_in_open(
  struct moberg *moberg,
  int index,
  struct moberg_analog_in *analog_in)
Anders Blomdell's avatar
Anders Blomdell committed
318
{
319
320
321
  if (! analog_in) {
    return MOBERG_ERRNO(EINVAL);
  }
Anders Blomdell's avatar
Anders Blomdell committed
322
323
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->analog_in, index, &channel);
324
325
326
327
328
329
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  } 
  struct moberg_status result = channel->open(channel);
  if (! OK(result)) {
    return result;
Anders Blomdell's avatar
Anders Blomdell committed
330
  }
331
  moberg->open_channels++;
332
333
  *analog_in = channel->action.analog_in;
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
334
335
}

336
337
338
339
struct moberg_status moberg_analog_in_close(
  struct moberg *moberg,
  int index,
  struct moberg_analog_in analog_in)
Anders Blomdell's avatar
Anders Blomdell committed
340
341
342
{
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->analog_in, index, &channel);
343
344
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
Anders Blomdell's avatar
Anders Blomdell committed
345
  }
346
347
348
349
  if (channel->action.analog_in.context != analog_in.context) {
    return MOBERG_ERRNO(EINVAL);
  }
  struct moberg_status result = channel->close(channel);
350
351
  moberg->open_channels--;
  free_if_unused(moberg);
352
353
354
355
  if (! OK(result)) {
    return result;
  }
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
356
357
}

358
359
360
361
struct moberg_status moberg_analog_out_open(
  struct moberg *moberg,
  int index,
  struct moberg_analog_out *analog_out)
Anders Blomdell's avatar
Anders Blomdell committed
362
{
363
364
365
  if (! analog_out) {
    return MOBERG_ERRNO(EINVAL);
  }
Anders Blomdell's avatar
Anders Blomdell committed
366
367
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->analog_out, index, &channel);
368
369
370
371
372
373
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  } 
  struct moberg_status result = channel->open(channel);
  if (! OK(result)) {
    return result;
Anders Blomdell's avatar
Anders Blomdell committed
374
  }
375
  moberg->open_channels++;
376
377
  *analog_out = channel->action.analog_out;
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
378
379
}

380
381
382
383
struct moberg_status moberg_analog_out_close(
  struct moberg *moberg,
  int index,
  struct moberg_analog_out analog_out)
Anders Blomdell's avatar
Anders Blomdell committed
384
385
386
{
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->analog_out, index, &channel);
387
388
389
390
391
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  }
  if (channel->action.analog_out.context != analog_out.context) {
    return MOBERG_ERRNO(EINVAL);
Anders Blomdell's avatar
Anders Blomdell committed
392
  }
393
  struct moberg_status result = channel->close(channel);
394
395
  moberg->open_channels--;
  free_if_unused(moberg);
396
397
398
399
  if (! OK(result)) {
    return result;
  }
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
400
401
}

402
403
404
405
struct moberg_status moberg_digital_in_open(
  struct moberg *moberg,
  int index,
  struct moberg_digital_in *digital_in)
Anders Blomdell's avatar
Anders Blomdell committed
406
{
407
408
409
  if (! digital_in) {
    return MOBERG_ERRNO(EINVAL);
  }
Anders Blomdell's avatar
Anders Blomdell committed
410
411
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->digital_in, index, &channel);
412
413
414
415
416
417
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  } 
  struct moberg_status result = channel->open(channel);
  if (! OK(result)) {
    return result;
Anders Blomdell's avatar
Anders Blomdell committed
418
  }
419
  moberg->open_channels++;
420
421
  *digital_in = channel->action.digital_in;
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
422
423
}

424
425
426
427
struct moberg_status moberg_digital_in_close(
  struct moberg *moberg,
  int index,
  struct moberg_digital_in digital_in)
Anders Blomdell's avatar
Anders Blomdell committed
428
429
430
{
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->digital_in, index, &channel);
431
432
433
434
435
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  }
  if (channel->action.digital_in.context != digital_in.context) {
    return MOBERG_ERRNO(EINVAL);
Anders Blomdell's avatar
Anders Blomdell committed
436
  }
437
  struct moberg_status result = channel->close(channel);
438
439
  moberg->open_channels--;
  free_if_unused(moberg);
440
441
442
443
  if (! OK(result)) {
    return result;
  }
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
444
445
}

446
447
448
449
struct moberg_status moberg_digital_out_open(
  struct moberg *moberg,
  int index,
  struct moberg_digital_out *digital_out)
Anders Blomdell's avatar
Anders Blomdell committed
450
{
451
452
453
  if (! digital_out) {
    return MOBERG_ERRNO(EINVAL);
  }
Anders Blomdell's avatar
Anders Blomdell committed
454
455
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->digital_out, index, &channel);
456
457
458
459
460
461
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  } 
  struct moberg_status result = channel->open(channel);
  if (! OK(result)) {
    return result;
Anders Blomdell's avatar
Anders Blomdell committed
462
  }
463
  moberg->open_channels++;
464
465
  *digital_out = channel->action.digital_out;
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
466
467
}

468
469
470
471
struct moberg_status moberg_digital_out_close(
  struct moberg *moberg,
  int index,
  struct moberg_digital_out digital_out)
Anders Blomdell's avatar
Anders Blomdell committed
472
473
474
{
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->digital_out, index, &channel);
475
476
477
478
479
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  }
  if (channel->action.digital_out.context != digital_out.context) {
    return MOBERG_ERRNO(EINVAL);
Anders Blomdell's avatar
Anders Blomdell committed
480
  }
481
  struct moberg_status result = channel->close(channel);
482
483
  moberg->open_channels--;
  free_if_unused(moberg);
484
485
486
487
  if (! OK(result)) {
    return result;
  }
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
488
489
}

490
491
492
493
struct moberg_status moberg_encoder_in_open(
  struct moberg *moberg,
  int index,
  struct moberg_encoder_in *encoder_in)
Anders Blomdell's avatar
Anders Blomdell committed
494
{
495
496
497
  if (! encoder_in) {
    return MOBERG_ERRNO(EINVAL);
  }
Anders Blomdell's avatar
Anders Blomdell committed
498
499
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->encoder_in, index, &channel);
500
501
502
503
504
505
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  } 
  struct moberg_status result = channel->open(channel);
  if (! OK(result)) {
    return result;
Anders Blomdell's avatar
Anders Blomdell committed
506
  }
507
  moberg->open_channels++;
508
509
  *encoder_in = channel->action.encoder_in;
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
510
}
Anders Blomdell's avatar
Anders Blomdell committed
511

512
513
514
515
struct moberg_status moberg_encoder_in_close(
  struct moberg *moberg,
  int index,
  struct moberg_encoder_in encoder_in)
Anders Blomdell's avatar
Anders Blomdell committed
516
517
518
{
  struct moberg_channel *channel = NULL;
  channel_list_get(&moberg->encoder_in, index, &channel);
519
520
521
522
523
524
525
  if (! channel) {
    return MOBERG_ERRNO(ENODEV);
  }
  if (channel->action.encoder_in.context != encoder_in.context) {
    return MOBERG_ERRNO(EINVAL);
  }
  struct moberg_status result = channel->close(channel);
526
527
  moberg->open_channels--;
  free_if_unused(moberg);
528
529
  if (! OK(result)) {
    return result;
Anders Blomdell's avatar
Anders Blomdell committed
530
  }
531
  return MOBERG_OK;
Anders Blomdell's avatar
Anders Blomdell committed
532
}
Anders Blomdell's avatar
Anders Blomdell committed
533
534
535

/* System init functionality (systemd/init/...) */

536
struct moberg_status moberg_start(
Anders Blomdell's avatar
Anders Blomdell committed
537
538
539
  struct moberg *moberg,
  FILE *f)
{
540
  return moberg_config_start(moberg->config, f);
Anders Blomdell's avatar
Anders Blomdell committed
541
542
}

543
struct moberg_status moberg_stop(
Anders Blomdell's avatar
Anders Blomdell committed
544
545
546
  struct moberg *moberg,
  FILE *f)
{
547
  return moberg_config_stop(moberg->config, f);
Anders Blomdell's avatar
Anders Blomdell committed
548
549
}

Anders Blomdell's avatar
Anders Blomdell committed
550
551
552
553
554
/* Intended for final cleanup actions (dlclose so far...) */

void moberg_deferred_action(struct moberg *moberg,
                            int (*action)(void *param),
                            void *param)
Anders Blomdell's avatar
Anders Blomdell committed
555
556
557
{
  struct deferred_action *deferred = malloc(sizeof(*deferred));
  if (deferred) {
Anders Blomdell's avatar
Anders Blomdell committed
558
    deferred->next = moberg->deferred_action;
Anders Blomdell's avatar
Anders Blomdell committed
559
560
    deferred->action = action;
    deferred->param = param;
Anders Blomdell's avatar
Anders Blomdell committed
561
    moberg->deferred_action = deferred;
Anders Blomdell's avatar
Anders Blomdell committed
562
563
  }
}