📄 hciemu.c
字号:
/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com> * Copyright (C) 2003-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 <ctype.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <stdint.h>#include <string.h>#include <signal.h>#include <getopt.h>#include <syslog.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/poll.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/resource.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <netdb.h>#include <glib.h>#if __BYTE_ORDER == __LITTLE_ENDIANstatic inline uint64_t ntoh64(uint64_t n){ uint64_t h; uint64_t tmp = ntohl(n & 0x00000000ffffffff); h = ntohl(n >> 32); h |= tmp << 32; return h;}#elif __BYTE_ORDER == __BIG_ENDIAN#define ntoh64(x) (x)#else#error "Unknown byte order"#endif#define hton64(x) ntoh64(x)#define GHCI_DEV "/dev/ghci"#define VHCI_DEV "/dev/vhci"#define VHCI_UDEV "/dev/hci_vhci"#define VHCI_MAX_CONN 12#define VHCI_ACL_MTU 192#define VHCI_ACL_MAX_PKT 8struct vhci_device { uint8_t features[8]; uint8_t name[248]; uint8_t dev_class[3]; uint8_t inq_mode; uint8_t eir_fec; uint8_t eir_data[240]; uint16_t acl_cnt; bdaddr_t bdaddr; int fd; int dd; GIOChannel *scan;};struct vhci_conn { bdaddr_t dest; uint16_t handle; GIOChannel *chan;};struct vhci_link_info { bdaddr_t bdaddr; uint8_t dev_class[3]; uint8_t link_type; uint8_t role;} __attribute__ ((packed));static struct vhci_device vdev;static struct vhci_conn *vconn[VHCI_MAX_CONN];struct btsnoop_hdr { uint8_t id[8]; /* Identification Pattern */ uint32_t version; /* Version Number = 1 */ uint32_t type; /* Datalink Type */} __attribute__ ((packed));#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))struct btsnoop_pkt { uint32_t size; /* Original Length */ uint32_t len; /* Included Length */ uint32_t flags; /* Packet Flags */ uint32_t drops; /* Cumulative Drops */ uint64_t ts; /* Timestamp microseconds */ uint8_t data[0]; /* Packet Data */} __attribute__ ((packed));#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };static GMainLoop *event_loop;static volatile sig_atomic_t __io_canceled;static inline void io_init(void){ __io_canceled = 0;}static inline void io_cancel(void){ __io_canceled = 1;}static void sig_term(int sig){ io_cancel(); g_main_loop_quit(event_loop);}static gboolean io_acl_data(GIOChannel *chan, GIOCondition cond, gpointer data);static gboolean io_conn_ind(GIOChannel *chan, GIOCondition cond, gpointer data);static gboolean io_hci_data(GIOChannel *chan, GIOCondition cond, gpointer data);static inline int read_n(int fd, void *buf, int len){ register int w, t = 0; while (!__io_canceled && len > 0) { if ((w = read(fd, buf, len)) < 0 ){ if( errno == EINTR || errno == EAGAIN ) continue; return -1; } if (!w) return 0; len -= w; buf += w; t += w; } return t;}/* Write exactly len bytes (Signal safe)*/static inline int write_n(int fd, void *buf, int len){ register int w, t = 0; while (!__io_canceled && len > 0) { if ((w = write(fd, buf, len)) < 0 ){ if( errno == EINTR || errno == EAGAIN ) continue; return -1; } if (!w) return 0; len -= w; buf += w; t += w; } return t;}static int create_snoop(char *file){ struct btsnoop_hdr hdr; int fd, len; fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) return fd; memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); hdr.version = htonl(1); hdr.type = htonl(1002); len = write(fd, &hdr, BTSNOOP_HDR_SIZE); if (len < 0) { close(fd); return -EIO; } if (len != BTSNOOP_HDR_SIZE) { close(fd); return -1; } return fd;}static int write_snoop(int fd, int type, int incoming, unsigned char *buf, int len){ struct btsnoop_pkt pkt; struct timeval tv; uint32_t size = len; uint64_t ts; int err; if (fd < 0) return -1; memset(&tv, 0, sizeof(tv)); gettimeofday(&tv, NULL); ts = (tv.tv_sec - 946684800ll) * 1000000ll + tv.tv_usec; pkt.size = htonl(size); pkt.len = pkt.size; pkt.flags = ntohl(incoming & 0x01); pkt.drops = htonl(0); pkt.ts = hton64(ts + 0x00E03AB44A676000ll); if (type == HCI_COMMAND_PKT || type == HCI_EVENT_PKT) pkt.flags |= ntohl(0x02); err = write(fd, &pkt, BTSNOOP_PKT_SIZE); err = write(fd, buf, size); return 0;}static struct vhci_conn *conn_get_by_bdaddr(bdaddr_t *ba){ register int i; for (i = 0; i < VHCI_MAX_CONN; i++) if (!bacmp(&vconn[i]->dest, ba)) return vconn[i]; return NULL;}static void command_status(uint16_t ogf, uint16_t ocf, uint8_t status){ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf; evt_cmd_status *cs; hci_event_hdr *he; /* Packet type */ *ptr++ = HCI_EVENT_PKT; /* Event header */ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE; he->evt = EVT_CMD_STATUS; he->plen = EVT_CMD_STATUS_SIZE; cs = (void *) ptr; ptr += EVT_CMD_STATUS_SIZE; cs->status = status; cs->ncmd = 1; cs->opcode = htobs(cmd_opcode_pack(ogf, ocf)); write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf); if (write(vdev.fd, buf, ptr - buf) < 0) syslog(LOG_ERR, "Can't send event: %s(%d)", strerror(errno), errno);}static void command_complete(uint16_t ogf, uint16_t ocf, int plen, void *data){ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf; evt_cmd_complete *cc; hci_event_hdr *he; /* Packet type */ *ptr++ = HCI_EVENT_PKT; /* Event header */ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE; he->evt = EVT_CMD_COMPLETE; he->plen = EVT_CMD_COMPLETE_SIZE + plen; cc = (void *) ptr; ptr += EVT_CMD_COMPLETE_SIZE; cc->ncmd = 1; cc->opcode = htobs(cmd_opcode_pack(ogf, ocf)); if (plen) { memcpy(ptr, data, plen); ptr += plen; } write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf); if (write(vdev.fd, buf, ptr - buf) < 0) syslog(LOG_ERR, "Can't send event: %s(%d)", strerror(errno), errno);}static void connect_request(struct vhci_conn *conn){ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf; evt_conn_request *cr; hci_event_hdr *he; /* Packet type */ *ptr++ = HCI_EVENT_PKT; /* Event header */ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE; he->evt = EVT_CONN_REQUEST; he->plen = EVT_CONN_REQUEST_SIZE; cr = (void *) ptr; ptr += EVT_CONN_REQUEST_SIZE; bacpy(&cr->bdaddr, &conn->dest); memset(&cr->dev_class, 0, sizeof(cr->dev_class)); cr->link_type = ACL_LINK; write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf); if (write(vdev.fd, buf, ptr - buf) < 0) syslog(LOG_ERR, "Can't send event: %s (%d)", strerror(errno), errno);}static void connect_complete(struct vhci_conn *conn){ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf; evt_conn_complete *cc; hci_event_hdr *he; /* Packet type */ *ptr++ = HCI_EVENT_PKT; /* Event header */ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE; he->evt = EVT_CONN_COMPLETE; he->plen = EVT_CONN_COMPLETE_SIZE; cc = (void *) ptr; ptr += EVT_CONN_COMPLETE_SIZE; bacpy(&cc->bdaddr, &conn->dest); cc->status = 0x00; cc->handle = htobs(conn->handle); cc->link_type = ACL_LINK; cc->encr_mode = 0x00; write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf); if (write(vdev.fd, buf, ptr - buf) < 0) syslog(LOG_ERR, "Can't send event: %s (%d)", strerror(errno), errno);}static void disconn_complete(struct vhci_conn *conn){ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf; evt_disconn_complete *dc; hci_event_hdr *he; /* Packet type */ *ptr++ = HCI_EVENT_PKT; /* Event header */ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE; he->evt = EVT_DISCONN_COMPLETE; he->plen = EVT_DISCONN_COMPLETE_SIZE; dc = (void *) ptr; ptr += EVT_DISCONN_COMPLETE_SIZE; dc->status = 0x00; dc->handle = htobs(conn->handle); dc->reason = 0x00; write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf); if (write(vdev.fd, buf, ptr - buf) < 0) syslog(LOG_ERR, "Can't send event: %s (%d)", strerror(errno), errno); vdev.acl_cnt = 0;}static void num_completed_pkts(struct vhci_conn *conn){ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf; evt_num_comp_pkts *np; hci_event_hdr *he; /* Packet type */ *ptr++ = HCI_EVENT_PKT; /* Event header */ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE; he->evt = EVT_NUM_COMP_PKTS; he->plen = EVT_NUM_COMP_PKTS_SIZE; np = (void *) ptr; ptr += EVT_NUM_COMP_PKTS_SIZE; np->num_hndl = 1; *((uint16_t *) ptr) = htobs(conn->handle); ptr += 2; *((uint16_t *) ptr) = htobs(vdev.acl_cnt); ptr += 2; write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf); if (write(vdev.fd, buf, ptr - buf) < 0) syslog(LOG_ERR, "Can't send event: %s (%d)", strerror(errno), errno);}static int scan_enable(uint8_t *data){ struct sockaddr_in sa; GIOChannel *sk_io; bdaddr_t ba; int sk, opt; if (!(*data & SCAN_PAGE)) { if (vdev.scan) { g_io_channel_close(vdev.scan); vdev.scan = NULL; } return 0; } if (vdev.scan) return 0; if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "Can't create socket: %s (%d)", strerror(errno), errno); return 1; } opt = 1; setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); baswap(&ba, &vdev.bdaddr); sa.sin_family = AF_INET; sa.sin_addr.s_addr = *(uint32_t *) &ba; sa.sin_port = *(uint16_t *) &ba.b[4]; if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) { syslog(LOG_ERR, "Can't bind socket: %s (%d)", strerror(errno), errno); goto failed; } if (listen(sk, 10)) { syslog(LOG_ERR, "Can't listen on socket: %s (%d)", strerror(errno), errno); goto failed; } sk_io = g_io_channel_unix_new(sk); g_io_add_watch(sk_io, G_IO_IN | G_IO_NVAL, io_conn_ind, NULL); vdev.scan = sk_io; return 0;failed: close(sk); return 1;}static void accept_connection(uint8_t *data){ accept_conn_req_cp *cp = (void *) data; struct vhci_conn *conn; if (!(conn = conn_get_by_bdaddr(&cp->bdaddr))) return; connect_complete(conn); g_io_add_watch(conn->chan, G_IO_IN | G_IO_NVAL | G_IO_HUP, io_acl_data, (gpointer) conn);}static void close_connection(struct vhci_conn *conn){ syslog(LOG_INFO, "Closing connection %s handle %d", batostr(&conn->dest), conn->handle); g_io_channel_close(conn->chan); g_io_channel_unref(conn->chan); vconn[conn->handle - 1] = NULL; disconn_complete(conn); free(conn);}static void disconnect(uint8_t *data){ disconnect_cp *cp = (void *) data; struct vhci_conn *conn; uint16_t handle; handle = btohs(cp->handle); if (handle - 1 > VHCI_MAX_CONN) return; if (!(conn = vconn[handle-1])) return; close_connection(conn);}static void create_connection(uint8_t *data){ create_conn_cp *cp = (void *) data; struct vhci_link_info info; struct vhci_conn *conn; struct sockaddr_in sa; int h, sk, opt; bdaddr_t ba; for (h = 0; h < VHCI_MAX_CONN; h++) if (!vconn[h]) goto do_connect; syslog(LOG_ERR, "Too many connections"); return;do_connect: if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "Can't create socket: %s (%d)", strerror(errno), errno); return; } opt = 1; setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); baswap(&ba, &vdev.bdaddr); sa.sin_family = AF_INET; sa.sin_addr.s_addr = INADDR_ANY; // *(uint32_t *) &ba; sa.sin_port = 0; // *(uint16_t *) &ba.b[4]; if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) { syslog(LOG_ERR, "Can't bind socket: %s (%d)", strerror(errno), errno); close(sk); return; } baswap(&ba, &cp->bdaddr); sa.sin_family = AF_INET; sa.sin_addr.s_addr = *(uint32_t *) &ba; sa.sin_port = *(uint16_t *) &ba.b[4]; if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) { syslog(LOG_ERR, "Can't connect: %s (%d)", strerror(errno), errno); close(sk); return; } /* Send info */ memset(&info, 0, sizeof(info)); bacpy(&info.bdaddr, &vdev.bdaddr); info.link_type = ACL_LINK; info.role = 1; write_n(sk, (void *) &info, sizeof(info)); if (!(conn = malloc(sizeof(*conn)))) { syslog(LOG_ERR, "Can't alloc new connection: %s (%d)", strerror(errno), errno); close(sk); return; } memcpy((uint8_t *) &ba, (uint8_t *) &sa.sin_addr, 4); memcpy((uint8_t *) &ba.b[4], (uint8_t *) &sa.sin_port, 2); baswap(&conn->dest, &ba); vconn[h] = conn; conn->handle = h + 1; conn->chan = g_io_channel_unix_new(sk); connect_complete(conn); g_io_add_watch(conn->chan, G_IO_IN | G_IO_NVAL | G_IO_HUP, io_acl_data, (gpointer) conn); return;}static void inline hci_link_control(uint16_t ocf, int plen, uint8_t *data){ uint8_t status; const uint16_t ogf = OGF_LINK_CTL; switch (ocf) { case OCF_CREATE_CONN: command_status(ogf, ocf, 0x00); create_connection(data); break; case OCF_ACCEPT_CONN_REQ: command_status(ogf, ocf, 0x00); accept_connection(data); break; case OCF_DISCONNECT: command_status(ogf, ocf, 0x00); disconnect(data); break; default: status = 0x01; command_complete(ogf, ocf, 1, &status); break; }}static void inline hci_link_policy(uint16_t ocf, int plen, uint8_t *data){ uint8_t status; const uint16_t ogf = OGF_INFO_PARAM; switch (ocf) { default: status = 0x01; command_complete(ogf, ocf, 1, &status); break; }}static void inline hci_host_control(uint16_t ocf, int plen, uint8_t *data){ read_local_name_rp ln; read_class_of_dev_rp cd; read_inquiry_mode_rp im; read_ext_inquiry_response_rp ir; uint8_t status; const uint16_t ogf = OGF_HOST_CTL; switch (ocf) { case OCF_RESET: status = 0x00; command_complete(ogf, ocf, 1, &status); break; case OCF_SET_EVENT_FLT: status = 0x00; command_complete(ogf, ocf, 1, &status); break; case OCF_CHANGE_LOCAL_NAME: status = 0x00; memcpy(vdev.name, data, sizeof(vdev.name)); command_complete(ogf, ocf, 1, &status); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -