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); 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 */
int ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr) { 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); 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); 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 = NG_HCI_LINK_ACL; con->state = NG_L2CAP_W4_LP_CON_CFM; ng_l2cap_lp_timeout(con); NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, NULL); if (error != 0) ng_l2cap_free_con(con); /* will remove timeout */ return (error); } /* ng_l2cap_lp_con_req */
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 */
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 */ con->state = NG_L2CAP_CON_CLOSED; ng_l2cap_con_fail(con, ep->reason); out: return (error); } /* ng_l2cap_lp_discon_ind */
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_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 */
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); if (con == NULL) { /* Submit LP_ConnectReq to the lower layer */ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr); 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); 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); 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 */ _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 */
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); 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 */
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); 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 = 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 */
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 */ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid); 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 = 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_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 */
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); if (con == NULL) { /* Submit LP_ConnectReq to the lower layer */ error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr); 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); 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 */
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); /* Check if we have this channel */ ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid); 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 */
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_DONTWAIT); 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 */
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); 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); 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 */