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