📄 l2cap.c
字号:
s32 i, retval = 0; u8 rev_bd[6]; D_RCV(__FUNCTION__ "\n"); PRINTPKT(__FUNCTION__ ": sent to bd ",bd, 6); /* Check bd_addr */ if (!bd[0] && !bd[1] && !bd[2] && !bd[3] && !bd[4] && !bd[5]) { D_ERR(__FUNCTION__ ": No BD-addr\n"); return -EINVAL; } /* Check PSM */ if ((psm % 2) == 0) { D_ERR(__FUNCTION__ ": incorrect PSM value\n"); return -EINVAL; } if ((psm > MAX_PSM) && (psm < MIN_DYNAMIC_PSM)) { D_ERR(__FUNCTION__ ": value of PSM preserved\n"); return -EINVAL; } if (psm > MAX_DYNAMIC_PSM) { D_ERR(__FUNCTION__ ": PSM value not valid\n"); return -EINVAL; } /* bd is big endian, reverse byte order to little endian */ for (i = 0; i < 6; i++) { rev_bd[5-i] = bd[i]; } tmpcon = get_con(rev_bd, ANY_STATE); /* Create connection object, hci_handle is set from lp_connect_cfm */ con = create_con(0/* not yet set */, get_cid(), 0/* not yet set */); /* Check connection */ if (con == NULL) { D_ERR(__FUNCTION__ ": no connection created\n"); return -ENOMEM; } memcpy(con->remote_bd, rev_bd, 6); print_data("bd", con->remote_bd, 6); /* we are client */ con->initiator = 1; /* we must send and receive a configure req */ con->conf_req_ready=0; con->conf_rsp_ready=0; con->psm = psm; con->flush_timeout = FLUSHTIMEOUT_DEFAULT; /* is set when receiving a pos lp_connect_cfm */ con->link_up = 0; insert_con(con); /* if this physical bd link exist for another l2cap con, use that! */ if (tmpcon) { D_STATE(__FUNCTION__ ": hci handle already exist.\n"); con->hci_hdl = tmpcon->hci_hdl; con->link_up = 1; ENTERSTATE(con, W4_L2CAP_CONNECT_RSP); PRINTSTATE(con); } else { D_STATE(__FUNCTION__ ": create new baseband link\n"); retval = lp_connect_req(con->remote_bd); if(retval < 0) { D_ERR(__FUNCTION__ ": failed (status %d)\n", retval); return retval; } /* wait here until we received a lp_connect_cfm */ l2ca_wait(__FUNCTION__ ": wait baseband", con); if (con->c_status != RES_SUCCESS) { D_ERR(__FUNCTION__ ": lp_connect_req failed, no connection (status %d)\n", con->c_status); retval = -MSGCODE(MSG_LAYER_HCI, con->c_status); delete_con(con); return retval; } } /* Now send connect req */ con->c_status = CSTATUS_RTX_TIMEOUT; /* Leave loop when either status failed or success */ while (con->c_status == CSTATUS_RTX_TIMEOUT) { /* baseband is up, now initiate send connect req */ retval = l2cap_connect_req(con, con->psm); if(retval < 0) { D_ERR(__FUNCTION__ ": failed (status %d)\n", retval); } /* wait until we received a response or after timeout */ l2ca_wait(__FUNCTION__ ": wait rsp", con); } if (con->c_status == CSTATUS_MAX_NO_RTX && !retval) { retval = -MSGCODE(MSG_LAYER_L2CAP, L2CAP_RTX_TIMEOUT); } return retval;}/* params determines how we would like to receive data */s32 l2ca_config_req(l2cap_con *con, u16 in_mtu, flow *outflow, u16 flush_timeout, u16 link_to){ if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return -EINVAL; } D_STATE("l2ca_config_req remote cid %d, in_mtu %d\n", con->remote_cid, in_mtu); /* should already be in CONFIG state */ if (con->current_state == CONFIG) { /* local mtu is set in l2cap_config_req */ con->conf_req_sent = 1; return l2cap_config_req(con, in_mtu, outflow, flush_timeout, link_to); } else if (con->current_state == CLOSED) { l2ca_config_cfm(con, CONF_REJ); return -MSGCODE(MSG_LAYER_L2CAP, L2CAP_NO_CONNECTION); } else if (con->current_state == OPEN) { DSYS("l2ca_config_req: state OPEN, reconfiguration !\n"); /* FIXME - Is data automatically suspended while reconfiguring ? */ ENTERSTATE(con, CONFIG); PRINTSTATE(con); /* local mtu is set in l2cap_config_req */ con->conf_req_ready = FALSE; con->conf_rsp_ready = FALSE; return l2cap_config_req(con, in_mtu, outflow, flush_timeout, link_to); } else { D_ERR(__FUNCTION__ ": invalid state\n"); PRINTSTATE(con); return -MSGCODE(MSG_LAYER_L2CAP, L2CAP_INVALID_STATE); }}s32 l2ca_disconnect_req(l2cap_con *con){ s32 retval = 0; u16 tmp_hdl = con->hci_hdl; if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return -EINVAL; } D_STATE(__FUNCTION__ ": remote cid : %d\n", con->remote_cid); if (con->current_state == OPEN || con->current_state == CONFIG) { ENTERSTATE(con, W4_L2CAP_DISCONNECT_RSP); PRINTSTATE(con); do { retval = l2cap_disconnect_req(con); if(retval < 0) { /* This will result in a timeout and we need to try again as the error is a result of insufficent space in our memorypool and we try again after a timeout */ D_ERR(__FUNCTION__ ": Failed (status %d)\n", retval); } l2ca_wait(__FUNCTION__, con); PRINTSTATE(con); } while (con->c_status == CSTATUS_RTX_TIMEOUT); } else { D_ERR(__FUNCTION__ ": Invalid state!\n"); PRINTSTATE(con); return -MSGCODE(MSG_LAYER_L2CAP, L2CAP_INVALID_STATE); } if(con->c_status != CSTATUS_MAX_NO_RTX) { if (con->current_state == CLOSED) { /* remove l2cap connection */ delete_con(con); } else { /* FIXME: If we reach here, should we delete the connection? */ D_ERR(__FUNCTION__ ": Failed (Connection not closed)"); PRINTSTATE(con); retval = -MSGCODE(MSG_LAYER_L2CAP, L2CAP_FAILED); } } /* fixme -- if we want to keep baseband connection we must leave the l2cap connection which holds the hci handle simply just clear l2cap params but keep hci handle ! */ if (count_con(tmp_hdl) == 0) { DSYS("l2ca_disconnect_req : (C) no more l2cap cons\n"); DSYS("Shutdown baseband\n"); lp_disconnect(tmp_hdl); } return retval;}/* Response from upper layer to the indication of a channel to a remote device */s32 l2ca_connect_rsp(l2cap_con* con, u16 response, u16 status){ s32 result; D_STATE(__FUNCTION__ "\n"); if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return -EINVAL; } /* optionally send l2cap_connect_rsp_pnd() */ if (con->current_state != W4_L2CA_CONNECT_RSP) { D_ERR(__FUNCTION__ ": invalid state \n"); PRINTSTATE(con); return -1; } result = l2cap_connect_rsp(con, response, status); if (response == RES_SUCCESS) { ENTERSTATE(con, CONFIG); PRINTSTATE(con); } else if (response == RES_PENDING) { /* remain in same state */ } else if (response >= RES_PSMNEG) { /* 'l2ca_connect_rsp_neg()' */ ENTERSTATE(con, CLOSED); PRINTSTATE(con); /* remove connection */ delete_con(con); } return result;}s32 l2ca_config_rsp(l2cap_con* con, u32 out_mtu, flow *in_flow, s32 result){ s32 ret_val = -1; if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return -EINVAL; } D_STATE(__FUNCTION__ ": remote cid %d remote mtu %d\n", con->remote_cid, con->remote_mtu); if (con->current_state != CONFIG) { D_ERR(__FUNCTION__": invalid state\n"); PRINTSTATE(con); return -1; } if (result == CONF_SUCCESS) { /* upper layers responded OK */ con->conf_rsp_ready = TRUE; ret_val = l2cap_config_rsp(con, out_mtu, in_flow, result); /* check if we have sent a configure request yet */ if (con->conf_req_ready && !con->remote_flags) { /* all done, proceed to OPEN */ ENTERSTATE(con, OPEN); PRINTSTATE(con); DSYS("l2cap channel (%d,%d) [%s] connected\n", con->local_cid, con->remote_cid, psm2str(con->psm)); /* reset variable */ con->conf_req_sent = 0; /* notify upper layers that we are opened */ l2ca_config_cfm(con, RES_SUCCESS); } else { D_STATE(__FUNCTION__ ": Conf not done or flags set\n"); } } else { D_STATE("We don't accepted remote options\n"); ret_val = l2cap_config_rsp(con, out_mtu, in_flow, result); } return ret_val;}s32 l2ca_disconnect_rsp(l2cap_con* con){ s32 result = -1; D_STATE(__FUNCTION__ "\n"); if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return -EINVAL; } SHOW_CON("disconnecting : ", con); if (con->current_state == W4_L2CA_DISCONNECT_RSP) { /* if we got a link down unexpectedly we cannot send back a response, send nothing... */ if (con->link_up) result = l2cap_disconnect_rsp(con); else { /* tell upper layers that disconnection was 'successful' */ result=0; D_STATE("baseband link down, cannot send rsp\n"); } ENTERSTATE(con, CLOSED); PRINTSTATE(con); DSYS("l2cap channel (%d,%d) [%s] disconnected\n", con->local_cid, con->remote_cid, psm2str(con->psm)); if (count_con(con->hci_hdl) > 1) { delete_con(con); } } else { D_ERR(__FUNCTION__ ": invalid state !\n\n"); PRINTSTATE(con); } return result;}/* only supports one call at a time */voidl2ca_wait(const char *str, l2cap_con *con){ if (!(con->c_flags & FLAG_WAKEMEUP)) { if (con->c_flags & FLAG_DONTSLEEP) { printk("l2ca_wait : don't sleep flag set\n"); return; } con->c_flags |= FLAG_WAKEMEUP; printk("%s, sleep on wq 0x%x\n", str, (int)&con->wq); interruptible_sleep_on(&con->wq); printk("%s, woke up !\n", str); } else { printk("%s, wq already in use\n", str); }}void l2ca_wakeup(const char *str, l2cap_con *con){ if (con->c_flags & FLAG_WAKEMEUP) { if (con->c_flags & FLAG_DONTSLEEP) { printk("l2ca_wakeup : don't sleep flag set\n"); con->c_flags &= ~FLAG_DONTSLEEP; return; } con->c_flags &= ~FLAG_WAKEMEUP; printk("%s, wake up wq 0x%x\n", str,(int)&con->wq); wake_up_interruptible(&con->wq); } else { printk("%s, wake up flag not set\n", str); }}/*******************************************************************//* (E5) Timer events *//********************/#ifdef CONFIG_BLUETOOTH_L2CAP_USE_TIMERSvoid l2ca_timeoutind(l2cap_con *con){ DSYS("l2ca_timeoutind\n"); con->c_status = CSTATUS_RTX_TIMEOUT; l2ca_wakeup("l2ca_timeoutind ", con);}#ifdef __KERNEL__voidl2cap_rtx_timeout(unsigned long ptr){ l2cap_con *con = (l2cap_con*)ptr;#else /* usermode timer */voidl2cap_rtx_timeout(void){ l2cap_con *con = timeout_con; if (timer_cancelled) { D_TIM("RTX timer already cancelled\n"); return; }#endif /* do some paranoia checks */ if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return; } D_TIM(__FUNCTION__ ": current no rtx : %d\n", con->timer.rtx_no); con->timer.rtx_inuse = 0; if (con->timer.rtx_no == MAX_NO_RTX) { con->c_status = CSTATUS_MAX_NO_RTX; D_TIM("Connection unresponsive\n"); show_con("Connection unresponsive\n", con); con->c_result = MSGCODE(MSG_LAYER_L2CAP, L2CAP_CON_UNRESPONSIVE); l2ca_wakeup("rtx_timeout", con); /* reset rtx counter */ con->timer.rtx_no = 0; if (con->timer.rtx_action == RTX_ACTION_START_ERTX) { /* start ertx timer */ D_TIM("Starting ERTX\n"); start_ertx(con, ERTX_TIMEOUT); } else if (con->timer.rtx_action == RTX_ACTION_TERMINATE) { D_TIM("Removing connection\n"); l2ca_disconnect_ind(con); ENTERSTATE(con, CLOSED); delete_con(con); } } else { con->timer.rtx_no++; l2ca_timeoutind(con); }#ifndef __KERNEL__ timeout_con = NULL;#endif}voidl2cap_ertx_timeout(unsigned long ptr){ l2cap_con *con; D_TIM(__FUNCTION__ "\n"); con = (l2cap_con*)ptr; /* do paranoia check */ if (PARANOIA_CHECKCON(con)) { D_ERR(__FUNCTION__ ": Paranoia check failed\n"); return; } if (con->timer.ertx_action == ERTX_ACTION_TERMINATE) { D_TIM("Removing connection\n"); l2ca_disconnect_ind(con); ENTERSTATE(con, CLOSED); delete_con(con); } else if (con->timer.ertx_action == ERTX_ACTION_DISCONNECT) { D_TIM("Disconnect connection\n"); /* set dont sleep flag so that we wont do sleep when not running in usermode context */ con->c_flags |= FLAG_DONTSLEEP; ENTERSTATE(con, CONFIG); /* CERT HACK ! */ l2ca_disconnect_req(con); }}#endif /* CONFIG_BLUETOOTH_L2CAP_USE_TIMERS *//*******************************************************************//*-------------------------- ACTIONS ------------------------------*//*******************************************************************//* Actions are partitioned into five categories: 1. l2cap to lower layers 2. l2cap to l2cap signalling 3. l2cap to l2cap data transmission 4. l2cap to upper layers 5. setting timers *//*******************************************************************//* (A1) l2cap to lower layers *//*****************************//* FIXME - lp_qos_req() */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -