DF-5738 / hci_stub.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | /* * hci_stub.c -- minimal L2CAP/SCO/socket stubs for the hci_link_harness KLD. * * The production netbt code (sys/netbt/hci_event.c, hci_link.c, hci_misc.c, * hci_unit.c, bt_input.c) is compiled VERBATIM into this module. Everything * it needs from the layers we do not compile (l2cap_*.c, sco_upper.c, * sco_socket.c, hci_socket.c, bt_proto.c) is stubbed here with the minimum * behaviour for the real link-engine code to run unmodified: * * - M_BLUETOOTH malloc type (lives in bt_proto.c normally) * - l2cap_pdu_pool / l2cap_req_pool vm_zones (bt_proto.c netbt_init) * - l2cap_active_list (l2cap_misc.c) * - l2cap_close/l2cap_start/l2cap_request_free/l2cap_recv_frame * - hci_mtap/hci_drop (hci_socket.c) -- no raw HCI sockets in the harness * * SCO support (needed for DF-5738: the SCO->ACL anchor reference taken by * production hci_sco_newconn(), sys/netbt/hci_link.c:810): * * - a LISTENING sco_pcb in the sco_pcb list, exactly what a userland * sco socket in listen(2) state looks like to hci_sco_newconn(); * - a fake btproto whose newconn op returns a fresh stub pcb (what * sco_socket.c:sco_newconn does), so the FULL production path * hci_event_con_req -> hci_sco_newconn -> hci_acl_open runs verbatim. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/systm.h> #include <sys/types.h> #include <net/if.h> #include <netbt/bluetooth.h> #include <netbt/hci.h> #include <netbt/l2cap.h> #include <netbt/sco.h> #include "stub.h" /* real malloc type lives in bt_proto.c which we do not compile in */ MALLOC_DEFINE(M_BLUETOOTH, "Bluetooth", "Bluetooth system memory"); /* zones normally created by netbt_init() in bt_proto.c */ vm_zone_t l2cap_pdu_pool; vm_zone_t l2cap_req_pool; /* channel list normally in l2cap_misc.c */ struct l2cap_channel_list l2cap_active_list = LIST_HEAD_INITIALIZER(l2cap_active_list); /* sco listener list normally in sco_upper.c */ struct sco_pcb_list sco_pcb = LIST_HEAD_INITIALIZER(sco_pcb); /* counters the harness uses to verify SCO pcb lifetime after the runs */ int stub_sco_newconn_count; int stub_sco_disconnected_count; int stub_sco_pcb_leaks; /* forward */ static void *stub_sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void stub_sco_disconnected(void *, int); static void stub_sco_connected(void *); static const struct btproto stub_sco_btproto = { NULL, /* connecting */ stub_sco_connected, /* connected */ stub_sco_disconnected, /* disconnected */ stub_sco_newconn, /* newconn */ NULL, /* complete */ NULL, /* linkmode */ NULL, /* input */ }; /* * The listening pcb: an unprivileged userland program does * s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); listen(s, 1); * which lands in sco_listen() setting SP_LISTENING. hci_sco_newconn() * walks this list. */ static struct sco_pcb stub_listener; void stub_sco_init(void) { memset(&stub_listener, 0, sizeof(stub_listener)); stub_listener.sp_flags = SP_LISTENING; stub_listener.sp_proto = &stub_sco_btproto; LIST_INSERT_HEAD(&sco_pcb, &stub_listener, sp_next); } /* * fake upper-layer newconn: allocate a pcb like sco_socket.c does * (attach a new socket+pcb). hci_sco_newconn() fills in addresses, * anchors the ACL link and links it both ways. */ static void * stub_sco_newconn(void *upper, struct sockaddr_bt *laddr, struct sockaddr_bt *raddr) { struct sco_pcb *pcb; pcb = kmalloc(sizeof(*pcb), M_BLUETOOTH, M_NOWAIT | M_ZERO); if (pcb == NULL) return NULL; pcb->sp_proto = &stub_sco_btproto; LIST_INSERT_HEAD(&sco_pcb, pcb, sp_next); stub_sco_newconn_count++; return pcb; } static void stub_sco_connected(void *arg) { /* socket layer: soisconnected(); nothing to do in the harness */ } static void stub_sco_disconnected(void *arg, int err) { stub_sco_disconnected_count++; } int sco_detach(struct sco_pcb **handle) { struct sco_pcb *pcb = *handle; *handle = NULL; if (pcb == NULL) return EINVAL; /* production sco_detach(): free the pcb unless it is our listener */ if (pcb != &stub_listener) { LIST_REMOVE(pcb, sp_next); kfree(pcb, M_BLUETOOTH); } return 0; } void hci_mtap(struct mbuf *m, struct hci_unit *unit) { /* no raw HCI sockets attached in the harness */ } void hci_drop(void *arg) { /* no socket bookkeeping to drop */ } void l2cap_close(struct l2cap_channel *chan, int err) { if (chan->lc_lcid != L2CAP_NULL_CID) { LIST_REMOVE(chan, lc_ncid); chan->lc_lcid = L2CAP_NULL_CID; } } int l2cap_start(struct l2cap_channel *chan) { return 0; } void l2cap_request_free(struct l2cap_req *req) { kfree(req, M_BLUETOOTH); } void l2cap_recv_frame(struct mbuf *m, struct hci_link *link) { m_freem(m); } /* signalling senders used by hci_acl_linkmode() (l2cap_signal.c) */ int l2cap_send_connect_req(struct l2cap_channel *chan) { return 0; } int l2cap_send_connect_rsp(struct hci_link *link, uint8_t ident, uint16_t dcid, uint16_t scid, uint16_t result) { return 0; } int l2cap_send_config_req(struct l2cap_channel *chan) { return 0; } |