summaryrefslogtreecommitdiff
path: root/emulator
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2012-02-27 10:56:43 -0800
committerMarcel Holtmann <marcel@holtmann.org>2012-02-27 10:56:43 -0800
commitd5be1cc111dd9834c05c040c10529b632ab8f99e (patch)
treebe725f83ca1c003440b8accb38e10892676e7d77 /emulator
parent9d3b93ede77ed7e5b0a040a5df45b55991274808 (diff)
emulator: Add initial version of new emulator
Diffstat (limited to 'emulator')
-rw-r--r--emulator/btdev.c1076
-rw-r--r--emulator/btdev.h38
-rw-r--r--emulator/main.c73
-rw-r--r--emulator/server.c288
-rw-r--r--emulator/server.h30
-rw-r--r--emulator/vhci.c133
-rw-r--r--emulator/vhci.h35
7 files changed, 1673 insertions, 0 deletions
diff --git a/emulator/btdev.c b/emulator/btdev.c
new file mode 100644
index 00000000..7d4517a5
--- /dev/null
+++ b/emulator/btdev.c
@@ -0,0 +1,1076 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bt.h"
+#include "btdev.h"
+
+#define le16_to_cpu(val) (val)
+#define cpu_to_le16(val) (val)
+
+struct btdev {
+ struct btdev *conn;
+
+ btdev_send_func send_handler;
+ void *send_data;
+
+ uint16_t manufacturer;
+ uint8_t version;
+ uint16_t revision;
+ uint8_t commands[64];
+ uint8_t features[8];
+ uint16_t acl_mtu;
+ uint16_t acl_max_pkt;
+ uint8_t country_code;
+ uint8_t bdaddr[6];
+ uint8_t le_features[8];
+ uint8_t le_states[8];
+
+ uint16_t default_link_policy;
+ uint8_t event_mask[8];
+ uint8_t event_filter;
+ uint8_t name[248];
+ uint8_t dev_class[3];
+ uint16_t voice_setting;
+ uint16_t conn_accept_timeout;
+ uint16_t page_timeout;
+ uint8_t scan_enable;
+ uint8_t auth_enable;
+ uint8_t inquiry_mode;
+ uint8_t afh_assess_mode;
+ uint8_t ext_inquiry_fec;
+ uint8_t ext_inquiry_rsp[240];
+ uint8_t simple_pairing_mode;
+ uint8_t le_supported;
+ uint8_t le_simultaneous;
+ uint8_t le_event_mask[8];
+};
+
+#define MAX_BTDEV_ENTRIES 16
+
+static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { };
+
+static inline int add_btdev(struct btdev *btdev)
+{
+ int i, index = -1;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (btdev_list[i] == NULL) {
+ index = i;
+ btdev_list[index] = btdev;
+ break;
+ }
+ }
+
+ return index;
+}
+
+static inline int del_btdev(struct btdev *btdev)
+{
+ int i, index = -1;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (btdev_list[i] == btdev) {
+ index = i;
+ btdev_list[index] = NULL;
+ break;
+ }
+ }
+
+ return index;
+}
+
+static inline struct btdev *find_btdev_by_bdaddr(const uint8_t *bdaddr)
+{
+ int i;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (btdev_list[i] && !memcmp(btdev_list[i]->bdaddr, bdaddr, 6))
+ return btdev_list[i];
+ }
+
+ return NULL;
+}
+
+static void hexdump(const unsigned char *buf, uint16_t len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char str[68];
+ uint16_t i;
+
+ if (!len)
+ return;
+
+ for (i = 0; i < len; i++) {
+ str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+ str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+ str[((i % 16) * 3) + 2] = ' ';
+ str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+ if ((i + 1) % 16 == 0) {
+ str[47] = ' ';
+ str[48] = ' ';
+ str[65] = '\0';
+ printf("%-12c%s\n", ' ', str);
+ str[0] = ' ';
+ }
+ }
+
+ if (i % 16 > 0) {
+ uint16_t j;
+ for (j = (i % 16); j < 16; j++) {
+ str[(j * 3) + 0] = ' ';
+ str[(j * 3) + 1] = ' ';
+ str[(j * 3) + 2] = ' ';
+ str[j + 49] = ' ';
+ }
+ str[47] = ' ';
+ str[48] = ' ';
+ str[65] = '\0';
+ printf("%-12c%s\n", ' ', str);
+ }
+}
+
+static void get_bdaddr(uint16_t id, uint8_t *bdaddr)
+{
+ bdaddr[0] = id & 0xff;
+ bdaddr[1] = id >> 8;
+ bdaddr[2] = 0x00;
+ bdaddr[3] = 0x01;
+ bdaddr[4] = 0xaa;
+ bdaddr[5] = 0x00;
+}
+
+struct btdev *btdev_create(uint16_t id)
+{
+ struct btdev *btdev;
+
+ btdev = malloc(sizeof(*btdev));
+ if (!btdev)
+ return NULL;
+
+ memset(btdev, 0, sizeof(*btdev));
+
+ btdev->manufacturer = 63;
+ btdev->version = 0x06;
+ btdev->revision = 0x0000;
+
+ btdev->features[0] |= 0x04; /* Encryption */
+ btdev->features[0] |= 0x20; /* Role switch */
+ btdev->features[0] |= 0x80; /* Sniff mode */
+ btdev->features[1] |= 0x08; /* SCO link */
+ btdev->features[3] |= 0x40; /* RSSI with inquiry results */
+ btdev->features[3] |= 0x80; /* Extended SCO link */
+ btdev->features[4] |= 0x08; /* AFH capable slave */
+ btdev->features[4] |= 0x10; /* AFH classification slave */
+ btdev->features[4] |= 0x40; /* LE Supported */
+ btdev->features[5] |= 0x02; /* Sniff subrating */
+ btdev->features[5] |= 0x04; /* Pause encryption */
+ btdev->features[5] |= 0x08; /* AFH capable master */
+ btdev->features[5] |= 0x10; /* AFH classification master */
+ btdev->features[6] |= 0x01; /* Extended Inquiry Response */
+ btdev->features[6] |= 0x02; /* Simultaneous LE and BR/EDR */
+ btdev->features[6] |= 0x08; /* Secure Simple Pairing */
+ btdev->features[6] |= 0x10; /* Encapsulated PDU */
+ btdev->features[6] |= 0x20; /* Erroneous Data Reporting */
+ btdev->features[6] |= 0x40; /* Non-flushable Packet Boundary Flag */
+ btdev->features[7] |= 0x01; /* Link Supervision Timeout Event */
+ btdev->features[7] |= 0x02; /* Inquiry TX Power Level */
+ btdev->features[7] |= 0x80; /* Extended features */
+
+ btdev->acl_mtu = 192;
+ btdev->acl_max_pkt = 1;
+
+ btdev->country_code = 0x00;
+
+ get_bdaddr(id, btdev->bdaddr);
+
+ add_btdev(btdev);
+
+ return btdev;
+}
+
+void btdev_destroy(struct btdev *btdev)
+{
+ if (!btdev)
+ return;
+
+ del_btdev(btdev);
+
+ free(btdev);
+}
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+ void *user_data)
+{
+ if (!btdev)
+ return;
+
+ btdev->send_handler = handler;
+ btdev->send_data = user_data;
+}
+
+static void send_packet(struct btdev *btdev, const void *data, uint16_t len)
+{
+ if (!btdev->send_handler)
+ return;
+
+ btdev->send_handler(data, len, btdev->send_data);
+}
+
+static void send_event(struct btdev *btdev, uint8_t event,
+ const void *data, uint8_t len)
+{
+ struct bt_hci_evt_hdr *hdr;
+ uint16_t pkt_len;
+ void *pkt_data;
+
+ pkt_len = 1 + sizeof(*hdr) + len;
+
+ pkt_data = malloc(pkt_len);
+ if (!pkt_data)
+ return;
+
+ ((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+ hdr = pkt_data + 1;
+ hdr->evt = event;
+ hdr->plen = len;
+
+ if (len > 0)
+ memcpy(pkt_data + 1 + sizeof(*hdr), data, len);
+
+ send_packet(btdev, pkt_data, pkt_len);
+
+ free(pkt_data);
+}
+
+static void cmd_complete(struct btdev *btdev, uint16_t opcode,
+ const void *data, uint8_t len)
+{
+ struct bt_hci_evt_hdr *hdr;
+ struct bt_hci_evt_cmd_complete *cc;
+ uint16_t pkt_len;
+ void *pkt_data;
+
+ pkt_len = 1 + sizeof(*hdr) + sizeof(*cc) + len;
+
+ pkt_data = malloc(pkt_len);
+ if (!pkt_data)
+ return;
+
+ ((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+ hdr = pkt_data + 1;
+ hdr->evt = BT_HCI_EVT_CMD_COMPLETE;
+ hdr->plen = sizeof(*cc) + len;
+
+ cc = pkt_data + 1 + sizeof(*hdr);
+ cc->ncmd = 0x01;
+ cc->opcode = cpu_to_le16(opcode);
+
+ if (len > 0)
+ memcpy(pkt_data + 1 + sizeof(*hdr) + sizeof(*cc), data, len);
+
+ send_packet(btdev, pkt_data, pkt_len);
+
+ free(pkt_data);
+}
+
+static void cmd_status(struct btdev *btdev, uint8_t status, uint16_t opcode)
+{
+ struct bt_hci_evt_cmd_status cs;
+
+ cs.status = status;
+ cs.ncmd = 0x01;
+ cs.opcode = cpu_to_le16(opcode);
+
+ send_event(btdev, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs));
+}
+
+static void num_completed_packets(struct btdev *btdev)
+{
+ if (btdev->conn) {
+ struct bt_hci_evt_num_completed_packets ncp;
+
+ ncp.num_handles = 1;
+ ncp.handle = cpu_to_le16(42);
+ ncp.count = cpu_to_le16(1);
+
+ send_event(btdev, BT_HCI_EVT_NUM_COMPLETED_PACKETS,
+ &ncp, sizeof(ncp));
+ }
+}
+
+static void inquiry_complete(struct btdev *btdev, uint8_t status)
+{
+ struct bt_hci_evt_inquiry_complete ic;
+ int i;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (!btdev_list[i] || btdev_list[i] == btdev)
+ continue;
+
+ if (!(btdev_list[i]->scan_enable & 0x02))
+ continue;
+
+ if (btdev->inquiry_mode == 0x02 &&
+ btdev_list[i]->ext_inquiry_rsp[0]) {
+ struct bt_hci_evt_ext_inquiry_result ir;
+
+ ir.num_resp = 0x01;
+ memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+ memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+ ir.rssi = -60;
+ memcpy(ir.data, btdev_list[i]->ext_inquiry_rsp, 240);
+
+ send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
+ &ir, sizeof(ir));
+ continue;
+ }
+
+ if (btdev->inquiry_mode > 0x00) {
+ struct bt_hci_evt_inquiry_result_with_rssi ir;
+
+ ir.num_resp = 0x01;
+ memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+ memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+ ir.rssi = -60;
+
+ send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
+ &ir, sizeof(ir));
+ } else {
+ struct bt_hci_evt_inquiry_result ir;
+
+ ir.num_resp = 0x01;
+ memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+ memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+
+ send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT,
+ &ir, sizeof(ir));
+ }
+ }
+
+ ic.status = status;
+
+ send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+}
+
+static void conn_complete(struct btdev *btdev,
+ const uint8_t *bdaddr, uint8_t status)
+{
+ struct bt_hci_evt_conn_complete cc;
+
+ if (!status) {
+ struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+ btdev->conn = remote;
+ remote->conn = btdev;
+
+ cc.status = status;
+ memcpy(cc.bdaddr, btdev->bdaddr, 6);
+ cc.encr_mode = 0x00;
+
+ cc.handle = cpu_to_le16(42);
+ cc.link_type = 0x01;
+
+ send_event(remote, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+
+ cc.handle = cpu_to_le16(42);
+ cc.link_type = 0x01;
+ } else {
+ cc.handle = cpu_to_le16(0x0000);
+ cc.link_type = 0x01;
+ }
+
+ cc.status = status;
+ memcpy(cc.bdaddr, bdaddr, 6);
+ cc.encr_mode = 0x00;
+
+ send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+}
+
+static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
+{
+ struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+ if (remote) {
+ if (remote->scan_enable & 0x01) {
+ struct bt_hci_evt_conn_request cr;
+
+ memcpy(cr.bdaddr, btdev->bdaddr, 6);
+ memcpy(cr.dev_class, btdev->dev_class, 3);
+ cr.link_type = 0x01;
+
+ send_event(remote, BT_HCI_EVT_CONN_REQUEST,
+ &cr, sizeof(cr));
+ } else
+ conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
+ } else
+ conn_complete(btdev, bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+}
+
+static void disconnect_complete(struct btdev *btdev, uint16_t handle,
+ uint8_t reason)
+{
+ struct bt_hci_evt_disconnect_complete dc;
+ struct btdev *remote;
+
+ if (!btdev) {
+ dc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ dc.handle = cpu_to_le16(handle);
+ dc.reason = 0x00;
+
+ send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE,
+ &dc, sizeof(dc));
+ return;
+ }
+
+ dc.status = BT_HCI_ERR_SUCCESS;
+ dc.handle = cpu_to_le16(handle);
+ dc.reason = reason;
+
+ remote = btdev->conn;
+
+ btdev->conn = NULL;
+ remote->conn = NULL;
+
+ send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+ send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+}
+
+static void name_request_complete(struct btdev *btdev,
+ const uint8_t *bdaddr, uint8_t status)
+{
+ struct bt_hci_evt_remote_name_req_complete nc;
+
+ nc.status = status;
+ memcpy(nc.bdaddr, bdaddr, 6);
+ memset(nc.name, 0, 248);
+
+ if (!status) {
+ struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+ if (remote)
+ memcpy(nc.name, remote->name, 248);
+ else
+ nc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE,
+ &nc, sizeof(nc));
+}
+
+static void remote_features_complete(struct btdev *btdev, uint16_t handle)
+{
+ struct bt_hci_evt_remote_features_complete rfc;
+
+ if (btdev->conn) {
+ rfc.status = BT_HCI_ERR_SUCCESS;
+ rfc.handle = cpu_to_le16(handle);
+ memcpy(rfc.features, btdev->conn->features, 8);
+ } else {
+ rfc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ rfc.handle = cpu_to_le16(handle);
+ memset(rfc.features, 0, 8);
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_FEATURES_COMPLETE,
+ &rfc, sizeof(rfc));
+}
+
+static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle,
+ uint8_t page)
+{
+ struct bt_hci_evt_remote_ext_features_complete refc;
+
+ if (btdev->conn && page < 0x02) {
+ refc.handle = cpu_to_le16(handle);
+ refc.page = page;
+ refc.max_page = 0x01;
+
+ switch (page) {
+ case 0x00:
+ refc.status = BT_HCI_ERR_SUCCESS;
+ memcpy(refc.features, btdev->conn->features, 8);
+ break;
+ case 0x01:
+ refc.status = BT_HCI_ERR_SUCCESS;
+ memset(refc.features, 0, 8);
+ break;
+ default:
+ refc.status = BT_HCI_ERR_INVALID_PARAMETERS;
+ memset(refc.features, 0, 8);
+ break;
+ }
+ } else {
+ refc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ refc.handle = cpu_to_le16(handle);
+ refc.page = page;
+ refc.max_page = 0x01;
+ memset(refc.features, 0, 8);
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE,
+ &refc, sizeof(refc));
+}
+
+static void remote_version_complete(struct btdev *btdev, uint16_t handle)
+{
+ struct bt_hci_evt_remote_version_complete rvc;
+
+ if (btdev->conn) {
+ rvc.status = BT_HCI_ERR_SUCCESS;
+ rvc.handle = cpu_to_le16(handle);
+ rvc.lmp_ver = btdev->conn->version;
+ rvc.manufacturer = cpu_to_le16(btdev->conn->manufacturer);
+ rvc.lmp_subver = cpu_to_le16(btdev->conn->revision);
+ } else {
+ rvc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ rvc.handle = cpu_to_le16(handle);
+ rvc.lmp_ver = 0x00;
+ rvc.manufacturer = cpu_to_le16(0);
+ rvc.lmp_subver = cpu_to_le16(0);
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_VERSION_COMPLETE,
+ &rvc, sizeof(rvc));
+}
+
+static void process_cmd(struct btdev *btdev, const void *data, uint16_t len)
+{
+ const struct bt_hci_cmd_hdr *hdr = data;
+ const struct bt_hci_cmd_create_conn *cc;
+ const struct bt_hci_cmd_disconnect *dc;
+ const struct bt_hci_cmd_create_conn_cancel *ccc;
+ const struct bt_hci_cmd_accept_conn_request *acr;
+ const struct bt_hci_cmd_reject_conn_request *rcr;
+ const struct bt_hci_cmd_remote_name_request *rnr;
+ const struct bt_hci_cmd_remote_name_request_cancel *rnrc;
+ const struct bt_hci_cmd_read_remote_features *rrf;
+ const struct bt_hci_cmd_read_remote_ext_features *rref;
+ const struct bt_hci_cmd_read_remote_version *rrv;
+ const struct bt_hci_cmd_write_default_link_policy *wdlp;
+ const struct bt_hci_cmd_set_event_mask *sem;
+ const struct bt_hci_cmd_set_event_filter *sef;
+ const struct bt_hci_cmd_write_local_name *wln;
+ const struct bt_hci_cmd_write_conn_accept_timeout *wcat;
+ const struct bt_hci_cmd_write_page_timeout *wpt;
+ const struct bt_hci_cmd_write_scan_enable *wse;
+ const struct bt_hci_cmd_write_auth_enable *wae;
+ const struct bt_hci_cmd_write_class_of_dev *wcod;
+ const struct bt_hci_cmd_write_voice_setting *wvs;
+ const struct bt_hci_cmd_write_inquiry_mode *wim;
+ const struct bt_hci_cmd_write_afh_assess_mode *waam;
+ const struct bt_hci_cmd_write_ext_inquiry_rsp *weir;
+ const struct bt_hci_cmd_write_simple_pairing_mode *wspm;
+ const struct bt_hci_cmd_write_le_host_supported *wlhs;
+ const struct bt_hci_cmd_le_set_event_mask *lsem;
+ struct bt_hci_rsp_read_default_link_policy rdlp;
+ struct bt_hci_rsp_read_stored_link_key rslk;
+ struct bt_hci_rsp_write_stored_link_key wslk;
+ struct bt_hci_rsp_delete_stored_link_key dslk;
+ struct bt_hci_rsp_read_local_name rln;
+ struct bt_hci_rsp_read_conn_accept_timeout rcat;
+ struct bt_hci_rsp_read_page_timeout rpt;
+ struct bt_hci_rsp_read_scan_enable rse;
+ struct bt_hci_rsp_read_auth_enable rae;
+ struct bt_hci_rsp_read_class_of_dev rcod;
+ struct bt_hci_rsp_read_voice_setting rvs;
+ struct bt_hci_rsp_read_inquiry_mode rim;
+ struct bt_hci_rsp_read_afh_assess_mode raam;
+ struct bt_hci_rsp_read_ext_inquiry_rsp reir;
+ struct bt_hci_rsp_read_simple_pairing_mode rspm;
+ struct bt_hci_rsp_read_inquiry_rsp_tx_power rirtp;
+ struct bt_hci_rsp_read_le_host_supported rlhs;
+ struct bt_hci_rsp_read_local_version rlv;
+ struct bt_hci_rsp_read_local_commands rlc;
+ struct bt_hci_rsp_read_local_features rlf;
+ struct bt_hci_rsp_read_local_ext_features rlef;
+ struct bt_hci_rsp_read_buffer_size rbs;
+ struct bt_hci_rsp_read_country_code rcc;
+ struct bt_hci_rsp_read_bd_addr rba;
+ struct bt_hci_rsp_read_data_block_size rdbs;
+ struct bt_hci_rsp_le_read_buffer_size lrbs;
+ struct bt_hci_rsp_le_read_local_features lrlf;
+ struct bt_hci_rsp_le_read_supported_states lrss;
+ uint16_t opcode;
+ uint8_t status, page;
+
+ if (len < sizeof(*hdr))
+ return;
+
+ opcode = le16_to_cpu(hdr->opcode);
+
+ switch (opcode) {
+ case BT_HCI_CMD_INQUIRY:
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ inquiry_complete(btdev, BT_HCI_ERR_SUCCESS);
+ break;
+
+ case BT_HCI_CMD_INQUIRY_CANCEL:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_CREATE_CONN:
+ cc = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_request(btdev, cc->bdaddr);
+ break;
+
+ case BT_HCI_CMD_DISCONNECT:
+ dc = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ disconnect_complete(btdev, le16_to_cpu(dc->handle), dc->reason);
+ break;
+
+ case BT_HCI_CMD_CREATE_CONN_CANCEL:
+ ccc = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_complete(btdev, ccc->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+ break;
+
+ case BT_HCI_CMD_ACCEPT_CONN_REQUEST:
+ acr = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_complete(btdev, acr->bdaddr, BT_HCI_ERR_SUCCESS);
+ break;
+
+ case BT_HCI_CMD_REJECT_CONN_REQUEST:
+ rcr = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+ break;
+
+ case BT_HCI_CMD_REMOTE_NAME_REQUEST:
+ rnr = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ name_request_complete(btdev, rnr->bdaddr, BT_HCI_ERR_SUCCESS);
+ break;
+
+ case BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL:
+ rnrc = data + sizeof(*hdr);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ name_request_complete(btdev, rnrc->bdaddr,
+ BT_HCI_ERR_UNKNOWN_CONN_ID);
+ break;
+
+ case BT_HCI_CMD_READ_REMOTE_FEATURES:
+ rrf = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ remote_features_complete(btdev, le16_to_cpu(rrf->handle));
+ break;
+
+ case BT_HCI_CMD_READ_REMOTE_EXT_FEATURES:
+ rref = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ remote_ext_features_complete(btdev, le16_to_cpu(rref->handle),
+ rref->page);
+ break;
+
+ case BT_HCI_CMD_READ_REMOTE_VERSION:
+ rrv = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ remote_version_complete(btdev, le16_to_cpu(rrv->handle));
+ break;
+
+ case BT_HCI_CMD_READ_DEFAULT_LINK_POLICY:
+ rdlp.status = BT_HCI_ERR_SUCCESS;
+ rdlp.policy = cpu_to_le16(btdev->default_link_policy);
+ cmd_complete(btdev, opcode, &rdlp, sizeof(rdlp));
+ break;
+
+ case BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY:
+ wdlp = data + sizeof(*hdr);
+ btdev->default_link_policy = le16_to_cpu(wdlp->policy);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_SET_EVENT_MASK:
+ sem = data + sizeof(*hdr);
+ memcpy(btdev->event_mask, sem->mask, 8);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_RESET:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_SET_EVENT_FILTER:
+ sef = data + sizeof(*hdr);
+ btdev->event_filter = sef->type;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_STORED_LINK_KEY:
+ rslk.status = BT_HCI_ERR_SUCCESS;
+ rslk.max_num_keys = cpu_to_le16(0);
+ rslk.num_keys = cpu_to_le16(0);
+ cmd_complete(btdev, opcode, &rslk, sizeof(rslk));
+ break;
+
+ case BT_HCI_CMD_WRITE_STORED_LINK_KEY:
+ wslk.status = BT_HCI_ERR_SUCCESS;
+ wslk.num_keys = 0;
+ cmd_complete(btdev, opcode, &wslk, sizeof(wslk));
+ break;
+
+ case BT_HCI_CMD_DELETE_STORED_LINK_KEY:
+ dslk.status = BT_HCI_ERR_SUCCESS;
+ dslk.num_keys = cpu_to_le16(0);
+ cmd_complete(btdev, opcode, &dslk, sizeof(dslk));
+ break;
+
+ case BT_HCI_CMD_WRITE_LOCAL_NAME:
+ wln = data + sizeof(*hdr);
+ memcpy(btdev->name, wln->name, 248);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_NAME:
+ rln.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rln.name, btdev->name, 248);
+ cmd_complete(btdev, opcode, &rln, sizeof(rln));
+ break;
+
+ case BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT:
+ rcat.status = BT_HCI_ERR_SUCCESS;
+ rcat.timeout = cpu_to_le16(btdev->conn_accept_timeout);
+ cmd_complete(btdev, opcode, &rcat, sizeof(rcat));
+ break;
+
+ case BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT:
+ wcat = data + sizeof(*hdr);
+ btdev->conn_accept_timeout = le16_to_cpu(wcat->timeout);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_PAGE_TIMEOUT:
+ rpt.status = BT_HCI_ERR_SUCCESS;
+ rpt.timeout = cpu_to_le16(btdev->page_timeout);
+ cmd_complete(btdev, opcode, &rpt, sizeof(rpt));
+ break;
+
+ case BT_HCI_CMD_WRITE_PAGE_TIMEOUT:
+ wpt = data + sizeof(*hdr);
+ btdev->page_timeout = le16_to_cpu(wpt->timeout);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_SCAN_ENABLE:
+ rse.status = BT_HCI_ERR_SUCCESS;
+ rse.enable = btdev->scan_enable;
+ cmd_complete(btdev, opcode, &rse, sizeof(rse));
+ break;
+
+ case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+ wse = data + sizeof(*hdr);
+ btdev->scan_enable = wse->enable;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_AUTH_ENABLE:
+ rae.status = BT_HCI_ERR_SUCCESS;
+ rae.enable = btdev->auth_enable;
+ cmd_complete(btdev, opcode, &rae, sizeof(rae));
+ break;
+
+ case BT_HCI_CMD_WRITE_AUTH_ENABLE:
+ wae = data + sizeof(*hdr);
+ btdev->auth_enable = wae->enable;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_CLASS_OF_DEV:
+ rcod.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rcod.dev_class, btdev->dev_class, 3);
+ cmd_complete(btdev, opcode, &rcod, sizeof(rcod));
+ break;
+
+ case BT_HCI_CMD_WRITE_CLASS_OF_DEV:
+ wcod = data + sizeof(*hdr);
+ memcpy(btdev->dev_class, wcod->dev_class, 3);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_VOICE_SETTING:
+ rvs.status = BT_HCI_ERR_SUCCESS;
+ rvs.setting = cpu_to_le16(btdev->voice_setting);
+ cmd_complete(btdev, opcode, &rvs, sizeof(rvs));
+ break;
+
+ case BT_HCI_CMD_WRITE_VOICE_SETTING:
+ wvs = data + sizeof(*hdr);
+ btdev->voice_setting = le16_to_cpu(wvs->setting);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_INQUIRY_MODE:
+ rim.status = BT_HCI_ERR_SUCCESS;
+ rim.mode = btdev->inquiry_mode;
+ cmd_complete(btdev, opcode, &rim, sizeof(rim));
+ break;
+
+ case BT_HCI_CMD_WRITE_INQUIRY_MODE:
+ wim = data + sizeof(*hdr);
+ btdev->inquiry_mode = wim->mode;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_AFH_ASSESS_MODE:
+ raam.status = BT_HCI_ERR_SUCCESS;
+ raam.mode = btdev->afh_assess_mode;
+ cmd_complete(btdev, opcode, &raam, sizeof(raam));
+ break;
+
+ case BT_HCI_CMD_WRITE_AFH_ASSESS_MODE:
+ waam = data + sizeof(*hdr);
+ btdev->afh_assess_mode = waam->mode;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_EXT_INQUIRY_RSP:
+ reir.status = BT_HCI_ERR_SUCCESS;
+ reir.fec = btdev->ext_inquiry_fec;
+ memcpy(reir.data, btdev->ext_inquiry_rsp, 240);
+ cmd_complete(btdev, opcode, &reir, sizeof(reir));
+ break;
+
+ case BT_HCI_CMD_WRITE_EXT_INQUIRY_RSP:
+ weir = data + sizeof(*hdr);
+ btdev->ext_inquiry_fec = weir->fec;
+ memcpy(btdev->ext_inquiry_rsp, weir->data, 240);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE:
+ rspm.status = BT_HCI_ERR_SUCCESS;
+ rspm.mode = btdev->simple_pairing_mode;
+ cmd_complete(btdev, opcode, &rspm, sizeof(rspm));
+ break;
+
+ case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+ wspm = data + sizeof(*hdr);
+ btdev->simple_pairing_mode = wspm->mode;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_INQUIRY_RSP_TX_POWER:
+ rirtp.status = BT_HCI_ERR_SUCCESS;
+ rirtp.level = 0;
+ cmd_complete(btdev, opcode, &rirtp, sizeof(rirtp));
+ break;
+
+ case BT_HCI_CMD_READ_LE_HOST_SUPPORTED:
+ rlhs.status = BT_HCI_ERR_SUCCESS;
+ rlhs.supported = btdev->le_supported;
+ rlhs.simultaneous = btdev->le_simultaneous;
+ cmd_complete(btdev, opcode, &rlhs, sizeof(rlhs));
+ break;
+
+ case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
+ wlhs = data + sizeof(*hdr);
+ btdev->le_supported = wlhs->supported;
+ btdev->le_simultaneous = wlhs->simultaneous;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_VERSION:
+ rlv.status = BT_HCI_ERR_SUCCESS;
+ rlv.hci_ver = btdev->version;
+ rlv.hci_rev = cpu_to_le16(btdev->revision);
+ rlv.lmp_ver = btdev->version;
+ rlv.manufacturer = cpu_to_le16(btdev->manufacturer);
+ rlv.lmp_subver = cpu_to_le16(btdev->revision);
+ cmd_complete(btdev, opcode, &rlv, sizeof(rlv));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_COMMANDS:
+ rlc.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rlc.commands, btdev->commands, 64);
+ cmd_complete(btdev, opcode, &rlc, sizeof(rlc));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_FEATURES:
+ rlf.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rlf.features, btdev->features, 8);
+ cmd_complete(btdev, opcode, &rlf, sizeof(rlf));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_EXT_FEATURES:
+ page = ((const uint8_t *) data)[sizeof(*hdr)];
+ switch (page) {
+ case 0x00:
+ rlef.status = BT_HCI_ERR_SUCCESS;
+ rlef.page = 0x00;
+ rlef.max_page = 0x01;
+ memcpy(rlef.features, btdev->features, 8);
+ break;
+ case 0x01:
+ rlef.status = BT_HCI_ERR_SUCCESS;
+ rlef.page = 0x01;
+ rlef.max_page = 0x01;
+ memset(rlef.features, 0, 8);
+ if (btdev->simple_pairing_mode)
+ rlef.features[0] |= 0x01;
+ if (btdev->le_supported)
+ rlef.features[0] |= 0x02;
+ if (btdev->le_simultaneous)
+ rlef.features[0] |= 0x04;
+ break;
+ default:
+ rlef.status = BT_HCI_ERR_INVALID_PARAMETERS;
+ rlef.page = page;
+ rlef.max_page = 0x01;
+ memset(rlef.features, 0, 8);
+ break;
+ }
+ cmd_complete(btdev, opcode, &rlef, sizeof(rlef));
+ break;
+
+ case BT_HCI_CMD_READ_BUFFER_SIZE:
+ rbs.status = BT_HCI_ERR_SUCCESS;
+ rbs.acl_mtu = cpu_to_le16(btdev->acl_mtu);
+ rbs.sco_mtu = 0;
+ rbs.acl_max_pkt = cpu_to_le16(btdev->acl_max_pkt);
+ rbs.sco_max_pkt = cpu_to_le16(0);
+ cmd_complete(btdev, opcode, &rbs, sizeof(rbs));
+ break;
+
+ case BT_HCI_CMD_READ_COUNTRY_CODE:
+ rcc.status = BT_HCI_ERR_SUCCESS;
+ rcc.code = btdev->country_code;
+ cmd_complete(btdev, opcode, &rcc, sizeof(rcc));
+ break;
+
+ case BT_HCI_CMD_READ_BD_ADDR:
+ rba.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rba.bdaddr, btdev->bdaddr, 6);
+ cmd_complete(btdev, opcode, &rba, sizeof(rba));
+ break;
+
+ case BT_HCI_CMD_READ_DATA_BLOCK_SIZE:
+ rdbs.status = BT_HCI_ERR_SUCCESS;
+ rdbs.max_acl_len = cpu_to_le16(btdev->acl_mtu);
+ rdbs.block_len = cpu_to_le16(btdev->acl_mtu);
+ rdbs.num_blocks = cpu_to_le16(btdev->acl_max_pkt);
+ cmd_complete(btdev, opcode, &rdbs, sizeof(rdbs));
+ break;
+
+ case BT_HCI_CMD_LE_SET_EVENT_MASK:
+ lsem = data + sizeof(*hdr);
+ memcpy(btdev->le_event_mask, lsem->mask, 8);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_LE_READ_BUFFER_SIZE:
+ lrbs.status = BT_HCI_ERR_SUCCESS;
+ lrbs.le_mtu = cpu_to_le16(btdev->acl_mtu);
+ lrbs.le_max_pkt = btdev->acl_max_pkt;
+ cmd_complete(btdev, opcode, &lrbs, sizeof(lrbs));
+ break;
+
+ case BT_HCI_CMD_LE_READ_LOCAL_FEATURES:
+ lrlf.status = BT_HCI_ERR_SUCCESS;
+ memcpy(lrlf.features, btdev->le_features, 8);
+ cmd_complete(btdev, opcode, &lrlf, sizeof(lrlf));
+ break;
+
+ case BT_HCI_CMD_LE_SET_SCAN_PARAMETERS:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_LE_READ_SUPPORTED_STATES:
+ lrss.status = BT_HCI_ERR_SUCCESS;
+ memcpy(lrss.states, btdev->le_states, 8);
+ cmd_complete(btdev, opcode, &lrss, sizeof(lrss));
+ break;
+
+ default:
+ printf("Unsupported command 0x%4.4x\n", opcode);
+ hexdump(data, len);
+ cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+ break;
+ }
+}
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len)
+{
+ uint8_t pkt_type;
+
+ if (!btdev)
+ return;
+
+ if (len < 1)
+ return;
+
+ pkt_type = ((const uint8_t *) data)[0];
+
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ process_cmd(btdev, data + 1, len - 1);
+ break;
+ case BT_H4_ACL_PKT:
+ if (btdev->conn)
+ send_packet(btdev->conn, data, len);
+ num_completed_packets(btdev);
+ break;
+ default:
+ printf("Unsupported packet 0x%2.2x\n", pkt_type);
+ break;
+ }
+}
diff --git a/emulator/btdev.h b/emulator/btdev.h
new file mode 100644
index 00000000..7b211a2c
--- /dev/null
+++ b/emulator/btdev.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <stdint.h>
+
+typedef void (*btdev_send_func) (const void *data, uint16_t len,
+ void *user_data);
+
+struct btdev;
+
+struct btdev *btdev_create(uint16_t id);
+void btdev_destroy(struct btdev *btdev);
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+ void *user_data);
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len);
diff --git a/emulator/main.c b/emulator/main.c
new file mode 100644
index 00000000..125460d5
--- /dev/null
+++ b/emulator/main.c
@@ -0,0 +1,73 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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 "mainloop.h"
+#include "server.h"
+#include "vhci.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct vhci *vhci;
+ struct server *server;
+ sigset_t mask;
+
+ mainloop_init();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ vhci = vhci_open(VHCI_TYPE_BREDR, 0x23);
+ if (!vhci) {
+ fprintf(stderr, "Failed to open Virtual HCI device\n");
+ return 1;
+ }
+
+ server = server_open_unix("/tmp/bt-server-bredr", 0x42);
+ if (!server) {
+ fprintf(stderr, "Failed to open server channel\n");
+ vhci_close(vhci);
+ return 1;
+ }
+
+ return mainloop_run();
+}
diff --git a/emulator/server.c b/emulator/server.c
new file mode 100644
index 00000000..1ff9904e
--- /dev/null
+++ b/emulator/server.c
@@ -0,0 +1,288 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "mainloop.h"
+#include "btdev.h"
+#include "server.h"
+
+struct server {
+ uint16_t id;
+ int fd;
+};
+
+struct client {
+ int fd;
+ struct btdev *btdev;
+ uint8_t *pkt_data;
+ uint8_t pkt_type;
+ uint16_t pkt_expect;
+ uint16_t pkt_len;
+ uint16_t pkt_offset;
+};
+
+static void server_destroy(void *user_data)
+{
+ struct server *server = user_data;
+
+ close(server->fd);
+
+ free(server);
+}
+
+static void client_destroy(void *user_data)
+{
+ struct client *client = user_data;
+
+ btdev_destroy(client->btdev);
+
+ close(client->fd);
+
+ free(client);
+}
+
+static void client_write_callback(const void *data, uint16_t len,
+ void *user_data)
+{
+ struct client *client = user_data;
+ ssize_t written;
+
+ written = send(client->fd, data, len, MSG_DONTWAIT);
+ if (written < 0)
+ return;
+}
+
+static void client_read_callback(int fd, uint32_t events, void *user_data)
+{
+ struct client *client = user_data;
+ static uint8_t buf[4096];
+ uint8_t *ptr = buf;
+ ssize_t len;
+ uint16_t count;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+again:
+ len = recv(fd, buf + client->pkt_offset,
+ sizeof(buf) - client->pkt_offset, MSG_DONTWAIT);
+ if (len < 0) {
+ if (errno == EAGAIN)
+ goto again;
+ return;
+ }
+
+ count = client->pkt_offset + len;
+
+ while (count > 0) {
+ hci_command_hdr *cmd_hdr;
+
+ if (!client->pkt_data) {
+ client->pkt_type = ptr[0];
+
+ switch (client->pkt_type) {
+ case HCI_COMMAND_PKT:
+ if (count < HCI_COMMAND_HDR_SIZE + 1) {
+ client->pkt_offset += len;
+ return;
+ }
+ cmd_hdr = (hci_command_hdr *) (ptr + 1);
+ client->pkt_expect = HCI_COMMAND_HDR_SIZE +
+ cmd_hdr->plen + 1;
+ client->pkt_data = malloc(client->pkt_expect);
+ client->pkt_len = 0;
+ break;
+ default:
+ printf("packet error\n");
+ return;
+ }
+
+ client->pkt_offset = 0;
+ }
+
+ if (count >= client->pkt_expect) {
+ memcpy(client->pkt_data + client->pkt_len,
+ ptr, client->pkt_expect);
+ ptr += client->pkt_expect;
+ count -= client->pkt_expect;
+
+ btdev_receive_h4(client->btdev, client->pkt_data,
+ client->pkt_len + client->pkt_expect);
+
+ free(client->pkt_data);
+ client->pkt_data = NULL;
+ } else {
+ memcpy(client->pkt_data + client->pkt_len, ptr, count);
+ client->pkt_len += count;
+ client->pkt_expect -= count;
+ count = 0;
+ }
+ }
+}
+
+static int accept_client(int fd)
+{
+ struct sockaddr_un addr;
+ socklen_t len;
+ int nfd;
+
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+
+ if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) {
+ perror("Failed to get socket name");
+ return -1;
+ }
+
+ printf("Request for %s\n", addr.sun_path);
+
+ nfd = accept(fd, (struct sockaddr *) &addr, &len);
+ if (nfd < 0) {
+ perror("Failed to accept client socket");
+ return -1;
+ }
+
+ return nfd;
+}
+
+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);
+ }
+}
+
+static int open_server(const char *path)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ unlink(path);
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("Failed to open server socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind server socket");
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 5) < 0) {
+ perror("Failed to listen server socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+struct server *server_open_unix(const char *path, uint16_t id)
+{
+ struct server *server;
+
+ server = malloc(sizeof(*server));
+ if (!server)
+ return NULL;
+
+ memset(server, 0, sizeof(*server));
+ server->id = id;
+
+ server->fd = open_server(path);
+ if (server->fd < 0) {
+ free(server);
+ return NULL;
+ }
+
+ if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback,
+ server, server_destroy) < 0) {
+ close(server->fd);
+ free(server);
+ return NULL;
+ }
+
+ return server;
+}
+
+void server_close(struct server *server)
+{
+ if (!server)
+ return;
+
+ mainloop_remove_fd(server->fd);
+}
diff --git a/emulator/server.h b/emulator/server.h
new file mode 100644
index 00000000..836db5f9
--- /dev/null
+++ b/emulator/server.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <stdint.h>
+
+struct server;
+
+struct server *server_open_unix(const char *path, uint16_t id);
+void server_close(struct server *server);
diff --git a/emulator/vhci.c b/emulator/vhci.c
new file mode 100644
index 00000000..940e562b
--- /dev/null
+++ b/emulator/vhci.c
@@ -0,0 +1,133 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mainloop.h"
+#include "btdev.h"
+#include "vhci.h"
+
+struct vhci {
+ enum vhci_type type;
+ int fd;
+ struct btdev *btdev;
+};
+
+static void vhci_destroy(void *user_data)
+{
+ struct vhci *vhci = user_data;
+
+ btdev_destroy(vhci->btdev);
+
+ close(vhci->fd);
+
+ free(vhci);
+}
+
+static void vhci_write_callback(const void *data, uint16_t len, void *user_data)
+{
+ struct vhci *vhci = user_data;
+ ssize_t written;
+
+ written = write(vhci->fd, data, len);
+ if (written < 0)
+ return;
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+ struct vhci *vhci = user_data;
+ unsigned char buf[4096];
+ ssize_t len;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+ len = read(vhci->fd, buf, sizeof(buf));
+ if (len < 0)
+ return;
+
+ btdev_receive_h4(vhci->btdev, buf, len);
+}
+
+struct vhci *vhci_open(enum vhci_type type, uint16_t id)
+{
+ struct vhci *vhci;
+
+ switch (type) {
+ case VHCI_TYPE_BREDR:
+ break;
+ case VHCI_TYPE_AMP:
+ return NULL;
+ }
+
+ vhci = malloc(sizeof(*vhci));
+ if (!vhci)
+ return NULL;
+
+ memset(vhci, 0, sizeof(*vhci));
+ vhci->type = type;
+
+ vhci->fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+ if (vhci->fd < 0) {
+ free(vhci);
+ return NULL;
+ }
+
+ vhci->btdev = btdev_create(id);
+ if (!vhci->btdev) {
+ close(vhci->fd);
+ free(vhci);
+ return NULL;
+ }
+
+ btdev_set_send_handler(vhci->btdev, vhci_write_callback, vhci);
+
+ if (mainloop_add_fd(vhci->fd, EPOLLIN, vhci_read_callback,
+ vhci, vhci_destroy) < 0) {
+ btdev_destroy(vhci->btdev);
+ close(vhci->fd);
+ free(vhci);
+ return NULL;
+ }
+
+ return vhci;
+}
+
+void vhci_close(struct vhci *vhci)
+{
+ if (!vhci)
+ return;
+
+ mainloop_remove_fd(vhci->fd);
+}
diff --git a/emulator/vhci.h b/emulator/vhci.h
new file mode 100644
index 00000000..4abb1830
--- /dev/null
+++ b/emulator/vhci.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <stdint.h>
+
+enum vhci_type {
+ VHCI_TYPE_BREDR = 0,
+ VHCI_TYPE_AMP = 1,
+};
+
+struct vhci;
+
+struct vhci *vhci_open(enum vhci_type type, uint16_t id);
+void vhci_close(struct vhci *vhci);