📄 l2cap_con.c
字号:
/* * l2cap_con.c -- Implementation of Bluetooth Logical Link Control * and Adaption Protocol (L2CAP), Connection Manager * * Copyright (C) 2000, 2001 Axis Communications AB * * Author: Mattias Agren <mattias.agren@axis.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Exceptionally, Axis Communications AB grants discretionary and * conditional permissions for additional use of the text contained * in the company's release of the AXIS OpenBT Stack under the * provisions set forth hereunder. * * Provided that, if you use the AXIS OpenBT Stack with other files, * that do not implement functionality as specified in the Bluetooth * System specification, to produce an executable, this does not by * itself cause the resulting executable to be covered by the GNU * General Public License. Your use of that executable is in no way * restricted on account of using the AXIS OpenBT Stack code with it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the provisions of the GNU * General Public License. * * $Id: l2cap_con.c,v 1.11 2001/09/18 12:01:05 pkj Exp $ * *//****************** INCLUDE FILES SECTION ***********************************/#define __NO_VERSION__ /* don't define kernel_version in module.h */#ifdef __KERNEL__#include <linux/malloc.h>#include <linux/bluetooth/l2cap.h>#include <linux/bluetooth/l2cap_con.h>#include <linux/bluetooth/l2cap_internal.h>#include <linux/bluetooth/btdebug.h>#else /* user mode */#include <stdlib.h>#include <string.h>#include <sys/time.h>#include <signal.h>#include "include/l2cap.h"#include "include/l2cap_con.h"#include "include/l2cap_internal.h"#include "include/btdebug.h"#endif/****************** DEBUG CONSTANT AND MACRO SECTION ************************/#if L2CAP_DEBUG_CON/* Connection manager */#define D_CON(fmt...) printk(L2CAP_DBG_STR fmt)#define SHOW_CON(str, con) show_con(str, con)#define SHOW_LIST() show_list()#else#define D_CON(fmt...)#define SHOW_CON(str, con)#define SHOW_LIST()#endif#if L2CAP_DEBUG_MISC/* Misc */#define D_MISC(fmt...) printk(L2CAP_DBG_STR fmt)#else#define D_MISC(fmt...)#endif#if L2CAP_DEBUG_DATA#define PRINTPKT(str, data, len) print_data(str, data, len)#else#define PRINTPKT(str, data, len)#endif/****************** CONSTANT AND MACRO SECTION ******************************/#define MAX_CONLISTSIZE 255 /* for now */static const u8* bool2str[] = { "no", "yes", };/****************** TYPE DEFINITION SECTION *********************************/typedef struct l2cap_con_list { l2cap_con *first; l2cap_con *last; l2cap_con *cur; u32 count;} l2cap_con_list;/****************** LOCAL FUNCTION DECLARATION SECTION **********************//****************** GLOBAL VARIABLE DECLARATION SECTION *********************//****************** LOCAL VARIABLE DECLARATION SECTION **********************/static l2cap_con_list con_list;/****************** FUNCTION DEFINITION SECTION *****************************/ /*************************************//* Connection manager list functions *//**************************************/void init_con_list(void){ con_list.first = NULL; con_list.last = NULL; con_list.cur = NULL; con_list.count = 0;/* l2cap->cid_count = MIN_CID; Moved to l2cap_init */}void free_con_list(void){ D_CON(__FUNCTION__ ": Freeing connection list\n"); while (con_list.count) delete_con(con_list.first); D_CON(__FUNCTION__ ": Connection list removed\n");} l2cap_con* create_con(u16 hci_hdl, CID lcid, CID rcid){ l2cap_con* con; D_CON(__FUNCTION__ ": rcid %d\n", rcid); if (con_list.count == MAX_CONLISTSIZE) { D_ERR("Connection list full!\n"); return NULL; } /* Add check if already in list ?? or rely on that otherwhere ?*/ /* Allocate new element */ if ((con = kmalloc(sizeof *con, GFP_ATOMIC)) < 0) { D_ERR(__FUNCTION__ ": could not allocate new l2cap con\n"); return NULL; } reset_con(con); con->hci_hdl = hci_hdl; con->local_cid = lcid; con->remote_cid = rcid; con->next = con; con->prev = con; init_flow(&con->remote_qos); return con;}void reset_con(l2cap_con* con){ con->psm = 0; /* invalid */ con->local_cid = 0; con->remote_cid = 0; con->local_mtu = MTU_DEFAULT; con->remote_mtu = MTU_DEFAULT; con->flush_timeout = FLUSHTIMEOUT_DEFAULT; con->current_state = CLOSED; con->conf_req_ready = TRUE; /* haven't started anything yet */ con->conf_rsp_ready = TRUE; /* haven't started anything yet */ con->conf_req_sent = 0; /* haven't sent anything yet */ con->initiator = FALSE; /* Other side initiated (default) */ con->magic = L2CAP_CON_MAGIC; con->upper_con = NULL; con->c_status = 0; con->c_flags = 0; con->timer.rtx_action = RTX_ACTION_DISCONNECT; con->timer.ertx_action = ERTX_ACTION_DISCONNECT; con->timer.rtx_inuse = 0; con->timer.ertx_inuse = 0; con->timer.rtx_no = 0;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) con->wq = NULL;#else init_waitqueue_head(&con->wq);#endif}l2cap_con* get_first_con(void){ return con_list.first;}l2cap_con* get_next_con(l2cap_con* con){ if (con->next == con_list.first) return NULL; else return con->next;}/* Searches list for remote bd addr and returns connection if found and if the connection is in state STATE */l2cap_con* get_con(BD_ADDR bd, s32 STATE){ s32 i = 0; D_CON(__FUNCTION__ ": look for connections in state %s (%d)\n", state_name[STATE], STATE); PRINTPKT(__FUNCTION__ ": look for bd : ", bd, 6); while (i < con_list.count) { if (memcmp(bd, con_list.cur->remote_bd, 6) == 0) { if (STATE == ANY_STATE) { /* simply look for all connections with BD address bd*/ PRINTPKT(__FUNCTION__ ": found bd ", con_list.cur->remote_bd, 6); if (PARANOIA_CHECKCON(con_list.cur)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return NULL; } return con_list.cur; } else if (con_list.cur->current_state == STATE) { PRINTPKT(__FUNCTION__": con_list bd ", con_list.cur->remote_bd, 6); return con_list.cur; } } /* Not yet found, keep on searching */ con_list.cur = con_list.cur->next; i++; } D_CON(__FUNCTION__ ": connection not found\n"); return NULL;}/* Searches list based on hci handle, if found it sets remote cid and returns the connection. If hci handle is found but connection not yet 'active' it simply returns the found con. If previously in use, a new l2cap con is created over the same hci handle. */l2cap_con* check_remote_cid(u16 hdl, CID remote_cid){ l2cap_con *found; l2cap_con *new_con; D_CON(__FUNCTION__ ": hdl %d remote_cid %d\n", hdl, remote_cid); found = get_con_hcihdl(hdl); if (found != NULL) { D_CON(__FUNCTION__ ": con handle found...\n"); if (found->remote_cid == 0) { /* This is the first l2cap connection over this hci handle (remote_cid is only set to 0 when receiving a new baseband connection) */ D_CON(__FUNCTION__ ": single connection/handle \n"); found->remote_cid = remote_cid; SHOW_LIST(); return found; } else if ((found->remote_cid >= 0x0040) && (found->remote_cid <= 0xffff)) { /* at least one l2cap connection with this hci handle exist now let's create another connection over same hci handle */ D_CON(__FUNCTION__ ": multiple connections/handle \n"); new_con = create_con(hdl, get_cid(), remote_cid); if (new_con == NULL) { D_ERR(__FUNCTION__ ": no connection created\n"); return NULL; } new_con->link_up = TRUE; new_con->initiator = FALSE; /* set bd address in new connection */ memcpy(new_con->remote_bd, found->remote_bd, 6); insert_con(new_con); SHOW_LIST(); return new_con; } } else { D_CON(__FUNCTION__ ": connection not found\n"); } return NULL;}/* Searches list for hci handle and returns first connection found */l2cap_con* get_con_hcihdl(u16 hdl){ s32 i = 0; D_CON(__FUNCTION__ ": hdl %d\n", hdl); while (i<con_list.count) { if (con_list.cur->hci_hdl == hdl) { if (PARANOIA_CHECKCON(con_list.cur)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return NULL; } return con_list.cur; } /* Not yet found, keep on searching */ con_list.cur = con_list.cur->next; i++; } D_CON(__FUNCTION__ ": no more connections on this handle\n"); return NULL; } /* Searches list for local cid and returns connection if found */l2cap_con* get_lcon(CID lcid){ s32 i = 0; D_CON(__FUNCTION__ ": lcid %d con_list.count = %d\n", lcid, con_list.count); while (i<con_list.count) { if (lcid == con_list.cur->local_cid) { if (PARANOIA_CHECKCON(con_list.cur)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return NULL; } return con_list.cur; } /* Not yet found, keep on searching */ con_list.cur = con_list.cur->next; i++; } D_CON(__FUNCTION__ ": connection lcid : %d not found\n", lcid); return NULL;}/* Searches list for remote cid and returns connection if found */l2cap_con* get_rcon(CID rcid){ s32 i = 0; D_CON(__FUNCTION__ ": rcid %d con_list.count = %d\n", rcid, con_list.count); while (i<con_list.count) { if (rcid == con_list.cur->remote_cid) { if (PARANOIA_CHECKCON(con_list.cur)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return NULL; } return con_list.cur; } /* Not yet found, keep on searching */ con_list.cur = con_list.cur->next; i++; } D_CON(__FUNCTION__ ": connection rcid : %d not found\n", rcid); return NULL;}void insert_con(l2cap_con *newcon){ l2cap_con *oldcon; SHOW_CON(__FUNCTION__, newcon); if (!con_list.count) { /* Empty list */ con_list.first = newcon; con_list.cur = newcon; con_list.last = newcon; con_list.count++; D_CON(__FUNCTION__ ": now %d connections\n", con_list.count); newcon->next = con_list.first; /* Pos32 at itself */ newcon->prev = con_list.first; return; } /* Not empty */ oldcon = con_list.first; /* mark oldcon first */ newcon->next = oldcon; con_list.last->next = newcon; oldcon->prev = newcon; newcon->prev = con_list.last; con_list.cur = newcon; /* Set current to new element */ con_list.first = newcon; /* Set first to new element */ con_list.count++; D_CON(__FUNCTION__ ": now %d connections\n", con_list.count);}s32 delete_con(l2cap_con* con){ if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return -1; } SHOW_CON(__FUNCTION__, con); /* Issue a warning if connection is not in CLOSED */ if (con->current_state != CLOSED) D_ERR("Deleting an active connection\n"); if (con_list.count == 1) { /* Last element */ con_list.count--; con_list.first = NULL; con_list.cur = NULL; con_list.last = NULL; kfree(con); D_CON("Now connection list is empty !\n"); return 0; } con->prev->next = con->next; /* Take it out */ con->next->prev = con->prev; if (con_list.first == con) /* Update first pointer */ con_list.first = con->next; if (con_list.last == con) /* Update last pointer */ con_list.last = con->prev; con_list.cur = con->next; /* Update cur pointer */ con_list.count--; kfree(con); return 0; }s32 count_con(u16 hci_hdl){ s32 i = 0; s32 sum = 0; while (i < con_list.count) { if (con_list.cur->hci_hdl == hci_hdl) { sum++; } con_list.cur = con_list.cur->next; i++; } D_CON(__FUNCTION__ ": %d l2cap_con's found with hci_hdl:%d\n",sum,hci_hdl); return sum;}#if L2CAP_SELFTESTs32 remove_rcid(CID rcid) /* Searches for remote_cid */{ D_CON("remove_rcid\n"); if (!con_list.count) { D_CON(__FUNCTION__ ": no connections in list\n"); return -1; } delete_con(get_rcon(rcid)); D_CON("remote cid %d removed\n", rcid); return 0;}void test_conlist(void){ /* Fixme - make it better... */ insert_con(create_con(0, 0, 0)); show_list(); insert_con(create_con(1, 1, 1)); show_list(); insert_con(create_con(2, 2, 2)); insert_con(create_con(3, 3, 3)); insert_con(create_con(4, 4, 4)); insert_con(create_con(5, 5, 5)); insert_con(create_con(6, 6, 6)); show_list(); remove_rcid(3); show_list(); remove_rcid(5); show_list(); free_con_list(); show_list(); /* ... */}#endif /* L2CAP_SELFTEST */void show_con(const u8* head, l2cap_con* con){ if (con) { printk(__FUNCTION__ ": %s (%d:%d) [%s] [%s] r_mtu[%d] C[%s] link_up[%s]\n", head, con->local_cid, con->remote_cid, state_name[con->current_state], psm2str(con->psm), con->remote_mtu, bool2str[con->initiator], bool2str[con->link_up]); } else { printk(__FUNCTION__ ": NULL\n"); }}/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */void show_list(void){ s32 i; l2cap_con *tmp; tmp = con_list.first; printk("----------- show_list count :%d -----------\n", con_list.count); for (i = 0; i < con_list.count; i++) { show_con(" - ", tmp); tmp = tmp->next; } printk("-------------------------------------------\n\n\n");}void init_flow(flow *f){ D_MISC(__FUNCTION__ "\n"); f->flags = 0; f->service = 0x01; f->token_rate = 0x00000000; /* No token rate specified, default */ f->bucket_size = 0x00000000; /* No bucket needed, default */ f->peak = 0x00000000; /* Unknown maximum bandwidth, default*/ f->latency = 0xffffffff; /* Don't care. default */ f->delay = 0xffffffff; /* Don't care. default */}/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *//* Status functions for /proc files */s32 l2cap_sprint_con(u8 *buf, l2cap_con* con){ u16 pos = 0; if (con) { pos += sprintf(buf + pos, "\nr_bd["); pos += l2cap_sprint_bd(buf + pos, con->remote_bd); pos += sprintf(buf + pos, " ]\n"); pos += sprintf(buf + pos, "lcid[%d] rcid[%d] state[%s] psm[%s]\n", con->local_cid, con->remote_cid, state_name[con->current_state], psm2str(con->psm)); pos += sprintf(buf + pos, "remote_mtu[%d] local_mtu [%d] clnt[%s] link_up[%s]\n", con->remote_mtu, con->local_mtu, bool2str[con->initiator], bool2str[con->link_up]); } return pos;}s32 l2cap_sprint_active(u8 *buf){ u16 i; u16 pos = 0; l2cap_con *tmp_con; tmp_con = con_list.first; for (i = 0; i < con_list.count; i++) { pos += l2cap_sprint_con(buf+pos, tmp_con); pos += sprintf(buf+pos, "\n"); tmp_con = tmp_con->next; } return pos;}/****************** END OF FILE l2cap_con.c **********************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -