summaryrefslogtreecommitdiff
path: root/hald/linux/addons/addon-usb-csr.c
diff options
context:
space:
mode:
Diffstat (limited to 'hald/linux/addons/addon-usb-csr.c')
-rw-r--r--hald/linux/addons/addon-usb-csr.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/hald/linux/addons/addon-usb-csr.c b/hald/linux/addons/addon-usb-csr.c
new file mode 100644
index 00000000..cd1747dc
--- /dev/null
+++ b/hald/linux/addons/addon-usb-csr.c
@@ -0,0 +1,329 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hal_addon_usb_csr.c : daemon handling CSR-based wireless mice
+ *
+ * Copyright (C) 2004 Sergey V. Udaltsov <svu@gnome.org>
+ * Copyright (C) 2005 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 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 <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <usb.h>
+
+#include <glib/gmain.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "libhal/libhal.h"
+#include "../../logger.h"
+#include "../../util_helper.h"
+
+#define TIMEOUT 30L
+
+/* Internal CSR registered, I presume - for some reason not addressed directly */
+#define P6 (buf[0])
+#define P0 (buf[1])
+#define P4 (buf[2])
+#define P5 (buf[3])
+#define P8 (buf[4])
+#define P9 (buf[5])
+#define PB0 (buf[6])
+#define PB1 (buf[7])
+
+typedef struct _PropertyCacheItem
+{
+ gboolean bus_no_present;
+ int bus_no;
+ gboolean port_no_present;
+ int port_no;
+ gboolean csr_is_dual_present;
+ gboolean csr_is_dual;
+ gboolean current_charge_present;
+ int current_charge;
+} PropertyCacheItem;
+
+/* globals */
+static PropertyCacheItem *dev_props = NULL;
+static LibHalContext *halctx = NULL;
+static GMainLoop *main_loop;
+static const char *device_udi;
+
+/* prototypes */
+static struct usb_device *find_device (const char *hal_device_udi, PropertyCacheItem *pci);
+
+static PropertyCacheItem*
+property_cache_item_get (const char *hal_device_udi)
+{
+ PropertyCacheItem * pci = g_new0 (PropertyCacheItem,1);
+ DBusError err;
+ dbus_error_init (&err);
+
+ pci->bus_no_present = libhal_device_property_exists (halctx, hal_device_udi,
+ "usb_device.bus_number", &err);
+ if (dbus_error_is_set (&err))
+ HAL_ERROR (("Error: [%s]/[%s]", err.name, err.message));
+
+ if (pci->bus_no_present)
+ pci->bus_no = libhal_device_get_property_int (halctx, hal_device_udi,
+ "usb_device.bus_number", &err);
+
+ pci->port_no_present = libhal_device_property_exists (halctx, hal_device_udi,
+ "usb_device.linux.device_number", &err);
+ if (pci->port_no_present)
+ pci->port_no = libhal_device_get_property_int (halctx, hal_device_udi,
+ "usb_device.linux.device_number", &err);
+
+ pci->csr_is_dual_present = libhal_device_property_exists (halctx, hal_device_udi,
+ "battery.csr.is_dual", &err);
+ if (pci->csr_is_dual_present)
+ pci->csr_is_dual = libhal_device_get_property_bool (halctx, hal_device_udi,
+ "battery.csr.is_dual", &err);
+
+ pci->current_charge_present = libhal_device_property_exists (halctx, hal_device_udi,
+ "battery.charge_level.current", &err);
+ if (pci->current_charge_present)
+ pci->current_charge = libhal_device_get_property_int (halctx, hal_device_udi,
+ "battery.charge_level.current", &err);
+
+ return pci;
+}
+
+/* Thanks to lmctl code. I'd LOVE, REALLY LOVE to see some docs though... */
+static void
+check_battery (const char *hal_device_udi, PropertyCacheItem *pci)
+{
+ struct usb_device *curr_device;
+ usb_dev_handle *handle;
+ char buf[80];
+ DBusError err;
+ unsigned int addr;
+ int is_dual = 0;
+ int percentage = 0;
+
+ if (pci == NULL)
+ return;
+
+ HAL_DEBUG (("CSR device: [%s]", hal_device_udi));
+ is_dual = pci->csr_is_dual;
+
+ /* Which of subdevices to address */
+ HAL_DEBUG (("Is dual: %d", is_dual));
+ addr = is_dual? 1<<8 : 0;
+
+ curr_device = find_device (hal_device_udi, pci);
+ if (curr_device == NULL) {
+ HAL_ERROR (("Device %s not found", hal_device_udi));
+ return;
+ }
+
+ handle = usb_open (curr_device);
+ if (handle == NULL) {
+ HAL_ERROR (("Could not open usb device"));
+ return;
+ }
+
+ if (!usb_control_msg (handle, 0xc0, 0x09, 0x03|addr, 0x00|addr,
+ buf, 8, TIMEOUT) != 8) {
+ if ((P0 == 0x3b) && (P4 == 0)) {
+ HAL_DEBUG (("Receiver busy, trying again later"));
+ } else {
+ int current_charge = P5 & 0x07;
+
+ HAL_DEBUG (("Charge level: %d->%d", pci->current_charge, current_charge));
+ if (current_charge != pci->current_charge) {
+ pci->current_charge = current_charge; dbus_error_init (&err);
+ libhal_device_set_property_int (halctx, hal_device_udi,
+ "battery.charge_level.current", current_charge, &err);
+ if (current_charge != 0)
+ percentage = (100.0 / 7.0) * current_charge;
+ libhal_device_set_property_int (halctx, hal_device_udi,
+ "battery.charge_level.percentage", percentage, &err);
+ }
+ }
+ } else
+ perror ("Writing to USB device");
+ usb_close (handle);
+}
+
+/* TODO: Is it linux-specific way to find the device? */
+static struct usb_device*
+find_device (const char *hal_device_udi, PropertyCacheItem *pci)
+{
+ struct usb_bus* curr_bus;
+ char LUdirname[5];
+ char LUfname[5];
+
+ if (!(pci->bus_no_present && pci->port_no_present)) {
+ /* no sysfs path */
+ HAL_ERROR (("No hal bus number and/or port number"));
+ return NULL;
+ }
+ snprintf (LUdirname, sizeof (LUdirname), "%03d", pci->bus_no);
+ snprintf (LUfname, sizeof (LUfname), "%03d",pci->port_no);
+ HAL_DEBUG (("Looking for: [%s][%s]", LUdirname, LUfname));
+
+ for (curr_bus = usb_busses; curr_bus != NULL; curr_bus = curr_bus->next) {
+ struct usb_device *curr_device;
+ /* dbg ("Checking bus: [%s]", curr_bus->dirname); */
+ if (g_strcasecmp (LUdirname, curr_bus->dirname))
+ continue;
+
+ for (curr_device = curr_bus->devices; curr_device != NULL;
+ curr_device = curr_device->next) {
+ /* dbg ("Checking port: [%s]", curr_device->filename); */
+ if (g_strcasecmp (LUfname, curr_device->filename))
+ continue;
+ HAL_DEBUG (("Matched device: [%s][%s][%04X:%04X]", curr_bus->dirname,
+ curr_device->filename,
+ curr_device->descriptor.idVendor,
+ curr_device->descriptor.idProduct));
+ return curr_device;
+ }
+ }
+ return NULL;
+}
+
+static gboolean
+check_all_batteries (gpointer data)
+{
+ HAL_DEBUG (("** Check batteries"));
+ /* TODO: make it configurable (not to rescan every time) */
+ usb_find_busses ();
+ usb_find_devices ();
+ check_battery (device_udi, dev_props);
+ return TRUE;
+}
+
+static gboolean
+is_the_device (const char *hal_device_udi)
+{
+ return !g_ascii_strcasecmp (device_udi, hal_device_udi);
+}
+
+static void
+device_removed (LibHalContext *ctx, const char *hal_device_udi)
+{
+ /* this device is removed */
+ if (is_the_device (hal_device_udi)) {
+ HAL_DEBUG (("** The device %s removed, exit", device_udi));
+ g_main_loop_quit (main_loop);
+ }
+}
+
+static void
+property_modified (LibHalContext *ctx,
+ const char *hal_device_udi,
+ const char *key,
+ dbus_bool_t is_removed,
+ dbus_bool_t is_added)
+{
+ /* "Key" property modified */
+ if (!g_ascii_strcasecmp (key, "battery.command_interface")) {
+ if (is_removed) {
+ HAL_DEBUG (("** Main Property %s removed: %s", key, hal_device_udi));
+ /* probably we'll have to exit if this is our device */
+ device_removed (ctx, hal_device_udi);
+ }
+ } else
+ /* "Secondary" property modified */
+ if (is_the_device (hal_device_udi))
+ {
+ if (!(g_ascii_strcasecmp (key, "usb_device.bus_number") &&
+ g_ascii_strcasecmp (key, "usb_device.linux.device_number") &&
+ g_ascii_strcasecmp (key, "battery.csr.is_dual"))) {
+ HAL_DEBUG (("** Property %s added/changed: %s", key, hal_device_udi));
+ if (dev_props)
+ g_free (dev_props);
+ dev_props = property_cache_item_get (hal_device_udi);
+ }
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ DBusError err;
+
+ hal_set_proc_title_init (argc, argv);
+
+ setup_logger ();
+
+ device_udi = getenv ("UDI");
+
+ HAL_DEBUG (("device:[%s]", device_udi));
+ if (device_udi == NULL) {
+ HAL_ERROR (("No device specified"));
+ return -2;
+ }
+
+ dbus_error_init (&err);
+ if ((halctx = libhal_ctx_init_direct (&err)) == NULL) {
+ HAL_ERROR (("Cannot connect to hald"));
+ return -3;
+ }
+
+ /* update_properties */
+ dbus_error_init (&err);
+ libhal_device_set_property_bool (halctx, device_udi,
+ "battery.present", TRUE, &err);
+ if (!libhal_device_property_exists (halctx, device_udi,
+ "battery.is_rechargeable", &err))
+ libhal_device_set_property_bool (halctx, device_udi,
+ "battery.is_rechargeable", FALSE, &err);
+ libhal_device_set_property_int (halctx, device_udi,
+ "battery.charge_level.design", 7, &err);
+ libhal_device_set_property_int (halctx, device_udi,
+ "battery.charge_level.last_full", 7, &err);
+ libhal_device_set_property_string (halctx, device_udi,
+ "info.category", "battery", &err);
+ libhal_device_set_property_string (halctx, device_udi,
+ "battery.command_interface", "csr", &err);
+
+ /* monitor change */
+ libhal_ctx_set_device_property_modified (halctx, property_modified);
+
+ /* Initial fillup */
+ dev_props = property_cache_item_get (device_udi);
+ HAL_ERROR (("** Initial fillup done"));
+
+ /* init usb */
+ usb_init ();
+
+ /* do coldplug */
+ check_all_batteries (NULL);
+
+ /* only add capability when initial charge_level key has been set */
+ dbus_error_init (&err);
+ libhal_device_add_capability (halctx, device_udi, "battery", &err);
+
+ hal_set_proc_title ("hald-addon-usb-csr: listening on '%s'",
+ libhal_device_get_property_string(halctx, device_udi,
+ "info.product", &err));
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add (1000L * TIMEOUT, check_all_batteries, NULL);
+ g_main_loop_run (main_loop);
+
+ libhal_ctx_shutdown (halctx, &err);
+ HAL_ERROR (("** Addon exits normally"));
+ return 0;
+}