usb_moded 0.86.0+mer58
usb_moded-worker.c
Go to the documentation of this file.
1
24
25#include "usb_moded-worker.h"
26
27#include "usb_moded.h"
28#include "usb_moded-android.h"
29#include "usb_moded-configfs.h"
30#include "usb_moded-control.h"
31#include "usb_moded-log.h"
32#include "usb_moded-modes.h"
34#include "usb_moded-modules.h"
35#include "usb_moded-appsync.h"
36
37#include <sys/stat.h>
38#include <sys/types.h>
39#include <sys/eventfd.h>
40
41#include <pthread.h> // NOTRIM
42#include <unistd.h>
43#include <pwd.h>
44
45/* ========================================================================= *
46 * Types
47 * ========================================================================= */
48
58
59static const char * const devstate_name[] = {
60 [DEVSTATE_UNKNOWN] = "unknown",
61 [DEVSTATE_UNMOUNTED] = "unmounted",
62 [DEVSTATE_MOUNTED] = "mounted",
63};
64
65/* ========================================================================= *
66 * Prototypes
67 * ========================================================================= */
68
69/* ------------------------------------------------------------------------- *
70 * WORKER
71 * ------------------------------------------------------------------------- */
72
73static bool worker_thread_p (void);
74bool worker_bailing_out (void);
75static devstate_t worker_get_mtp_device_state (void);
76static void worker_unmount_mtp_device (void);
77static bool worker_mount_mtp_device (void);
78static bool worker_mode_is_mtp_mode (const char *mode);
79static bool worker_is_mtpd_running (void);
80static bool worker_mtpd_running_p (void *aptr);
81static bool worker_mtpd_stopped_p (void *aptr);
82static bool worker_stop_mtpd (void);
83static bool worker_start_mtpd (void);
84static bool worker_switch_to_charging (void);
85const char *worker_get_kernel_module (void);
86bool worker_set_kernel_module (const char *module);
87void worker_clear_kernel_module (void);
90void worker_set_usb_mode_data (const modedata_t *data);
91static const char *worker_get_activated_mode_locked(void);
92static bool worker_set_activated_mode_locked(const char *mode);
93static const char *worker_get_requested_mode_locked(void);
94static bool worker_set_requested_mode_locked(const char *mode);
95void worker_request_hardware_mode (const char *mode);
96void worker_clear_hardware_mode (void);
97static void worker_execute (void);
98static void worker_switch_to_mode (const char *mode);
99static guint worker_add_iowatch (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr);
100static void *worker_thread_cb (void *aptr);
101static gboolean worker_notify_cb (GIOChannel *chn, GIOCondition cnd, gpointer data);
102static bool worker_start_thread (void);
103static void worker_stop_thread (void);
104static void worker_delete_eventfd (void);
105static bool worker_create_eventfd (void);
106bool worker_init (void);
107void worker_quit (void);
108void worker_wakeup (void);
109static void worker_notify (void);
110
111/* ========================================================================= *
112 * Data
113 * ========================================================================= */
114
115static pthread_t worker_thread_id = 0;
116
117static pthread_mutex_t worker_mutex = PTHREAD_MUTEX_INITIALIZER;
118
124static volatile bool worker_bailout_requested = false;
125
131static volatile bool worker_bailout_handled = false;
132
133#define WORKER_LOCKED_ENTER do {\
134 if( pthread_mutex_lock(&worker_mutex) != 0 ) { \
135 log_crit("WORKER LOCK FAILED");\
136 _exit(EXIT_FAILURE);\
137 }\
138}while(0)
139
140#define WORKER_LOCKED_LEAVE do {\
141 if( pthread_mutex_unlock(&worker_mutex) != 0 ) { \
142 log_crit("WORKER UNLOCK FAILED");\
143 _exit(EXIT_FAILURE);\
144 }\
145}while(0)
146
147/* ========================================================================= *
148 * Functions
149 * ========================================================================= */
150
151static bool
152worker_thread_p(void)
153{
154 LOG_REGISTER_CONTEXT;
155
156 return worker_thread_id && worker_thread_id == pthread_self();
157}
158
159bool
160worker_bailing_out(void)
161{
162 LOG_REGISTER_CONTEXT;
163
164 // ref: see common_msleep_()
165 return (worker_thread_p() &&
166 worker_bailout_requested &&
167 !worker_bailout_handled);
168}
169
170/* ------------------------------------------------------------------------- *
171 * MTP_DEVICE
172 * ------------------------------------------------------------------------- */
173
191static devstate_t
192worker_get_mtp_device_state(void)
193{
194 LOG_REGISTER_CONTEXT;
195
197
198 if( access("/dev/mtp/ep0", F_OK) == 0 )
199 state = DEVSTATE_MOUNTED;
200 else if( errno == ENOENT )
201 state = DEVSTATE_UNMOUNTED;
202 else
203 log_warning("/dev/mtp/ep0: %m");
204
205 log_debug("mtp device state = %s", devstate_name[state]);
206 return state;
207}
208
211static void
212worker_unmount_mtp_device(void)
213{
214 LOG_REGISTER_CONTEXT;
215
216 if( worker_get_mtp_device_state() != DEVSTATE_UNMOUNTED ) {
217 log_debug("unmounting mtp device");
218 common_system("/bin/umount /dev/mtp");
219 }
220}
221
229static bool
230worker_mount_mtp_device(void)
231{
232 LOG_REGISTER_CONTEXT;
233
234 bool mounted = false;
235
236 /* Fail if control endpoint is already present */
237 if( worker_get_mtp_device_state() != DEVSTATE_UNMOUNTED ) {
238 log_err("mtp device already mounted");
239 goto EXIT;
240 }
241
242 /* Ensure that device directory exists */
243 if( mkdir("/dev/mtp", 0755) == -1 && errno != EEXIST ) {
244 log_err("failed to create /dev/mtp directory: %m");
245 goto EXIT;
246 }
247
248 /* Probe currently active user for uid/gid info. In case these
249 * can't be obtained, use values for default user as fallback. */
250 gid_t gid = 100000;
251 uid_t uid = usbmoded_get_current_user();
252 if( uid == UID_UNKNOWN )
253 uid = 100000;
254
255 struct passwd *pw = getpwuid(uid);
256 if( pw )
257 gid = pw->pw_gid;
258
259 /* Attempt to mount mtp device using root uid and primary
260 * gid of the current user.
261 */
262 char cmd[256];
263 snprintf(cmd, sizeof cmd,
264 "/bin/mount -o mode=0770,uid=0,gid=%u -t functionfs mtp /dev/mtp",
265 (unsigned)gid);
266
267 log_debug("mounting mtp device");
268 if( common_system(cmd) != 0 )
269 goto EXIT;
270
271 /* Check that control endpoint is present */
272 if( worker_get_mtp_device_state() != DEVSTATE_MOUNTED ) {
273 log_err("mtp control not mounted");
274 goto EXIT;
275 }
276
277 mounted = true;
278
279EXIT:
280 return mounted;
281}
282
283/* ------------------------------------------------------------------------- *
284 * MTP_DAEMON
285 * ------------------------------------------------------------------------- */
286
297static unsigned worker_mtp_start_delay = 120 * 1000;
298
305static unsigned worker_mtp_stop_delay = 15 * 1000;
306
313static bool worker_mtp_service_started = false;
314
315static bool worker_mode_is_mtp_mode(const char *mode)
316{
317 LOG_REGISTER_CONTEXT;
318
319 return mode && !strcmp(mode, "mtp_mode");
320}
321
322static bool worker_is_mtpd_running(void)
323{
324 LOG_REGISTER_CONTEXT;
325
326 /* ep0 becomes available when /dev/mtp is mounted.
327 *
328 * ep1, ep2, ep3 exist while mtp daemon is running,
329 * has ep0 opened and has written config data to it.
330 */
331 static const char * const lut[] = {
332 "/dev/mtp/ep0",
333 "/dev/mtp/ep1",
334 "/dev/mtp/ep2",
335 "/dev/mtp/ep3",
336 0
337 };
338
339 bool ack = true;
340
341 for( size_t i = 0; lut[i]; ++i ) {
342 if( access(lut[i], F_OK) == -1 ) {
343 ack = false;
344 break;
345 }
346 }
347
348 return ack;
349}
350
351static bool
352worker_mtpd_running_p(void *aptr)
353{
354 LOG_REGISTER_CONTEXT;
355
356 (void)aptr;
357 return worker_is_mtpd_running();
358}
359
360static bool
361worker_mtpd_stopped_p(void *aptr)
362{
363 LOG_REGISTER_CONTEXT;
364
365 (void)aptr;
366 return !worker_is_mtpd_running();
367}
368
369static bool
370worker_stop_mtpd(void)
371{
372 LOG_REGISTER_CONTEXT;
373
374 bool ack = false;
375
376 if( !worker_mtp_service_started && worker_mtpd_stopped_p(0) ) {
377 log_debug("mtp daemon is not running");
378 goto SUCCESS;
379 }
380
381 int rc = common_system("systemctl-user stop buteo-mtp.service");
382 if( rc != 0 ) {
383 log_warning("failed to stop mtp daemon; exit code = %d", rc);
384 goto FAILURE;
385 }
386
387 /* Have succesfully stopped mtp service */
388 worker_mtp_service_started = false;
389
390 if( common_wait(worker_mtp_stop_delay, worker_mtpd_stopped_p, 0) != WAIT_READY ) {
391 log_warning("failed to stop mtp daemon; giving up");
392 goto FAILURE;
393 }
394
395 log_debug("mtp daemon has stopped");
396
397SUCCESS:
398 ack = true;
399
400FAILURE:
401 return ack;
402}
403
404static bool
405worker_start_mtpd(void)
406{
407 LOG_REGISTER_CONTEXT;
408
409 bool ack = false;
410
411 if( worker_mtpd_running_p(0) ) {
412 log_debug("mtp daemon is running");
413 goto SUCCESS;
414 }
415
416 /* Have attempted to start mtp service */
417 worker_mtp_service_started = true;
418
419 int rc = common_system("systemctl-user start buteo-mtp.service");
420 if( rc != 0 ) {
421 log_warning("failed to start mtp daemon; exit code = %d", rc);
422 goto FAILURE;
423 }
424
425 if( common_wait(worker_mtp_start_delay, worker_mtpd_running_p, 0) != WAIT_READY ) {
426 log_warning("failed to start mtp daemon; giving up");
427 goto FAILURE;
428 }
429
430 log_debug("mtp daemon has started");
431
432SUCCESS:
433 ack = true;
434
435FAILURE:
436 return ack;
437}
438
439static bool worker_switch_to_charging(void)
440{
441 LOG_REGISTER_CONTEXT;
442
443 bool ack = true;
444
445 if( android_set_charging_mode() )
446 goto SUCCESS;
447
448 if( configfs_set_charging_mode() )
449 goto SUCCESS;
450
451 if( modules_in_use() ) {
452 if( worker_set_kernel_module(MODULE_MASS_STORAGE) )
453 goto SUCCESS;
454 worker_set_kernel_module(MODULE_NONE);
455 }
456
457 log_err("switch to charging mode failed");
458
459 ack = false;
460SUCCESS:
461 return ack;
462}
463
464/* ------------------------------------------------------------------------- *
465 * KERNEL_MODULE
466 * ------------------------------------------------------------------------- */
467
469static char *worker_kernel_module = NULL;
470
476const char * worker_get_kernel_module(void)
477{
478 LOG_REGISTER_CONTEXT;
479
480 return worker_kernel_module ?: MODULE_NONE;
481}
482
488bool worker_set_kernel_module(const char *module)
489{
490 LOG_REGISTER_CONTEXT;
491
492 bool ack = false;
493
494 if( !module )
495 module = MODULE_NONE;
496
497 const char *current = worker_get_kernel_module();
498
499 log_debug("current module: %s -> %s", current, module);
500
501 if( !g_strcmp0(current, module) )
502 goto SUCCESS;
503
504 if( modules_unload_module(current) != 0 )
505 goto EXIT;
506
507 free(worker_kernel_module), worker_kernel_module = 0;
508
509 if( modules_load_module(module) != 0 )
510 goto EXIT;
511
512 if( g_strcmp0(module, MODULE_NONE) )
513 worker_kernel_module = strdup(module);
514
515SUCCESS:
516 ack = true;
517EXIT:
518 return ack;
519}
520
521void worker_clear_kernel_module(void)
522{
523 LOG_REGISTER_CONTEXT;
524
525 free(worker_kernel_module), worker_kernel_module = 0;
526}
527
528/* ------------------------------------------------------------------------- *
529 * MODE_DATA
530 * ------------------------------------------------------------------------- */
531
533static modedata_t *worker_mode_data = NULL;
534
542{
543 LOG_REGISTER_CONTEXT;
544
545 return worker_mode_data;
546}
547
555{
556 LOG_REGISTER_CONTEXT;
557
558 WORKER_LOCKED_ENTER;
559
560 modedata_t *modedata = modedata_copy(worker_mode_data);
561
562 WORKER_LOCKED_LEAVE;
563
564 return modedata;;
565}
566
574{
575 LOG_REGISTER_CONTEXT;
576
577 WORKER_LOCKED_ENTER;
578
579 modedata_free(worker_mode_data),
580 worker_mode_data = modedata_copy(data);
581
582 WORKER_LOCKED_LEAVE;
583}
584
585/* ------------------------------------------------------------------------- *
586 * HARDWARE_MODE
587 * ------------------------------------------------------------------------- */
588
589/* The hardware mode name
590 *
591 * How the usb hardware has been configured.
592 *
593 * For example internal_mode=MODE_ASK gets
594 * mapped to hardware_mode=MODE_CHARGING */
595static gchar *worker_requested_mode = NULL;
596
597static gchar *worker_activated_mode = NULL;
598
599static const char *
600worker_get_activated_mode_locked(void)
601{
602 LOG_REGISTER_CONTEXT;
603
604 return worker_activated_mode ?: MODE_UNDEFINED;
605}
606
607static bool
608worker_set_activated_mode_locked(const char *mode)
609{
610 LOG_REGISTER_CONTEXT;
611
612 bool changed = false;
613 const char *prev = worker_get_activated_mode_locked();
614
615 if( !g_strcmp0(prev, mode) )
616 goto EXIT;
617
618 log_debug("activated_mode: %s -> %s", prev, mode);
619 g_free(worker_activated_mode),
620 worker_activated_mode = g_strdup(mode);
621 changed = true;
622
623EXIT:
624 return changed;
625}
626
627static const char *
628worker_get_requested_mode_locked(void)
629{
630 LOG_REGISTER_CONTEXT;
631
632 return worker_requested_mode ?: MODE_UNDEFINED;
633}
634
635static bool
636worker_set_requested_mode_locked(const char *mode)
637{
638 LOG_REGISTER_CONTEXT;
639
640 bool changed = false;
641 const char *prev = worker_get_requested_mode_locked();
642
643 if( !g_strcmp0(prev, mode) )
644 goto EXIT;
645
646 log_debug("requested_mode: %s -> %s", prev, mode);
647 g_free(worker_requested_mode),
648 worker_requested_mode = g_strdup(mode);
649 changed = true;
650
651EXIT:
652 return changed;
653}
654
655void worker_request_hardware_mode(const char *mode)
656{
657 LOG_REGISTER_CONTEXT;
658
659 WORKER_LOCKED_ENTER;
660
661 if( !worker_set_requested_mode_locked(mode) )
662 goto EXIT;
663
664 worker_wakeup();
665
666EXIT:
667 WORKER_LOCKED_LEAVE;
668 return;
669}
670
671void worker_clear_hardware_mode(void)
672{
673 LOG_REGISTER_CONTEXT;
674
675 WORKER_LOCKED_ENTER;
676 g_free(worker_requested_mode), worker_requested_mode = 0;
677 WORKER_LOCKED_LEAVE;
678}
679
680static void
681worker_execute(void)
682{
683 LOG_REGISTER_CONTEXT;
684
685 WORKER_LOCKED_ENTER;
686
687 const char *activated = worker_get_activated_mode_locked();
688 const char *requested = worker_get_requested_mode_locked();
689 const char *activate = common_map_mode_to_hardware(requested);
690
691 log_debug("activated = %s", activated);
692 log_debug("requested = %s", requested);
693 log_debug("activate = %s", activate);
694
695 bool changed = g_strcmp0(activated, activate) != 0;
696 gchar *mode = g_strdup(activate);
697
698 WORKER_LOCKED_LEAVE;
699
700 if( changed )
701 worker_switch_to_mode(mode);
702 else
703 worker_notify();
704
705 g_free(mode);
706
707 return;
708}
709
710/* ------------------------------------------------------------------------- *
711 * MODE_SWITCH
712 * ------------------------------------------------------------------------- */
713
714static void
715worker_switch_to_mode(const char *mode)
716{
717 LOG_REGISTER_CONTEXT;
718
719 const char *override = 0;
720 modedata_t *data = 0;
721
722 /* set return to 1 to be sure to error out if no matching mode is found either */
723
724 log_debug("Cleaning up previous mode");
725
726 /* Either mtp daemon is not needed, or it must be *started* in
727 * correct phase of gadget configuration when entering mtp mode.
728 *
729 * Similarly, unmount mtp device to make sure sure it gets mounted
730 * with appropriate uid/gid values when it is actually needed.
731 */
732 worker_stop_mtpd();
733 worker_unmount_mtp_device();
734
736 modesetting_leave_dynamic_mode();
738 }
739
740 /* Mode specific applications have been stopped and we can
741 * take updated appsync configuration in use.
742 */
744
745 log_debug("Setting %s\n", mode);
746
747 /* Mode mapping should mean we only see MODE_CHARGING here, but just
748 * in case redirect fixed charging related things to charging ... */
749
750 if( !strcmp(mode, MODE_CHARGING) ||
751 !strcmp(mode, MODE_CHARGING_FALLBACK) ||
752 !strcmp(mode, MODE_CHARGER) ||
753 !strcmp(mode, MODE_UNDEFINED) ||
754 !strcmp(mode, MODE_ASK)) {
755 goto CHARGE;
756 }
757
758 if( !usbmoded_can_export() ) {
759 log_warning("Policy does not allow mode: %s", mode);
760 goto FAILED;
761 }
762
763 if( (data = usbmoded_dup_modedata(mode)) ) {
764 log_debug("Matching mode %s found.\n", mode);
765
766 /* set data before calling any of the dynamic mode functions
767 * as they will use the worker_get_usb_mode_data function */
769
770 /* When dealing with configfs, we can't enable UDC without
771 * already having mtpd running */
772 if( worker_mode_is_mtp_mode(mode) && configfs_in_use() ) {
773 if( !worker_mount_mtp_device() )
774 goto FAILED;
775 if( !worker_start_mtpd() )
776 goto FAILED;
777 }
778
780 goto FAILED;
781
782 if( !modesetting_enter_dynamic_mode() )
783 goto FAILED;
784
785 /* When dealing with android usb, it must be enabled before
786 * we can start mtpd. Assumption is that the same applies
787 * when using kernel modules. */
788 if( worker_mode_is_mtp_mode(mode) && !configfs_in_use() ) {
789 if( !worker_mount_mtp_device() )
790 goto FAILED;
791 if( !worker_start_mtpd() )
792 goto FAILED;
793 }
794
795 goto SUCCESS;
796 }
797
798 log_warning("Matching mode %s was not found.", mode);
799
800FAILED:
801 worker_bailout_handled = true;
802
803 /* Undo any changes we might have might have already done */
805 log_debug("Cleaning up failed mode switch");
806 worker_stop_mtpd();
807 modesetting_leave_dynamic_mode();
809 }
810
811 /* From usb configuration point of view MODE_UNDEFINED and
812 * MODE_CHARGING are the same, but for the purposes of exposing
813 * a sane state over D-Bus we need to differentiate between
814 * "failure to set mode" and "aborting mode setting due to cable
815 * disconnect" by inspecting whether target mode has been
816 * switched to undefined.
817 */
818 WORKER_LOCKED_ENTER;
819 const char *requested = worker_get_requested_mode_locked();
820 if( !g_strcmp0(requested, MODE_UNDEFINED) )
821 override = MODE_UNDEFINED;
822 else
823 override = MODE_CHARGING;
824 WORKER_LOCKED_LEAVE;
825 log_warning("mode setting failed, try %s", override);
826
827CHARGE:
828 if( worker_switch_to_charging() )
829 goto SUCCESS;
830
831 log_crit("failed to activate charging, all bets are off");
832
833 /* FIXME: double check this error path */
834
835 /* If we get here then usb_module loading failed,
836 * no mode matched, and charging setup failed too.
837 */
838
839 override = MODE_UNDEFINED;
840 log_warning("mode setting failed, fallback to %s", override);
841 worker_set_kernel_module(MODULE_NONE);
842
843SUCCESS:
844
845 WORKER_LOCKED_ENTER;
846 if( override ) {
847 worker_set_requested_mode_locked(override);
848 override = common_map_mode_to_hardware(override);
849 worker_set_activated_mode_locked(override);
850 }
851 else {
852 worker_set_activated_mode_locked(mode);
853 }
854 WORKER_LOCKED_LEAVE;
855
856 worker_notify();
857
858 modedata_free(data);
859
860 return;
861}
862
863/* ------------------------------------------------------------------------- *
864 * WORKER_THREAD
865 * ------------------------------------------------------------------------- */
866
868static int worker_req_evfd = -1;
869
871static int worker_rsp_evfd = -1;
872
874static guint worker_rsp_wid = 0;
875
876static guint
877worker_add_iowatch(int fd, bool close_on_unref,
878 GIOCondition cnd, GIOFunc io_cb, gpointer aptr)
879{
880 LOG_REGISTER_CONTEXT;
881
882 guint wid = 0;
883 GIOChannel *chn = 0;
884
885 if( !(chn = g_io_channel_unix_new(fd)) )
886 goto cleanup;
887
888 g_io_channel_set_close_on_unref(chn, close_on_unref);
889
890 cnd |= G_IO_ERR | G_IO_HUP | G_IO_NVAL;
891
892 if( !(wid = g_io_add_watch(chn, cnd, io_cb, aptr)) )
893 goto cleanup;
894
895cleanup:
896 if( chn != 0 ) g_io_channel_unref(chn);
897
898 return wid;
899
900}
901
902static void *worker_thread_cb(void *aptr)
903{
904 LOG_REGISTER_CONTEXT;
905
906 (void)aptr;
907
908 /* Async cancellation, but disabled */
909 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
910 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
911
912 /* Leave INT/TERM signal processing up to the main thread */
913 sigset_t ss;
914 sigemptyset(&ss);
915 sigaddset(&ss, SIGINT);
916 sigaddset(&ss, SIGTERM);
917 pthread_sigmask(SIG_BLOCK, &ss, 0);
918
919 /* Loop until explicitly canceled */
920 for( ;; ) {
921 /* Async cancellation point at wait() */
922 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
923 uint64_t cnt = 0;
924 int rc = read(worker_req_evfd, &cnt, sizeof cnt);
925 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
926
927 if( rc == -1 ) {
928 if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
929 continue;
930 log_err("read: %m");
931 goto EXIT;
932 }
933
934 if( rc != sizeof cnt )
935 continue;
936
937 if( cnt > 0 ) {
938 worker_bailout_requested = false;
939 worker_bailout_handled = false;
940 worker_execute();
941 }
942
943 }
944EXIT:
945 return 0;
946}
947
948static gboolean
949worker_notify_cb(GIOChannel *chn, GIOCondition cnd, gpointer data)
950{
951 LOG_REGISTER_CONTEXT;
952
953 (void)data;
954
955 gboolean keep_going = FALSE;
956
957 if( !worker_rsp_wid )
958 goto cleanup_nak;
959
960 int fd = g_io_channel_unix_get_fd(chn);
961
962 if( fd < 0 )
963 goto cleanup_nak;
964
965 if( cnd & ~G_IO_IN )
966 goto cleanup_nak;
967
968 if( !(cnd & G_IO_IN) )
969 goto cleanup_ack;
970
971 uint64_t cnt = 0;
972
973 int rc = read(fd, &cnt, sizeof cnt);
974
975 if( rc == 0 ) {
976 log_err("unexpected eof");
977 goto cleanup_nak;
978 }
979
980 if( rc == -1 ) {
981 if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
982 goto cleanup_ack;
983
984 log_err("read error: %m");
985 goto cleanup_nak;
986 }
987
988 if( rc != sizeof cnt )
989 goto cleanup_nak;
990
991 {
992 WORKER_LOCKED_ENTER;
993 const char *mode = worker_get_requested_mode_locked();
994 gchar *work = g_strdup(mode);
995 WORKER_LOCKED_LEAVE;
996
997 control_mode_switched(work);
998 g_free(work);
999 }
1000
1001cleanup_ack:
1002 keep_going = TRUE;
1003
1004cleanup_nak:
1005
1006 if( !keep_going ) {
1007 worker_rsp_wid = 0;
1008 log_crit("worker notifications disabled");
1009 }
1010
1011 return keep_going;
1012}
1013
1014static bool
1015worker_start_thread(void)
1016{
1017 LOG_REGISTER_CONTEXT;
1018
1019 bool ack = false;
1020 int err = pthread_create(&worker_thread_id, 0, worker_thread_cb, 0);
1021 if( err ) {
1022 worker_thread_id = 0;
1023 log_err("failed to start worker thread");
1024 }
1025 else {
1026 ack = true;
1027 log_debug("worker thread started");
1028 }
1029
1030 return ack;
1031}
1032
1033static void
1034worker_stop_thread(void)
1035{
1036 LOG_REGISTER_CONTEXT;
1037
1038 if( !worker_thread_id )
1039 goto EXIT;
1040
1041 log_debug("stopping worker thread");
1042 int err = pthread_cancel(worker_thread_id);
1043 if( err ) {
1044 log_err("failed to cancel worker thread");
1045 }
1046 else {
1047 log_debug("waiting for worker thread to exit ...");
1048 void *ret = 0;
1049 struct timespec tmo = { 0, 0};
1050 clock_gettime(CLOCK_REALTIME, &tmo);
1051 tmo.tv_sec += 3;
1052 err = pthread_timedjoin_np(worker_thread_id, &ret, &tmo);
1053 if( err ) {
1054 log_err("worker thread did not exit");
1055 }
1056 else {
1057 log_debug("worker thread terminated");
1058 worker_thread_id = 0;
1059 }
1060 }
1061
1062 if( worker_thread_id ) {
1063 /* Orderly exit is not safe, just die */
1064 _exit(EXIT_FAILURE);
1065 }
1066
1067EXIT:
1068 return;
1069}
1070
1071static void
1072worker_delete_eventfd(void)
1073{
1074 LOG_REGISTER_CONTEXT;
1075
1076 if( worker_req_evfd != -1 )
1077 close(worker_req_evfd), worker_req_evfd = -1;
1078
1079 if( worker_rsp_wid )
1080 g_source_remove(worker_rsp_wid), worker_rsp_wid = 0;
1081
1082 if( worker_rsp_evfd != -1 )
1083 close(worker_rsp_evfd), worker_req_evfd = -1;
1084}
1085
1086static bool
1087worker_create_eventfd(void)
1088{
1089 LOG_REGISTER_CONTEXT;
1090
1091 bool ack = false;
1092
1093 /* Setup notify pipeline */
1094
1095 if( (worker_rsp_evfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) == -1 )
1096 goto EXIT;
1097
1098 worker_rsp_wid = worker_add_iowatch(worker_rsp_evfd, false, G_IO_IN,
1099 worker_notify_cb, 0);
1100 if( !worker_rsp_wid )
1101 goto EXIT;
1102
1103 /* Setup request pipeline */
1104
1105 if( (worker_req_evfd = eventfd(0, EFD_CLOEXEC)) == -1 )
1106 goto EXIT;
1107
1108 ack = true;
1109
1110EXIT:
1111
1112 return ack;
1113}
1114
1115bool
1116worker_init(void)
1117{
1118 LOG_REGISTER_CONTEXT;
1119
1120 bool ack = false;
1121
1122 if( !worker_create_eventfd() )
1123 goto EXIT;
1124
1125 if( !worker_start_thread() )
1126 goto EXIT;
1127
1128 ack = true;
1129
1130EXIT:
1131 if( !ack )
1132 worker_quit();
1133
1134 return ack;
1135}
1136
1137void
1138worker_quit(void)
1139{
1140 LOG_REGISTER_CONTEXT;
1141
1142 worker_stop_thread();
1143 worker_delete_eventfd();
1144
1145 /* Worker thread is stopped and resources can be released. */
1147}
1148
1149void
1150worker_wakeup(void)
1151{
1152 LOG_REGISTER_CONTEXT;
1153
1154 worker_bailout_requested = true;
1155
1156 uint64_t cnt = 1;
1157 if( write(worker_req_evfd, &cnt, sizeof cnt) == -1 ) {
1158 log_err("failed to signal requested: %m");
1159 }
1160}
1161
1162static void
1163worker_notify(void)
1164{
1165 LOG_REGISTER_CONTEXT;
1166
1167 uint64_t cnt = 1;
1168 if( write(worker_rsp_evfd, &cnt, sizeof cnt) == -1 ) {
1169 log_err("failed to signal handled: %m");
1170 }
1171}
void appsync_switch_configuration(void)
void modedata_free(modedata_t *self)
modedata_t * modedata_copy(const modedata_t *that)
#define MODE_ASK
#define MODE_CHARGING
#define MODE_CHARGING_FALLBACK
#define MODE_UNDEFINED
#define MODE_CHARGER
int modules_load_module(const char *module)
int modules_unload_module(const char *module)
bool worker_set_kernel_module(const char *module)
modedata_t * worker_dup_usb_mode_data(void)
const modedata_t * worker_get_usb_mode_data(void)
devstate_t
@ DEVSTATE_UNKNOWN
@ DEVSTATE_UNMOUNTED
@ DEVSTATE_MOUNTED
void worker_set_usb_mode_data(const modedata_t *data)
const char * worker_get_kernel_module(void)
modedata_t * usbmoded_dup_modedata(const char *modename)
Definition usb_moded.c:279
bool usbmoded_can_export(void)
Definition usb_moded.c:591
uid_t usbmoded_get_current_user(void)
Definition usb_moded.c:572