Path: blob/main/sys/netgraph/bluetooth/l2cap/ng_l2cap_main.c
34814 views
/*1* ng_l2cap_main.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: ng_l2cap_main.c,v 1.2 2003/04/28 21:44:59 max Exp $32*/3334#include <sys/param.h>35#include <sys/systm.h>36#include <sys/kernel.h>37#include <sys/malloc.h>38#include <sys/mbuf.h>39#include <sys/queue.h>40#include <netgraph/ng_message.h>41#include <netgraph/netgraph.h>42#include <netgraph/ng_parse.h>43#include <netgraph/bluetooth/include/ng_bluetooth.h>44#include <netgraph/bluetooth/include/ng_hci.h>45#include <netgraph/bluetooth/include/ng_l2cap.h>46#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>47#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>48#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>49#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>50#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>51#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>52#include <netgraph/bluetooth/l2cap/ng_l2cap_prse.h>5354/******************************************************************************55******************************************************************************56** This node implements Link Layer Control and Adaptation Protocol (L2CAP)57******************************************************************************58******************************************************************************/5960/* MALLOC define */61#ifdef NG_SEPARATE_MALLOC62MALLOC_DEFINE(M_NETGRAPH_L2CAP, "netgraph_l2cap",63"Netgraph Bluetooth L2CAP node");64#else65#define M_NETGRAPH_L2CAP M_NETGRAPH66#endif /* NG_SEPARATE_MALLOC */6768/* Netgraph node methods */69static ng_constructor_t ng_l2cap_constructor;70static ng_shutdown_t ng_l2cap_shutdown;71static ng_newhook_t ng_l2cap_newhook;72static ng_connect_t ng_l2cap_connect;73static ng_disconnect_t ng_l2cap_disconnect;74static ng_rcvmsg_t ng_l2cap_lower_rcvmsg;75static ng_rcvmsg_t ng_l2cap_upper_rcvmsg;76static ng_rcvmsg_t ng_l2cap_default_rcvmsg;77static ng_rcvdata_t ng_l2cap_rcvdata;7879/* Netgraph node type descriptor */80static struct ng_type typestruct = {81.version = NG_ABI_VERSION,82.name = NG_L2CAP_NODE_TYPE,83.constructor = ng_l2cap_constructor,84.rcvmsg = ng_l2cap_default_rcvmsg,85.shutdown = ng_l2cap_shutdown,86.newhook = ng_l2cap_newhook,87.connect = ng_l2cap_connect,88.rcvdata = ng_l2cap_rcvdata,89.disconnect = ng_l2cap_disconnect,90.cmdlist = ng_l2cap_cmdlist,91};92NETGRAPH_INIT(l2cap, &typestruct);93MODULE_VERSION(ng_l2cap, NG_BLUETOOTH_VERSION);94MODULE_DEPEND(ng_l2cap, ng_bluetooth, NG_BLUETOOTH_VERSION,95NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);9697/*****************************************************************************98*****************************************************************************99** Netgraph methods implementation100*****************************************************************************101*****************************************************************************/102103static void ng_l2cap_cleanup (ng_l2cap_p);104static void ng_l2cap_destroy_channels (ng_l2cap_p);105106/*107* Create new instance of L2CAP node108*/109110static int111ng_l2cap_constructor(node_p node)112{113ng_l2cap_p l2cap = NULL;114115/* Create new L2CAP node */116l2cap = malloc(sizeof(*l2cap), M_NETGRAPH_L2CAP, M_WAITOK | M_ZERO);117118l2cap->node = node;119l2cap->debug = NG_L2CAP_WARN_LEVEL;120l2cap->discon_timo = 5; /* sec */121122LIST_INIT(&l2cap->con_list);123LIST_INIT(&l2cap->chan_list);124125NG_NODE_SET_PRIVATE(node, l2cap);126NG_NODE_FORCE_WRITER(node);127128return (0);129} /* ng_l2cap_constructor */130131/*132* Shutdown L2CAP node133*/134135static int136ng_l2cap_shutdown(node_p node)137{138ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);139140NG_NODE_SET_PRIVATE(node, NULL);141NG_NODE_UNREF(node);142143/* Clean up L2CAP node. Delete all connection, channels and commands */144l2cap->node = NULL;145ng_l2cap_cleanup(l2cap);146147bzero(l2cap, sizeof(*l2cap));148free(l2cap, M_NETGRAPH_L2CAP);149150return (0);151} /* ng_l2cap_shutdown */152153/*154* Give our OK for a hook to be added. HCI layer is connected to the HCI155* (NG_L2CAP_HOOK_HCI) hook. As per specification L2CAP layer MUST provide156* Procol/Service Multiplexing, so the L2CAP node provides separate hooks157* for SDP (NG_L2CAP_HOOK_SDP), RFCOMM (NG_L2CAP_HOOK_RFCOMM) and TCP158* (NG_L2CAP_HOOK_TCP) protcols. Unknown PSM will be forwarded to159* NG_L2CAP_HOOK_ORPHAN hook. Control node/application is connected to160* control (NG_L2CAP_HOOK_CTL) hook.161*/162163static int164ng_l2cap_newhook(node_p node, hook_p hook, char const *name)165{166ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);167hook_p *h = NULL;168169if (strcmp(name, NG_L2CAP_HOOK_HCI) == 0)170h = &l2cap->hci;171else if (strcmp(name, NG_L2CAP_HOOK_L2C) == 0)172h = &l2cap->l2c;173else if (strcmp(name, NG_L2CAP_HOOK_CTL) == 0)174h = &l2cap->ctl;175else176return (EINVAL);177178if (*h != NULL)179return (EISCONN);180181*h = hook;182183return (0);184} /* ng_l2cap_newhook */185186/*187* Give our final OK to connect hook. Nothing to do here.188*/189190static int191ng_l2cap_connect(hook_p hook)192{193ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));194int error = 0;195196if (hook == l2cap->hci)197NG_HOOK_SET_RCVMSG(hook, ng_l2cap_lower_rcvmsg);198else199if (hook == l2cap->l2c || hook == l2cap->ctl) {200NG_HOOK_SET_RCVMSG(hook, ng_l2cap_upper_rcvmsg);201202/* Send delayed notification to the upper layer */203error = ng_send_fn(l2cap->node, hook, ng_l2cap_send_hook_info,204NULL, 0);205} else206error = EINVAL;207208return (error);209} /* ng_l2cap_connect */210211/*212* Disconnect the hook. For downstream hook we must notify upper layers.213*214* XXX For upstream hooks this is really ugly :( Hook was disconnected and it215* XXX is now too late to do anything. For now we just clean up our own mess216* XXX and remove all channels that use disconnected upstream hook. If we don't217* XXX do that then L2CAP node can get out of sync with upper layers.218* XXX No notification will be sent to remote peer.219*/220221static int222ng_l2cap_disconnect(hook_p hook)223{224ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));225hook_p *h = NULL;226227if (hook == l2cap->hci) {228ng_l2cap_cleanup(l2cap);229h = &l2cap->hci;230} else231if (hook == l2cap->l2c) {232ng_l2cap_destroy_channels(l2cap);233h = &l2cap->l2c;234} else235if (hook == l2cap->ctl)236h = &l2cap->ctl;237else238return (EINVAL);239240*h = NULL;241242/* Shutdown when all hooks are disconnected */243if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&244NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))245ng_rmnode_self(NG_HOOK_NODE(hook));246247return (0);248} /* ng_l2cap_disconnect */249250/*251* Process control message from lower layer252*/253254static int255ng_l2cap_lower_rcvmsg(node_p node, item_p item, hook_p lasthook)256{257ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);258struct ng_mesg *msg = NGI_MSG(item); /* item still has message */259int error = 0;260261switch (msg->header.typecookie) {262case NGM_HCI_COOKIE:263switch (msg->header.cmd) {264/* HCI node is ready */265case NGM_HCI_NODE_UP: {266ng_hci_node_up_ep *ep = NULL;267268if (msg->header.arglen != sizeof(*ep))269error = EMSGSIZE;270else {271ep = (ng_hci_node_up_ep *)(msg->data);272273NG_L2CAP_INFO(274"%s: %s - HCI node is up, bdaddr: %x:%x:%x:%x:%x:%x, " \275"pkt_size=%d bytes, num_pkts=%d\n", __func__, NG_NODE_NAME(l2cap->node),276ep->bdaddr.b[5], ep->bdaddr.b[4],277ep->bdaddr.b[3], ep->bdaddr.b[2],278ep->bdaddr.b[1], ep->bdaddr.b[0],279ep->pkt_size, ep->num_pkts);280281bcopy(&ep->bdaddr, &l2cap->bdaddr,282sizeof(l2cap->bdaddr));283l2cap->pkt_size = ep->pkt_size;284l2cap->num_pkts = ep->num_pkts;285286/* Notify upper layers */287ng_l2cap_send_hook_info(l2cap->node,288l2cap->l2c, NULL, 0);289ng_l2cap_send_hook_info(l2cap->node,290l2cap->ctl, NULL, 0);291}292} break;293294case NGM_HCI_SYNC_CON_QUEUE: {295ng_hci_sync_con_queue_ep *ep = NULL;296ng_l2cap_con_p con = NULL;297298if (msg->header.arglen != sizeof(*ep))299error = EMSGSIZE;300else {301ep = (ng_hci_sync_con_queue_ep *)(msg->data);302con = ng_l2cap_con_by_handle(l2cap,303ep->con_handle);304if (con == NULL)305break;306307NG_L2CAP_INFO(308"%s: %s - sync HCI connection queue, con_handle=%d, pending=%d, completed=%d\n",309__func__, NG_NODE_NAME(l2cap->node),310ep->con_handle, con->pending,311ep->completed);312313con->pending -= ep->completed;314if (con->pending < 0) {315NG_L2CAP_WARN(316"%s: %s - pending packet counter is out of sync! " \317"con_handle=%d, pending=%d, completed=%d\n", __func__,318NG_NODE_NAME(l2cap->node),319con->con_handle, con->pending,320ep->completed);321322con->pending = 0;323}324325ng_l2cap_lp_deliver(con);326}327} break;328329/* LP_ConnectCfm[Neg] */330case NGM_HCI_LP_CON_CFM:331error = ng_l2cap_lp_con_cfm(l2cap, msg);332break;333334/* LP_ConnectInd */335case NGM_HCI_LP_CON_IND:336error = ng_l2cap_lp_con_ind(l2cap, msg);337break;338339/* LP_DisconnectInd */340case NGM_HCI_LP_DISCON_IND:341error = ng_l2cap_lp_discon_ind(l2cap, msg);342break;343344/* LP_QoSSetupCfm[Neg] */345case NGM_HCI_LP_QOS_CFM:346error = ng_l2cap_lp_qos_cfm(l2cap, msg);347break;348349/* LP_OoSViolationInd */350case NGM_HCI_LP_QOS_IND:351error = ng_l2cap_lp_qos_ind(l2cap, msg);352break;353case NGM_HCI_LP_ENC_CHG:354error = ng_l2cap_lp_enc_change(l2cap, msg);355break;356default:357error = EINVAL;358break;359}360break;361362default:363return (ng_l2cap_default_rcvmsg(node, item, lasthook));364/* NOT REACHED */365}366367NG_FREE_ITEM(item);368369return (error);370} /* ng_l2cap_lower_rcvmsg */371372/*373* Process control message from upper layer374*/375376static int377ng_l2cap_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)378{379ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);380struct ng_mesg *msg = NGI_MSG(item); /* item still has message */381int error = 0;382383switch (msg->header.typecookie) {384case NGM_L2CAP_COOKIE:385switch (msg->header.cmd) {386/* L2CA_Connect */387case NGM_L2CAP_L2CA_CON:388error = ng_l2cap_l2ca_con_req(l2cap, msg);389break;390391/* L2CA_ConnectRsp */392case NGM_L2CAP_L2CA_CON_RSP:393error = ng_l2cap_l2ca_con_rsp_req(l2cap, msg);394break;395396/* L2CA_Config */397case NGM_L2CAP_L2CA_CFG:398error = ng_l2cap_l2ca_cfg_req(l2cap, msg);399break;400401/* L2CA_ConfigRsp */402case NGM_L2CAP_L2CA_CFG_RSP:403error = ng_l2cap_l2ca_cfg_rsp_req(l2cap, msg);404break;405406/* L2CA_Disconnect */407case NGM_L2CAP_L2CA_DISCON:408error = ng_l2cap_l2ca_discon_req(l2cap, msg);409break;410411/* L2CA_GroupCreate */412case NGM_L2CAP_L2CA_GRP_CREATE:413error = ng_l2cap_l2ca_grp_create(l2cap, msg);414break;415416/* L2CA_GroupClose */417case NGM_L2CAP_L2CA_GRP_CLOSE:418error = ng_l2cap_l2ca_grp_close(l2cap, msg);419break;420421/* L2CA_GroupAddMember */422case NGM_L2CAP_L2CA_GRP_ADD_MEMBER:423error = ng_l2cap_l2ca_grp_add_member_req(l2cap, msg);424break;425426/* L2CA_GroupDeleteMember */427case NGM_L2CAP_L2CA_GRP_REM_MEMBER:428error = ng_l2cap_l2ca_grp_rem_member(l2cap, msg);429break;430431/* L2CA_GroupMembership */432case NGM_L2CAP_L2CA_GRP_MEMBERSHIP:433error = ng_l2cap_l2ca_grp_get_members(l2cap, msg);434break;435436/* L2CA_Ping */437case NGM_L2CAP_L2CA_PING:438error = ng_l2cap_l2ca_ping_req(l2cap, msg);439break;440441/* L2CA_GetInfo */442case NGM_L2CAP_L2CA_GET_INFO:443error = ng_l2cap_l2ca_get_info_req(l2cap, msg);444break;445446/* L2CA_EnableCLT */447case NGM_L2CAP_L2CA_ENABLE_CLT:448error = ng_l2cap_l2ca_enable_clt(l2cap, msg);449break;450451default:452return (ng_l2cap_default_rcvmsg(node, item, lasthook));453/* NOT REACHED */454}455break;456457default:458return (ng_l2cap_default_rcvmsg(node, item, lasthook));459/* NOT REACHED */460}461462NG_FREE_ITEM(item);463464return (error);465} /* ng_l2cap_upper_rcvmsg */466467/*468* Default control message processing routine469*/470471static int472ng_l2cap_default_rcvmsg(node_p node, item_p item, hook_p lasthook)473{474ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);475struct ng_mesg *msg = NULL, *rsp = NULL;476int error = 0;477478/* Detach and process message */479NGI_GET_MSG(item, msg);480481switch (msg->header.typecookie) {482case NGM_GENERIC_COOKIE:483switch (msg->header.cmd) {484case NGM_TEXT_STATUS:485NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);486if (rsp == NULL)487error = ENOMEM;488else489snprintf(rsp->data, NG_TEXTRESPONSE,490"bdaddr %x:%x:%x:%x:%x:%x, " \491"pkt_size %d\n" \492"Hooks %s %s %s\n" \493"Flags %#x\n",494l2cap->bdaddr.b[5], l2cap->bdaddr.b[4],495l2cap->bdaddr.b[3], l2cap->bdaddr.b[2],496l2cap->bdaddr.b[1], l2cap->bdaddr.b[0],497l2cap->pkt_size,498(l2cap->hci != NULL)?499NG_L2CAP_HOOK_HCI : "",500(l2cap->l2c != NULL)?501NG_L2CAP_HOOK_L2C : "",502(l2cap->ctl != NULL)?503NG_L2CAP_HOOK_CTL : "",504l2cap->flags);505break;506507default:508error = EINVAL;509break;510}511break;512513/* Messages from the upper layer or directed to the local node */514case NGM_L2CAP_COOKIE:515switch (msg->header.cmd) {516/* Get node flags */517case NGM_L2CAP_NODE_GET_FLAGS:518NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_flags_ep),519M_NOWAIT);520if (rsp == NULL)521error = ENOMEM;522else523*((ng_l2cap_node_flags_ep *)(rsp->data)) =524l2cap->flags;525break;526527/* Get node debug */528case NGM_L2CAP_NODE_GET_DEBUG:529NG_MKRESPONSE(rsp, msg, sizeof(ng_l2cap_node_debug_ep),530M_NOWAIT);531if (rsp == NULL)532error = ENOMEM;533else534*((ng_l2cap_node_debug_ep *)(rsp->data)) =535l2cap->debug;536break;537538/* Set node debug */539case NGM_L2CAP_NODE_SET_DEBUG:540if (msg->header.arglen !=541sizeof(ng_l2cap_node_debug_ep))542error = EMSGSIZE;543else544l2cap->debug =545*((ng_l2cap_node_debug_ep *)(msg->data));546break;547548/* Get connection list */549case NGM_L2CAP_NODE_GET_CON_LIST: {550ng_l2cap_con_p con = NULL;551ng_l2cap_node_con_list_ep *e1 = NULL;552ng_l2cap_node_con_ep *e2 = NULL;553int n = 0;554555/* Count number of connections */556LIST_FOREACH(con, &l2cap->con_list, next)557n++;558if (n > NG_L2CAP_MAX_CON_NUM)559n = NG_L2CAP_MAX_CON_NUM;560561/* Prepare response */562NG_MKRESPONSE(rsp, msg,563sizeof(*e1) + n * sizeof(*e2), M_NOWAIT);564if (rsp == NULL) {565error = ENOMEM;566break;567}568569e1 = (ng_l2cap_node_con_list_ep *)(rsp->data);570e2 = (ng_l2cap_node_con_ep *)(e1 + 1);571572e1->num_connections = n;573574LIST_FOREACH(con, &l2cap->con_list, next) {575e2->state = con->state;576577e2->flags = con->flags;578if (con->tx_pkt != NULL)579e2->flags |= NG_L2CAP_CON_TX;580if (con->rx_pkt != NULL)581e2->flags |= NG_L2CAP_CON_RX;582583e2->pending = con->pending;584585e2->con_handle = con->con_handle;586bcopy(&con->remote, &e2->remote,587sizeof(e2->remote));588589e2 ++;590if (--n <= 0)591break;592}593} break;594595/* Get channel list */596case NGM_L2CAP_NODE_GET_CHAN_LIST: {597ng_l2cap_chan_p ch = NULL;598ng_l2cap_node_chan_list_ep *e1 = NULL;599ng_l2cap_node_chan_ep *e2 = NULL;600int n = 0;601602/* Count number of channels */603LIST_FOREACH(ch, &l2cap->chan_list, next)604n ++;605if (n > NG_L2CAP_MAX_CHAN_NUM)606n = NG_L2CAP_MAX_CHAN_NUM;607608/* Prepare response */609NG_MKRESPONSE(rsp, msg,610sizeof(ng_l2cap_node_chan_list_ep) +611n * sizeof(ng_l2cap_node_chan_ep), M_NOWAIT);612if (rsp == NULL) {613error = ENOMEM;614break;615}616617e1 = (ng_l2cap_node_chan_list_ep *)(rsp->data);618e2 = (ng_l2cap_node_chan_ep *)(e1 + 1);619620e1->num_channels = n;621622LIST_FOREACH(ch, &l2cap->chan_list, next) {623e2->state = ch->state;624625e2->scid = ch->scid;626e2->dcid = ch->dcid;627628e2->imtu = ch->imtu;629e2->omtu = ch->omtu;630631e2->psm = ch->psm;632bcopy(&ch->con->remote, &e2->remote,633sizeof(e2->remote));634635e2 ++;636if (--n <= 0)637break;638}639} break;640641case NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO:642NG_MKRESPONSE(rsp, msg,643sizeof(ng_l2cap_node_auto_discon_ep), M_NOWAIT);644if (rsp == NULL)645error = ENOMEM;646else647*((ng_l2cap_node_auto_discon_ep *)(rsp->data)) =648l2cap->discon_timo;649break;650651case NGM_L2CAP_NODE_SET_AUTO_DISCON_TIMO:652if (msg->header.arglen !=653sizeof(ng_l2cap_node_auto_discon_ep))654error = EMSGSIZE;655else656l2cap->discon_timo =657*((ng_l2cap_node_auto_discon_ep *)658(msg->data));659break;660661default:662error = EINVAL;663break;664}665break;666667default:668error = EINVAL;669break;670}671672NG_RESPOND_MSG(error, node, item, rsp);673NG_FREE_MSG(msg);674675return (error);676} /* ng_l2cap_rcvmsg */677678/*679* Process data packet from one of our hooks.680*681* From the HCI hook we expect to receive ACL data packets. ACL data packets682* gets re-assembled into one L2CAP packet (according to length) and then gets683* processed.684*685* NOTE: We expect to receive L2CAP packet header in the first fragment.686* Otherwise we WILL NOT be able to get length of the L2CAP packet.687*688* Signaling L2CAP packets (destination channel ID == 0x1) are processed within689* the node. Connectionless data packets (destination channel ID == 0x2) will690* be forwarded to appropriate upstream hook unless it is not connected or691* connectionless traffic for the specified PSM was disabled.692*693* From the upstream hooks we expect to receive data packets. These data694* packets will be converted into L2CAP data packets. The length of each695* L2CAP packet must not exceed channel's omtu (our peer's imtu). Then696* these L2CAP packets will be converted to ACL data packets (according to697* HCI layer MTU) and sent to lower layer.698*699* No data is expected from the control hook.700*/701702static int703ng_l2cap_rcvdata(hook_p hook, item_p item)704{705ng_l2cap_p l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));706struct mbuf *m = NULL;707int error = 0;708709/* Detach mbuf, discard item and process data */710NGI_GET_M(item, m);711NG_FREE_ITEM(item);712713if (hook == l2cap->hci)714error = ng_l2cap_lp_receive(l2cap, m);715else if (hook == l2cap->l2c)716error = ng_l2cap_l2ca_write_req(l2cap, m);717else {718NG_FREE_M(m);719error = EINVAL;720}721722return (error);723} /* ng_l2cap_rcvdata */724725/*726* Clean all connections, channels and commands for the L2CAP node727*/728729static void730ng_l2cap_cleanup(ng_l2cap_p l2cap)731{732ng_l2cap_con_p con = NULL;733734/* Clean up connection and channels */735while (!LIST_EMPTY(&l2cap->con_list)) {736con = LIST_FIRST(&l2cap->con_list);737738if (con->flags & NG_L2CAP_CON_LP_TIMO)739ng_l2cap_lp_untimeout(con);740else if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)741ng_l2cap_discon_untimeout(con);742743/* Connection terminated by local host */744ng_l2cap_con_fail(con, 0x16);745}746} /* ng_l2cap_cleanup */747748/*749* Destroy all channels that use specified upstream hook750*/751752static void753ng_l2cap_destroy_channels(ng_l2cap_p l2cap)754{755while (!LIST_EMPTY(&l2cap->chan_list))756ng_l2cap_free_chan(LIST_FIRST(&l2cap->chan_list));757} /* ng_l2cap_destroy_channels_by_hook */758759760