@@ -34,16 +34,20 @@
#include "src/shared/queue.h"
#include "emulator/hciemu.h"
+struct hciemu_client {
+ struct bthost *host;
+ struct btdev *dev;
+ guint start_source;
+ guint host_source;
+ guint source;
+};
+
struct hciemu {
int ref_count;
enum btdev_type btdev_type;
- struct bthost *host_stack;
- struct btdev *master_dev;
- struct btdev *client_dev;
- guint host_source;
- guint master_source;
- guint client_source;
- guint start_source;
+ struct btdev *dev;
+ struct queue *clients;
+ guint source;
struct queue *post_command_hooks;
char bdaddr_str[18];
@@ -245,70 +249,123 @@ static bool create_vhci(struct hciemu *hciemu)
return false;
}
- hciemu->master_dev = btdev;
+ hciemu->dev = btdev;
- hciemu->master_source = create_source_btdev(fd, btdev);
+ hciemu->source = create_source_btdev(fd, btdev);
return true;
}
+struct hciemu_client *hciemu_get_client(struct hciemu *hciemu, int num)
+{
+ const struct queue_entry *entry;
+
+ if (!hciemu)
+ return NULL;
+
+ for (entry = queue_get_entries(hciemu->clients); entry;
+ entry = entry->next, num--) {
+ if (!num)
+ return entry->data;
+ }
+
+ return NULL;
+}
+
+struct bthost *hciemu_client_host(struct hciemu_client *client)
+{
+ if (!client)
+ return NULL;
+
+ return client->host;
+}
+
struct bthost *hciemu_client_get_host(struct hciemu *hciemu)
{
+ struct hciemu_client *client;
+
if (!hciemu)
return NULL;
- return hciemu->host_stack;
+ client = hciemu_get_client(hciemu, 0);
+
+ return hciemu_client_host(client);
}
-static bool create_stack(struct hciemu *hciemu)
+static gboolean start_host(gpointer user_data)
{
- struct btdev *btdev;
- struct bthost *bthost;
- int sv[2];
+ struct hciemu_client *client = user_data;
- btdev = btdev_create(hciemu->btdev_type, 0x00);
- if (!btdev)
- return false;
+ client->start_source = 0;
- bthost = bthost_create();
- if (!bthost) {
- btdev_destroy(btdev);
- return false;
- }
+ bthost_start(client->host);
+
+ return FALSE;
+}
- btdev_set_command_handler(btdev, client_command_callback, hciemu);
+static void hciemu_client_destroy(void *data)
+{
+ struct hciemu_client *client = data;
- if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC,
- 0, sv) < 0) {
- bthost_destroy(bthost);
- btdev_destroy(btdev);
- return false;
- }
+ if (client->start_source)
+ g_source_remove(client->start_source);
- hciemu->client_dev = btdev;
- hciemu->host_stack = bthost;
+ g_source_remove(client->host_source);
+ g_source_remove(client->source);
- hciemu->client_source = create_source_btdev(sv[0], btdev);
- hciemu->host_source = create_source_bthost(sv[1], bthost);
+ bthost_destroy(client->host);
+ btdev_destroy(client->dev);
- return true;
+ free(client);
}
-static gboolean start_stack(gpointer user_data)
+static struct hciemu_client *hciemu_client_new(struct hciemu *hciemu)
{
- struct hciemu *hciemu = user_data;
+ struct hciemu_client *client;
+ int sv[2];
+ static uint8_t id;
- hciemu->start_source = 0;
+ client = new0(struct hciemu_client, 1);
+ if (!client)
+ return NULL;
- bthost_start(hciemu->host_stack);
+ client->dev = btdev_create(hciemu->btdev_type, id++);
+ if (!client->dev) {
+ free(client);
+ return NULL;
+ }
- return FALSE;
+ client->host = bthost_create();
+ if (!client->host) {
+ btdev_destroy(client->dev);
+ free(client);
+ return NULL;
+ }
+
+ btdev_set_command_handler(client->dev, client_command_callback, client);
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC,
+ 0, sv) < 0) {
+ bthost_destroy(client->host);
+ btdev_destroy(client->dev);
+ return NULL;
+ }
+
+ client->source = create_source_btdev(sv[0], client->dev);
+ client->host_source = create_source_bthost(sv[1], client->host);
+ client->start_source = g_idle_add(start_host, client);
+
+ return client;
}
-struct hciemu *hciemu_new(enum hciemu_type type)
+struct hciemu *hciemu_new_num(enum hciemu_type type, uint8_t num)
{
+
struct hciemu *hciemu;
+ if (!num)
+ return NULL;
+
hciemu = new0(struct hciemu, 1);
if (!hciemu)
return NULL;
@@ -348,19 +405,27 @@ struct hciemu *hciemu_new(enum hciemu_type type)
return NULL;
}
- if (!create_stack(hciemu)) {
- g_source_remove(hciemu->master_source);
- btdev_destroy(hciemu->master_dev);
- queue_destroy(hciemu->post_command_hooks, NULL);
- free(hciemu);
- return NULL;
- }
+ hciemu->clients = queue_new();
+
+ while (num--) {
+ struct hciemu_client *client = hciemu_client_new(hciemu);
- hciemu->start_source = g_idle_add(start_stack, hciemu);
+ if (!client) {
+ queue_destroy(hciemu->clients, hciemu_client_destroy);
+ break;
+ }
+
+ queue_push_tail(hciemu->clients, client);
+ }
return hciemu_ref(hciemu);
}
+struct hciemu *hciemu_new(enum hciemu_type type)
+{
+ return hciemu_new_num(type, 1);
+}
+
struct hciemu *hciemu_ref(struct hciemu *hciemu)
{
if (!hciemu)
@@ -380,17 +445,10 @@ void hciemu_unref(struct hciemu *hciemu)
return;
queue_destroy(hciemu->post_command_hooks, destroy_command_hook);
+ queue_destroy(hciemu->clients, hciemu_client_destroy);
- if (hciemu->start_source)
- g_source_remove(hciemu->start_source);
-
- g_source_remove(hciemu->host_source);
- g_source_remove(hciemu->client_source);
- g_source_remove(hciemu->master_source);
-
- bthost_destroy(hciemu->host_stack);
- btdev_destroy(hciemu->client_dev);
- btdev_destroy(hciemu->master_dev);
+ g_source_remove(hciemu->source);
+ btdev_destroy(hciemu->dev);
free(hciemu);
}
@@ -419,6 +477,15 @@ static void btdev_client_debug(const char *str, void *user_data)
"btdev[bthost]: %s", str);
}
+static void hciemu_client_set_debug(void *data, void *user_data)
+{
+ struct hciemu_client *client = data;
+ struct hciemu *hciemu = user_data;
+
+ btdev_set_debug(client->dev, btdev_client_debug, hciemu, NULL);
+ bthost_set_debug(client->host, bthost_debug, hciemu, NULL);
+}
+
bool hciemu_set_debug(struct hciemu *hciemu, hciemu_debug_func_t callback,
void *user_data, hciemu_destroy_func_t destroy)
{
@@ -432,9 +499,9 @@ bool hciemu_set_debug(struct hciemu *hciemu, hciemu_debug_func_t callback,
hciemu->debug_destroy = destroy;
hciemu->debug_data = user_data;
- btdev_set_debug(hciemu->master_dev, btdev_master_debug, hciemu, NULL);
- btdev_set_debug(hciemu->client_dev, btdev_client_debug, hciemu, NULL);
- bthost_set_debug(hciemu->host_stack, bthost_debug, hciemu, NULL);
+ btdev_set_debug(hciemu->dev, btdev_master_debug, hciemu, NULL);
+
+ queue_foreach(hciemu->clients, hciemu_client_set_debug, hciemu);
return true;
}
@@ -443,10 +510,10 @@ const char *hciemu_get_address(struct hciemu *hciemu)
{
const uint8_t *addr;
- if (!hciemu || !hciemu->master_dev)
+ if (!hciemu || !hciemu->dev)
return NULL;
- addr = btdev_get_bdaddr(hciemu->master_dev);
+ addr = btdev_get_bdaddr(hciemu->dev);
sprintf(hciemu->bdaddr_str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
return hciemu->bdaddr_str;
@@ -454,50 +521,62 @@ const char *hciemu_get_address(struct hciemu *hciemu)
uint8_t *hciemu_get_features(struct hciemu *hciemu)
{
- if (!hciemu || !hciemu->master_dev)
+ if (!hciemu || !hciemu->dev)
return NULL;
- return btdev_get_features(hciemu->master_dev);
+ return btdev_get_features(hciemu->dev);
}
const uint8_t *hciemu_get_master_bdaddr(struct hciemu *hciemu)
{
- if (!hciemu || !hciemu->master_dev)
+ if (!hciemu || !hciemu->dev)
return NULL;
- return btdev_get_bdaddr(hciemu->master_dev);
+ return btdev_get_bdaddr(hciemu->dev);
+}
+
+const uint8_t *hciemu_client_bdaddr(struct hciemu_client *client)
+{
+ if (!client)
+ return NULL;
+
+ return btdev_get_bdaddr(client->dev);
}
const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu)
{
- if (!hciemu || !hciemu->client_dev)
+ struct hciemu_client *client;
+
+ if (!hciemu)
return NULL;
- return btdev_get_bdaddr(hciemu->client_dev);
+ client = hciemu_get_client(hciemu, 0);
+
+ return hciemu_client_bdaddr(client);
}
uint8_t hciemu_get_master_scan_enable(struct hciemu *hciemu)
{
- if (!hciemu || !hciemu->master_dev)
+ if (!hciemu || !hciemu->dev)
return 0;
- return btdev_get_scan_enable(hciemu->master_dev);
+ return btdev_get_scan_enable(hciemu->dev);
}
uint8_t hciemu_get_master_le_scan_enable(struct hciemu *hciemu)
{
- if (!hciemu || !hciemu->master_dev)
+ if (!hciemu || !hciemu->dev)
return 0;
- return btdev_get_le_scan_enable(hciemu->master_dev);
+ return btdev_get_le_scan_enable(hciemu->dev);
}
void hciemu_set_master_le_states(struct hciemu *hciemu, const uint8_t *le_states)
{
- if (!hciemu || !hciemu->master_dev)
+ if (!hciemu || !hciemu->dev)
return;
- btdev_set_le_states(hciemu->master_dev, le_states);
+ btdev_set_le_states(hciemu->dev, le_states);
}
bool hciemu_add_master_post_command_hook(struct hciemu *hciemu,
@@ -559,7 +638,7 @@ int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
return -1;
}
- return btdev_add_hook(hciemu->master_dev, hook_type, opcode, function,
+ return btdev_add_hook(hciemu->dev, hook_type, opcode, function,
user_data);
}
@@ -588,5 +667,5 @@ bool hciemu_del_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
return false;
}
- return btdev_del_hook(hciemu->master_dev, hook_type, opcode);
+ return btdev_del_hook(hciemu->dev, hook_type, opcode);
}
@@ -12,6 +12,7 @@
#include <stdint.h>
struct hciemu;
+struct hciemu_client;
enum hciemu_type {
HCIEMU_TYPE_BREDRLE,
@@ -30,10 +31,15 @@ enum hciemu_hook_type {
};
struct hciemu *hciemu_new(enum hciemu_type type);
+struct hciemu *hciemu_new_num(enum hciemu_type type, uint8_t num);
struct hciemu *hciemu_ref(struct hciemu *hciemu);
void hciemu_unref(struct hciemu *hciemu);
+struct hciemu_client *hciemu_get_client(struct hciemu *hciemu, int num);
+struct bthost *hciemu_client_host(struct hciemu_client *client);
+const uint8_t *hciemu_client_bdaddr(struct hciemu_client *client);
+
typedef void (*hciemu_debug_func_t)(const char *str, void *user_data);
typedef void (*hciemu_destroy_func_t)(void *user_data);
bool hciemu_set_debug(struct hciemu *hciemu, hciemu_debug_func_t callback,
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This adds support for creating multiple clients (bthost). --- emulator/hciemu.c | 233 +++++++++++++++++++++++++++++++--------------- emulator/hciemu.h | 6 ++ 2 files changed, 162 insertions(+), 77 deletions(-)