/*************************************************************************** * * drvctl.c : NetBSD drvctl events * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Licensed under the Academic Free License version 2.1 * **************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "../osspec.h" #include "../logger.h" #include "../hald.h" #include "../hald_dbus.h" #include "../device_info.h" #include "../util.h" #include "osspec_netbsd.h" #include "hotplug.h" #include "devinfo.h" #include "devinfo_storage.h" #include "drvctl.h" static gboolean drvctl_iochannel_data(GIOChannel *, GIOCondition, gpointer); static void drvctl_dev_add(gchar *); static void drvctl_dev_remove(gchar *); static void drvctl_dev_branch(gchar *); static int drvctl_fd; static GIOChannel *drvctl_iochannel; gboolean drvctl_init(void) { drvctl_fd = open (DRVCTLDEV, O_RDWR); if (drvctl_fd == -1) { HAL_INFO (("open(%s, O_RDWR) failed: %s", DRVCTLDEV, strerror(errno))); return FALSE; } drvctl_iochannel = g_io_channel_unix_new (drvctl_fd); if (drvctl_iochannel == NULL) { HAL_INFO (("g_io_channel_unix_new failed")); return FALSE; } g_io_add_watch (drvctl_iochannel, G_IO_IN, drvctl_iochannel_data, NULL); return TRUE; } void drvctl_fini(void) { HAL_INFO (("drvctl_fini")); } static gboolean drvctl_iochannel_data (GIOChannel *source, GIOCondition condition, gpointer user_data) { prop_dictionary_t ev; const char *event, *device; int res; HAL_INFO (("drvctl_iochannel_data")); res = prop_dictionary_recv_ioctl (drvctl_fd, DRVGETEVENT, &ev); if (res) { HAL_WARNING (("DRVGETEVENT failed: %s", strerror(errno))); return FALSE; } if (!prop_dictionary_get_cstring_nocopy (ev, "event", &event)) { HAL_WARNING (("DRVGETEVENT missing \"event\" parameter")); goto done; } if (!prop_dictionary_get_cstring_nocopy (ev, "device", &device)) { HAL_WARNING (("DRVGETEVENT missing \"device\" parameter")); goto done; } HAL_INFO (("DRVGETEVENT event=%s device=%s", event, device)); if (strcmp (event, "device-attach") == 0) { drvctl_dev_add (device); } else { drvctl_dev_remove (device); } done: prop_object_release(ev); return TRUE; } static void drvctl_dev_add(gchar *name) { HalDevice *parent, *d; gchar pdevnode[512]; if (drvctl_find_parent (name, pdevnode) == FALSE) { HAL_INFO (("dev_add: name=%s orphan", name)); parent = NULL; } else { parent = hal_device_store_match_key_value_string ( hald_get_gdl(), "netbsd.device", pdevnode); if (parent == NULL) HAL_INFO (("dev_add: name=%s but netbsd.device=%s not found",name, pdevnode)); } d = devinfo_add_node (parent, name); if (d == NULL) HAL_WARNING (("dev_add: couldn't add %s node (parent=%p)", name, parent)); hotplug_event_process_queue (); } static void drvctl_dev_remove(gchar *name) { HAL_INFO (("dev_remove: %s", name)); devinfo_remove_branch (name, NULL); hotplug_event_process_queue (); } static void drvctl_dev_branch(gchar *name) { HAL_INFO (("branch_remove: %s", name)); devinfo_remove_branch (name, NULL); hotplug_event_process_queue (); } int drvctl_list(const gchar *name, struct devlistargs *laa) { size_t children; /* HAL_INFO (("drvctl_list: %s", name)); */ memset (laa, 0, sizeof (*laa)); strlcpy (laa->l_devname, name, sizeof (laa->l_devname)); if (ioctl (drvctl_fd, DRVLISTDEV, laa) == -1) { HAL_INFO (("DRVLISTDEV/1 failed: %s", strerror(errno))); return -1; } children = laa->l_children; /* HAL_INFO (("%s: found %d children", name, children)); */ if (children == 0) return -1; laa->l_childname = malloc (children * sizeof (laa->l_childname[0])); if (laa->l_childname == NULL) { HAL_INFO (("drvctl_list couldn't allocate %d children: %s\n", children, strerror(errno))); return -1; } if (ioctl (drvctl_fd, DRVLISTDEV, laa) == -1) { HAL_INFO (("DRVLISTDEV/2 failed: %s", strerror(errno))); return -1; } if (children != laa->l_children) HAL_WARNING (("DRVLISTDEV/3 expected %d children, got %d", children, laa->l_childname)); return 0; } gboolean drvctl_find_device(const gchar *devnode, prop_dictionary_t *properties) { prop_dictionary_t command_dict; prop_dictionary_t args_dict; prop_dictionary_t results_dict; int err; command_dict = prop_dictionary_create (); args_dict = prop_dictionary_create (); prop_dictionary_set_cstring_nocopy (command_dict, "drvctl-command", "get-properties"); prop_dictionary_set_cstring_nocopy (args_dict, "device-name", devnode); prop_dictionary_set (command_dict, "drvctl-arguments", args_dict); prop_object_release (args_dict); err = prop_dictionary_sendrecv_ioctl (command_dict, drvctl_fd, DRVCTLCOMMAND, &results_dict); prop_object_release (command_dict); if (err) return FALSE; if (prop_dictionary_get_int8 (results_dict, "drvctl-error", &err) == false || err != 0) { prop_object_release (results_dict); return FALSE; } if (properties) { prop_dictionary_t result_data; result_data = prop_dictionary_get (results_dict, "drvctl-result-data"); if (result_data) *properties = prop_dictionary_copy (result_data); } prop_object_release (results_dict); return TRUE; } static gboolean drvctl_find_device_with_child(const gchar *curnode, const gchar *devnode, char *parent) { struct devlistargs laa; u_int i; if (drvctl_list (curnode, &laa) == -1) return FALSE; for (i = 0; i < laa.l_children; i++) { if (strcmp (laa.l_childname[i], devnode) == 0) { strlcpy(parent, curnode, 16); free(laa.l_childname); return TRUE; } if (drvctl_find_device_with_child (laa.l_childname[i], devnode, parent) == TRUE) { free(laa.l_childname); return TRUE; } } if (laa.l_childname) free(laa.l_childname); HAL_INFO (("%s: couldn't find device with child %s", curnode, devnode)); return FALSE; } gboolean drvctl_find_parent(const gchar *devnode, char *parent) { gboolean ret; ret = drvctl_find_device_with_child("mainbus0", devnode, parent); if (ret == FALSE) { ret = drvctl_find_device_with_child("armfdt0", devnode, parent); } return ret; } #if 0 static void drvctl_lofi_add(gchar *devfs_path, gchar *name) { di_node_t node; const char *parent_udi; HalDevice *d, *parent; HAL_INFO (("lofi_add: %s %s", name, devfs_path)); if ((d = hal_device_store_match_key_value_string (hald_get_gdl (), "solaris.devfs_path", devfs_path)) == NULL) { HAL_INFO (("device not found in GDL %s", devfs_path)); return; } parent_udi = hal_device_property_get_string (d, "info.parent"); if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) { HAL_INFO (("parent not found in GDL %s", parent_udi)); return; } if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (), "info.udi", parent_udi)) == NULL) { HAL_INFO (("parent not found in GDL %s", parent_udi)); return; } if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { HAL_INFO (("device not found in devinfo %s", devfs_path)); return; } HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi)); devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d); di_fini (node); hotplug_event_process_queue (); } static void drvctl_lofi_remove(gchar *parent_devfs_path, gchar *name) { devinfo_lofi_remove_minor(parent_devfs_path, name); hotplug_event_process_queue (); } #endif