static bool create_stack(struct hciemu *hciemu) { struct btdev *btdev; struct bthost *bthost; int sv[2]; btdev = btdev_create(hciemu->btdev_type, 0x00); if (!btdev) return false; bthost = bthost_create(); if (!bthost) { btdev_destroy(btdev); return false; } btdev_set_command_handler(btdev, client_command_callback, hciemu); if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, sv) < 0) { bthost_destroy(bthost); btdev_destroy(btdev); return false; } hciemu->client_dev = btdev; hciemu->host_stack = bthost; hciemu->client_source = create_source_btdev(sv[0], btdev); hciemu->host_source = create_source_bthost(sv[1], bthost); return true; }
static bool create_vhci(struct hciemu *hciemu) { struct btdev *btdev; uint8_t create_req[2]; ssize_t written; int fd; btdev = btdev_create(hciemu->btdev_type, 0x00); if (!btdev) return false; btdev_set_command_handler(btdev, master_command_callback, hciemu); fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { perror("Opening /dev/vhci failed"); btdev_destroy(btdev); return false; } create_req[0] = HCI_VENDOR_PKT; create_req[1] = HCI_BREDR; written = write(fd, create_req, sizeof(create_req)); if (written < 0) { close(fd); btdev_destroy(btdev); return false; } hciemu->master_dev = btdev; hciemu->master_source = create_source_btdev(fd, btdev); return true; }
static bool create_vhci(struct hciemu *hciemu) { struct btdev *btdev; uint8_t bdaddr[6]; const char *str; int fd, i; btdev = btdev_create(hciemu->btdev_type, 0x00); if (!btdev) return false; str = hciemu_get_address(hciemu); for (i = 5; i >= 0; i--, str += 3) bdaddr[i] = strtol(str, NULL, 16); btdev_set_bdaddr(btdev, bdaddr); btdev_set_command_handler(btdev, master_command_callback, hciemu); fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { btdev_destroy(btdev); return false; } hciemu->master_dev = btdev; hciemu->master_source = create_source_btdev(fd, btdev); return true; }
static void server_accept_callback(int fd, uint32_t events, void *user_data) { struct server *server = user_data; struct client *client; enum btdev_type uninitialized_var(type); if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(server->fd); return; } client = malloc(sizeof(*client)); if (!client) return; memset(client, 0, sizeof(*client)); client->fd = accept_client(server->fd); if (client->fd < 0) { free(client); return; } switch (server->type) { case SERVER_TYPE_BREDRLE: type = BTDEV_TYPE_BREDRLE; break; case SERVER_TYPE_BREDR: type = BTDEV_TYPE_BREDR; break; case SERVER_TYPE_LE: type = BTDEV_TYPE_LE; break; case SERVER_TYPE_AMP: type = BTDEV_TYPE_AMP; break; case SERVER_TYPE_MONITOR: goto done; } client->btdev = btdev_create(type, server->id); if (!client->btdev) { close(client->fd); free(client); return; } btdev_set_send_handler(client->btdev, client_write_callback, client); done: if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback, client, client_destroy) < 0) { btdev_destroy(client->btdev); close(client->fd); free(client); } }
static void client_destroy(void *user_data) { struct client *client = user_data; btdev_destroy(client->btdev); close(client->fd); free(client); }
void hciemu_unref(struct hciemu *hciemu) { if (!hciemu) return; if (__sync_sub_and_fetch(&hciemu->ref_count, 1)) return; queue_destroy(hciemu->post_command_hooks, destroy_command_hook); 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); free(hciemu); }
void hciemu_unref(struct hciemu *hciemu) { if (!hciemu) return; if (__sync_sub_and_fetch(&hciemu->ref_count, 1) > 0) return; g_list_foreach(hciemu->post_command_hooks, destroy_command_hook, NULL); g_list_free(hciemu->post_command_hooks); bthost_stop(hciemu->host_stack); 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_free(hciemu); }
struct hciemu *hciemu_new(enum hciemu_type type) { struct hciemu *hciemu; hciemu = new0(struct hciemu, 1); if (!hciemu) return NULL; switch (type) { case HCIEMU_TYPE_BREDRLE: hciemu->btdev_type = BTDEV_TYPE_BREDRLE; break; case HCIEMU_TYPE_BREDR: hciemu->btdev_type = BTDEV_TYPE_BREDR; break; case HCIEMU_TYPE_LE: hciemu->btdev_type = BTDEV_TYPE_LE; break; case HCIEMU_TYPE_LEGACY: hciemu->btdev_type = BTDEV_TYPE_BREDR20; break; default: return NULL; } hciemu->post_command_hooks = queue_new(); if (!hciemu->post_command_hooks) { free(hciemu); return NULL; } if (!create_vhci(hciemu)) { queue_destroy(hciemu->post_command_hooks, NULL); free(hciemu); 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; } g_idle_add(start_stack, hciemu); return hciemu_ref(hciemu); }
struct hciemu *hciemu_new(enum hciemu_type type) { struct hciemu *hciemu; hciemu = g_try_new0(struct hciemu, 1); if (!hciemu) return NULL; switch (type) { case HCIEMU_TYPE_BREDRLE: hciemu->btdev_type = BTDEV_TYPE_BREDRLE; break; case HCIEMU_TYPE_BREDR: hciemu->btdev_type = BTDEV_TYPE_BREDR; break; case HCIEMU_TYPE_LE: hciemu->btdev_type = BTDEV_TYPE_LE; break; default: return NULL; } if (!create_vhci(hciemu)) { g_free(hciemu); return NULL; } if (!create_stack(hciemu)) { g_source_remove(hciemu->master_source); btdev_destroy(hciemu->master_dev); g_free(hciemu); return NULL; } g_idle_add(start_stack, hciemu); return hciemu_ref(hciemu); }
static void server_accept_callback(int fd, uint32_t events, void *user_data) { struct server *server = user_data; struct client *client; if (events & (EPOLLERR | EPOLLHUP)) return; client = malloc(sizeof(*client)); if (!client) return; memset(client, 0, sizeof(*client)); client->fd = accept_client(server->fd); if (client->fd < 0) { free(client); return; } client->btdev = btdev_create(server->id); if (!client->btdev) { close(client->fd); free(client); return; } btdev_set_send_handler(client->btdev, client_write_callback, client); if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback, client, client_destroy) < 0) { btdev_destroy(client->btdev); close(client->fd); free(client); } }