#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define OTP_PSM 0x0025
+
+/* OTP Client */
+#define BT_OTC_SERVICE_NAME "org.otp.client"
+#define BT_OTC_OBJECT_PATH "/org/otp/client"
+#define BT_OTC_INTERFACE_NAME "org.otp.otc_channel"
+
+/* OTP Server */
+#define BT_OTS_SERVICE_NAME "org.projectx.otp"
+#define BT_OTS_OBJECT_PATH "/org/projectx/otp"
+#define BT_OTS_INTERFACE_NAME "org.projectx.otp_service"
+
+typedef enum {
+ BT_OTP_CLIENT_ROLE = 0x00,
+ BT_OTP_SERVER_ROLE,
+} bt_otp_role_e;
+
+struct otc_conn_info {
+ const char *dev_path;
+ bt_otp_role_e role;
+ GIOChannel *io;
+ bool otc_connected;
+};
+
+GSList *otc_connection_list = NULL;
+#endif
+
static int device_browse_gatt(struct btd_device *device, DBusMessage *msg);
static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);
return dbus_message_new_method_return(msg);;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static GSList *find_otc_conn_info(GSList *list, const char *path)
+{
+ GSList *l;
+
+ for (l = list; l != NULL; l = g_slist_next(l)) {
+ struct otc_conn_info *info = l->data;
+
+ if (g_strcmp0(info->dev_path, path) == 0)
+ return l;
+ }
+
+ return NULL;
+}
+
+static gboolean otc_disconnected_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct otc_conn_info *conn_info;
+ const char *dev_path = (const char *) user_data;
+ GSList *l;
+
+ DBG("OTC Disconnected");
+
+ l = find_otc_conn_info(otc_connection_list, dev_path);
+ if (!l)
+ return FALSE;
+
+ conn_info = l->data;
+ conn_info->otc_connected = false;
+
+ g_dbus_emit_signal(dbus_conn, dev_path,
+ DEVICE_INTERFACE, "OtcDisconnected",
+ DBUS_TYPE_INVALID);
+
+ if (conn_info->io) {
+ g_io_channel_shutdown(conn_info->io, TRUE, NULL);
+ g_io_channel_unref(conn_info->io);
+ }
+
+ otc_connection_list = g_slist_remove(otc_connection_list, conn_info);
+ g_free(conn_info);
+
+ return FALSE;
+}
+
+static void otc_connect_cb(GIOChannel *chan, GError *gerr,
+ gpointer user_data)
+{
+ const char *dev_path;
+ GSList *l;
+ struct otc_conn_info *conn_info = NULL;
+ DBusMessage *msg = NULL;
+ DBusMessageIter iter;
+ int fd;
+
+ if (gerr)
+ return;
+
+ dev_path = (const char *) user_data;
+ l = find_otc_conn_info(otc_connection_list, dev_path);
+
+ if (!l)
+ return;
+
+ conn_info = l->data;
+ conn_info->otc_connected = true;
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ DBG("OTC Connected fd [%d], role [%s]",
+ fd, conn_info->role ? "server" : "client");
+ DBG("dev_path [%s]", dev_path);
+
+ g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ otc_disconnected_cb, (gpointer) dev_path);
+
+ if (conn_info->role == BT_OTP_CLIENT_ROLE) {
+ msg = dbus_message_new_method_call(BT_OTC_SERVICE_NAME,
+ BT_OTC_OBJECT_PATH,
+ BT_OTC_INTERFACE_NAME,
+ "NewConnection");
+ } else if (conn_info->role == BT_OTP_SERVER_ROLE) {
+ msg = dbus_message_new_method_call(BT_OTS_SERVICE_NAME,
+ BT_OTS_OBJECT_PATH,
+ BT_OTS_INTERFACE_NAME,
+ "NewConnection");
+ }
+
+ if (!msg) {
+ error("Unable to create NewConnection call");
+ return;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &dev_path);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd);
+
+ if (!g_dbus_send_message(dbus_conn, msg)) {
+ error("sending NewConnection failed");
+ dbus_message_unref(msg);
+ }
+}
+
+int btd_adapter_connect_otc(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const bdaddr_t *src = btd_adapter_get_address(adapter);
+ uint8_t src_type = btd_adapter_get_le_address_type(adapter);
+ const bdaddr_t *dest = device_get_address(device);
+ uint8_t dest_type = device->bdaddr_type;
+ struct otc_conn_info *conn_info = NULL;
+ const char *dev_path = device_get_path(device);
+ GError *gerr = NULL;
+
+ conn_info = g_malloc0(sizeof(struct otc_conn_info));
+
+ conn_info->io = bt_io_connect(otc_connect_cb,
+ (gpointer) dev_path, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_SOURCE_TYPE, src_type,
+ BT_IO_OPT_DEST_BDADDR, dest,
+ BT_IO_OPT_DEST_TYPE, dest_type,
+ BT_IO_OPT_PSM, OTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ if (!conn_info->io) {
+ error("OTC Connect failed : %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(conn_info->io);
+ g_free(conn_info);
+ return -EIO;
+ }
+
+ conn_info->dev_path = dev_path;
+ conn_info->role = BT_OTP_CLIENT_ROLE;
+ conn_info->otc_connected = false;
+ g_io_channel_set_close_on_unref(conn_info->io, FALSE);
+
+ otc_connection_list = g_slist_append(otc_connection_list, conn_info);
+ return 0;
+}
+
+static DBusMessage *connect_otc(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ GSList *l;
+
+ if (device == NULL)
+ return btd_error_invalid_args(msg);
+
+ l = find_otc_conn_info(otc_connection_list, device_get_path(device));
+
+ if (l) {
+ struct otc_conn_info *info = l->data;
+ if (info->otc_connected)
+ return btd_error_already_connected(msg);
+ else
+ return btd_error_busy(msg);
+ }
+
+ if (btd_adapter_connect_otc(device) != 0)
+ return btd_error_failed(msg, "ConnectFailed");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disconnect_otc(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ struct otc_conn_info *info = NULL;
+ GError *gerr = NULL;
+ GSList *l;
+ int sock;
+
+ l = find_otc_conn_info(otc_connection_list, device_get_path(device));
+
+ if (!l)
+ return btd_error_does_not_exist(msg);
+
+ info = l->data;
+
+ if (!info->otc_connected)
+ return btd_error_not_connected(msg);
+
+ sock = g_io_channel_unix_get_fd(info->io);
+
+ shutdown(sock, SHUT_RDWR);
+
+ g_io_channel_shutdown(info->io, FALSE, NULL);
+
+ g_io_channel_unref(info->io);
+ info->io = NULL;
+
+ return dbus_message_new_method_return(msg);
+}
+
+int btd_adapter_listen_otc(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const bdaddr_t *src = btd_adapter_get_address(adapter);
+ uint8_t type = btd_adapter_get_le_address_type(adapter);
+ struct otc_conn_info *conn_info = NULL;
+ const char *dev_path = device_get_path(device);
+ GError *gerr = NULL;
+
+ conn_info = g_malloc0(sizeof(struct otc_conn_info));
+
+ conn_info->io = bt_io_listen(otc_connect_cb, NULL, (gpointer) dev_path,
+ NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_SOURCE_TYPE, type,
+ BT_IO_OPT_PSM, OTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ if (!conn_info->io) {
+ error("OTC Listen failed : %s", gerr->message);
+ g_error_free(gerr);
+ return -EIO;
+ }
+
+ conn_info->dev_path = dev_path;
+ conn_info->otc_connected = false;
+ conn_info->role = BT_OTP_SERVER_ROLE;
+ g_io_channel_set_close_on_unref(conn_info->io, FALSE);
+
+ otc_connection_list = g_slist_append(otc_connection_list, conn_info);
+ return 0;
+}
+
+static DBusMessage *listen_otc(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ GSList *l;
+
+ if (device == NULL)
+ return btd_error_invalid_args(msg);
+
+ l = find_otc_conn_info(otc_connection_list, device_get_path(device));
+
+ if (l) {
+ struct otc_conn_info *info = l->data;
+ if (info->otc_connected)
+ return btd_error_already_connected(msg);
+ else
+ return btd_error_busy(msg);
+ }
+
+ if (btd_adapter_listen_otc(device) != 0)
+ return btd_error_failed(msg, "ListenFailed");
+
+ return dbus_message_new_method_return(msg);
+}
+#endif
+
static DBusMessage *le_set_data_length(
DBusConnection *conn, DBusMessage *msg,
void *user_data)
{ GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
{ GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
{ GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("ConnectOtc", NULL, NULL, connect_otc) },
+ { GDBUS_METHOD("DisconnectOtc", NULL, NULL, disconnect_otc) },
+ { GDBUS_METHOD("ListenOtc", NULL, NULL, listen_otc) },
+#endif
{ GDBUS_ASYNC_METHOD("LESetDataLength",
GDBUS_ARGS({"max_tx_octets", "q" },
{ "max_tx_time", "q" }), NULL,