?? rfcomm.c
字號:
/* The lower protocol layer, L2CAP, indicates that the lower layer connection is about to disconnect */void rfcomm_disconnect_ind(l2cap_con *l2cap){ rfcomm_con *rfcomm; DSYS(FNC "remote cid %d\n", l2cap->remote_cid); if ((rfcomm = (rfcomm_con*)l2cap->upper_con)) { /* This l2cap connection is going down, remove all rfcomm cons and notify upper tty */ if (!l2cap->link_up) { DSYS(FNC "Baseband is down, reset this RFCOMM session\n");#ifdef __KERNEL__ bt_unregister_rfcomm(rfcomm->line);#endif bt_disconnect_ind(CREATE_RFCOMM_ID(rfcomm->line, 0)); } rfcomm_reset_con(rfcomm->line); } /* always try to send back rsp (if link is down con is deleted) */ if (l2ca_disconnect_rsp(l2cap)) { D_ERR(FNC "l2ca_disconnect_rsp failed\n"); return; }}void rfcomm_disconnect_cfm(l2cap_con *l2cap){ rfcomm_con *rfcomm; D_CTRL(FNC"disconnected\n"); rfcomm = (rfcomm_con*) l2cap->upper_con; /* fixme -- should we indicate to bt interface when rfcomm is down or when l2cap for rfcomm is down ? */ bt_disconnect_ind(CREATE_RFCOMM_ID(rfcomm->line, 0)); /* fixme -- add these glue layer functions in userstack */#ifdef __KERNEL__ bt_unregister_rfcomm(rfcomm->line); /* wake up bt line */ bt_disconnect_cfm(CREATE_RFCOMM_ID(rfcomm->line, 0), l2cap->c_result);#endif rfcomm_reset_con(rfcomm->line); }/* This function should be called from the L2CAP layer data should pos32 at the beginning of the RFCOMM data, and len should be the length of that data.*/void rfcomm_receive_data(l2cap_con *l2cap, u8 *data, u32 len){ rfcomm_con *rfcomm; short_frame *short_pkt; long_frame *long_pkt; u8* uih_data_start; u32 uih_len; u8 tmp_dlci; RF_DATA(FNC"rfcomm_receive_data:",data,len); D_REC(FNC"%d bytes, our cid is %d\n",len, l2cap->remote_cid); short_pkt = (short_frame*) data; tmp_dlci = (((short_pkt->h.addr.server_chn & 0x1f) << 1) | (short_pkt->h.addr.d & 0x1)); switch (CLR_PF(short_pkt->h.control)) { case SABM: D_CTRL(FNC"SABM-packet received\n"); if (crc_check((u8*) short_pkt, LONG_CRC_CHECK, short_pkt->data[0]) < 0) { break; } /* If the SABM command wants to open the control channel, we have to create a new rfcomm_con object */ if ((tmp_dlci) == 0) { D_CTRL(FNC"server channel == 0\n"); rfcomm = ((rfcomm_con*) l2cap->upper_con); D_CTRL(FNC"setting l2cap_con, no serv.channel yet\n"); rfcomm->dlci[0].state = CONNECTED; D_CTRL(FNC"sending back UA - control channel\n"); send_ua((rfcomm_con*) l2cap->upper_con, tmp_dlci); bt_connect_ind(CREATE_RFCOMM_ID(rfcomm->line, 0)); } /* If the channel isn't the control channel, we know that the object exist. If we haven't opened any other channel than the control channel we open the new channel here */ else if (valid_dlci(tmp_dlci)) { rfcomm = ((rfcomm_con*) l2cap->upper_con); D_REC(FNC"setting server channel %d\n", short_pkt->h.addr.server_chn);#ifdef CONFIG_BLUETOOTH_USE_SECURITY_MANAGER rfcomm_check_allowed_security(rfcomm->l2cap->remote_bd, tmp_dlci, rfcomm); /* fixme -- check result and send neg response if not allowed */ #else bt_connect_ind(CREATE_RFCOMM_ID(rfcomm->line, tmp_dlci)); rfcomm->dlci[tmp_dlci].state = CONNECTED; /* registers in bt driver */ bt_register_rfcomm(rfcomm, tmp_dlci); /* wake up any blocking connect/waits */ bt_connect_cfm(CREATE_RFCOMM_ID(rfcomm->line, tmp_dlci), 0 /* status ok*/ ); D_CTRL(FNC"sending back UA - other channel\n"); send_ua(rfcomm, tmp_dlci);#endif /* CONFIG_BLUETOOTH_USE_SECURITY_MANAGER */ } else { D_CTRL(FNC"no server on channel %d, sending DM\n", tmp_dlci); send_dm(((rfcomm_con*) l2cap->upper_con),tmp_dlci); } break; case UA: rfcomm = ((rfcomm_con*) l2cap->upper_con); if (rfcomm->magic != RFCOMM_MAGIC) { D_ERR(FNC"Invalid magic number\n"); break; } D_CTRL(FNC"UA packet received on line %d\n", rfcomm->line); if (rfcomm->dlci[0].state == CONNECTING) { /* Now we are the initiating side and we have got a respons to our SABM command. We then send a PN messages to negotiat the frame length */ rfcomm->dlci[0].state = CONNECTED; tmp_dlci = ((rfcomm->server_chn << 1) | ((~rfcomm->initiator) & 0x1)); /* must fit in l2cap mtu incl rfcomm hdrs */ send_pn_msg(rfcomm, 7, rfcomm->dlci[tmp_dlci].mtu, 0, 0, tmp_dlci, TRUE); rfcomm->dlci[tmp_dlci].state = NEGOTIATING; } else if (rfcomm->dlci[tmp_dlci].state == NEGOTIATING) { rfcomm->dlci[tmp_dlci].state = CONNECTED; /* registers in bt driver */ bt_register_rfcomm(rfcomm, tmp_dlci); /* wake up any blocking connect/waits */ bt_connect_cfm(CREATE_RFCOMM_ID(rfcomm->line, tmp_dlci), 0 /* status ok*/ ); D_CTRL(FNC"successfully connected on DLCI:%d\n", tmp_dlci); /* FIXME: Should we always do this ? */ rfcomm_msc_msg(rfcomm, EA | RTC | RTR | DV, MCC_CMD, tmp_dlci); rfcomm->dlci[tmp_dlci].initiator = TRUE; } else if (rfcomm->dlci[tmp_dlci].state == DISCONNECTING) { if (tmp_dlci == 0) { /* Control channel */#ifdef __KERNEL__ release_wq_timer(&rfcomm_timer);#endif DSYS(FNC"RFCOMM disconnected ctrl ch (local) on line [%d]\n", rfcomm->line); rfcomm->dlci[0].state = DISCONNECTED; /* this will take down l2cap aswell */ wake_up_interruptible(&rfcomm_disconnect_wq); } else { /* Data channel */ s32 tmp; rfcomm->dlci[tmp_dlci].state = DISCONNECTED; tmp = get_connected_dlci(rfcomm); if (tmp >= 0) { rfcomm->dlci[tmp].state = DISCONNECTING; send_disc(rfcomm, tmp); } else { D_ERR("Could not find connected DLCI\n"); } } } else if (rfcomm->dlci[tmp_dlci].state == DISCONNECTED) { rfcomm->dlci[tmp_dlci].state = CONNECTED; } else { D_WARN(FNC" Something wrong receiving UA packet\n"); } break; /* Disconnect mode, 'NAK on SABM/DISC' */ case DM: D_CTRL(FNC"DM packet received\n"); rfcomm = ((rfcomm_con*) l2cap->upper_con); rfcomm->dlci[tmp_dlci].state = DISCONNECTED; /* Notify upper tty */ /* bt_disconnect_ind() ? */ break; case DISC: D_CTRL(FNC"DISC packet received\n"); if (crc_check(data, LONG_CRC_CHECK, short_pkt->data[0]) < 0) { break; } rfcomm = ((rfcomm_con*) l2cap->upper_con); /* When the serverchannel is closing the whole connection should be removed */ if (rfcomm->dlci[tmp_dlci].state == DISCONNECTED) { send_dm(rfcomm, tmp_dlci); } else if ((short_pkt->h.addr.server_chn) == 0) { rfcomm->dlci[0].state = DISCONNECTED; DSYS("RFCOMM control ch disconnected (remotely) [line:%d]\n", rfcomm->line); send_ua(rfcomm, 0); } else { rfcomm->dlci[tmp_dlci].state = DISCONNECTED; send_ua(rfcomm, tmp_dlci); D_CTRL("dlci %d was disconnected\n", tmp_dlci); bt_disconnect_ind(CREATE_RFCOMM_ID(rfcomm->line, tmp_dlci));#ifdef __KERNEL__ bt_unregister_rfcomm(rfcomm->line);#endif } break; case UIH: rfcomm = ((rfcomm_con*) l2cap->upper_con); if ((short_pkt->h.length.ea) == 0) { /* Then we cast the rfcomm packet to a long rfcomm packet */ D_REC(FNC"Long UIH packet received\n"); long_pkt = (long_frame*) data; swap_long_frame(long_pkt); uih_len = long_pkt->h.length.bits.len; uih_data_start = long_pkt->h.data; D_REC(FNC"long packet length %d\n", uih_len); if (uih_len > (len - 5)) { D_WARN(FNC", Long packet length doesn't match, setting length to l2cap len - 5\n"); uih_len = len - 5; } } else { D_REC(FNC"Short UIH pkt received\n"); uih_len = short_pkt->h.length.len; uih_data_start = short_pkt->data; if (uih_len > (len - 4)) { D_WARN(FNC", Short packet length doesn't match, setting length to l2cap len - 4\n"); uih_len = len - 4; } } if (GET_PF(short_pkt->h.control)) { D_REC(FNC" %d more credits on dlci:%d...\n", *uih_data_start, tmp_dlci); rfcomm->dlci[tmp_dlci].local_credits += uih_data_start[0];#ifdef __KERNEL__ bt_feedstack();#endif D_REC(FNC"Local_credits:%d\n", rfcomm->dlci[tmp_dlci].local_credits); uih_data_start++; if (uih_len == 0) { break; } /* feed uih data to tty if any */ } if (crc_check(data, SHORT_CRC_CHECK, *(uih_data_start + uih_len)) < 0) { break; } if (short_pkt->h.addr.server_chn == 0) { D_REC(FNC"UIH on serv_channel 0\n"); process_mcc(data, len, rfcomm, !(short_pkt->h.length.ea)); } else { u32 con_id = CREATE_RFCOMM_ID(rfcomm->line, tmp_dlci); if (rfcomm->credit_flow) { --rfcomm->dlci[tmp_dlci].remote_credits; D_CTRL(FNC": Remote credits: %d\n",rfcomm->dlci[tmp_dlci].remote_credits); if (rfcomm->dlci[tmp_dlci].remote_credits < MIN_CREDITS) { u8 newcredits = MAX_CREDITS - rfcomm->dlci[tmp_dlci].remote_credits; rfcomm_send_credits(rfcomm, tmp_dlci, newcredits); rfcomm->dlci[tmp_dlci].remote_credits += newcredits; D_SND(FNC"Remote credits: %d\n",rfcomm->dlci[tmp_dlci].remote_credits); } } bt_receive_top(con_id, uih_data_start, uih_len); } break; default: D_REC(FNC"illegal packet\n"); break; }}/* Copies the tty data into the stack and creates headers and FCS, then the packet is sent to the lower layer L2CAP. The packet must not be larger than the actual MTU for the L2CAP layer. */s32 rfcomm_send_data(u32 con_id, u8 *data, u32 count){ u32 c; u32 total = 0; u8 line; u8 dlci; rfcomm_con *rfcomm; line = GET_LINE(con_id); dlci = GET_RFCOMMDLCI(con_id); if (dlci == 0) { D_ERR(FNC"Not allowed to send data on DLCI 0\n"); return -MSGCODE(MSG_LAYER_RFCOMM, RFCOMM_NO_DATA_ALLOWED); } rfcomm = &rfcomm_con_list[line]; RF_DATA(FNC, data, count); if (rfcomm == NULL) { D_ERR(FNC" ERROR rfcomm_con == NULL\n"); return -MSGCODE(MSG_LAYER_RFCOMM, RFCOMM_NO_CONNECTION); } else if (rfcomm->magic != RFCOMM_MAGIC) { D_ERR(FNC"ERROR magic test failed\n"); return -MSGCODE(MSG_LAYER_RFCOMM, RFCOMM_BAD_MAGIC_NUMBER); } else if(rfcomm->dlci[0].state == FLOW_STOPPED) { D_SND(FNC"Flow stopped on all channels, returning zero\n"); return 0; } else if (rfcomm->dlci[dlci].state == FLOW_STOPPED) { D_SND(FNC"Flow stopped, returning zero\n"); return 0; } /* Check whether there are any data channels connected */ else if (rfcomm->dlci[dlci].state == CONNECTED) { D_SND(FNC"trying to send %d bytes\n", count); while (count) { if (rfcomm->credit_flow &&(rfcomm->dlci[dlci].local_credits <= 0)) { D_SND(FNC"Flow stopped, no credits returning %d\n", total); return total; } /* As long as there are anything to send we try to send it. We split the data into parts not bigger than the current MTU size. */ c = MIN(count, rfcomm->dlci[dlci].mtu); /* FIXME: Optimize c! If we try to send 200 bytes and there are only 150 bytes space in buffer, set c to 150 - sizeof(rfcomm_tx_buf) */ c = send_uih(data, c, rfcomm, dlci); if (c < 0) { return c; /* error */ } else if (c == 0) { return total; } else { if (rfcomm->credit_flow) { --rfcomm->dlci[dlci].local_credits; D_SND(FNC"Local credits:%d\n", rfcomm->dlci[dlci].local_credits); } total += c; data += c; count -= c; } } D_SND(FNC"sent %d bytes\n", total); return total; } else { D_ERR(FNC"DLCI %d not connected\n", dlci);#ifdef __KERNEL__ /* FIXME - should we return an _error_ if rfcomm isn't up yet or only tell user process that 0 bytes was written ??? */ return 0;#else return -1;#endif }}s32rfcomm_flow_stop(u8 line, u8 dlci){ if (rfcomm_con_list[line].credit_flow &&(rfcomm_con_list[line].dlci[dlci].local_credits <= 0)) { return TRUE; } else if (rfcomm_con_list[line].dlci[dlci].state == FLOW_STOPPED) { return TRUE; } else if (rfcomm_con_list[line].dlci[0].state == FLOW_STOPPED) { return TRUE; } else { return FALSE; }}void rfcomm_send_testdata(u32 count, u8 line){ static u8 testdata[1024]; static s32 testdata_init = 0; s32 i; u32 con_id = CREATE_RFCOMM_ID(line, PPP_DLCI); DSYS("rfcomm_send_testdata: sending %d bytes\n",count); if (!testdata_init) { for (i = 0; i < 1024; i++) { testdata[i] = (i%25)+ 65; } } rfcomm_send_data(con_id, testdata, count);}/* Parses a multiplexer control channel packet */void process_mcc(u8* data, u32 len, rfcomm_con *rfcomm, s32 longpkt){ mcc_short_frame *mcc_short_pkt; D_CTRL("process_mcc\n"); if (longpkt) { mcc_short_pkt = (mcc_short_frame*)(((long_frame*)data)->data); } else { mcc_short_pkt = (mcc_short_frame*)(((short_frame*)data)->data); } switch (mcc_short_pkt->h.type.type) { case TEST: if (mcc_short_pkt->h.type.cr == MCC_RSP) { D_CTRL(FNC"Received test command\n"); } else { if ((mcc_short_pkt->h.length.ea) == 0) { mcc_long_frame *mcc_long_pkt; mcc_long_pkt = (mcc_long_frame*) mcc_short_pkt; swap_mcc_long_frame(mcc_long_pkt); rfcomm_test_msg(rfcomm, mcc_long_pkt->value, mcc_long_pkt->h.length.bits.len, MCC_RSP); } else { rfcomm_test_msg(rfcomm, mcc_short_pkt->value,
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -