blob: 4c373470cde144724f2a19939a0d4ae6c5bd6e28 [file] [log] [blame]
Vincent Palatin0540b3b2013-08-23 20:35:55 -07001/*
2 * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6#define _GNU_SOURCE
7#include <errno.h>
8#include <getopt.h>
9#include <glob.h>
10#include <stdarg.h>
11#include <stdint.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <syslog.h>
16#include <sys/types.h>
17#include <sys/wait.h>
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +080018#include <dirent.h>
Vincent Palatin0540b3b2013-08-23 20:35:55 -070019#include <time.h>
20
21#include <alsa/asoundlib.h>
22#include <linux/input.h>
Haixia Shi291ace22014-06-03 12:15:24 -070023#include <linux/uinput.h>
Vincent Palatin0540b3b2013-08-23 20:35:55 -070024
25#define DAEMON_NAME "jabra_vold"
26
27/* endpoint used for the HID interrupt */
28#define JABRA_ENDPOINT 0x81
29
30/*
31 * Report numbers used by the Jabra device.
32 *
33 * We could parse them from the HID descriptors,
34 * but that's long and complicated.
35 */
36enum jabra_report {
37 JABRA_HID_REPORT = 1,
38 JABRA_TELEPHONY_REPORT = 3,
39};
40
41/* bit for mute key press in the telephony report */
42#define MUTE_FLAG 0x08
43
44/* Names used by the kernel Alsa audio driver */
45#define ALSA_DEV_NAME "Jabra SPEAK"
46#define ALSA_CONTROL_NAME "PCM Playback Volume"
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +080047#define CARD_PREFIX "card"
Vincent Palatin0540b3b2013-08-23 20:35:55 -070048
Haixia Shi291ace22014-06-03 12:15:24 -070049#define KEY_CROS_VOLDOWN KEY_VOLUMEDOWN
50#define KEY_CROS_VOLUP KEY_VOLUMEUP
Vincent Palatin0540b3b2013-08-23 20:35:55 -070051
Haixia Shi8078f192014-05-22 11:49:29 -070052/* Maximum number of supported Alsa devices for Jabra */
53#define ALSA_DEV_COUNT_MAX 4
54
Vincent Palatin0540b3b2013-08-23 20:35:55 -070055/*
56 * Linux kernel usbmon binary interface format
57 * from Documentation/usb/usbmon.txt
58 *
59 * Only the 48 first bytes as we are using read() rather the IOCTL
60 * which returns the full header.
61 */
62struct usbmon_packet {
63 uint64_t id; /* 0: URB ID - from submission to callback */
64 unsigned char type; /* 8: Same as text; extensible. */
65 unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */
66 unsigned char epnum; /* Endpoint number and transfer direction */
67 unsigned char devnum; /* Device address */
68 uint16_t busnum; /* 12: Bus number */
69 char flag_setup; /* 14: Same as text */
70 char flag_data; /* 15: Same as text; Binary zero is OK. */
71 int64_t ts_sec; /* 16: gettimeofday */
72 int32_t ts_usec; /* 24: gettimeofday */
73 int status; /* 28: */
74 unsigned int length; /* 32: Length of data (submitted or actual) */
75 unsigned int len_cap; /* 36: Delivered length */
76 union { /* 40: */
77 unsigned char setup[8]; /* Only for Control S-type */
78 struct iso_rec { /* Only for ISO */
79 int error_count;
80 int numdesc;
81 } iso;
82 } s;
83};
84
85/* Device node for the usbmon binary interface */
86#define USBMON_DEV_FMT "/dev/usbmon%d"
87
88/* device context */
89struct jabra_dev {
Haixia Shi291ace22014-06-03 12:15:24 -070090 int uinput_fd;
Haixia Shi8078f192014-05-22 11:49:29 -070091 int alsa_dev_count;
92 snd_hctl_t *hctl[ALSA_DEV_COUNT_MAX];
93 snd_hctl_elem_t *playback_vol[ALSA_DEV_COUNT_MAX];
Vincent Palatin0540b3b2013-08-23 20:35:55 -070094 int mon_fd;
95 int devnum;
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +080096 int alsa_card_idx;
Vincent Palatin0540b3b2013-08-23 20:35:55 -070097};
98
99static void logd(int level, const char *fmt, ...)
100 __attribute__((format (printf, 2, 3)));
101static void logd(int level, const char *fmt, ...)
102{
103 va_list ap;
104
105 va_start(ap, fmt);
106 vsyslog(level, fmt, ap);
107 va_end(ap);
108}
109
110static int alsa_touch_volume(struct jabra_dev *dev)
111{
112 snd_ctl_elem_value_t *elem;
Haixia Shi8078f192014-05-22 11:49:29 -0700113 int err = 0, idx;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700114
115 snd_ctl_elem_value_alloca(&elem);
Haixia Shi8078f192014-05-22 11:49:29 -0700116 for (idx = 0; idx < dev->alsa_dev_count; ++idx) {
117 err = snd_hctl_elem_read(dev->playback_vol[idx], elem);
118 if (err < 0)
119 goto return_err;
120 err = snd_hctl_elem_write(dev->playback_vol[idx], elem);
121 if (err < 0)
122 goto return_err;
123 logd(LOG_INFO, "Touch volume: %ld\n",
124 snd_ctl_elem_value_get_integer(elem, 0));
125 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700126
Haixia Shi8078f192014-05-22 11:49:29 -0700127return_err:
128 return (err < 0) ? err : 0;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700129}
130
131static int alsa_init(struct jabra_dev *dev)
132{
133 int ret;
134 snd_ctl_elem_id_t *id;
135 int idx;
136 char *card_name;
Haixia Shi8078f192014-05-22 11:49:29 -0700137 char *hwid[ALSA_DEV_COUNT_MAX] = { NULL, };
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700138 time_t deadline = time(NULL) + 5 /* 5-second timeout */;
139
Haixia Shi5d3f1d02014-06-20 13:16:48 -0700140 /* allow a brief initial delay so that multiple ALSA devices can be found */
141 usleep(500000);
142
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700143 while (time(NULL) < deadline) {
144 /* find the Jabra device id */
145 for (idx = -1; (snd_card_next(&idx) == 0) && (idx >= 0); ) {
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800146 if ((-1 != dev->alsa_card_idx) && (idx != dev->alsa_card_idx))
147 continue;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700148 ret = snd_card_get_name(idx, &card_name);
149 if (!ret && (strncmp(card_name, ALSA_DEV_NAME,
Haixia Shi8078f192014-05-22 11:49:29 -0700150 sizeof(ALSA_DEV_NAME) - 1) == 0)) {
151 ret = asprintf(&hwid[dev->alsa_dev_count], "hw:%d", idx);
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700152 logd(LOG_NOTICE, "ALSA dev %s (%s)\n",
Haixia Shi8078f192014-05-22 11:49:29 -0700153 hwid[dev->alsa_dev_count], card_name);
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700154 free(card_name);
Haixia Shi8078f192014-05-22 11:49:29 -0700155 if (dev->alsa_dev_count++ >= ALSA_DEV_COUNT_MAX) {
156 logd(LOG_ERR, "Too many ALSA devices found for Jabra\n");
157 break;
158 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700159 }
160 if (!ret)
161 free(card_name);
162 }
Haixia Shi8078f192014-05-22 11:49:29 -0700163 if (dev->alsa_dev_count)
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700164 break;
165 logd(LOG_INFO, "no ALSA device, retry later ...\n");
166 /*
167 * card not found, loading the kernel module and initializing
168 * the driver might take some time, retry later.
169 */
170 usleep(250000);
171 }
Haixia Shi8078f192014-05-22 11:49:29 -0700172 if (!dev->alsa_dev_count) {
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700173 logd(LOG_ERR, "Cannot find ALSA device for Jabra\n");
174 return -ENODEV;
175 }
176
177 /* initialize the Jabra USB-audio mixer control */
Haixia Shi8078f192014-05-22 11:49:29 -0700178 for (idx = 0; idx < dev->alsa_dev_count; ++idx) {
179 ret = snd_hctl_open(&dev->hctl[idx], hwid[idx], 0);
180 if (ret < 0)
181 return -ENODEV;
182 ret = snd_hctl_load(dev->hctl[idx]);
183 if (ret < 0)
184 goto fail_after_open;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700185
Haixia Shi8078f192014-05-22 11:49:29 -0700186 snd_ctl_elem_id_alloca(&id);
187 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
188 snd_ctl_elem_id_set_name(id, ALSA_CONTROL_NAME);
189 dev->playback_vol[idx] = snd_hctl_find_elem(dev->hctl[idx], id);
190 if (!dev->playback_vol[idx])
191 goto fail_after_open;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700192
Haixia Shi8078f192014-05-22 11:49:29 -0700193 free(hwid[idx]);
194 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700195 return 0;
196
197fail_after_open:
198 logd(LOG_ERR, "alsa initialization failed (%d)\n", ret);
Haixia Shi8078f192014-05-22 11:49:29 -0700199 for (idx = 0; idx < dev->alsa_dev_count; ++idx) {
200 if (dev->hctl[idx]) {
201 snd_hctl_close(dev->hctl[idx]);
202 dev->hctl[idx] = NULL;
203 }
204 free(hwid[idx]);
205 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700206 return ret;
207}
208
209static int hid_init(struct jabra_dev *dev)
210{
211 int ret;
Haixia Shi291ace22014-06-03 12:15:24 -0700212 size_t count;
213 char* uidev_ptr;
Haixia Shi8078f192014-05-22 11:49:29 -0700214 struct uinput_user_dev *uidev = calloc(1, sizeof(struct uinput_user_dev));
215
Haixia Shi71a06492014-09-09 17:04:32 -0700216 time_t deadline = time(NULL) + 5 /* 5-second timeout */;
217
Haixia Shi8078f192014-05-22 11:49:29 -0700218 if (!uidev) {
219 logd(LOG_ERR, "Failed to allocate uinput_user_dev buffer");
220 return -ENOMEM;
221 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700222
Haixia Shi71a06492014-09-09 17:04:32 -0700223 while (time(NULL) < deadline) {
224 dev->uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
225 if (dev->uinput_fd >= 0)
226 break;
227 logd(LOG_INFO, "uinput not started, retry later ...\n");
228 /*
229 * uinput not started, retry later.
230 */
231 usleep(250000);
232 }
233
234
Haixia Shi291ace22014-06-03 12:15:24 -0700235 if (dev->uinput_fd < 0) {
236 logd(LOG_ERR, "Cannot open uinput for HID event injection\n");
237 return -ENOMEM;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700238 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700239
Haixia Shi291ace22014-06-03 12:15:24 -0700240 ioctl(dev->uinput_fd, UI_SET_EVBIT, EV_KEY);
241 ioctl(dev->uinput_fd, UI_SET_EVBIT, EV_SYN);
242 ioctl(dev->uinput_fd, UI_SET_KEYBIT, KEY_CROS_VOLDOWN);
243 ioctl(dev->uinput_fd, UI_SET_KEYBIT, KEY_CROS_VOLUP);
244
Haixia Shi8078f192014-05-22 11:49:29 -0700245 snprintf(uidev->name, UINPUT_MAX_NAME_SIZE, "jabra_vold input device");
246 uidev->id.bustype = BUS_USB;
247 uidev->id.version = 1;
Haixia Shi291ace22014-06-03 12:15:24 -0700248
Haixia Shi8078f192014-05-22 11:49:29 -0700249 for (uidev_ptr = (char*)uidev, count = sizeof(*uidev); count; ) {
Haixia Shi291ace22014-06-03 12:15:24 -0700250 int written = write(dev->uinput_fd, uidev_ptr, count);
251 if (written < 0) {
252 if (errno == EINTR || errno == EAGAIN) continue;
253 logd(LOG_ERR, "Failed to configure input device\n");
254 ret = errno;
255 goto fail_exit;
256 } else {
257 uidev_ptr += written;
258 count -= written;
259 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700260 }
Haixia Shi291ace22014-06-03 12:15:24 -0700261 ret = ioctl(dev->uinput_fd, UI_DEV_CREATE);
262 if (ret < 0) {
263 logd(LOG_ERR, "Failed to create input device\n");
264 goto fail_exit;
265 }
266
Haixia Shi8078f192014-05-22 11:49:29 -0700267 free(uidev);
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700268 return 0;
Haixia Shi291ace22014-06-03 12:15:24 -0700269
270fail_exit:
271 close(dev->uinput_fd);
272 dev->uinput_fd = 0;
Haixia Shi8078f192014-05-22 11:49:29 -0700273 free(uidev);
Haixia Shi291ace22014-06-03 12:15:24 -0700274 return ret;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700275}
276
277static void inject_hid_event(struct jabra_dev *dev, uint8_t key)
278{
279 struct input_event evt = { .type = EV_KEY, .code = key, .value = 1 };
280 struct input_event syn = { .type = EV_SYN, .code = SYN_REPORT };
281 int ret;
282
283 ret = gettimeofday(&evt.time, NULL);
284 if (ret)
285 logd(LOG_WARNING, "Cannot get time\n");
286 /* key press event */
Haixia Shi291ace22014-06-03 12:15:24 -0700287 ret = write(dev->uinput_fd, &evt, sizeof(evt));
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700288 if (ret < 0)
289 logd(LOG_WARNING, "Cannot inject HID event\n");
290 /* key release event */
291 evt.value = 0;
Haixia Shi291ace22014-06-03 12:15:24 -0700292 ret = write(dev->uinput_fd, &evt, sizeof(evt));
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700293 if (ret < 0)
294 logd(LOG_WARNING, "Cannot inject HID event\n");
295 syn.time = evt.time;
Haixia Shi291ace22014-06-03 12:15:24 -0700296 ret = write(dev->uinput_fd, &syn, sizeof(syn));
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700297 if (ret < 0)
298 logd(LOG_WARNING, "Cannot inject HID SYN report\n");
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700299}
300
301static void handle_hid_event(struct jabra_dev *dev,
302 uint8_t report, uint8_t code)
303{
304 if (report == JABRA_HID_REPORT) { /* volume keys */
305 switch(code)
306 {
307 case 0: /* volume key release */
308 /* touch the volume on the USB Audio interface
309 * to ensure we receive the next one
310 */
311 alsa_touch_volume(dev);
312 break;
313 case 1: /* volume- press */
314 inject_hid_event(dev, KEY_CROS_VOLDOWN);
315 break;
316 case 2: /* volume+ press */
317 inject_hid_event(dev, KEY_CROS_VOLUP);
318 break;
319 }
320 } else if (report == JABRA_TELEPHONY_REPORT) { /* other keys */
Vincent Palatin961a02f2013-10-10 09:50:21 -0700321 /*
322 * if 'code' has the MUTE_FLAG bit set, the user has pressed
323 * the mic mute button, but the UI is not handling mic mute key
324 * so far (only speaker mute), so we don't inject any HID event.
325 */
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700326 } else {
327 logd(LOG_NOTICE, "HID message %02x %02x\n", report, code);
328 }
329}
330
331static int usbmon_poll(struct jabra_dev *dev)
332{
333 struct pkt {
334 struct usbmon_packet hdr;
335 uint8_t payload[64];
336 } pkt;
337 ssize_t rlen;
338
339 while (1) {
340 rlen = read(dev->mon_fd, &pkt, sizeof(pkt));
341 if (rlen <= 0)
342 return rlen;
343 if ((pkt.hdr.devnum == dev->devnum) &&
344 (pkt.hdr.epnum == JABRA_ENDPOINT) &&
345 (pkt.hdr.type == 'C')) {
346
347 if ((pkt.hdr.status == 0) && (pkt.hdr.len_cap == 3))
348 handle_hid_event(dev, pkt.payload[0],
349 pkt.payload[1]);
350 if (pkt.hdr.status == -EPROTO) {
351 logd(LOG_NOTICE,
352 "Comm failed : disconnected ?\n");
353 return 0;
354 }
355 }
356 }
357}
358
359static int usbmon_open(struct jabra_dev *dev, int busnum)
360{
361 char *node;
362 int ret;
363
364 ret = asprintf(&node, USBMON_DEV_FMT, busnum);
365 if (ret < 0)
366 return ret;
367
368 dev->mon_fd = open(node, O_RDONLY);
369 if (dev->mon_fd < 0) {
370 dev->mon_fd = 0;
371 ret = errno;
372 logd(LOG_ERR, "Cannot open usbmon node %s (%d)\n",
373 node, ret);
374 goto fail_free;
375 }
376 logd(LOG_INFO,"Using %s bus %d device %d\n", node, busnum, dev->devnum);
377 ret = 0;
378
379fail_free:
380 free(node);
381 return ret;
382}
383
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800384/* Search for the sound card index under /sys/devices/, the directory
385 * path starts with the DEVPATH environment variable provided by udev,
386 * and follow by the subdirectory to sound subsustem. Example:
387 * /sys/devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.0/sound/
388 */
389static int find_card(char *devpath, int busnum, int devnum)
390{
391 char path[256];
392 char *tokens, *tmp, *last;
393 DIR * dir;
394 struct dirent * ptr;
395 int alsa_card_idx = -1;
396 int opendir_retry = 5;
397
398 /* Extract the last layer of directory from devpath. i.e
399 * '1-6' from /sys/devices/pci0000:00/0000:00:14.0/usb1/1-6
400 */
401 last = NULL;
402 tokens = strdup(devpath);
403 tmp = strtok(tokens, "/");
404 while (tmp) {
405 last = tmp;
406 tmp = strtok(NULL, "/");
407 }
408 snprintf(path, sizeof(path), "%s/%s:1.0/sound/", devpath, last);
409 free(tokens);
410
411usb_open_dir:
412 dir = opendir(path);
413 if (dir == NULL) {
414 if (errno == ENOENT) {
415 if (opendir_retry-- == 0) {
416 logd(LOG_ERR, "Open %s timeout\n", path);
417 return -EINVAL;
418 }
419 usleep(100000);
420 goto usb_open_dir;
421 }
422 logd(LOG_ERR, "Open %s fail, %s\n", path, strerror(errno));
423 return -errno;
424 }
425
426 /* Search for the file named 'cardX' where X is the sound card index
427 * given by ALSA framework. */
428 while((ptr = readdir(dir)) != NULL) {
429 if (strncmp(CARD_PREFIX, ptr->d_name, sizeof(CARD_PREFIX) - 1))
430 continue;
431
432 alsa_card_idx = atoi(ptr->d_name + sizeof(CARD_PREFIX) - 1);
433 logd(LOG_INFO, "Successfully found card %d by parsing %s\n",
434 alsa_card_idx, path);
435 break;
436 }
437
438 closedir(dir);
439 return alsa_card_idx;
440}
441
442static int use_jabra_device(char *devpath, int busnum, int devnum, int loglvl, int errlog)
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700443{
444 struct jabra_dev *dev;
Haixia Shi8078f192014-05-22 11:49:29 -0700445 int idx, ret;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700446
447 /* setup the syslog facility and honor UDEV log level if set */
448 openlog(DAEMON_NAME, errlog ? LOG_PERROR : 0, LOG_DAEMON);
449 setlogmask(LOG_UPTO(loglvl));
450
451 dev = calloc(1, sizeof(struct jabra_dev));
452 if (!dev)
453 return -ENOMEM;
454 dev->devnum = devnum;
455
456 ret = hid_init(dev);
457 if (ret)
458 goto err;
459
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800460 /* Look up the sound card index before alsa_init(). Set to -1 if
461 * card index cannot be found, in which case alsa_init() will
462 * keep all volume controls from all sound cards to use. */
463 if (devpath) {
464 ret = find_card(devpath, busnum, devnum);
465 dev->alsa_card_idx = (ret < 0) ? -1 : ret;
466 }
467
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700468 ret = alsa_init(dev);
469 if (ret)
470 goto err;
471
472 ret = usbmon_open(dev, busnum);
473 if (ret)
474 goto err;
475
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800476 /* Set volume is required to receive HID packets afterwards. */
477 alsa_touch_volume(dev);
478
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700479 usbmon_poll(dev);
480err:
481 if (dev->mon_fd)
482 close(dev->mon_fd);
Haixia Shi8078f192014-05-22 11:49:29 -0700483 for (idx = 0; idx < dev->alsa_dev_count; ++idx) {
484 if (dev->hctl[idx])
485 snd_hctl_close(dev->hctl[idx]);
486 }
Haixia Shi291ace22014-06-03 12:15:24 -0700487 if (dev->uinput_fd) {
488 ioctl(dev->uinput_fd, UI_DEV_DESTROY);
489 close(dev->uinput_fd);
490 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700491 free(dev);
492 closelog();
493 return ret;
494}
495
496int main(int argc, char *argv[])
497{
498 int opt;
Haixia Shi8078f192014-05-22 11:49:29 -0700499 char *action_str = "start";
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700500 char *busnum_str = getenv("BUSNUM");
501 char *devnum_str = getenv("DEVNUM");
502 char *loglevel_str = getenv("LOGLEVEL");
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800503 char *devpath = getenv("DEVPATH");
Haixia Shi8078f192014-05-22 11:49:29 -0700504 char pid_filename[128];
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700505 int use_stderr = 0;
506 int busnum, devnum;
507 int loglevel = LOG_NOTICE;
Haixia Shi8078f192014-05-22 11:49:29 -0700508 int pid;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700509
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800510 while ((opt = getopt(argc, argv, "a:b:d:n:p:")) != -1) {
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700511 switch (opt) {
Haixia Shi8078f192014-05-22 11:49:29 -0700512 case 'a':
513 action_str = optarg;
514 break;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700515 case 'b':
516 busnum_str = optarg;
517 break;
518 case 'd':
519 loglevel_str = optarg;
520 use_stderr = 1;
521 break;
522 case 'n':
523 devnum_str = optarg;
524 break;
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800525 case 'p':
526 if (optarg)
527 devpath = optarg;
528 break;
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700529 }
530 }
531
532 if (!busnum_str || !devnum_str) {
533 fprintf(stderr, "USB bus/device not defined\n");
534 return -ENODEV;
535 }
536 busnum = atoi(busnum_str);
537 devnum = atoi(devnum_str);
538 if (loglevel_str)
539 loglevel = atoi(loglevel_str);
Mike Frysinger9e21dc22017-03-01 12:38:44 -0500540 snprintf(pid_filename, sizeof(pid_filename), "/run/jabra_vold/jabra_vold.%d.%d.pid",
Haixia Shi8078f192014-05-22 11:49:29 -0700541 busnum, devnum);
542
543 if (strcmp(action_str, "start") == 0) {
Matthias Kaehlcke88309112017-03-15 12:33:48 -0700544 if (daemon(0, 0)) {
545 perror("Failed to daemonize");
546 return errno;
Haixia Shi8078f192014-05-22 11:49:29 -0700547 }
Matthias Kaehlcke88309112017-03-15 12:33:48 -0700548
549 FILE* pid_file = fopen(pid_filename, "r");
550 if (pid_file) {
551 int pid_to_kill;
552 if (fscanf(pid_file, "%d", &pid_to_kill) > 0) {
553 logd(LOG_INFO, "Stopping stale jabra_vold process %d\n", pid_to_kill);
554 kill(pid_to_kill, SIGKILL);
Haixia Shi8078f192014-05-22 11:49:29 -0700555 }
556 fclose(pid_file);
Haixia Shi8078f192014-05-22 11:49:29 -0700557 }
Matthias Kaehlcke88309112017-03-15 12:33:48 -0700558 pid_file = fopen(pid_filename, "we");
559 if (!pid_file) {
560 fprintf(stderr, "Failed to open PID file to write for jabra_vold %d:%d: %m\n",
561 busnum, devnum);
562 return errno;
563 }
564 pid = getpid();
565 logd(LOG_INFO, "Started jabra_vold process %d.\n", pid);
566 if (fprintf(pid_file, "%d\n", pid) <= 0) {
567 int ret = errno;
568 perror("Failed to write PID to the PID file.");
569 fclose(pid_file);
570 return ret;
571 }
572 fclose(pid_file);
Haixia Shi8078f192014-05-22 11:49:29 -0700573 } else if (strcmp(action_str, "stop") == 0) {
574 FILE* pid_file = fopen(pid_filename, "r");
575 if (!pid_file) {
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800576 logd(LOG_ERR, "Failed to open PID file to read for jabra_vold %d:%d\n",
Haixia Shi8078f192014-05-22 11:49:29 -0700577 busnum, devnum);
578 return -ENOMEM;
579 }
580 if (fscanf(pid_file, "%d", &pid) <= 0) {
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800581 logd(LOG_ERR, "Failed to obtain PID for jabra_vold %d:%d\n",
Haixia Shi8078f192014-05-22 11:49:29 -0700582 busnum, devnum);
583 return -ENOMEM;
584 }
585 fclose(pid_file);
586 remove(pid_filename);
587 logd(LOG_INFO, "Stopping jabra_vold process %d\n", pid);
588 kill(pid, SIGKILL);
589 return 0;
590 } else {
591 fprintf(stderr, "Unrecognized action string '%s'\n", action_str);
592 return -EINVAL;
593 }
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700594
Hsin-Yu Chaoa8c39322016-10-14 15:46:05 +0800595 return use_jabra_device(devpath, busnum, devnum, loglevel, use_stderr);
Vincent Palatin0540b3b2013-08-23 20:35:55 -0700596}