Index: head/sys/netgraph/bluetooth/hci/ng_hci_evnt.c =================================================================== --- head/sys/netgraph/bluetooth/hci/ng_hci_evnt.c (revision 290037) +++ head/sys/netgraph/bluetooth/hci/ng_hci_evnt.c (revision 290038) @@ -1,1373 +1,1374 @@ /* * ng_hci_evnt.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_hci_evnt.c,v 1.6 2003/09/08 18:57:51 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** ****************************************************************************** ** HCI event processing module ****************************************************************************** ******************************************************************************/ /* * Event processing routines */ static int inquiry_result (ng_hci_unit_p, struct mbuf *); static int con_compl (ng_hci_unit_p, struct mbuf *); static int con_req (ng_hci_unit_p, struct mbuf *); static int discon_compl (ng_hci_unit_p, struct mbuf *); static int encryption_change (ng_hci_unit_p, struct mbuf *); static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *); static int qos_setup_compl (ng_hci_unit_p, struct mbuf *); static int hardware_error (ng_hci_unit_p, struct mbuf *); static int role_change (ng_hci_unit_p, struct mbuf *); static int num_compl_pkts (ng_hci_unit_p, struct mbuf *); static int mode_change (ng_hci_unit_p, struct mbuf *); static int data_buffer_overflow (ng_hci_unit_p, struct mbuf *); static int read_clock_offset_compl (ng_hci_unit_p, struct mbuf *); static int qos_violation (ng_hci_unit_p, struct mbuf *); static int page_scan_mode_change (ng_hci_unit_p, struct mbuf *); static int page_scan_rep_mode_change (ng_hci_unit_p, struct mbuf *); static int sync_con_queue (ng_hci_unit_p, ng_hci_unit_con_p, int); static int send_data_packets (ng_hci_unit_p, int, int); static int le_event (ng_hci_unit_p, struct mbuf *); /* * Process HCI event packet */ int ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_event_pkt_t *hdr = NULL; int error = 0; /* Get event packet header */ NG_HCI_M_PULLUP(event, sizeof(*hdr)); if (event == NULL) return (ENOBUFS); hdr = mtod(event, ng_hci_event_pkt_t *); NG_HCI_INFO( "%s: %s - got HCI event=%#x, length=%d\n", __func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length); /* Get rid of event header and process event */ m_adj(event, sizeof(*hdr)); switch (hdr->event) { case NG_HCI_EVENT_INQUIRY_COMPL: case NG_HCI_EVENT_RETURN_LINK_KEYS: case NG_HCI_EVENT_PIN_CODE_REQ: case NG_HCI_EVENT_LINK_KEY_REQ: case NG_HCI_EVENT_LINK_KEY_NOTIFICATION: case NG_HCI_EVENT_LOOPBACK_COMMAND: case NG_HCI_EVENT_AUTH_COMPL: case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL: case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL: case NG_HCI_EVENT_FLUSH_OCCUR: /* XXX Do we have to handle it? */ case NG_HCI_EVENT_MAX_SLOT_CHANGE: case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED: case NG_HCI_EVENT_BT_LOGO: case NG_HCI_EVENT_VENDOR: case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL: case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL: /* These do not need post processing */ NG_FREE_M(event); break; case NG_HCI_EVENT_LE: error = le_event(unit, event); break; case NG_HCI_EVENT_INQUIRY_RESULT: error = inquiry_result(unit, event); break; case NG_HCI_EVENT_CON_COMPL: error = con_compl(unit, event); break; case NG_HCI_EVENT_CON_REQ: error = con_req(unit, event); break; case NG_HCI_EVENT_DISCON_COMPL: error = discon_compl(unit, event); break; case NG_HCI_EVENT_ENCRYPTION_CHANGE: error = encryption_change(unit, event); break; case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL: error = read_remote_features_compl(unit, event); break; case NG_HCI_EVENT_QOS_SETUP_COMPL: error = qos_setup_compl(unit, event); break; case NG_HCI_EVENT_COMMAND_COMPL: error = ng_hci_process_command_complete(unit, event); break; case NG_HCI_EVENT_COMMAND_STATUS: error = ng_hci_process_command_status(unit, event); break; case NG_HCI_EVENT_HARDWARE_ERROR: error = hardware_error(unit, event); break; case NG_HCI_EVENT_ROLE_CHANGE: error = role_change(unit, event); break; case NG_HCI_EVENT_NUM_COMPL_PKTS: error = num_compl_pkts(unit, event); break; case NG_HCI_EVENT_MODE_CHANGE: error = mode_change(unit, event); break; case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW: error = data_buffer_overflow(unit, event); break; case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL: error = read_clock_offset_compl(unit, event); break; case NG_HCI_EVENT_QOS_VIOLATION: error = qos_violation(unit, event); break; case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE: error = page_scan_mode_change(unit, event); break; case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE: error = page_scan_rep_mode_change(unit, event); break; default: NG_FREE_M(event); error = EINVAL; break; } return (error); } /* ng_hci_process_event */ /* * Send ACL and/or SCO data to the unit driver */ void ng_hci_send_data(ng_hci_unit_p unit) { int count; /* Send ACL data */ NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count); NG_HCI_INFO( "%s: %s - sending ACL data packets, count=%d\n", __func__, NG_NODE_NAME(unit->node), count); if (count > 0) { count = send_data_packets(unit, NG_HCI_LINK_ACL, count); NG_HCI_STAT_ACL_SENT(unit->stat, count); NG_HCI_BUFF_ACL_USE(unit->buffer, count); } /* Send SCO data */ NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count); NG_HCI_INFO( "%s: %s - sending SCO data packets, count=%d\n", __func__, NG_NODE_NAME(unit->node), count); if (count > 0) { count = send_data_packets(unit, NG_HCI_LINK_SCO, count); NG_HCI_STAT_SCO_SENT(unit->stat, count); NG_HCI_BUFF_SCO_USE(unit->buffer, count); } } /* ng_hci_send_data */ /* * Send data packets to the lower layer. */ static int send_data_packets(ng_hci_unit_p unit, int link_type, int limit) { ng_hci_unit_con_p con = NULL, winner = NULL; int reallink_type; item_p item = NULL; int min_pending, total_sent, sent, error, v; for (total_sent = 0; limit > 0; ) { min_pending = 0x0fffffff; winner = NULL; /* * Find the connection that has has data to send * and the smallest number of pending packets */ LIST_FOREACH(con, &unit->con_list, next) { reallink_type = (con->link_type == NG_HCI_LINK_SCO)? NG_HCI_LINK_SCO: NG_HCI_LINK_ACL; if (reallink_type != link_type){ continue; } if (NG_BT_ITEMQ_LEN(&con->conq) == 0) continue; if (con->pending < min_pending) { winner = con; min_pending = con->pending; } } if (winner == NULL) break; /* * OK, we have a winner now send as much packets as we can * Count the number of packets we have sent and then sync * winner connection queue. */ for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) { NG_BT_ITEMQ_DEQUEUE(&winner->conq, item); if (item == NULL) break; NG_HCI_INFO( "%s: %s - sending data packet, handle=%d, len=%d\n", __func__, NG_NODE_NAME(unit->node), winner->con_handle, NGI_M(item)->m_pkthdr.len); /* Check if driver hook still there */ v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv)); if (!v || (unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { NG_HCI_ERR( "%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n", __func__, NG_NODE_NAME(unit->node), NG_HCI_HOOK_DRV, ((v)? "" : "not "), unit->state); NG_FREE_ITEM(item); error = ENOTCONN; } else { v = NGI_M(item)->m_pkthdr.len; /* Give packet to raw hook */ ng_hci_mtap(unit, NGI_M(item)); /* ... and forward item to the driver */ NG_FWD_ITEM_HOOK(error, item, unit->drv); } if (error != 0) { NG_HCI_ERR( "%s: %s - could not send data packet, handle=%d, error=%d\n", __func__, NG_NODE_NAME(unit->node), winner->con_handle, error); break; } winner->pending ++; NG_HCI_STAT_BYTES_SENT(unit->stat, v); } /* * Sync connection queue for the winner */ sync_con_queue(unit, winner, sent); } return (total_sent); } /* send_data_packets */ /* * Send flow control messages to the upper layer */ static int sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed) { hook_p hook = NULL; struct ng_mesg *msg = NULL; ng_hci_sync_con_queue_ep *state = NULL; int error; hook = (con->link_type != NG_HCI_LINK_SCO)? unit->acl : unit->sco; if (hook == NULL || NG_HOOK_NOT_VALID(hook)) return (ENOTCONN); NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE, sizeof(*state), M_NOWAIT); if (msg == NULL) return (ENOMEM); state = (ng_hci_sync_con_queue_ep *)(msg->data); state->con_handle = con->con_handle; state->completed = completed; NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0); return (error); } /* sync_con_queue */ /* le meta event */ /* Inquiry result event */ static int le_advertizing_report(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_le_advertising_report_ep *ep = NULL; ng_hci_neighbor_p n = NULL; bdaddr_t bdaddr; int error = 0; u_int8_t event_type; u_int8_t addr_type; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_le_advertising_report_ep *); m_adj(event, sizeof(*ep)); for (; ep->num_reports > 0; ep->num_reports --) { /* Get remote unit address */ NG_HCI_M_PULLUP(event, sizeof(u_int8_t)); event_type = *mtod(event, u_int8_t *); m_adj(event, sizeof(u_int8_t)); NG_HCI_M_PULLUP(event, sizeof(u_int8_t)); addr_type = *mtod(event, u_int8_t *); m_adj(event, sizeof(u_int8_t)); m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr); m_adj(event, sizeof(bdaddr)); /* Lookup entry in the cache */ n = ng_hci_get_neighbor(unit, &bdaddr, (addr_type) ? NG_HCI_LINK_LE_RANDOM:NG_HCI_LINK_LE_PUBLIC); if (n == NULL) { /* Create new entry */ n = ng_hci_new_neighbor(unit); if (n == NULL) { error = ENOMEM; break; } bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr)); n->addrtype = (addr_type)? NG_HCI_LINK_LE_RANDOM : NG_HCI_LINK_LE_PUBLIC; } else getmicrotime(&n->updated); #if 0 { /* * TODO: Make these information * Available from userland. */ u_int8_t length_data; char *rssi; NG_HCI_M_PULLUP(event, sizeof(u_int8_t)); length_data = *mtod(event, u_int8_t *); m_adj(event, sizeof(u_int8_t)); /*Advertizement data*/ NG_HCI_M_PULLUP(event, length_data); m_adj(event, length_data); NG_HCI_M_PULLUP(event, sizeof(char )); /*Get RSSI*/ rssi = mtod(event, char *); m_adj(event, sizeof(u_int8_t)); } #endif } NG_FREE_M(event); return (error); } /* inquiry_result */ static int le_connection_complete(ng_hci_unit_p unit, struct mbuf *event) { int error = 0; ng_hci_le_connection_complete_ep *ep = NULL; ng_hci_unit_con_p con = NULL; int link_type; uint8_t uclass[3] = {0,0,0};//dummy uclass NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_le_connection_complete_ep *); link_type = (ep->address_type)? NG_HCI_LINK_LE_RANDOM : NG_HCI_LINK_LE_PUBLIC; /* * Find the first connection descriptor that matches the following: * * 1) con->link_type == link_type * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE * 3) con->bdaddr == ep->address */ LIST_FOREACH(con, &unit->con_list, next) if (con->link_type == link_type && con->state == NG_HCI_CON_W4_CONN_COMPLETE && bcmp(&con->bdaddr, &ep->address, sizeof(bdaddr_t)) == 0) break; /* * Two possible cases: * * 1) We have found connection descriptor. That means upper layer has * requested this connection via LP_CON_REQ message. In this case * connection must have timeout set. If ng_hci_con_untimeout() fails * then timeout message already went into node's queue. In this case * ignore Connection_Complete event and let timeout deal with it. * * 2) We do not have connection descriptor. That means upper layer * nas not requested this connection , (less likely) we gave up * on this connection (timeout) or as node act as slave role. * The most likely scenario is that * we have received LE_Create_Connection command * from the RAW hook */ if (con == NULL) { if (ep->status != 0) goto out; con = ng_hci_new_con(unit, link_type); if (con == NULL) { error = ENOMEM; goto out; } con->state = NG_HCI_CON_W4_LP_CON_RSP; ng_hci_con_timeout(con); bcopy(&ep->address, &con->bdaddr, sizeof(con->bdaddr)); error = ng_hci_lp_con_ind(con, uclass); if (error != 0) { ng_hci_con_untimeout(con); ng_hci_free_con(con); } } else if ((error = ng_hci_con_untimeout(con)) != 0) goto out; /* * Update connection descriptor and send notification * to the upper layers. */ con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->handle)); con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE; ng_hci_lp_con_cfm(con, ep->status); /* Adjust connection state */ if (ep->status != 0) ng_hci_free_con(con); else { con->state = NG_HCI_CON_OPEN; /* * Change link policy for the ACL connections. Enable all * supported link modes. Enable Role switch as well if * device supports it. */ } out: NG_FREE_M(event); return (error); } static int le_connection_update(ng_hci_unit_p unit, struct mbuf *event) { int error = 0; /*TBD*/ NG_FREE_M(event); return error; } static int le_event(ng_hci_unit_p unit, struct mbuf *event) { int error = 0; ng_hci_le_ep *lep; NG_HCI_M_PULLUP(event, sizeof(*lep)); if(event ==NULL){ return ENOBUFS; } lep = mtod(event, ng_hci_le_ep *); m_adj(event, sizeof(*lep)); switch(lep->subevent_code){ case NG_HCI_LEEV_CON_COMPL: le_connection_complete(unit, event); break; case NG_HCI_LEEV_ADVREP: le_advertizing_report(unit, event); break; case NG_HCI_LEEV_CON_UPDATE_COMPL: le_connection_update(unit, event); break; case NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL: //TBD /*FALLTHROUGH*/ case NG_HCI_LEEV_LONG_TERM_KEY_REQUEST: //TBD /*FALLTHROUGH*/ default: NG_FREE_M(event); } return error; } /* Inquiry result event */ static int inquiry_result(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_inquiry_result_ep *ep = NULL; ng_hci_neighbor_p n = NULL; bdaddr_t bdaddr; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_inquiry_result_ep *); m_adj(event, sizeof(*ep)); for (; ep->num_responses > 0; ep->num_responses --) { /* Get remote unit address */ m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr); m_adj(event, sizeof(bdaddr)); /* Lookup entry in the cache */ n = ng_hci_get_neighbor(unit, &bdaddr, NG_HCI_LINK_ACL); if (n == NULL) { /* Create new entry */ n = ng_hci_new_neighbor(unit); if (n == NULL) { error = ENOMEM; break; } } else getmicrotime(&n->updated); bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr)); n->addrtype = NG_HCI_LINK_ACL; /* XXX call m_pullup here? */ n->page_scan_rep_mode = *mtod(event, u_int8_t *); m_adj(event, sizeof(u_int8_t)); /* page_scan_period_mode */ m_adj(event, sizeof(u_int8_t)); n->page_scan_mode = *mtod(event, u_int8_t *); m_adj(event, sizeof(u_int8_t)); /* class */ m_adj(event, NG_HCI_CLASS_SIZE); /* clock offset */ m_copydata(event, 0, sizeof(n->clock_offset), (caddr_t) &n->clock_offset); n->clock_offset = le16toh(n->clock_offset); } NG_FREE_M(event); return (error); } /* inquiry_result */ /* Connection complete event */ static int con_compl(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_con_compl_ep *ep = NULL; ng_hci_unit_con_p con = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_con_compl_ep *); /* * Find the first connection descriptor that matches the following: * * 1) con->link_type == ep->link_type * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE * 3) con->bdaddr == ep->bdaddr */ LIST_FOREACH(con, &unit->con_list, next) if (con->link_type == ep->link_type && con->state == NG_HCI_CON_W4_CONN_COMPLETE && bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) break; /* * Two possible cases: * * 1) We have found connection descriptor. That means upper layer has * requested this connection via LP_CON_REQ message. In this case * connection must have timeout set. If ng_hci_con_untimeout() fails * then timeout message already went into node's queue. In this case * ignore Connection_Complete event and let timeout deal with it. * * 2) We do not have connection descriptor. That means upper layer * nas not requested this connection or (less likely) we gave up * on this connection (timeout). The most likely scenario is that * we have received Create_Connection/Add_SCO_Connection command * from the RAW hook */ if (con == NULL) { if (ep->status != 0) goto out; con = ng_hci_new_con(unit, ep->link_type); if (con == NULL) { error = ENOMEM; goto out; } bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); } else if ((error = ng_hci_con_untimeout(con)) != 0) goto out; /* * Update connection descriptor and send notification * to the upper layers. */ con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con->encryption_mode = ep->encryption_mode; ng_hci_lp_con_cfm(con, ep->status); /* Adjust connection state */ if (ep->status != 0) ng_hci_free_con(con); else { con->state = NG_HCI_CON_OPEN; /* * Change link policy for the ACL connections. Enable all * supported link modes. Enable Role switch as well if * device supports it. */ if (ep->link_type == NG_HCI_LINK_ACL) { struct __link_policy { ng_hci_cmd_pkt_t hdr; ng_hci_write_link_policy_settings_cp cp; } __attribute__ ((packed)) *lp; struct mbuf *m; MGETHDR(m, M_NOWAIT, MT_DATA); if (m != NULL) { m->m_pkthdr.len = m->m_len = sizeof(*lp); lp = mtod(m, struct __link_policy *); lp->hdr.type = NG_HCI_CMD_PKT; lp->hdr.opcode = htole16(NG_HCI_OPCODE( NG_HCI_OGF_LINK_POLICY, NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS)); lp->hdr.length = sizeof(lp->cp); lp->cp.con_handle = ep->con_handle; lp->cp.settings = 0; if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch) lp->cp.settings |= 0x1; if (unit->features[0] & NG_HCI_LMP_HOLD_MODE) lp->cp.settings |= 0x2; if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE) lp->cp.settings |= 0x4; if (unit->features[1] & NG_HCI_LMP_PARK_MODE) lp->cp.settings |= 0x8; lp->cp.settings &= unit->link_policy_mask; lp->cp.settings = htole16(lp->cp.settings); NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) ng_hci_send_command(unit); } } } out: NG_FREE_M(event); return (error); } /* con_compl */ /* Connection request event */ static int con_req(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_con_req_ep *ep = NULL; ng_hci_unit_con_p con = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_con_req_ep *); /* * Find the first connection descriptor that matches the following: * * 1) con->link_type == ep->link_type * * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || * con->state == NG_HCI_CON_W4_CONN_COMPL * * 3) con->bdaddr == ep->bdaddr * * Possible cases: * * 1) We do not have connection descriptor. This is simple. Create * new fresh connection descriptor and send notification to the * appropriate upstream hook (based on link_type). * * 2) We found connection handle. This is more complicated. * * 2.1) ACL links * * Since only one ACL link can exist between each pair of * units then we have a race. Our upper layer has requested * an ACL connection to the remote unit, but we did not send * command yet. At the same time the remote unit has requested * an ACL connection from us. In this case we will ignore * Connection_Request event. This probably will cause connect * failure on both units. * * 2.2) SCO links * * The spec on page 45 says : * * "The master can support up to three SCO links to the same * slave or to different slaves. A slave can support up to * three SCO links from the same master, or two SCO links if * the links originate from different masters." * * The only problem is how to handle multiple SCO links between * matster and slave. For now we will assume that multiple SCO * links MUST be opened one after another. */ LIST_FOREACH(con, &unit->con_list, next) if (con->link_type == ep->link_type && (con->state == NG_HCI_CON_W4_LP_CON_RSP || con->state == NG_HCI_CON_W4_CONN_COMPLETE) && bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) break; if (con == NULL) { con = ng_hci_new_con(unit, ep->link_type); if (con != NULL) { bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); con->state = NG_HCI_CON_W4_LP_CON_RSP; ng_hci_con_timeout(con); error = ng_hci_lp_con_ind(con, ep->uclass); if (error != 0) { ng_hci_con_untimeout(con); ng_hci_free_con(con); } } else error = ENOMEM; } NG_FREE_M(event); return (error); } /* con_req */ /* Disconnect complete event */ static int discon_compl(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_discon_compl_ep *ep = NULL; ng_hci_unit_con_p con = NULL; int error = 0; u_int16_t h; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_discon_compl_ep *); /* * XXX * Do we have to send notification if ep->status != 0? * For now we will send notification for both ACL and SCO connections * ONLY if ep->status == 0. */ if (ep->status == 0) { h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con != NULL) { error = ng_hci_lp_discon_ind(con, ep->reason); /* Remove all timeouts (if any) */ if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) ng_hci_con_untimeout(con); ng_hci_free_con(con); } else { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; } } NG_FREE_M(event); return (error); } /* discon_compl */ /* Encryption change event */ static int encryption_change(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_encryption_change_ep *ep = NULL; ng_hci_unit_con_p con = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_encryption_change_ep *); if (ep->status == 0) { u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con == NULL) { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; - } else if (con->link_type != NG_HCI_LINK_ACL) { + } else if (con->link_type == NG_HCI_LINK_SCO) { NG_HCI_ALERT( "%s: %s - invalid link type=%d\n", __func__, NG_NODE_NAME(unit->node), con->link_type); error = EINVAL; } else if (ep->encryption_enable) /* XXX is that true? */ con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P; else con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE; + ng_hci_lp_enc_change(con, ep->encryption_enable); } else NG_HCI_ERR( "%s: %s - failed to change encryption mode, status=%d\n", __func__, NG_NODE_NAME(unit->node), ep->status); NG_FREE_M(event); return (error); } /* encryption_change */ /* Read remote feature complete event */ static int read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_read_remote_features_compl_ep *ep = NULL; ng_hci_unit_con_p con = NULL; ng_hci_neighbor_p n = NULL; u_int16_t h; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_read_remote_features_compl_ep *); if (ep->status == 0) { /* Check if we have this connection handle */ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con == NULL) { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; goto out; } /* Update cache entry */ n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL); if (n == NULL) { n = ng_hci_new_neighbor(unit); if (n == NULL) { error = ENOMEM; goto out; } bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); n->addrtype = NG_HCI_LINK_ACL; } else getmicrotime(&n->updated); bcopy(ep->features, n->features, sizeof(n->features)); } else NG_HCI_ERR( "%s: %s - failed to read remote unit features, status=%d\n", __func__, NG_NODE_NAME(unit->node), ep->status); out: NG_FREE_M(event); return (error); } /* read_remote_features_compl */ /* QoS setup complete event */ static int qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_qos_setup_compl_ep *ep = NULL; ng_hci_unit_con_p con = NULL; u_int16_t h; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_qos_setup_compl_ep *); /* Check if we have this connection handle */ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con == NULL) { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; } else if (con->link_type != NG_HCI_LINK_ACL) { NG_HCI_ALERT( "%s: %s - invalid link type=%d, handle=%d\n", __func__, NG_NODE_NAME(unit->node), con->link_type, h); error = EINVAL; } else if (con->state != NG_HCI_CON_OPEN) { NG_HCI_ALERT( "%s: %s - invalid connection state=%d, handle=%d\n", __func__, NG_NODE_NAME(unit->node), con->state, h); error = EINVAL; } else /* Notify upper layer */ error = ng_hci_lp_qos_cfm(con, ep->status); NG_FREE_M(event); return (error); } /* qos_setup_compl */ /* Hardware error event */ static int hardware_error(ng_hci_unit_p unit, struct mbuf *event) { NG_HCI_ALERT( "%s: %s - hardware error %#x\n", __func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *)); NG_FREE_M(event); return (0); } /* hardware_error */ /* Role change event */ static int role_change(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_role_change_ep *ep = NULL; ng_hci_unit_con_p con = NULL; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_role_change_ep *); if (ep->status == 0) { /* XXX shoud we also change "role" for SCO connections? */ con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); if (con != NULL) con->role = ep->role; else NG_HCI_ALERT( "%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n", __func__, NG_NODE_NAME(unit->node), ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); } else NG_HCI_ERR( "%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n", __func__, NG_NODE_NAME(unit->node), ep->status, ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); NG_FREE_M(event); return (0); } /* role_change */ /* Number of completed packets event */ static int num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_num_compl_pkts_ep *ep = NULL; ng_hci_unit_con_p con = NULL; u_int16_t h, p; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_num_compl_pkts_ep *); m_adj(event, sizeof(*ep)); for (; ep->num_con_handles > 0; ep->num_con_handles --) { /* Get connection handle */ m_copydata(event, 0, sizeof(h), (caddr_t) &h); m_adj(event, sizeof(h)); h = NG_HCI_CON_HANDLE(le16toh(h)); /* Get number of completed packets */ m_copydata(event, 0, sizeof(p), (caddr_t) &p); m_adj(event, sizeof(p)); p = le16toh(p); /* Check if we have this connection handle */ con = ng_hci_con_by_handle(unit, h); if (con != NULL) { con->pending -= p; if (con->pending < 0) { NG_HCI_WARN( "%s: %s - pending packet counter is out of sync! " \ "handle=%d, pending=%d, ncp=%d\n", __func__, NG_NODE_NAME(unit->node), con->con_handle, con->pending, p); con->pending = 0; } /* Update buffer descriptor */ if (con->link_type != NG_HCI_LINK_SCO) NG_HCI_BUFF_ACL_FREE(unit->buffer, p); else NG_HCI_BUFF_SCO_FREE(unit->buffer, p); } else NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); } NG_FREE_M(event); /* Send more data */ ng_hci_send_data(unit); return (0); } /* num_compl_pkts */ /* Mode change event */ static int mode_change(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_mode_change_ep *ep = NULL; ng_hci_unit_con_p con = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_mode_change_ep *); if (ep->status == 0) { u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con == NULL) { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; } else if (con->link_type != NG_HCI_LINK_ACL) { NG_HCI_ALERT( "%s: %s - invalid link type=%d\n", __func__, NG_NODE_NAME(unit->node), con->link_type); error = EINVAL; } else con->mode = ep->unit_mode; } else NG_HCI_ERR( "%s: %s - failed to change mode, status=%d\n", __func__, NG_NODE_NAME(unit->node), ep->status); NG_FREE_M(event); return (error); } /* mode_change */ /* Data buffer overflow event */ static int data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event) { NG_HCI_ALERT( "%s: %s - %s data buffer overflow\n", __func__, NG_NODE_NAME(unit->node), (*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO"); NG_FREE_M(event); return (0); } /* data_buffer_overflow */ /* Read clock offset complete event */ static int read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_read_clock_offset_compl_ep *ep = NULL; ng_hci_unit_con_p con = NULL; ng_hci_neighbor_p n = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_read_clock_offset_compl_ep *); if (ep->status == 0) { u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con == NULL) { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; goto out; } /* Update cache entry */ n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL); if (n == NULL) { n = ng_hci_new_neighbor(unit); if (n == NULL) { error = ENOMEM; goto out; } bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); n->addrtype = NG_HCI_LINK_ACL; } else getmicrotime(&n->updated); n->clock_offset = le16toh(ep->clock_offset); } else NG_HCI_ERR( "%s: %s - failed to Read Remote Clock Offset, status=%d\n", __func__, NG_NODE_NAME(unit->node), ep->status); out: NG_FREE_M(event); return (error); } /* read_clock_offset_compl */ /* QoS violation event */ static int qos_violation(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_qos_violation_ep *ep = NULL; ng_hci_unit_con_p con = NULL; u_int16_t h; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_qos_violation_ep *); /* Check if we have this connection handle */ h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); con = ng_hci_con_by_handle(unit, h); if (con == NULL) { NG_HCI_ALERT( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), h); error = ENOENT; } else if (con->link_type != NG_HCI_LINK_ACL) { NG_HCI_ALERT( "%s: %s - invalid link type=%d\n", __func__, NG_NODE_NAME(unit->node), con->link_type); error = EINVAL; } else if (con->state != NG_HCI_CON_OPEN) { NG_HCI_ALERT( "%s: %s - invalid connection state=%d, handle=%d\n", __func__, NG_NODE_NAME(unit->node), con->state, h); error = EINVAL; } else /* Notify upper layer */ error = ng_hci_lp_qos_ind(con); NG_FREE_M(event); return (error); } /* qos_violation */ /* Page scan mode change event */ static int page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_page_scan_mode_change_ep *ep = NULL; ng_hci_neighbor_p n = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_page_scan_mode_change_ep *); /* Update cache entry */ n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL); if (n == NULL) { n = ng_hci_new_neighbor(unit); if (n == NULL) { error = ENOMEM; goto out; } bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); n->addrtype = NG_HCI_LINK_ACL; } else getmicrotime(&n->updated); n->page_scan_mode = ep->page_scan_mode; out: NG_FREE_M(event); return (error); } /* page_scan_mode_change */ /* Page scan repetition mode change event */ static int page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event) { ng_hci_page_scan_rep_mode_change_ep *ep = NULL; ng_hci_neighbor_p n = NULL; int error = 0; NG_HCI_M_PULLUP(event, sizeof(*ep)); if (event == NULL) return (ENOBUFS); ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *); /* Update cache entry */ n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL); if (n == NULL) { n = ng_hci_new_neighbor(unit); if (n == NULL) { error = ENOMEM; goto out; } bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); n->addrtype = NG_HCI_LINK_ACL; } else getmicrotime(&n->updated); n->page_scan_rep_mode = ep->page_scan_rep_mode; out: NG_FREE_M(event); return (error); } /* page_scan_rep_mode_change */ Index: head/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c =================================================================== --- head/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision 290037) +++ head/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision 290038) @@ -1,1398 +1,1429 @@ /* * ng_hci_ulpi.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** ****************************************************************************** ** Upper Layer Protocol Interface module ****************************************************************************** ******************************************************************************/ static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p); static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p); static int ng_hci_lp_le_con_req (ng_hci_unit_p, item_p, hook_p, int); /* * Process LP_ConnectReq event from the upper layer protocol */ int ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) { int link_type; if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { NG_HCI_WARN( "%s: %s - unit is not ready, state=%#x\n", __func__, NG_NODE_NAME(unit->node), unit->state); NG_FREE_ITEM(item); return (ENXIO); } if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) { NG_HCI_ALERT( "%s: %s - invalid LP_ConnectReq message size=%d\n", __func__, NG_NODE_NAME(unit->node), NGI_MSG(item)->header.arglen); NG_FREE_ITEM(item); return (EMSGSIZE); } link_type = ((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type; switch(link_type){ case NG_HCI_LINK_ACL: return (ng_hci_lp_acl_con_req(unit, item, hook)); case NG_HCI_LINK_SCO: if (hook != unit->sco ) { NG_HCI_WARN( "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n", __func__, NG_NODE_NAME(unit->node), hook); NG_FREE_ITEM(item); return (EINVAL); } return (ng_hci_lp_sco_con_req(unit, item, hook)); case NG_HCI_LINK_LE_PUBLIC: case NG_HCI_LINK_LE_RANDOM: return (ng_hci_lp_le_con_req(unit, item, hook, link_type)); default: panic("%s: link_type invalid.", __func__); } return (EINVAL); } /* ng_hci_lp_con_req */ /* * Request to create new ACL connection */ static int ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) { struct acl_con_req { ng_hci_cmd_pkt_t hdr; ng_hci_create_con_cp cp; } __attribute__ ((packed)) *req = NULL; ng_hci_lp_con_req_ep *ep = NULL; ng_hci_unit_con_p con = NULL; ng_hci_neighbor_t *n = NULL; struct mbuf *m = NULL; int error = 0; ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); /* * Only one ACL connection can exist between each pair of units. * So try to find ACL connection descriptor (in any state) that * has requested remote BD_ADDR. * * Two cases: * * 1) We do not have connection to the remote unit. This is simple. * Just create new connection descriptor and send HCI command to * create new connection. * * 2) We do have connection descriptor. We need to check connection * state: * * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of * accepting connection from the remote unit. This is a race * condition. We will ignore this message. * * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already * requested connection or we just accepted it. In any case * all we need to do here is set appropriate notification bit * and wait. * * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back * and let upper layer know that we have connection already. */ con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); if (con != NULL) { switch (con->state) { case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ error = EALREADY; break; case NG_HCI_CON_W4_CONN_COMPLETE: if (hook == unit->acl) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; break; case NG_HCI_CON_OPEN: { struct ng_mesg *msg = NULL; ng_hci_lp_con_cfm_ep *cfm = NULL; if (hook != NULL && NG_HOOK_IS_VALID(hook)) { NGI_GET_MSG(item, msg); NG_FREE_MSG(msg); NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, sizeof(*cfm), M_NOWAIT); if (msg != NULL) { cfm = (ng_hci_lp_con_cfm_ep *)msg->data; cfm->status = 0; cfm->link_type = con->link_type; cfm->con_handle = con->con_handle; bcopy(&con->bdaddr, &cfm->bdaddr, sizeof(cfm->bdaddr)); /* * This will forward item back to * sender and set item to NULL */ _NGI_MSG(item) = msg; NG_FWD_ITEM_HOOK(error, item, hook); } else error = ENOMEM; } else NG_HCI_INFO( "%s: %s - Source hook is not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), hook); } break; default: panic( "%s: %s - Invalid connection state=%d\n", __func__, NG_NODE_NAME(unit->node), con->state); break; } goto out; } /* * If we got here then we need to create new ACL connection descriptor * and submit HCI command. First create new connection desriptor, set * bdaddr and notification flags. */ con = ng_hci_new_con(unit, NG_HCI_LINK_ACL); if (con == NULL) { error = ENOMEM; goto out; } bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); /* * Create HCI command */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { ng_hci_free_con(con); error = ENOBUFS; goto out; } m->m_pkthdr.len = m->m_len = sizeof(*req); req = mtod(m, struct acl_con_req *); req->hdr.type = NG_HCI_CMD_PKT; req->hdr.length = sizeof(req->cp); req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_CREATE_CON)); bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr)); req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); if (unit->features[0] & NG_HCI_LMP_3SLOT) req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3); if (unit->features[0] & NG_HCI_LMP_5SLOT) req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5); req->cp.pkt_type &= unit->packet_mask; if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1| NG_HCI_PKT_DM3|NG_HCI_PKT_DH3| NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0) req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); req->cp.pkt_type = htole16(req->cp.pkt_type); if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch) req->cp.accept_role_switch = 1; else req->cp.accept_role_switch = 0; /* * We may speed up connect by specifying valid parameters. * So check the neighbor cache. */ n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL); if (n == NULL) { req->cp.page_scan_rep_mode = 0; req->cp.page_scan_mode = 0; req->cp.clock_offset = 0; } else { req->cp.page_scan_rep_mode = n->page_scan_rep_mode; req->cp.page_scan_mode = n->page_scan_mode; req->cp.clock_offset = htole16(n->clock_offset); } /* * Adust connection state */ if (hook == unit->acl) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; con->state = NG_HCI_CON_W4_CONN_COMPLETE; ng_hci_con_timeout(con); /* * Queue and send HCI command */ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) error = ng_hci_send_command(unit); out: if (item != NULL) NG_FREE_ITEM(item); return (error); } /* ng_hci_lp_acl_con_req */ /* * Request to create new SCO connection */ static int ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) { struct sco_con_req { ng_hci_cmd_pkt_t hdr; ng_hci_add_sco_con_cp cp; } __attribute__ ((packed)) *req = NULL; ng_hci_lp_con_req_ep *ep = NULL; ng_hci_unit_con_p acl_con = NULL, sco_con = NULL; struct mbuf *m = NULL; int error = 0; ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); /* * SCO connection without ACL link * * If upper layer requests SCO connection and there is no open ACL * connection to the desired remote unit, we will reject the request. */ LIST_FOREACH(acl_con, &unit->con_list, next) if (acl_con->link_type == NG_HCI_LINK_ACL && acl_con->state == NG_HCI_CON_OPEN && bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) break; if (acl_con == NULL) { NG_HCI_INFO( "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n", __func__, NG_NODE_NAME(unit->node), ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); error = ENOENT; goto out; } /* * Multiple SCO connections can exist between the same pair of units. * We assume that multiple SCO connections have to be opened one after * another. * * Try to find SCO connection descriptor that matches the following: * * 1) sco_con->link_type == NG_HCI_LINK_SCO * * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE * * 3) sco_con->bdaddr == ep->bdaddr * * Two cases: * * 1) We do not have connection descriptor. This is simple. Just * create new connection and submit Add_SCO_Connection command. * * 2) We do have connection descriptor. We need to check the state. * * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting * connection from the remote unit. This is a race condition and * we will ignore the request. * * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested * connection or we just accepted it. */ LIST_FOREACH(sco_con, &unit->con_list, next) if (sco_con->link_type == NG_HCI_LINK_SCO && (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) && bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) break; if (sco_con != NULL) { switch (sco_con->state) { case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ error = EALREADY; break; case NG_HCI_CON_W4_CONN_COMPLETE: sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; break; default: panic( "%s: %s - Invalid connection state=%d\n", __func__, NG_NODE_NAME(unit->node), sco_con->state); break; } goto out; } /* * If we got here then we need to create new SCO connection descriptor * and submit HCI command. */ sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); if (sco_con == NULL) { error = ENOMEM; goto out; } bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); /* * Create HCI command */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { ng_hci_free_con(sco_con); error = ENOBUFS; goto out; } m->m_pkthdr.len = m->m_len = sizeof(*req); req = mtod(m, struct sco_con_req *); req->hdr.type = NG_HCI_CMD_PKT; req->hdr.length = sizeof(req->cp); req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_ADD_SCO_CON)); req->cp.con_handle = htole16(acl_con->con_handle); req->cp.pkt_type = NG_HCI_PKT_HV1; if (unit->features[1] & NG_HCI_LMP_HV2_PKT) req->cp.pkt_type |= NG_HCI_PKT_HV2; if (unit->features[1] & NG_HCI_LMP_HV3_PKT) req->cp.pkt_type |= NG_HCI_PKT_HV3; req->cp.pkt_type &= unit->packet_mask; if ((req->cp.pkt_type & (NG_HCI_PKT_HV1| NG_HCI_PKT_HV2| NG_HCI_PKT_HV3)) == 0) req->cp.pkt_type = NG_HCI_PKT_HV1; req->cp.pkt_type = htole16(req->cp.pkt_type); /* * Adust connection state */ sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; ng_hci_con_timeout(sco_con); /* * Queue and send HCI command */ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) error = ng_hci_send_command(unit); out: NG_FREE_ITEM(item); return (error); } /* ng_hci_lp_sco_con_req */ static int ng_hci_lp_le_con_req(ng_hci_unit_p unit, item_p item, hook_p hook, int link_type) { struct acl_con_req { ng_hci_cmd_pkt_t hdr; ng_hci_le_create_connection_cp cp; } __attribute__ ((packed)) *req = NULL; ng_hci_lp_con_req_ep *ep = NULL; ng_hci_unit_con_p con = NULL; struct mbuf *m = NULL; int error = 0; ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); if((link_type != NG_HCI_LINK_LE_PUBLIC)&& (link_type != NG_HCI_LINK_LE_RANDOM)){ printf("%s: Link type %d Cannot be here \n", __func__, link_type); } /* * Only one ACL connection can exist between each pair of units. * So try to find ACL connection descriptor (in any state) that * has requested remote BD_ADDR. * * Two cases: * * 1) We do not have connection to the remote unit. This is simple. * Just create new connection descriptor and send HCI command to * create new connection. * * 2) We do have connection descriptor. We need to check connection * state: * * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of * accepting connection from the remote unit. This is a race * condition. We will ignore this message. * * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already * requested connection or we just accepted it. In any case * all we need to do here is set appropriate notification bit * and wait. * * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back * and let upper layer know that we have connection already. */ con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, link_type); if (con != NULL) { switch (con->state) { case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ error = EALREADY; break; case NG_HCI_CON_W4_CONN_COMPLETE: if (hook != unit->sco) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; break; case NG_HCI_CON_OPEN: { struct ng_mesg *msg = NULL; ng_hci_lp_con_cfm_ep *cfm = NULL; if (hook != NULL && NG_HOOK_IS_VALID(hook)) { NGI_GET_MSG(item, msg); NG_FREE_MSG(msg); NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, sizeof(*cfm), M_NOWAIT); if (msg != NULL) { cfm = (ng_hci_lp_con_cfm_ep *)msg->data; cfm->status = 0; cfm->link_type = con->link_type; cfm->con_handle = con->con_handle; bcopy(&con->bdaddr, &cfm->bdaddr, sizeof(cfm->bdaddr)); /* * This will forward item back to * sender and set item to NULL */ _NGI_MSG(item) = msg; NG_FWD_ITEM_HOOK(error, item, hook); } else error = ENOMEM; } else NG_HCI_INFO( "%s: %s - Source hook is not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), hook); } break; default: panic( "%s: %s - Invalid connection state=%d\n", __func__, NG_NODE_NAME(unit->node), con->state); break; } goto out; } /* * If we got here then we need to create new ACL connection descriptor * and submit HCI command. First create new connection desriptor, set * bdaddr and notification flags. */ con = ng_hci_new_con(unit, link_type); if (con == NULL) { error = ENOMEM; goto out; } bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); /* * Create HCI command */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { ng_hci_free_con(con); error = ENOBUFS; goto out; } m->m_pkthdr.len = m->m_len = sizeof(*req); req = mtod(m, struct acl_con_req *); req->hdr.type = NG_HCI_CMD_PKT; req->hdr.length = sizeof(req->cp); req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LE, NG_HCI_OCF_LE_CREATE_CONNECTION)); bcopy(&ep->bdaddr, &req->cp.peer_addr, sizeof(req->cp.peer_addr)); req->cp.own_address_type = 0; req->cp.peer_addr_type = (link_type == NG_HCI_LINK_LE_RANDOM)? 1:0; req->cp.scan_interval = htole16(4); req->cp.scan_window = htole16(4); req->cp.filter_policy = 0; req->cp.conn_interval_min = htole16(0xf); req->cp.conn_interval_max = htole16(0xf); req->cp.conn_latency = htole16(0); req->cp.supervision_timeout = htole16(0xc80); req->cp.min_ce_length = htole16(1); req->cp.max_ce_length = htole16(1); /* * Adust connection state */ if (hook != unit->sco) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; con->state = NG_HCI_CON_W4_CONN_COMPLETE; ng_hci_con_timeout(con); /* * Queue and send HCI command */ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) error = ng_hci_send_command(unit); out: if (item != NULL) NG_FREE_ITEM(item); return (error); } /* ng_hci_lp_acl_con_req */ /* * Process LP_DisconnectReq event from the upper layer protocol */ int ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) { struct discon_req { ng_hci_cmd_pkt_t hdr; ng_hci_discon_cp cp; } __attribute__ ((packed)) *req = NULL; ng_hci_lp_discon_req_ep *ep = NULL; ng_hci_unit_con_p con = NULL; struct mbuf *m = NULL; int error = 0; /* Check if unit is ready */ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { NG_HCI_WARN( "%s: %s - unit is not ready, state=%#x\n", __func__, NG_NODE_NAME(unit->node), unit->state); error = ENXIO; goto out; } if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { NG_HCI_ALERT( "%s: %s - invalid LP_DisconnectReq message size=%d\n", __func__, NG_NODE_NAME(unit->node), NGI_MSG(item)->header.arglen); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); con = ng_hci_con_by_handle(unit, ep->con_handle); if (con == NULL) { NG_HCI_ERR( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), ep->con_handle); error = ENOENT; goto out; } if (con->state != NG_HCI_CON_OPEN) { NG_HCI_ERR( "%s: %s - invalid connection state=%d, handle=%d\n", __func__, NG_NODE_NAME(unit->node), con->state, ep->con_handle); error = EINVAL; goto out; } /* * Create HCI command */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { error = ENOBUFS; goto out; } m->m_pkthdr.len = m->m_len = sizeof(*req); req = mtod(m, struct discon_req *); req->hdr.type = NG_HCI_CMD_PKT; req->hdr.length = sizeof(req->cp); req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_DISCON)); req->cp.con_handle = htole16(ep->con_handle); req->cp.reason = ep->reason; /* * Queue and send HCI command */ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) error = ng_hci_send_command(unit); out: NG_FREE_ITEM(item); return (error); } /* ng_hci_lp_discon_req */ /* * Send LP_ConnectCfm event to the upper layer protocol */ int ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) { ng_hci_unit_p unit = con->unit; struct ng_mesg *msg = NULL; ng_hci_lp_con_cfm_ep *ep = NULL; int error; /* * Check who wants to be notified. For ACL links both ACL and SCO * upstream hooks will be notified (if required). For SCO links * only SCO upstream hook will receive notification */ if (con->link_type != NG_HCI_LINK_SCO && con->flags & NG_HCI_CON_NOTIFY_ACL) { if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, sizeof(*ep), M_NOWAIT); if (msg != NULL) { ep = (ng_hci_lp_con_cfm_ep *) msg->data; ep->status = status; ep->link_type = con->link_type; ep->con_handle = con->con_handle; bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0); } } else NG_HCI_INFO( "%s: %s - ACL hook not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->acl); con->flags &= ~NG_HCI_CON_NOTIFY_ACL; } if (con->flags & NG_HCI_CON_NOTIFY_SCO) { if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, sizeof(*ep), M_NOWAIT); if (msg != NULL) { ep = (ng_hci_lp_con_cfm_ep *) msg->data; ep->status = status; ep->link_type = con->link_type; ep->con_handle = con->con_handle; bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); } } else NG_HCI_INFO( "%s: %s - SCO hook not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->acl); con->flags &= ~NG_HCI_CON_NOTIFY_SCO; } return (0); } /* ng_hci_lp_con_cfm */ +int +ng_hci_lp_enc_change(ng_hci_unit_con_p con, int status) +{ + ng_hci_unit_p unit = con->unit; + struct ng_mesg *msg = NULL; + ng_hci_lp_enc_change_ep *ep = NULL; + int error; + + + if (con->link_type != NG_HCI_LINK_SCO) { + if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { + NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_ENC_CHG, + sizeof(*ep), M_NOWAIT); + if (msg != NULL) { + ep = (ng_hci_lp_enc_change_ep *) msg->data; + ep->status = status; + ep->link_type = con->link_type; + ep->con_handle = con->con_handle; + + NG_SEND_MSG_HOOK(error, unit->node, msg, + unit->acl, 0); + } + } else + NG_HCI_INFO( +"%s: %s - ACL hook not valid, hook=%p\n", + __func__, NG_NODE_NAME(unit->node), unit->acl); + + } + return (0); +} /* ng_hci_lp_con_cfm */ + /* * Send LP_ConnectInd event to the upper layer protocol */ int ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) { ng_hci_unit_p unit = con->unit; struct ng_mesg *msg = NULL; ng_hci_lp_con_ind_ep *ep = NULL; hook_p hook = NULL; int error = 0; /* * Connection_Request event is generated for specific link type. * Use link_type to select upstream hook. */ if (con->link_type != NG_HCI_LINK_SCO) hook = unit->acl; else hook = unit->sco; if (hook != NULL && NG_HOOK_IS_VALID(hook)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, sizeof(*ep), M_NOWAIT); if (msg == NULL) return (ENOMEM); ep = (ng_hci_lp_con_ind_ep *)(msg->data); ep->link_type = con->link_type; bcopy(uclass, ep->uclass, sizeof(ep->uclass)); bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0); } else { NG_HCI_WARN( "%s: %s - Upstream hook is not connected or not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), hook); error = ENOTCONN; } return (error); } /* ng_hci_lp_con_ind */ /* * Process LP_ConnectRsp event from the upper layer protocol */ int ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) { struct con_rsp_req { ng_hci_cmd_pkt_t hdr; union { ng_hci_accept_con_cp acc; ng_hci_reject_con_cp rej; } __attribute__ ((packed)) cp; } __attribute__ ((packed)) *req = NULL; ng_hci_lp_con_rsp_ep *ep = NULL; ng_hci_unit_con_p con = NULL; struct mbuf *m = NULL; int error = 0; /* Check if unit is ready */ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { NG_HCI_WARN( "%s: %s - unit is not ready, state=%#x\n", __func__, NG_NODE_NAME(unit->node), unit->state); error = ENXIO; goto out; } if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { NG_HCI_ALERT( "%s: %s - invalid LP_ConnectRsp message size=%d\n", __func__, NG_NODE_NAME(unit->node), NGI_MSG(item)->header.arglen); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); /* * Here we have to deal with race. Upper layers might send conflicting * requests. One might send Accept and other Reject. We will not try * to solve all the problems, so first request will always win. * * Try to find connection that matches the following: * * 1) con->link_type == ep->link_type * * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || * con->state == NG_HCI_CON_W4_CONN_COMPLETE * * 3) con->bdaddr == ep->bdaddr * * Two cases: * * 1) We do not have connection descriptor. Could be bogus request or * we have rejected connection already. * * 2) We do have connection descriptor. Then we need to check state: * * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested * connection and it is a first response from the upper layer. * if "status == 0" (Accept) then we will send Accept_Connection * command and change connection state to W4_CONN_COMPLETE, else * send reject and delete connection. * * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted * connection. If "status == 0" we just need to link request * and wait, else ignore Reject request. */ LIST_FOREACH(con, &unit->con_list, next) if (con->link_type == ep->link_type && (con->state == NG_HCI_CON_W4_LP_CON_RSP || con->state == NG_HCI_CON_W4_CONN_COMPLETE) && bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) break; if (con == NULL) { /* Reject for non-existing connection is fine */ error = (ep->status == 0)? ENOENT : 0; goto out; } /* * Remove connection timeout and check connection state. * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then * timeout already happened and event went into node's queue. */ if ((error = ng_hci_con_untimeout(con)) != 0) goto out; switch (con->state) { case NG_HCI_CON_W4_LP_CON_RSP: /* * Create HCI command */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { error = ENOBUFS; goto out; } req = mtod(m, struct con_rsp_req *); req->hdr.type = NG_HCI_CMD_PKT; if (ep->status == 0) { req->hdr.length = sizeof(req->cp.acc); req->hdr.opcode = htole16(NG_HCI_OPCODE( NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_ACCEPT_CON)); bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, sizeof(req->cp.acc.bdaddr)); /* * We are accepting connection, so if we support role * switch and role switch was enabled then set role to * NG_HCI_ROLE_MASTER and let LM peform role switch. * Otherwise we remain slave. In this case LM WILL NOT * perform role switch. */ if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch) req->cp.acc.role = NG_HCI_ROLE_MASTER; else req->cp.acc.role = NG_HCI_ROLE_SLAVE; /* * Adjust connection state */ if (hook == unit->acl) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; con->state = NG_HCI_CON_W4_CONN_COMPLETE; ng_hci_con_timeout(con); } else { req->hdr.length = sizeof(req->cp.rej); req->hdr.opcode = htole16(NG_HCI_OPCODE( NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_REJECT_CON)); bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, sizeof(req->cp.rej.bdaddr)); req->cp.rej.reason = ep->status; /* * Free connection descritor * Item will be deleted just before return. */ ng_hci_free_con(con); } m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; /* Queue and send HCI command */ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) error = ng_hci_send_command(unit); break; case NG_HCI_CON_W4_CONN_COMPLETE: if (ep->status == 0) { if (hook == unit->acl) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; } else error = EPERM; break; default: panic( "%s: %s - Invalid connection state=%d\n", __func__, NG_NODE_NAME(unit->node), con->state); break; } out: NG_FREE_ITEM(item); return (error); } /* ng_hci_lp_con_rsp */ /* * Send LP_DisconnectInd to the upper layer protocol */ int ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) { ng_hci_unit_p unit = con->unit; struct ng_mesg *msg = NULL; ng_hci_lp_discon_ind_ep *ep = NULL; int error = 0; /* * Disconnect_Complete event is generated for specific connection * handle. For ACL connection handles both ACL and SCO upstream * hooks will receive notification. For SCO connection handles * only SCO upstream hook will receive notification. */ if (con->link_type != NG_HCI_LINK_SCO) { if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); if (msg == NULL) return (ENOMEM); ep = (ng_hci_lp_discon_ind_ep *) msg->data; ep->reason = reason; ep->link_type = con->link_type; ep->con_handle = con->con_handle; NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0); } else NG_HCI_INFO( "%s: %s - ACL hook is not connected or not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->acl); } if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); if (msg == NULL) return (ENOMEM); ep = (ng_hci_lp_discon_ind_ep *) msg->data; ep->reason = reason; ep->link_type = con->link_type; ep->con_handle = con->con_handle; NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); } else NG_HCI_INFO( "%s: %s - SCO hook is not connected or not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->sco); return (0); } /* ng_hci_lp_discon_ind */ /* * Process LP_QoSReq action from the upper layer protocol */ int ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) { struct qos_setup_req { ng_hci_cmd_pkt_t hdr; ng_hci_qos_setup_cp cp; } __attribute__ ((packed)) *req = NULL; ng_hci_lp_qos_req_ep *ep = NULL; ng_hci_unit_con_p con = NULL; struct mbuf *m = NULL; int error = 0; /* Check if unit is ready */ if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { NG_HCI_WARN( "%s: %s - unit is not ready, state=%#x\n", __func__, NG_NODE_NAME(unit->node), unit->state); error = ENXIO; goto out; } if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { NG_HCI_ALERT( "%s: %s - invalid LP_QoSSetupReq message size=%d\n", __func__, NG_NODE_NAME(unit->node), NGI_MSG(item)->header.arglen); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); con = ng_hci_con_by_handle(unit, ep->con_handle); if (con == NULL) { NG_HCI_ERR( "%s: %s - invalid connection handle=%d\n", __func__, NG_NODE_NAME(unit->node), ep->con_handle); error = EINVAL; goto out; } if (con->link_type != NG_HCI_LINK_ACL) { NG_HCI_ERR("%s: %s - invalid link type=%d\n", __func__, NG_NODE_NAME(unit->node), con->link_type); error = EINVAL; goto out; } if (con->state != NG_HCI_CON_OPEN) { NG_HCI_ERR( "%s: %s - invalid connection state=%d, handle=%d\n", __func__, NG_NODE_NAME(unit->node), con->state, con->con_handle); error = EINVAL; goto out; } /* * Create HCI command */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { error = ENOBUFS; goto out; } m->m_pkthdr.len = m->m_len = sizeof(*req); req = mtod(m, struct qos_setup_req *); req->hdr.type = NG_HCI_CMD_PKT; req->hdr.length = sizeof(req->cp); req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, NG_HCI_OCF_QOS_SETUP)); req->cp.con_handle = htole16(ep->con_handle); req->cp.flags = ep->flags; req->cp.service_type = ep->service_type; req->cp.token_rate = htole32(ep->token_rate); req->cp.peak_bandwidth = htole32(ep->peak_bandwidth); req->cp.latency = htole32(ep->latency); req->cp.delay_variation = htole32(ep->delay_variation); /* * Adjust connection state */ if (hook == unit->acl) con->flags |= NG_HCI_CON_NOTIFY_ACL; else con->flags |= NG_HCI_CON_NOTIFY_SCO; /* * Queue and send HCI command */ NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) error = ng_hci_send_command(unit); out: NG_FREE_ITEM(item); return (error); } /* ng_hci_lp_qos_req */ /* * Send LP_QoSCfm event to the upper layer protocol */ int ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) { ng_hci_unit_p unit = con->unit; struct ng_mesg *msg = NULL; ng_hci_lp_qos_cfm_ep *ep = NULL; int error; if (con->flags & NG_HCI_CON_NOTIFY_ACL) { if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, sizeof(*ep), M_NOWAIT); if (msg != NULL) { ep = (ng_hci_lp_qos_cfm_ep *) msg->data; ep->status = status; ep->con_handle = con->con_handle; NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0); } } else NG_HCI_INFO( "%s: %s - ACL hook not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->acl); con->flags &= ~NG_HCI_CON_NOTIFY_ACL; } if (con->flags & NG_HCI_CON_NOTIFY_SCO) { if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, sizeof(*ep), M_NOWAIT); if (msg != NULL) { ep = (ng_hci_lp_qos_cfm_ep *) msg->data; ep->status = status; ep->con_handle = con->con_handle; NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); } } else NG_HCI_INFO( "%s: %s - SCO hook not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->sco); con->flags &= ~NG_HCI_CON_NOTIFY_SCO; } return (0); } /* ng_hci_lp_qos_cfm */ /* * Send LP_QoSViolationInd event to the upper layer protocol */ int ng_hci_lp_qos_ind(ng_hci_unit_con_p con) { ng_hci_unit_p unit = con->unit; struct ng_mesg *msg = NULL; ng_hci_lp_qos_ind_ep *ep = NULL; int error; /* * QoS Violation can only be generated for ACL connection handles. * Both ACL and SCO upstream hooks will receive notification. */ if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, sizeof(*ep), M_NOWAIT); if (msg == NULL) return (ENOMEM); ep = (ng_hci_lp_qos_ind_ep *) msg->data; ep->con_handle = con->con_handle; NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0); } else NG_HCI_INFO( "%s: %s - ACL hook is not connected or not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->acl); if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, sizeof(*ep), M_NOWAIT); if (msg == NULL) return (ENOMEM); ep = (ng_hci_lp_qos_ind_ep *) msg->data; ep->con_handle = con->con_handle; NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); } else NG_HCI_INFO( "%s: %s - SCO hook is not connected or not valid, hook=%p\n", __func__, NG_NODE_NAME(unit->node), unit->sco); return (0); } /* ng_hci_lp_qos_ind */ /* * Process connection timeout */ void ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle) { ng_hci_unit_p unit = NULL; ng_hci_unit_con_p con = NULL; if (NG_NODE_NOT_VALID(node)) { printf("%s: Netgraph node is not valid\n", __func__); return; } unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); con = ng_hci_con_by_handle(unit, con_handle); if (con == NULL) { NG_HCI_ALERT( "%s: %s - could not find connection, handle=%d\n", __func__, NG_NODE_NAME(node), con_handle); return; } if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) { NG_HCI_ALERT( "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(node), con_handle, con->state, con->flags); return; } con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; /* * We expect to receive connection timeout in one of the following * states: * * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded * to our LP_CON_IND. Do nothing and destroy connection. Remote peer * most likely already gave up on us. * * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection * (or we in the process of accepting it) and baseband has timedout * on us. Inform upper layers and send LP_CON_CFM. */ switch (con->state) { case NG_HCI_CON_W4_LP_CON_RSP: break; case NG_HCI_CON_W4_CONN_COMPLETE: ng_hci_lp_con_cfm(con, 0xee); break; default: panic( "%s: %s - Invalid connection state=%d\n", __func__, NG_NODE_NAME(node), con->state); break; } ng_hci_free_con(con); } /* ng_hci_process_con_timeout */ Index: head/sys/netgraph/bluetooth/hci/ng_hci_ulpi.h =================================================================== --- head/sys/netgraph/bluetooth/hci/ng_hci_ulpi.h (revision 290037) +++ head/sys/netgraph/bluetooth/hci/ng_hci_ulpi.h (revision 290038) @@ -1,54 +1,55 @@ /* * ng_hci_ulpi.h */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_hci_ulpi.h,v 1.2 2003/04/26 22:35:21 max Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_HCI_ULPI_H_ #define _NETGRAPH_HCI_ULPI_H_ /* * LP_xxx event handlers */ int ng_hci_lp_con_req (ng_hci_unit_p, item_p, hook_p); int ng_hci_lp_discon_req (ng_hci_unit_p, item_p, hook_p); int ng_hci_lp_con_cfm (ng_hci_unit_con_p, int); int ng_hci_lp_con_ind (ng_hci_unit_con_p, u_int8_t *); int ng_hci_lp_con_rsp (ng_hci_unit_p, item_p, hook_p); int ng_hci_lp_discon_ind (ng_hci_unit_con_p, int); int ng_hci_lp_qos_req (ng_hci_unit_p, item_p, hook_p); int ng_hci_lp_qos_cfm (ng_hci_unit_con_p, int); int ng_hci_lp_qos_ind (ng_hci_unit_con_p); +int ng_hci_lp_enc_change (ng_hci_unit_con_p, int); void ng_hci_process_con_timeout (node_p, hook_p, void *, int); #endif /* ndef _NETGRAPH_HCI_ULPI_H_ */ Index: head/sys/netgraph/bluetooth/include/ng_btsocket.h =================================================================== --- head/sys/netgraph/bluetooth/include/ng_btsocket.h (revision 290037) +++ head/sys/netgraph/bluetooth/include/ng_btsocket.h (revision 290038) @@ -1,380 +1,380 @@ /* * ng_btsocket.h */ /*- * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_btsocket.h,v 1.8 2003/04/26 22:32:10 max Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_BTSOCKET_H_ #define _NETGRAPH_BTSOCKET_H_ /* * Bluetooth protocols */ #define BLUETOOTH_PROTO_HCI 134 /* HCI protocol number */ #define BLUETOOTH_PROTO_L2CAP 135 /* L2CAP protocol number */ #define BLUETOOTH_PROTO_RFCOMM 136 /* RFCOMM protocol number */ #define BLUETOOTH_PROTO_SCO 137 /* SCO protocol number */ /* * Bluetooth version of struct sockaddr for raw HCI sockets */ struct sockaddr_hci { u_char hci_len; /* total length */ u_char hci_family; /* address family */ char hci_node[32]; /* address (size == NG_NODESIZ ) */ }; /* Raw HCI socket options */ #define SOL_HCI_RAW 0x0802 /* socket options level */ #define SO_HCI_RAW_FILTER 1 /* get/set filter on socket */ #define SO_HCI_RAW_DIRECTION 2 /* turn on/off direction info */ #define SCM_HCI_RAW_DIRECTION SO_HCI_RAW_DIRECTION /* cmsg_type */ /* * Raw HCI socket filter. * * For packet mask use (1 << (HCI packet indicator - 1)) * For event mask use (1 << (Event - 1)) */ struct ng_btsocket_hci_raw_filter { bitstr_t bit_decl(packet_mask, 32); bitstr_t bit_decl(event_mask, (NG_HCI_EVENT_MASK_SIZE * 8)); }; /* * Raw HCI sockets ioctl's */ /* Get state */ struct ng_btsocket_hci_raw_node_state { ng_hci_node_state_ep state; }; #define SIOC_HCI_RAW_NODE_GET_STATE \ _IOWR('b', NGM_HCI_NODE_GET_STATE, \ struct ng_btsocket_hci_raw_node_state) /* Initialize */ #define SIOC_HCI_RAW_NODE_INIT \ _IO('b', NGM_HCI_NODE_INIT) /* Get/Set debug level */ struct ng_btsocket_hci_raw_node_debug { ng_hci_node_debug_ep debug; }; #define SIOC_HCI_RAW_NODE_GET_DEBUG \ _IOWR('b', NGM_HCI_NODE_GET_DEBUG, \ struct ng_btsocket_hci_raw_node_debug) #define SIOC_HCI_RAW_NODE_SET_DEBUG \ _IOWR('b', NGM_HCI_NODE_SET_DEBUG, \ struct ng_btsocket_hci_raw_node_debug) /* Get buffer info */ struct ng_btsocket_hci_raw_node_buffer { ng_hci_node_buffer_ep buffer; }; #define SIOC_HCI_RAW_NODE_GET_BUFFER \ _IOWR('b', NGM_HCI_NODE_GET_BUFFER, \ struct ng_btsocket_hci_raw_node_buffer) /* Get BD_ADDR */ struct ng_btsocket_hci_raw_node_bdaddr { bdaddr_t bdaddr; }; #define SIOC_HCI_RAW_NODE_GET_BDADDR \ _IOWR('b', NGM_HCI_NODE_GET_BDADDR, \ struct ng_btsocket_hci_raw_node_bdaddr) /* Get features */ struct ng_btsocket_hci_raw_node_features { u_int8_t features[NG_HCI_FEATURES_SIZE]; }; #define SIOC_HCI_RAW_NODE_GET_FEATURES \ _IOWR('b', NGM_HCI_NODE_GET_FEATURES, \ struct ng_btsocket_hci_raw_node_features) /* Get stat */ struct ng_btsocket_hci_raw_node_stat { ng_hci_node_stat_ep stat; }; #define SIOC_HCI_RAW_NODE_GET_STAT \ _IOWR('b', NGM_HCI_NODE_GET_STAT, \ struct ng_btsocket_hci_raw_node_stat) /* Reset stat */ #define SIOC_HCI_RAW_NODE_RESET_STAT \ _IO('b', NGM_HCI_NODE_RESET_STAT) /* Flush neighbor cache */ #define SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE \ _IO('b', NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE) /* Get neighbor cache */ struct ng_btsocket_hci_raw_node_neighbor_cache { u_int32_t num_entries; ng_hci_node_neighbor_cache_entry_ep *entries; }; #define SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE \ _IOWR('b', NGM_HCI_NODE_GET_NEIGHBOR_CACHE, \ struct ng_btsocket_hci_raw_node_neighbor_cache) /* Get connection list */ struct ng_btsocket_hci_raw_con_list { u_int32_t num_connections; ng_hci_node_con_ep *connections; }; #define SIOC_HCI_RAW_NODE_GET_CON_LIST \ _IOWR('b', NGM_HCI_NODE_GET_CON_LIST, \ struct ng_btsocket_hci_raw_con_list) /* Get/Set link policy settings mask */ struct ng_btsocket_hci_raw_node_link_policy_mask { ng_hci_node_link_policy_mask_ep policy_mask; }; #define SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK \ _IOWR('b', NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK, \ struct ng_btsocket_hci_raw_node_link_policy_mask) #define SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK \ _IOWR('b', NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK, \ struct ng_btsocket_hci_raw_node_link_policy_mask) /* Get/Set packet mask */ struct ng_btsocket_hci_raw_node_packet_mask { ng_hci_node_packet_mask_ep packet_mask; }; #define SIOC_HCI_RAW_NODE_GET_PACKET_MASK \ _IOWR('b', NGM_HCI_NODE_GET_PACKET_MASK, \ struct ng_btsocket_hci_raw_node_packet_mask) #define SIOC_HCI_RAW_NODE_SET_PACKET_MASK \ _IOWR('b', NGM_HCI_NODE_SET_PACKET_MASK, \ struct ng_btsocket_hci_raw_node_packet_mask) /* Get/Set role switch */ struct ng_btsocket_hci_raw_node_role_switch { ng_hci_node_role_switch_ep role_switch; }; #define SIOC_HCI_RAW_NODE_GET_ROLE_SWITCH \ _IOWR('b', NGM_HCI_NODE_GET_ROLE_SWITCH, \ struct ng_btsocket_hci_raw_node_role_switch) #define SIOC_HCI_RAW_NODE_SET_ROLE_SWITCH \ _IOWR('b', NGM_HCI_NODE_SET_ROLE_SWITCH, \ struct ng_btsocket_hci_raw_node_role_switch) /* Get list of HCI node names */ struct ng_btsocket_hci_raw_node_list_names { u_int32_t num_names; struct nodeinfo *names; }; #define SIOC_HCI_RAW_NODE_LIST_NAMES \ _IOWR('b', NGM_HCI_NODE_LIST_NAMES, \ struct ng_btsocket_hci_raw_node_list_names) /* * XXX FIXME: probably does not belong here * Bluetooth version of struct sockaddr for SCO sockets (SEQPACKET) */ struct sockaddr_sco { u_char sco_len; /* total length */ u_char sco_family; /* address family */ bdaddr_t sco_bdaddr; /* address */ }; /* SCO socket options */ #define SOL_SCO 0x0209 /* socket options level */ #define SO_SCO_MTU 1 /* get sockets mtu */ #define SO_SCO_CONNINFO 2 /* get HCI connection handle */ /* * XXX FIXME: probably does not belong here * Bluetooth version of struct sockaddr for L2CAP sockets (RAW and SEQPACKET) */ struct sockaddr_l2cap_compat { u_char l2cap_len; /* total length */ u_char l2cap_family; /* address family */ u_int16_t l2cap_psm; /* PSM (Protocol/Service Multiplexor) */ bdaddr_t l2cap_bdaddr; /* address */ }; #define BDADDR_BREDR 0 #define BDADDR_LE_PUBLIC 1 #define BDADDR_LE_RANDOM 2 struct sockaddr_l2cap { u_char l2cap_len; /* total length */ u_char l2cap_family; /* address family */ u_int16_t l2cap_psm; /* PSM (Protocol/Service Multiplexor) */ bdaddr_t l2cap_bdaddr; /* address */ u_int16_t l2cap_cid; /*cid*/ u_int8_t l2cap_bdaddr_type; /*address type*/ }; #if !defined(L2CAP_SOCKET_CHECKED) && !defined(_KERNEL) #warning "Make sure new member of socket address initialized" #endif /* L2CAP socket options */ #define SOL_L2CAP 0x1609 /* socket option level */ #define SO_L2CAP_IMTU 1 /* get/set incoming MTU */ #define SO_L2CAP_OMTU 2 /* get outgoing (peer incoming) MTU */ #define SO_L2CAP_IFLOW 3 /* get incoming flow spec. */ #define SO_L2CAP_OFLOW 4 /* get/set outgoing flow spec. */ #define SO_L2CAP_FLUSH 5 /* get/set flush timeout */ - +#define SO_L2CAP_ENCRYPTED 6 /* get/set whether wait for encryptin on connect */ /* * Raw L2CAP sockets ioctl's */ /* Ping */ struct ng_btsocket_l2cap_raw_ping { u_int32_t result; u_int32_t echo_size; u_int8_t *echo_data; }; #define SIOC_L2CAP_L2CA_PING \ _IOWR('b', NGM_L2CAP_L2CA_PING, \ struct ng_btsocket_l2cap_raw_ping) /* Get info */ struct ng_btsocket_l2cap_raw_get_info { u_int32_t result; u_int32_t info_type; u_int32_t info_size; u_int8_t *info_data; }; #define SIOC_L2CAP_L2CA_GET_INFO \ _IOWR('b', NGM_L2CAP_L2CA_GET_INFO, \ struct ng_btsocket_l2cap_raw_get_info) /* Get flags */ struct ng_btsocket_l2cap_raw_node_flags { ng_l2cap_node_flags_ep flags; }; #define SIOC_L2CAP_NODE_GET_FLAGS \ _IOWR('b', NGM_L2CAP_NODE_GET_FLAGS, \ struct ng_btsocket_l2cap_raw_node_flags) /* Get/Set debug level */ struct ng_btsocket_l2cap_raw_node_debug { ng_l2cap_node_debug_ep debug; }; #define SIOC_L2CAP_NODE_GET_DEBUG \ _IOWR('b', NGM_L2CAP_NODE_GET_DEBUG, \ struct ng_btsocket_l2cap_raw_node_debug) #define SIOC_L2CAP_NODE_SET_DEBUG \ _IOWR('b', NGM_L2CAP_NODE_SET_DEBUG, \ struct ng_btsocket_l2cap_raw_node_debug) /* Get connection list */ struct ng_btsocket_l2cap_raw_con_list { u_int32_t num_connections; ng_l2cap_node_con_ep *connections; }; #define SIOC_L2CAP_NODE_GET_CON_LIST \ _IOWR('b', NGM_L2CAP_NODE_GET_CON_LIST, \ struct ng_btsocket_l2cap_raw_con_list) /* Get channel list */ struct ng_btsocket_l2cap_raw_chan_list { u_int32_t num_channels; ng_l2cap_node_chan_ep *channels; }; #define SIOC_L2CAP_NODE_GET_CHAN_LIST \ _IOWR('b', NGM_L2CAP_NODE_GET_CHAN_LIST, \ struct ng_btsocket_l2cap_raw_chan_list) /* Get/Set auto disconnect timeout */ struct ng_btsocket_l2cap_raw_auto_discon_timo { ng_l2cap_node_auto_discon_ep timeout; }; #define SIOC_L2CAP_NODE_GET_AUTO_DISCON_TIMO \ _IOWR('b', NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO, \ struct ng_btsocket_l2cap_raw_auto_discon_timo) #define SIOC_L2CAP_NODE_SET_AUTO_DISCON_TIMO \ _IOWR('b', NGM_L2CAP_NODE_SET_AUTO_DISCON_TIMO, \ struct ng_btsocket_l2cap_raw_auto_discon_timo) /* * XXX FIXME: probably does not belong here * Bluetooth version of struct sockaddr for RFCOMM sockets (STREAM) */ struct sockaddr_rfcomm { u_char rfcomm_len; /* total length */ u_char rfcomm_family; /* address family */ bdaddr_t rfcomm_bdaddr; /* address */ u_int8_t rfcomm_channel; /* channel */ }; /* Flow control information */ struct ng_btsocket_rfcomm_fc_info { u_int8_t lmodem; /* modem signals (local) */ u_int8_t rmodem; /* modem signals (remote) */ u_int8_t tx_cred; /* TX credits */ u_int8_t rx_cred; /* RX credits */ u_int8_t cfc; /* credit flow control */ u_int8_t reserved; }; /* STREAM RFCOMM socket options */ #define SOL_RFCOMM 0x0816 /* socket options level */ #define SO_RFCOMM_MTU 1 /* get channel MTU */ #define SO_RFCOMM_FC_INFO 2 /* get flow control information */ /* * Netgraph node type name and cookie */ #define NG_BTSOCKET_HCI_RAW_NODE_TYPE "btsock_hci_raw" #define NG_BTSOCKET_L2CAP_RAW_NODE_TYPE "btsock_l2c_raw" #define NG_BTSOCKET_L2CAP_NODE_TYPE "btsock_l2c" #define NG_BTSOCKET_SCO_NODE_TYPE "btsock_sco" /* * Debug levels */ #define NG_BTSOCKET_ALERT_LEVEL 1 #define NG_BTSOCKET_ERR_LEVEL 2 #define NG_BTSOCKET_WARN_LEVEL 3 #define NG_BTSOCKET_INFO_LEVEL 4 #endif /* _NETGRAPH_BTSOCKET_H_ */ Index: head/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h =================================================================== --- head/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h (revision 290037) +++ head/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h (revision 290038) @@ -1,214 +1,216 @@ /* * ng_btsocket_l2cap.h */ /*- * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_btsocket_l2cap.h,v 1.4 2003/03/25 23:53:33 max Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_BTSOCKET_L2CAP_H_ #define _NETGRAPH_BTSOCKET_L2CAP_H_ /* * L2CAP routing entry */ struct ng_hook; struct ng_message; struct ng_btsocket_l2cap_rtentry { bdaddr_t src; /* source BD_ADDR */ struct ng_hook *hook; /* downstream hook */ LIST_ENTRY(ng_btsocket_l2cap_rtentry) next; /* link to next */ }; typedef struct ng_btsocket_l2cap_rtentry ng_btsocket_l2cap_rtentry_t; typedef struct ng_btsocket_l2cap_rtentry * ng_btsocket_l2cap_rtentry_p; /***************************************************************************** ***************************************************************************** ** SOCK_RAW L2CAP sockets ** ***************************************************************************** *****************************************************************************/ #define NG_BTSOCKET_L2CAP_RAW_SENDSPACE NG_L2CAP_MTU_DEFAULT #define NG_BTSOCKET_L2CAP_RAW_RECVSPACE NG_L2CAP_MTU_DEFAULT /* * Bluetooth raw L2CAP socket PCB */ struct ng_btsocket_l2cap_raw_pcb { struct socket *so; /* socket */ u_int32_t flags; /* flags */ #define NG_BTSOCKET_L2CAP_RAW_PRIVILEGED (1 << 0) bdaddr_t src; /* source address */ bdaddr_t dst; /* dest address */ uint8_t srctype;/*source addr type*/ uint8_t dsttype;/*source addr type*/ ng_btsocket_l2cap_rtentry_p rt; /* routing info */ u_int32_t token; /* message token */ struct ng_mesg *msg; /* message */ struct mtx pcb_mtx; /* pcb mutex */ LIST_ENTRY(ng_btsocket_l2cap_raw_pcb) next; /* link to next PCB */ }; typedef struct ng_btsocket_l2cap_raw_pcb ng_btsocket_l2cap_raw_pcb_t; typedef struct ng_btsocket_l2cap_raw_pcb * ng_btsocket_l2cap_raw_pcb_p; #define so2l2cap_raw_pcb(so) \ ((struct ng_btsocket_l2cap_raw_pcb *)((so)->so_pcb)) /* * Bluetooth raw L2CAP socket methods */ #ifdef _KERNEL void ng_btsocket_l2cap_raw_init (void); void ng_btsocket_l2cap_raw_abort (struct socket *); void ng_btsocket_l2cap_raw_close (struct socket *); int ng_btsocket_l2cap_raw_attach (struct socket *, int, struct thread *); int ng_btsocket_l2cap_raw_bind (struct socket *, struct sockaddr *, struct thread *); int ng_btsocket_l2cap_raw_connect (struct socket *, struct sockaddr *, struct thread *); int ng_btsocket_l2cap_raw_control (struct socket *, u_long, caddr_t, struct ifnet *, struct thread *); void ng_btsocket_l2cap_raw_detach (struct socket *); int ng_btsocket_l2cap_raw_disconnect (struct socket *); int ng_btsocket_l2cap_raw_peeraddr (struct socket *, struct sockaddr **); int ng_btsocket_l2cap_raw_send (struct socket *, int, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *); int ng_btsocket_l2cap_raw_sockaddr (struct socket *, struct sockaddr **); #endif /* _KERNEL */ /***************************************************************************** ***************************************************************************** ** SOCK_SEQPACKET L2CAP sockets ** ***************************************************************************** *****************************************************************************/ #define NG_BTSOCKET_L2CAP_SENDSPACE NG_L2CAP_MTU_DEFAULT /* (64 * 1024) */ #define NG_BTSOCKET_L2CAP_RECVSPACE (64 * 1024) /* * Bluetooth L2CAP socket PCB */ struct ng_btsocket_l2cap_pcb { struct socket *so; /* Pointer to socket */ bdaddr_t src; /* Source address */ bdaddr_t dst; /* Destination address */ uint8_t srctype; /*source addr type*/ uint8_t dsttype; /*source addr type*/ u_int16_t psm; /* PSM */ u_int16_t cid; /* Local channel ID */ - + uint8_t idtype; u_int16_t flags; /* socket flags */ #define NG_BTSOCKET_L2CAP_CLIENT (1 << 0) /* socket is client */ #define NG_BTSOCKET_L2CAP_TIMO (1 << 1) /* timeout pending */ u_int8_t state; /* socket state */ #define NG_BTSOCKET_L2CAP_CLOSED 0 /* socket closed */ #define NG_BTSOCKET_L2CAP_CONNECTING 1 /* wait for connect */ #define NG_BTSOCKET_L2CAP_CONFIGURING 2 /* wait for config */ #define NG_BTSOCKET_L2CAP_OPEN 3 /* socket open */ #define NG_BTSOCKET_L2CAP_DISCONNECTING 4 /* wait for disconnect */ +#define NG_BTSOCKET_L2CAP_W4_ENC_CHANGE 5 u_int8_t cfg_state; /* config state */ #define NG_BTSOCKET_L2CAP_CFG_IN (1 << 0) /* incoming path done */ #define NG_BTSOCKET_L2CAP_CFG_OUT (1 << 1) /* outgoing path done */ #define NG_BTSOCKET_L2CAP_CFG_BOTH \ (NG_BTSOCKET_L2CAP_CFG_IN | NG_BTSOCKET_L2CAP_CFG_OUT) #define NG_BTSOCKET_L2CAP_CFG_IN_SENT (1 << 2) /* L2CAP ConfigReq sent */ #define NG_BTSOCKET_L2CAP_CFG_OUT_SENT (1 << 3) /* ---/--- */ - + uint8_t encryption; u_int16_t imtu; /* Incoming MTU */ ng_l2cap_flow_t iflow; /* Input flow spec */ u_int16_t omtu; /* Outgoing MTU */ ng_l2cap_flow_t oflow; /* Outgoing flow spec */ u_int16_t flush_timo; /* flush timeout */ u_int16_t link_timo; /* link timeout */ struct callout timo; /* timeout */ u_int32_t token; /* message token */ ng_btsocket_l2cap_rtentry_p rt; /* routing info */ struct mtx pcb_mtx; /* pcb mutex */ - + uint16_t need_encrypt; /*encryption needed*/ + LIST_ENTRY(ng_btsocket_l2cap_pcb) next; /* link to next PCB */ }; typedef struct ng_btsocket_l2cap_pcb ng_btsocket_l2cap_pcb_t; typedef struct ng_btsocket_l2cap_pcb * ng_btsocket_l2cap_pcb_p; #define so2l2cap_pcb(so) \ ((struct ng_btsocket_l2cap_pcb *)((so)->so_pcb)) /* * Bluetooth L2CAP socket methods */ #ifdef _KERNEL void ng_btsocket_l2cap_init (void); void ng_btsocket_l2cap_abort (struct socket *); void ng_btsocket_l2cap_close (struct socket *); int ng_btsocket_l2cap_accept (struct socket *, struct sockaddr **); int ng_btsocket_l2cap_attach (struct socket *, int, struct thread *); int ng_btsocket_l2cap_bind (struct socket *, struct sockaddr *, struct thread *); int ng_btsocket_l2cap_connect (struct socket *, struct sockaddr *, struct thread *); int ng_btsocket_l2cap_control (struct socket *, u_long, caddr_t, struct ifnet *, struct thread *); int ng_btsocket_l2cap_ctloutput (struct socket *, struct sockopt *); void ng_btsocket_l2cap_detach (struct socket *); int ng_btsocket_l2cap_disconnect (struct socket *); int ng_btsocket_l2cap_listen (struct socket *, int, struct thread *); int ng_btsocket_l2cap_peeraddr (struct socket *, struct sockaddr **); int ng_btsocket_l2cap_send (struct socket *, int, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *); int ng_btsocket_l2cap_sockaddr (struct socket *, struct sockaddr **); #endif /* _KERNEL */ #endif /* _NETGRAPH_BTSOCKET_L2CAP_H_ */ Index: head/sys/netgraph/bluetooth/include/ng_hci.h =================================================================== --- head/sys/netgraph/bluetooth/include/ng_hci.h (revision 290037) +++ head/sys/netgraph/bluetooth/include/ng_hci.h (revision 290038) @@ -1,1983 +1,1989 @@ /* * ng_hci.h */ /*- * Copyright (c) 2001 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_hci.h,v 1.2 2003/03/18 00:09:37 max Exp $ * $FreeBSD$ */ /* * This file contains everything that application needs to know about * Host Controller Interface (HCI). All information was obtained from * Bluetooth Specification Book v1.1. * * This file can be included by both kernel and userland applications. * * NOTE: Here and after Bluetooth device is called a "unit". Bluetooth * specification refers to both devices and units. They are the * same thing (i think), so to be consistent word "unit" will be * used. */ #ifndef _NETGRAPH_HCI_H_ #define _NETGRAPH_HCI_H_ /************************************************************************** ************************************************************************** ** Netgraph node hook name, type name and type cookie and commands ************************************************************************** **************************************************************************/ /* Node type name and type cookie */ #define NG_HCI_NODE_TYPE "hci" #define NGM_HCI_COOKIE 1000774184 /* Netgraph node hook names */ #define NG_HCI_HOOK_DRV "drv" /* Driver <-> HCI */ #define NG_HCI_HOOK_ACL "acl" /* HCI <-> Upper */ #define NG_HCI_HOOK_SCO "sco" /* HCI <-> Upper */ #define NG_HCI_HOOK_RAW "raw" /* HCI <-> Upper */ /************************************************************************** ************************************************************************** ** Common defines and types (HCI) ************************************************************************** **************************************************************************/ /* All sizes are in bytes */ #define NG_HCI_BDADDR_SIZE 6 /* unit address */ #define NG_HCI_LAP_SIZE 3 /* unit LAP */ #define NG_HCI_KEY_SIZE 16 /* link key */ #define NG_HCI_PIN_SIZE 16 /* link PIN */ #define NG_HCI_EVENT_MASK_SIZE 8 /* event mask */ #define NG_HCI_LE_EVENT_MASK_SIZE 8 /* event mask */ #define NG_HCI_CLASS_SIZE 3 /* unit class */ #define NG_HCI_FEATURES_SIZE 8 /* LMP features */ #define NG_HCI_UNIT_NAME_SIZE 248 /* unit name size */ #define NG_HCI_COMMANDS_SIZE 64 /*Command list BMP size*/ /* HCI specification */ #define NG_HCI_SPEC_V10 0x00 /* v1.0 */ #define NG_HCI_SPEC_V11 0x01 /* v1.1 */ /* 0x02 - 0xFF - reserved for future use */ /* LMP features */ /* ------------------- byte 0 --------------------*/ #define NG_HCI_LMP_3SLOT 0x01 #define NG_HCI_LMP_5SLOT 0x02 #define NG_HCI_LMP_ENCRYPTION 0x04 #define NG_HCI_LMP_SLOT_OFFSET 0x08 #define NG_HCI_LMP_TIMING_ACCURACY 0x10 #define NG_HCI_LMP_SWITCH 0x20 #define NG_HCI_LMP_HOLD_MODE 0x40 #define NG_HCI_LMP_SNIFF_MODE 0x80 /* ------------------- byte 1 --------------------*/ #define NG_HCI_LMP_PARK_MODE 0x01 #define NG_HCI_LMP_RSSI 0x02 #define NG_HCI_LMP_CHANNEL_QUALITY 0x04 #define NG_HCI_LMP_SCO_LINK 0x08 #define NG_HCI_LMP_HV2_PKT 0x10 #define NG_HCI_LMP_HV3_PKT 0x20 #define NG_HCI_LMP_ULAW_LOG 0x40 #define NG_HCI_LMP_ALAW_LOG 0x80 /* ------------------- byte 2 --------------------*/ #define NG_HCI_LMP_CVSD 0x01 #define NG_HCI_LMP_PAGING_SCHEME 0x02 #define NG_HCI_LMP_POWER_CONTROL 0x04 #define NG_HCI_LMP_TRANSPARENT_SCO 0x08 #define NG_HCI_LMP_FLOW_CONTROL_LAG0 0x10 #define NG_HCI_LMP_FLOW_CONTROL_LAG1 0x20 #define NG_HCI_LMP_FLOW_CONTROL_LAG2 0x40 /* Link types */ #define NG_HCI_LINK_SCO 0x00 /* Voice */ #define NG_HCI_LINK_ACL 0x01 /* Data */ #define NG_HCI_LINK_LE_PUBLIC 0x02 /* LE Public*/ #define NG_HCI_LINK_LE_RANDOM 0x03 /* LE Random*/ /* 0x02 - 0xFF - reserved for future use */ /* Packet types */ /* 0x0001 - 0x0004 - reserved for future use */ #define NG_HCI_PKT_DM1 0x0008 /* ACL link */ #define NG_HCI_PKT_DH1 0x0010 /* ACL link */ #define NG_HCI_PKT_HV1 0x0020 /* SCO link */ #define NG_HCI_PKT_HV2 0x0040 /* SCO link */ #define NG_HCI_PKT_HV3 0x0080 /* SCO link */ /* 0x0100 - 0x0200 - reserved for future use */ #define NG_HCI_PKT_DM3 0x0400 /* ACL link */ #define NG_HCI_PKT_DH3 0x0800 /* ACL link */ /* 0x1000 - 0x2000 - reserved for future use */ #define NG_HCI_PKT_DM5 0x4000 /* ACL link */ #define NG_HCI_PKT_DH5 0x8000 /* ACL link */ /* * Connection modes/Unit modes * * This is confusing. It means that one of the units change its mode * for the specific connection. For example one connection was put on * hold (but i could be wrong :) */ #define NG_HCI_UNIT_MODE_ACTIVE 0x00 #define NG_HCI_UNIT_MODE_HOLD 0x01 #define NG_HCI_UNIT_MODE_SNIFF 0x02 #define NG_HCI_UNIT_MODE_PARK 0x03 /* 0x04 - 0xFF - reserved for future use */ /* Page scan modes */ #define NG_HCI_MANDATORY_PAGE_SCAN_MODE 0x00 #define NG_HCI_OPTIONAL_PAGE_SCAN_MODE1 0x01 #define NG_HCI_OPTIONAL_PAGE_SCAN_MODE2 0x02 #define NG_HCI_OPTIONAL_PAGE_SCAN_MODE3 0x03 /* 0x04 - 0xFF - reserved for future use */ /* Page scan repetition modes */ #define NG_HCI_SCAN_REP_MODE0 0x00 #define NG_HCI_SCAN_REP_MODE1 0x01 #define NG_HCI_SCAN_REP_MODE2 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Page scan period modes */ #define NG_HCI_PAGE_SCAN_PERIOD_MODE0 0x00 #define NG_HCI_PAGE_SCAN_PERIOD_MODE1 0x01 #define NG_HCI_PAGE_SCAN_PERIOD_MODE2 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Scan enable */ #define NG_HCI_NO_SCAN_ENABLE 0x00 #define NG_HCI_INQUIRY_ENABLE_PAGE_DISABLE 0x01 #define NG_HCI_INQUIRY_DISABLE_PAGE_ENABLE 0x02 #define NG_HCI_INQUIRY_ENABLE_PAGE_ENABLE 0x03 /* 0x04 - 0xFF - reserved for future use */ /* Hold mode activities */ #define NG_HCI_HOLD_MODE_NO_CHANGE 0x00 #define NG_HCI_HOLD_MODE_SUSPEND_PAGE_SCAN 0x01 #define NG_HCI_HOLD_MODE_SUSPEND_INQUIRY_SCAN 0x02 #define NG_HCI_HOLD_MODE_SUSPEND_PERIOD_INQUIRY 0x04 /* 0x08 - 0x80 - reserved for future use */ /* Connection roles */ #define NG_HCI_ROLE_MASTER 0x00 #define NG_HCI_ROLE_SLAVE 0x01 /* 0x02 - 0xFF - reserved for future use */ /* Key flags */ #define NG_HCI_USE_SEMI_PERMANENT_LINK_KEYS 0x00 #define NG_HCI_USE_TEMPORARY_LINK_KEY 0x01 /* 0x02 - 0xFF - reserved for future use */ /* Pin types */ #define NG_HCI_PIN_TYPE_VARIABLE 0x00 #define NG_HCI_PIN_TYPE_FIXED 0x01 /* Link key types */ #define NG_HCI_LINK_KEY_TYPE_COMBINATION_KEY 0x00 #define NG_HCI_LINK_KEY_TYPE_LOCAL_UNIT_KEY 0x01 #define NG_HCI_LINK_KEY_TYPE_REMOTE_UNIT_KEY 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Encryption modes */ #define NG_HCI_ENCRYPTION_MODE_NONE 0x00 #define NG_HCI_ENCRYPTION_MODE_P2P 0x01 #define NG_HCI_ENCRYPTION_MODE_ALL 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Quality of service types */ #define NG_HCI_SERVICE_TYPE_NO_TRAFFIC 0x00 #define NG_HCI_SERVICE_TYPE_BEST_EFFORT 0x01 #define NG_HCI_SERVICE_TYPE_GUARANTEED 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Link policy settings */ #define NG_HCI_LINK_POLICY_DISABLE_ALL_LM_MODES 0x0000 #define NG_HCI_LINK_POLICY_ENABLE_ROLE_SWITCH 0x0001 /* Master/Slave switch */ #define NG_HCI_LINK_POLICY_ENABLE_HOLD_MODE 0x0002 #define NG_HCI_LINK_POLICY_ENABLE_SNIFF_MODE 0x0004 #define NG_HCI_LINK_POLICY_ENABLE_PARK_MODE 0x0008 /* 0x0010 - 0x8000 - reserved for future use */ /* Event masks */ #define NG_HCI_EVMSK_ALL 0x00000000ffffffff #define NG_HCI_EVMSK_NONE 0x0000000000000000 #define NG_HCI_EVMSK_INQUIRY_COMPL 0x0000000000000001 #define NG_HCI_EVMSK_INQUIRY_RESULT 0x0000000000000002 #define NG_HCI_EVMSK_CON_COMPL 0x0000000000000004 #define NG_HCI_EVMSK_CON_REQ 0x0000000000000008 #define NG_HCI_EVMSK_DISCON_COMPL 0x0000000000000010 #define NG_HCI_EVMSK_AUTH_COMPL 0x0000000000000020 #define NG_HCI_EVMSK_REMOTE_NAME_REQ_COMPL 0x0000000000000040 #define NG_HCI_EVMSK_ENCRYPTION_CHANGE 0x0000000000000080 #define NG_HCI_EVMSK_CHANGE_CON_LINK_KEY_COMPL 0x0000000000000100 #define NG_HCI_EVMSK_MASTER_LINK_KEY_COMPL 0x0000000000000200 #define NG_HCI_EVMSK_READ_REMOTE_FEATURES_COMPL 0x0000000000000400 #define NG_HCI_EVMSK_READ_REMOTE_VER_INFO_COMPL 0x0000000000000800 #define NG_HCI_EVMSK_QOS_SETUP_COMPL 0x0000000000001000 #define NG_HCI_EVMSK_COMMAND_COMPL 0x0000000000002000 #define NG_HCI_EVMSK_COMMAND_STATUS 0x0000000000004000 #define NG_HCI_EVMSK_HARDWARE_ERROR 0x0000000000008000 #define NG_HCI_EVMSK_FLUSH_OCCUR 0x0000000000010000 #define NG_HCI_EVMSK_ROLE_CHANGE 0x0000000000020000 #define NG_HCI_EVMSK_NUM_COMPL_PKTS 0x0000000000040000 #define NG_HCI_EVMSK_MODE_CHANGE 0x0000000000080000 #define NG_HCI_EVMSK_RETURN_LINK_KEYS 0x0000000000100000 #define NG_HCI_EVMSK_PIN_CODE_REQ 0x0000000000200000 #define NG_HCI_EVMSK_LINK_KEY_REQ 0x0000000000400000 #define NG_HCI_EVMSK_LINK_KEY_NOTIFICATION 0x0000000000800000 #define NG_HCI_EVMSK_LOOPBACK_COMMAND 0x0000000001000000 #define NG_HCI_EVMSK_DATA_BUFFER_OVERFLOW 0x0000000002000000 #define NG_HCI_EVMSK_MAX_SLOT_CHANGE 0x0000000004000000 #define NG_HCI_EVMSK_READ_CLOCK_OFFSET_COMLETE 0x0000000008000000 #define NG_HCI_EVMSK_CON_PKT_TYPE_CHANGED 0x0000000010000000 #define NG_HCI_EVMSK_QOS_VIOLATION 0x0000000020000000 #define NG_HCI_EVMSK_PAGE_SCAN_MODE_CHANGE 0x0000000040000000 #define NG_HCI_EVMSK_PAGE_SCAN_REP_MODE_CHANGE 0x0000000080000000 /* 0x0000000100000000 - 0x8000000000000000 - reserved for future use */ /* Filter types */ #define NG_HCI_FILTER_TYPE_NONE 0x00 #define NG_HCI_FILTER_TYPE_INQUIRY_RESULT 0x01 #define NG_HCI_FILTER_TYPE_CON_SETUP 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Filter condition types for NG_HCI_FILTER_TYPE_INQUIRY_RESULT */ #define NG_HCI_FILTER_COND_INQUIRY_NEW_UNIT 0x00 #define NG_HCI_FILTER_COND_INQUIRY_UNIT_CLASS 0x01 #define NG_HCI_FILTER_COND_INQUIRY_BDADDR 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Filter condition types for NG_HCI_FILTER_TYPE_CON_SETUP */ #define NG_HCI_FILTER_COND_CON_ANY_UNIT 0x00 #define NG_HCI_FILTER_COND_CON_UNIT_CLASS 0x01 #define NG_HCI_FILTER_COND_CON_BDADDR 0x02 /* 0x03 - 0xFF - reserved for future use */ /* Xmit level types */ #define NG_HCI_XMIT_LEVEL_CURRENT 0x00 #define NG_HCI_XMIT_LEVEL_MAXIMUM 0x01 /* 0x02 - 0xFF - reserved for future use */ /* Host to Host Controller flow control */ #define NG_HCI_H2HC_FLOW_CONTROL_NONE 0x00 #define NG_HCI_H2HC_FLOW_CONTROL_ACL 0x01 #define NG_HCI_H2HC_FLOW_CONTROL_SCO 0x02 #define NG_HCI_H2HC_FLOW_CONTROL_BOTH 0x03 /* ACL and SCO */ /* 0x04 - 0xFF - reserved future use */ /* Country codes */ #define NG_HCI_COUNTRY_CODE_NAM_EUR_JP 0x00 #define NG_HCI_COUNTRY_CODE_FRANCE 0x01 /* 0x02 - 0xFF - reserved future use */ /* Loopback modes */ #define NG_HCI_LOOPBACK_NONE 0x00 #define NG_HCI_LOOPBACK_LOCAL 0x01 #define NG_HCI_LOOPBACK_REMOTE 0x02 /* 0x03 - 0xFF - reserved future use */ /************************************************************************** ************************************************************************** ** Link level defines, headers and types ************************************************************************** **************************************************************************/ /* * Macro(s) to combine OpCode and extract OGF (OpCode Group Field) * and OCF (OpCode Command Field) from OpCode. */ #define NG_HCI_OPCODE(gf,cf) ((((gf) & 0x3f) << 10) | ((cf) & 0x3ff)) #define NG_HCI_OCF(op) ((op) & 0x3ff) #define NG_HCI_OGF(op) (((op) >> 10) & 0x3f) /* * Marco(s) to extract/combine connection handle, BC (Broadcast) and * PB (Packet boundary) flags. */ #define NG_HCI_CON_HANDLE(h) ((h) & 0x0fff) #define NG_HCI_PB_FLAG(h) (((h) & 0x3000) >> 12) #define NG_HCI_BC_FLAG(h) (((h) & 0xc000) >> 14) #define NG_HCI_MK_CON_HANDLE(h, pb, bc) \ (((h) & 0x0fff) | (((pb) & 3) << 12) | (((bc) & 3) << 14)) /* PB flag values */ /* 00 - reserved for future use */ #define NG_HCI_PACKET_FRAGMENT 0x1 #define NG_HCI_PACKET_START 0x2 /* 11 - reserved for future use */ /* BC flag values */ #define NG_HCI_POINT2POINT 0x0 /* only Host controller to Host */ #define NG_HCI_BROADCAST_ACTIVE 0x1 /* both directions */ #define NG_HCI_BROADCAST_PICONET 0x2 /* both directions */ /* 11 - reserved for future use */ /* HCI command packet header */ #define NG_HCI_CMD_PKT 0x01 #define NG_HCI_CMD_PKT_SIZE 0xff /* without header */ typedef struct { u_int8_t type; /* MUST be 0x1 */ u_int16_t opcode; /* OpCode */ u_int8_t length; /* parameter(s) length in bytes */ } __attribute__ ((packed)) ng_hci_cmd_pkt_t; /* ACL data packet header */ #define NG_HCI_ACL_DATA_PKT 0x02 #define NG_HCI_ACL_PKT_SIZE 0xffff /* without header */ typedef struct { u_int8_t type; /* MUST be 0x2 */ u_int16_t con_handle; /* connection handle + PB + BC flags */ u_int16_t length; /* payload length in bytes */ } __attribute__ ((packed)) ng_hci_acldata_pkt_t; /* SCO data packet header */ #define NG_HCI_SCO_DATA_PKT 0x03 #define NG_HCI_SCO_PKT_SIZE 0xff /* without header */ typedef struct { u_int8_t type; /* MUST be 0x3 */ u_int16_t con_handle; /* connection handle + reserved bits */ u_int8_t length; /* payload length in bytes */ } __attribute__ ((packed)) ng_hci_scodata_pkt_t; /* HCI event packet header */ #define NG_HCI_EVENT_PKT 0x04 #define NG_HCI_EVENT_PKT_SIZE 0xff /* without header */ typedef struct { u_int8_t type; /* MUST be 0x4 */ u_int8_t event; /* event */ u_int8_t length; /* parameter(s) length in bytes */ } __attribute__ ((packed)) ng_hci_event_pkt_t; /* Bluetooth unit address */ typedef struct { u_int8_t b[NG_HCI_BDADDR_SIZE]; } __attribute__ ((packed)) bdaddr_t; typedef bdaddr_t * bdaddr_p; /* Any BD_ADDR. Note: This is actually 7 bytes (count '\0' terminator) */ #define NG_HCI_BDADDR_ANY ((bdaddr_p) "\000\000\000\000\000\000") /* HCI status return parameter */ typedef struct { u_int8_t status; /* 0x00 - success */ } __attribute__ ((packed)) ng_hci_status_rp; /************************************************************************** ************************************************************************** ** Upper layer protocol interface. LP_xxx event parameters ************************************************************************** **************************************************************************/ /* Connection Request Event */ #define NGM_HCI_LP_CON_REQ 1 /* Upper -> HCI */ typedef struct { u_int16_t link_type; /* type of connection */ bdaddr_t bdaddr; /* remote unit address */ } ng_hci_lp_con_req_ep; /* * XXX XXX XXX * * NOTE: This request is not defined by Bluetooth specification, * but i find it useful :) */ #define NGM_HCI_LP_DISCON_REQ 2 /* Upper -> HCI */ typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t reason; /* reason to disconnect (only low byte) */ } ng_hci_lp_discon_req_ep; /* Connection Confirmation Event */ #define NGM_HCI_LP_CON_CFM 3 /* HCI -> Upper */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t link_type; /* link type */ u_int16_t con_handle; /* con_handle */ bdaddr_t bdaddr; /* remote unit address */ } ng_hci_lp_con_cfm_ep; /* Connection Indication Event */ #define NGM_HCI_LP_CON_IND 4 /* HCI -> Upper */ typedef struct { u_int8_t link_type; /* link type */ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* unit class */ bdaddr_t bdaddr; /* remote unit address */ } ng_hci_lp_con_ind_ep; /* Connection Response Event */ #define NGM_HCI_LP_CON_RSP 5 /* Upper -> HCI */ typedef struct { u_int8_t status; /* 0x00 - accept connection */ u_int8_t link_type; /* link type */ bdaddr_t bdaddr; /* remote unit address */ } ng_hci_lp_con_rsp_ep; /* Disconnection Indication Event */ #define NGM_HCI_LP_DISCON_IND 6 /* HCI -> Upper */ typedef struct { u_int8_t reason; /* reason to disconnect (only low byte) */ u_int8_t link_type; /* link type */ u_int16_t con_handle; /* connection handle */ } ng_hci_lp_discon_ind_ep; /* QoS Setup Request Event */ #define NGM_HCI_LP_QOS_REQ 7 /* Upper -> HCI */ typedef struct { u_int16_t con_handle; /* connection handle */ u_int8_t flags; /* reserved */ u_int8_t service_type; /* service type */ u_int32_t token_rate; /* bytes/sec */ u_int32_t peak_bandwidth; /* bytes/sec */ u_int32_t latency; /* msec */ u_int32_t delay_variation; /* msec */ } ng_hci_lp_qos_req_ep; /* QoS Conformition Event */ #define NGM_HCI_LP_QOS_CFM 8 /* HCI -> Upper */ typedef struct { u_int16_t status; /* 0x00 - success (only low byte) */ u_int16_t con_handle; /* connection handle */ } ng_hci_lp_qos_cfm_ep; /* QoS Violation Indication Event */ #define NGM_HCI_LP_QOS_IND 9 /* HCI -> Upper */ typedef struct { u_int16_t con_handle; /* connection handle */ } ng_hci_lp_qos_ind_ep; - +/*Encryption Change event*/ +#define NGM_HCI_LP_ENC_CHG 10 /* HCI->Upper*/ +typedef struct { + uint16_t con_handle; + uint8_t status; + uint8_t link_type; +}ng_hci_lp_enc_change_ep; /************************************************************************** ************************************************************************** ** HCI node command/event parameters ************************************************************************** **************************************************************************/ /* Debug levels */ #define NG_HCI_ALERT_LEVEL 1 #define NG_HCI_ERR_LEVEL 2 #define NG_HCI_WARN_LEVEL 3 #define NG_HCI_INFO_LEVEL 4 /* Unit states */ #define NG_HCI_UNIT_CONNECTED (1 << 0) #define NG_HCI_UNIT_INITED (1 << 1) #define NG_HCI_UNIT_READY (NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED) #define NG_HCI_UNIT_COMMAND_PENDING (1 << 2) /* Connection state */ #define NG_HCI_CON_CLOSED 0 /* connection closed */ #define NG_HCI_CON_W4_LP_CON_RSP 1 /* wait for LP_ConnectRsp */ #define NG_HCI_CON_W4_CONN_COMPLETE 2 /* wait for Connection_Complete evt */ #define NG_HCI_CON_OPEN 3 /* connection open */ /* Get HCI node (unit) state (see states above) */ #define NGM_HCI_NODE_GET_STATE 100 /* HCI -> User */ typedef u_int16_t ng_hci_node_state_ep; /* Turn on "inited" bit */ #define NGM_HCI_NODE_INIT 101 /* User -> HCI */ /* No parameters */ /* Get/Set node debug level (see debug levels above) */ #define NGM_HCI_NODE_GET_DEBUG 102 /* HCI -> User */ #define NGM_HCI_NODE_SET_DEBUG 103 /* User -> HCI */ typedef u_int16_t ng_hci_node_debug_ep; /* Get node buffer info */ #define NGM_HCI_NODE_GET_BUFFER 104 /* HCI -> User */ typedef struct { u_int8_t cmd_free; /* number of free command packets */ u_int8_t sco_size; /* max. size of SCO packet */ u_int16_t sco_pkts; /* number of SCO packets */ u_int16_t sco_free; /* number of free SCO packets */ u_int16_t acl_size; /* max. size of ACL packet */ u_int16_t acl_pkts; /* number of ACL packets */ u_int16_t acl_free; /* number of free ACL packets */ } ng_hci_node_buffer_ep; /* Get BDADDR */ #define NGM_HCI_NODE_GET_BDADDR 105 /* HCI -> User */ /* bdaddr_t -- BDADDR */ /* Get features */ #define NGM_HCI_NODE_GET_FEATURES 106 /* HCI -> User */ /* features[NG_HCI_FEATURES_SIZE] -- features */ #define NGM_HCI_NODE_GET_STAT 107 /* HCI -> User */ typedef struct { u_int32_t cmd_sent; /* number of HCI commands sent */ u_int32_t evnt_recv; /* number of HCI events received */ u_int32_t acl_recv; /* number of ACL packets received */ u_int32_t acl_sent; /* number of ACL packets sent */ u_int32_t sco_recv; /* number of SCO packets received */ u_int32_t sco_sent; /* number of SCO packets sent */ u_int32_t bytes_recv; /* total number of bytes received */ u_int32_t bytes_sent; /* total number of bytes sent */ } ng_hci_node_stat_ep; #define NGM_HCI_NODE_RESET_STAT 108 /* User -> HCI */ /* No parameters */ #define NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE 109 /* User -> HCI */ #define NGM_HCI_NODE_GET_NEIGHBOR_CACHE 110 /* HCI -> User */ typedef struct { u_int32_t num_entries; /* number of entries */ } ng_hci_node_get_neighbor_cache_ep; typedef struct { u_int16_t page_scan_rep_mode; /* page rep scan mode */ u_int16_t page_scan_mode; /* page scan mode */ u_int16_t clock_offset; /* clock offset */ bdaddr_t bdaddr; /* bdaddr */ u_int8_t features[NG_HCI_FEATURES_SIZE]; /* features */ } ng_hci_node_neighbor_cache_entry_ep; #define NG_HCI_MAX_NEIGHBOR_NUM \ ((0xffff - sizeof(ng_hci_node_get_neighbor_cache_ep))/sizeof(ng_hci_node_neighbor_cache_entry_ep)) #define NGM_HCI_NODE_GET_CON_LIST 111 /* HCI -> User */ typedef struct { u_int32_t num_connections; /* number of connections */ } ng_hci_node_con_list_ep; typedef struct { u_int8_t link_type; /* ACL or SCO */ u_int8_t encryption_mode; /* none, p2p, ... */ u_int8_t mode; /* ACTIVE, HOLD ... */ u_int8_t role; /* MASTER/SLAVE */ u_int16_t state; /* connection state */ u_int16_t reserved; /* place holder */ u_int16_t pending; /* number of pending packets */ u_int16_t queue_len; /* number of packets in queue */ u_int16_t con_handle; /* connection handle */ bdaddr_t bdaddr; /* remote bdaddr */ } ng_hci_node_con_ep; #define NG_HCI_MAX_CON_NUM \ ((0xffff - sizeof(ng_hci_node_con_list_ep))/sizeof(ng_hci_node_con_ep)) #define NGM_HCI_NODE_UP 112 /* HCI -> Upper */ typedef struct { u_int16_t pkt_size; /* max. ACL/SCO packet size (w/out header) */ u_int16_t num_pkts; /* ACL/SCO packet queue size */ u_int16_t reserved; /* place holder */ bdaddr_t bdaddr; /* bdaddr */ } ng_hci_node_up_ep; #define NGM_HCI_SYNC_CON_QUEUE 113 /* HCI -> Upper */ typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t completed; /* number of completed packets */ } ng_hci_sync_con_queue_ep; #define NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK 114 /* HCI -> User */ #define NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK 115 /* User -> HCI */ typedef u_int16_t ng_hci_node_link_policy_mask_ep; #define NGM_HCI_NODE_GET_PACKET_MASK 116 /* HCI -> User */ #define NGM_HCI_NODE_SET_PACKET_MASK 117 /* User -> HCI */ typedef u_int16_t ng_hci_node_packet_mask_ep; #define NGM_HCI_NODE_GET_ROLE_SWITCH 118 /* HCI -> User */ #define NGM_HCI_NODE_SET_ROLE_SWITCH 119 /* User -> HCI */ typedef u_int16_t ng_hci_node_role_switch_ep; #define NGM_HCI_NODE_LIST_NAMES 200 /* HCI -> User */ /************************************************************************** ************************************************************************** ** Link control commands and return parameters ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_LINK_CONTROL 0x01 /* OpCode Group Field */ #define NG_HCI_OCF_INQUIRY 0x0001 typedef struct { u_int8_t lap[NG_HCI_LAP_SIZE]; /* LAP */ u_int8_t inquiry_length; /* (N x 1.28) sec */ u_int8_t num_responses; /* Max. # of responses before halted */ } __attribute__ ((packed)) ng_hci_inquiry_cp; /* No return parameter(s) */ #define NG_HCI_OCF_INQUIRY_CANCEL 0x0002 /* No command parameter(s) */ typedef ng_hci_status_rp ng_hci_inquiry_cancel_rp; #define NG_HCI_OCF_PERIODIC_INQUIRY 0x0003 typedef struct { u_int16_t max_period_length; /* Max. and min. amount of time */ u_int16_t min_period_length; /* between consecutive inquiries */ u_int8_t lap[NG_HCI_LAP_SIZE]; /* LAP */ u_int8_t inquiry_length; /* (inquiry_length * 1.28) sec */ u_int8_t num_responses; /* Max. # of responses */ } __attribute__ ((packed)) ng_hci_periodic_inquiry_cp; typedef ng_hci_status_rp ng_hci_periodic_inquiry_rp; #define NG_HCI_OCF_EXIT_PERIODIC_INQUIRY 0x0004 /* No command parameter(s) */ typedef ng_hci_status_rp ng_hci_exit_periodic_inquiry_rp; #define NG_HCI_OCF_CREATE_CON 0x0005 typedef struct { bdaddr_t bdaddr; /* destination address */ u_int16_t pkt_type; /* packet type */ u_int8_t page_scan_rep_mode; /* page scan repetition mode */ u_int8_t page_scan_mode; /* page scan mode */ u_int16_t clock_offset; /* clock offset */ u_int8_t accept_role_switch; /* accept role switch? 0x00 - no */ } __attribute__ ((packed)) ng_hci_create_con_cp; /* No return parameter(s) */ #define NG_HCI_OCF_DISCON 0x0006 typedef struct { u_int16_t con_handle; /* connection handle */ u_int8_t reason; /* reason to disconnect */ } __attribute__ ((packed)) ng_hci_discon_cp; /* No return parameter(s) */ #define NG_HCI_OCF_ADD_SCO_CON 0x0007 typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t pkt_type; /* packet type */ } __attribute__ ((packed)) ng_hci_add_sco_con_cp; /* No return parameter(s) */ #define NG_HCI_OCF_ACCEPT_CON 0x0009 typedef struct { bdaddr_t bdaddr; /* address of unit to be connected */ u_int8_t role; /* connection role */ } __attribute__ ((packed)) ng_hci_accept_con_cp; /* No return parameter(s) */ #define NG_HCI_OCF_REJECT_CON 0x000a typedef struct { bdaddr_t bdaddr; /* remote address */ u_int8_t reason; /* reason to reject */ } __attribute__ ((packed)) ng_hci_reject_con_cp; /* No return parameter(s) */ #define NG_HCI_OCF_LINK_KEY_REP 0x000b typedef struct { bdaddr_t bdaddr; /* remote address */ u_int8_t key[NG_HCI_KEY_SIZE]; /* key */ } __attribute__ ((packed)) ng_hci_link_key_rep_cp; typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* unit address */ } __attribute__ ((packed)) ng_hci_link_key_rep_rp; #define NG_HCI_OCF_LINK_KEY_NEG_REP 0x000c typedef struct { bdaddr_t bdaddr; /* remote address */ } __attribute__ ((packed)) ng_hci_link_key_neg_rep_cp; typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* unit address */ } __attribute__ ((packed)) ng_hci_link_key_neg_rep_rp; #define NG_HCI_OCF_PIN_CODE_REP 0x000d typedef struct { bdaddr_t bdaddr; /* remote address */ u_int8_t pin_size; /* pin code length (in bytes) */ u_int8_t pin[NG_HCI_PIN_SIZE]; /* pin code */ } __attribute__ ((packed)) ng_hci_pin_code_rep_cp; typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* unit address */ } __attribute__ ((packed)) ng_hci_pin_code_rep_rp; #define NG_HCI_OCF_PIN_CODE_NEG_REP 0x000e typedef struct { bdaddr_t bdaddr; /* remote address */ } __attribute__ ((packed)) ng_hci_pin_code_neg_rep_cp; typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* unit address */ } __attribute__ ((packed)) ng_hci_pin_code_neg_rep_rp; #define NG_HCI_OCF_CHANGE_CON_PKT_TYPE 0x000f typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t pkt_type; /* packet type */ } __attribute__ ((packed)) ng_hci_change_con_pkt_type_cp; /* No return parameter(s) */ #define NG_HCI_OCF_AUTH_REQ 0x0011 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_auth_req_cp; /* No return parameter(s) */ #define NG_HCI_OCF_SET_CON_ENCRYPTION 0x0013 typedef struct { u_int16_t con_handle; /* connection handle */ u_int8_t encryption_enable; /* 0x00 - disable, 0x01 - enable */ } __attribute__ ((packed)) ng_hci_set_con_encryption_cp; /* No return parameter(s) */ #define NG_HCI_OCF_CHANGE_CON_LINK_KEY 0x0015 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_change_con_link_key_cp; /* No return parameter(s) */ #define NG_HCI_OCF_MASTER_LINK_KEY 0x0017 typedef struct { u_int8_t key_flag; /* key flag */ } __attribute__ ((packed)) ng_hci_master_link_key_cp; /* No return parameter(s) */ #define NG_HCI_OCF_REMOTE_NAME_REQ 0x0019 typedef struct { bdaddr_t bdaddr; /* remote address */ u_int8_t page_scan_rep_mode; /* page scan repetition mode */ u_int8_t page_scan_mode; /* page scan mode */ u_int16_t clock_offset; /* clock offset */ } __attribute__ ((packed)) ng_hci_remote_name_req_cp; /* No return parameter(s) */ #define NG_HCI_OCF_READ_REMOTE_FEATURES 0x001b typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_remote_features_cp; /* No return parameter(s) */ #define NG_HCI_OCF_READ_REMOTE_VER_INFO 0x001d typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_remote_ver_info_cp; /* No return parameter(s) */ #define NG_HCI_OCF_READ_CLOCK_OFFSET 0x001f typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_clock_offset_cp; /* No return parameter(s) */ /************************************************************************** ************************************************************************** ** Link policy commands and return parameters ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_LINK_POLICY 0x02 /* OpCode Group Field */ #define NG_HCI_OCF_HOLD_MODE 0x0001 typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t max_interval; /* (max_interval * 0.625) msec */ u_int16_t min_interval; /* (max_interval * 0.625) msec */ } __attribute__ ((packed)) ng_hci_hold_mode_cp; /* No return parameter(s) */ #define NG_HCI_OCF_SNIFF_MODE 0x0003 typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t max_interval; /* (max_interval * 0.625) msec */ u_int16_t min_interval; /* (max_interval * 0.625) msec */ u_int16_t attempt; /* (2 * attempt - 1) * 0.625 msec */ u_int16_t timeout; /* (2 * attempt - 1) * 0.625 msec */ } __attribute__ ((packed)) ng_hci_sniff_mode_cp; /* No return parameter(s) */ #define NG_HCI_OCF_EXIT_SNIFF_MODE 0x0004 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_exit_sniff_mode_cp; /* No return parameter(s) */ #define NG_HCI_OCF_PARK_MODE 0x0005 typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t max_interval; /* (max_interval * 0.625) msec */ u_int16_t min_interval; /* (max_interval * 0.625) msec */ } __attribute__ ((packed)) ng_hci_park_mode_cp; /* No return parameter(s) */ #define NG_HCI_OCF_EXIT_PARK_MODE 0x0006 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_exit_park_mode_cp; /* No return parameter(s) */ #define NG_HCI_OCF_QOS_SETUP 0x0007 typedef struct { u_int16_t con_handle; /* connection handle */ u_int8_t flags; /* reserved for future use */ u_int8_t service_type; /* service type */ u_int32_t token_rate; /* bytes per second */ u_int32_t peak_bandwidth; /* bytes per second */ u_int32_t latency; /* microseconds */ u_int32_t delay_variation; /* microseconds */ } __attribute__ ((packed)) ng_hci_qos_setup_cp; /* No return parameter(s) */ #define NG_HCI_OCF_ROLE_DISCOVERY 0x0009 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_role_discovery_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int8_t role; /* role for the connection handle */ } __attribute__ ((packed)) ng_hci_role_discovery_rp; #define NG_HCI_OCF_SWITCH_ROLE 0x000b typedef struct { bdaddr_t bdaddr; /* remote address */ u_int8_t role; /* new local role */ } __attribute__ ((packed)) ng_hci_switch_role_cp; /* No return parameter(s) */ #define NG_HCI_OCF_READ_LINK_POLICY_SETTINGS 0x000c typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_link_policy_settings_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int16_t settings; /* link policy settings */ } __attribute__ ((packed)) ng_hci_read_link_policy_settings_rp; #define NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS 0x000d typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t settings; /* link policy settings */ } __attribute__ ((packed)) ng_hci_write_link_policy_settings_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_write_link_policy_settings_rp; /************************************************************************** ************************************************************************** ** Host controller and baseband commands and return parameters ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_HC_BASEBAND 0x03 /* OpCode Group Field */ #define NG_HCI_OCF_SET_EVENT_MASK 0x0001 typedef struct { u_int8_t event_mask[NG_HCI_EVENT_MASK_SIZE]; /* event_mask */ } __attribute__ ((packed)) ng_hci_set_event_mask_cp; typedef ng_hci_status_rp ng_hci_set_event_mask_rp; #define NG_HCI_EVENT_MASK_DEFAULT 0x1fffffffffff #define NG_HCI_EVENT_MASK_LE 0x2000000000000000 #define NG_HCI_OCF_RESET 0x0003 /* No command parameter(s) */ typedef ng_hci_status_rp ng_hci_reset_rp; #define NG_HCI_OCF_SET_EVENT_FILTER 0x0005 typedef struct { u_int8_t filter_type; /* filter type */ u_int8_t filter_condition_type; /* filter condition type */ u_int8_t condition[0]; /* conditions - variable size */ } __attribute__ ((packed)) ng_hci_set_event_filter_cp; typedef ng_hci_status_rp ng_hci_set_event_filter_rp; #define NG_HCI_OCF_FLUSH 0x0008 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_flush_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_flush_rp; #define NG_HCI_OCF_READ_PIN_TYPE 0x0009 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t pin_type; /* PIN type */ } __attribute__ ((packed)) ng_hci_read_pin_type_rp; #define NG_HCI_OCF_WRITE_PIN_TYPE 0x000a typedef struct { u_int8_t pin_type; /* PIN type */ } __attribute__ ((packed)) ng_hci_write_pin_type_cp; typedef ng_hci_status_rp ng_hci_write_pin_type_rp; #define NG_HCI_OCF_CREATE_NEW_UNIT_KEY 0x000b /* No command parameter(s) */ typedef ng_hci_status_rp ng_hci_create_new_unit_key_rp; #define NG_HCI_OCF_READ_STORED_LINK_KEY 0x000d typedef struct { bdaddr_t bdaddr; /* address */ u_int8_t read_all; /* read all keys? 0x01 - yes */ } __attribute__ ((packed)) ng_hci_read_stored_link_key_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t max_num_keys; /* Max. number of keys */ u_int16_t num_keys_read; /* Number of stored keys */ } __attribute__ ((packed)) ng_hci_read_stored_link_key_rp; #define NG_HCI_OCF_WRITE_STORED_LINK_KEY 0x0011 typedef struct { u_int8_t num_keys_write; /* # of keys to write */ /* these are repeated "num_keys_write" times bdaddr_t bdaddr; --- remote address(es) u_int8_t key[NG_HCI_KEY_SIZE]; --- key(s) */ } __attribute__ ((packed)) ng_hci_write_stored_link_key_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t num_keys_written; /* # of keys successfully written */ } __attribute__ ((packed)) ng_hci_write_stored_link_key_rp; #define NG_HCI_OCF_DELETE_STORED_LINK_KEY 0x0012 typedef struct { bdaddr_t bdaddr; /* address */ u_int8_t delete_all; /* delete all keys? 0x01 - yes */ } __attribute__ ((packed)) ng_hci_delete_stored_link_key_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t num_keys_deleted; /* Number of keys deleted */ } __attribute__ ((packed)) ng_hci_delete_stored_link_key_rp; #define NG_HCI_OCF_CHANGE_LOCAL_NAME 0x0013 typedef struct { char name[NG_HCI_UNIT_NAME_SIZE]; /* new unit name */ } __attribute__ ((packed)) ng_hci_change_local_name_cp; typedef ng_hci_status_rp ng_hci_change_local_name_rp; #define NG_HCI_OCF_READ_LOCAL_NAME 0x0014 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ char name[NG_HCI_UNIT_NAME_SIZE]; /* unit name */ } __attribute__ ((packed)) ng_hci_read_local_name_rp; #define NG_HCI_OCF_READ_CON_ACCEPT_TIMO 0x0015 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t timeout; /* (timeout * 0.625) msec */ } __attribute__ ((packed)) ng_hci_read_con_accept_timo_rp; #define NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO 0x0016 typedef struct { u_int16_t timeout; /* (timeout * 0.625) msec */ } __attribute__ ((packed)) ng_hci_write_con_accept_timo_cp; typedef ng_hci_status_rp ng_hci_write_con_accept_timo_rp; #define NG_HCI_OCF_READ_PAGE_TIMO 0x0017 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t timeout; /* (timeout * 0.625) msec */ } __attribute__ ((packed)) ng_hci_read_page_timo_rp; #define NG_HCI_OCF_WRITE_PAGE_TIMO 0x0018 typedef struct { u_int16_t timeout; /* (timeout * 0.625) msec */ } __attribute__ ((packed)) ng_hci_write_page_timo_cp; typedef ng_hci_status_rp ng_hci_write_page_timo_rp; #define NG_HCI_OCF_READ_SCAN_ENABLE 0x0019 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t scan_enable; /* Scan enable */ } __attribute__ ((packed)) ng_hci_read_scan_enable_rp; #define NG_HCI_OCF_WRITE_SCAN_ENABLE 0x001a typedef struct { u_int8_t scan_enable; /* Scan enable */ } __attribute__ ((packed)) ng_hci_write_scan_enable_cp; typedef ng_hci_status_rp ng_hci_write_scan_enable_rp; #define NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY 0x001b /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t page_scan_interval; /* interval * 0.625 msec */ u_int16_t page_scan_window; /* window * 0.625 msec */ } __attribute__ ((packed)) ng_hci_read_page_scan_activity_rp; #define NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY 0x001c typedef struct { u_int16_t page_scan_interval; /* interval * 0.625 msec */ u_int16_t page_scan_window; /* window * 0.625 msec */ } __attribute__ ((packed)) ng_hci_write_page_scan_activity_cp; typedef ng_hci_status_rp ng_hci_write_page_scan_activity_rp; #define NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY 0x001d /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t inquiry_scan_interval; /* interval * 0.625 msec */ u_int16_t inquiry_scan_window; /* window * 0.625 msec */ } __attribute__ ((packed)) ng_hci_read_inquiry_scan_activity_rp; #define NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY 0x001e typedef struct { u_int16_t inquiry_scan_interval; /* interval * 0.625 msec */ u_int16_t inquiry_scan_window; /* window * 0.625 msec */ } __attribute__ ((packed)) ng_hci_write_inquiry_scan_activity_cp; typedef ng_hci_status_rp ng_hci_write_inquiry_scan_activity_rp; #define NG_HCI_OCF_READ_AUTH_ENABLE 0x001f /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t auth_enable; /* 0x01 - enabled */ } __attribute__ ((packed)) ng_hci_read_auth_enable_rp; #define NG_HCI_OCF_WRITE_AUTH_ENABLE 0x0020 typedef struct { u_int8_t auth_enable; /* 0x01 - enabled */ } __attribute__ ((packed)) ng_hci_write_auth_enable_cp; typedef ng_hci_status_rp ng_hci_write_auth_enable_rp; #define NG_HCI_OCF_READ_ENCRYPTION_MODE 0x0021 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t encryption_mode; /* encryption mode */ } __attribute__ ((packed)) ng_hci_read_encryption_mode_rp; #define NG_HCI_OCF_WRITE_ENCRYPTION_MODE 0x0022 typedef struct { u_int8_t encryption_mode; /* encryption mode */ } __attribute__ ((packed)) ng_hci_write_encryption_mode_cp; typedef ng_hci_status_rp ng_hci_write_encryption_mode_rp; #define NG_HCI_OCF_READ_UNIT_CLASS 0x0023 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* unit class */ } __attribute__ ((packed)) ng_hci_read_unit_class_rp; #define NG_HCI_OCF_WRITE_UNIT_CLASS 0x0024 typedef struct { u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* unit class */ } __attribute__ ((packed)) ng_hci_write_unit_class_cp; typedef ng_hci_status_rp ng_hci_write_unit_class_rp; #define NG_HCI_OCF_READ_VOICE_SETTINGS 0x0025 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t settings; /* voice settings */ } __attribute__ ((packed)) ng_hci_read_voice_settings_rp; #define NG_HCI_OCF_WRITE_VOICE_SETTINGS 0x0026 typedef struct { u_int16_t settings; /* voice settings */ } __attribute__ ((packed)) ng_hci_write_voice_settings_cp; typedef ng_hci_status_rp ng_hci_write_voice_settings_rp; #define NG_HCI_OCF_READ_AUTO_FLUSH_TIMO 0x0027 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_auto_flush_timo_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int16_t timeout; /* 0x00 - no flush, timeout * 0.625 msec */ } __attribute__ ((packed)) ng_hci_read_auto_flush_timo_rp; #define NG_HCI_OCF_WRITE_AUTO_FLUSH_TIMO 0x0028 typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t timeout; /* 0x00 - no flush, timeout * 0.625 msec */ } __attribute__ ((packed)) ng_hci_write_auto_flush_timo_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_write_auto_flush_timo_rp; #define NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS 0x0029 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t counter; /* number of broadcast retransmissions */ } __attribute__ ((packed)) ng_hci_read_num_broadcast_retrans_rp; #define NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS 0x002a typedef struct { u_int8_t counter; /* number of broadcast retransmissions */ } __attribute__ ((packed)) ng_hci_write_num_broadcast_retrans_cp; typedef ng_hci_status_rp ng_hci_write_num_broadcast_retrans_rp; #define NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY 0x002b /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t hold_mode_activity; /* Hold mode activities */ } __attribute__ ((packed)) ng_hci_read_hold_mode_activity_rp; #define NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY 0x002c typedef struct { u_int8_t hold_mode_activity; /* Hold mode activities */ } __attribute__ ((packed)) ng_hci_write_hold_mode_activity_cp; typedef ng_hci_status_rp ng_hci_write_hold_mode_activity_rp; #define NG_HCI_OCF_READ_XMIT_LEVEL 0x002d typedef struct { u_int16_t con_handle; /* connection handle */ u_int8_t type; /* Xmit level type */ } __attribute__ ((packed)) ng_hci_read_xmit_level_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ char level; /* -30 <= level <= 30 dBm */ } __attribute__ ((packed)) ng_hci_read_xmit_level_rp; #define NG_HCI_OCF_READ_SCO_FLOW_CONTROL 0x002e /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t flow_control; /* 0x00 - disabled */ } __attribute__ ((packed)) ng_hci_read_sco_flow_control_rp; #define NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL 0x002f typedef struct { u_int8_t flow_control; /* 0x00 - disabled */ } __attribute__ ((packed)) ng_hci_write_sco_flow_control_cp; typedef ng_hci_status_rp ng_hci_write_sco_flow_control_rp; #define NG_HCI_OCF_H2HC_FLOW_CONTROL 0x0031 typedef struct { u_int8_t h2hc_flow; /* Host to Host controller flow control */ } __attribute__ ((packed)) ng_hci_h2hc_flow_control_cp; typedef ng_hci_status_rp ng_hci_h2hc_flow_control_rp; #define NG_HCI_OCF_HOST_BUFFER_SIZE 0x0033 typedef struct { u_int16_t max_acl_size; /* Max. size of ACL packet (bytes) */ u_int8_t max_sco_size; /* Max. size of SCO packet (bytes) */ u_int16_t num_acl_pkt; /* Max. number of ACL packets */ u_int16_t num_sco_pkt; /* Max. number of SCO packets */ } __attribute__ ((packed)) ng_hci_host_buffer_size_cp; typedef ng_hci_status_rp ng_hci_host_buffer_size_rp; #define NG_HCI_OCF_HOST_NUM_COMPL_PKTS 0x0035 typedef struct { u_int8_t num_con_handles; /* # of connection handles */ /* these are repeated "num_con_handles" times u_int16_t con_handle; --- connection handle(s) u_int16_t compl_pkt; --- # of completed packets */ } __attribute__ ((packed)) ng_hci_host_num_compl_pkts_cp; /* No return parameter(s) */ #define NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO 0x0036 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_link_supervision_timo_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int16_t timeout; /* Link supervision timeout * 0.625 msec */ } __attribute__ ((packed)) ng_hci_read_link_supervision_timo_rp; #define NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO 0x0037 typedef struct { u_int16_t con_handle; /* connection handle */ u_int16_t timeout; /* Link supervision timeout * 0.625 msec */ } __attribute__ ((packed)) ng_hci_write_link_supervision_timo_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_write_link_supervision_timo_rp; #define NG_HCI_OCF_READ_SUPPORTED_IAC_NUM 0x0038 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t num_iac; /* # of supported IAC during scan */ } __attribute__ ((packed)) ng_hci_read_supported_iac_num_rp; #define NG_HCI_OCF_READ_IAC_LAP 0x0039 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t num_iac; /* # of IAC */ /* these are repeated "num_iac" times u_int8_t laps[NG_HCI_LAP_SIZE]; --- LAPs */ } __attribute__ ((packed)) ng_hci_read_iac_lap_rp; #define NG_HCI_OCF_WRITE_IAC_LAP 0x003a typedef struct { u_int8_t num_iac; /* # of IAC */ /* these are repeated "num_iac" times u_int8_t laps[NG_HCI_LAP_SIZE]; --- LAPs */ } __attribute__ ((packed)) ng_hci_write_iac_lap_cp; typedef ng_hci_status_rp ng_hci_write_iac_lap_rp; /*0x003b-0x003e commands are depricated v2.0 or later*/ #define NG_HCI_OCF_READ_PAGE_SCAN_PERIOD 0x003b /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t page_scan_period_mode; /* Page scan period mode */ } __attribute__ ((packed)) ng_hci_read_page_scan_period_rp; #define NG_HCI_OCF_WRITE_PAGE_SCAN_PERIOD 0x003c typedef struct { u_int8_t page_scan_period_mode; /* Page scan period mode */ } __attribute__ ((packed)) ng_hci_write_page_scan_period_cp; typedef ng_hci_status_rp ng_hci_write_page_scan_period_rp; #define NG_HCI_OCF_READ_PAGE_SCAN 0x003d /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t page_scan_mode; /* Page scan mode */ } __attribute__ ((packed)) ng_hci_read_page_scan_rp; #define NG_HCI_OCF_WRITE_PAGE_SCAN 0x003e typedef struct { u_int8_t page_scan_mode; /* Page scan mode */ } __attribute__ ((packed)) ng_hci_write_page_scan_cp; typedef ng_hci_status_rp ng_hci_write_page_scan_rp; #define NG_HCI_OCF_READ_LE_HOST_SUPPORTED 0x6c typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t le_supported_host ;/* LE host supported?*/ u_int8_t simultaneous_le_host; /* BR/LE simulateneous? */ } __attribute__ ((packed)) ng_hci_read_le_host_supported_rp; #define NG_HCI_OCF_WRITE_LE_HOST_SUPPORTED 0x6d typedef struct { u_int8_t le_supported_host; /* LE host supported?*/ u_int8_t simultaneous_le_host; /* LE host supported?*/ } __attribute__ ((packed)) ng_hci_write_le_host_supported_cp; typedef ng_hci_status_rp ng_hci_write_le_host_supported_rp; /************************************************************************** ************************************************************************** ** Informational commands and return parameters ** All commands in this category do not accept any parameters ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_INFO 0x04 /* OpCode Group Field */ #define NG_HCI_OCF_READ_LOCAL_VER 0x0001 typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t hci_version; /* HCI version */ u_int16_t hci_revision; /* HCI revision */ u_int8_t lmp_version; /* LMP version */ u_int16_t manufacturer; /* Hardware manufacturer name */ u_int16_t lmp_subversion; /* LMP sub-version */ } __attribute__ ((packed)) ng_hci_read_local_ver_rp; #define NG_HCI_OCF_READ_LOCAL_COMMANDS 0x0002 typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t features[NG_HCI_COMMANDS_SIZE]; /* command bitmsk*/ } __attribute__ ((packed)) ng_hci_read_local_commands_rp; #define NG_HCI_OCF_READ_LOCAL_FEATURES 0x0003 typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t features[NG_HCI_FEATURES_SIZE]; /* LMP features bitmsk*/ } __attribute__ ((packed)) ng_hci_read_local_features_rp; #define NG_HCI_OCF_READ_BUFFER_SIZE 0x0005 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t max_acl_size; /* Max. size of ACL packet (bytes) */ u_int8_t max_sco_size; /* Max. size of SCO packet (bytes) */ u_int16_t num_acl_pkt; /* Max. number of ACL packets */ u_int16_t num_sco_pkt; /* Max. number of SCO packets */ } __attribute__ ((packed)) ng_hci_read_buffer_size_rp; #define NG_HCI_OCF_READ_COUNTRY_CODE 0x0007 typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t country_code; /* 0x00 - NAM, EUR, JP; 0x01 - France */ } __attribute__ ((packed)) ng_hci_read_country_code_rp; #define NG_HCI_OCF_READ_BDADDR 0x0009 typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* unit address */ } __attribute__ ((packed)) ng_hci_read_bdaddr_rp; /************************************************************************** ************************************************************************** ** Status commands and return parameters ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_STATUS 0x05 /* OpCode Group Field */ #define NG_HCI_OCF_READ_FAILED_CONTACT_CNTR 0x0001 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_failed_contact_cntr_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int16_t counter; /* number of consecutive failed contacts */ } __attribute__ ((packed)) ng_hci_read_failed_contact_cntr_rp; #define NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR 0x0002 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_reset_failed_contact_cntr_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_reset_failed_contact_cntr_rp; #define NG_HCI_OCF_GET_LINK_QUALITY 0x0003 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_get_link_quality_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int8_t quality; /* higher value means better quality */ } __attribute__ ((packed)) ng_hci_get_link_quality_rp; #define NG_HCI_OCF_READ_RSSI 0x0005 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_read_rssi_cp; typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ char rssi; /* -127 <= rssi <= 127 dB */ } __attribute__ ((packed)) ng_hci_read_rssi_rp; /************************************************************************** ************************************************************************** ** Testing commands and return parameters ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_TESTING 0x06 /* OpCode Group Field */ #define NG_HCI_OCF_READ_LOOPBACK_MODE 0x0001 /* No command parameter(s) */ typedef struct { u_int8_t status; /* 0x00 - success */ u_int8_t lbmode; /* loopback mode */ } __attribute__ ((packed)) ng_hci_read_loopback_mode_rp; #define NG_HCI_OCF_WRITE_LOOPBACK_MODE 0x0002 typedef struct { u_int8_t lbmode; /* loopback mode */ } __attribute__ ((packed)) ng_hci_write_loopback_mode_cp; typedef ng_hci_status_rp ng_hci_write_loopback_mode_rp; #define NG_HCI_OCF_ENABLE_UNIT_UNDER_TEST 0x0003 /* No command parameter(s) */ typedef ng_hci_status_rp ng_hci_enable_unit_under_test_rp; /************************************************************************** ************************************************************************** ** LE OpCode group field ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_LE 0x08 /* OpCode Group Field */ #define NG_HCI_OCF_LE_SET_EVENT_MASK 0x0001 typedef struct { u_int8_t event_mask[NG_HCI_LE_EVENT_MASK_SIZE]; /* event_mask*/ } __attribute__ ((packed)) ng_hci_le_set_event_mask_cp; typedef ng_hci_status_rp ng_hci_le_set_event_mask_rp; #define NG_HCI_LE_EVENT_MASK_ALL 0x1f #define NG_HCI_OCF_LE_READ_BUFFER_SIZE 0x0002 /*No command parameter */ typedef struct { u_int8_t status; /*status*/ u_int16_t hc_le_data_packet_length; u_int8_t hc_total_num_le_data_packets; } __attribute__ ((packed)) ng_hci_le_read_buffer_size_rp; #define NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES 0x0003 /*No command parameter */ typedef struct { u_int8_t status; /*status*/ u_int64_t le_features; } __attribute__ ((packed)) ng_hci_le_read_local_supported_features_rp; #define NG_HCI_OCF_LE_SET_RANDOM_ADDRESS 0x0005 typedef struct { bdaddr_t random_address; } __attribute__ ((packed)) ng_hci_le_set_random_address_cp_; typedef ng_hci_status_rp ng_hci_le_set_random_address_rp; #define NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006 typedef struct { u_int16_t advertising_interval_min; u_int16_t advertising_interval_max; u_int8_t advertising_type; u_int8_t own_address_type; u_int8_t direct_address_type; bdaddr_t direct_address; u_int8_t advertising_channel_map; u_int8_t advertising_filter_policy; } __attribute__ ((packed)) ng_hci_le_set_advertising_parameters_cp; typedef ng_hci_status_rp ng_hci_le_set_advertising_parameters_rp; #define NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER 0x0007 /*No command parameter*/ typedef struct { u_int8_t status; u_int8_t transmit_power_level; } __attribute__ ((packed)) ng_hci_le_read_advertising_channel_tx_power_rp; #define NG_HCI_OCF_LE_SET_ADVERTISING_DATA 0x0008 #define NG_HCI_ADVERTISING_DATA_SIZE 31 typedef struct { u_int8_t advertising_data_length; char advertising_data[NG_HCI_ADVERTISING_DATA_SIZE]; } __attribute__ ((packed)) ng_hci_le_set_advertising_data_cp; typedef ng_hci_status_rp ng_hci_le_set_advertising_data_rp; #define NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009 typedef struct { u_int8_t scan_response_data_length; char scan_response_data[NG_HCI_ADVERTISING_DATA_SIZE]; } __attribute__ ((packed)) ng_hci_le_set_scan_response_data_cp; typedef ng_hci_status_rp ng_hci_le_set_scan_response_data_rp; #define NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE 0x000a typedef struct { u_int8_t advertising_enable; }__attribute__ ((packed)) ng_hci_le_set_advertise_enable_cp; typedef ng_hci_status_rp ng_hci_le_set_advertise_enable_rp; #define NG_HCI_OCF_LE_SET_SCAN_PARAMETERS 0x000b typedef struct { u_int8_t le_scan_type; u_int16_t le_scan_interval; u_int16_t le_scan_window; u_int8_t own_address_type; u_int8_t scanning_filter_policy; }__attribute__ ((packed)) ng_hci_le_set_scan_parameters_cp; typedef ng_hci_status_rp ng_hci_le_set_scan_parameters_rp; #define NG_HCI_OCF_LE_SET_SCAN_ENABLE 0x000c typedef struct { u_int8_t le_scan_enable; u_int8_t filter_duplicates; }__attribute__ ((packed)) ng_hci_le_set_scan_enable_cp; typedef ng_hci_status_rp ng_hci_le_set_scan_enable_rp; #define NG_HCI_OCF_LE_CREATE_CONNECTION 0x000d typedef struct { u_int16_t scan_interval; u_int16_t scan_window; u_int8_t filter_policy; u_int8_t peer_addr_type; bdaddr_t peer_addr; u_int8_t own_address_type; u_int16_t conn_interval_min; u_int16_t conn_interval_max; u_int16_t conn_latency; u_int16_t supervision_timeout; u_int16_t min_ce_length; u_int16_t max_ce_length; }__attribute__((packed)) ng_hci_le_create_connection_cp; /* no return paramters*/ #define NG_HCI_OCF_LE_CREATE_CONNECTION_CANCEL 0x000e /*No command parameter*/ typedef ng_hci_status_rp ng_hci_le_create_connection_cancel_rp; #define NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE 0x000f /*No command parameter*/ typedef struct { u_int8_t status; u_int8_t white_list_size; } __attribute__ ((packed)) ng_hci_le_read_white_list_size_rp; #define NG_HCI_OCF_LE_CLEAR_WHITE_LIST 0x0010 /*No command paramters*/ typedef ng_hci_status_rp ng_hci_le_clear_white_list_rp; #define NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST 0x0011 typedef struct { u_int8_t address_type; bdaddr_t address; } __attribute__ ((packed)) ng_hci_le_add_device_to_white_list_cp; typedef ng_hci_status_rp ng_hci_le_add_device_to_white_list_rp; #define NG_HCI_OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST 0x0012 typedef struct { u_int8_t address_type; bdaddr_t address; } __attribute__ ((packed)) ng_hci_le_remove_device_from_white_list_cp; typedef ng_hci_status_rp ng_hci_le_remove_device_from_white_list_rp; #define NG_HCI_OCF_LE_CONNECTION_UPDATE 0x0013 typedef struct { u_int16_t connection_handle; u_int16_t conn_interval_min; u_int16_t conn_interval_max; u_int16_t conn_latency; u_int16_t supervision_timeout; u_int16_t minimum_ce_length; u_int16_t maximum_ce_length; }__attribute__ ((packed)) ng_hci_le_connection_update_cp; /*no return parameter*/ #define NG_HCI_OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION 0x0014 typedef struct{ u_int8_t le_channel_map[5]; }__attribute__ ((packed)) ng_hci_le_set_host_channel_classification_cp; typedef ng_hci_status_rp ng_hci_le_set_host_channel_classification_rp; #define NG_HCI_OCF_LE_READ_CHANNEL_MAP 0x0015 typedef struct { u_int16_t connection_handle; }__attribute__ ((packed)) ng_hci_le_read_channel_map_cp; typedef struct { u_int8_t status; u_int16_t connection_handle; u_int8_t le_channel_map[5]; } __attribute__ ((packed)) ng_hci_le_read_channel_map_rp; #define NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES 0x0016 typedef struct { u_int16_t connection_handle; }__attribute__ ((packed)) ng_hci_le_read_remote_used_features_cp; /*No return parameter*/ #define NG_HCI_128BIT 16 #define NG_HCI_OCF_LE_ENCRYPT 0x0017 typedef struct { u_int8_t key[NG_HCI_128BIT]; u_int8_t plaintext_data[NG_HCI_128BIT]; }__attribute__ ((packed)) ng_hci_le_encrypt_cp; typedef struct { u_int8_t status; u_int8_t plaintext_data[NG_HCI_128BIT]; }__attribute__ ((packed)) ng_hci_le_encrypt_rp; #define NG_HCI_OCF_LE_RAND 0x0018 /*No command parameter*/ typedef struct { u_int8_t status; u_int64_t random_number; }__attribute__ ((packed)) ng_hci_le_rand_rp; #define NG_HCI_OCF_LE_START_ENCRYPTION 0x0019 typedef struct { u_int16_t connection_handle; u_int64_t random_number; u_int16_t encrypted_diversifier; u_int8_t long_term_key[NG_HCI_128BIT]; }__attribute__ ((packed)) ng_hci_le_start_encryption_cp; /*No return parameter*/ #define NG_HCI_OCF_LE_LONG_TERM_KEY_REQUEST_REPLY 0x001a typedef struct { u_int16_t connection_handle; u_int8_t long_term_key[NG_HCI_128BIT]; }__attribute__ ((packed)) ng_hci_le_long_term_key_request_reply_cp; typedef struct { u_int8_t status; u_int16_t connection_handle; }__attribute__ ((packed)) ng_hci_le_long_term_key_request_reply_rp; #define NG_HCI_OCF_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY 0x001b typedef struct{ u_int16_t connection_handle; }ng_hci_le_long_term_key_request_negative_reply_cp; typedef struct { u_int8_t status; u_int16_t connection_handle; }__attribute__ ((packed)) ng_hci_le_long_term_key_request_negative_reply_rp; #define NG_HCI_OCF_LE_READ_SUPPORTED_STATUS 0x001c /*No command parameter*/ typedef struct { u_int8_t status; u_int64_t le_status; }__attribute__ ((packed)) ng_hci_le_read_supported_status_rp; #define NG_HCI_OCF_LE_RECEIVER_TEST 0x001d typedef struct{ u_int8_t rx_frequency; } __attribute__((packed)) ng_le_receiver_test_cp; typedef ng_hci_status_rp ng_hci_le_receiver_test_rp; #define NG_HCI_OCF_LE_TRANSMITTER_TEST 0x001e typedef struct{ u_int8_t tx_frequency; u_int8_t length_of_test_data; u_int8_t packet_payload; } __attribute__((packed)) ng_le_transmitter_test_cp; typedef ng_hci_status_rp ng_hci_le_transmitter_test_rp; #define NG_HCI_OCF_LE_TEST_END 0x001f /*No command paramter*/ typedef struct { u_int8_t status; u_int16_t number_of_packets; }__attribute__ ((packed)) ng_hci_le_test_end_rp; /************************************************************************** ************************************************************************** ** Special HCI OpCode group field values ************************************************************************** **************************************************************************/ #define NG_HCI_OGF_BT_LOGO 0x3e #define NG_HCI_OGF_VENDOR 0x3f /************************************************************************** ************************************************************************** ** Events and event parameters ************************************************************************** **************************************************************************/ #define NG_HCI_EVENT_INQUIRY_COMPL 0x01 typedef struct { u_int8_t status; /* 0x00 - success */ } __attribute__ ((packed)) ng_hci_inquiry_compl_ep; #define NG_HCI_EVENT_INQUIRY_RESULT 0x02 typedef struct { u_int8_t num_responses; /* number of responses */ /* ng_hci_inquiry_response[num_responses] -- see below */ } __attribute__ ((packed)) ng_hci_inquiry_result_ep; typedef struct { bdaddr_t bdaddr; /* unit address */ u_int8_t page_scan_rep_mode; /* page scan rep. mode */ u_int8_t page_scan_period_mode; /* page scan period mode */ u_int8_t page_scan_mode; /* page scan mode */ u_int8_t uclass[NG_HCI_CLASS_SIZE];/* unit class */ u_int16_t clock_offset; /* clock offset */ } __attribute__ ((packed)) ng_hci_inquiry_response; #define NG_HCI_EVENT_CON_COMPL 0x03 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ bdaddr_t bdaddr; /* remote unit address */ u_int8_t link_type; /* Link type */ u_int8_t encryption_mode; /* Encryption mode */ } __attribute__ ((packed)) ng_hci_con_compl_ep; #define NG_HCI_EVENT_CON_REQ 0x04 typedef struct { bdaddr_t bdaddr; /* remote unit address */ u_int8_t uclass[NG_HCI_CLASS_SIZE]; /* remote unit class */ u_int8_t link_type; /* link type */ } __attribute__ ((packed)) ng_hci_con_req_ep; #define NG_HCI_EVENT_DISCON_COMPL 0x05 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int8_t reason; /* reason to disconnect */ } __attribute__ ((packed)) ng_hci_discon_compl_ep; #define NG_HCI_EVENT_AUTH_COMPL 0x06 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_auth_compl_ep; #define NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL 0x7 typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* remote unit address */ char name[NG_HCI_UNIT_NAME_SIZE]; /* remote unit name */ } __attribute__ ((packed)) ng_hci_remote_name_req_compl_ep; #define NG_HCI_EVENT_ENCRYPTION_CHANGE 0x08 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ u_int8_t encryption_enable; /* 0x00 - disable */ } __attribute__ ((packed)) ng_hci_encryption_change_ep; #define NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL 0x09 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ } __attribute__ ((packed)) ng_hci_change_con_link_key_compl_ep; #define NG_HCI_EVENT_MASTER_LINK_KEY_COMPL 0x0a typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ u_int8_t key_flag; /* Key flag */ } __attribute__ ((packed)) ng_hci_master_link_key_compl_ep; #define NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL 0x0b typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ u_int8_t features[NG_HCI_FEATURES_SIZE]; /* LMP features bitmsk*/ } __attribute__ ((packed)) ng_hci_read_remote_features_compl_ep; #define NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL 0x0c typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ u_int8_t lmp_version; /* LMP version */ u_int16_t manufacturer; /* Hardware manufacturer name */ u_int16_t lmp_subversion; /* LMP sub-version */ } __attribute__ ((packed)) ng_hci_read_remote_ver_info_compl_ep; #define NG_HCI_EVENT_QOS_SETUP_COMPL 0x0d typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int8_t flags; /* reserved for future use */ u_int8_t service_type; /* service type */ u_int32_t token_rate; /* bytes per second */ u_int32_t peak_bandwidth; /* bytes per second */ u_int32_t latency; /* microseconds */ u_int32_t delay_variation; /* microseconds */ } __attribute__ ((packed)) ng_hci_qos_setup_compl_ep; #define NG_HCI_EVENT_COMMAND_COMPL 0x0e typedef struct { u_int8_t num_cmd_pkts; /* # of HCI command packets */ u_int16_t opcode; /* command OpCode */ /* command return parameters (if any) */ } __attribute__ ((packed)) ng_hci_command_compl_ep; #define NG_HCI_EVENT_COMMAND_STATUS 0x0f typedef struct { u_int8_t status; /* 0x00 - pending */ u_int8_t num_cmd_pkts; /* # of HCI command packets */ u_int16_t opcode; /* command OpCode */ } __attribute__ ((packed)) ng_hci_command_status_ep; #define NG_HCI_EVENT_HARDWARE_ERROR 0x10 typedef struct { u_int8_t hardware_code; /* hardware error code */ } __attribute__ ((packed)) ng_hci_hardware_error_ep; #define NG_HCI_EVENT_FLUSH_OCCUR 0x11 typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_flush_occur_ep; #define NG_HCI_EVENT_ROLE_CHANGE 0x12 typedef struct { u_int8_t status; /* 0x00 - success */ bdaddr_t bdaddr; /* address of remote unit */ u_int8_t role; /* new connection role */ } __attribute__ ((packed)) ng_hci_role_change_ep; #define NG_HCI_EVENT_NUM_COMPL_PKTS 0x13 typedef struct { u_int8_t num_con_handles; /* # of connection handles */ /* these are repeated "num_con_handles" times u_int16_t con_handle; --- connection handle(s) u_int16_t compl_pkt; --- # of completed packets */ } __attribute__ ((packed)) ng_hci_num_compl_pkts_ep; #define NG_HCI_EVENT_MODE_CHANGE 0x14 typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int8_t unit_mode; /* remote unit mode */ u_int16_t interval; /* interval * 0.625 msec */ } __attribute__ ((packed)) ng_hci_mode_change_ep; #define NG_HCI_EVENT_RETURN_LINK_KEYS 0x15 typedef struct { u_int8_t num_keys; /* # of keys */ /* these are repeated "num_keys" times bdaddr_t bdaddr; --- remote address(es) u_int8_t key[NG_HCI_KEY_SIZE]; --- key(s) */ } __attribute__ ((packed)) ng_hci_return_link_keys_ep; #define NG_HCI_EVENT_PIN_CODE_REQ 0x16 typedef struct { bdaddr_t bdaddr; /* remote unit address */ } __attribute__ ((packed)) ng_hci_pin_code_req_ep; #define NG_HCI_EVENT_LINK_KEY_REQ 0x17 typedef struct { bdaddr_t bdaddr; /* remote unit address */ } __attribute__ ((packed)) ng_hci_link_key_req_ep; #define NG_HCI_EVENT_LINK_KEY_NOTIFICATION 0x18 typedef struct { bdaddr_t bdaddr; /* remote unit address */ u_int8_t key[NG_HCI_KEY_SIZE]; /* link key */ u_int8_t key_type; /* type of the key */ } __attribute__ ((packed)) ng_hci_link_key_notification_ep; #define NG_HCI_EVENT_LOOPBACK_COMMAND 0x19 typedef struct { u_int8_t command[0]; /* Command packet */ } __attribute__ ((packed)) ng_hci_loopback_command_ep; #define NG_HCI_EVENT_DATA_BUFFER_OVERFLOW 0x1a typedef struct { u_int8_t link_type; /* Link type */ } __attribute__ ((packed)) ng_hci_data_buffer_overflow_ep; #define NG_HCI_EVENT_MAX_SLOT_CHANGE 0x1b typedef struct { u_int16_t con_handle; /* connection handle */ u_int8_t lmp_max_slots; /* Max. # of slots allowed */ } __attribute__ ((packed)) ng_hci_max_slot_change_ep; #define NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL 0x1c typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* Connection handle */ u_int16_t clock_offset; /* Clock offset */ } __attribute__ ((packed)) ng_hci_read_clock_offset_compl_ep; #define NG_HCI_EVENT_CON_PKT_TYPE_CHANGED 0x1d typedef struct { u_int8_t status; /* 0x00 - success */ u_int16_t con_handle; /* connection handle */ u_int16_t pkt_type; /* packet type */ } __attribute__ ((packed)) ng_hci_con_pkt_type_changed_ep; #define NG_HCI_EVENT_QOS_VIOLATION 0x1e typedef struct { u_int16_t con_handle; /* connection handle */ } __attribute__ ((packed)) ng_hci_qos_violation_ep; #define NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE 0x1f typedef struct { bdaddr_t bdaddr; /* destination address */ u_int8_t page_scan_mode; /* page scan mode */ } __attribute__ ((packed)) ng_hci_page_scan_mode_change_ep; #define NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE 0x20 typedef struct { bdaddr_t bdaddr; /* destination address */ u_int8_t page_scan_rep_mode; /* page scan repetition mode */ } __attribute__ ((packed)) ng_hci_page_scan_rep_mode_change_ep; #define NG_HCI_EVENT_LE 0x3e typedef struct { u_int8_t subevent_code; }__attribute__ ((packed)) ng_hci_le_ep; #define NG_HCI_LEEV_CON_COMPL 0x01 typedef struct { u_int8_t status; u_int16_t handle; u_int8_t role; u_int8_t address_type; bdaddr_t address; u_int16_t interval; u_int8_t latency; u_int16_t supervision_timeout; u_int8_t master_clock_accracy; } __attribute__ ((packed)) ng_hci_le_connection_complete_ep; #define NG_HCI_LEEV_ADVREP 0x02 typedef struct { u_int8_t num_reports; }__attribute__ ((packed)) ng_hci_le_advertising_report_ep; #define NG_HCI_SCAN_RESPONSE_DATA_MAX 0x1f typedef struct { u_int8_t event_type; u_int8_t addr_type; bdaddr_t bdaddr; u_int8_t length_data; u_int8_t data[NG_HCI_SCAN_RESPONSE_DATA_MAX]; }__attribute__((packed)) ng_hci_le_advreport; #define NG_HCI_LEEV_CON_UPDATE_COMPL 0x03 typedef struct { u_int8_t status; u_int16_t connection_handle; u_int16_t conn_interval; u_int16_t conn_latency; u_int16_t supervision_timeout; }__attribute__((packed)) ng_hci_connection_update_complete_ep; #define NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL 0x04 //TBD #define NG_HCI_LEEV_LONG_TERM_KEY_REQUEST 0x05 //TBD #define NG_HCI_EVENT_BT_LOGO 0xfe #define NG_HCI_EVENT_VENDOR 0xff #endif /* ndef _NETGRAPH_HCI_H_ */ Index: head/sys/netgraph/bluetooth/include/ng_l2cap.h =================================================================== --- head/sys/netgraph/bluetooth/include/ng_l2cap.h (revision 290037) +++ head/sys/netgraph/bluetooth/include/ng_l2cap.h (revision 290038) @@ -1,697 +1,706 @@ /* * ng_l2cap.h */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap.h,v 1.2 2003/04/27 00:52:26 max Exp $ * $FreeBSD$ */ /* * This file contains everything that application needs to know about * Link Layer Control and Adaptation Protocol (L2CAP). All information * was obtained from Bluetooth Specification Book v1.1. * * This file can be included by both kernel and userland applications. */ #ifndef _NETGRAPH_L2CAP_H_ #define _NETGRAPH_L2CAP_H_ /************************************************************************** ************************************************************************** ** Netgraph node hook name, type name and type cookie and commands ************************************************************************** **************************************************************************/ /* Netgraph node hook names */ #define NG_L2CAP_HOOK_HCI "hci" /* HCI <-> L2CAP */ #define NG_L2CAP_HOOK_L2C "l2c" /* L2CAP <-> Upper */ #define NG_L2CAP_HOOK_CTL "ctl" /* L2CAP <-> User */ /* Node type name and type cookie */ #define NG_L2CAP_NODE_TYPE "l2cap" #define NGM_L2CAP_COOKIE 1000774185 /************************************************************************** ************************************************************************** ** Common defines and types (L2CAP) ************************************************************************** **************************************************************************/ /* * Channel IDs are assigned relative to the instance of L2CAP node, i.e. * relative to the unit. So the total number of channels that unit can have * open at the same time is 0xffff - 0x0040 = 0xffbf (65471). This number * does not depend on number of connections. */ #define NG_L2CAP_NULL_CID 0x0000 /* DO NOT USE THIS CID */ #define NG_L2CAP_SIGNAL_CID 0x0001 /* signaling channel ID */ #define NG_L2CAP_CLT_CID 0x0002 /* connectionless channel ID */ #define NG_L2CAP_A2MP_CID 0x0003 #define NG_L2CAP_ATT_CID 0x0004 #define NG_L2CAP_LESIGNAL_CID 0x0005 #define NG_L2CAP_SMP_CID 0x0006 /* 0x0007 - 0x003f Reserved */ #define NG_L2CAP_FIRST_CID 0x0040 /* dynamically alloc. (start) */ #define NG_L2CAP_LAST_CID 0xffff /* dynamically alloc. (end) */ #define NG_L2CAP_LELAST_CID 0x007f /* L2CAP MTU */ #define NG_L2CAP_MTU_LE_MINIMAM 23 #define NG_L2CAP_MTU_MINIMUM 48 #define NG_L2CAP_MTU_DEFAULT 672 #define NG_L2CAP_MTU_MAXIMUM 0xffff /* L2CAP flush and link timeouts */ #define NG_L2CAP_FLUSH_TIMO_DEFAULT 0xffff /* always retransmit */ #define NG_L2CAP_LINK_TIMO_DEFAULT 0xffff /* L2CAP Command Reject reasons */ #define NG_L2CAP_REJ_NOT_UNDERSTOOD 0x0000 #define NG_L2CAP_REJ_MTU_EXCEEDED 0x0001 #define NG_L2CAP_REJ_INVALID_CID 0x0002 /* 0x0003 - 0xffff - reserved for future use */ /* Protocol/Service Multioplexor (PSM) values */ #define NG_L2CAP_PSM_ANY 0x0000 /* Any/Invalid PSM */ #define NG_L2CAP_PSM_SDP 0x0001 /* Service Discovery Protocol */ #define NG_L2CAP_PSM_RFCOMM 0x0003 /* RFCOMM protocol */ #define NG_L2CAP_PSM_TCP 0x0005 /* Telephony Control Protocol */ /* 0x0006 - 0x1000 - reserved for future use */ /* L2CAP Connection response command result codes */ #define NG_L2CAP_SUCCESS 0x0000 #define NG_L2CAP_PENDING 0x0001 #define NG_L2CAP_PSM_NOT_SUPPORTED 0x0002 #define NG_L2CAP_SEQUIRY_BLOCK 0x0003 #define NG_L2CAP_NO_RESOURCES 0x0004 #define NG_L2CAP_TIMEOUT 0xeeee #define NG_L2CAP_UNKNOWN 0xffff /* 0x0005 - 0xffff - reserved for future use */ /* L2CAP Connection response status codes */ #define NG_L2CAP_NO_INFO 0x0000 #define NG_L2CAP_AUTH_PENDING 0x0001 #define NG_L2CAP_AUTZ_PENDING 0x0002 /* 0x0003 - 0xffff - reserved for future use */ /* L2CAP Configuration response result codes */ #define NG_L2CAP_UNACCEPTABLE_PARAMS 0x0001 #define NG_L2CAP_REJECT 0x0002 #define NG_L2CAP_UNKNOWN_OPTION 0x0003 /* 0x0003 - 0xffff - reserved for future use */ /* L2CAP Configuration options */ #define NG_L2CAP_OPT_CFLAG_BIT 0x0001 #define NG_L2CAP_OPT_CFLAG(flags) ((flags) & NG_L2CAP_OPT_CFLAG_BIT) #define NG_L2CAP_OPT_HINT_BIT 0x80 #define NG_L2CAP_OPT_HINT(type) ((type) & NG_L2CAP_OPT_HINT_BIT) #define NG_L2CAP_OPT_HINT_MASK 0x7f #define NG_L2CAP_OPT_MTU 0x01 #define NG_L2CAP_OPT_MTU_SIZE sizeof(u_int16_t) #define NG_L2CAP_OPT_FLUSH_TIMO 0x02 #define NG_L2CAP_OPT_FLUSH_TIMO_SIZE sizeof(u_int16_t) #define NG_L2CAP_OPT_QOS 0x03 #define NG_L2CAP_OPT_QOS_SIZE sizeof(ng_l2cap_flow_t) /* 0x4 - 0xff - reserved for future use */ /* L2CAP Information request type codes */ #define NG_L2CAP_CONNLESS_MTU 0x0001 /* 0x0002 - 0xffff - reserved for future use */ /* L2CAP Information response codes */ #define NG_L2CAP_NOT_SUPPORTED 0x0001 /* 0x0002 - 0xffff - reserved for future use */ /* L2CAP flow control */ typedef struct { u_int8_t flags; /* reserved for future use */ u_int8_t service_type; /* service type */ u_int32_t token_rate; /* bytes per second */ u_int32_t token_bucket_size; /* bytes */ u_int32_t peak_bandwidth; /* bytes per second */ u_int32_t latency; /* microseconds */ u_int32_t delay_variation; /* microseconds */ } __attribute__ ((packed)) ng_l2cap_flow_t; typedef ng_l2cap_flow_t * ng_l2cap_flow_p; /************************************************************************** ************************************************************************** ** Link level defines, headers and types ************************************************************************** **************************************************************************/ /* L2CAP header */ typedef struct { u_int16_t length; /* payload size */ u_int16_t dcid; /* destination channel ID */ } __attribute__ ((packed)) ng_l2cap_hdr_t; /* L2CAP ConnectionLess Traffic (CLT) (if destination cid == 0x2) */ typedef struct { u_int16_t psm; /* Protocol/Service Multiplexor */ } __attribute__ ((packed)) ng_l2cap_clt_hdr_t; #define NG_L2CAP_CLT_MTU_MAXIMUM \ (NG_L2CAP_MTU_MAXIMUM - sizeof(ng_l2cap_clt_hdr_t)) /* L2CAP command header */ typedef struct { u_int8_t code; /* command OpCode */ u_int8_t ident; /* identifier to match request and response */ u_int16_t length; /* command parameters length */ } __attribute__ ((packed)) ng_l2cap_cmd_hdr_t; /* L2CAP Command Reject */ #define NG_L2CAP_CMD_REJ 0x01 typedef struct { u_int16_t reason; /* reason to reject command */ /* u_int8_t data[]; -- optional data (depends on reason) */ } __attribute__ ((packed)) ng_l2cap_cmd_rej_cp; /* CommandReject data */ typedef union { /* NG_L2CAP_REJ_MTU_EXCEEDED */ struct { u_int16_t mtu; /* actual signaling MTU */ } __attribute__ ((packed)) mtu; /* NG_L2CAP_REJ_INVALID_CID */ struct { u_int16_t scid; /* local CID */ u_int16_t dcid; /* remote CID */ } __attribute__ ((packed)) cid; } ng_l2cap_cmd_rej_data_t; typedef ng_l2cap_cmd_rej_data_t * ng_l2cap_cmd_rej_data_p; /* L2CAP Connection Request */ #define NG_L2CAP_CON_REQ 0x02 typedef struct { u_int16_t psm; /* Protocol/Service Multiplexor (PSM) */ u_int16_t scid; /* source channel ID */ } __attribute__ ((packed)) ng_l2cap_con_req_cp; /* L2CAP Connection Response */ #define NG_L2CAP_CON_RSP 0x03 typedef struct { u_int16_t dcid; /* destination channel ID */ u_int16_t scid; /* source channel ID */ u_int16_t result; /* 0x00 - success */ u_int16_t status; /* more info if result != 0x00 */ } __attribute__ ((packed)) ng_l2cap_con_rsp_cp; /* L2CAP Configuration Request */ #define NG_L2CAP_CFG_REQ 0x04 typedef struct { u_int16_t dcid; /* destination channel ID */ u_int16_t flags; /* flags */ /* u_int8_t options[] -- options */ } __attribute__ ((packed)) ng_l2cap_cfg_req_cp; /* L2CAP Configuration Response */ #define NG_L2CAP_CFG_RSP 0x05 typedef struct { u_int16_t scid; /* source channel ID */ u_int16_t flags; /* flags */ u_int16_t result; /* 0x00 - success */ /* u_int8_t options[] -- options */ } __attribute__ ((packed)) ng_l2cap_cfg_rsp_cp; /* L2CAP configuration option */ typedef struct { u_int8_t type; u_int8_t length; /* u_int8_t value[] -- option value (depends on type) */ } __attribute__ ((packed)) ng_l2cap_cfg_opt_t; typedef ng_l2cap_cfg_opt_t * ng_l2cap_cfg_opt_p; /* L2CAP configuration option value */ typedef union { u_int16_t mtu; /* NG_L2CAP_OPT_MTU */ u_int16_t flush_timo; /* NG_L2CAP_OPT_FLUSH_TIMO */ ng_l2cap_flow_t flow; /* NG_L2CAP_OPT_QOS */ + uint16_t encryption; } ng_l2cap_cfg_opt_val_t; typedef ng_l2cap_cfg_opt_val_t * ng_l2cap_cfg_opt_val_p; /* L2CAP Disconnect Request */ #define NG_L2CAP_DISCON_REQ 0x06 typedef struct { u_int16_t dcid; /* destination channel ID */ u_int16_t scid; /* source channel ID */ } __attribute__ ((packed)) ng_l2cap_discon_req_cp; /* L2CAP Disconnect Response */ #define NG_L2CAP_DISCON_RSP 0x07 typedef ng_l2cap_discon_req_cp ng_l2cap_discon_rsp_cp; /* L2CAP Echo Request */ #define NG_L2CAP_ECHO_REQ 0x08 /* No command parameters, only optional data */ /* L2CAP Echo Response */ #define NG_L2CAP_ECHO_RSP 0x09 #define NG_L2CAP_MAX_ECHO_SIZE \ (NG_L2CAP_MTU_MAXIMUM - sizeof(ng_l2cap_cmd_hdr_t)) /* No command parameters, only optional data */ /* L2CAP Information Request */ #define NG_L2CAP_INFO_REQ 0x0a typedef struct { u_int16_t type; /* requested information type */ } __attribute__ ((packed)) ng_l2cap_info_req_cp; /* L2CAP Information Response */ #define NG_L2CAP_INFO_RSP 0x0b typedef struct { u_int16_t type; /* requested information type */ u_int16_t result; /* 0x00 - success */ /* u_int8_t info[] -- info data (depends on type) * * NG_L2CAP_CONNLESS_MTU - 2 bytes connectionless MTU */ } __attribute__ ((packed)) ng_l2cap_info_rsp_cp; typedef union { /* NG_L2CAP_CONNLESS_MTU */ struct { u_int16_t mtu; } __attribute__ ((packed)) mtu; } ng_l2cap_info_rsp_data_t; typedef ng_l2cap_info_rsp_data_t * ng_l2cap_info_rsp_data_p; #define NG_L2CAP_CMD_PARAM_UPDATE_REQUEST 0x12 typedef struct { uint16_t interval_min; uint16_t interval_max; uint16_t slave_latency; uint16_t timeout_mpl; } __attribute__ ((packed)) ng_l2cap_param_update_req_cp; #define NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE 0x13 #define NG_L2CAP_UPDATE_PARAM_ACCEPT 0 #define NG_L2CAP_UPDATE_PARAM_REJECT 1 //typedef uint16_t update_response; /************************************************************************** ************************************************************************** ** Upper layer protocol interface. L2CA_xxx messages ************************************************************************** **************************************************************************/ /* * NOTE! NOTE! NOTE! * * Bluetooth specification says that L2CA_xxx request must block until * response is ready. We are not allowed to block in Netgraph, so we * need to queue request and save some information that can be used * later and help match request and response. * * The idea is to use "token" field from Netgraph message header. The * upper layer protocol _MUST_ populate "token". L2CAP will queue request * (using L2CAP command descriptor) and start processing. Later, when * response is ready or timeout has occur L2CAP layer will create new * Netgraph message, set "token" and RESP flag and send the message to * the upper layer protocol. * * L2CA_xxx_Ind messages _WILL_NOT_ populate "token" and _WILL_NOT_ * set RESP flag. There is no reason for this, because they are just * notifications and do not require acknowlegment. * * NOTE: This is _NOT_ what NG_MKRESPONSE and NG_RESPOND_MSG do, however * it is somewhat similar. */ /* L2CA data packet header */ typedef struct { u_int32_t token; /* token to use in L2CAP_L2CA_WRITE */ u_int16_t length; /* length of the data */ u_int16_t lcid; /* local channel ID */ uint16_t idtype; } __attribute__ ((packed)) ng_l2cap_l2ca_hdr_t; #define NG_L2CAP_L2CA_IDTYPE_BREDR 0 #define NG_L2CAP_L2CA_IDTYPE_ATT 1 #define NG_L2CAP_L2CA_IDTYPE_LE 2 +#define NG_L2CAP_L2CA_IDTYPE_SMP 3 /* L2CA_Connect */ #define NGM_L2CAP_L2CA_CON 0x80 /* Upper -> L2CAP */ typedef struct { u_int16_t psm; /* Protocol/Service Multiplexor */ bdaddr_t bdaddr; /* remote unit address */ uint8_t linktype; uint8_t idtype; } ng_l2cap_l2ca_con_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t lcid; /* local channel ID */ uint16_t idtype; /*ID type*/ u_int16_t result; /* 0x00 - success */ u_int16_t status; /* if result != 0x00 */ + uint8_t encryption; } ng_l2cap_l2ca_con_op; /* L2CA_ConnectInd */ #define NGM_L2CAP_L2CA_CON_IND 0x81 /* L2CAP -> Upper */ typedef struct { bdaddr_t bdaddr; /* remote unit address */ u_int16_t lcid; /* local channel ID */ u_int16_t psm; /* Procotol/Service Multiplexor */ u_int8_t ident; /* indentifier */ u_int8_t linktype; /* link type*/ } ng_l2cap_l2ca_con_ind_ip; /* No output parameters */ /* L2CA_ConnectRsp */ #define NGM_L2CAP_L2CA_CON_RSP 0x82 /* Upper -> L2CAP */ typedef struct { bdaddr_t bdaddr; /* remote unit address */ u_int8_t ident; /* "ident" from L2CAP_ConnectInd event */ u_int8_t linktype; /*link type */ u_int16_t lcid; /* local channel ID */ u_int16_t result; /* 0x00 - success */ u_int16_t status; /* if response != 0x00 */ } ng_l2cap_l2ca_con_rsp_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - success */ } ng_l2cap_l2ca_con_rsp_op; /* L2CA_Config */ #define NGM_L2CAP_L2CA_CFG 0x83 /* Upper -> L2CAP */ typedef struct { u_int16_t lcid; /* local channel ID */ u_int16_t imtu; /* receiving MTU for the local channel */ ng_l2cap_flow_t oflow; /* out flow */ u_int16_t flush_timo; /* flush timeout (msec) */ u_int16_t link_timo; /* link timeout (msec) */ } ng_l2cap_l2ca_cfg_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - success */ u_int16_t imtu; /* sending MTU for the remote channel */ ng_l2cap_flow_t oflow; /* out flow */ u_int16_t flush_timo; /* flush timeout (msec) */ } ng_l2cap_l2ca_cfg_op; /* L2CA_ConfigRsp */ #define NGM_L2CAP_L2CA_CFG_RSP 0x84 /* Upper -> L2CAP */ typedef struct { u_int16_t lcid; /* local channel ID */ u_int16_t omtu; /* sending MTU for the local channel */ ng_l2cap_flow_t iflow; /* in FLOW */ } ng_l2cap_l2ca_cfg_rsp_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - sucsess */ } ng_l2cap_l2ca_cfg_rsp_op; /* L2CA_ConfigInd */ #define NGM_L2CAP_L2CA_CFG_IND 0x85 /* L2CAP -> Upper */ typedef struct { u_int16_t lcid; /* local channel ID */ u_int16_t omtu; /* outgoing MTU for the local channel */ ng_l2cap_flow_t iflow; /* in flow */ u_int16_t flush_timo; /* flush timeout (msec) */ } ng_l2cap_l2ca_cfg_ind_ip; /* No output parameters */ /* L2CA_QoSViolationInd */ #define NGM_L2CAP_L2CA_QOS_IND 0x86 /* L2CAP -> Upper */ typedef struct { bdaddr_t bdaddr; /* remote unit address */ } ng_l2cap_l2ca_qos_ind_ip; /* No output parameters */ /* L2CA_Disconnect */ #define NGM_L2CAP_L2CA_DISCON 0x87 /* Upper -> L2CAP */ typedef struct { u_int16_t lcid; /* local channel ID */ u_int16_t idtype; } ng_l2cap_l2ca_discon_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - sucsess */ } ng_l2cap_l2ca_discon_op; /* L2CA_DisconnectInd */ #define NGM_L2CAP_L2CA_DISCON_IND 0x88 /* L2CAP -> Upper */ typedef ng_l2cap_l2ca_discon_ip ng_l2cap_l2ca_discon_ind_ip; /* No output parameters */ /* L2CA_Write response */ #define NGM_L2CAP_L2CA_WRITE 0x89 /* No input parameters */ /* L2CAP -> Upper */ typedef struct { int result; /* result (0x00 - success) */ u_int16_t length; /* amount of data written */ u_int16_t lcid; /* local channel ID */ uint16_t idtype; } ng_l2cap_l2ca_write_op; /* L2CA_GroupCreate */ #define NGM_L2CAP_L2CA_GRP_CREATE 0x8a /* Upper -> L2CAP */ typedef struct { u_int16_t psm; /* Protocol/Service Multiplexor */ } ng_l2cap_l2ca_grp_create_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t lcid; /* local group channel ID */ } ng_l2cap_l2ca_grp_create_op; /* L2CA_GroupClose */ #define NGM_L2CAP_L2CA_GRP_CLOSE 0x8b /* Upper -> L2CAP */ typedef struct { u_int16_t lcid; /* local group channel ID */ } ng_l2cap_l2ca_grp_close_ip; #if 0 /* L2CAP -> Upper */ * typedef struct { * u_int16_t result; /* 0x00 - success */ * } ng_l2cap_l2ca_grp_close_op; #endif /* L2CA_GroupAddMember */ #define NGM_L2CAP_L2CA_GRP_ADD_MEMBER 0x8c /* Upper -> L2CAP */ typedef struct { u_int16_t lcid; /* local group channel ID */ bdaddr_t bdaddr; /* remote unit address */ } ng_l2cap_l2ca_grp_add_member_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - success */ } ng_l2cap_l2ca_grp_add_member_op; /* L2CA_GroupRemoveMember */ #define NGM_L2CAP_L2CA_GRP_REM_MEMBER 0x8d /* Upper -> L2CAP */ typedef ng_l2cap_l2ca_grp_add_member_ip ng_l2cap_l2ca_grp_rem_member_ip; /* L2CAP -> Upper */ #if 0 * typedef ng_l2cap_l2ca_grp_add_member_op ng_l2cap_l2ca_grp_rem_member_op; #endif /* L2CA_GroupMembeship */ #define NGM_L2CAP_L2CA_GRP_MEMBERSHIP 0x8e /* Upper -> L2CAP */ typedef struct { u_int16_t lcid; /* local group channel ID */ } ng_l2cap_l2ca_grp_get_members_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - success */ u_int16_t nmembers; /* number of group members */ /* bdaddr_t members[] -- group memebers */ } ng_l2cap_l2ca_grp_get_members_op; /* L2CA_Ping */ #define NGM_L2CAP_L2CA_PING 0x8f /* Upper -> L2CAP */ typedef struct { bdaddr_t bdaddr; /* remote unit address */ u_int16_t echo_size; /* size of echo data in bytes */ /* u_int8_t echo_data[] -- echo data */ } ng_l2cap_l2ca_ping_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - success */ bdaddr_t bdaddr; /* remote unit address */ u_int16_t echo_size; /* size of echo data in bytes */ /* u_int8_t echo_data[] -- echo data */ } ng_l2cap_l2ca_ping_op; /* L2CA_GetInfo */ #define NGM_L2CAP_L2CA_GET_INFO 0x90 /* Upper -> L2CAP */ typedef struct { bdaddr_t bdaddr; /* remote unit address */ u_int16_t info_type; /* info type */ uint8_t linktype; uint8_t unused; } ng_l2cap_l2ca_get_info_ip; /* L2CAP -> Upper */ typedef struct { u_int16_t result; /* 0x00 - success */ u_int16_t info_size; /* size of info data in bytes */ /* u_int8_t info_data[] -- info data */ } ng_l2cap_l2ca_get_info_op; /* L2CA_EnableCLT/L2CA_DisableCLT */ #define NGM_L2CAP_L2CA_ENABLE_CLT 0x91 /* Upper -> L2CAP */ typedef struct { u_int16_t psm; /* Protocol/Service Multiplexor */ u_int16_t enable; /* 0x00 - disable */ } ng_l2cap_l2ca_enable_clt_ip; #if 0 /* L2CAP -> Upper */ * typedef struct { * u_int16_t result; /* 0x00 - success */ * } ng_l2cap_l2ca_enable_clt_op; #endif +#define NGM_L2CAP_L2CA_ENC_CHANGE 0x92 +typedef struct { + uint16_t lcid; + uint16_t result; + uint8_t idtype; +} ng_l2cap_l2ca_enc_chg_op; /************************************************************************** ************************************************************************** ** L2CAP node messages ************************************************************************** **************************************************************************/ /* L2CAP connection states */ #define NG_L2CAP_CON_CLOSED 0 /* connection closed */ #define NG_L2CAP_W4_LP_CON_CFM 1 /* waiting... */ #define NG_L2CAP_CON_OPEN 2 /* connection open */ /* L2CAP channel states */ #define NG_L2CAP_CLOSED 0 /* channel closed */ #define NG_L2CAP_W4_L2CAP_CON_RSP 1 /* wait for L2CAP resp. */ #define NG_L2CAP_W4_L2CA_CON_RSP 2 /* wait for upper resp. */ #define NG_L2CAP_CONFIG 3 /* L2CAP configuration */ #define NG_L2CAP_OPEN 4 /* channel open */ #define NG_L2CAP_W4_L2CAP_DISCON_RSP 5 /* wait for L2CAP discon. */ #define NG_L2CAP_W4_L2CA_DISCON_RSP 6 /* wait for upper discon. */ /* Node flags */ #define NG_L2CAP_CLT_SDP_DISABLED (1 << 0) /* disable SDP CLT */ #define NG_L2CAP_CLT_RFCOMM_DISABLED (1 << 1) /* disable RFCOMM CLT */ #define NG_L2CAP_CLT_TCP_DISABLED (1 << 2) /* disable TCP CLT */ /* Debug levels */ #define NG_L2CAP_ALERT_LEVEL 1 #define NG_L2CAP_ERR_LEVEL 2 #define NG_L2CAP_WARN_LEVEL 3 #define NG_L2CAP_INFO_LEVEL 4 /* Get node flags (see flags above) */ #define NGM_L2CAP_NODE_GET_FLAGS 0x400 /* L2CAP -> User */ typedef u_int16_t ng_l2cap_node_flags_ep; /* Get/Set debug level (see levels above) */ #define NGM_L2CAP_NODE_GET_DEBUG 0x401 /* L2CAP -> User */ #define NGM_L2CAP_NODE_SET_DEBUG 0x402 /* User -> L2CAP */ typedef u_int16_t ng_l2cap_node_debug_ep; #define NGM_L2CAP_NODE_HOOK_INFO 0x409 /* L2CAP -> Upper */ typedef struct { bdaddr_t addr; }ng_l2cap_node_hook_info_ep; #define NGM_L2CAP_NODE_GET_CON_LIST 0x40a /* L2CAP -> User */ typedef struct { u_int32_t num_connections; /* number of connections */ } ng_l2cap_node_con_list_ep; /* Connection flags */ #define NG_L2CAP_CON_TX (1 << 0) /* sending data */ #define NG_L2CAP_CON_RX (1 << 1) /* receiving data */ #define NG_L2CAP_CON_OUTGOING (1 << 2) /* outgoing connection */ #define NG_L2CAP_CON_LP_TIMO (1 << 3) /* LP timeout */ #define NG_L2CAP_CON_AUTO_DISCON_TIMO (1 << 4) /* auto discon. timeout */ #define NG_L2CAP_CON_DYING (1 << 5) /* connection is dying */ typedef struct { u_int8_t state; /* connection state */ u_int8_t flags; /* flags */ int16_t pending; /* num. pending packets */ u_int16_t con_handle; /* connection handle */ bdaddr_t remote; /* remote bdaddr */ } ng_l2cap_node_con_ep; #define NG_L2CAP_MAX_CON_NUM \ ((0xffff - sizeof(ng_l2cap_node_con_list_ep))/sizeof(ng_l2cap_node_con_ep)) #define NGM_L2CAP_NODE_GET_CHAN_LIST 0x40b /* L2CAP -> User */ typedef struct { u_int32_t num_channels; /* number of channels */ } ng_l2cap_node_chan_list_ep; typedef struct { u_int32_t state; /* channel state */ u_int16_t scid; /* source (local) channel ID */ u_int16_t dcid; /* destination (remote) channel ID */ u_int16_t imtu; /* incomming MTU */ u_int16_t omtu; /* outgoing MTU */ u_int16_t psm; /* PSM */ bdaddr_t remote; /* remote bdaddr */ } ng_l2cap_node_chan_ep; #define NG_L2CAP_MAX_CHAN_NUM \ ((0xffff - sizeof(ng_l2cap_node_chan_list_ep))/sizeof(ng_l2cap_node_chan_ep)) #define NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO 0x40c /* L2CAP -> User */ #define NGM_L2CAP_NODE_SET_AUTO_DISCON_TIMO 0x40d /* User -> L2CAP */ typedef u_int16_t ng_l2cap_node_auto_discon_ep; #endif /* ndef _NETGRAPH_L2CAP_H_ */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c (revision 290038) @@ -1,1472 +1,1476 @@ /* * ng_l2cap_evnt.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** ****************************************************************************** ** L2CAP events processing module ****************************************************************************** ******************************************************************************/ static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p); static int ng_l2cap_process_lesignal_cmd (ng_l2cap_con_p); static int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_cmd_urq (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_cmd_urs (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t); static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t); static int send_l2cap_reject (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t); static int send_l2cap_con_rej (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t); static int send_l2cap_cfg_rsp (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *); static int send_l2cap_param_urs (ng_l2cap_con_p , u_int8_t , u_int16_t); static int get_next_l2cap_opt (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p); /* * Receive L2CAP packet. First get L2CAP header and verify packet. Than * get destination channel and process packet. */ int ng_l2cap_receive(ng_l2cap_con_p con) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_hdr_t *hdr = NULL; int error = 0; /* Check packet */ if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->rx_pkt->m_pkthdr.len); error = EMSGSIZE; goto drop; } /* Get L2CAP header */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); if (con->rx_pkt == NULL) return (ENOBUFS); hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *); hdr->length = le16toh(hdr->length); hdr->dcid = le16toh(hdr->dcid); /* Check payload size */ if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n", __func__, NG_NODE_NAME(l2cap->node), hdr->length, con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); error = EMSGSIZE; goto drop; } /* Process packet */ switch (hdr->dcid) { case NG_L2CAP_SIGNAL_CID: /* L2CAP command */ m_adj(con->rx_pkt, sizeof(*hdr)); error = ng_l2cap_process_signal_cmd(con); break; case NG_L2CAP_LESIGNAL_CID: m_adj(con->rx_pkt, sizeof(*hdr)); error = ng_l2cap_process_lesignal_cmd(con); break; case NG_L2CAP_CLT_CID: /* Connectionless packet */ error = ng_l2cap_l2ca_clt_receive(con); break; default: /* Data packet */ error = ng_l2cap_l2ca_receive(con); break; } return (error); drop: NG_FREE_M(con->rx_pkt); return (error); } /* ng_l2cap_receive */ /* * Process L2CAP signaling command. We already know that destination channel ID * is 0x1 that means we have received signaling command from peer's L2CAP layer. * So get command header, decode and process it. * * XXX do we need to check signaling MTU here? */ static int ng_l2cap_process_signal_cmd(ng_l2cap_con_p con) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_cmd_hdr_t *hdr = NULL; struct mbuf *m = NULL; while (con->rx_pkt != NULL) { /* Verify packet length */ if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->rx_pkt->m_pkthdr.len); NG_FREE_M(con->rx_pkt); return (EMSGSIZE); } /* Get signaling command */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); if (con->rx_pkt == NULL) return (ENOBUFS); hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); hdr->length = le16toh(hdr->length); m_adj(con->rx_pkt, sizeof(*hdr)); /* Verify command length */ if (con->rx_pkt->m_pkthdr.len < hdr->length) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \ "Invalid command length=%d, m_pkthdr.len=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->code, hdr->ident, hdr->length, con->rx_pkt->m_pkthdr.len); NG_FREE_M(con->rx_pkt); return (EMSGSIZE); } /* Get the command, save the rest (if any) */ if (con->rx_pkt->m_pkthdr.len > hdr->length) m = m_split(con->rx_pkt, hdr->length, M_NOWAIT); else m = NULL; /* Process command */ switch (hdr->code) { case NG_L2CAP_CMD_REJ: ng_l2cap_process_cmd_rej(con, hdr->ident); break; case NG_L2CAP_CON_REQ: ng_l2cap_process_con_req(con, hdr->ident); break; case NG_L2CAP_CON_RSP: ng_l2cap_process_con_rsp(con, hdr->ident); break; case NG_L2CAP_CFG_REQ: ng_l2cap_process_cfg_req(con, hdr->ident); break; case NG_L2CAP_CFG_RSP: ng_l2cap_process_cfg_rsp(con, hdr->ident); break; case NG_L2CAP_DISCON_REQ: ng_l2cap_process_discon_req(con, hdr->ident); break; case NG_L2CAP_DISCON_RSP: ng_l2cap_process_discon_rsp(con, hdr->ident); break; case NG_L2CAP_ECHO_REQ: ng_l2cap_process_echo_req(con, hdr->ident); break; case NG_L2CAP_ECHO_RSP: ng_l2cap_process_echo_rsp(con, hdr->ident); break; case NG_L2CAP_INFO_REQ: ng_l2cap_process_info_req(con, hdr->ident); break; case NG_L2CAP_INFO_RSP: ng_l2cap_process_info_rsp(con, hdr->ident); break; default: NG_L2CAP_ERR( "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->code, hdr->ident, hdr->length); /* * Send L2CAP_CommandRej. Do not really care * about the result */ send_l2cap_reject(con, hdr->ident, NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0); NG_FREE_M(con->rx_pkt); break; } con->rx_pkt = m; } return (0); } /* ng_l2cap_process_signal_cmd */ static int ng_l2cap_process_lesignal_cmd(ng_l2cap_con_p con) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_cmd_hdr_t *hdr = NULL; struct mbuf *m = NULL; while (con->rx_pkt != NULL) { /* Verify packet length */ if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->rx_pkt->m_pkthdr.len); NG_FREE_M(con->rx_pkt); return (EMSGSIZE); } /* Get signaling command */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); if (con->rx_pkt == NULL) return (ENOBUFS); hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); hdr->length = le16toh(hdr->length); m_adj(con->rx_pkt, sizeof(*hdr)); /* Verify command length */ if (con->rx_pkt->m_pkthdr.len < hdr->length) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \ "Invalid command length=%d, m_pkthdr.len=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->code, hdr->ident, hdr->length, con->rx_pkt->m_pkthdr.len); NG_FREE_M(con->rx_pkt); return (EMSGSIZE); } /* Get the command, save the rest (if any) */ if (con->rx_pkt->m_pkthdr.len > hdr->length) m = m_split(con->rx_pkt, hdr->length, M_NOWAIT); else m = NULL; /* Process command */ switch (hdr->code) { case NG_L2CAP_CMD_REJ: ng_l2cap_process_cmd_rej(con, hdr->ident); break; case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST: ng_l2cap_process_cmd_urq(con, hdr->ident); break; case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE: ng_l2cap_process_cmd_urs(con, hdr->ident); break; default: NG_L2CAP_ERR( "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->code, hdr->ident, hdr->length); /* * Send L2CAP_CommandRej. Do not really care * about the result */ send_l2cap_reject(con, hdr->ident, NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0); NG_FREE_M(con->rx_pkt); break; } con->rx_pkt = m; } return (0); } /* ng_l2cap_process_signal_cmd */ /*Update Paramater Request*/ static int ng_l2cap_process_cmd_urq(ng_l2cap_con_p con, uint8_t ident) { /*We do not implement paramter negotiasion for now*/ send_l2cap_param_urs(con, ident, NG_L2CAP_UPDATE_PARAM_ACCEPT); NG_FREE_M(con->rx_pkt); return 0; } static int ng_l2cap_process_cmd_urs(ng_l2cap_con_p con, uint8_t ident) { /* We only support master side yet .*/ //send_l2cap_reject(con,ident ... ); NG_FREE_M(con->rx_pkt); return 0; } /* * Process L2CAP_CommandRej command */ static int ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_cmd_rej_cp *cp = NULL; ng_l2cap_cmd_p cmd = NULL; /* Get command parameters */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); if (con->rx_pkt == NULL) return (ENOBUFS); cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *); cp->reason = le16toh(cp->reason); /* Check if we have pending command descriptor */ cmd = ng_l2cap_cmd_by_ident(con, ident); if (cmd != NULL) { /* If command timeout already happened then ignore reject */ if (ng_l2cap_command_untimeout(cmd) != 0) { NG_FREE_M(con->rx_pkt); return (ETIMEDOUT); } ng_l2cap_unlink_cmd(cmd); switch (cmd->code) { case NG_L2CAP_CON_REQ: ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0); ng_l2cap_free_chan(cmd->ch); break; case NG_L2CAP_CFG_REQ: ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason); break; case NG_L2CAP_DISCON_REQ: ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason); ng_l2cap_free_chan(cmd->ch); /* XXX free channel */ break; case NG_L2CAP_ECHO_REQ: ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, cp->reason, NULL); break; case NG_L2CAP_INFO_REQ: ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, cp->reason, NULL); break; default: NG_L2CAP_ALERT( "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n", __func__, NG_NODE_NAME(l2cap->node), cmd->code); break; } ng_l2cap_free_cmd(cmd); } else NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_CommandRej command. " \ "Requested ident does not exist, ident=%d\n", __func__, NG_NODE_NAME(l2cap->node), ident); NG_FREE_M(con->rx_pkt); return (0); } /* ng_l2cap_process_cmd_rej */ /* * Process L2CAP_ConnectReq command */ static int ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; struct mbuf *m = con->rx_pkt; ng_l2cap_con_req_cp *cp = NULL; ng_l2cap_chan_p ch = NULL; int error = 0; u_int16_t dcid, psm; int idtype; /* Get command parameters */ NG_L2CAP_M_PULLUP(m, sizeof(*cp)); if (m == NULL) return (ENOBUFS); cp = mtod(m, ng_l2cap_con_req_cp *); psm = le16toh(cp->psm); dcid = le16toh(cp->scid); NG_FREE_M(m); con->rx_pkt = NULL; if(dcid == NG_L2CAP_ATT_CID) idtype = NG_L2CAP_L2CA_IDTYPE_ATT; + else if(dcid == NG_L2CAP_SMP_CID) + idtype = NG_L2CAP_L2CA_IDTYPE_SMP; else if( con->linktype != NG_HCI_LINK_ACL) idtype = NG_L2CAP_L2CA_IDTYPE_LE; else idtype = NG_L2CAP_L2CA_IDTYPE_BREDR; /* * Create new channel and send L2CA_ConnectInd notification * to the upper layer protocol. */ ch = ng_l2cap_new_chan(l2cap, con, psm, idtype); if (ch == NULL) return (send_l2cap_con_rej(con, ident, 0, dcid, NG_L2CAP_NO_RESOURCES)); /* Update channel IDs */ ch->dcid = dcid; /* Sent L2CA_ConnectInd notification to the upper layer */ ch->ident = ident; ch->state = NG_L2CAP_W4_L2CA_CON_RSP; error = ng_l2cap_l2ca_con_ind(ch); if (error != 0) { send_l2cap_con_rej(con, ident, ch->scid, dcid, (error == ENOMEM)? NG_L2CAP_NO_RESOURCES : NG_L2CAP_PSM_NOT_SUPPORTED); ng_l2cap_free_chan(ch); } return (error); } /* ng_l2cap_process_con_req */ /* * Process L2CAP_ConnectRsp command */ static int ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; struct mbuf *m = con->rx_pkt; ng_l2cap_con_rsp_cp *cp = NULL; ng_l2cap_cmd_p cmd = NULL; u_int16_t scid, dcid, result, status; int error = 0; /* Get command parameters */ NG_L2CAP_M_PULLUP(m, sizeof(*cp)); if (m == NULL) return (ENOBUFS); cp = mtod(m, ng_l2cap_con_rsp_cp *); dcid = le16toh(cp->dcid); scid = le16toh(cp->scid); result = le16toh(cp->result); status = le16toh(cp->status); NG_FREE_M(m); con->rx_pkt = NULL; /* Check if we have pending command descriptor */ cmd = ng_l2cap_cmd_by_ident(con, ident); if (cmd == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), ident, con->con_handle); return (ENOENT); } /* Verify channel state, if invalid - do nothing */ if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConnectRsp. " \ "Invalid channel state, cid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), scid, cmd->ch->state); goto reject; } /* Verify CIDs and send reject if does not match */ if (cmd->ch->scid != scid) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n", __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, scid); goto reject; } /* * Looks good. We got confirmation from our peer. Now process * it. First disable RTX timer. Then check the result and send * notification to the upper layer. If command timeout already * happened then ignore response. */ if ((error = ng_l2cap_command_untimeout(cmd)) != 0) return (error); if (result == NG_L2CAP_PENDING) { /* * Our peer wants more time to complete connection. We shall * start ERTX timer and wait. Keep command in the list. */ cmd->ch->dcid = dcid; ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout()); error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, status); if (error != 0) ng_l2cap_free_chan(cmd->ch); } else { ng_l2cap_unlink_cmd(cmd); if (result == NG_L2CAP_SUCCESS) { /* * Channel is open. Complete command and move to CONFIG * state. Since we have sent positive confirmation we * expect to receive L2CA_Config request from the upper * layer protocol. */ cmd->ch->dcid = dcid; - cmd->ch->state = (cmd->ch->scid == NG_L2CAP_ATT_CID)? + cmd->ch->state = ((cmd->ch->scid == NG_L2CAP_ATT_CID)|| + (cmd->ch->scid == NG_L2CAP_SMP_CID)) + ? NG_L2CAP_OPEN : NG_L2CAP_CONFIG; } else /* There was an error, so close the channel */ NG_L2CAP_INFO( "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n", __func__, NG_NODE_NAME(l2cap->node), result, status); error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, status); /* XXX do we have to remove the channel on error? */ if (error != 0 || result != NG_L2CAP_SUCCESS) ng_l2cap_free_chan(cmd->ch); ng_l2cap_free_cmd(cmd); } return (error); reject: /* Send reject. Do not really care about the result */ send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); return (0); } /* ng_l2cap_process_con_rsp */ /* * Process L2CAP_ConfigReq command */ static int ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; struct mbuf *m = con->rx_pkt; ng_l2cap_cfg_req_cp *cp = NULL; ng_l2cap_chan_p ch = NULL; u_int16_t dcid, respond, result; ng_l2cap_cfg_opt_t hdr; ng_l2cap_cfg_opt_val_t val; int off, error = 0; /* Get command parameters */ con->rx_pkt = NULL; NG_L2CAP_M_PULLUP(m, sizeof(*cp)); if (m == NULL) return (ENOBUFS); cp = mtod(m, ng_l2cap_cfg_req_cp *); dcid = le16toh(cp->dcid); respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); m_adj(m, sizeof(*cp)); /* Check if we have this channel and it is in valid state */ ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR); if (ch == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConfigReq command. " \ "Channel does not exist, cid=%d\n", __func__, NG_NODE_NAME(l2cap->node), dcid); goto reject; } /* Verify channel state */ if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConfigReq. " \ "Invalid channel state, cid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); goto reject; } if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */ ch->cfg_state = 0; ch->state = NG_L2CAP_CONFIG; } for (result = 0, off = 0; ; ) { error = get_next_l2cap_opt(m, &off, &hdr, &val); if (error == 0) { /* We done with this packet */ NG_FREE_M(m); break; } else if (error > 0) { /* Got option */ switch (hdr.type) { case NG_L2CAP_OPT_MTU: ch->omtu = val.mtu; break; case NG_L2CAP_OPT_FLUSH_TIMO: ch->flush_timo = val.flush_timo; break; case NG_L2CAP_OPT_QOS: bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow)); break; default: /* Ignore unknown hint option */ break; } } else { /* Oops, something is wrong */ respond = 1; if (error == -3) { /* * Adjust mbuf so we can get to the start * of the first option we did not like. */ m_adj(m, off - sizeof(hdr)); m->m_pkthdr.len = sizeof(hdr) + hdr.length; result = NG_L2CAP_UNKNOWN_OPTION; } else { /* XXX FIXME Send other reject codes? */ NG_FREE_M(m); result = NG_L2CAP_REJECT; } break; } } /* * Now check and see if we have to respond. If everything was OK then * respond contain "C flag" and (if set) we will respond with empty * packet and will wait for more options. * * Other case is that we did not like peer's options and will respond * with L2CAP_Config response command with Reject error code. * * When "respond == 0" than we have received all options and we will * sent L2CA_ConfigInd event to the upper layer protocol. */ if (respond) { error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m); if (error != 0) { ng_l2cap_l2ca_discon_ind(ch); ng_l2cap_free_chan(ch); } } else { /* Send L2CA_ConfigInd event to the upper layer protocol */ ch->ident = ident; error = ng_l2cap_l2ca_cfg_ind(ch); if (error != 0) ng_l2cap_free_chan(ch); } return (error); reject: /* Send reject. Do not really care about the result */ NG_FREE_M(m); send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid); return (0); } /* ng_l2cap_process_cfg_req */ /* * Process L2CAP_ConfigRsp command */ static int ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; struct mbuf *m = con->rx_pkt; ng_l2cap_cfg_rsp_cp *cp = NULL; ng_l2cap_cmd_p cmd = NULL; u_int16_t scid, cflag, result; ng_l2cap_cfg_opt_t hdr; ng_l2cap_cfg_opt_val_t val; int off, error = 0; /* Get command parameters */ con->rx_pkt = NULL; NG_L2CAP_M_PULLUP(m, sizeof(*cp)); if (m == NULL) return (ENOBUFS); cp = mtod(m, ng_l2cap_cfg_rsp_cp *); scid = le16toh(cp->scid); cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags)); result = le16toh(cp->result); m_adj(m, sizeof(*cp)); /* Check if we have this command */ cmd = ng_l2cap_cmd_by_ident(con, ident); if (cmd == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), ident, con->con_handle); NG_FREE_M(m); return (ENOENT); } /* Verify CIDs and send reject if does not match */ if (cmd->ch->scid != scid) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConfigRsp. " \ "Channel ID does not match, scid=%d(%d)\n", __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, scid); goto reject; } /* Verify channel state and reject if invalid */ if (cmd->ch->state != NG_L2CAP_CONFIG) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_ConfigRsp. " \ "Invalid channel state, scid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, cmd->ch->state); goto reject; } /* * Looks like it is our response, so process it. First parse options, * then verify C flag. If it is set then we shall expect more * configuration options from the peer and we will wait. Otherwise we * have received all options and we will send L2CA_ConfigRsp event to * the upper layer protocol. If command timeout already happened then * ignore response. */ if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { NG_FREE_M(m); return (error); } for (off = 0; ; ) { error = get_next_l2cap_opt(m, &off, &hdr, &val); if (error == 0) /* We done with this packet */ break; else if (error > 0) { /* Got option */ switch (hdr.type) { case NG_L2CAP_OPT_MTU: cmd->ch->imtu = val.mtu; break; case NG_L2CAP_OPT_FLUSH_TIMO: cmd->ch->flush_timo = val.flush_timo; break; case NG_L2CAP_OPT_QOS: bcopy(&val.flow, &cmd->ch->oflow, sizeof(cmd->ch->oflow)); break; default: /* Ignore unknown hint option */ break; } } else { /* * XXX FIXME What to do here? * * This is really BAD :( options packet was broken, or * peer sent us option that we did not understand. Let * upper layer know and do not wait for more options. */ NG_L2CAP_ALERT( "%s: %s - failed to parse configuration options, error=%d\n", __func__, NG_NODE_NAME(l2cap->node), error); result = NG_L2CAP_UNKNOWN; cflag = 0; break; } } NG_FREE_M(m); if (cflag) /* Restart timer and wait for more options */ ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout()); else { ng_l2cap_unlink_cmd(cmd); /* Send L2CA_Config response to the upper layer protocol */ error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result); if (error != 0) { /* * XXX FIXME what to do here? we were not able to send * response to the upper layer protocol, so for now * just close the channel. Send L2CAP_Disconnect to * remote peer? */ NG_L2CAP_ERR( "%s: %s - failed to send L2CA_Config response, error=%d\n", __func__, NG_NODE_NAME(l2cap->node), error); ng_l2cap_free_chan(cmd->ch); } ng_l2cap_free_cmd(cmd); } return (error); reject: /* Send reject. Do not really care about the result */ NG_FREE_M(m); send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0); return (0); } /* ng_l2cap_process_cfg_rsp */ /* * Process L2CAP_DisconnectReq command */ static int ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_discon_req_cp *cp = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; u_int16_t scid, dcid; /* Get command parameters */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); if (con->rx_pkt == NULL) return (ENOBUFS); cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *); dcid = le16toh(cp->dcid); scid = le16toh(cp->scid); NG_FREE_M(con->rx_pkt); /* Check if we have this channel and it is in valid state */ ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR); if (ch == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_DisconnectReq message. " \ "Channel does not exist, cid=%d\n", __func__, NG_NODE_NAME(l2cap->node), dcid); goto reject; } /* XXX Verify channel state and reject if invalid -- is that true? */ if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_DisconnectReq. " \ "Invalid channel state, cid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state); goto reject; } /* Match destination channel ID */ if (ch->dcid != scid || ch->scid != dcid) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_DisconnectReq. " \ "Channel IDs does not match, channel: scid=%d, dcid=%d, " \ "request: scid=%d, dcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid, scid, dcid); goto reject; } /* * Looks good, so notify upper layer protocol that channel is about * to be disconnected and send L2CA_DisconnectInd message. Then respond * with L2CAP_DisconnectRsp. */ if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */ ng_l2cap_free_chan(ch); } /* Send L2CAP_DisconnectRsp */ cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0); if (cmd == NULL) return (ENOMEM); _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); return (ENOBUFS); } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); reject: /* Send reject. Do not really care about the result */ send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid); return (0); } /* ng_l2cap_process_discon_req */ /* * Process L2CAP_DisconnectRsp command */ static int ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_discon_rsp_cp *cp = NULL; ng_l2cap_cmd_p cmd = NULL; u_int16_t scid, dcid; int error = 0; /* Get command parameters */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); if (con->rx_pkt == NULL) return (ENOBUFS); cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *); dcid = le16toh(cp->dcid); scid = le16toh(cp->scid); NG_FREE_M(con->rx_pkt); /* Check if we have pending command descriptor */ cmd = ng_l2cap_cmd_by_ident(con, ident); if (cmd == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), ident, con->con_handle); goto out; } /* Verify channel state, do nothing if invalid */ if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_DisconnectRsp. " \ "Invalid channel state, cid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), scid, cmd->ch->state); goto out; } /* Verify CIDs and send reject if does not match */ if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_DisconnectRsp. " \ "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n", __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid, scid, cmd->ch->dcid, dcid); goto out; } /* * Looks like we have successfuly disconnected channel, so notify * upper layer. If command timeout already happened then ignore * response. */ if ((error = ng_l2cap_command_untimeout(cmd)) != 0) goto out; error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS); ng_l2cap_free_chan(cmd->ch); /* this will free commands too */ out: return (error); } /* ng_l2cap_process_discon_rsp */ /* * Process L2CAP_EchoReq command */ static int ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_cmd_hdr_t *hdr = NULL; ng_l2cap_cmd_p cmd = NULL; con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr)); if (con->rx_pkt == NULL) { NG_L2CAP_ALERT( "%s: %s - ng_l2cap_prepend() failed, size=%zd\n", __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr)); return (ENOBUFS); } hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *); hdr->code = NG_L2CAP_ECHO_RSP; hdr->ident = ident; hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr)); cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0); if (cmd == NULL) { NG_FREE_M(con->rx_pkt); return (ENOBUFS); } /* Attach data and link command to the queue */ cmd->aux = con->rx_pkt; con->rx_pkt = NULL; ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); } /* ng_l2cap_process_echo_req */ /* * Process L2CAP_EchoRsp command */ static int ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_cmd_p cmd = NULL; int error = 0; /* Check if we have this command */ cmd = ng_l2cap_cmd_by_ident(con, ident); if (cmd != NULL) { /* If command timeout already happened then ignore response */ if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { NG_FREE_M(con->rx_pkt); return (error); } ng_l2cap_unlink_cmd(cmd); error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token, NG_L2CAP_SUCCESS, con->rx_pkt); ng_l2cap_free_cmd(cmd); con->rx_pkt = NULL; } else { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_EchoRsp command. " \ "Requested ident does not exist, ident=%d\n", __func__, NG_NODE_NAME(l2cap->node), ident); NG_FREE_M(con->rx_pkt); } return (error); } /* ng_l2cap_process_echo_rsp */ /* * Process L2CAP_InfoReq command */ static int ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_cmd_p cmd = NULL; u_int16_t type; /* Get command parameters */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp)); if (con->rx_pkt == NULL) return (ENOBUFS); type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type); NG_FREE_M(con->rx_pkt); cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0); if (cmd == NULL) return (ENOMEM); switch (type) { case NG_L2CAP_CONNLESS_MTU: _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU, NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT); break; default: _ng_l2cap_info_rsp(cmd->aux, ident, type, NG_L2CAP_NOT_SUPPORTED, 0); break; } if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); return (ENOBUFS); } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); } /* ng_l2cap_process_info_req */ /* * Process L2CAP_InfoRsp command */ static int ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_info_rsp_cp *cp = NULL; ng_l2cap_cmd_p cmd = NULL; int error = 0; /* Get command parameters */ NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp)); if (con->rx_pkt == NULL) return (ENOBUFS); cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *); cp->type = le16toh(cp->type); cp->result = le16toh(cp->result); m_adj(con->rx_pkt, sizeof(*cp)); /* Check if we have pending command descriptor */ cmd = ng_l2cap_cmd_by_ident(con, ident); if (cmd == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CAP_InfoRsp command. " \ "Requested ident does not exist, ident=%d\n", __func__, NG_NODE_NAME(l2cap->node), ident); NG_FREE_M(con->rx_pkt); return (ENOENT); } /* If command timeout already happened then ignore response */ if ((error = ng_l2cap_command_untimeout(cmd)) != 0) { NG_FREE_M(con->rx_pkt); return (error); } ng_l2cap_unlink_cmd(cmd); if (cp->result == NG_L2CAP_SUCCESS) { switch (cp->type) { case NG_L2CAP_CONNLESS_MTU: if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t)) *mtod(con->rx_pkt, u_int16_t *) = le16toh(*mtod(con->rx_pkt,u_int16_t *)); else { cp->result = NG_L2CAP_UNKNOWN; /* XXX */ NG_L2CAP_ERR( "%s: %s - invalid L2CAP_InfoRsp command. " \ "Bad connectionless MTU parameter, len=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->rx_pkt->m_pkthdr.len); } break; default: NG_L2CAP_WARN( "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n", __func__, NG_NODE_NAME(l2cap->node), cp->type); break; } } error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, cp->result, con->rx_pkt); ng_l2cap_free_cmd(cmd); con->rx_pkt = NULL; return (error); } /* ng_l2cap_process_info_rsp */ /* * Send L2CAP reject */ static int send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason, u_int16_t mtu, u_int16_t scid, u_int16_t dcid) { ng_l2cap_cmd_p cmd = NULL; cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0); if (cmd == NULL) return (ENOMEM); _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); return (ENOBUFS); } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); } /* send_l2cap_reject */ /* * Send L2CAP connection reject */ static int send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, u_int16_t dcid, u_int16_t result) { ng_l2cap_cmd_p cmd = NULL; cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0); if (cmd == NULL) return (ENOMEM); _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); return (ENOBUFS); } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); } /* send_l2cap_con_rej */ /* * Send L2CAP config response */ static int send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid, u_int16_t result, struct mbuf *opt) { ng_l2cap_cmd_p cmd = NULL; cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0); if (cmd == NULL) { NG_FREE_M(opt); return (ENOMEM); } _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); return (ENOBUFS); } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); } /* send_l2cap_cfg_rsp */ static int send_l2cap_param_urs(ng_l2cap_con_p con, u_int8_t ident, u_int16_t result) { ng_l2cap_cmd_p cmd = NULL; cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE, 0); if (cmd == NULL) { return (ENOMEM); } _ng_l2cap_cmd_urs(cmd->aux, cmd->ident, result); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); return (ENOBUFS); } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); return (0); } /* send_l2cap_cfg_rsp */ /* * Get next L2CAP configuration option * * Return codes: * 0 no option * 1 we have got option * -1 header too short * -2 bad option value or length * -3 unknown option */ static int get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr, ng_l2cap_cfg_opt_val_p val) { int hint, len = m->m_pkthdr.len - (*off); if (len == 0) return (0); if (len < 0 || len < sizeof(*hdr)) return (-1); m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr); *off += sizeof(*hdr); len -= sizeof(*hdr); hint = NG_L2CAP_OPT_HINT(hdr->type); hdr->type &= NG_L2CAP_OPT_HINT_MASK; switch (hdr->type) { case NG_L2CAP_OPT_MTU: if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length) return (-2); m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val); val->mtu = le16toh(val->mtu); *off += NG_L2CAP_OPT_MTU_SIZE; break; case NG_L2CAP_OPT_FLUSH_TIMO: if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE || len < hdr->length) return (-2); m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val); val->flush_timo = le16toh(val->flush_timo); *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE; break; case NG_L2CAP_OPT_QOS: if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length) return (-2); m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val); val->flow.token_rate = le32toh(val->flow.token_rate); val->flow.token_bucket_size = le32toh(val->flow.token_bucket_size); val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth); val->flow.latency = le32toh(val->flow.latency); val->flow.delay_variation = le32toh(val->flow.delay_variation); *off += NG_L2CAP_OPT_QOS_SIZE; break; default: if (hint) *off += hdr->length; else return (-3); break; } return (1); } /* get_next_l2cap_opt */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c (revision 290038) @@ -1,915 +1,967 @@ /* * ng_l2cap_llpi.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_llpi.c,v 1.5 2003/09/08 19:11:45 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** ****************************************************************************** ** Lower Layer Protocol (HCI) Interface module ****************************************************************************** ******************************************************************************/ /* * Send LP_ConnectReq event to the lower layer protocol. Create new connection * descriptor and initialize it. Create LP_ConnectReq event and send it to the * lower layer, then adjust connection state and start timer. The function WILL * FAIL if connection to the remote unit already exists. */ int ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr, int type) { struct ng_mesg *msg = NULL; ng_hci_lp_con_req_ep *ep = NULL; ng_l2cap_con_p con = NULL; int error = 0; /* Verify that we DO NOT have connection to the remote unit */ con = ng_l2cap_con_by_addr(l2cap, bdaddr, type); if (con != NULL) { NG_L2CAP_ALERT( "%s: %s - unexpected LP_ConnectReq event. " \ "Connection already exists, state=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state, con->con_handle); return (EEXIST); } /* Check if lower layer protocol is still connected */ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { NG_L2CAP_ERR( "%s: %s - hook \"%s\" is not connected or valid\n", __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); return (ENOTCONN); } /* Create and intialize new connection descriptor */ con = ng_l2cap_new_con(l2cap, bdaddr, type); if (con == NULL) return (ENOMEM); /* Create and send LP_ConnectReq event */ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ, sizeof(*ep), M_NOWAIT); if (msg == NULL) { ng_l2cap_free_con(con); return (ENOMEM); } ep = (ng_hci_lp_con_req_ep *) (msg->data); bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); ep->link_type = type; con->flags |= NG_L2CAP_CON_OUTGOING; con->state = NG_L2CAP_W4_LP_CON_CFM; ng_l2cap_lp_timeout(con); NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); if (error != 0) { if (ng_l2cap_lp_untimeout(con) == 0) ng_l2cap_free_con(con); /* * Do not free connection if ng_l2cap_lp_untimeout() failed * let timeout handler deal with it. Always return error to * the caller. */ } return (error); } /* ng_l2cap_lp_con_req */ /* * Process LP_ConnectCfm event from the lower layer protocol. It could be * positive or negative. Verify remote unit address then stop the timer and * process event. */ int ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_hci_lp_con_cfm_ep *ep = NULL; ng_l2cap_con_p con = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ep)) { NG_L2CAP_ALERT( "%s: %s - invalid LP_ConnectCfm[Neg] message size\n", __func__, NG_NODE_NAME(l2cap->node)); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_con_cfm_ep *) (msg->data); /* Check if we have requested/accepted this connection */ con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type); if (con == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n", __func__, NG_NODE_NAME(l2cap->node)); error = ENOENT; goto out; } /* Check connection state */ if (con->state != NG_L2CAP_W4_LP_CON_CFM) { NG_L2CAP_ALERT( "%s: %s - unexpected LP_ConnectCfm event. " \ "Invalid connection state, state=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state, con->con_handle); error = EINVAL; goto out; } /* * Looks like it is our confirmation. It is safe now to cancel * connection timer and notify upper layer. If timeout already * happened then ignore connection confirmation and let timeout * handle that. */ if ((error = ng_l2cap_lp_untimeout(con)) != 0) goto out; if (ep->status == 0) { con->state = NG_L2CAP_CON_OPEN; con->con_handle = ep->con_handle; ng_l2cap_lp_deliver(con); } else /* Negative confirmation - remove connection descriptor */ ng_l2cap_con_fail(con, ep->status); out: return (error); } /* ng_l2cap_lp_con_cfm */ /* * Process LP_ConnectInd event from the lower layer protocol. This is a good * place to put some extra check on remote unit address and/or class. We could * even forward this information to control hook (or check against internal * black list) and thus implement some kind of firewall. But for now be simple * and create new connection descriptor, start timer and send LP_ConnectRsp * event (i.e. accept connection). */ int ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_hci_lp_con_ind_ep *ep = NULL; ng_hci_lp_con_rsp_ep *rp = NULL; struct ng_mesg *rsp = NULL; ng_l2cap_con_p con = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ep)) { NG_L2CAP_ALERT( "%s: %s - invalid LP_ConnectInd message size\n", __func__, NG_NODE_NAME(l2cap->node)); return (EMSGSIZE); } ep = (ng_hci_lp_con_ind_ep *) (msg->data); /* Make sure we have only one connection to the remote unit */ con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type); if (con != NULL) { NG_L2CAP_ALERT( "%s: %s - unexpected LP_ConnectInd event. " \ "Connection already exists, state=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state, con->con_handle); return (EEXIST); } /* Check if lower layer protocol is still connected */ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { NG_L2CAP_ERR( "%s: %s - hook \"%s\" is not connected or valid", __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); return (ENOTCONN); } /* Create and intialize new connection descriptor */ con = ng_l2cap_new_con(l2cap, &ep->bdaddr, ep->link_type); if (con == NULL) return (ENOMEM); /* Create and send LP_ConnectRsp event */ NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP, sizeof(*rp), M_NOWAIT); if (rsp == NULL) { ng_l2cap_free_con(con); return (ENOMEM); } rp = (ng_hci_lp_con_rsp_ep *)(rsp->data); rp->status = 0x00; /* accept connection */ rp->link_type = NG_HCI_LINK_ACL; bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr)); con->state = NG_L2CAP_W4_LP_CON_CFM; ng_l2cap_lp_timeout(con); NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0); if (error != 0) { if (ng_l2cap_lp_untimeout(con) == 0) ng_l2cap_free_con(con); /* * Do not free connection if ng_l2cap_lp_untimeout() failed * let timeout handler deal with it. Always return error to * the caller. */ } return (error); } /* ng_l2cap_lp_con_ind */ /* * Process LP_DisconnectInd event from the lower layer protocol. We have been * disconnected from the remote unit. So notify the upper layer protocol. */ int ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_hci_lp_discon_ind_ep *ep = NULL; ng_l2cap_con_p con = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ep)) { NG_L2CAP_ALERT( "%s: %s - invalid LP_DisconnectInd message size\n", __func__, NG_NODE_NAME(l2cap->node)); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_discon_ind_ep *) (msg->data); /* Check if we have this connection */ con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); if (con == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected LP_DisconnectInd event. " \ "Connection does not exist, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); error = ENOENT; goto out; } /* XXX Verify connection state -- do we need to check this? */ if (con->state != NG_L2CAP_CON_OPEN) { NG_L2CAP_ERR( "%s: %s - unexpected LP_DisconnectInd event. " \ "Invalid connection state, state=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state, con->con_handle); error = EINVAL; goto out; } /* * Notify upper layer and remove connection * Note: The connection could have auto disconnect timeout set. Try * to remove it. If auto disconnect timeout happened then ignore * disconnect indication and let timeout handle that. */ if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) if ((error = ng_l2cap_discon_untimeout(con)) != 0) return (error); ng_l2cap_con_fail(con, ep->reason); out: return (error); } /* ng_l2cap_lp_discon_ind */ /* * Send LP_QoSSetupReq event to the lower layer protocol */ int ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle, ng_l2cap_flow_p flow) { struct ng_mesg *msg = NULL; ng_hci_lp_qos_req_ep *ep = NULL; ng_l2cap_con_p con = NULL; int error = 0; /* Verify that we have this connection */ con = ng_l2cap_con_by_handle(l2cap, con_handle); if (con == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected LP_QoSSetupReq event. " \ "Connection does not exist, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con_handle); return (ENOENT); } /* Verify connection state */ if (con->state != NG_L2CAP_CON_OPEN) { NG_L2CAP_ERR( "%s: %s - unexpected LP_QoSSetupReq event. " \ "Invalid connection state, state=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state, con->con_handle); return (EINVAL); } /* Check if lower layer protocol is still connected */ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { NG_L2CAP_ERR( "%s: %s - hook \"%s\" is not connected or valid", __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); return (ENOTCONN); } /* Create and send LP_QoSSetupReq event */ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ, sizeof(*ep), M_NOWAIT); if (msg == NULL) return (ENOMEM); ep = (ng_hci_lp_qos_req_ep *) (msg->data); ep->con_handle = con_handle; ep->flags = flow->flags; ep->service_type = flow->service_type; ep->token_rate = flow->token_rate; ep->peak_bandwidth = flow->peak_bandwidth; ep->latency = flow->latency; ep->delay_variation = flow->delay_variation; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); return (error); } /* ng_l2cap_lp_con_req */ /* * Process LP_QoSSetupCfm from the lower layer protocol */ int ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_hci_lp_qos_cfm_ep *ep = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ep)) { NG_L2CAP_ALERT( "%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n", __func__, NG_NODE_NAME(l2cap->node)); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_qos_cfm_ep *) (msg->data); /* XXX FIXME do something */ out: return (error); } /* ng_l2cap_lp_qos_cfm */ /* * Process LP_QoSViolationInd event from the lower layer protocol. Lower * layer protocol has detected QoS Violation, so we MUST notify the * upper layer. */ int ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_hci_lp_qos_ind_ep *ep = NULL; ng_l2cap_con_p con = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ep)) { NG_L2CAP_ALERT( "%s: %s - invalid LP_QoSViolation message size\n", __func__, NG_NODE_NAME(l2cap->node)); error = EMSGSIZE; goto out; } ep = (ng_hci_lp_qos_ind_ep *) (msg->data); /* Check if we have this connection */ con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); if (con == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected LP_QoSViolationInd event. " \ "Connection does not exist, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); error = ENOENT; goto out; } /* Verify connection state */ if (con->state != NG_L2CAP_CON_OPEN) { NG_L2CAP_ERR( "%s: %s - unexpected LP_QoSViolationInd event. " \ "Invalid connection state, state=%d, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state, con->con_handle); error = EINVAL; goto out; } /* XXX FIXME Notify upper layer and terminate channels if required */ out: return (error); } /* ng_l2cap_qos_ind */ +int +ng_l2cap_lp_enc_change(ng_l2cap_p l2cap, struct ng_mesg *msg) +{ + ng_hci_lp_enc_change_ep *ep = NULL; + ng_l2cap_con_p con = NULL; + int error = 0; + ng_l2cap_chan_p ch = NULL; + /* Check message */ + if (msg->header.arglen != sizeof(*ep)) { + NG_L2CAP_ALERT( +"%s: %s - invalid LP_ENCChange message size\n", + __func__, NG_NODE_NAME(l2cap->node)); + error = EMSGSIZE; + goto out; + } + + ep = (ng_hci_lp_enc_change_ep *) (msg->data); + + /* Check if we have this connection */ + con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); + if (con == NULL) { + NG_L2CAP_ERR( +"%s: %s - unexpected LP_Enc Change Event. " \ +"Connection does not exist, con_handle=%d\n", + __func__, NG_NODE_NAME(l2cap->node), ep->con_handle); + error = ENOENT; + goto out; + } + + /* Verify connection state */ + if (con->state != NG_L2CAP_CON_OPEN) { + NG_L2CAP_ERR( +"%s: %s - unexpected ENC_CHANGE event. " \ +"Invalid connection state, state=%d, con_handle=%d\n", + __func__, NG_NODE_NAME(l2cap->node), con->state, + con->con_handle); + error = EINVAL; + goto out; + } + + con->encryption = ep->status; + + LIST_FOREACH(ch, &l2cap->chan_list, next){ + if((ch->con->con_handle == ep->con_handle) && + (ch->con->linktype == ep->link_type)) + ng_l2cap_l2ca_encryption_change(ch, ep->status); + } + +out: + return (error); +} /* ng_l2cap_enc_change */ + /* * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then * segment it according to HCI MTU. */ int ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_hdr_t *l2cap_hdr = NULL; ng_hci_acldata_pkt_t *acl_hdr = NULL; struct mbuf *m_last = NULL, *m = NULL; int len, flag = NG_HCI_PACKET_START; KASSERT((con->tx_pkt == NULL), ("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node))); KASSERT((l2cap->pkt_size > 0), ("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node))); /* Prepend mbuf with L2CAP header */ m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr)); if (m0 == NULL) { NG_L2CAP_ALERT( "%s: %s - ng_l2cap_prepend(%zd) failed\n", __func__, NG_NODE_NAME(l2cap->node), sizeof(*l2cap_hdr)); goto fail; } l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *); l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr)); l2cap_hdr->dcid = htole16(dcid); /* * Segment single L2CAP packet according to the HCI layer MTU. Convert * each segment into ACL data packet and prepend it with ACL data packet * header. Link all segments together via m_nextpkt link. * * XXX BC (Broadcast flag) will always be 0 (zero). */ while (m0 != NULL) { /* Check length of the packet against HCI MTU */ len = m0->m_pkthdr.len; if (len > l2cap->pkt_size) { m = m_split(m0, l2cap->pkt_size, M_NOWAIT); if (m == NULL) { NG_L2CAP_ALERT( "%s: %s - m_split(%d) failed\n", __func__, NG_NODE_NAME(l2cap->node), l2cap->pkt_size); goto fail; } len = l2cap->pkt_size; } /* Convert packet fragment into ACL data packet */ m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr)); if (m0 == NULL) { NG_L2CAP_ALERT( "%s: %s - ng_l2cap_prepend(%zd) failed\n", __func__, NG_NODE_NAME(l2cap->node), sizeof(*acl_hdr)); goto fail; } acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *); acl_hdr->type = NG_HCI_ACL_DATA_PKT; acl_hdr->length = htole16(len); acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE( con->con_handle, flag, 0)); /* Add fragment to the chain */ m0->m_nextpkt = NULL; if (con->tx_pkt == NULL) con->tx_pkt = m_last = m0; else { m_last->m_nextpkt = m0; m_last = m0; } NG_L2CAP_INFO( "%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->con_handle, flag, len); m0 = m; m = NULL; flag = NG_HCI_PACKET_FRAGMENT; } return (0); fail: NG_FREE_M(m0); NG_FREE_M(m); while (con->tx_pkt != NULL) { m = con->tx_pkt->m_nextpkt; m_freem(con->tx_pkt); con->tx_pkt = m; } return (ENOBUFS); } /* ng_l2cap_lp_send */ /* * Receive ACL data packet from the HCI layer. First strip ACL packet header * and get connection handle, PB (Packet Boundary) flag and payload length. * Then find connection descriptor and verify its state. Then process ACL * packet as follows. * * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP * header and get total length of the L2CAP packet. Then start new L2CAP * packet. * * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT) * then add segment to the packet. */ int ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m) { ng_hci_acldata_pkt_t *acl_hdr = NULL; ng_l2cap_hdr_t *l2cap_hdr = NULL; ng_l2cap_con_p con = NULL; u_int16_t con_handle, length, pb; int error = 0; /* Check ACL data packet */ if (m->m_pkthdr.len < sizeof(*acl_hdr)) { NG_L2CAP_ERR( "%s: %s - invalid ACL data packet. Packet too small, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len); error = EMSGSIZE; goto drop; } /* Strip ACL data packet header */ NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr)); if (m == NULL) return (ENOBUFS); acl_hdr = mtod(m, ng_hci_acldata_pkt_t *); m_adj(m, sizeof(*acl_hdr)); /* Get ACL connection handle, PB flag and payload length */ acl_hdr->con_handle = le16toh(acl_hdr->con_handle); con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle); pb = NG_HCI_PB_FLAG(acl_hdr->con_handle); length = le16toh(acl_hdr->length); NG_L2CAP_INFO( "%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length); /* Get connection descriptor */ con = ng_l2cap_con_by_handle(l2cap, con_handle); if (con == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected ACL data packet. " \ "Connection does not exist, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con_handle); error = ENOENT; goto drop; } /* Verify connection state */ if (con->state != NG_L2CAP_CON_OPEN) { NG_L2CAP_ERR( "%s: %s - unexpected ACL data packet. Invalid connection state=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->state); error = EHOSTDOWN; goto drop; } /* Process packet */ if (pb == NG_HCI_PACKET_START) { if (con->rx_pkt != NULL) { NG_L2CAP_ERR( "%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n", __func__, NG_NODE_NAME(l2cap->node), con->rx_pkt->m_pkthdr.len, con->rx_pkt_len); NG_FREE_M(con->rx_pkt); con->rx_pkt_len = 0; } /* Get L2CAP header */ if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len); error = EMSGSIZE; goto drop; } NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr)); if (m == NULL) return (ENOBUFS); l2cap_hdr = mtod(m, ng_l2cap_hdr_t *); NG_L2CAP_INFO( "%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), con_handle, le16toh(l2cap_hdr->length)); /* Start new L2CAP packet */ con->rx_pkt = m; con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr); } else if (pb == NG_HCI_PACKET_FRAGMENT) { if (con->rx_pkt == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected ACL data packet fragment, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->con_handle); goto drop; } /* Add fragment to the L2CAP packet */ m_cat(con->rx_pkt, m); con->rx_pkt->m_pkthdr.len += length; } else { NG_L2CAP_ERR( "%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n", __func__, NG_NODE_NAME(l2cap->node), pb); error = EINVAL; goto drop; } con->rx_pkt_len -= length; if (con->rx_pkt_len < 0) { NG_L2CAP_ALERT( "%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n", __func__, NG_NODE_NAME(l2cap->node), con->rx_pkt->m_pkthdr.len, con->rx_pkt_len); NG_FREE_M(con->rx_pkt); con->rx_pkt_len = 0; } else if (con->rx_pkt_len == 0) { /* OK, we have got complete L2CAP packet, so process it */ error = ng_l2cap_receive(con); con->rx_pkt = NULL; con->rx_pkt_len = 0; } return (error); drop: NG_FREE_M(m); return (error); } /* ng_l2cap_lp_receive */ /* * Send queued ACL packets to the HCI layer */ void ng_l2cap_lp_deliver(ng_l2cap_con_p con) { ng_l2cap_p l2cap = con->l2cap; struct mbuf *m = NULL; int error; /* Check connection */ if (con->state != NG_L2CAP_CON_OPEN) return; if (con->tx_pkt == NULL) ng_l2cap_con_wakeup(con); if (con->tx_pkt == NULL) return; /* Check if lower layer protocol is still connected */ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { NG_L2CAP_ERR( "%s: %s - hook \"%s\" is not connected or valid", __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); goto drop; /* XXX what to do with "pending"? */ } /* Send ACL data packets */ while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) { m = con->tx_pkt; con->tx_pkt = con->tx_pkt->m_nextpkt; m->m_nextpkt = NULL; if(m->m_flags &M_PROTO2){ ng_l2cap_lp_receive(con->l2cap, m); continue; } NG_L2CAP_INFO( "%s: %s - sending ACL packet, con_handle=%d, len=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->con_handle, m->m_pkthdr.len); NG_SEND_DATA_ONLY(error, l2cap->hci, m); if (error != 0) { NG_L2CAP_ERR( "%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->con_handle, error); goto drop; /* XXX what to do with "pending"? */ } con->pending ++; } NG_L2CAP_INFO( "%s: %s - %d ACL packets have been sent, con_handle=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->pending, con->con_handle); return; drop: while (con->tx_pkt != NULL) { m = con->tx_pkt->m_nextpkt; m_freem(con->tx_pkt); con->tx_pkt = m; } } /* ng_l2cap_lp_deliver */ /* * Process connection timeout. Remove connection from the list. If there * are any channels that wait for the connection then notify them. Free * connection descriptor. */ void ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle) { ng_l2cap_p l2cap = NULL; ng_l2cap_con_p con = NULL; if (NG_NODE_NOT_VALID(node)) { printf("%s: Netgraph node is not valid\n", __func__); return; } l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); con = ng_l2cap_con_by_handle(l2cap, con_handle); if (con == NULL) { NG_L2CAP_ALERT( "%s: %s - could not find connection, con_handle=%d\n", __func__, NG_NODE_NAME(node), con_handle); return; } if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) { NG_L2CAP_ALERT( "%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(node), con_handle, con->state, con->flags); return; } /* * Notify channels that connection has timed out. This will remove * connection, channels and pending commands. */ con->flags &= ~NG_L2CAP_CON_LP_TIMO; ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT); } /* ng_l2cap_process_lp_timeout */ /* * Process auto disconnect timeout and send LP_DisconReq event to the * lower layer protocol */ void ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle) { ng_l2cap_p l2cap = NULL; ng_l2cap_con_p con = NULL; struct ng_mesg *msg = NULL; ng_hci_lp_discon_req_ep *ep = NULL; int error; if (NG_NODE_NOT_VALID(node)) { printf("%s: Netgraph node is not valid\n", __func__); return; } l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); con = ng_l2cap_con_by_handle(l2cap, con_handle); if (con == NULL) { NG_L2CAP_ALERT( "%s: %s - could not find connection, con_handle=%d\n", __func__, NG_NODE_NAME(node), con_handle); return; } if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) { NG_L2CAP_ALERT( "%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(node), con_handle, con->state, con->flags); return; } con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; /* Check if lower layer protocol is still connected */ if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) { NG_L2CAP_ERR( "%s: %s - hook \"%s\" is not connected or valid\n", __func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI); return; } /* Create and send LP_DisconReq event */ NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ, sizeof(*ep), M_NOWAIT); if (msg == NULL) return; ep = (ng_hci_lp_discon_req_ep *) (msg->data); ep->con_handle = con->con_handle; ep->reason = 0x13; /* User Ended Connection */ NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0); } /* ng_l2cap_process_discon_timeout */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.h (revision 290038) @@ -1,51 +1,52 @@ /* * ng_l2cap_llpi.h */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_llpi.h,v 1.2 2003/04/28 21:44:59 max Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_L2CAP_LLPI_H_ #define _NETGRAPH_L2CAP_LLPI_H_ int ng_l2cap_lp_con_req (ng_l2cap_p, bdaddr_p, int); int ng_l2cap_lp_con_cfm (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_lp_con_ind (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_lp_discon_ind (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_lp_qos_req (ng_l2cap_p, u_int16_t, ng_l2cap_flow_p); int ng_l2cap_lp_qos_cfm (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_lp_qos_ind (ng_l2cap_p, struct ng_mesg *); +int ng_l2cap_lp_enc_change (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_lp_send (ng_l2cap_con_p, u_int16_t,struct mbuf *); int ng_l2cap_lp_receive (ng_l2cap_p, struct mbuf *); void ng_l2cap_lp_deliver (ng_l2cap_con_p); void ng_l2cap_process_lp_timeout (node_p, hook_p, void *, int); void ng_l2cap_process_discon_timeout (node_p, hook_p, void *, int); #endif /* ndef _NETGRAPH_L2CAP_LLPI_H_ */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c (revision 290038) @@ -1,756 +1,758 @@ /* * ng_l2cap_main.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_main.c,v 1.2 2003/04/28 21:44:59 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** ****************************************************************************** ** This node implements Link Layer Control and Adaptation Protocol (L2CAP) ****************************************************************************** ******************************************************************************/ /* MALLOC define */ #ifdef NG_SEPARATE_MALLOC MALLOC_DEFINE(M_NETGRAPH_L2CAP, "netgraph_l2cap", "Netgraph Bluetooth L2CAP node"); #else #define M_NETGRAPH_L2CAP M_NETGRAPH #endif /* NG_SEPARATE_MALLOC */ /* Netgraph node methods */ static ng_constructor_t ng_l2cap_constructor; static ng_shutdown_t ng_l2cap_shutdown; static ng_newhook_t ng_l2cap_newhook; static ng_connect_t ng_l2cap_connect; static ng_disconnect_t ng_l2cap_disconnect; static ng_rcvmsg_t ng_l2cap_lower_rcvmsg; static ng_rcvmsg_t ng_l2cap_upper_rcvmsg; static ng_rcvmsg_t ng_l2cap_default_rcvmsg; static ng_rcvdata_t ng_l2cap_rcvdata; /* Netgraph node type descriptor */ static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_L2CAP_NODE_TYPE, .constructor = ng_l2cap_constructor, .rcvmsg = ng_l2cap_default_rcvmsg, .shutdown = ng_l2cap_shutdown, .newhook = ng_l2cap_newhook, .connect = ng_l2cap_connect, .rcvdata = ng_l2cap_rcvdata, .disconnect = ng_l2cap_disconnect, .cmdlist = ng_l2cap_cmdlist, }; NETGRAPH_INIT(l2cap, &typestruct); MODULE_VERSION(ng_l2cap, NG_BLUETOOTH_VERSION); MODULE_DEPEND(ng_l2cap, ng_bluetooth, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); /***************************************************************************** ***************************************************************************** ** Netgraph methods implementation ***************************************************************************** *****************************************************************************/ static void ng_l2cap_cleanup (ng_l2cap_p); static void ng_l2cap_destroy_channels (ng_l2cap_p); /* * Create new instance of L2CAP node */ static int ng_l2cap_constructor(node_p node) { ng_l2cap_p l2cap = NULL; /* Create new L2CAP node */ l2cap = malloc(sizeof(*l2cap), M_NETGRAPH_L2CAP, M_WAITOK | M_ZERO); l2cap->node = node; l2cap->debug = NG_L2CAP_WARN_LEVEL; l2cap->discon_timo = 5; /* sec */ LIST_INIT(&l2cap->con_list); LIST_INIT(&l2cap->chan_list); NG_NODE_SET_PRIVATE(node, l2cap); NG_NODE_FORCE_WRITER(node); return (0); } /* ng_l2cap_constructor */ /* * Shutdown L2CAP node */ static int ng_l2cap_shutdown(node_p node) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); /* Clean up L2CAP node. Delete all connection, channels and commands */ l2cap->node = NULL; ng_l2cap_cleanup(l2cap); bzero(l2cap, sizeof(*l2cap)); free(l2cap, M_NETGRAPH_L2CAP); return (0); } /* ng_l2cap_shutdown */ /* * Give our OK for a hook to be added. HCI layer is connected to the HCI * (NG_L2CAP_HOOK_HCI) hook. As per specification L2CAP layer MUST provide * Procol/Service Multiplexing, so the L2CAP node provides separate hooks * for SDP (NG_L2CAP_HOOK_SDP), RFCOMM (NG_L2CAP_HOOK_RFCOMM) and TCP * (NG_L2CAP_HOOK_TCP) protcols. Unknown PSM will be forwarded to * NG_L2CAP_HOOK_ORPHAN hook. Control node/application is connected to * control (NG_L2CAP_HOOK_CTL) hook. */ static int ng_l2cap_newhook(node_p node, hook_p hook, char const *name) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); hook_p *h = NULL; if (strcmp(name, NG_L2CAP_HOOK_HCI) == 0) h = &l2cap->hci; else if (strcmp(name, NG_L2CAP_HOOK_L2C) == 0) h = &l2cap->l2c; else if (strcmp(name, NG_L2CAP_HOOK_CTL) == 0) h = &l2cap->ctl; else return (EINVAL); if (*h != NULL) return (EISCONN); *h = hook; return (0); } /* ng_l2cap_newhook */ /* * Give our final OK to connect hook. Nothing to do here. */ static int ng_l2cap_connect(hook_p hook) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int error = 0; if (hook == l2cap->hci) NG_HOOK_SET_RCVMSG(hook, ng_l2cap_lower_rcvmsg); else if (hook == l2cap->l2c || hook == l2cap->ctl) { NG_HOOK_SET_RCVMSG(hook, ng_l2cap_upper_rcvmsg); /* Send delayed notification to the upper layer */ error = ng_send_fn(l2cap->node, hook, ng_l2cap_send_hook_info, NULL, 0); } else error = EINVAL; return (error); } /* ng_l2cap_connect */ /* * Disconnect the hook. For downstream hook we must notify upper layers. * * XXX For upstream hooks this is really ugly :( Hook was disconnected and it * XXX is now too late to do anything. For now we just clean up our own mess * XXX and remove all channels that use disconnected upstream hook. If we don't * XXX do that then L2CAP node can get out of sync with upper layers. * XXX No notification will be sent to remote peer. */ static int ng_l2cap_disconnect(hook_p hook) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); hook_p *h = NULL; if (hook == l2cap->hci) { ng_l2cap_cleanup(l2cap); h = &l2cap->hci; } else if (hook == l2cap->l2c) { ng_l2cap_destroy_channels(l2cap); h = &l2cap->l2c; } else if (hook == l2cap->ctl) h = &l2cap->ctl; else return (EINVAL); *h = NULL; /* Shutdown when all hooks are disconnected */ if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 && NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); } /* ng_l2cap_disconnect */ /* * Process control message from lower layer */ static int ng_l2cap_lower_rcvmsg(node_p node, item_p item, hook_p lasthook) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); struct ng_mesg *msg = NGI_MSG(item); /* item still has message */ int error = 0; switch (msg->header.typecookie) { case NGM_HCI_COOKIE: switch (msg->header.cmd) { /* HCI node is ready */ case NGM_HCI_NODE_UP: { ng_hci_node_up_ep *ep = NULL; if (msg->header.arglen != sizeof(*ep)) error = EMSGSIZE; else { ep = (ng_hci_node_up_ep *)(msg->data); NG_L2CAP_INFO( "%s: %s - HCI node is up, bdaddr: %x:%x:%x:%x:%x:%x, " \ "pkt_size=%d bytes, num_pkts=%d\n", __func__, NG_NODE_NAME(l2cap->node), ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0], ep->pkt_size, ep->num_pkts); bcopy(&ep->bdaddr, &l2cap->bdaddr, sizeof(l2cap->bdaddr)); l2cap->pkt_size = ep->pkt_size; l2cap->num_pkts = ep->num_pkts; /* Notify upper layers */ ng_l2cap_send_hook_info(l2cap->node, l2cap->l2c, NULL, 0); ng_l2cap_send_hook_info(l2cap->node, l2cap->ctl, NULL, 0); } } break; case NGM_HCI_SYNC_CON_QUEUE: { ng_hci_sync_con_queue_ep *ep = NULL; ng_l2cap_con_p con = NULL; if (msg->header.arglen != sizeof(*ep)) error = EMSGSIZE; else { ep = (ng_hci_sync_con_queue_ep *)(msg->data); con = ng_l2cap_con_by_handle(l2cap, ep->con_handle); if (con == NULL) break; NG_L2CAP_INFO( "%s: %s - sync HCI connection queue, con_handle=%d, pending=%d, completed=%d\n", __func__, NG_NODE_NAME(l2cap->node), ep->con_handle, con->pending, ep->completed); con->pending -= ep->completed; if (con->pending < 0) { NG_L2CAP_WARN( "%s: %s - pending packet counter is out of sync! " \ "con_handle=%d, pending=%d, completed=%d\n", __func__, NG_NODE_NAME(l2cap->node), con->con_handle, con->pending, ep->completed); con->pending = 0; } ng_l2cap_lp_deliver(con); } } break; /* LP_ConnectCfm[Neg] */ case NGM_HCI_LP_CON_CFM: error = ng_l2cap_lp_con_cfm(l2cap, msg); break; /* LP_ConnectInd */ case NGM_HCI_LP_CON_IND: error = ng_l2cap_lp_con_ind(l2cap, msg); break; /* LP_DisconnectInd */ case NGM_HCI_LP_DISCON_IND: error = ng_l2cap_lp_discon_ind(l2cap, msg); break; /* LP_QoSSetupCfm[Neg] */ case NGM_HCI_LP_QOS_CFM: error = ng_l2cap_lp_qos_cfm(l2cap, msg); break; /* LP_OoSViolationInd */ case NGM_HCI_LP_QOS_IND: error = ng_l2cap_lp_qos_ind(l2cap, msg); break; - + case NGM_HCI_LP_ENC_CHG: + error = ng_l2cap_lp_enc_change(l2cap, msg); + break; default: error = EINVAL; break; } break; default: return (ng_l2cap_default_rcvmsg(node, item, lasthook)); /* NOT REACHED */ } NG_FREE_ITEM(item); return (error); } /* ng_l2cap_lower_rcvmsg */ /* * Process control message from upper layer */ static int ng_l2cap_upper_rcvmsg(node_p node, item_p item, hook_p lasthook) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); struct ng_mesg *msg = NGI_MSG(item); /* item still has message */ int error = 0; switch (msg->header.typecookie) { case NGM_L2CAP_COOKIE: switch (msg->header.cmd) { /* L2CA_Connect */ case NGM_L2CAP_L2CA_CON: error = ng_l2cap_l2ca_con_req(l2cap, msg); break; /* L2CA_ConnectRsp */ case NGM_L2CAP_L2CA_CON_RSP: error = ng_l2cap_l2ca_con_rsp_req(l2cap, msg); break; /* L2CA_Config */ case NGM_L2CAP_L2CA_CFG: error = ng_l2cap_l2ca_cfg_req(l2cap, msg); break; /* L2CA_ConfigRsp */ case NGM_L2CAP_L2CA_CFG_RSP: error = ng_l2cap_l2ca_cfg_rsp_req(l2cap, msg); break; /* L2CA_Disconnect */ case NGM_L2CAP_L2CA_DISCON: error = ng_l2cap_l2ca_discon_req(l2cap, msg); break; /* L2CA_GroupCreate */ case NGM_L2CAP_L2CA_GRP_CREATE: error = ng_l2cap_l2ca_grp_create(l2cap, msg); break; /* L2CA_GroupClose */ case NGM_L2CAP_L2CA_GRP_CLOSE: error = ng_l2cap_l2ca_grp_close(l2cap, msg); break; /* L2CA_GroupAddMember */ case NGM_L2CAP_L2CA_GRP_ADD_MEMBER: error = ng_l2cap_l2ca_grp_add_member_req(l2cap, msg); break; /* L2CA_GroupDeleteMember */ case NGM_L2CAP_L2CA_GRP_REM_MEMBER: error = ng_l2cap_l2ca_grp_rem_member(l2cap, msg); break; /* L2CA_GroupMembership */ case NGM_L2CAP_L2CA_GRP_MEMBERSHIP: error = ng_l2cap_l2ca_grp_get_members(l2cap, msg); break; /* L2CA_Ping */ case NGM_L2CAP_L2CA_PING: error = ng_l2cap_l2ca_ping_req(l2cap, msg); break; /* L2CA_GetInfo */ case NGM_L2CAP_L2CA_GET_INFO: error = ng_l2cap_l2ca_get_info_req(l2cap, msg); break; /* L2CA_EnableCLT */ case NGM_L2CAP_L2CA_ENABLE_CLT: error = ng_l2cap_l2ca_enable_clt(l2cap, msg); break; default: return (ng_l2cap_default_rcvmsg(node, item, lasthook)); /* NOT REACHED */ } break; default: return (ng_l2cap_default_rcvmsg(node, item, lasthook)); /* NOT REACHED */ } NG_FREE_ITEM(item); return (error); } /* ng_l2cap_upper_rcvmsg */ /* * Default control message processing routine */ static int ng_l2cap_default_rcvmsg(node_p node, item_p item, hook_p lasthook) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); struct ng_mesg *msg = NULL, *rsp = NULL; int error = 0; /* Detach and process message */ NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { case NGM_TEXT_STATUS: NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); if (rsp == NULL) error = ENOMEM; else snprintf(rsp->data, NG_TEXTRESPONSE, "bdaddr %x:%x:%x:%x:%x:%x, " \ "pkt_size %d\n" \ "Hooks %s %s %s\n" \ "Flags %#x\n", l2cap->bdaddr.b[5], l2cap->bdaddr.b[4], l2cap->bdaddr.b[3], l2cap->bdaddr.b[2], l2cap->bdaddr.b[1], l2cap->bdaddr.b[0], l2cap->pkt_size, (l2cap->hci != NULL)? NG_L2CAP_HOOK_HCI : "", (l2cap->l2c != NULL)? NG_L2CAP_HOOK_L2C : "", (l2cap->ctl != NULL)? NG_L2CAP_HOOK_CTL : "", l2cap->flags); break; default: error = EINVAL; break; } break; /* Messages from the upper layer or directed to the local node */ case NGM_L2CAP_COOKIE: switch (msg->header.cmd) { /* Get node flags */ case NGM_L2CAP_NODE_GET_FLAGS: NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_flags_ep), M_NOWAIT); if (rsp == NULL) error = ENOMEM; else *((ng_l2cap_node_flags_ep *)(rsp->data)) = l2cap->flags; break; /* Get node debug */ case NGM_L2CAP_NODE_GET_DEBUG: NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_debug_ep), M_NOWAIT); if (rsp == NULL) error = ENOMEM; else *((ng_l2cap_node_debug_ep *)(rsp->data)) = l2cap->debug; break; /* Set node debug */ case NGM_L2CAP_NODE_SET_DEBUG: if (msg->header.arglen != sizeof(ng_l2cap_node_debug_ep)) error = EMSGSIZE; else l2cap->debug = *((ng_l2cap_node_debug_ep *)(msg->data)); break; /* Get connection list */ case NGM_L2CAP_NODE_GET_CON_LIST: { ng_l2cap_con_p con = NULL; ng_l2cap_node_con_list_ep *e1 = NULL; ng_l2cap_node_con_ep *e2 = NULL; int n = 0; /* Count number of connections */ LIST_FOREACH(con, &l2cap->con_list, next) n++; if (n > NG_L2CAP_MAX_CON_NUM) n = NG_L2CAP_MAX_CON_NUM; /* Prepare response */ NG_MKRESPONSE(rsp, msg, sizeof(*e1) + n * sizeof(*e2), M_NOWAIT); if (rsp == NULL) { error = ENOMEM; break; } e1 = (ng_l2cap_node_con_list_ep *)(rsp->data); e2 = (ng_l2cap_node_con_ep *)(e1 + 1); e1->num_connections = n; LIST_FOREACH(con, &l2cap->con_list, next) { e2->state = con->state; e2->flags = con->flags; if (con->tx_pkt != NULL) e2->flags |= NG_L2CAP_CON_TX; if (con->rx_pkt != NULL) e2->flags |= NG_L2CAP_CON_RX; e2->pending = con->pending; e2->con_handle = con->con_handle; bcopy(&con->remote, &e2->remote, sizeof(e2->remote)); e2 ++; if (--n <= 0) break; } } break; /* Get channel list */ case NGM_L2CAP_NODE_GET_CHAN_LIST: { ng_l2cap_chan_p ch = NULL; ng_l2cap_node_chan_list_ep *e1 = NULL; ng_l2cap_node_chan_ep *e2 = NULL; int n = 0; /* Count number of channels */ LIST_FOREACH(ch, &l2cap->chan_list, next) n ++; if (n > NG_L2CAP_MAX_CHAN_NUM) n = NG_L2CAP_MAX_CHAN_NUM; /* Prepare response */ NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_chan_list_ep) + n * sizeof(ng_l2cap_node_chan_ep), M_NOWAIT); if (rsp == NULL) { error = ENOMEM; break; } e1 = (ng_l2cap_node_chan_list_ep *)(rsp->data); e2 = (ng_l2cap_node_chan_ep *)(e1 + 1); e1->num_channels = n; LIST_FOREACH(ch, &l2cap->chan_list, next) { e2->state = ch->state; e2->scid = ch->scid; e2->dcid = ch->dcid; e2->imtu = ch->imtu; e2->omtu = ch->omtu; e2->psm = ch->psm; bcopy(&ch->con->remote, &e2->remote, sizeof(e2->remote)); e2 ++; if (--n <= 0) break; } } break; case NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO: NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_auto_discon_ep), M_NOWAIT); if (rsp == NULL) error = ENOMEM; else *((ng_l2cap_node_auto_discon_ep *)(rsp->data)) = l2cap->discon_timo; break; case NGM_L2CAP_NODE_SET_AUTO_DISCON_TIMO: if (msg->header.arglen != sizeof(ng_l2cap_node_auto_discon_ep)) error = EMSGSIZE; else l2cap->discon_timo = *((ng_l2cap_node_auto_discon_ep *) (msg->data)); break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } NG_RESPOND_MSG(error, node, item, rsp); NG_FREE_MSG(msg); return (error); } /* ng_l2cap_rcvmsg */ /* * Process data packet from one of our hooks. * * From the HCI hook we expect to receive ACL data packets. ACL data packets * gets re-assembled into one L2CAP packet (according to length) and then gets * processed. * * NOTE: We expect to receive L2CAP packet header in the first fragment. * Otherwise we WILL NOT be able to get length of the L2CAP packet. * * Signaling L2CAP packets (destination channel ID == 0x1) are processed within * the node. Connectionless data packets (destination channel ID == 0x2) will * be forwarded to appropriate upstream hook unless it is not connected or * connectionless traffic for the specified PSM was disabled. * * From the upstream hooks we expect to receive data packets. These data * packets will be converted into L2CAP data packets. The length of each * L2CAP packet must not exceed channel's omtu (our peer's imtu). Then * these L2CAP packets will be converted to ACL data packets (according to * HCI layer MTU) and sent to lower layer. * * No data is expected from the control hook. */ static int ng_l2cap_rcvdata(hook_p hook, item_p item) { ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct mbuf *m = NULL; int error = 0; /* Detach mbuf, discard item and process data */ NGI_GET_M(item, m); NG_FREE_ITEM(item); if (hook == l2cap->hci) error = ng_l2cap_lp_receive(l2cap, m); else if (hook == l2cap->l2c) error = ng_l2cap_l2ca_write_req(l2cap, m); else { NG_FREE_M(m); error = EINVAL; } return (error); } /* ng_l2cap_rcvdata */ /* * Clean all connections, channels and commands for the L2CAP node */ static void ng_l2cap_cleanup(ng_l2cap_p l2cap) { ng_l2cap_con_p con = NULL; /* Clean up connection and channels */ while (!LIST_EMPTY(&l2cap->con_list)) { con = LIST_FIRST(&l2cap->con_list); if (con->flags & NG_L2CAP_CON_LP_TIMO) ng_l2cap_lp_untimeout(con); else if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) ng_l2cap_discon_untimeout(con); /* Connection terminated by local host */ ng_l2cap_con_fail(con, 0x16); } } /* ng_l2cap_cleanup */ /* * Destroy all channels that use specified upstream hook */ static void ng_l2cap_destroy_channels(ng_l2cap_p l2cap) { while (!LIST_EMPTY(&l2cap->chan_list)) ng_l2cap_free_chan(LIST_FIRST(&l2cap->chan_list)); } /* ng_l2cap_destroy_channels_by_hook */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c (revision 290038) @@ -1,695 +1,697 @@ /* * ng_l2cap_misc.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_misc.c,v 1.5 2003/09/08 19:11:45 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static u_int16_t ng_l2cap_get_cid (ng_l2cap_p, int); /****************************************************************************** ****************************************************************************** ** Utility routines ****************************************************************************** ******************************************************************************/ /* * Send hook information to the upper layer */ void ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2) { ng_l2cap_p l2cap = NULL; struct ng_mesg *msg = NULL; int error = 0; ng_l2cap_node_hook_info_ep *ep ; if (node == NULL || NG_NODE_NOT_VALID(node) || hook == NULL || NG_HOOK_NOT_VALID(hook)) return; l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) || bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0) return; NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO, sizeof(*ep), M_NOWAIT); if (msg != NULL) { ep = (ng_l2cap_node_hook_info_ep *) &msg->data; bcopy(&l2cap->bdaddr, &ep->addr, sizeof(bdaddr_t)); NG_SEND_MSG_HOOK(error, node, msg, hook, 0); } else error = ENOMEM; if (error != 0) NG_L2CAP_INFO( "%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n", __func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook), error); } /* ng_l2cap_send_hook_info */ /* * Create new connection descriptor for the "remote" unit. * Will link connection descriptor to the l2cap node. */ ng_l2cap_con_p ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr, int type) { static int fake_con_handle = 0x0f00; ng_l2cap_con_p con = NULL; /* Create new connection descriptor */ con = malloc(sizeof(*con), M_NETGRAPH_L2CAP, M_NOWAIT|M_ZERO); if (con == NULL) return (NULL); con->l2cap = l2cap; con->state = NG_L2CAP_CON_CLOSED; - + con->encryption = 0; /* * XXX * * Assign fake connection handle to the connection descriptor. * Bluetooth specification marks 0x0f00 - 0x0fff connection * handles as reserved. We need this fake connection handles * for timeouts. Connection handle will be passed as argument * to timeout so when timeout happens we can find the right * connection descriptor. We can not pass pointers, because * timeouts are external (to Netgraph) events and there might * be a race when node/hook goes down and timeout event already * went into node's queue */ con->con_handle = fake_con_handle ++; if (fake_con_handle > 0x0fff) fake_con_handle = 0x0f00; bcopy(bdaddr, &con->remote, sizeof(con->remote)); con->linktype = type; ng_callout_init(&con->con_timo); con->ident = NG_L2CAP_FIRST_IDENT - 1; TAILQ_INIT(&con->cmd_list); /* Link connection */ LIST_INSERT_HEAD(&l2cap->con_list, con, next); return (con); } /* ng_l2cap_new_con */ /* * Add reference to the connection descriptor */ void ng_l2cap_con_ref(ng_l2cap_con_p con) { con->refcnt ++; if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) { if ((con->state != NG_L2CAP_CON_OPEN) || (con->flags & NG_L2CAP_CON_OUTGOING) == 0) panic( "%s: %s - bad auto disconnect timeout, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(con->l2cap->node), con->state, con->flags); ng_l2cap_discon_untimeout(con); } } /* ng_l2cap_con_ref */ /* * Remove reference from the connection descriptor */ void ng_l2cap_con_unref(ng_l2cap_con_p con) { con->refcnt --; if (con->refcnt < 0) panic( "%s: %s - con->refcnt < 0\n", __func__, NG_NODE_NAME(con->l2cap->node)); /* * Set auto disconnect timer only if the following conditions are met: * 1) we have no reference on the connection * 2) connection is in OPEN state * 3) it is an outgoing connection * 4) disconnect timeout > 0 * 5) connection is not dying */ if ((con->refcnt == 0) && (con->state == NG_L2CAP_CON_OPEN) && (con->flags & NG_L2CAP_CON_OUTGOING) && (con->l2cap->discon_timo > 0) && ((con->flags & NG_L2CAP_CON_DYING) == 0)) ng_l2cap_discon_timeout(con); } /* ng_l2cap_con_unref */ /* * Set auto disconnect timeout * XXX FIXME: check return code from ng_callout */ int ng_l2cap_discon_timeout(ng_l2cap_con_p con) { if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) panic( "%s: %s - invalid timeout, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(con->l2cap->node), con->state, con->flags); con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO; ng_callout(&con->con_timo, con->l2cap->node, NULL, con->l2cap->discon_timo * hz, ng_l2cap_process_discon_timeout, NULL, con->con_handle); return (0); } /* ng_l2cap_discon_timeout */ /* * Unset auto disconnect timeout */ int ng_l2cap_discon_untimeout(ng_l2cap_con_p con) { if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) panic( "%s: %s - no disconnect timeout, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(con->l2cap->node), con->state, con->flags); if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) return (ETIMEDOUT); con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; return (0); } /* ng_l2cap_discon_untimeout */ /* * Free connection descriptor. Will unlink connection and free everything. */ void ng_l2cap_free_con(ng_l2cap_con_p con) { ng_l2cap_chan_p f = NULL, n = NULL; con->state = NG_L2CAP_CON_CLOSED; while (con->tx_pkt != NULL) { struct mbuf *m = con->tx_pkt->m_nextpkt; m_freem(con->tx_pkt); con->tx_pkt = m; } NG_FREE_M(con->rx_pkt); for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) { n = LIST_NEXT(f, next); if (f->con == con) ng_l2cap_free_chan(f); f = n; } while (!TAILQ_EMPTY(&con->cmd_list)) { ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list); ng_l2cap_unlink_cmd(cmd); if (cmd->flags & NG_L2CAP_CMD_PENDING) ng_l2cap_command_untimeout(cmd); ng_l2cap_free_cmd(cmd); } if (con->flags & (NG_L2CAP_CON_AUTO_DISCON_TIMO|NG_L2CAP_CON_LP_TIMO)) panic( "%s: %s - timeout pending! state=%d, flags=%#x\n", __func__, NG_NODE_NAME(con->l2cap->node), con->state, con->flags); LIST_REMOVE(con, next); bzero(con, sizeof(*con)); free(con, M_NETGRAPH_L2CAP); } /* ng_l2cap_free_con */ /* * Get connection by "remote" address */ ng_l2cap_con_p ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr, unsigned int type) { ng_l2cap_con_p con = NULL; LIST_FOREACH(con, &l2cap->con_list, next) if ((bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0)&& (con->linktype == type)) break; return (con); } /* ng_l2cap_con_by_addr */ /* * Get connection by "handle" */ ng_l2cap_con_p ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle) { ng_l2cap_con_p con = NULL; LIST_FOREACH(con, &l2cap->con_list, next) if (con->con_handle == con_handle) break; return (con); } /* ng_l2cap_con_by_handle */ /* * Allocate new L2CAP channel descriptor on "con" conection with "psm". * Will link the channel to the l2cap node */ ng_l2cap_chan_p ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm, int idtype) { ng_l2cap_chan_p ch = NULL; ch = malloc(sizeof(*ch), M_NETGRAPH_L2CAP, M_NOWAIT|M_ZERO); if (ch == NULL) return (NULL); if(idtype == NG_L2CAP_L2CA_IDTYPE_ATT){ ch->scid = ch->dcid = NG_L2CAP_ATT_CID; + }else if(idtype == NG_L2CAP_L2CA_IDTYPE_SMP){ + ch->scid = ch->dcid = NG_L2CAP_SMP_CID; }else{ ch->scid = ng_l2cap_get_cid(l2cap, (con->linktype!= NG_HCI_LINK_ACL)); } if (ch->scid != NG_L2CAP_NULL_CID) { /* Initialize channel */ ch->psm = psm; ch->con = con; ch->state = NG_L2CAP_CLOSED; /* Set MTU and flow control settings to defaults */ ch->imtu = NG_L2CAP_MTU_DEFAULT; bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow)); ch->omtu = NG_L2CAP_MTU_DEFAULT; bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)); ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT; ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT; LIST_INSERT_HEAD(&l2cap->chan_list, ch, next); ng_l2cap_con_ref(con); } else { bzero(ch, sizeof(*ch)); free(ch, M_NETGRAPH_L2CAP); ch = NULL; } return (ch); } /* ng_l2cap_new_chan */ ng_l2cap_chan_p ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid, int idtype) { ng_l2cap_chan_p ch = NULL; - if(idtype == NG_L2CAP_L2CA_IDTYPE_ATT){ + if((idtype == NG_L2CAP_L2CA_IDTYPE_ATT)|| + (idtype == NG_L2CAP_L2CA_IDTYPE_SMP)){ return NULL; } LIST_FOREACH(ch, &l2cap->chan_list, next){ if((idtype != NG_L2CAP_L2CA_IDTYPE_BREDR)&& (ch->con->linktype == NG_HCI_LINK_ACL )) continue; if((idtype != NG_L2CAP_L2CA_IDTYPE_LE)&& (ch->con->linktype != NG_HCI_LINK_ACL )) continue; - if (ch->scid == scid) break; } return (ch); } /* ng_l2cap_chan_by_scid */ ng_l2cap_chan_p ng_l2cap_chan_by_conhandle(ng_l2cap_p l2cap, uint16_t scid, u_int16_t con_handle) { ng_l2cap_chan_p ch = NULL; LIST_FOREACH(ch, &l2cap->chan_list, next){ if ((ch->scid == scid) && (ch->con->con_handle == con_handle)) break; } return (ch); } /* ng_l2cap_chan_by_scid */ /* * Free channel descriptor. */ void ng_l2cap_free_chan(ng_l2cap_chan_p ch) { ng_l2cap_cmd_p f = NULL, n = NULL; f = TAILQ_FIRST(&ch->con->cmd_list); while (f != NULL) { n = TAILQ_NEXT(f, next); if (f->ch == ch) { ng_l2cap_unlink_cmd(f); if (f->flags & NG_L2CAP_CMD_PENDING) ng_l2cap_command_untimeout(f); ng_l2cap_free_cmd(f); } f = n; } LIST_REMOVE(ch, next); ng_l2cap_con_unref(ch->con); bzero(ch, sizeof(*ch)); free(ch, M_NETGRAPH_L2CAP); } /* ng_l2cap_free_chan */ /* * Create new L2CAP command descriptor. WILL NOT add command to the queue. */ ng_l2cap_cmd_p ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident, u_int8_t code, u_int32_t token) { ng_l2cap_cmd_p cmd = NULL; KASSERT((ch == NULL || ch->con == con), ("%s: %s - invalid channel pointer!\n", __func__, NG_NODE_NAME(con->l2cap->node))); cmd = malloc(sizeof(*cmd), M_NETGRAPH_L2CAP, M_NOWAIT|M_ZERO); if (cmd == NULL) return (NULL); cmd->con = con; cmd->ch = ch; cmd->ident = ident; cmd->code = code; cmd->token = token; ng_callout_init(&cmd->timo); return (cmd); } /* ng_l2cap_new_cmd */ /* * Get pending (i.e. initiated by local side) L2CAP command descriptor by ident */ ng_l2cap_cmd_p ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident) { ng_l2cap_cmd_p cmd = NULL; TAILQ_FOREACH(cmd, &con->cmd_list, next) { if ((cmd->flags & NG_L2CAP_CMD_PENDING) && cmd->ident == ident) { KASSERT((cmd->con == con), ("%s: %s - invalid connection pointer!\n", __func__, NG_NODE_NAME(con->l2cap->node))); break; } } return (cmd); } /* ng_l2cap_cmd_by_ident */ /* * Set LP timeout * XXX FIXME: check return code from ng_callout */ int ng_l2cap_lp_timeout(ng_l2cap_con_p con) { if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) panic( "%s: %s - invalid timeout, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(con->l2cap->node), con->state, con->flags); con->flags |= NG_L2CAP_CON_LP_TIMO; ng_callout(&con->con_timo, con->l2cap->node, NULL, bluetooth_hci_connect_timeout(), ng_l2cap_process_lp_timeout, NULL, con->con_handle); return (0); } /* ng_l2cap_lp_timeout */ /* * Unset LP timeout */ int ng_l2cap_lp_untimeout(ng_l2cap_con_p con) { if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) panic( "%s: %s - no LP connection timeout, state=%d, flags=%#x\n", __func__, NG_NODE_NAME(con->l2cap->node), con->state, con->flags); if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) return (ETIMEDOUT); con->flags &= ~NG_L2CAP_CON_LP_TIMO; return (0); } /* ng_l2cap_lp_untimeout */ /* * Set L2CAP command timeout * XXX FIXME: check return code from ng_callout */ int ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo) { int arg; if (cmd->flags & NG_L2CAP_CMD_PENDING) panic( "%s: %s - duplicated command timeout, code=%#x, flags=%#x\n", __func__, NG_NODE_NAME(cmd->con->l2cap->node), cmd->code, cmd->flags); arg = ((cmd->ident << 16) | cmd->con->con_handle); cmd->flags |= NG_L2CAP_CMD_PENDING; ng_callout(&cmd->timo, cmd->con->l2cap->node, NULL, timo, ng_l2cap_process_command_timeout, NULL, arg); return (0); } /* ng_l2cap_command_timeout */ /* * Unset L2CAP command timeout */ int ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd) { if (!(cmd->flags & NG_L2CAP_CMD_PENDING)) panic( "%s: %s - no command timeout, code=%#x, flags=%#x\n", __func__, NG_NODE_NAME(cmd->con->l2cap->node), cmd->code, cmd->flags); if (ng_uncallout(&cmd->timo, cmd->con->l2cap->node) == 0) return (ETIMEDOUT); cmd->flags &= ~NG_L2CAP_CMD_PENDING; return (0); } /* ng_l2cap_command_untimeout */ /* * Prepend "m"buf with "size" bytes */ struct mbuf * ng_l2cap_prepend(struct mbuf *m, int size) { M_PREPEND(m, size, M_NOWAIT); if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL)) return (NULL); return (m); } /* ng_l2cap_prepend */ /* * Default flow settings */ ng_l2cap_flow_p ng_l2cap_default_flow(void) { static ng_l2cap_flow_t default_flow = { /* flags */ 0x0, /* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT, /* token_rate */ 0xffffffff, /* maximum */ /* token_bucket_size */ 0xffffffff, /* maximum */ /* peak_bandwidth */ 0x00000000, /* maximum */ /* latency */ 0xffffffff, /* don't care */ /* delay_variation */ 0xffffffff /* don't care */ }; return (&default_flow); } /* ng_l2cap_default_flow */ /* * Get next available channel ID * XXX FIXME this is *UGLY* but will do for now */ static u_int16_t ng_l2cap_get_cid(ng_l2cap_p l2cap,int isle) { u_int16_t cid ; u_int16_t endcid; uint16_t mask; int idtype; if(isle){ endcid = l2cap->lecid; /*Assume Last CID is 2^n-1 */ mask = NG_L2CAP_LELAST_CID; idtype = NG_L2CAP_L2CA_IDTYPE_LE; }else{ endcid = l2cap->cid; /*Assume Last CID is 2^n-1 */ mask = NG_L2CAP_LAST_CID; idtype = NG_L2CAP_L2CA_IDTYPE_BREDR; } cid = (endcid+1) & mask; if (cid < NG_L2CAP_FIRST_CID) cid = NG_L2CAP_FIRST_CID; while (cid != endcid) { if (ng_l2cap_chan_by_scid(l2cap, cid, idtype) == NULL) { if(!isle){ l2cap->cid = cid; }else{ l2cap->lecid = cid; } return (cid); } cid ++; cid &= mask; if (cid < NG_L2CAP_FIRST_CID) cid = NG_L2CAP_FIRST_CID; } return (NG_L2CAP_NULL_CID); } /* ng_l2cap_get_cid */ /* * Get next available command ident * XXX FIXME this is *UGLY* but will do for now */ u_int8_t ng_l2cap_get_ident(ng_l2cap_con_p con) { u_int8_t ident = con->ident + 1; if (ident < NG_L2CAP_FIRST_IDENT) ident = NG_L2CAP_FIRST_IDENT; while (ident != con->ident) { if (ng_l2cap_cmd_by_ident(con, ident) == NULL) { con->ident = ident; return (ident); } ident ++; if (ident < NG_L2CAP_FIRST_IDENT) ident = NG_L2CAP_FIRST_IDENT; } return (NG_L2CAP_NULL_IDENT); } /* ng_l2cap_get_ident */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c (revision 290038) @@ -1,1723 +1,1815 @@ /* * ng_l2cap_ulpi.c */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_ulpi.c,v 1.1 2002/11/24 19:47:06 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** ****************************************************************************** ** Upper Layer Protocol Interface module ****************************************************************************** ******************************************************************************/ /* * Process L2CA_Connect request from the upper layer protocol. */ int ng_l2cap_l2ca_con_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_con_ip *ip = NULL; ng_l2cap_con_p con = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_Connect request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_con_ip *)(msg->data); /* Check if we have connection to the remote unit */ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, ip->linktype); if (con == NULL) { /* Submit LP_ConnectReq to the lower layer */ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr,ip->linktype); if (error != 0) { NG_L2CAP_ERR( "%s: %s - unable to send LP_ConnectReq message, error=%d\n", __func__, NG_NODE_NAME(l2cap->node), error); goto out; } /* This should not fail */ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, ip->linktype); KASSERT((con != NULL), ("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node))); } /* * Create new empty channel descriptor. In case of any failure do * not touch connection descriptor. */ ch = ng_l2cap_new_chan(l2cap, con, ip->psm, ip->idtype); if (ch == NULL) { error = ENOMEM; goto out; } /* Now create L2CAP_ConnectReq command */ cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(con), NG_L2CAP_CON_REQ, msg->header.token); if (cmd == NULL) { ng_l2cap_free_chan(ch); error = ENOMEM; goto out; } if (cmd->ident == NG_L2CAP_NULL_IDENT) { ng_l2cap_free_cmd(cmd); ng_l2cap_free_chan(ch); error = EIO; goto out; } /* Create L2CAP command packet */ if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_ATT){ _ng_l2cap_con_rsp(cmd->aux, cmd->ident, NG_L2CAP_ATT_CID, NG_L2CAP_ATT_CID, 0, 0); cmd->aux->m_flags |= M_PROTO2; + }else if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_SMP){ + _ng_l2cap_con_rsp(cmd->aux, cmd->ident, NG_L2CAP_SMP_CID, + NG_L2CAP_SMP_CID, 0, 0); + cmd->aux->m_flags |= M_PROTO2; }else{ _ng_l2cap_con_req(cmd->aux, cmd->ident, ch->psm, ch->scid); } if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); ng_l2cap_free_chan(ch); error = ENOBUFS; goto out; } ch->state = NG_L2CAP_W4_L2CAP_CON_RSP; /* Link command to the queue */ ng_l2cap_link_cmd(ch->con, cmd); ng_l2cap_lp_deliver(ch->con); out: return (error); } /* ng_l2cap_l2ca_con_req */ /* * Send L2CA_Connect response to the upper layer protocol. */ int ng_l2cap_l2ca_con_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result, u_int16_t status) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_con_op *op = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_Connect response message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_Connect response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON, sizeof(*op), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_con_op *)(msg->data); /* * XXX Spec. says we should only populate LCID when result == 0 * What about PENDING? What the heck, for now always populate * LCID :) */ if(ch->scid == NG_L2CAP_ATT_CID){ op->idtype = NG_L2CAP_L2CA_IDTYPE_ATT; op->lcid = ch->con->con_handle; + }else if(ch->scid == NG_L2CAP_SMP_CID){ + op->idtype = NG_L2CAP_L2CA_IDTYPE_SMP; + op->lcid = ch->con->con_handle; }else{ op->idtype = (ch->con->linktype == NG_HCI_LINK_ACL)? NG_L2CAP_L2CA_IDTYPE_BREDR : NG_L2CAP_L2CA_IDTYPE_LE; op->lcid = ch->scid; } - + op->encryption = ch->con->encryption; op->result = result; op->status = status; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_con_rsp */ /* * Process L2CA_ConnectRsp request from the upper layer protocol. */ int ng_l2cap_l2ca_con_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_con_rsp_ip *ip = NULL; ng_l2cap_con_p con = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; u_int16_t dcid; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_ConnectRsp request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data); /* Check if we have this channel */ - if(ip->lcid != NG_L2CAP_ATT_CID){ + if((ip->lcid != NG_L2CAP_ATT_CID)&& + (ip->lcid != NG_L2CAP_SMP_CID)){ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid ,(ip->linktype == NG_HCI_LINK_ACL)? NG_L2CAP_L2CA_IDTYPE_BREDR: NG_L2CAP_L2CA_IDTYPE_LE); }else{ // For now not support on ATT device. ch = NULL; } if (ch == NULL) { NG_L2CAP_ALERT( "%s: %s - unexpected L2CA_ConnectRsp request message. " \ "Channel does not exist, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->lcid); error = ENOENT; goto out; } /* Check channel state */ if (ch->state != NG_L2CAP_W4_L2CA_CON_RSP) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_ConnectRsp request message. " \ "Invalid channel state, state=%d, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->state, ip->lcid); error = EINVAL; goto out; } dcid = ch->dcid; con = ch->con; /* * Now we are pretty much sure it is our response. So create and send * L2CAP_ConnectRsp message to our peer. */ if (ch->ident != ip->ident) NG_L2CAP_WARN( "%s: %s - channel ident and response ident do not match, scid=%d, ident=%d. " \ "Will use response ident=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->ident, ip->ident); /* Check result */ switch (ip->result) { case NG_L2CAP_SUCCESS: - ch->state = (ch->scid == NG_L2CAP_ATT_CID)? + ch->state = ((ch->scid == NG_L2CAP_ATT_CID)|| + (ch->scid == NG_L2CAP_SMP_CID))? NG_L2CAP_OPEN : NG_L2CAP_CONFIG; ch->cfg_state = 0; break; case NG_L2CAP_PENDING: break; default: ng_l2cap_free_chan(ch); ch = NULL; break; } /* Create L2CAP command */ cmd = ng_l2cap_new_cmd(con, ch, ip->ident, NG_L2CAP_CON_RSP, msg->header.token); if (cmd == NULL) { if (ch != NULL) ng_l2cap_free_chan(ch); error = ENOMEM; goto out; } _ng_l2cap_con_rsp(cmd->aux, cmd->ident, ip->lcid, dcid, ip->result, ip->status); if (cmd->aux == NULL) { if (ch != NULL) ng_l2cap_free_chan(ch); ng_l2cap_free_cmd(cmd); error = ENOBUFS; goto out; } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); out: return (error); } /* ng_l2cap_l2ca_con_rsp_req */ +int ng_l2cap_l2ca_encryption_change(ng_l2cap_chan_p ch, uint16_t result) +{ + ng_l2cap_p l2cap = ch->con->l2cap; + struct ng_mesg *msg = NULL; + ng_l2cap_l2ca_enc_chg_op *op = NULL; + int error = 0; + + /* Check if upstream hook is connected and valid */ + if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { + NG_L2CAP_ERR( +"%s: %s - unable to send L2CA_ConnectRsp response message. " \ +"Hook is not connected or valid, psm=%d\n", + __func__, NG_NODE_NAME(l2cap->node), ch->psm); + + return (ENOTCONN); + } + + /* Create and send L2CA_ConnectRsp response message */ + NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENC_CHANGE, + sizeof(*op), M_NOWAIT); + if (msg == NULL) + error = ENOMEM; + else { + msg->header.token = 0; + msg->header.flags |= NGF_RESP; + + op = (ng_l2cap_l2ca_enc_chg_op *)(msg->data); + op->result = result; + if(ch->scid ==NG_L2CAP_ATT_CID|| + ch->scid ==NG_L2CAP_SMP_CID){ + op->lcid = ch->con->con_handle; + op->idtype = (ch->scid==NG_L2CAP_ATT_CID)? + NG_L2CAP_L2CA_IDTYPE_ATT: + NG_L2CAP_L2CA_IDTYPE_SMP; + }else{ + op->idtype =(ch->con->linktype ==NG_HCI_LINK_ACL)? + NG_L2CAP_L2CA_IDTYPE_BREDR: + NG_L2CAP_L2CA_IDTYPE_LE; + } + + + NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); + } + + return (error); + +} /* * Send L2CAP_ConnectRsp response to the upper layer */ int ng_l2cap_l2ca_con_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_con_rsp_op *op = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_ConnectRsp response message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_ConnectRsp response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP, sizeof(*op), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data); op->result = result; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_con_rsp_rsp */ /* * Send L2CA_ConnectInd message to the upper layer protocol. */ int ng_l2cap_l2ca_con_ind(ng_l2cap_chan_p ch) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_con_ind_ip *ip = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_ConnectInd message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_ConnectInd message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_IND, sizeof(*ip), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data); bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr)); ip->lcid = ch->scid; ip->psm = ch->psm; ip->ident = ch->ident; ip->linktype = ch->con->linktype; + NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_con_ind */ /* * Process L2CA_Config request from the upper layer protocol */ int ng_l2cap_l2ca_cfg_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_cfg_ip *ip = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; struct mbuf *opt = NULL; u_int16_t *mtu = NULL, *flush_timo = NULL; ng_l2cap_flow_p flow = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - Invalid L2CA_Config request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data); /* Check if we have this channel */ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid, NG_L2CAP_L2CA_IDTYPE_BREDR); if (ch == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_Config request message. " \ "Channel does not exist, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->lcid); error = ENOENT; goto out; } /* Check channel state */ if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_Config request message. " \ "Invalid channel state, state=%d, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->state, ch->scid); error = EINVAL; goto out; } /* Set requested channel configuration options */ ch->imtu = ip->imtu; bcopy(&ip->oflow, &ch->oflow, sizeof(ch->oflow)); ch->flush_timo = ip->flush_timo; ch->link_timo = ip->link_timo; /* Compare channel settings with defaults */ if (ch->imtu != NG_L2CAP_MTU_DEFAULT) mtu = &ch->imtu; if (ch->flush_timo != NG_L2CAP_FLUSH_TIMO_DEFAULT) flush_timo = &ch->flush_timo; if (bcmp(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)) != 0) flow = &ch->oflow; /* Create configuration options */ _ng_l2cap_build_cfg_options(opt, mtu, flush_timo, flow); if (opt == NULL) { error = ENOBUFS; goto out; } /* Create L2CAP command descriptor */ cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con), NG_L2CAP_CFG_REQ, msg->header.token); if (cmd == NULL) { NG_FREE_M(opt); error = ENOMEM; goto out; } if (cmd->ident == NG_L2CAP_NULL_IDENT) { ng_l2cap_free_cmd(cmd); NG_FREE_M(opt); error = EIO; goto out; } /* Create L2CAP command packet */ _ng_l2cap_cfg_req(cmd->aux, cmd->ident, ch->dcid, 0, opt); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); error = ENOBUFS; goto out; } /* Adjust channel state for re-configuration */ if (ch->state == NG_L2CAP_OPEN) { - ch->state = (ch->scid == NG_L2CAP_ATT_CID)? + ch->state = ((ch->scid == NG_L2CAP_ATT_CID)|| + (ch->scid == NG_L2CAP_SMP_CID))? NG_L2CAP_OPEN : NG_L2CAP_CONFIG; ch->cfg_state = 0; } /* Link command to the queue */ ng_l2cap_link_cmd(ch->con, cmd); ng_l2cap_lp_deliver(ch->con); out: return (error); } /* ng_l2cap_l2ca_cfg_req */ /* * Send L2CA_Config response to the upper layer protocol */ int ng_l2cap_l2ca_cfg_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_cfg_op *op = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_Config response message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_Config response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG, sizeof(*op), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_cfg_op *)(msg->data); op->result = result; op->imtu = ch->imtu; bcopy(&ch->oflow, &op->oflow, sizeof(op->oflow)); op->flush_timo = ch->flush_timo; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); if (error == 0 && result == NG_L2CAP_SUCCESS) { ch->cfg_state |= NG_L2CAP_CFG_IN; if (ch->cfg_state == NG_L2CAP_CFG_BOTH) ch->state = NG_L2CAP_OPEN; } } return (error); } /* ng_l2cap_l2ca_cfg_rsp */ /* * Process L2CA_ConfigRsp request from the upper layer protocol * * XXX XXX XXX * * NOTE: The Bluetooth specification says that Configuration_Response * (L2CA_ConfigRsp) should be used to issue response to configuration request * indication. The minor problem here is L2CAP command ident. We should use * ident from original L2CAP request to make sure our peer can match request * and response. For some reason Bluetooth specification does not include * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident * field. So we should store last known L2CAP request command ident in channel. * Also it seems that upper layer can not reject configuration request, as * Configuration_Response message does not have status/reason field. */ int ng_l2cap_l2ca_cfg_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_cfg_rsp_ip *ip = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; struct mbuf *opt = NULL; u_int16_t *mtu = NULL; ng_l2cap_flow_p flow = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_ConfigRsp request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data); /* Check if we have this channel */ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid, NG_L2CAP_L2CA_IDTYPE_BREDR); if (ch == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_ConfigRsp request message. " \ "Channel does not exist, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->lcid); error = ENOENT; goto out; } /* Check channel state */ if (ch->state != NG_L2CAP_CONFIG) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_ConfigRsp request message. " \ "Invalid channel state, state=%d, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->state, ch->scid); error = EINVAL; goto out; } /* Set channel settings */ if (ip->omtu != ch->omtu) { ch->omtu = ip->omtu; mtu = &ch->omtu; } if (bcmp(&ip->iflow, &ch->iflow, sizeof(ch->iflow)) != 0) { bcopy(&ip->iflow, &ch->iflow, sizeof(ch->iflow)); flow = &ch->iflow; } if (mtu != NULL || flow != NULL) { _ng_l2cap_build_cfg_options(opt, mtu, NULL, flow); if (opt == NULL) { error = ENOBUFS; goto out; } } /* Create L2CAP command */ cmd = ng_l2cap_new_cmd(ch->con, ch, ch->ident, NG_L2CAP_CFG_RSP, msg->header.token); if (cmd == NULL) { NG_FREE_M(opt); error = ENOMEM; goto out; } _ng_l2cap_cfg_rsp(cmd->aux,cmd->ident,ch->dcid,0,NG_L2CAP_SUCCESS,opt); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); error = ENOBUFS; goto out; } /* XXX FIXME - not here ??? */ ch->cfg_state |= NG_L2CAP_CFG_OUT; if (ch->cfg_state == NG_L2CAP_CFG_BOTH) ch->state = NG_L2CAP_OPEN; /* Link command to the queue */ ng_l2cap_link_cmd(ch->con, cmd); ng_l2cap_lp_deliver(ch->con); out: return (error); } /* ng_l2cap_l2ca_cfg_rsp_req */ /* * Send L2CA_ConfigRsp response to the upper layer protocol */ int ng_l2cap_l2ca_cfg_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_cfg_rsp_op *op = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_ConfigRsp response message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_ConfigRsp response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP, sizeof(*op), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data); op->result = result; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_cfg_rsp_rsp */ /* * Send L2CA_ConfigInd message to the upper layer protocol * * XXX XXX XXX * * NOTE: The Bluetooth specification says that Configuration_Response * (L2CA_ConfigRsp) should be used to issue response to configuration request * indication. The minor problem here is L2CAP command ident. We should use * ident from original L2CAP request to make sure our peer can match request * and response. For some reason Bluetooth specification does not include * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident * field. So we should store last known L2CAP request command ident in channel. * Also it seems that upper layer can not reject configuration request, as * Configuration_Response message does not have status/reason field. */ int ng_l2cap_l2ca_cfg_ind(ng_l2cap_chan_p ch) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_cfg_ind_ip *ip = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - Unable to send L2CA_ConfigInd message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_ConnectInd message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_IND, sizeof(*ip), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data); ip->lcid = ch->scid; ip->omtu = ch->omtu; bcopy(&ch->iflow, &ip->iflow, sizeof(ip->iflow)); ip->flush_timo = ch->flush_timo; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_cfg_ind */ /* * Process L2CA_Write event */ int ng_l2cap_l2ca_write_req(ng_l2cap_p l2cap, struct mbuf *m) { ng_l2cap_l2ca_hdr_t *l2ca_hdr = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; int error = 0; u_int32_t token = 0; /* Make sure we can access L2CA data packet header */ if (m->m_pkthdr.len < sizeof(*l2ca_hdr)) { NG_L2CAP_ERR( "%s: %s - L2CA Data packet too small, len=%d\n", __func__,NG_NODE_NAME(l2cap->node),m->m_pkthdr.len); error = EMSGSIZE; goto drop; } /* Get L2CA data packet header */ NG_L2CAP_M_PULLUP(m, sizeof(*l2ca_hdr)); if (m == NULL) return (ENOBUFS); l2ca_hdr = mtod(m, ng_l2cap_l2ca_hdr_t *); token = l2ca_hdr->token; m_adj(m, sizeof(*l2ca_hdr)); /* Verify payload size */ if (l2ca_hdr->length != m->m_pkthdr.len) { NG_L2CAP_ERR( "%s: %s - invalid L2CA Data packet. " \ "Payload length does not match, length=%d, len=%d\n", __func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->length, m->m_pkthdr.len); error = EMSGSIZE; goto drop; } /* Check channel ID */ if (l2ca_hdr->idtype == NG_L2CAP_L2CA_IDTYPE_ATT){ ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_ATT_CID, l2ca_hdr->lcid); - } else{ + } else if (l2ca_hdr->idtype == NG_L2CAP_L2CA_IDTYPE_SMP){ + ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_SMP_CID, + l2ca_hdr->lcid); + }else{ if (l2ca_hdr->lcid < NG_L2CAP_FIRST_CID) { NG_L2CAP_ERR( "%s: %s - invalid L2CA Data packet. Inavlid channel ID, cid=%d\n", __func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid); error = EINVAL; goto drop; } /* Verify that we have the channel and make sure it is open */ ch = ng_l2cap_chan_by_scid(l2cap, l2ca_hdr->lcid, l2ca_hdr->idtype); } if (ch == NULL) { NG_L2CAP_ERR( "%s: %s - invalid L2CA Data packet. Channel does not exist, cid=%d\n", __func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid); error = ENOENT; goto drop; } if (ch->state != NG_L2CAP_OPEN) { NG_L2CAP_ERR( "%s: %s - invalid L2CA Data packet. Invalid channel state, scid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->state); error = EHOSTDOWN; goto drop; /* XXX not always - re-configure */ } /* Create L2CAP command descriptor */ cmd = ng_l2cap_new_cmd(ch->con, ch, 0, NGM_L2CAP_L2CA_WRITE, token); if (cmd == NULL) { error = ENOMEM; goto drop; } /* Attach data packet and link command to the queue */ cmd->aux = m; ng_l2cap_link_cmd(ch->con, cmd); ng_l2cap_lp_deliver(ch->con); return (error); drop: NG_FREE_M(m); return (error); } /* ng_l2cap_l2ca_write_req */ /* * Send L2CA_Write response */ int ng_l2cap_l2ca_write_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result, u_int16_t length) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_write_op *op = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_WriteRsp message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_WriteRsp message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_WRITE, sizeof(*op), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_write_op *)(msg->data); op->result = result; op->length = length; if(ch->scid == NG_L2CAP_ATT_CID){ op->idtype = NG_L2CAP_L2CA_IDTYPE_ATT; op->lcid = ch->con->con_handle; + }else if(ch->scid == NG_L2CAP_SMP_CID){ + op->idtype = NG_L2CAP_L2CA_IDTYPE_SMP; + op->lcid = ch->con->con_handle; }else{ op->idtype = (ch->con->linktype == NG_HCI_LINK_ACL)? NG_L2CAP_L2CA_IDTYPE_BREDR : NG_L2CAP_L2CA_IDTYPE_LE; op->lcid = ch->scid; } NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_write_rsp */ /* * Receive packet from the lower layer protocol and send it to the upper * layer protocol (L2CAP_Read) */ int ng_l2cap_l2ca_receive(ng_l2cap_con_p con) { ng_l2cap_p l2cap = con->l2cap; ng_l2cap_hdr_t *hdr = NULL; ng_l2cap_chan_p ch = NULL; int error = 0; int idtype; uint16_t *idp; - + int silent = 0; + NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); if (con->rx_pkt == NULL) return (ENOBUFS); hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *); /* Check channel */ if(hdr->dcid == NG_L2CAP_ATT_CID){ idtype = NG_L2CAP_L2CA_IDTYPE_ATT; ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_ATT_CID, con->con_handle); /* * Here,ATT channel is distinguished by * connection handle */ + hdr->dcid = con->con_handle; + silent = 1; + }else if(hdr->dcid == NG_L2CAP_SMP_CID){ + idtype = NG_L2CAP_L2CA_IDTYPE_SMP; + ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_SMP_CID, + con->con_handle); + /* + * Here,SMP channel is distinguished by + * connection handle + */ + silent = 1; hdr->dcid = con->con_handle; }else{ idtype = (con->linktype==NG_HCI_LINK_ACL)? NG_L2CAP_L2CA_IDTYPE_BREDR: NG_L2CAP_L2CA_IDTYPE_LE; ch = ng_l2cap_chan_by_scid(l2cap, hdr->dcid, idtype); } if (ch == NULL) { - NG_L2CAP_ERR( + if(!silent) + NG_L2CAP_ERR( "%s: %s - unexpected L2CAP data packet. Channel does not exist, cid=%d, idtype=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->dcid, idtype); error = ENOENT; goto drop; } /* Check channel state */ if (ch->state != NG_L2CAP_OPEN) { NG_L2CAP_WARN( "%s: %s - unexpected L2CAP data packet. " \ "Invalid channel state, cid=%d, state=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->state); error = EHOSTDOWN; /* XXX not always - re-configuration */ goto drop; } /* Check payload size and channel's MTU */ if (hdr->length > ch->imtu) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP data packet. " \ "Packet too big, length=%d, imtu=%d, cid=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->length, ch->imtu, ch->scid); error = EMSGSIZE; goto drop; } /* * If we got here then everything looks good and we can sent packet * to the upper layer protocol. */ /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CAP data packet. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); error = ENOTCONN; goto drop; } M_PREPEND(con->rx_pkt, sizeof(uint16_t), M_NOWAIT); if(con->rx_pkt == NULL) goto drop; idp = mtod(con->rx_pkt, uint16_t *); *idp = idtype; NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt); con->rx_pkt = NULL; drop: NG_FREE_M(con->rx_pkt); /* checks for != NULL */ return (error); } /* ng_l2cap_receive */ /* * Receive connectioless (multicast) packet from the lower layer protocol and * send it to the upper layer protocol */ int ng_l2cap_l2ca_clt_receive(ng_l2cap_con_p con) { struct _clt_pkt { ng_l2cap_hdr_t h; ng_l2cap_clt_hdr_t c_h; } __attribute__ ((packed)) *hdr = NULL; ng_l2cap_p l2cap = con->l2cap; int length, error = 0; NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr)); if (con->rx_pkt == NULL) return (ENOBUFS); hdr = mtod(con->rx_pkt, struct _clt_pkt *); /* Check packet */ length = con->rx_pkt->m_pkthdr.len - sizeof(*hdr); if (length < 0) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP CLT data packet. Packet too small, length=%d\n", __func__, NG_NODE_NAME(l2cap->node), length); error = EMSGSIZE; goto drop; } /* Check payload size against CLT MTU */ if (length > NG_L2CAP_MTU_DEFAULT) { NG_L2CAP_ERR( "%s: %s - invalid L2CAP CLT data packet. Packet too big, length=%d, mtu=%d\n", __func__, NG_NODE_NAME(l2cap->node), length, NG_L2CAP_MTU_DEFAULT); error = EMSGSIZE; goto drop; } hdr->c_h.psm = le16toh(hdr->c_h.psm); /* * If we got here then everything looks good and we can sent packet * to the upper layer protocol. */ /* Select upstream hook based on PSM */ switch (hdr->c_h.psm) { case NG_L2CAP_PSM_SDP: if (l2cap->flags & NG_L2CAP_CLT_SDP_DISABLED) goto drop; break; case NG_L2CAP_PSM_RFCOMM: if (l2cap->flags & NG_L2CAP_CLT_RFCOMM_DISABLED) goto drop; break; case NG_L2CAP_PSM_TCP: if (l2cap->flags & NG_L2CAP_CLT_TCP_DISABLED) goto drop; break; } /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CAP CLT data packet. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), hdr->c_h.psm); error = ENOTCONN; goto drop; } NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt); con->rx_pkt = NULL; drop: NG_FREE_M(con->rx_pkt); /* checks for != NULL */ return (error); } /* ng_l2cap_l2ca_clt_receive */ /* * Send L2CA_QoSViolationInd to the upper layer protocol */ int ng_l2cap_l2ca_qos_ind(ng_l2cap_chan_p ch) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_qos_ind_ip *ip = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_QoSViolationInd message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_QoSViolationInd message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_QOS_IND, sizeof(*ip), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { ip = (ng_l2cap_l2ca_qos_ind_ip *)(msg->data); bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr)); NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_qos_ind */ /* * Process L2CA_Disconnect request from the upper layer protocol. */ int ng_l2cap_l2ca_discon_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_discon_ip *ip = NULL; ng_l2cap_chan_p ch = NULL; ng_l2cap_cmd_p cmd = NULL; int error = 0; /* Check message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_Disconnect request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_discon_ip *)(msg->data); if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_ATT){ /* Don't send Disconnect request on L2CAP Layer*/ ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_ATT_CID, + ip->lcid); + + if(ch != NULL){ + ng_l2cap_free_chan(ch); + }else{ + NG_L2CAP_ERR( +"%s: %s - unexpected L2CA_Disconnect request message. " \ +"Channel does not exist, conhandle=%d\n", + __func__, NG_NODE_NAME(l2cap->node), ip->lcid); + error = EINVAL; + } + goto out; + }else if(ip->idtype == NG_L2CAP_L2CA_IDTYPE_SMP){ + /* Don't send Disconnect request on L2CAP Layer*/ + ch = ng_l2cap_chan_by_conhandle(l2cap, NG_L2CAP_SMP_CID, ip->lcid); if(ch != NULL){ ng_l2cap_free_chan(ch); }else{ NG_L2CAP_ERR( "%s: %s - unexpected L2CA_Disconnect request message. " \ "Channel does not exist, conhandle=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->lcid); error = EINVAL; } goto out; }else{ /* Check if we have this channel */ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid, ip->idtype); } if (ch == NULL) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_Disconnect request message. " \ "Channel does not exist, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->lcid); error = ENOENT; goto out; } /* Check channel state */ if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) { NG_L2CAP_ERR( "%s: %s - unexpected L2CA_Disconnect request message. " \ "Invalid channel state, state=%d, lcid=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->state, ch->scid); error = EINVAL; goto out; } /* Create and send L2CAP_DisconReq message */ cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con), NG_L2CAP_DISCON_REQ, msg->header.token); if (cmd == NULL) { ng_l2cap_free_chan(ch); error = ENOMEM; goto out; } if (cmd->ident == NG_L2CAP_NULL_IDENT) { ng_l2cap_free_chan(ch); ng_l2cap_free_cmd(cmd); error = EIO; goto out; } _ng_l2cap_discon_req(cmd->aux, cmd->ident, ch->dcid, ch->scid); if (cmd->aux == NULL) { ng_l2cap_free_chan(ch); ng_l2cap_free_cmd(cmd); error = ENOBUFS; goto out; } ch->state = NG_L2CAP_W4_L2CAP_DISCON_RSP; /* Link command to the queue */ ng_l2cap_link_cmd(ch->con, cmd); ng_l2cap_lp_deliver(ch->con); out: return (error); } /* ng_l2cap_l2ca_discon_req */ /* * Send L2CA_Disconnect response to the upper layer protocol */ int ng_l2cap_l2ca_discon_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_discon_op *op = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_Disconnect response message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_Disconnect response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON, sizeof(*op), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_discon_op *)(msg->data); op->result = result; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_discon_rsp */ /* * Send L2CA_DisconnectInd message to the upper layer protocol. */ int ng_l2cap_l2ca_discon_ind(ng_l2cap_chan_p ch) { ng_l2cap_p l2cap = ch->con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_discon_ind_ip *ip = NULL; int error = 0; /* Check if upstream hook is connected and valid */ if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) { NG_L2CAP_ERR( "%s: %s - unable to send L2CA_DisconnectInd message. " \ "Hook is not connected or valid, psm=%d\n", __func__, NG_NODE_NAME(l2cap->node), ch->psm); return (ENOTCONN); } /* Create and send L2CA_DisconnectInd message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON_IND, sizeof(*ip), M_NOWAIT); if (msg == NULL) error = ENOMEM; else { ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data); ip->lcid = ch->scid; NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0); } return (error); } /* ng_l2cap_l2ca_discon_ind */ /* * Process L2CA_GroupCreate request from the upper layer protocol. * XXX FIXME */ int ng_l2cap_l2ca_grp_create(ng_l2cap_p l2cap, struct ng_mesg *msg) { return (ENOTSUP); } /* ng_l2cap_l2ca_grp_create */ /* * Process L2CA_GroupClose request from the upper layer protocol * XXX FIXME */ int ng_l2cap_l2ca_grp_close(ng_l2cap_p l2cap, struct ng_mesg *msg) { return (ENOTSUP); } /* ng_l2cap_l2ca_grp_close */ /* * Process L2CA_GroupAddMember request from the upper layer protocol. * XXX FIXME */ int ng_l2cap_l2ca_grp_add_member_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { return (ENOTSUP); } /* ng_l2cap_l2ca_grp_add_member_req */ /* * Send L2CA_GroupAddMember response to the upper layer protocol. * XXX FIXME */ int ng_l2cap_l2ca_grp_add_member_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result) { return (0); } /* ng_l2cap_l2ca_grp_add_member_rsp */ /* * Process L2CA_GroupDeleteMember request from the upper layer protocol * XXX FIXME */ int ng_l2cap_l2ca_grp_rem_member(ng_l2cap_p l2cap, struct ng_mesg *msg) { return (ENOTSUP); } /* ng_l2cap_l2ca_grp_rem_member */ /* * Process L2CA_GroupGetMembers request from the upper layer protocol * XXX FIXME */ int ng_l2cap_l2ca_grp_get_members(ng_l2cap_p l2cap, struct ng_mesg *msg) { return (ENOTSUP); } /* ng_l2cap_l2ca_grp_get_members */ /* * Process L2CA_Ping request from the upper layer protocol */ int ng_l2cap_l2ca_ping_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_ping_ip *ip = NULL; ng_l2cap_con_p con = NULL; ng_l2cap_cmd_p cmd = NULL; int error = 0; /* Verify message */ if (msg->header.arglen < sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_Ping request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_ping_ip *)(msg->data); if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) { NG_L2CAP_WARN( "%s: %s - invalid L2CA_Ping request. Echo size is too big, echo_size=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->echo_size); error = EMSGSIZE; goto out; } /* Check if we have connection to the unit */ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, NG_HCI_LINK_ACL); if (con == NULL) { /* Submit LP_ConnectReq to the lower layer */ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr, NG_HCI_LINK_ACL); if (error != 0) { NG_L2CAP_ERR( "%s: %s - unable to send LP_ConnectReq message, error=%d\n", __func__, NG_NODE_NAME(l2cap->node), error); goto out; } /* This should not fail */ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, NG_HCI_LINK_ACL); KASSERT((con != NULL), ("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node))); } /* Create L2CAP command descriptor */ cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con), NG_L2CAP_ECHO_REQ, msg->header.token); if (cmd == NULL) { error = ENOMEM; goto out; } if (cmd->ident == NG_L2CAP_NULL_IDENT) { ng_l2cap_free_cmd(cmd); error = EIO; goto out; } /* Create L2CAP command packet */ _ng_l2cap_echo_req(cmd->aux, cmd->ident, msg->data + sizeof(*ip), ip->echo_size); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); error = ENOBUFS; goto out; } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); out: return (error); } /* ng_l2cap_l2ca_ping_req */ /* * Send L2CA_Ping response to the upper layer protocol */ int ng_l2cap_l2ca_ping_rsp(ng_l2cap_con_p con, u_int32_t token, u_int16_t result, struct mbuf *data) { ng_l2cap_p l2cap = con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_ping_op *op = NULL; int error = 0, size = 0; /* Check if control hook is connected and valid */ if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) { NG_L2CAP_WARN( "%s: %s - unable to send L2CA_Ping response message. " \ "Hook is not connected or valid\n", __func__, NG_NODE_NAME(l2cap->node)); error = ENOTCONN; goto out; } size = (data == NULL)? 0 : data->m_pkthdr.len; /* Create and send L2CA_Ping response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_PING, sizeof(*op) + size, M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_ping_op *)(msg->data); op->result = result; bcopy(&con->remote, &op->bdaddr, sizeof(op->bdaddr)); if (data != NULL && size > 0) { op->echo_size = size; m_copydata(data, 0, size, (caddr_t) op + sizeof(*op)); } NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0); } out: NG_FREE_M(data); return (error); } /* ng_l2cap_l2ca_ping_rsp */ /* * Process L2CA_GetInfo request from the upper layer protocol */ int ng_l2cap_l2ca_get_info_req(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_get_info_ip *ip = NULL; ng_l2cap_con_p con = NULL; ng_l2cap_cmd_p cmd = NULL; int error = 0; /* Verify message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_GetInfo request message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); error = EMSGSIZE; goto out; } ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data); /* Check if we have connection to the unit */ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr,ip->linktype); if (con == NULL) { /* Submit LP_ConnectReq to the lower layer */ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr,ip->linktype); if (error != 0) { NG_L2CAP_ERR( "%s: %s - unable to send LP_ConnectReq message, error=%d\n", __func__, NG_NODE_NAME(l2cap->node), error); goto out; } /* This should not fail */ con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr, ip->linktype); KASSERT((con != NULL), ("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node))); } /* Create L2CAP command descriptor */ cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con), NG_L2CAP_INFO_REQ, msg->header.token); if (cmd == NULL) { error = ENOMEM; goto out; } if (cmd->ident == NG_L2CAP_NULL_IDENT) { ng_l2cap_free_cmd(cmd); error = EIO; goto out; } /* Create L2CAP command packet */ _ng_l2cap_info_req(cmd->aux, cmd->ident, ip->info_type); if (cmd->aux == NULL) { ng_l2cap_free_cmd(cmd); error = ENOBUFS; goto out; } /* Link command to the queue */ ng_l2cap_link_cmd(con, cmd); ng_l2cap_lp_deliver(con); out: return (error); } /* ng_l2cap_l2ca_get_info_req */ /* * Send L2CA_GetInfo response to the upper layer protocol */ int ng_l2cap_l2ca_get_info_rsp(ng_l2cap_con_p con, u_int32_t token, u_int16_t result, struct mbuf *data) { ng_l2cap_p l2cap = con->l2cap; struct ng_mesg *msg = NULL; ng_l2cap_l2ca_get_info_op *op = NULL; int error = 0, size; /* Check if control hook is connected and valid */ if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) { NG_L2CAP_WARN( "%s: %s - unable to send L2CA_GetInfo response message. " \ "Hook is not connected or valid\n", __func__, NG_NODE_NAME(l2cap->node)); error = ENOTCONN; goto out; } size = (data == NULL)? 0 : data->m_pkthdr.len; /* Create and send L2CA_GetInfo response message */ NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_GET_INFO, sizeof(*op) + size, M_NOWAIT); if (msg == NULL) error = ENOMEM; else { msg->header.token = token; msg->header.flags |= NGF_RESP; op = (ng_l2cap_l2ca_get_info_op *)(msg->data); op->result = result; if (data != NULL && size > 0) { op->info_size = size; m_copydata(data, 0, size, (caddr_t) op + sizeof(*op)); } NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0); } out: NG_FREE_M(data); return (error); } /* ng_l2cap_l2ca_get_info_rsp */ /* * Process L2CA_EnableCLT message from the upper layer protocol * XXX convert to NGN_L2CAP_NODE_SET_FLAGS? */ int ng_l2cap_l2ca_enable_clt(ng_l2cap_p l2cap, struct ng_mesg *msg) { ng_l2cap_l2ca_enable_clt_ip *ip = NULL; int error = 0; #if 0 * ng_l2cap_l2ca_enable_clt_op *op = NULL; * u_int16_t result; * u_int32_t token; #endif /* Check message */ if (msg->header.arglen != sizeof(*ip)) { NG_L2CAP_ALERT( "%s: %s - invalid L2CA_EnableCLT message size, size=%d\n", __func__, NG_NODE_NAME(l2cap->node), msg->header.arglen); return (EMSGSIZE); } /* Process request */ ip = (ng_l2cap_l2ca_enable_clt_ip *) (msg->data); #if 0 * result = NG_L2CAP_SUCCESS; #endif switch (ip->psm) { case 0: /* Special case: disable/enable all PSM */ if (ip->enable) l2cap->flags &= ~(NG_L2CAP_CLT_SDP_DISABLED | NG_L2CAP_CLT_RFCOMM_DISABLED | NG_L2CAP_CLT_TCP_DISABLED); else l2cap->flags |= (NG_L2CAP_CLT_SDP_DISABLED | NG_L2CAP_CLT_RFCOMM_DISABLED | NG_L2CAP_CLT_TCP_DISABLED); break; case NG_L2CAP_PSM_SDP: if (ip->enable) l2cap->flags &= ~NG_L2CAP_CLT_SDP_DISABLED; else l2cap->flags |= NG_L2CAP_CLT_SDP_DISABLED; break; case NG_L2CAP_PSM_RFCOMM: if (ip->enable) l2cap->flags &= ~NG_L2CAP_CLT_RFCOMM_DISABLED; else l2cap->flags |= NG_L2CAP_CLT_RFCOMM_DISABLED; break; case NG_L2CAP_PSM_TCP: if (ip->enable) l2cap->flags &= ~NG_L2CAP_CLT_TCP_DISABLED; else l2cap->flags |= NG_L2CAP_CLT_TCP_DISABLED; break; default: NG_L2CAP_ERR( "%s: %s - unsupported PSM=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->psm); #if 0 * result = NG_L2CAP_PSM_NOT_SUPPORTED; #endif error = ENOTSUP; break; } #if 0 * /* Create and send response message */ * token = msg->header.token; * NG_FREE_MSG(msg); * NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENABLE_CLT, * sizeof(*op), M_NOWAIT); * if (msg == NULL) * error = ENOMEM; * else { * msg->header.token = token; * msg->header.flags |= NGF_RESP; * * op = (ng_l2cap_l2ca_enable_clt_op *)(msg->data); * op->result = result; * } * * /* Send response to control hook */ * if (l2cap->ctl != NULL && NG_HOOK_IS_VALID(l2cap->ctl)) * NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0); #endif return (error); } /* ng_l2cap_l2ca_enable_clt */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h (revision 290038) @@ -1,79 +1,79 @@ /* * ng_l2cap_ulpi.h */ /*- * Copyright (c) Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_ulpi.h,v 1.1 2002/11/24 19:47:06 max Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_L2CAP_ULPI_H_ #define _NETGRAPH_L2CAP_ULPI_H_ int ng_l2cap_l2ca_con_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_con_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t, u_int16_t); int ng_l2cap_l2ca_con_rsp_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_con_rsp_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t); int ng_l2cap_l2ca_con_ind (ng_l2cap_chan_p); int ng_l2cap_l2ca_cfg_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_cfg_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t); int ng_l2cap_l2ca_cfg_rsp_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_cfg_rsp_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t); int ng_l2cap_l2ca_cfg_ind (ng_l2cap_chan_p); int ng_l2cap_l2ca_write_req (ng_l2cap_p, struct mbuf *); int ng_l2cap_l2ca_write_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t, u_int16_t); int ng_l2cap_l2ca_receive (ng_l2cap_con_p); int ng_l2cap_l2ca_clt_receive (ng_l2cap_con_p); int ng_l2cap_l2ca_qos_ind (ng_l2cap_chan_p); int ng_l2cap_l2ca_discon_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_discon_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t); int ng_l2cap_l2ca_discon_ind (ng_l2cap_chan_p); int ng_l2cap_l2ca_grp_create (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_grp_close (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_grp_add_member_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_grp_add_member_rsp (ng_l2cap_chan_p, u_int32_t, u_int16_t); int ng_l2cap_l2ca_grp_rem_member (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_grp_get_members (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_ping_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_ping_rsp (ng_l2cap_con_p, u_int32_t, u_int16_t, struct mbuf *); int ng_l2cap_l2ca_get_info_req (ng_l2cap_p, struct ng_mesg *); int ng_l2cap_l2ca_get_info_rsp (ng_l2cap_con_p, u_int32_t, u_int16_t, struct mbuf *); int ng_l2cap_l2ca_enable_clt (ng_l2cap_p, struct ng_mesg *); - +int ng_l2cap_l2ca_encryption_change(ng_l2cap_chan_p , uint16_t ); #endif /* ndef _NETGRAPH_L2CAP_ULPI_H_ */ Index: head/sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h =================================================================== --- head/sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h (revision 290037) +++ head/sys/netgraph/bluetooth/l2cap/ng_l2cap_var.h (revision 290038) @@ -1,193 +1,194 @@ /* * ng_l2cap_var.h */ /*- * Copyright (c) 2001 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_l2cap_var.h,v 1.2 2003/04/28 21:44:59 max Exp $ * $FreeBSD$ */ #ifndef _NETGRAPH_L2CAP_VAR_H_ #define _NETGRAPH_L2CAP_VAR_H_ /* MALLOC decalation */ #ifdef NG_SEPARATE_MALLOC MALLOC_DECLARE(M_NETGRAPH_L2CAP); #else #define M_NETGRAPH_L2CAP M_NETGRAPH #endif /* NG_SEPARATE_MALLOC */ /* Debug */ #define NG_L2CAP_ALERT if (l2cap->debug >= NG_L2CAP_ALERT_LEVEL) printf #define NG_L2CAP_ERR if (l2cap->debug >= NG_L2CAP_ERR_LEVEL) printf #define NG_L2CAP_WARN if (l2cap->debug >= NG_L2CAP_WARN_LEVEL) printf #define NG_L2CAP_INFO if (l2cap->debug >= NG_L2CAP_INFO_LEVEL) printf /* Wrapper around m_pullup */ #define NG_L2CAP_M_PULLUP(m, s) \ do { \ if ((m)->m_len < (s)) \ (m) = m_pullup((m), (s)); \ if ((m) == NULL) \ NG_L2CAP_ALERT("%s: %s - m_pullup(%zd) failed\n", \ __func__, NG_NODE_NAME(l2cap->node), (s)); \ } while (0) /* * L2CAP signaling command ident's are assigned relative to the connection, * because there is only one signaling channel (cid == 0x01) for every * connection. So up to 254 (0xff - 0x01) L2CAP commands can be pending at the * same time for the same connection. */ #define NG_L2CAP_NULL_IDENT 0x00 /* DO NOT USE THIS IDENT */ #define NG_L2CAP_FIRST_IDENT 0x01 /* dynamically alloc. (start) */ #define NG_L2CAP_LAST_IDENT 0xff /* dynamically alloc. (end) */ /* * L2CAP (Node private) */ struct ng_l2cap_con; struct ng_l2cap_chan; typedef struct ng_l2cap { node_p node; /* node ptr */ ng_l2cap_node_debug_ep debug; /* debug level */ ng_l2cap_node_flags_ep flags; /* L2CAP node flags */ ng_l2cap_node_auto_discon_ep discon_timo; /* auto discon. timeout */ u_int16_t pkt_size; /* max. ACL packet size */ u_int16_t num_pkts; /* out queue size */ bdaddr_t bdaddr; /* unit BDADDR */ hook_p hci; /* HCI downstream hook */ hook_p l2c; /* L2CAP upstream hook */ hook_p ctl; /* control hook */ LIST_HEAD(, ng_l2cap_con) con_list; /* ACL connections */ u_int16_t cid; /* last allocated CID */ u_int16_t lecid; /* last allocated CID for LE */ LIST_HEAD(, ng_l2cap_chan) chan_list; /* L2CAP channels */ } ng_l2cap_t; typedef ng_l2cap_t * ng_l2cap_p; /* * L2CAP connection descriptor */ struct ng_l2cap_cmd; typedef struct ng_l2cap_con { ng_l2cap_p l2cap; /* pointer to L2CAP */ u_int16_t state; /* ACL connection state */ u_int16_t flags; /* ACL connection flags */ int32_t refcnt; /* reference count */ bdaddr_t remote; /* remote unit address */ u_int16_t con_handle; /* ACL connection handle */ struct callout con_timo; /* connection timeout */ u_int8_t ident; /* last allocated ident */ uint8_t linktype; - + uint8_t encryption; + TAILQ_HEAD(, ng_l2cap_cmd) cmd_list; /* pending L2CAP cmds */ struct mbuf *tx_pkt; /* xmitted L2CAP packet */ int pending; /* num. of pending pkts */ struct mbuf *rx_pkt; /* received L2CAP packet */ int rx_pkt_len; /* packet len. so far */ LIST_ENTRY(ng_l2cap_con) next; /* link */ } ng_l2cap_con_t; typedef ng_l2cap_con_t * ng_l2cap_con_p; /* * L2CAP channel descriptor */ typedef struct ng_l2cap_chan { ng_l2cap_con_p con; /* pointer to connection */ u_int16_t state; /* channel state */ u_int8_t cfg_state; /* configuration state */ #define NG_L2CAP_CFG_IN (1 << 0) /* incoming cfg path done */ #define NG_L2CAP_CFG_OUT (1 << 1) /* outgoing cfg path done */ #define NG_L2CAP_CFG_BOTH (NG_L2CAP_CFG_IN|NG_L2CAP_CFG_OUT) u_int8_t ident; /* last L2CAP req. ident */ u_int16_t psm; /* channel PSM */ u_int16_t scid; /* source channel ID */ u_int16_t dcid; /* destination channel ID */ uint16_t idtype; u_int16_t imtu; /* incoming channel MTU */ ng_l2cap_flow_t iflow; /* incoming flow control */ u_int16_t omtu; /* outgoing channel MTU */ ng_l2cap_flow_t oflow; /* outgoing flow control */ u_int16_t flush_timo; /* flush timeout */ u_int16_t link_timo; /* link timeout */ LIST_ENTRY(ng_l2cap_chan) next; /* link */ } ng_l2cap_chan_t; typedef ng_l2cap_chan_t * ng_l2cap_chan_p; /* * L2CAP command descriptor */ typedef struct ng_l2cap_cmd { ng_l2cap_con_p con; /* L2CAP connection */ ng_l2cap_chan_p ch; /* L2CAP channel */ u_int16_t flags; /* command flags */ #define NG_L2CAP_CMD_PENDING (1 << 0) /* command is pending */ u_int8_t code; /* L2CAP command opcode */ u_int8_t ident; /* L2CAP command ident */ u_int32_t token; /* L2CA message token */ struct callout timo; /* RTX/ERTX timeout */ struct mbuf *aux; /* optional data */ TAILQ_ENTRY(ng_l2cap_cmd) next; /* link */ } ng_l2cap_cmd_t; typedef ng_l2cap_cmd_t * ng_l2cap_cmd_p; #endif /* ndef _NETGRAPH_L2CAP_VAR_H_ */ Index: head/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c =================================================================== --- head/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c (revision 290037) +++ head/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c (revision 290038) @@ -1,2903 +1,2967 @@ /* * ng_btsocket_l2cap.c */ /*- * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ng_btsocket_l2cap.c,v 1.16 2003/09/14 23:29:06 max Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* MALLOC define */ #ifdef NG_SEPARATE_MALLOC static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_L2CAP, "netgraph_btsocks_l2cap", "Netgraph Bluetooth L2CAP sockets"); #else #define M_NETGRAPH_BTSOCKET_L2CAP M_NETGRAPH #endif /* NG_SEPARATE_MALLOC */ /* Netgraph node methods */ static ng_constructor_t ng_btsocket_l2cap_node_constructor; static ng_rcvmsg_t ng_btsocket_l2cap_node_rcvmsg; static ng_shutdown_t ng_btsocket_l2cap_node_shutdown; static ng_newhook_t ng_btsocket_l2cap_node_newhook; static ng_connect_t ng_btsocket_l2cap_node_connect; static ng_rcvdata_t ng_btsocket_l2cap_node_rcvdata; static ng_disconnect_t ng_btsocket_l2cap_node_disconnect; static void ng_btsocket_l2cap_input (void *, int); static void ng_btsocket_l2cap_rtclean (void *, int); /* Netgraph type descriptor */ static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_BTSOCKET_L2CAP_NODE_TYPE, .constructor = ng_btsocket_l2cap_node_constructor, .rcvmsg = ng_btsocket_l2cap_node_rcvmsg, .shutdown = ng_btsocket_l2cap_node_shutdown, .newhook = ng_btsocket_l2cap_node_newhook, .connect = ng_btsocket_l2cap_node_connect, .rcvdata = ng_btsocket_l2cap_node_rcvdata, .disconnect = ng_btsocket_l2cap_node_disconnect, }; /* Globals */ extern int ifqmaxlen; static u_int32_t ng_btsocket_l2cap_debug_level; static node_p ng_btsocket_l2cap_node; static struct ng_bt_itemq ng_btsocket_l2cap_queue; static struct mtx ng_btsocket_l2cap_queue_mtx; static struct task ng_btsocket_l2cap_queue_task; static LIST_HEAD(, ng_btsocket_l2cap_pcb) ng_btsocket_l2cap_sockets; static struct mtx ng_btsocket_l2cap_sockets_mtx; static LIST_HEAD(, ng_btsocket_l2cap_rtentry) ng_btsocket_l2cap_rt; static struct mtx ng_btsocket_l2cap_rt_mtx; static struct task ng_btsocket_l2cap_rt_task; static struct timeval ng_btsocket_l2cap_lasttime; static int ng_btsocket_l2cap_curpps; /* Sysctl tree */ SYSCTL_DECL(_net_bluetooth_l2cap_sockets); static SYSCTL_NODE(_net_bluetooth_l2cap_sockets, OID_AUTO, seq, CTLFLAG_RW, 0, "Bluetooth SEQPACKET L2CAP sockets family"); SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, debug_level, CTLFLAG_RW, &ng_btsocket_l2cap_debug_level, NG_BTSOCKET_WARN_LEVEL, "Bluetooth SEQPACKET L2CAP sockets debug level"); SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_len, CTLFLAG_RD, &ng_btsocket_l2cap_queue.len, 0, "Bluetooth SEQPACKET L2CAP sockets input queue length"); SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_maxlen, CTLFLAG_RD, &ng_btsocket_l2cap_queue.maxlen, 0, "Bluetooth SEQPACKET L2CAP sockets input queue max. length"); SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_drops, CTLFLAG_RD, &ng_btsocket_l2cap_queue.drops, 0, "Bluetooth SEQPACKET L2CAP sockets input queue drops"); /* Debug */ #define NG_BTSOCKET_L2CAP_INFO \ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_INFO_LEVEL && \ ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \ printf #define NG_BTSOCKET_L2CAP_WARN \ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_WARN_LEVEL && \ ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \ printf #define NG_BTSOCKET_L2CAP_ERR \ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ERR_LEVEL && \ ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \ printf #define NG_BTSOCKET_L2CAP_ALERT \ if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \ ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \ printf /* * Netgraph message processing routines */ static int ng_btsocket_l2cap_process_l2ca_con_req_rsp (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_con_rsp_rsp (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_con_ind (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_cfg_req_rsp (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_cfg_ind (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_discon_rsp (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_discon_ind (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); static int ng_btsocket_l2cap_process_l2ca_write_rsp (struct ng_mesg *, ng_btsocket_l2cap_rtentry_p); /* * Send L2CA_xxx messages to the lower layer */ static int ng_btsocket_l2cap_send_l2ca_con_req (ng_btsocket_l2cap_pcb_p); static int ng_btsocket_l2cap_send_l2ca_con_rsp_req (u_int32_t, ng_btsocket_l2cap_rtentry_p, bdaddr_p, int, int, int, int); static int ng_btsocket_l2cap_send_l2ca_cfg_req (ng_btsocket_l2cap_pcb_p); static int ng_btsocket_l2cap_send_l2ca_cfg_rsp (ng_btsocket_l2cap_pcb_p); static int ng_btsocket_l2cap_send_l2ca_discon_req (u_int32_t, ng_btsocket_l2cap_pcb_p); static int ng_btsocket_l2cap_send2 (ng_btsocket_l2cap_pcb_p); /* * Timeout processing routines */ static void ng_btsocket_l2cap_timeout (ng_btsocket_l2cap_pcb_p); static void ng_btsocket_l2cap_untimeout (ng_btsocket_l2cap_pcb_p); static void ng_btsocket_l2cap_process_timeout (void *); /* * Other stuff */ static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_addr(bdaddr_p, int); static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_token(u_int32_t); static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_cid (bdaddr_p, int,int); static int ng_btsocket_l2cap_result2errno(int); static int ng_btsock_l2cap_addrtype_to_linktype(int addrtype); -static int ng_btsock_l2cap_pcb_to_idtype(struct ng_btsocket_l2cap_pcb *); + #define ng_btsocket_l2cap_wakeup_input_task() \ taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_l2cap_queue_task) #define ng_btsocket_l2cap_wakeup_route_task() \ taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_l2cap_rt_task) -int ng_btsock_l2cap_pcb_to_idtype(struct ng_btsocket_l2cap_pcb *pcb) -{ - if(pcb->dsttype == BDADDR_BREDR){ - return NG_L2CAP_L2CA_IDTYPE_BREDR; - }else if(pcb->psm == 0){ - return NG_L2CAP_L2CA_IDTYPE_ATT; - }else{ - return NG_L2CAP_L2CA_IDTYPE_LE; - } -} int ng_btsock_l2cap_addrtype_to_linktype(int addrtype) { switch(addrtype){ case BDADDR_LE_PUBLIC: return NG_HCI_LINK_LE_PUBLIC; case BDADDR_LE_RANDOM: return NG_HCI_LINK_LE_RANDOM; default: return NG_HCI_LINK_ACL; } } /***************************************************************************** ***************************************************************************** ** Netgraph node interface ***************************************************************************** *****************************************************************************/ /* * Netgraph node constructor. Do not allow to create node of this type. */ static int ng_btsocket_l2cap_node_constructor(node_p node) { return (EINVAL); } /* ng_btsocket_l2cap_node_constructor */ /* * Do local shutdown processing. Let old node go and create new fresh one. */ static int ng_btsocket_l2cap_node_shutdown(node_p node) { int error = 0; NG_NODE_UNREF(node); /* Create new node */ error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node); if (error != 0) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not create Netgraph node, error=%d\n", __func__, error); ng_btsocket_l2cap_node = NULL; return (error); } error = ng_name_node(ng_btsocket_l2cap_node, NG_BTSOCKET_L2CAP_NODE_TYPE); if (error != 0) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not name Netgraph node, error=%d\n", __func__, error); NG_NODE_UNREF(ng_btsocket_l2cap_node); ng_btsocket_l2cap_node = NULL; return (error); } return (0); } /* ng_btsocket_l2cap_node_shutdown */ /* * We allow any hook to be connected to the node. */ static int ng_btsocket_l2cap_node_newhook(node_p node, hook_p hook, char const *name) { return (0); } /* ng_btsocket_l2cap_node_newhook */ /* * Just say "YEP, that's OK by me!" */ static int ng_btsocket_l2cap_node_connect(hook_p hook) { NG_HOOK_SET_PRIVATE(hook, NULL); NG_HOOK_REF(hook); /* Keep extra reference to the hook */ #if 0 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); NG_HOOK_FORCE_QUEUE(hook); #endif return (0); } /* ng_btsocket_l2cap_node_connect */ /* * Hook disconnection. Schedule route cleanup task */ static int ng_btsocket_l2cap_node_disconnect(hook_p hook) { /* * If hook has private information than we must have this hook in * the routing table and must schedule cleaning for the routing table. * Otherwise hook was connected but we never got "hook_info" message, * so we have never added this hook to the routing table and it save * to just delete it. */ if (NG_HOOK_PRIVATE(hook) != NULL) return (ng_btsocket_l2cap_wakeup_route_task()); NG_HOOK_UNREF(hook); /* Remove extra reference */ return (0); } /* ng_btsocket_l2cap_node_disconnect */ /* * Process incoming messages */ static int ng_btsocket_l2cap_node_rcvmsg(node_p node, item_p item, hook_p hook) { struct ng_mesg *msg = NGI_MSG(item); /* item still has message */ int error = 0; if (msg != NULL && msg->header.typecookie == NGM_L2CAP_COOKIE) { mtx_lock(&ng_btsocket_l2cap_queue_mtx); if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) { NG_BTSOCKET_L2CAP_ERR( "%s: Input queue is full (msg)\n", __func__); NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue); NG_FREE_ITEM(item); error = ENOBUFS; } else { if (hook != NULL) { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); } NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item); error = ng_btsocket_l2cap_wakeup_input_task(); } mtx_unlock(&ng_btsocket_l2cap_queue_mtx); } else { NG_FREE_ITEM(item); error = EINVAL; } return (error); } /* ng_btsocket_l2cap_node_rcvmsg */ /* * Receive data on a hook */ static int ng_btsocket_l2cap_node_rcvdata(hook_p hook, item_p item) { int error = 0; mtx_lock(&ng_btsocket_l2cap_queue_mtx); if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) { NG_BTSOCKET_L2CAP_ERR( "%s: Input queue is full (data)\n", __func__); NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue); NG_FREE_ITEM(item); error = ENOBUFS; } else { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item); error = ng_btsocket_l2cap_wakeup_input_task(); } mtx_unlock(&ng_btsocket_l2cap_queue_mtx); return (error); } /* ng_btsocket_l2cap_node_rcvdata */ /* * Process L2CA_Connect respose. Socket layer must have initiated connection, * so we have to have a socket associated with message token. */ static int ng_btsocket_l2cap_process_l2ca_con_req_rsp(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_con_op *op = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; int error = 0; if (msg->header.arglen != sizeof(*op)) return (EMSGSIZE); op = (ng_l2cap_l2ca_con_op *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Look for the socket with the token */ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Connect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, status=%d, " \ "state=%d\n", __func__, msg->header.token, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, op->lcid, op->result, op->status, pcb->state); if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) { mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } ng_btsocket_l2cap_untimeout(pcb); if (op->result == NG_L2CAP_PENDING) { ng_btsocket_l2cap_timeout(pcb); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } if (op->result == NG_L2CAP_SUCCESS){ - if(ng_btsock_l2cap_pcb_to_idtype(pcb) == - NG_L2CAP_L2CA_IDTYPE_ATT){ - pcb->state = NG_BTSOCKET_L2CAP_OPEN; - soisconnected(pcb->so); - pcb->cid = op->lcid; + if((pcb->idtype == NG_L2CAP_L2CA_IDTYPE_ATT)|| + (pcb->idtype == NG_L2CAP_L2CA_IDTYPE_SMP)){ + pcb->encryption = op->encryption; pcb->cid = op->lcid; + if(pcb->need_encrypt && !(pcb->encryption)){ + pcb->state = NG_BTSOCKET_L2CAP_W4_ENC_CHANGE; + }else{ + pcb->state = NG_BTSOCKET_L2CAP_OPEN; + soisconnected(pcb->so); + } }else{ /* * Channel is now open, so update local channel ID and * start configuration process. Source and destination * addresses as well as route must be already set. */ pcb->cid = op->lcid; - + pcb->encryption = op->encryption; error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb); if (error != 0) { /* Send disconnect request with "zero" token */ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb); /* ... and close the socket */ pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); } else { pcb->cfg_state = NG_BTSOCKET_L2CAP_CFG_IN_SENT; pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING; ng_btsocket_l2cap_timeout(pcb); } } } else { /* * We have failed to open connection, so convert result * code to "errno" code and disconnect the socket. Channel * already has been closed. */ pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result); pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); } - mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (error); } /* ng_btsocket_l2cap_process_l2ca_con_req_rsp */ /* * Process L2CA_ConnectRsp response */ static int ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_con_rsp_op *op = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; if (msg->header.arglen != sizeof(*op)) return (EMSGSIZE); op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Look for the socket with the token */ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_ConnectRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n", __func__, msg->header.token, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, op->result, pcb->state); if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) { mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } ng_btsocket_l2cap_untimeout(pcb); /* Check the result and disconnect the socket on failure */ if (op->result != NG_L2CAP_SUCCESS) { /* Close the socket - channel already closed */ pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result); pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); } else { /* Move to CONFIGURING state and wait for CONFIG_IND */ pcb->cfg_state = 0; pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING; ng_btsocket_l2cap_timeout(pcb); } mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* ng_btsocket_process_l2ca_con_rsp_rsp */ /* * Process L2CA_Connect indicator. Find socket that listens on address * and PSM. Find exact or closest match. Create new socket and initiate * connection. */ static int ng_btsocket_l2cap_process_l2ca_con_ind(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_con_ind_ip *ip = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL, *pcb1 = NULL; int error = 0; u_int32_t token = 0; u_int16_t result = 0; if (msg->header.arglen != sizeof(*ip)) return (EMSGSIZE); ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Connect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, ident=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], ip->bdaddr.b[5], ip->bdaddr.b[4], ip->bdaddr.b[3], ip->bdaddr.b[2], ip->bdaddr.b[1], ip->bdaddr.b[0], ip->psm, ip->lcid, ip->ident); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); pcb = ng_btsocket_l2cap_pcb_by_addr(&rt->src, ip->psm); if (pcb != NULL) { struct socket *so1 = NULL; mtx_lock(&pcb->pcb_mtx); /* * First check the pending connections queue and if we have * space then create new socket and set proper source address. */ if (pcb->so->so_qlen <= pcb->so->so_qlimit) { CURVNET_SET(pcb->so->so_vnet); so1 = sonewconn(pcb->so, 0); CURVNET_RESTORE(); } if (so1 == NULL) { result = NG_L2CAP_NO_RESOURCES; goto respond; } /* * If we got here than we have created new socket. So complete * connection. If we we listening on specific address then copy * source address from listening socket, otherwise copy source * address from hook's routing information. */ pcb1 = so2l2cap_pcb(so1); KASSERT((pcb1 != NULL), ("%s: pcb1 == NULL\n", __func__)); mtx_lock(&pcb1->pcb_mtx); if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0) bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src)); else bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src)); pcb1->flags &= ~NG_BTSOCKET_L2CAP_CLIENT; bcopy(&ip->bdaddr, &pcb1->dst, sizeof(pcb1->dst)); pcb1->psm = ip->psm; pcb1->cid = ip->lcid; pcb1->rt = rt; /* Copy socket settings */ pcb1->imtu = pcb->imtu; bcopy(&pcb->oflow, &pcb1->oflow, sizeof(pcb1->oflow)); pcb1->flush_timo = pcb->flush_timo; token = pcb1->token; } else /* Nobody listens on requested BDADDR/PSM */ result = NG_L2CAP_PSM_NOT_SUPPORTED; respond: error = ng_btsocket_l2cap_send_l2ca_con_rsp_req(token, rt, &ip->bdaddr, ip->ident, ip->lcid, result,ip->linktype); if (pcb1 != NULL) { if (error != 0) { pcb1->so->so_error = error; pcb1->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb1->so); } else { pcb1->state = NG_BTSOCKET_L2CAP_CONNECTING; soisconnecting(pcb1->so); ng_btsocket_l2cap_timeout(pcb1); } mtx_unlock(&pcb1->pcb_mtx); } if (pcb != NULL) mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (error); } /* ng_btsocket_l2cap_process_l2ca_con_ind */ +/*Encryption Change*/ +static int ng_btsocket_l2cap_process_l2ca_enc_change(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) +{ + ng_l2cap_l2ca_enc_chg_op *op = NULL; + ng_btsocket_l2cap_pcb_t *pcb = NULL; + + if (msg->header.arglen != sizeof(*op)) + return (EMSGSIZE); + + op = (ng_l2cap_l2ca_enc_chg_op *)(msg->data); + + pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, op->lcid, + op->idtype); + mtx_lock(&pcb->pcb_mtx); + pcb->encryption = op->result; + + if(pcb->need_encrypt){ + if(pcb->state != NG_BTSOCKET_L2CAP_W4_ENC_CHANGE){ + NG_BTSOCKET_L2CAP_WARN("%s: Invalid pcb status %d", + __func__, pcb->state); + }else if(pcb->encryption){ + pcb->state = NG_BTSOCKET_L2CAP_OPEN; + soisconnected(pcb->so); + }else{ + pcb->so->so_error = EPERM; + pcb->state = NG_BTSOCKET_L2CAP_CLOSED; + soisdisconnected(pcb->so); + } + } + mtx_unlock(&pcb->pcb_mtx); + + return 0; +} /* * Process L2CA_Config response */ static int ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_cfg_op *op = NULL; ng_btsocket_l2cap_pcb_p pcb = NULL; if (msg->header.arglen != sizeof(*op)) return (EMSGSIZE); op = (ng_l2cap_l2ca_cfg_op *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* * Socket must have issued a Configure request, so we must have a * socket that wants to be configured. Use Netgraph message token * to find it */ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token); if (pcb == NULL) { /* * XXX FIXME what to do here? We could not find a * socket with requested token. We even can not send * Disconnect, because we do not know channel ID */ mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Config response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \ "cfg_state=%x\n", __func__, msg->header.token, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state); if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) { mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } if (op->result == NG_L2CAP_SUCCESS) { /* * XXX FIXME Actually set flush and link timeout. * Set QoS here if required. Resolve conficts (flush_timo). * Save incoming MTU (peer's outgoing MTU) and outgoing flow * spec. */ pcb->imtu = op->imtu; bcopy(&op->oflow, &pcb->oflow, sizeof(pcb->oflow)); pcb->flush_timo = op->flush_timo; /* * We have configured incoming side, so record it and check * if configuration is complete. If complete then mark socket * as connected, otherwise wait for the peer. */ pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_IN_SENT; pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN; if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) { /* Configuration complete - mark socket as open */ ng_btsocket_l2cap_untimeout(pcb); pcb->state = NG_BTSOCKET_L2CAP_OPEN; soisconnected(pcb->so); } } else { /* * Something went wrong. Could be unacceptable parameters, * reject or unknown option. That's too bad, but we will * not negotiate. Send Disconnect and close the channel. */ ng_btsocket_l2cap_untimeout(pcb); switch (op->result) { case NG_L2CAP_UNACCEPTABLE_PARAMS: case NG_L2CAP_UNKNOWN_OPTION: pcb->so->so_error = EINVAL; break; default: pcb->so->so_error = ECONNRESET; break; } /* Send disconnect with "zero" token */ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb); /* ... and close the socket */ pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); } mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* ng_btsocket_l2cap_process_l2ca_cfg_req_rsp */ /* * Process L2CA_ConfigRsp response */ static int ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_cfg_rsp_op *op = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; int error = 0; if (msg->header.arglen != sizeof(*op)) return (EMSGSIZE); op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Look for the socket with the token */ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_ConfigRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \ "cfg_state=%x\n", __func__, msg->header.token, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state); if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) { mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } /* Check the result and disconnect socket of failure */ if (op->result != NG_L2CAP_SUCCESS) goto disconnect; /* * Now we done with remote side configuration. Configure local * side if we have not done it yet. */ pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_OUT_SENT; pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT; if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) { /* Configuration complete - mask socket as open */ ng_btsocket_l2cap_untimeout(pcb); pcb->state = NG_BTSOCKET_L2CAP_OPEN; soisconnected(pcb->so); } else { if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_IN_SENT)) { /* Send L2CA_Config request - incoming path */ error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb); if (error != 0) goto disconnect; pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN_SENT; } } mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (error); disconnect: ng_btsocket_l2cap_untimeout(pcb); /* Send disconnect with "zero" token */ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb); /* ... and close the socket */ pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (error); } /* ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp */ /* * Process L2CA_Config indicator */ static int ng_btsocket_l2cap_process_l2ca_cfg_ind(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_cfg_ind_ip *ip = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; int error = 0; if (msg->header.arglen != sizeof(*ip)) return (EMSGSIZE); ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Check for the open socket that has given channel ID */ pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid, NG_L2CAP_L2CA_IDTYPE_BREDR); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Config indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d, cfg_state=%x\n", __func__, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, pcb->state, pcb->cfg_state); /* XXX FIXME re-configuration on open socket */ if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) { mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } /* * XXX FIXME Actually set flush and link timeout. Set QoS here if * required. Resolve conficts (flush_timo). Note outgoing MTU (peer's * incoming MTU) and incoming flow spec. */ pcb->omtu = ip->omtu; bcopy(&ip->iflow, &pcb->iflow, sizeof(pcb->iflow)); pcb->flush_timo = ip->flush_timo; /* * Send L2CA_Config response to our peer and check for the errors, * if any send disconnect to close the channel. */ if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_OUT_SENT)) { error = ng_btsocket_l2cap_send_l2ca_cfg_rsp(pcb); if (error != 0) { ng_btsocket_l2cap_untimeout(pcb); pcb->so->so_error = error; /* Send disconnect with "zero" token */ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb); /* ... and close the socket */ pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); } else pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT_SENT; } mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (error); } /* ng_btsocket_l2cap_process_l2cap_cfg_ind */ /* * Process L2CA_Disconnect response */ static int ng_btsocket_l2cap_process_l2ca_discon_rsp(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_discon_op *op = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; /* Check message */ if (msg->header.arglen != sizeof(*op)) return (EMSGSIZE); op = (ng_l2cap_l2ca_discon_op *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* * Socket layer must have issued L2CA_Disconnect request, so there * must be a socket that wants to be disconnected. Use Netgraph * message token to find it. */ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } mtx_lock(&pcb->pcb_mtx); /* XXX Close socket no matter what op->result says */ if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) { NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Disconnect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n", __func__, msg->header.token, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, op->result, pcb->state); ng_btsocket_l2cap_untimeout(pcb); pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); } mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* ng_btsocket_l2cap_process_l2ca_discon_rsp */ /* * Process L2CA_Disconnect indicator */ static int ng_btsocket_l2cap_process_l2ca_discon_ind(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_discon_ind_ip *ip = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; /* Check message */ if (msg->header.arglen != sizeof(*ip)) return (EMSGSIZE); ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Look for the socket with given channel ID */ pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid, NG_L2CAP_L2CA_IDTYPE_BREDR); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* * Channel has already been destroyed, so disconnect the socket * and be done with it. If there was any pending request we can * not do anything here anyway. */ mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Disconnect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d\n", __func__, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, pcb->state); if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) ng_btsocket_l2cap_untimeout(pcb); pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* ng_btsocket_l2cap_process_l2ca_discon_ind */ /* * Process L2CA_Write response */ static int ng_btsocket_l2cap_process_l2ca_write_rsp(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt) { ng_l2cap_l2ca_write_op *op = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; /* Check message */ if (msg->header.arglen != sizeof(*op)) return (EMSGSIZE); op = (ng_l2cap_l2ca_write_op *)(msg->data); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Look for the socket with given token */ pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } mtx_lock(&pcb->pcb_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CA_Write response, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, length=%d, " \ "state=%d\n", __func__, pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], pcb->psm, pcb->cid, op->result, op->length, pcb->state); if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) { mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (ENOENT); } ng_btsocket_l2cap_untimeout(pcb); /* * Check if we have more data to send */ sbdroprecord(&pcb->so->so_snd); if (sbavail(&pcb->so->so_snd) > 0) { if (ng_btsocket_l2cap_send2(pcb) == 0) ng_btsocket_l2cap_timeout(pcb); else sbdroprecord(&pcb->so->so_snd); /* XXX */ } /* * Now set the result, drop packet from the socket send queue and * ask for more (wakeup sender) */ pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result); sowwakeup(pcb->so); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* ng_btsocket_l2cap_process_l2ca_write_rsp */ /* * Send L2CA_Connect request */ static int ng_btsocket_l2cap_send_l2ca_con_req(ng_btsocket_l2cap_pcb_p pcb) { struct ng_mesg *msg = NULL; ng_l2cap_l2ca_con_ip *ip = NULL; int error = 0; mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (pcb->rt == NULL || pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) return (ENETDOWN); NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON, sizeof(*ip), M_NOWAIT); if (msg == NULL) return (ENOMEM); msg->header.token = pcb->token; ip = (ng_l2cap_l2ca_con_ip *)(msg->data); bcopy(&pcb->dst, &ip->bdaddr, sizeof(ip->bdaddr)); ip->psm = pcb->psm; ip->linktype = ng_btsock_l2cap_addrtype_to_linktype(pcb->dsttype); - ip->idtype = ng_btsock_l2cap_pcb_to_idtype(pcb); + ip->idtype = pcb->idtype; NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0); return (error); } /* ng_btsocket_l2cap_send_l2ca_con_req */ /* * Send L2CA_Connect response */ static int ng_btsocket_l2cap_send_l2ca_con_rsp_req(u_int32_t token, ng_btsocket_l2cap_rtentry_p rt, bdaddr_p dst, int ident, int lcid, int result, int linktype) { struct ng_mesg *msg = NULL; ng_l2cap_l2ca_con_rsp_ip *ip = NULL; int error = 0; if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) return (ENETDOWN); NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP, sizeof(*ip), M_NOWAIT); if (msg == NULL) return (ENOMEM); msg->header.token = token; ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data); bcopy(dst, &ip->bdaddr, sizeof(ip->bdaddr)); ip->ident = ident; ip->lcid = lcid; ip->linktype = linktype; ip->result = result; ip->status = 0; NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, rt->hook, 0); return (error); } /* ng_btsocket_l2cap_send_l2ca_con_rsp_req */ /* * Send L2CA_Config request */ static int ng_btsocket_l2cap_send_l2ca_cfg_req(ng_btsocket_l2cap_pcb_p pcb) { struct ng_mesg *msg = NULL; ng_l2cap_l2ca_cfg_ip *ip = NULL; int error = 0; mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (pcb->rt == NULL || pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) return (ENETDOWN); NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG, sizeof(*ip), M_NOWAIT); if (msg == NULL) return (ENOMEM); msg->header.token = pcb->token; ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data); ip->lcid = pcb->cid; ip->imtu = pcb->imtu; bcopy(&pcb->oflow, &ip->oflow, sizeof(ip->oflow)); ip->flush_timo = pcb->flush_timo; ip->link_timo = pcb->link_timo; NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0); return (error); } /* ng_btsocket_l2cap_send_l2ca_cfg_req */ /* * Send L2CA_Config response */ static int ng_btsocket_l2cap_send_l2ca_cfg_rsp(ng_btsocket_l2cap_pcb_p pcb) { struct ng_mesg *msg = NULL; ng_l2cap_l2ca_cfg_rsp_ip *ip = NULL; int error = 0; mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (pcb->rt == NULL || pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) return (ENETDOWN); NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP, sizeof(*ip), M_NOWAIT); if (msg == NULL) return (ENOMEM); msg->header.token = pcb->token; ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data); ip->lcid = pcb->cid; ip->omtu = pcb->omtu; bcopy(&pcb->iflow, &ip->iflow, sizeof(ip->iflow)); NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, pcb->rt->hook, 0); return (error); } /* ng_btsocket_l2cap_send_l2ca_cfg_rsp */ /* * Send L2CA_Disconnect request */ static int ng_btsocket_l2cap_send_l2ca_discon_req(u_int32_t token, ng_btsocket_l2cap_pcb_p pcb) { struct ng_mesg *msg = NULL; ng_l2cap_l2ca_discon_ip *ip = NULL; int error = 0; mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (pcb->rt == NULL || pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) return (ENETDOWN); NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON, sizeof(*ip), M_NOWAIT); if (msg == NULL) return (ENOMEM); msg->header.token = token; ip = (ng_l2cap_l2ca_discon_ip *)(msg->data); ip->lcid = pcb->cid; - ip->idtype = ng_btsock_l2cap_pcb_to_idtype(pcb); + ip->idtype = pcb->idtype; NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0); return (error); } /* ng_btsocket_l2cap_send_l2ca_discon_req */ /***************************************************************************** ***************************************************************************** ** Socket interface ***************************************************************************** *****************************************************************************/ /* * L2CAP sockets data input routine */ static void ng_btsocket_l2cap_data_input(struct mbuf *m, hook_p hook) { ng_l2cap_hdr_t *hdr = NULL; ng_l2cap_clt_hdr_t *clt_hdr = NULL; ng_btsocket_l2cap_pcb_t *pcb = NULL; ng_btsocket_l2cap_rtentry_t *rt = NULL; uint16_t idtype; if (hook == NULL) { NG_BTSOCKET_L2CAP_ALERT( "%s: Invalid source hook for L2CAP data packet\n", __func__); goto drop; } rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook); if (rt == NULL) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not find out source bdaddr for L2CAP data packet\n", __func__); goto drop; } m = m_pullup(m, sizeof(uint16_t)); idtype = *mtod(m, uint16_t *); m_adj(m, sizeof(uint16_t)); /* Make sure we can access header */ if (m->m_pkthdr.len < sizeof(*hdr)) { NG_BTSOCKET_L2CAP_ERR( "%s: L2CAP data packet too small, len=%d\n", __func__, m->m_pkthdr.len); goto drop; } if (m->m_len < sizeof(*hdr)) { m = m_pullup(m, sizeof(*hdr)); if (m == NULL) goto drop; } /* Strip L2CAP packet header and verify packet length */ hdr = mtod(m, ng_l2cap_hdr_t *); m_adj(m, sizeof(*hdr)); if (hdr->length != m->m_pkthdr.len) { NG_BTSOCKET_L2CAP_ERR( "%s: Bad L2CAP data packet length, len=%d, length=%d\n", __func__, m->m_pkthdr.len, hdr->length); goto drop; } /* * Now process packet. Two cases: * * 1) Normal packet (cid != 2) then find connected socket and append * mbuf to the socket queue. Wakeup socket. * * 2) Broadcast packet (cid == 2) then find all sockets that connected * to the given PSM and have SO_BROADCAST bit set and append mbuf * to the socket queue. Wakeup socket. */ NG_BTSOCKET_L2CAP_INFO( "%s: Received L2CAP data packet: src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dcid=%d, length=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], hdr->dcid, hdr->length); if ((hdr->dcid >= NG_L2CAP_FIRST_CID) || - (idtype == NG_L2CAP_L2CA_IDTYPE_ATT)){ + (idtype == NG_L2CAP_L2CA_IDTYPE_ATT)|| + (idtype == NG_L2CAP_L2CA_IDTYPE_SMP) + ){ mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* Normal packet: find connected socket */ pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, hdr->dcid,idtype); if (pcb == NULL) { mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); goto drop; } mtx_lock(&pcb->pcb_mtx); if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) { NG_BTSOCKET_L2CAP_ERR( "%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, " \ "state=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], hdr->dcid, pcb->state); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); goto drop; } /* Check packet size against socket's incoming MTU */ if (hdr->length > pcb->imtu) { NG_BTSOCKET_L2CAP_ERR( "%s: L2CAP data packet too big, src bdaddr=%x:%x:%x:%x:%x:%x, " \ "dcid=%d, length=%d, imtu=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], hdr->dcid, hdr->length, pcb->imtu); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); goto drop; } /* Check if we have enough space in socket receive queue */ if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) { /* * This is really bad. Receive queue on socket does * not have enough space for the packet. We do not * have any other choice but drop the packet. L2CAP * does not provide any flow control. */ NG_BTSOCKET_L2CAP_ERR( "%s: Not enough space in socket receive queue. Dropping L2CAP data packet, " \ "src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, len=%d, space=%ld\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], hdr->dcid, m->m_pkthdr.len, sbspace(&pcb->so->so_rcv)); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); goto drop; } /* Append packet to the socket receive queue and wakeup */ sbappendrecord(&pcb->so->so_rcv, m); m = NULL; sorwakeup(pcb->so); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); } else if (hdr->dcid == NG_L2CAP_CLT_CID) { /* Broadcast packet: give packet to all sockets */ /* Check packet size against connectionless MTU */ if (hdr->length > NG_L2CAP_MTU_DEFAULT) { NG_BTSOCKET_L2CAP_ERR( "%s: Connectionless L2CAP data packet too big, " \ "src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], hdr->length); goto drop; } /* Make sure we can access connectionless header */ if (m->m_pkthdr.len < sizeof(*clt_hdr)) { NG_BTSOCKET_L2CAP_ERR( "%s: Can not get L2CAP connectionless packet header, " \ "src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], hdr->length); goto drop; } if (m->m_len < sizeof(*clt_hdr)) { m = m_pullup(m, sizeof(*clt_hdr)); if (m == NULL) goto drop; } /* Strip connectionless header and deliver packet */ clt_hdr = mtod(m, ng_l2cap_clt_hdr_t *); m_adj(m, sizeof(*clt_hdr)); NG_BTSOCKET_L2CAP_INFO( "%s: Got L2CAP connectionless data packet, " \ "src bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, length=%d\n", __func__, rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0], clt_hdr->psm, hdr->length); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next) { struct mbuf *copy = NULL; mtx_lock(&pcb->pcb_mtx); if (bcmp(&rt->src, &pcb->src, sizeof(pcb->src)) != 0 || pcb->psm != clt_hdr->psm || pcb->state != NG_BTSOCKET_L2CAP_OPEN || (pcb->so->so_options & SO_BROADCAST) == 0 || m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) goto next; /* * Create a copy of the packet and append it to the * socket's queue. If m_dup() failed - no big deal * it is a broadcast traffic after all */ copy = m_dup(m, M_NOWAIT); if (copy != NULL) { sbappendrecord(&pcb->so->so_rcv, copy); sorwakeup(pcb->so); } next: mtx_unlock(&pcb->pcb_mtx); } mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); } drop: NG_FREE_M(m); /* checks for m != NULL */ } /* ng_btsocket_l2cap_data_input */ /* * L2CAP sockets default message input routine */ static void ng_btsocket_l2cap_default_msg_input(struct ng_mesg *msg, hook_p hook) { switch (msg->header.cmd) { case NGM_L2CAP_NODE_HOOK_INFO: { ng_btsocket_l2cap_rtentry_t *rt = NULL; ng_l2cap_node_hook_info_ep *ep = (ng_l2cap_node_hook_info_ep *)msg->data; if (hook == NULL || msg->header.arglen != sizeof(*ep)) break; if (bcmp(&ep->addr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) break; mtx_lock(&ng_btsocket_l2cap_rt_mtx); rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook); if (rt == NULL) { rt = malloc(sizeof(*rt), M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT|M_ZERO); if (rt == NULL) { mtx_unlock(&ng_btsocket_l2cap_rt_mtx); break; } LIST_INSERT_HEAD(&ng_btsocket_l2cap_rt, rt, next); NG_HOOK_SET_PRIVATE(hook, rt); } bcopy(&ep->addr, &rt->src, sizeof(rt->src)); rt->hook = hook; mtx_unlock(&ng_btsocket_l2cap_rt_mtx); NG_BTSOCKET_L2CAP_INFO( "%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x\n", __func__, NG_HOOK_NAME(hook), rt->src.b[5], rt->src.b[4], rt->src.b[3], rt->src.b[2], rt->src.b[1], rt->src.b[0]); } break; default: NG_BTSOCKET_L2CAP_WARN( "%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd); break; } NG_FREE_MSG(msg); /* Checks for msg != NULL */ } /* ng_btsocket_l2cap_default_msg_input */ /* * L2CAP sockets L2CA message input routine */ static void ng_btsocket_l2cap_l2ca_msg_input(struct ng_mesg *msg, hook_p hook) { ng_btsocket_l2cap_rtentry_p rt = NULL; if (hook == NULL) { NG_BTSOCKET_L2CAP_ALERT( "%s: Invalid source hook for L2CA message\n", __func__); goto drop; } rt = (ng_btsocket_l2cap_rtentry_p) NG_HOOK_PRIVATE(hook); if (rt == NULL) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not find out source bdaddr for L2CA message\n", __func__); goto drop; } switch (msg->header.cmd) { case NGM_L2CAP_L2CA_CON: /* L2CA_Connect response */ ng_btsocket_l2cap_process_l2ca_con_req_rsp(msg, rt); break; case NGM_L2CAP_L2CA_CON_RSP: /* L2CA_ConnectRsp response */ ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(msg, rt); break; case NGM_L2CAP_L2CA_CON_IND: /* L2CA_Connect indicator */ ng_btsocket_l2cap_process_l2ca_con_ind(msg, rt); break; case NGM_L2CAP_L2CA_CFG: /* L2CA_Config response */ ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(msg, rt); break; case NGM_L2CAP_L2CA_CFG_RSP: /* L2CA_ConfigRsp response */ ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(msg, rt); break; case NGM_L2CAP_L2CA_CFG_IND: /* L2CA_Config indicator */ ng_btsocket_l2cap_process_l2ca_cfg_ind(msg, rt); break; case NGM_L2CAP_L2CA_DISCON: /* L2CA_Disconnect response */ ng_btsocket_l2cap_process_l2ca_discon_rsp(msg, rt); break; case NGM_L2CAP_L2CA_DISCON_IND: /* L2CA_Disconnect indicator */ ng_btsocket_l2cap_process_l2ca_discon_ind(msg, rt); break; case NGM_L2CAP_L2CA_WRITE: /* L2CA_Write response */ ng_btsocket_l2cap_process_l2ca_write_rsp(msg, rt); break; + case NGM_L2CAP_L2CA_ENC_CHANGE: + ng_btsocket_l2cap_process_l2ca_enc_change(msg, rt); + break; /* XXX FIXME add other L2CA messages */ default: NG_BTSOCKET_L2CAP_WARN( "%s: Unknown L2CA message, cmd=%d\n", __func__, msg->header.cmd); break; } drop: NG_FREE_MSG(msg); } /* ng_btsocket_l2cap_l2ca_msg_input */ /* * L2CAP sockets input routine */ static void ng_btsocket_l2cap_input(void *context, int pending) { item_p item = NULL; hook_p hook = NULL; for (;;) { mtx_lock(&ng_btsocket_l2cap_queue_mtx); NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_l2cap_queue, item); mtx_unlock(&ng_btsocket_l2cap_queue_mtx); if (item == NULL) break; NGI_GET_HOOK(item, hook); if (hook != NULL && NG_HOOK_NOT_VALID(hook)) goto drop; switch(item->el_flags & NGQF_TYPE) { case NGQF_DATA: { struct mbuf *m = NULL; NGI_GET_M(item, m); ng_btsocket_l2cap_data_input(m, hook); } break; case NGQF_MESG: { struct ng_mesg *msg = NULL; NGI_GET_MSG(item, msg); switch (msg->header.cmd) { case NGM_L2CAP_L2CA_CON: case NGM_L2CAP_L2CA_CON_RSP: case NGM_L2CAP_L2CA_CON_IND: case NGM_L2CAP_L2CA_CFG: case NGM_L2CAP_L2CA_CFG_RSP: case NGM_L2CAP_L2CA_CFG_IND: case NGM_L2CAP_L2CA_DISCON: case NGM_L2CAP_L2CA_DISCON_IND: case NGM_L2CAP_L2CA_WRITE: + case NGM_L2CAP_L2CA_ENC_CHANGE: /* XXX FIXME add other L2CA messages */ ng_btsocket_l2cap_l2ca_msg_input(msg, hook); break; default: ng_btsocket_l2cap_default_msg_input(msg, hook); break; } } break; default: KASSERT(0, ("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE))); break; } drop: if (hook != NULL) NG_HOOK_UNREF(hook); NG_FREE_ITEM(item); } } /* ng_btsocket_l2cap_input */ /* * Route cleanup task. Gets scheduled when hook is disconnected. Here we * will find all sockets that use "invalid" hook and disconnect them. */ static void ng_btsocket_l2cap_rtclean(void *context, int pending) { ng_btsocket_l2cap_pcb_p pcb = NULL, pcb_next = NULL; ng_btsocket_l2cap_rtentry_p rt = NULL; mtx_lock(&ng_btsocket_l2cap_rt_mtx); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); /* * First disconnect all sockets that use "invalid" hook */ for (pcb = LIST_FIRST(&ng_btsocket_l2cap_sockets); pcb != NULL; ) { mtx_lock(&pcb->pcb_mtx); pcb_next = LIST_NEXT(pcb, next); if (pcb->rt != NULL && pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) { if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) ng_btsocket_l2cap_untimeout(pcb); pcb->so->so_error = ENETDOWN; pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); pcb->token = 0; pcb->cid = 0; pcb->rt = NULL; } mtx_unlock(&pcb->pcb_mtx); pcb = pcb_next; } /* * Now cleanup routing table */ for (rt = LIST_FIRST(&ng_btsocket_l2cap_rt); rt != NULL; ) { ng_btsocket_l2cap_rtentry_p rt_next = LIST_NEXT(rt, next); if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) { LIST_REMOVE(rt, next); NG_HOOK_SET_PRIVATE(rt->hook, NULL); NG_HOOK_UNREF(rt->hook); /* Remove extra reference */ bzero(rt, sizeof(*rt)); free(rt, M_NETGRAPH_BTSOCKET_L2CAP); } rt = rt_next; } mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); mtx_unlock(&ng_btsocket_l2cap_rt_mtx); } /* ng_btsocket_l2cap_rtclean */ /* * Initialize everything */ void ng_btsocket_l2cap_init(void) { int error = 0; /* Skip initialization of globals for non-default instances. */ if (!IS_DEFAULT_VNET(curvnet)) return; ng_btsocket_l2cap_node = NULL; ng_btsocket_l2cap_debug_level = NG_BTSOCKET_WARN_LEVEL; /* Register Netgraph node type */ error = ng_newtype(&typestruct); if (error != 0) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not register Netgraph node type, error=%d\n", __func__, error); return; } /* Create Netgrapg node */ error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node); if (error != 0) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not create Netgraph node, error=%d\n", __func__, error); ng_btsocket_l2cap_node = NULL; return; } error = ng_name_node(ng_btsocket_l2cap_node, NG_BTSOCKET_L2CAP_NODE_TYPE); if (error != 0) { NG_BTSOCKET_L2CAP_ALERT( "%s: Could not name Netgraph node, error=%d\n", __func__, error); NG_NODE_UNREF(ng_btsocket_l2cap_node); ng_btsocket_l2cap_node = NULL; return; } /* Create input queue */ NG_BT_ITEMQ_INIT(&ng_btsocket_l2cap_queue, ifqmaxlen); mtx_init(&ng_btsocket_l2cap_queue_mtx, "btsocks_l2cap_queue_mtx", NULL, MTX_DEF); TASK_INIT(&ng_btsocket_l2cap_queue_task, 0, ng_btsocket_l2cap_input, NULL); /* Create list of sockets */ LIST_INIT(&ng_btsocket_l2cap_sockets); mtx_init(&ng_btsocket_l2cap_sockets_mtx, "btsocks_l2cap_sockets_mtx", NULL, MTX_DEF); /* Routing table */ LIST_INIT(&ng_btsocket_l2cap_rt); mtx_init(&ng_btsocket_l2cap_rt_mtx, "btsocks_l2cap_rt_mtx", NULL, MTX_DEF); TASK_INIT(&ng_btsocket_l2cap_rt_task, 0, ng_btsocket_l2cap_rtclean, NULL); } /* ng_btsocket_l2cap_init */ /* * Abort connection on socket */ void ng_btsocket_l2cap_abort(struct socket *so) { so->so_error = ECONNABORTED; (void)ng_btsocket_l2cap_disconnect(so); } /* ng_btsocket_l2cap_abort */ void ng_btsocket_l2cap_close(struct socket *so) { (void)ng_btsocket_l2cap_disconnect(so); } /* ng_btsocket_l2cap_close */ /* * Accept connection on socket. Nothing to do here, socket must be connected * and ready, so just return peer address and be done with it. */ int ng_btsocket_l2cap_accept(struct socket *so, struct sockaddr **nam) { if (ng_btsocket_l2cap_node == NULL) return (EINVAL); return (ng_btsocket_l2cap_peeraddr(so, nam)); } /* ng_btsocket_l2cap_accept */ /* * Create and attach new socket */ int ng_btsocket_l2cap_attach(struct socket *so, int proto, struct thread *td) { static u_int32_t token = 0; ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); int error; /* Check socket and protocol */ if (ng_btsocket_l2cap_node == NULL) return (EPROTONOSUPPORT); if (so->so_type != SOCK_SEQPACKET) return (ESOCKTNOSUPPORT); #if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */ if (proto != 0) if (proto != BLUETOOTH_PROTO_L2CAP) return (EPROTONOSUPPORT); #endif /* XXX */ if (pcb != NULL) return (EISCONN); /* Reserve send and receive space if it is not reserved yet */ if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) { error = soreserve(so, NG_BTSOCKET_L2CAP_SENDSPACE, NG_BTSOCKET_L2CAP_RECVSPACE); if (error != 0) return (error); } /* Allocate the PCB */ pcb = malloc(sizeof(*pcb), M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT | M_ZERO); if (pcb == NULL) return (ENOMEM); /* Link the PCB and the socket */ so->so_pcb = (caddr_t) pcb; pcb->so = so; pcb->state = NG_BTSOCKET_L2CAP_CLOSED; /* Initialize PCB */ pcb->imtu = pcb->omtu = NG_L2CAP_MTU_DEFAULT; /* Default flow */ pcb->iflow.flags = 0x0; pcb->iflow.service_type = NG_HCI_SERVICE_TYPE_BEST_EFFORT; pcb->iflow.token_rate = 0xffffffff; /* maximum */ pcb->iflow.token_bucket_size = 0xffffffff; /* maximum */ pcb->iflow.peak_bandwidth = 0x00000000; /* maximum */ pcb->iflow.latency = 0xffffffff; /* don't care */ pcb->iflow.delay_variation = 0xffffffff; /* don't care */ bcopy(&pcb->iflow, &pcb->oflow, sizeof(pcb->oflow)); pcb->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT; pcb->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT; /* * XXX Mark PCB mutex as DUPOK to prevent "duplicated lock of * the same type" message. When accepting new L2CAP connection * ng_btsocket_l2cap_process_l2ca_con_ind() holds both PCB mutexes * for "old" (accepting) PCB and "new" (created) PCB. */ mtx_init(&pcb->pcb_mtx, "btsocks_l2cap_pcb_mtx", NULL, MTX_DEF|MTX_DUPOK); callout_init_mtx(&pcb->timo, &pcb->pcb_mtx, 0); /* * Add the PCB to the list * * XXX FIXME VERY IMPORTANT! * * This is totally FUBAR. We could get here in two cases: * * 1) When user calls socket() * 2) When we need to accept new incomming connection and call * sonewconn() * * In the first case we must acquire ng_btsocket_l2cap_sockets_mtx. * In the second case we hold ng_btsocket_l2cap_sockets_mtx already. * So we now need to distinguish between these cases. From reading * /sys/kern/uipc_socket.c we can find out that sonewconn() calls * pru_attach with proto == 0 and td == NULL. For now use this fact * to figure out if we were called from socket() or from sonewconn(). */ if (td != NULL) mtx_lock(&ng_btsocket_l2cap_sockets_mtx); else mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED); /* Set PCB token. Use ng_btsocket_l2cap_sockets_mtx for protection */ if (++ token == 0) token ++; pcb->token = token; LIST_INSERT_HEAD(&ng_btsocket_l2cap_sockets, pcb, next); if (td != NULL) mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (0); } /* ng_btsocket_l2cap_attach */ /* * Bind socket */ int ng_btsocket_l2cap_bind(struct socket *so, struct sockaddr *nam, struct thread *td) { ng_btsocket_l2cap_pcb_t *pcb = NULL; struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *) nam; int psm, error = 0; if (ng_btsocket_l2cap_node == NULL) return (EINVAL); /* Verify address */ if (sa == NULL) return (EINVAL); if (sa->l2cap_family != AF_BLUETOOTH) return (EAFNOSUPPORT); /*For the time being, Not support LE binding.*/ if ((sa->l2cap_len != sizeof(*sa))&& (sa->l2cap_len != sizeof(struct sockaddr_l2cap_compat))) return (EINVAL); psm = le16toh(sa->l2cap_psm); /* * Check if other socket has this address already (look for exact * match PSM and bdaddr) and assign socket address if it's available. * * Note: socket can be bound to ANY PSM (zero) thus allowing several * channels with the same PSM between the same pair of BD_ADDR'es. */ mtx_lock(&ng_btsocket_l2cap_sockets_mtx); LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next) if (psm != 0 && psm == pcb->psm && bcmp(&pcb->src, &sa->l2cap_bdaddr, sizeof(bdaddr_t)) == 0) break; if (pcb == NULL) { /* Set socket address */ pcb = so2l2cap_pcb(so); if (pcb != NULL) { bcopy(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src)); pcb->psm = psm; } else error = EINVAL; } else error = EADDRINUSE; mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); return (error); } /* ng_btsocket_l2cap_bind */ /* * Connect socket */ int ng_btsocket_l2cap_connect(struct socket *so, struct sockaddr *nam, struct thread *td) { ng_btsocket_l2cap_pcb_t *pcb = so2l2cap_pcb(so); struct sockaddr_l2cap_compat *sal = (struct sockaddr_l2cap_compat *) nam; struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *)nam; struct sockaddr_l2cap ba; ng_btsocket_l2cap_rtentry_t *rt = NULL; int have_src, error = 0; - + int idtype = NG_L2CAP_L2CA_IDTYPE_BREDR; /* Check socket */ if (pcb == NULL) return (EINVAL); if (ng_btsocket_l2cap_node == NULL) return (EINVAL); if (pcb->state == NG_BTSOCKET_L2CAP_CONNECTING) return (EINPROGRESS); /* Verify address */ if (sa == NULL) return (EINVAL); if (sa->l2cap_family != AF_BLUETOOTH) return (EAFNOSUPPORT); if (sa->l2cap_len == sizeof(*sal)){ bcopy(sal, &ba, sizeof(*sal)); sa = &ba; sa->l2cap_len = sizeof(*sa); sa->l2cap_bdaddr_type = BDADDR_BREDR; } if (sa->l2cap_len != sizeof(*sa)) return (EINVAL); if ((sa->l2cap_psm && sa->l2cap_cid)) return EINVAL; if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) return (EDESTADDRREQ); if((sa->l2cap_bdaddr_type == BDADDR_BREDR)&& (sa->l2cap_psm == 0)) return EDESTADDRREQ; - if((sa->l2cap_bdaddr_type != BDADDR_BREDR)&& - (sa->l2cap_cid != NG_L2CAP_ATT_CID)){ - return EINVAL; + if(sa->l2cap_bdaddr_type != BDADDR_BREDR){ + if(sa->l2cap_cid == NG_L2CAP_ATT_CID){ + idtype = NG_L2CAP_L2CA_IDTYPE_ATT; + }else if (sa->l2cap_cid == NG_L2CAP_SMP_CID){ + idtype =NG_L2CAP_L2CA_IDTYPE_SMP; + }else{ + //if cid == 0 idtype = NG_L2CAP_L2CA_IDTYPE_LE; + // Not supported yet + return EINVAL; + } } if (pcb->psm != 0 && pcb->psm != le16toh(sa->l2cap_psm)) return (EINVAL); /* * Routing. Socket should be bound to some source address. The source * address can be ANY. Destination address must be set and it must not * be ANY. If source address is ANY then find first rtentry that has * src != dst. */ mtx_lock(&ng_btsocket_l2cap_rt_mtx); mtx_lock(&ng_btsocket_l2cap_sockets_mtx); mtx_lock(&pcb->pcb_mtx); /* Send destination address and PSM */ bcopy(&sa->l2cap_bdaddr, &pcb->dst, sizeof(pcb->dst)); pcb->psm = le16toh(sa->l2cap_psm); pcb->dsttype = sa->l2cap_bdaddr_type; - pcb->cid = sa->l2cap_cid; - + pcb->cid = 0; + pcb->idtype = idtype; pcb->rt = NULL; have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)); LIST_FOREACH(rt, &ng_btsocket_l2cap_rt, next) { if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) continue; /* Match src and dst */ if (have_src) { if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0) break; } else { if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0) break; } } if (rt != NULL) { pcb->rt = rt; if (!have_src){ bcopy(&rt->src, &pcb->src, sizeof(pcb->src)); pcb->srctype = (sa->l2cap_bdaddr_type == BDADDR_BREDR)? - BDADDR_BREDR : BDADDR_LE_RANDOM; + BDADDR_BREDR : BDADDR_LE_PUBLIC; } } else error = EHOSTUNREACH; /* * Send L2CA_Connect request */ if (error == 0) { error = ng_btsocket_l2cap_send_l2ca_con_req(pcb); if (error == 0) { pcb->flags |= NG_BTSOCKET_L2CAP_CLIENT; pcb->state = NG_BTSOCKET_L2CAP_CONNECTING; soisconnecting(pcb->so); ng_btsocket_l2cap_timeout(pcb); } } mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); mtx_unlock(&ng_btsocket_l2cap_rt_mtx); return (error); } /* ng_btsocket_l2cap_connect */ /* * Process ioctl's calls on socket */ int ng_btsocket_l2cap_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { return (EINVAL); } /* ng_btsocket_l2cap_control */ /* * Process getsockopt/setsockopt system calls */ int ng_btsocket_l2cap_ctloutput(struct socket *so, struct sockopt *sopt) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); int error = 0; ng_l2cap_cfg_opt_val_t v; if (pcb == NULL) return (EINVAL); if (ng_btsocket_l2cap_node == NULL) return (EINVAL); if (sopt->sopt_level != SOL_L2CAP) return (0); mtx_lock(&pcb->pcb_mtx); switch (sopt->sopt_dir) { case SOPT_GET: switch (sopt->sopt_name) { case SO_L2CAP_IMTU: /* get incoming MTU */ error = sooptcopyout(sopt, &pcb->imtu, sizeof(pcb->imtu)); break; case SO_L2CAP_OMTU: /* get outgoing (peer incoming) MTU */ error = sooptcopyout(sopt, &pcb->omtu, sizeof(pcb->omtu)); break; case SO_L2CAP_IFLOW: /* get incoming flow spec. */ error = sooptcopyout(sopt, &pcb->iflow, sizeof(pcb->iflow)); break; case SO_L2CAP_OFLOW: /* get outgoing flow spec. */ error = sooptcopyout(sopt, &pcb->oflow, sizeof(pcb->oflow)); break; case SO_L2CAP_FLUSH: /* get flush timeout */ error = sooptcopyout(sopt, &pcb->flush_timo, sizeof(pcb->flush_timo)); break; + case SO_L2CAP_ENCRYPTED: /* get encrypt required */ + error = sooptcopyout(sopt, &pcb->need_encrypt, + sizeof(pcb->need_encrypt)); + break; + default: error = ENOPROTOOPT; break; } break; case SOPT_SET: /* * XXX * We do not allow to change these parameters while socket is * connected or we are in the process of creating a connection. * May be this should indicate re-configuration of the open * channel? */ if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) { error = EACCES; break; } switch (sopt->sopt_name) { case SO_L2CAP_IMTU: /* set incoming MTU */ error = sooptcopyin(sopt, &v, sizeof(v), sizeof(v.mtu)); if (error == 0) pcb->imtu = v.mtu; break; case SO_L2CAP_OFLOW: /* set outgoing flow spec. */ error = sooptcopyin(sopt, &v, sizeof(v),sizeof(v.flow)); if (error == 0) bcopy(&v.flow, &pcb->oflow, sizeof(pcb->oflow)); break; case SO_L2CAP_FLUSH: /* set flush timeout */ error = sooptcopyin(sopt, &v, sizeof(v), sizeof(v.flush_timo)); if (error == 0) pcb->flush_timo = v.flush_timo; break; - + case SO_L2CAP_ENCRYPTED: /*set connect encryption opt*/ + if((pcb->state != NG_BTSOCKET_L2CAP_OPEN) && + (pcb->state != NG_BTSOCKET_L2CAP_W4_ENC_CHANGE)){ + error = sooptcopyin(sopt, &v, sizeof(v), + sizeof(v.encryption)); + if(error == 0) + pcb->need_encrypt = (v.encryption)?1:0; + }else{ + error = EINVAL; + } + break; default: error = ENOPROTOOPT; break; } break; default: error = EINVAL; break; } mtx_unlock(&pcb->pcb_mtx); return (error); } /* ng_btsocket_l2cap_ctloutput */ /* * Detach and destroy socket */ void ng_btsocket_l2cap_detach(struct socket *so) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); KASSERT(pcb != NULL, ("ng_btsocket_l2cap_detach: pcb == NULL")); if (ng_btsocket_l2cap_node == NULL) return; mtx_lock(&ng_btsocket_l2cap_sockets_mtx); mtx_lock(&pcb->pcb_mtx); /* XXX what to do with pending request? */ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) ng_btsocket_l2cap_untimeout(pcb); if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED && pcb->state != NG_BTSOCKET_L2CAP_DISCONNECTING) /* Send disconnect request with "zero" token */ ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb); pcb->state = NG_BTSOCKET_L2CAP_CLOSED; LIST_REMOVE(pcb, next); mtx_unlock(&pcb->pcb_mtx); mtx_unlock(&ng_btsocket_l2cap_sockets_mtx); mtx_destroy(&pcb->pcb_mtx); bzero(pcb, sizeof(*pcb)); free(pcb, M_NETGRAPH_BTSOCKET_L2CAP); soisdisconnected(so); so->so_pcb = NULL; } /* ng_btsocket_l2cap_detach */ /* * Disconnect socket */ int ng_btsocket_l2cap_disconnect(struct socket *so) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); int error = 0; if (pcb == NULL) return (EINVAL); if (ng_btsocket_l2cap_node == NULL) return (EINVAL); mtx_lock(&pcb->pcb_mtx); if (pcb->state == NG_BTSOCKET_L2CAP_DISCONNECTING) { mtx_unlock(&pcb->pcb_mtx); return (EINPROGRESS); } if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) { /* XXX FIXME what to do with pending request? */ if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) ng_btsocket_l2cap_untimeout(pcb); error = ng_btsocket_l2cap_send_l2ca_discon_req(pcb->token, pcb); if (error == 0) { pcb->state = NG_BTSOCKET_L2CAP_DISCONNECTING; soisdisconnecting(so); ng_btsocket_l2cap_timeout(pcb); } /* XXX FIXME what to do if error != 0 */ } mtx_unlock(&pcb->pcb_mtx); return (error); } /* ng_btsocket_l2cap_disconnect */ /* * Listen on socket */ int ng_btsocket_l2cap_listen(struct socket *so, int backlog, struct thread *td) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); int error; SOCK_LOCK(so); error = solisten_proto_check(so); if (error != 0) goto out; if (pcb == NULL) { error = EINVAL; goto out; } if (ng_btsocket_l2cap_node == NULL) { error = EINVAL; goto out; } if (pcb->psm == 0) { error = EADDRNOTAVAIL; goto out; } solisten_proto(so, backlog); out: SOCK_UNLOCK(so); return (error); } /* ng_btsocket_listen */ /* * Get peer address */ int ng_btsocket_l2cap_peeraddr(struct socket *so, struct sockaddr **nam) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); struct sockaddr_l2cap sa; if (pcb == NULL) return (EINVAL); if (ng_btsocket_l2cap_node == NULL) return (EINVAL); bcopy(&pcb->dst, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr)); sa.l2cap_psm = htole16(pcb->psm); sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BLUETOOTH; - sa.l2cap_cid = 0; + switch(pcb->idtype){ + case NG_L2CAP_L2CA_IDTYPE_ATT: + sa.l2cap_cid = NG_L2CAP_ATT_CID; + break; + case NG_L2CAP_L2CA_IDTYPE_SMP: + sa.l2cap_cid = NG_L2CAP_SMP_CID; + break; + default: + sa.l2cap_cid = 0; + break; + } sa.l2cap_bdaddr_type = pcb->dsttype; *nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT); return ((*nam == NULL)? ENOMEM : 0); } /* ng_btsocket_l2cap_peeraddr */ /* * Send data to socket */ int ng_btsocket_l2cap_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct thread *td) { ng_btsocket_l2cap_pcb_t *pcb = so2l2cap_pcb(so); int error = 0; if (ng_btsocket_l2cap_node == NULL) { error = ENETDOWN; goto drop; } /* Check socket and input */ if (pcb == NULL || m == NULL || control != NULL) { error = EINVAL; goto drop; } mtx_lock(&pcb->pcb_mtx); /* Make sure socket is connected */ if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) { mtx_unlock(&pcb->pcb_mtx); error = ENOTCONN; goto drop; } /* Check route */ if (pcb->rt == NULL || pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) { mtx_unlock(&pcb->pcb_mtx); error = ENETDOWN; goto drop; } /* Check packet size agains outgoing (peer's incoming) MTU) */ if (m->m_pkthdr.len > pcb->omtu) { NG_BTSOCKET_L2CAP_ERR( "%s: Packet too big, len=%d, omtu=%d\n", __func__, m->m_pkthdr.len, pcb->omtu); mtx_unlock(&pcb->pcb_mtx); error = EMSGSIZE; goto drop; } /* * First put packet on socket send queue. Then check if we have * pending timeout. If we do not have timeout then we must send * packet and schedule timeout. Otherwise do nothing and wait for * L2CA_WRITE_RSP. */ sbappendrecord(&pcb->so->so_snd, m); m = NULL; if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) { error = ng_btsocket_l2cap_send2(pcb); if (error == 0) ng_btsocket_l2cap_timeout(pcb); else sbdroprecord(&pcb->so->so_snd); /* XXX */ } mtx_unlock(&pcb->pcb_mtx); drop: NG_FREE_M(m); /* checks for != NULL */ NG_FREE_M(control); return (error); } /* ng_btsocket_l2cap_send */ /* * Send first packet in the socket queue to the L2CAP layer */ static int ng_btsocket_l2cap_send2(ng_btsocket_l2cap_pcb_p pcb) { struct mbuf *m = NULL; ng_l2cap_l2ca_hdr_t *hdr = NULL; int error = 0; mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (sbavail(&pcb->so->so_snd) == 0) return (EINVAL); /* XXX */ m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT); if (m == NULL) return (ENOBUFS); /* Create L2CA packet header */ M_PREPEND(m, sizeof(*hdr), M_NOWAIT); if (m != NULL) if (m->m_len < sizeof(*hdr)) m = m_pullup(m, sizeof(*hdr)); if (m == NULL) { NG_BTSOCKET_L2CAP_ERR( "%s: Failed to create L2CA packet header\n", __func__); return (ENOBUFS); } hdr = mtod(m, ng_l2cap_l2ca_hdr_t *); hdr->token = pcb->token; hdr->length = m->m_pkthdr.len - sizeof(*hdr); hdr->lcid = pcb->cid; - hdr->idtype = ng_btsock_l2cap_pcb_to_idtype(pcb); + hdr->idtype = pcb->idtype; NG_BTSOCKET_L2CAP_INFO( "%s: Sending packet: len=%d, length=%d, lcid=%d, token=%d, state=%d\n", __func__, m->m_pkthdr.len, hdr->length, hdr->lcid, hdr->token, pcb->state); /* * If we got here than we have successfuly creates new L2CAP * data packet and now we can send it to the L2CAP layer */ NG_SEND_DATA_ONLY(error, pcb->rt->hook, m); return (error); } /* ng_btsocket_l2cap_send2 */ /* * Get socket address */ int ng_btsocket_l2cap_sockaddr(struct socket *so, struct sockaddr **nam) { ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so); struct sockaddr_l2cap sa; if (pcb == NULL) return (EINVAL); if (ng_btsocket_l2cap_node == NULL) return (EINVAL); bcopy(&pcb->src, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr)); sa.l2cap_psm = htole16(pcb->psm); sa.l2cap_len = sizeof(sa); sa.l2cap_family = AF_BLUETOOTH; sa.l2cap_cid = 0; sa.l2cap_bdaddr_type = pcb->srctype; *nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT); return ((*nam == NULL)? ENOMEM : 0); } /* ng_btsocket_l2cap_sockaddr */ /***************************************************************************** ***************************************************************************** ** Misc. functions ***************************************************************************** *****************************************************************************/ /* * Look for the socket that listens on given PSM and bdaddr. Returns exact or * close match (if any). Caller must hold ng_btsocket_l2cap_sockets_mtx. */ static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_addr(bdaddr_p bdaddr, int psm) { ng_btsocket_l2cap_pcb_p p = NULL, p1 = NULL; mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED); LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next) { if (p->so == NULL || !(p->so->so_options & SO_ACCEPTCONN) || p->psm != psm) continue; if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0) break; if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0) p1 = p; } return ((p != NULL)? p : p1); } /* ng_btsocket_l2cap_pcb_by_addr */ /* * Look for the socket that has given token. * Caller must hold ng_btsocket_l2cap_sockets_mtx. */ static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_token(u_int32_t token) { ng_btsocket_l2cap_pcb_p p = NULL; if (token == 0) return (NULL); mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED); LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next) if (p->token == token) break; return (p); } /* ng_btsocket_l2cap_pcb_by_token */ /* * Look for the socket that assigned to given source address and channel ID. * Caller must hold ng_btsocket_l2cap_sockets_mtx */ static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_cid(bdaddr_p src, int cid, int idtype) { ng_btsocket_l2cap_pcb_p p = NULL; mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED); LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next){ if (p->cid == cid && bcmp(src, &p->src, sizeof(p->src)) == 0&& - ng_btsock_l2cap_pcb_to_idtype(p) == idtype) + p->idtype == idtype) break; } return (p); } /* ng_btsocket_l2cap_pcb_by_cid */ /* * Set timeout on socket */ static void ng_btsocket_l2cap_timeout(ng_btsocket_l2cap_pcb_p pcb) { mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) { pcb->flags |= NG_BTSOCKET_L2CAP_TIMO; callout_reset(&pcb->timo, bluetooth_l2cap_ertx_timeout(), ng_btsocket_l2cap_process_timeout, pcb); } else KASSERT(0, ("%s: Duplicated socket timeout?!\n", __func__)); } /* ng_btsocket_l2cap_timeout */ /* * Unset timeout on socket */ static void ng_btsocket_l2cap_untimeout(ng_btsocket_l2cap_pcb_p pcb) { mtx_assert(&pcb->pcb_mtx, MA_OWNED); if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) { callout_stop(&pcb->timo); pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO; } else KASSERT(0, ("%s: No socket timeout?!\n", __func__)); } /* ng_btsocket_l2cap_untimeout */ /* * Process timeout on socket */ static void ng_btsocket_l2cap_process_timeout(void *xpcb) { ng_btsocket_l2cap_pcb_p pcb = (ng_btsocket_l2cap_pcb_p) xpcb; mtx_assert(&pcb->pcb_mtx, MA_OWNED); pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO; pcb->so->so_error = ETIMEDOUT; switch (pcb->state) { case NG_BTSOCKET_L2CAP_CONNECTING: case NG_BTSOCKET_L2CAP_CONFIGURING: /* Send disconnect request with "zero" token */ if (pcb->cid != 0) ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb); /* ... and close the socket */ pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); break; case NG_BTSOCKET_L2CAP_OPEN: /* Send timeout - drop packet and wakeup sender */ sbdroprecord(&pcb->so->so_snd); sowwakeup(pcb->so); break; case NG_BTSOCKET_L2CAP_DISCONNECTING: /* Disconnect timeout - disconnect the socket anyway */ pcb->state = NG_BTSOCKET_L2CAP_CLOSED; soisdisconnected(pcb->so); break; default: NG_BTSOCKET_L2CAP_ERR( "%s: Invalid socket state=%d\n", __func__, pcb->state); break; } } /* ng_btsocket_l2cap_process_timeout */ /* * Translate HCI/L2CAP error code into "errno" code * XXX Note: Some L2CAP and HCI error codes have the same value, but * different meaning */ static int ng_btsocket_l2cap_result2errno(int result) { switch (result) { case 0x00: /* No error */ return (0); case 0x01: /* Unknown HCI command */ return (ENODEV); case 0x02: /* No connection */ return (ENOTCONN); case 0x03: /* Hardware failure */ return (EIO); case 0x04: /* Page timeout */ return (EHOSTDOWN); case 0x05: /* Authentication failure */ case 0x06: /* Key missing */ case 0x18: /* Pairing not allowed */ case 0x21: /* Role change not allowed */ case 0x24: /* LMP PSU not allowed */ case 0x25: /* Encryption mode not acceptable */ case 0x26: /* Unit key used */ return (EACCES); case 0x07: /* Memory full */ return (ENOMEM); case 0x08: /* Connection timeout */ case 0x10: /* Host timeout */ case 0x22: /* LMP response timeout */ case 0xee: /* HCI timeout */ case 0xeeee: /* L2CAP timeout */ return (ETIMEDOUT); case 0x09: /* Max number of connections */ case 0x0a: /* Max number of SCO connections to a unit */ return (EMLINK); case 0x0b: /* ACL connection already exists */ return (EEXIST); case 0x0c: /* Command disallowed */ return (EBUSY); case 0x0d: /* Host rejected due to limited resources */ case 0x0e: /* Host rejected due to securiity reasons */ case 0x0f: /* Host rejected due to remote unit is a personal unit */ case 0x1b: /* SCO offset rejected */ case 0x1c: /* SCO interval rejected */ case 0x1d: /* SCO air mode rejected */ return (ECONNREFUSED); case 0x11: /* Unsupported feature or parameter value */ case 0x19: /* Unknown LMP PDU */ case 0x1a: /* Unsupported remote feature */ case 0x20: /* Unsupported LMP parameter value */ case 0x27: /* QoS is not supported */ case 0x29: /* Paring with unit key not supported */ return (EOPNOTSUPP); case 0x12: /* Invalid HCI command parameter */ case 0x1e: /* Invalid LMP parameters */ return (EINVAL); case 0x13: /* Other end terminated connection: User ended connection */ case 0x14: /* Other end terminated connection: Low resources */ case 0x15: /* Other end terminated connection: About to power off */ return (ECONNRESET); case 0x16: /* Connection terminated by local host */ return (ECONNABORTED); #if 0 /* XXX not yet */ case 0x17: /* Repeated attempts */ case 0x1f: /* Unspecified error */ case 0x23: /* LMP error transaction collision */ case 0x28: /* Instant passed */ #endif } return (ENOSYS); } /* ng_btsocket_l2cap_result2errno */