| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * 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 |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <netinet/in.h> |
| |
| #include <glib.h> |
| #include <dbus/dbus.h> |
| |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/hci.h> |
| #include <bluetooth/hci_lib.h> |
| |
| #include "dbus.h" |
| #include "dbus-helper.h" |
| #include "logging.h" |
| #include "textfile.h" |
| |
| #include "device.h" |
| |
| static DBusHandlerResult device_get_address(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct device *device = data; |
| DBusMessage *reply; |
| char address[18], *ptr = address; |
| |
| reply = dbus_message_new_method_return(msg); |
| if (!reply) |
| return DBUS_HANDLER_RESULT_NEED_MEMORY; |
| |
| ba2str(&device->dst, address); |
| |
| dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, |
| DBUS_TYPE_INVALID); |
| |
| return send_message_and_unref(conn, reply); |
| } |
| |
| static DBusHandlerResult device_get_connected(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| DBusMessageIter iter, array_iter; |
| struct device *device = data; |
| DBusMessage *reply; |
| const char *iface; |
| |
| reply = dbus_message_new_method_return(msg); |
| if (!reply) |
| return DBUS_HANDLER_RESULT_NEED_MEMORY; |
| |
| dbus_message_iter_init_append(reply, &iter); |
| |
| dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, |
| DBUS_TYPE_STRING_AS_STRING, |
| &array_iter); |
| |
| if (device->headset && |
| headset_get_state(device) >= HEADSET_STATE_CONNECTED) { |
| iface = AUDIO_HEADSET_INTERFACE; |
| dbus_message_iter_append_basic(&array_iter, |
| DBUS_TYPE_STRING, &iface); |
| } |
| |
| dbus_message_iter_close_container(&iter, &array_iter); |
| |
| return send_message_and_unref(conn, reply); |
| } |
| |
| static DBusMethodVTable device_methods[] = { |
| { "GetAddress", device_get_address, "", "s" }, |
| { "GetConnectedInterfaces", device_get_connected, "", "s" }, |
| { NULL, NULL, NULL, NULL } |
| }; |
| |
| static void device_free(struct device *device) |
| { |
| if (device->headset) |
| headset_free(device); |
| |
| if (device->conn) |
| dbus_connection_unref(device->conn); |
| |
| if (device->adapter_path) |
| g_free(device->adapter_path); |
| |
| if (device->path) |
| g_free(device->path); |
| |
| g_free(device); |
| } |
| |
| static void device_unregister(DBusConnection *conn, void *data) |
| { |
| struct device *device = data; |
| |
| info("Unregistered device path:%s", device->path); |
| |
| device_free(device); |
| } |
| |
| struct device *device_register(DBusConnection *conn, |
| const char *path, bdaddr_t *bda) |
| { |
| struct device *device; |
| bdaddr_t src; |
| int dev_id; |
| |
| if (!conn || !path) |
| return NULL; |
| |
| bacpy(&src, BDADDR_ANY); |
| dev_id = hci_get_route(NULL); |
| if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) |
| return NULL; |
| |
| device = g_new0(struct device, 1); |
| |
| if (!dbus_connection_create_object_path(conn, path, device, |
| device_unregister)) { |
| error("D-Bus failed to register %s path", path); |
| device_free(device); |
| return NULL; |
| } |
| |
| if (!dbus_connection_register_interface(conn, path, |
| AUDIO_DEVICE_INTERFACE, device_methods, NULL, NULL)) { |
| error("Failed to register %s interface to %s", |
| AUDIO_DEVICE_INTERFACE, path); |
| dbus_connection_destroy_object_path(conn, path); |
| return NULL; |
| } |
| |
| device->path = g_strdup(path); |
| bacpy(&device->dst, bda); |
| bacpy(&device->src, &src); |
| device->conn = dbus_connection_ref(conn); |
| device->adapter_path = g_malloc0(16); |
| snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); |
| |
| return device; |
| } |
| |
| int device_store(struct device *device, gboolean is_default) |
| { |
| char value[64]; |
| char filename[PATH_MAX + 1]; |
| char src_addr[18], dst_addr[18]; |
| |
| if (!device->path) |
| return -EINVAL; |
| |
| ba2str(&device->dst, dst_addr); |
| ba2str(&device->src, src_addr); |
| |
| create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio"); |
| create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| |
| if (is_default) |
| textfile_put(filename, "default", dst_addr); |
| if (device->headset) |
| snprintf(value, 64, "headset"); |
| else if (device->gateway) |
| snprintf(value, 64, "gateway"); |
| else if (device->sink) |
| snprintf(value, 64, "sink"); |
| else if (device->source) |
| snprintf(value, 64, "source"); |
| else if (device->control) |
| snprintf(value, 64, "control"); |
| else |
| snprintf(value, 64, "target"); |
| |
| return textfile_put(filename, dst_addr, value); |
| } |
| |
| void device_finish_sdp_transaction(struct device *device) |
| { |
| char address[18], *addr_ptr = address; |
| DBusMessage *msg, *reply; |
| DBusError derr; |
| |
| ba2str(&device->dst, address); |
| |
| msg = dbus_message_new_method_call("org.bluez", device->adapter_path, |
| "org.bluez.Adapter", |
| "FinishRemoteServiceTransaction"); |
| if (!msg) { |
| error("Unable to allocate new method call"); |
| return; |
| } |
| |
| dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, |
| DBUS_TYPE_INVALID); |
| |
| dbus_error_init(&derr); |
| reply = dbus_connection_send_with_reply_and_block(device->conn, |
| msg, -1, &derr); |
| |
| dbus_message_unref(msg); |
| |
| if (dbus_error_is_set(&derr) || |
| dbus_set_error_from_message(&derr, reply)) { |
| error("FinishRemoteServiceTransaction(%s) failed: %s", |
| address, derr.message); |
| dbus_error_free(&derr); |
| return; |
| } |
| |
| dbus_message_unref(reply); |
| } |