/** * @file usb_moded-trigger.c * * Copyright (C) 2011 Nokia Corporation. All rights reserved. * Copyright (C) 2014-2018 Jolla Ltd. * * @author: Philippe De Swert * @author: Philippe De Swert * @author: Philippe De Swert * @author: Philippe De Swert * @author: Simo Piiroinen * * This program is free software; you can redistribute it and/or * modify it under the terms of the Lesser GNU General Public License * version 2 as published by the Free Software Foundation. * * 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 Lesser GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include "usb_moded.h" #include "usb_moded-log.h" #include "usb_moded-config-private.h" #include "usb_moded-udev.h" #include "usb_moded-modesetting.h" #include "usb_moded-trigger.h" #if defined MEEGOLOCK # include "usb_moded-devicelock.h" #endif /* MEEGOLOCK */ /* ========================================================================= * * Prototypes * ========================================================================= */ /* -- trigger -- */ static void trigger_notify_issue (gpointer data); gboolean trigger_init (void); static gboolean trigger_udev_event_cb (GIOChannel *iochannel, GIOCondition cond, gpointer data); void trigger_stop (void); static void trigger_parse_udev_properties(struct udev_device *dev); /* ========================================================================= * * Data * ========================================================================= */ /* global variables */ static struct udev *udev = 0; static struct udev_monitor *mon = 0; static GIOChannel *iochannel = 0; static guint watch_id = 0; static const char *dev_name = 0; /* ========================================================================= * * Functions * ========================================================================= */ static void trigger_notify_issue (gpointer data) { (void)data; log_debug("trigger watch destroyed\n!"); /* clean up & restart trigger */ trigger_stop(); trigger_init(); } gboolean trigger_init(void) { const gchar *udev_path = NULL; struct udev_device *dev; int ret = 0; /* Create the udev object */ udev = udev_new(); if (!udev) { log_err("Can't create udev\n"); return 1; } udev_path = config_check_trigger(); if(udev_path) dev = udev_device_new_from_syspath(udev, udev_path); else { log_err("No trigger path. Not starting trigger.\n"); return 1; } if (!dev) { log_err("Unable to find the trigger device."); return 1; } else { dev_name = udev_device_get_sysname(dev); log_debug("device name = %s\n", dev_name); } mon = udev_monitor_new_from_netlink (udev, "udev"); if (!mon) { log_err("Unable to monitor the netlink\n"); /* communicate failure, mainloop will exit and call appropriate clean-up */ return 1; } ret = udev_monitor_filter_add_match_subsystem_devtype(mon, config_get_trigger_subsystem(), NULL); if(ret != 0) { log_err("Udev match failed.\n"); return 1; } ret = udev_monitor_enable_receiving (mon); if(ret != 0) { log_err("Failed to enable monitor recieving.\n"); return 1; } /* check if we are already connected */ trigger_parse_udev_properties(dev); iochannel = g_io_channel_unix_new(udev_monitor_get_fd(mon)); watch_id = g_io_add_watch_full(iochannel, 0, G_IO_IN, trigger_udev_event_cb, NULL, trigger_notify_issue); /* everything went well */ log_debug("Trigger enabled!\n"); return 0; } static gboolean trigger_udev_event_cb(GIOChannel *iochannel G_GNUC_UNUSED, GIOCondition cond, gpointer data G_GNUC_UNUSED) { struct udev_device *dev; if(cond & G_IO_IN) { /* This normally blocks but G_IO_IN indicates that we can read */ dev = udev_monitor_receive_device (mon); if (dev) { /* check if it is the actual device we want to check */ if(strcmp(dev_name, udev_device_get_sysname(dev))) { log_crit("name does not match, disabling udev trigger io-watch"); watch_id = 0; return FALSE; } if(!strcmp(udev_device_get_action(dev), "change")) { log_debug("Trigger event recieved.\n"); trigger_parse_udev_properties(dev); } udev_device_unref(dev); } /* if we get something else something bad happened stop watching to avoid busylooping */ else { log_debug("Bad trigger data. Stopping\n"); watch_id = 0; trigger_stop(); return FALSE; } } /* keep watching */ return TRUE; } void trigger_stop(void) { if(watch_id) { g_source_remove(watch_id); watch_id = 0; } if(iochannel) { g_io_channel_unref(iochannel); iochannel = NULL; } if(mon) { udev_monitor_unref(mon); mon = 0; } if(udev) { udev_unref(udev); udev = 0; } dev_name = 0; } static void trigger_parse_udev_properties(struct udev_device *dev) { const char *tmp = 0; char *trigger = 0; trigger = config_get_trigger_property(); tmp = udev_device_get_property_value(dev, trigger); if(!tmp) { /* do nothing and return */ free(trigger); return; } else { free(trigger); trigger = config_get_trigger_value(); if(trigger) { if(!strcmp(tmp, trigger)) { #if defined MEEGOLOCK if(devicelock_have_export_permission()) #endif /* MEEGOLOCK */ if(strcmp(config_get_trigger_mode(), usbmoded_get_usb_mode()) != 0) { usbmoded_set_usb_mode(config_get_trigger_mode()); } free(trigger); } else { free(trigger); return; } } else /* for triggers without trigger value */ { #if defined MEEGOLOCK if(devicelock_have_export_permission()) #endif /* MEEGOLOCK */ if(strcmp(config_get_trigger_mode(), usbmoded_get_usb_mode()) != 0) { usbmoded_set_usb_mode(config_get_trigger_mode()); } } return; } }