Path: blob/main/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c
34814 views
/*1* ng_l2cap_misc.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_misc.c,v 1.5 2003/09/08 19:11:45 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/bluetooth/include/ng_bluetooth.h>43#include <netgraph/bluetooth/include/ng_hci.h>44#include <netgraph/bluetooth/include/ng_l2cap.h>45#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>46#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>47#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>48#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>49#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>50#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>5152static u_int16_t ng_l2cap_get_cid (ng_l2cap_p, int);5354/******************************************************************************55******************************************************************************56** Utility routines57******************************************************************************58******************************************************************************/5960/*61* Send hook information to the upper layer62*/6364void65ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2)66{67ng_l2cap_p l2cap = NULL;68struct ng_mesg *msg = NULL;69int error = 0;70ng_l2cap_node_hook_info_ep *ep ;7172if (node == NULL || NG_NODE_NOT_VALID(node) ||73hook == NULL || NG_HOOK_NOT_VALID(hook))74return;7576l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);77if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) ||78bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0)79return;8081NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO,82sizeof(*ep), M_NOWAIT);8384if (msg != NULL) {85ep = (ng_l2cap_node_hook_info_ep *) &msg->data;86bcopy(&l2cap->bdaddr, &ep->addr, sizeof(bdaddr_t));87NG_SEND_MSG_HOOK(error, node, msg, hook, 0);88} else89error = ENOMEM;9091if (error != 0)92NG_L2CAP_INFO(93"%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n",94__func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook),95error);96} /* ng_l2cap_send_hook_info */9798/*99* Create new connection descriptor for the "remote" unit.100* Will link connection descriptor to the l2cap node.101*/102103ng_l2cap_con_p104ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr, int type)105{106static int fake_con_handle = 0x0f00;107ng_l2cap_con_p con = NULL;108109/* Create new connection descriptor */110con = malloc(sizeof(*con), M_NETGRAPH_L2CAP,111M_NOWAIT|M_ZERO);112if (con == NULL)113return (NULL);114115con->l2cap = l2cap;116con->state = NG_L2CAP_CON_CLOSED;117con->encryption = 0;118/*119* XXX120*121* Assign fake connection handle to the connection descriptor.122* Bluetooth specification marks 0x0f00 - 0x0fff connection123* handles as reserved. We need this fake connection handles124* for timeouts. Connection handle will be passed as argument125* to timeout so when timeout happens we can find the right126* connection descriptor. We can not pass pointers, because127* timeouts are external (to Netgraph) events and there might128* be a race when node/hook goes down and timeout event already129* went into node's queue130*/131132con->con_handle = fake_con_handle ++;133if (fake_con_handle > 0x0fff)134fake_con_handle = 0x0f00;135136bcopy(bdaddr, &con->remote, sizeof(con->remote));137con->linktype = type;138ng_callout_init(&con->con_timo);139140con->ident = NG_L2CAP_FIRST_IDENT - 1;141TAILQ_INIT(&con->cmd_list);142143/* Link connection */144LIST_INSERT_HEAD(&l2cap->con_list, con, next);145146return (con);147} /* ng_l2cap_new_con */148149/*150* Add reference to the connection descriptor151*/152153void154ng_l2cap_con_ref(ng_l2cap_con_p con)155{156con->refcnt ++;157158if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) {159if ((con->state != NG_L2CAP_CON_OPEN) ||160(con->flags & NG_L2CAP_CON_OUTGOING) == 0)161panic(162"%s: %s - bad auto disconnect timeout, state=%d, flags=%#x\n",163__func__, NG_NODE_NAME(con->l2cap->node),164con->state, con->flags);165166ng_l2cap_discon_untimeout(con);167}168} /* ng_l2cap_con_ref */169170/*171* Remove reference from the connection descriptor172*/173174void175ng_l2cap_con_unref(ng_l2cap_con_p con)176{177con->refcnt --;178179if (con->refcnt < 0)180panic(181"%s: %s - con->refcnt < 0\n", __func__, NG_NODE_NAME(con->l2cap->node));182183/*184* Set auto disconnect timer only if the following conditions are met:185* 1) we have no reference on the connection186* 2) connection is in OPEN state187* 3) it is an outgoing connection188* 4) disconnect timeout > 0189* 5) connection is not dying190*/191192if ((con->refcnt == 0) &&193(con->state == NG_L2CAP_CON_OPEN) &&194(con->flags & NG_L2CAP_CON_OUTGOING) &&195(con->l2cap->discon_timo > 0) &&196((con->flags & NG_L2CAP_CON_DYING) == 0))197ng_l2cap_discon_timeout(con);198} /* ng_l2cap_con_unref */199200/*201* Set auto disconnect timeout202* XXX FIXME: check return code from ng_callout203*/204205int206ng_l2cap_discon_timeout(ng_l2cap_con_p con)207{208if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO))209panic(210"%s: %s - invalid timeout, state=%d, flags=%#x\n",211__func__, NG_NODE_NAME(con->l2cap->node),212con->state, con->flags);213214con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO;215ng_callout(&con->con_timo, con->l2cap->node, NULL,216con->l2cap->discon_timo * hz,217ng_l2cap_process_discon_timeout, NULL,218con->con_handle);219220return (0);221} /* ng_l2cap_discon_timeout */222223/*224* Unset auto disconnect timeout225*/226227int228ng_l2cap_discon_untimeout(ng_l2cap_con_p con)229{230if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO))231panic(232"%s: %s - no disconnect timeout, state=%d, flags=%#x\n",233__func__, NG_NODE_NAME(con->l2cap->node),234con->state, con->flags);235236if (ng_uncallout(&con->con_timo, con->l2cap->node) < 1)237return (ETIMEDOUT);238239con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;240241return (0);242} /* ng_l2cap_discon_untimeout */243244/*245* Free connection descriptor. Will unlink connection and free everything.246*/247248void249ng_l2cap_free_con(ng_l2cap_con_p con)250{251ng_l2cap_chan_p f = NULL, n = NULL;252253con->state = NG_L2CAP_CON_CLOSED;254255while (con->tx_pkt != NULL) {256struct mbuf *m = con->tx_pkt->m_nextpkt;257258m_freem(con->tx_pkt);259con->tx_pkt = m;260}261262NG_FREE_M(con->rx_pkt);263264for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) {265n = LIST_NEXT(f, next);266267if (f->con == con)268ng_l2cap_free_chan(f);269270f = n;271}272273while (!TAILQ_EMPTY(&con->cmd_list)) {274ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list);275276ng_l2cap_unlink_cmd(cmd);277if (cmd->flags & NG_L2CAP_CMD_PENDING)278ng_l2cap_command_untimeout(cmd);279ng_l2cap_free_cmd(cmd);280}281282if (con->flags & (NG_L2CAP_CON_AUTO_DISCON_TIMO|NG_L2CAP_CON_LP_TIMO))283panic(284"%s: %s - timeout pending! state=%d, flags=%#x\n",285__func__, NG_NODE_NAME(con->l2cap->node),286con->state, con->flags);287288LIST_REMOVE(con, next);289290bzero(con, sizeof(*con));291free(con, M_NETGRAPH_L2CAP);292} /* ng_l2cap_free_con */293294/*295* Get connection by "remote" address296*/297298ng_l2cap_con_p299ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr, unsigned int type)300{301ng_l2cap_con_p con = NULL;302303LIST_FOREACH(con, &l2cap->con_list, next)304if ((bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0)&&305(con->linktype == type))306break;307308return (con);309} /* ng_l2cap_con_by_addr */310311/*312* Get connection by "handle"313*/314315ng_l2cap_con_p316ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle)317{318ng_l2cap_con_p con = NULL;319320LIST_FOREACH(con, &l2cap->con_list, next)321if (con->con_handle == con_handle)322break;323324return (con);325} /* ng_l2cap_con_by_handle */326327/*328* Allocate new L2CAP channel descriptor on "con" connection with "psm".329* Will link the channel to the l2cap node330*/331332ng_l2cap_chan_p333ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm, int idtype)334{335ng_l2cap_chan_p ch = NULL;336337ch = malloc(sizeof(*ch), M_NETGRAPH_L2CAP,338M_NOWAIT|M_ZERO);339if (ch == NULL)340return (NULL);341if(idtype == NG_L2CAP_L2CA_IDTYPE_ATT){342ch->scid = ch->dcid = NG_L2CAP_ATT_CID;343}else if(idtype == NG_L2CAP_L2CA_IDTYPE_SMP){344ch->scid = ch->dcid = NG_L2CAP_SMP_CID;345}else{346ch->scid = ng_l2cap_get_cid(l2cap,347(con->linktype!= NG_HCI_LINK_ACL));348}349350ch->idtype = idtype;351if (ch->scid != NG_L2CAP_NULL_CID) {352/* Initialize channel */353ch->psm = psm;354ch->con = con;355ch->state = NG_L2CAP_CLOSED;356357/* Set MTU and flow control settings to defaults */358ch->imtu = NG_L2CAP_MTU_DEFAULT;359bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow));360361ch->omtu = NG_L2CAP_MTU_DEFAULT;362bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow));363364ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;365ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;366367LIST_INSERT_HEAD(&l2cap->chan_list, ch, next);368369ng_l2cap_con_ref(con);370} else {371bzero(ch, sizeof(*ch));372free(ch, M_NETGRAPH_L2CAP);373ch = NULL;374}375376return (ch);377} /* ng_l2cap_new_chan */378379ng_l2cap_chan_p380ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid, int idtype)381{382ng_l2cap_chan_p ch = NULL;383384if((idtype == NG_L2CAP_L2CA_IDTYPE_ATT)||385(idtype == NG_L2CAP_L2CA_IDTYPE_SMP)){386return NULL;387}388389LIST_FOREACH(ch, &l2cap->chan_list, next){390if((idtype != NG_L2CAP_L2CA_IDTYPE_BREDR)&&391(ch->con->linktype == NG_HCI_LINK_ACL ))392continue;393if((idtype != NG_L2CAP_L2CA_IDTYPE_LE)&&394(ch->con->linktype != NG_HCI_LINK_ACL ))395continue;396if (ch->scid == scid)397break;398}399return (ch);400} /* ng_l2cap_chan_by_scid */401402ng_l2cap_chan_p403ng_l2cap_chan_by_conhandle(ng_l2cap_p l2cap, uint16_t scid,404u_int16_t con_handle)405{406ng_l2cap_chan_p ch = NULL;407408LIST_FOREACH(ch, &l2cap->chan_list, next){409if ((ch->scid == scid) &&410(ch->con->con_handle == con_handle))411break;412}413return (ch);414} /* ng_l2cap_chan_by_scid */415416/*417* Free channel descriptor.418*/419420void421ng_l2cap_free_chan(ng_l2cap_chan_p ch)422{423ng_l2cap_cmd_p f = NULL, n = NULL;424425f = TAILQ_FIRST(&ch->con->cmd_list);426427while (f != NULL) {428n = TAILQ_NEXT(f, next);429430if (f->ch == ch) {431ng_l2cap_unlink_cmd(f);432if (f->flags & NG_L2CAP_CMD_PENDING)433ng_l2cap_command_untimeout(f);434ng_l2cap_free_cmd(f);435}436437f = n;438}439440LIST_REMOVE(ch, next);441442ng_l2cap_con_unref(ch->con);443444bzero(ch, sizeof(*ch));445free(ch, M_NETGRAPH_L2CAP);446} /* ng_l2cap_free_chan */447448/*449* Create new L2CAP command descriptor. WILL NOT add command to the queue.450*/451452ng_l2cap_cmd_p453ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident,454u_int8_t code, u_int32_t token)455{456ng_l2cap_cmd_p cmd = NULL;457458KASSERT((ch == NULL || ch->con == con),459("%s: %s - invalid channel pointer!\n",460__func__, NG_NODE_NAME(con->l2cap->node)));461462cmd = malloc(sizeof(*cmd), M_NETGRAPH_L2CAP,463M_NOWAIT|M_ZERO);464if (cmd == NULL)465return (NULL);466467cmd->con = con;468cmd->ch = ch;469cmd->ident = ident;470cmd->code = code;471cmd->token = token;472ng_callout_init(&cmd->timo);473474return (cmd);475} /* ng_l2cap_new_cmd */476477/*478* Get pending (i.e. initiated by local side) L2CAP command descriptor by ident479*/480481ng_l2cap_cmd_p482ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident)483{484ng_l2cap_cmd_p cmd = NULL;485486TAILQ_FOREACH(cmd, &con->cmd_list, next) {487if ((cmd->flags & NG_L2CAP_CMD_PENDING) && cmd->ident == ident) {488KASSERT((cmd->con == con),489("%s: %s - invalid connection pointer!\n",490__func__, NG_NODE_NAME(con->l2cap->node)));491492break;493}494}495496return (cmd);497} /* ng_l2cap_cmd_by_ident */498499/*500* Set LP timeout501* XXX FIXME: check return code from ng_callout502*/503504int505ng_l2cap_lp_timeout(ng_l2cap_con_p con)506{507if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO))508panic(509"%s: %s - invalid timeout, state=%d, flags=%#x\n",510__func__, NG_NODE_NAME(con->l2cap->node),511con->state, con->flags);512513con->flags |= NG_L2CAP_CON_LP_TIMO;514ng_callout(&con->con_timo, con->l2cap->node, NULL,515bluetooth_hci_connect_timeout(),516ng_l2cap_process_lp_timeout, NULL,517con->con_handle);518519return (0);520} /* ng_l2cap_lp_timeout */521522/*523* Unset LP timeout524*/525526int527ng_l2cap_lp_untimeout(ng_l2cap_con_p con)528{529if (!(con->flags & NG_L2CAP_CON_LP_TIMO))530panic(531"%s: %s - no LP connection timeout, state=%d, flags=%#x\n",532__func__, NG_NODE_NAME(con->l2cap->node),533con->state, con->flags);534535if (ng_uncallout(&con->con_timo, con->l2cap->node) < 1)536return (ETIMEDOUT);537538con->flags &= ~NG_L2CAP_CON_LP_TIMO;539540return (0);541} /* ng_l2cap_lp_untimeout */542543/*544* Set L2CAP command timeout545* XXX FIXME: check return code from ng_callout546*/547548int549ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo)550{551int arg;552553if (cmd->flags & NG_L2CAP_CMD_PENDING)554panic(555"%s: %s - duplicated command timeout, code=%#x, flags=%#x\n",556__func__, NG_NODE_NAME(cmd->con->l2cap->node),557cmd->code, cmd->flags);558559arg = ((cmd->ident << 16) | cmd->con->con_handle);560cmd->flags |= NG_L2CAP_CMD_PENDING;561ng_callout(&cmd->timo, cmd->con->l2cap->node, NULL, timo,562ng_l2cap_process_command_timeout, NULL, arg);563564return (0);565} /* ng_l2cap_command_timeout */566567/*568* Unset L2CAP command timeout569*/570571int572ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd)573{574if (!(cmd->flags & NG_L2CAP_CMD_PENDING))575panic(576"%s: %s - no command timeout, code=%#x, flags=%#x\n",577__func__, NG_NODE_NAME(cmd->con->l2cap->node),578cmd->code, cmd->flags);579580if (ng_uncallout(&cmd->timo, cmd->con->l2cap->node) < 1)581return (ETIMEDOUT);582583cmd->flags &= ~NG_L2CAP_CMD_PENDING;584585return (0);586} /* ng_l2cap_command_untimeout */587588/*589* Prepend "m"buf with "size" bytes590*/591592struct mbuf *593ng_l2cap_prepend(struct mbuf *m, int size)594{595M_PREPEND(m, size, M_NOWAIT);596if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL))597return (NULL);598599return (m);600} /* ng_l2cap_prepend */601602/*603* Default flow settings604*/605606ng_l2cap_flow_p607ng_l2cap_default_flow(void)608{609static ng_l2cap_flow_t default_flow = {610/* flags */ 0x0,611/* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT,612/* token_rate */ 0xffffffff, /* maximum */613/* token_bucket_size */ 0xffffffff, /* maximum */614/* peak_bandwidth */ 0x00000000, /* maximum */615/* latency */ 0xffffffff, /* don't care */616/* delay_variation */ 0xffffffff /* don't care */617};618619return (&default_flow);620} /* ng_l2cap_default_flow */621622/*623* Get next available channel ID624* XXX FIXME this is *UGLY* but will do for now625*/626627static u_int16_t628ng_l2cap_get_cid(ng_l2cap_p l2cap,int isle)629{630u_int16_t cid ;631u_int16_t endcid;632uint16_t mask;633int idtype;634if(isle){635endcid = l2cap->lecid;636/*Assume Last CID is 2^n-1 */637mask = NG_L2CAP_LELAST_CID;638idtype = NG_L2CAP_L2CA_IDTYPE_LE;639}else{640endcid = l2cap->cid;641/*Assume Last CID is 2^n-1 */642mask = NG_L2CAP_LAST_CID;643idtype = NG_L2CAP_L2CA_IDTYPE_BREDR;644}645cid = (endcid+1) & mask;646647if (cid < NG_L2CAP_FIRST_CID)648cid = NG_L2CAP_FIRST_CID;649650while (cid != endcid) {651if (ng_l2cap_chan_by_scid(l2cap, cid, idtype) == NULL) {652if(!isle){653l2cap->cid = cid;654}else{655l2cap->lecid = cid;656}657return (cid);658}659660cid ++;661cid &= mask;662if (cid < NG_L2CAP_FIRST_CID)663cid = NG_L2CAP_FIRST_CID;664}665666return (NG_L2CAP_NULL_CID);667} /* ng_l2cap_get_cid */668669/*670* Get next available command ident671* XXX FIXME this is *UGLY* but will do for now672*/673674u_int8_t675ng_l2cap_get_ident(ng_l2cap_con_p con)676{677u_int8_t ident = con->ident + 1;678679if (ident < NG_L2CAP_FIRST_IDENT)680ident = NG_L2CAP_FIRST_IDENT;681682while (ident != con->ident) {683if (ng_l2cap_cmd_by_ident(con, ident) == NULL) {684con->ident = ident;685686return (ident);687}688689ident ++;690if (ident < NG_L2CAP_FIRST_IDENT)691ident = NG_L2CAP_FIRST_IDENT;692}693694return (NG_L2CAP_NULL_IDENT);695} /* ng_l2cap_get_ident */696697698