Pulse Oximeter (NONIN) communicates with mbed via Bluetooth dongle and sends Heart Rate and Oxygen Saturation via GPRS module
Dependencies: C12832 GPS GSM mbed
Fork of myBlueUSB_localfix by
rfcomm/RFCOMM.cpp@3:55a622e3dbb5, 2015-04-14 (annotated)
- Committer:
- samialshorman
- Date:
- Tue Apr 14 21:48:07 2015 +0000
- Revision:
- 3:55a622e3dbb5
- Parent:
- 0:003889bc474f
Nonin (Pulse Oximeter) connected to mbed lpc 1768 by Bluetooth dongle and sends SMS including Heart Rate and Oxygen saturation by GPRS module
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
nobukuma | 0:003889bc474f | 1 | #include "mbed.h" |
nobukuma | 0:003889bc474f | 2 | #include "Utils.h" |
nobukuma | 0:003889bc474f | 3 | #include "RFCOMM.h" |
nobukuma | 0:003889bc474f | 4 | |
nobukuma | 0:003889bc474f | 5 | // Control field values bit no. 1 2 3 4 5 6 7 8 |
nobukuma | 0:003889bc474f | 6 | #define BT_RFCOMM_SABM 0x3F // 1 1 1 1 P/F 1 0 0 |
nobukuma | 0:003889bc474f | 7 | #define BT_RFCOMM_UA 0x73 // 1 1 0 0 P/F 1 1 0 |
nobukuma | 0:003889bc474f | 8 | #define BT_RFCOMM_DM 0x0F // 1 1 1 1 P/F 0 0 0 |
nobukuma | 0:003889bc474f | 9 | #define BT_RFCOMM_DM_PF 0x1F |
nobukuma | 0:003889bc474f | 10 | #define BT_RFCOMM_DISC 0x53 // 1 1 0 0 P/F 0 1 0 |
nobukuma | 0:003889bc474f | 11 | #define BT_RFCOMM_UIH 0xEF // 1 1 1 1 P/F 1 1 1 |
nobukuma | 0:003889bc474f | 12 | #define BT_RFCOMM_UIH_PF 0xFF |
nobukuma | 0:003889bc474f | 13 | |
nobukuma | 0:003889bc474f | 14 | // Multiplexer message types |
nobukuma | 0:003889bc474f | 15 | #define BT_RFCOMM_PN_CMD 0x83 |
nobukuma | 0:003889bc474f | 16 | #define BT_RFCOMM_PN_RSP 0x81 |
nobukuma | 0:003889bc474f | 17 | #define BT_RFCOMM_TEST_CMD 0x23 |
nobukuma | 0:003889bc474f | 18 | #define BT_RFCOMM_TEST_RSP 0x21 |
nobukuma | 0:003889bc474f | 19 | #define BT_RFCOMM_FCON_CMD 0xA3 |
nobukuma | 0:003889bc474f | 20 | #define BT_RFCOMM_FCON_RSP 0xA1 |
nobukuma | 0:003889bc474f | 21 | #define BT_RFCOMM_FCOFF_CMD 0x63 |
nobukuma | 0:003889bc474f | 22 | #define BT_RFCOMM_FCOFF_RSP 0x61 |
nobukuma | 0:003889bc474f | 23 | #define BT_RFCOMM_MSC_CMD 0xE3 |
nobukuma | 0:003889bc474f | 24 | #define BT_RFCOMM_MSC_RSP 0xE1 |
nobukuma | 0:003889bc474f | 25 | #define BT_RFCOMM_RPN_CMD 0x93 |
nobukuma | 0:003889bc474f | 26 | #define BT_RFCOMM_RPN_RSP 0x91 |
nobukuma | 0:003889bc474f | 27 | #define BT_RFCOMM_RLS_CMD 0x53 |
nobukuma | 0:003889bc474f | 28 | #define BT_RFCOMM_RLS_RSP 0x51 |
nobukuma | 0:003889bc474f | 29 | #define BT_RFCOMM_NSC_RSP 0x11 |
nobukuma | 0:003889bc474f | 30 | |
nobukuma | 0:003889bc474f | 31 | // FCS calc |
nobukuma | 0:003889bc474f | 32 | #define BT_RFCOMM_CODE_WORD 0xE0 // pol = x8+x2+x1+1 |
nobukuma | 0:003889bc474f | 33 | #define BT_RFCOMM_CRC_CHECK_LEN 3 |
nobukuma | 0:003889bc474f | 34 | #define BT_RFCOMM_UIHCRC_CHECK_LEN 2 |
nobukuma | 0:003889bc474f | 35 | |
nobukuma | 0:003889bc474f | 36 | #define NR_CREDITS 1 |
nobukuma | 0:003889bc474f | 37 | #define INITIAL_CREDITS 1 //0...7 |
nobukuma | 0:003889bc474f | 38 | |
nobukuma | 0:003889bc474f | 39 | #define DCD (1<<7) //DV data carrier detect |
nobukuma | 0:003889bc474f | 40 | #define RI (1<<6) //IC ring indicator |
nobukuma | 0:003889bc474f | 41 | #define RTS (1<<3) //RTR request to send |
nobukuma | 0:003889bc474f | 42 | #define DSR (1<<2) //RTC data set ready |
nobukuma | 0:003889bc474f | 43 | #define FC (1<<1) //Flow Control |
nobukuma | 0:003889bc474f | 44 | #define EA (1<<0) //extended address (always 1) |
nobukuma | 0:003889bc474f | 45 | #define SIGNALS (DCD | RTS | DSR | EA) |
nobukuma | 0:003889bc474f | 46 | |
nobukuma | 0:003889bc474f | 47 | #define DEBUG 1 |
nobukuma | 0:003889bc474f | 48 | #define TAKE_INITIATIVE |
nobukuma | 0:003889bc474f | 49 | |
nobukuma | 0:003889bc474f | 50 | RFCOMMManager rfcomm_manager; |
nobukuma | 0:003889bc474f | 51 | |
nobukuma | 0:003889bc474f | 52 | //uint8_t rfcomm_out_buffer[1000];//seems a bit big as default max framesize is 127 |
nobukuma | 0:003889bc474f | 53 | //unsigned rfcomm::maxframesize = MAX_FRAME_SIZE; //only initial value |
nobukuma | 0:003889bc474f | 54 | |
nobukuma | 0:003889bc474f | 55 | //these functions are obtained from rfcomm.c on google code |
nobukuma | 0:003889bc474f | 56 | void _bt_rfcomm_send_sabm(unsigned short source_cid, unsigned char initiator, unsigned char channel); |
nobukuma | 0:003889bc474f | 57 | void _bt_rfcomm_send_uih_pn_command(unsigned short source_cid, unsigned char initiator, unsigned char channel, unsigned short max_frame_size); |
nobukuma | 0:003889bc474f | 58 | void _bt_rfcomm_send_uih_msc_cmd(unsigned short source_cid, unsigned char initiator, unsigned char channel, unsigned char signals); |
nobukuma | 0:003889bc474f | 59 | void _bt_rfcomm_send_uih_rpn_cmd(uint16_t source_cid, uint8_t initiator, uint8_t dlci, port_settings *val); |
nobukuma | 0:003889bc474f | 60 | int rfcomm_send_packet(unsigned short source_cid, unsigned char address, unsigned char control, unsigned char credits, const unsigned char *data, unsigned short len); |
nobukuma | 0:003889bc474f | 61 | uint8_t crc8_calc(uint8_t *data, uint16_t len); |
nobukuma | 0:003889bc474f | 62 | |
nobukuma | 0:003889bc474f | 63 | |
nobukuma | 0:003889bc474f | 64 | //find a free socket slot for channel ch |
nobukuma | 0:003889bc474f | 65 | int rfcomm::find_slot(unsigned ch) { |
nobukuma | 0:003889bc474f | 66 | for (int i = 0; i < MAX_RFCOMM_SCKTS; i++) { |
nobukuma | 0:003889bc474f | 67 | if (sckts[i] != 0) { //socket is in use |
nobukuma | 0:003889bc474f | 68 | RFCOMMSocket *s = (RFCOMMSocket*)GetSocketInternal(sckts[i]); |
nobukuma | 0:003889bc474f | 69 | if (s==0) { |
nobukuma | 0:003889bc474f | 70 | printf("find_slot: socket %d not found\n", sckts[i]); |
nobukuma | 0:003889bc474f | 71 | continue; |
nobukuma | 0:003889bc474f | 72 | } |
nobukuma | 0:003889bc474f | 73 | if (s->dlci >> 1 == ch) { |
nobukuma | 0:003889bc474f | 74 | printf("Channel %d is already in use on socket %d\n", ch, sckts[i]); |
nobukuma | 0:003889bc474f | 75 | return -1; |
nobukuma | 0:003889bc474f | 76 | } |
nobukuma | 0:003889bc474f | 77 | } else //slot is free |
nobukuma | 0:003889bc474f | 78 | return i; |
nobukuma | 0:003889bc474f | 79 | } |
nobukuma | 0:003889bc474f | 80 | return -2; //no free slots |
nobukuma | 0:003889bc474f | 81 | } |
nobukuma | 0:003889bc474f | 82 | |
nobukuma | 0:003889bc474f | 83 | //find the rfcomm socket for dlci |
nobukuma | 0:003889bc474f | 84 | RFCOMMSocket* rfcomm::find_socket(unsigned dlci) { |
nobukuma | 0:003889bc474f | 85 | for (int i = 0; i < MAX_RFCOMM_SCKTS; i++) { |
nobukuma | 0:003889bc474f | 86 | if (sckts[i] != 0) { //socket is in use |
nobukuma | 0:003889bc474f | 87 | RFCOMMSocket *s = (RFCOMMSocket*)GetSocketInternal(sckts[i]); |
nobukuma | 0:003889bc474f | 88 | if (s==0) { |
nobukuma | 0:003889bc474f | 89 | printf("find_socket: socket %d not found\n", sckts[i]); |
nobukuma | 0:003889bc474f | 90 | continue; |
nobukuma | 0:003889bc474f | 91 | } |
nobukuma | 0:003889bc474f | 92 | if (s->dlci == dlci) { |
nobukuma | 0:003889bc474f | 93 | return s; |
nobukuma | 0:003889bc474f | 94 | } |
nobukuma | 0:003889bc474f | 95 | } |
nobukuma | 0:003889bc474f | 96 | } |
nobukuma | 0:003889bc474f | 97 | printf("RFCOMMSocket for dlci %d was not found!\n", dlci); |
nobukuma | 0:003889bc474f | 98 | return 0; //socket not found |
nobukuma | 0:003889bc474f | 99 | } |
nobukuma | 0:003889bc474f | 100 | |
nobukuma | 0:003889bc474f | 101 | //send a PN command to all sockets waiting to be opened |
nobukuma | 0:003889bc474f | 102 | void rfcomm::initChannels(int socket) { |
nobukuma | 0:003889bc474f | 103 | for (int i = 0; i < MAX_RFCOMM_SCKTS; i++) { |
nobukuma | 0:003889bc474f | 104 | if (sckts[i] != 0) { //socket is in use |
nobukuma | 0:003889bc474f | 105 | RFCOMMSocket *s = (RFCOMMSocket*)GetSocketInternal(sckts[i]); |
nobukuma | 0:003889bc474f | 106 | if (s==0) { |
nobukuma | 0:003889bc474f | 107 | printf("initChannels: socket %d not found\n", sckts[i]); |
nobukuma | 0:003889bc474f | 108 | continue; |
nobukuma | 0:003889bc474f | 109 | } |
nobukuma | 0:003889bc474f | 110 | if (s->State == SocketState_Opening) { |
nobukuma | 0:003889bc474f | 111 | printf("Sending PN for DLCI %d on socket %d\n", s->dlci, sckts[i]); |
nobukuma | 0:003889bc474f | 112 | _bt_rfcomm_send_uih_pn_command(socket, 1, s->dlci, maxframesize); |
nobukuma | 0:003889bc474f | 113 | s->State = SocketState_L2CAP_Config_wait; |
nobukuma | 0:003889bc474f | 114 | } |
nobukuma | 0:003889bc474f | 115 | } |
nobukuma | 0:003889bc474f | 116 | } |
nobukuma | 0:003889bc474f | 117 | } |
nobukuma | 0:003889bc474f | 118 | |
nobukuma | 0:003889bc474f | 119 | unsigned rfcomm::release_channel(unsigned dlci) { |
nobukuma | 0:003889bc474f | 120 | int n = 0; |
nobukuma | 0:003889bc474f | 121 | for (int i = 0; i < MAX_RFCOMM_SCKTS; i++) { |
nobukuma | 0:003889bc474f | 122 | if (sckts[i] != 0) { //socket is in use |
nobukuma | 0:003889bc474f | 123 | RFCOMMSocket *s = (RFCOMMSocket*)GetSocketInternal(sckts[i]); |
nobukuma | 0:003889bc474f | 124 | if (s==0) { |
nobukuma | 0:003889bc474f | 125 | printf("Release: socket for dlci %d not found\n", dlci); |
nobukuma | 0:003889bc474f | 126 | continue; |
nobukuma | 0:003889bc474f | 127 | } |
nobukuma | 0:003889bc474f | 128 | if (s->dlci == dlci) |
nobukuma | 0:003889bc474f | 129 | sckts[i] = 0; |
nobukuma | 0:003889bc474f | 130 | else |
nobukuma | 0:003889bc474f | 131 | n++; |
nobukuma | 0:003889bc474f | 132 | } |
nobukuma | 0:003889bc474f | 133 | } |
nobukuma | 0:003889bc474f | 134 | return n; |
nobukuma | 0:003889bc474f | 135 | } |
nobukuma | 0:003889bc474f | 136 | |
nobukuma | 0:003889bc474f | 137 | int rfcomm::Send(SocketInternal *sock, const u8* data, int len) {//also see if credits need to be send |
nobukuma | 0:003889bc474f | 138 | RFCOMMSocket *s = (RFCOMMSocket*)sock; |
nobukuma | 0:003889bc474f | 139 | char credits = 0; |
nobukuma | 0:003889bc474f | 140 | char control = BT_RFCOMM_UIH; |
nobukuma | 0:003889bc474f | 141 | if (len + 14 > maxframesize) //hci/l2cap header =8, rfcomm header ~ 6 |
nobukuma | 0:003889bc474f | 142 | printf("Error! packetsize = %d, maxframesize = %d\n", len, maxframesize); |
nobukuma | 0:003889bc474f | 143 | if (s->peer_credits == 0) {//peer is low on credits |
nobukuma | 0:003889bc474f | 144 | credits = NR_CREDITS; |
nobukuma | 0:003889bc474f | 145 | control = BT_RFCOMM_UIH_PF; |
nobukuma | 0:003889bc474f | 146 | s->peer_credits += NR_CREDITS;//so provide some more |
nobukuma | 0:003889bc474f | 147 | } |
nobukuma | 0:003889bc474f | 148 | unsigned char address = (1 << 0) | (initiator << 1) | (s->dlci << 2); |
nobukuma | 0:003889bc474f | 149 | if (s->my_credits) { |
nobukuma | 0:003889bc474f | 150 | s->my_credits--; |
nobukuma | 0:003889bc474f | 151 | return rfcomm_send_packet(_l2cap, address, control, credits, data, len); |
nobukuma | 0:003889bc474f | 152 | } else |
nobukuma | 0:003889bc474f | 153 | return rfcomm_send_packet(_l2cap, address, control, credits, data, 0);//send an empty packet when credits run out |
nobukuma | 0:003889bc474f | 154 | } |
nobukuma | 0:003889bc474f | 155 | |
nobukuma | 0:003889bc474f | 156 | int rfcomm::Close(SocketInternal* sock) { |
nobukuma | 0:003889bc474f | 157 | RFCOMMSocket *s = (RFCOMMSocket*)sock; |
nobukuma | 0:003889bc474f | 158 | int id = s->dlci; |
nobukuma | 0:003889bc474f | 159 | printf("Closing rfcomm dlci %d state=%d\n", id, s->State); |
nobukuma | 0:003889bc474f | 160 | Disconnect(s); |
nobukuma | 0:003889bc474f | 161 | int n = release_channel(id); |
nobukuma | 0:003889bc474f | 162 | printf("%d channels are still open\n", n); |
nobukuma | 0:003889bc474f | 163 | if (n == 0) {//all rfcomm channels are closed |
nobukuma | 0:003889bc474f | 164 | printf("....L2CAP must be closed as well\n"); |
nobukuma | 0:003889bc474f | 165 | rfcomm_send_packet(_l2cap, (1 << 0) | (initiator << 1), BT_RFCOMM_DISC, 0, 0, 0); //close dlci0 |
nobukuma | 0:003889bc474f | 166 | Socket_Close(_l2cap); |
nobukuma | 0:003889bc474f | 167 | _l2cap = 0; //return rfcomm to the pool |
nobukuma | 0:003889bc474f | 168 | } |
nobukuma | 0:003889bc474f | 169 | return 0; |
nobukuma | 0:003889bc474f | 170 | } |
nobukuma | 0:003889bc474f | 171 | |
nobukuma | 0:003889bc474f | 172 | int rfcomm::SetOpt(SocketInternal *sock, int so, int* data, int len) { |
nobukuma | 0:003889bc474f | 173 | switch (so) { |
nobukuma | 0:003889bc474f | 174 | case SO_RECBUF: |
nobukuma | 0:003889bc474f | 175 | case SO_SNDBUF: |
nobukuma | 0:003889bc474f | 176 | maxframesize = *data; //pointless because setting takes effect only before socket is opened (before PN) |
nobukuma | 0:003889bc474f | 177 | break; |
nobukuma | 0:003889bc474f | 178 | default: return NOPROTOOPT; |
nobukuma | 0:003889bc474f | 179 | } |
nobukuma | 0:003889bc474f | 180 | return 0; |
nobukuma | 0:003889bc474f | 181 | } |
nobukuma | 0:003889bc474f | 182 | |
nobukuma | 0:003889bc474f | 183 | int rfcomm::GetOpt(SocketInternal *sock, int so, int* data, int len) { |
nobukuma | 0:003889bc474f | 184 | switch (so) { |
nobukuma | 0:003889bc474f | 185 | case SO_RECBUF: |
nobukuma | 0:003889bc474f | 186 | case SO_SNDBUF: |
nobukuma | 0:003889bc474f | 187 | if (len >= sizeof(int)) |
nobukuma | 0:003889bc474f | 188 | *data = maxframesize; |
nobukuma | 0:003889bc474f | 189 | break; |
nobukuma | 0:003889bc474f | 190 | default: return NOPROTOOPT; |
nobukuma | 0:003889bc474f | 191 | } |
nobukuma | 0:003889bc474f | 192 | return 0; |
nobukuma | 0:003889bc474f | 193 | } |
nobukuma | 0:003889bc474f | 194 | |
nobukuma | 0:003889bc474f | 195 | int rfcomm::Disconnect(RFCOMMSocket *s) { |
nobukuma | 0:003889bc474f | 196 | unsigned char address = (1 << 0) | (initiator << 1) | (s->dlci << 2); |
nobukuma | 0:003889bc474f | 197 | return rfcomm_send_packet(_l2cap, address, BT_RFCOMM_DISC, 0, 0, 0); |
nobukuma | 0:003889bc474f | 198 | } |
nobukuma | 0:003889bc474f | 199 | |
nobukuma | 0:003889bc474f | 200 | //expect this to be called with socket type=SOCKET_RFCOM and addr->psm = channel and addr->bdaddr is the BT addr |
nobukuma | 0:003889bc474f | 201 | //of the device to connect to. |
nobukuma | 0:003889bc474f | 202 | //eg. Socket_Open(SOCKET_RFCOM, rfcommaddr(bdaddr, chan), receiver_func, appl_obj); |
nobukuma | 0:003889bc474f | 203 | int rfcomm::Open(SocketInternal* sock, SocketAddrHdr* addr) { |
nobukuma | 0:003889bc474f | 204 | int ch = ((L2CAPAddr*)addr)->psm;//abused psm for channel ID |
nobukuma | 0:003889bc474f | 205 | RFCOMMSocket *s = (RFCOMMSocket*)sock; |
nobukuma | 0:003889bc474f | 206 | int slot = find_slot(ch); |
nobukuma | 0:003889bc474f | 207 | if (slot < 0) return 0; |
nobukuma | 0:003889bc474f | 208 | sckts[slot] = s->ID; |
nobukuma | 0:003889bc474f | 209 | s->serdevice = this; |
nobukuma | 0:003889bc474f | 210 | s->State = SocketState_Opening; |
nobukuma | 0:003889bc474f | 211 | |
nobukuma | 0:003889bc474f | 212 | if (_l2cap == 0) { //no rfcomm -> l2cap connection yet |
nobukuma | 0:003889bc474f | 213 | printf("Need to open L2CAP channel first before opening RFCOMM channel %d\n", s->dlci); |
nobukuma | 0:003889bc474f | 214 | ((L2CAPAddr*)addr)->psm = L2CAP_PSM_RFCOMM;//open the l2cap channel and the rfcomm_ch channel |
nobukuma | 0:003889bc474f | 215 | initiator = 1; |
nobukuma | 0:003889bc474f | 216 | s->dlci = (ch<<1)|!initiator; |
nobukuma | 0:003889bc474f | 217 | _l2cap = Socket_Open(SOCKET_L2CAP, addr, OnRfCommControl, this);//this is the socket between the RFCOMM and the L2CAP layer |
nobukuma | 0:003889bc474f | 218 | if (_l2cap) |
nobukuma | 0:003889bc474f | 219 | printf("Successfully opened L2CAP channel on socket %d\n", _l2cap); |
nobukuma | 0:003889bc474f | 220 | else { |
nobukuma | 0:003889bc474f | 221 | printf("Opening L2CAP channel failed\n"); |
nobukuma | 0:003889bc474f | 222 | sckts[slot] = 0; |
nobukuma | 0:003889bc474f | 223 | s->State = SocketState_Closed; |
nobukuma | 0:003889bc474f | 224 | return 0; |
nobukuma | 0:003889bc474f | 225 | } |
nobukuma | 0:003889bc474f | 226 | } else {//bypass the l2cap channel creation |
nobukuma | 0:003889bc474f | 227 | s->dlci = (ch<<1)|!initiator; |
nobukuma | 0:003889bc474f | 228 | _bt_rfcomm_send_uih_pn_command(_l2cap, initiator, s->dlci, maxframesize); |
nobukuma | 0:003889bc474f | 229 | s->State = SocketState_L2CAP_Config_wait; |
nobukuma | 0:003889bc474f | 230 | } |
nobukuma | 0:003889bc474f | 231 | return s->ID; //return the application unique socket nr. |
nobukuma | 0:003889bc474f | 232 | } |
nobukuma | 0:003889bc474f | 233 | |
nobukuma | 0:003889bc474f | 234 | int rfcomm::set_remote_port_parameters(unsigned char dlci, port_settings *p) { |
nobukuma | 0:003889bc474f | 235 | _bt_rfcomm_send_uih_rpn_cmd(_l2cap, initiator, dlci, p); |
nobukuma | 0:003889bc474f | 236 | return 0; |
nobukuma | 0:003889bc474f | 237 | } |
nobukuma | 0:003889bc474f | 238 | |
nobukuma | 0:003889bc474f | 239 | //socket is an L2CAP socket and state is the state of the L2CAP socket, not the RFCOMM socket |
nobukuma | 0:003889bc474f | 240 | void rfcomm::OnRfCommControl(int socket, SocketState state, const u8* data, int len, void* userData) { |
nobukuma | 0:003889bc474f | 241 | int packet_processed = 0; |
nobukuma | 0:003889bc474f | 242 | rfcomm* self = (rfcomm*)userData; |
nobukuma | 0:003889bc474f | 243 | const u8 initiator = self->initiator; |
nobukuma | 0:003889bc474f | 244 | printf("\x1B[%dm", 32); //debug: set a different colour |
nobukuma | 0:003889bc474f | 245 | printf("OnRfCommControl sock = %d, state = %d, length = %d\n", socket, state, len); |
nobukuma | 0:003889bc474f | 246 | |
nobukuma | 0:003889bc474f | 247 | if (len == 0) {//client only |
nobukuma | 0:003889bc474f | 248 | if (state==SocketState_Open) {//callback after change to 'open', the rfcomm->l2cap channel is now open |
nobukuma | 0:003889bc474f | 249 | _bt_rfcomm_send_sabm(socket, initiator, 0); //setup the rfcomm control channel dlci==0 |
nobukuma | 0:003889bc474f | 250 | return; |
nobukuma | 0:003889bc474f | 251 | } |
nobukuma | 0:003889bc474f | 252 | return; //or other states to handle, e.g. Closing or Closed |
nobukuma | 0:003889bc474f | 253 | } |
nobukuma | 0:003889bc474f | 254 | //we have data, so parse the header |
nobukuma | 0:003889bc474f | 255 | const u8 &addr = data[0]; |
nobukuma | 0:003889bc474f | 256 | u8 dlci = addr>>2; |
nobukuma | 0:003889bc474f | 257 | const u8 &control = data[1]; |
nobukuma | 0:003889bc474f | 258 | u16 length = data[2]>>1; |
nobukuma | 0:003889bc474f | 259 | const u8 *payload = data+3; |
nobukuma | 0:003889bc474f | 260 | const u8 *pFCS = data+len-1; //expected position of the CRC |
nobukuma | 0:003889bc474f | 261 | if (!(data[2]&1)) { //two byte length |
nobukuma | 0:003889bc474f | 262 | length += ((unsigned)data[3])<<7; |
nobukuma | 0:003889bc474f | 263 | payload++; |
nobukuma | 0:003889bc474f | 264 | } |
nobukuma | 0:003889bc474f | 265 | u8 credits = 0; |
nobukuma | 0:003889bc474f | 266 | if (control == BT_RFCOMM_UIH_PF)//this packet carries credits |
nobukuma | 0:003889bc474f | 267 | credits = *(payload++); |
nobukuma | 0:003889bc474f | 268 | //sanity check |
nobukuma | 0:003889bc474f | 269 | if (payload+length != pFCS) |
nobukuma | 0:003889bc474f | 270 | printf("RFCOMM size mismatch, expected %d payload bytes, got %d\n", length, pFCS-payload); |
nobukuma | 0:003889bc474f | 271 | |
nobukuma | 0:003889bc474f | 272 | if (DEBUG) { |
nobukuma | 0:003889bc474f | 273 | printf("RFCOMM: EA=%d, C/R=%d, D=%d, ch=%d; control=%02X (P/F=%d); length=%d\n", addr&1, (addr>>1)&1, (addr>>2)&1, (addr>>3), control, (control>>4)&1, length); |
nobukuma | 0:003889bc474f | 274 | printfBytes("payload:", payload, length); |
nobukuma | 0:003889bc474f | 275 | } |
nobukuma | 0:003889bc474f | 276 | if (dlci == 0) { //dlci==0 control channel |
nobukuma | 0:003889bc474f | 277 | L2CAPSocket *s = (L2CAPSocket*)GetSocketInternal(socket); |
nobukuma | 0:003889bc474f | 278 | switch (control) { |
nobukuma | 0:003889bc474f | 279 | case BT_RFCOMM_UA:// received 1. message BT_RF_COMM_UA |
nobukuma | 0:003889bc474f | 280 | packet_processed++; |
nobukuma | 0:003889bc474f | 281 | if (s->si.State == SocketState_Closing || s->si.State==SocketState_L2CAP_WaitDisconnect) { //Confirmation of disconnect |
nobukuma | 0:003889bc474f | 282 | printf("Remote side confirmed disconnect for socket %d\n", s->si.ID); |
nobukuma | 0:003889bc474f | 283 | s->si.SetState(SocketState_Closed); |
nobukuma | 0:003889bc474f | 284 | break; |
nobukuma | 0:003889bc474f | 285 | } |
nobukuma | 0:003889bc474f | 286 | printf("Received RFCOMM unnumbered acknowledgement for channel 0 - multiplexer working\n"); |
nobukuma | 0:003889bc474f | 287 | printf("Sending UIH Parameter Negotiation Command from OnRfCommControl\n"); |
nobukuma | 0:003889bc474f | 288 | self->initChannels(socket); |
nobukuma | 0:003889bc474f | 289 | break; |
nobukuma | 0:003889bc474f | 290 | case BT_RFCOMM_UIH:// received UIH Parameter Negotiation Response |
nobukuma | 0:003889bc474f | 291 | switch (payload[0]) { |
nobukuma | 0:003889bc474f | 292 | case BT_RFCOMM_PN_RSP: {//client |
nobukuma | 0:003889bc474f | 293 | packet_processed++; |
nobukuma | 0:003889bc474f | 294 | printf("UIH Parameter Negotiation Response\n"); |
nobukuma | 0:003889bc474f | 295 | printf("Sending SABM #%u\n", payload[2]); |
nobukuma | 0:003889bc474f | 296 | _bt_rfcomm_send_sabm(socket, initiator, payload[2]);//was rfcomm_ch |
nobukuma | 0:003889bc474f | 297 | RFCOMMSocket *r = self->find_socket(payload[2]); |
nobukuma | 0:003889bc474f | 298 | if (r==0) break; |
nobukuma | 0:003889bc474f | 299 | r->my_credits = payload[9]; //initial amount of credits |
nobukuma | 0:003889bc474f | 300 | self->maxframesize = min(self->maxframesize, payload[6] + (payload[7]<<8)); |
nobukuma | 0:003889bc474f | 301 | printf("Max Frame Size = %d, got %d initial credits\n", self->maxframesize, payload[9]); |
nobukuma | 0:003889bc474f | 302 | } |
nobukuma | 0:003889bc474f | 303 | break; |
nobukuma | 0:003889bc474f | 304 | case BT_RFCOMM_PN_CMD: { //remote side sent PN command, mtu and initial credits |
nobukuma | 0:003889bc474f | 305 | packet_processed++; |
nobukuma | 0:003889bc474f | 306 | printf("UIH Parameter Negotiation Indication\n"); |
nobukuma | 0:003889bc474f | 307 | self->maxframesize = min(self->maxframesize, payload[6] + (payload[7]<<8)); |
nobukuma | 0:003889bc474f | 308 | unsigned char cred = payload[9] & 7; |
nobukuma | 0:003889bc474f | 309 | unsigned char _dlci = payload[2]; |
nobukuma | 0:003889bc474f | 310 | |
nobukuma | 0:003889bc474f | 311 | int skt = rfcomm_manager.find_socket(_dlci>>1); |
nobukuma | 0:003889bc474f | 312 | if (skt == 0) { //No-one is listening |
nobukuma | 0:003889bc474f | 313 | printf("No-one is Listening on channel %d\n", _dlci>>1); |
nobukuma | 0:003889bc474f | 314 | rfcomm_send_packet(socket, (_dlci<<2)|1, BT_RFCOMM_DM, 0, 0, 0); |
nobukuma | 0:003889bc474f | 315 | break; |
nobukuma | 0:003889bc474f | 316 | } |
nobukuma | 0:003889bc474f | 317 | RFCOMMSocket *r = (RFCOMMSocket*)GetSocketInternal(skt); |
nobukuma | 0:003889bc474f | 318 | r->my_credits = cred; |
nobukuma | 0:003889bc474f | 319 | r->peer_credits = INITIAL_CREDITS; |
nobukuma | 0:003889bc474f | 320 | unsigned char reply[10]; |
nobukuma | 0:003889bc474f | 321 | memcpy(reply, payload, sizeof(reply)); |
nobukuma | 0:003889bc474f | 322 | reply[0] = BT_RFCOMM_PN_RSP;//[1]=len, [2]=dlci, [4]=priority, [5]=timer(must be 0), [8] retransmissions (must be 0) |
nobukuma | 0:003889bc474f | 323 | reply[3] = payload[3]==0xF0 ? 0xE0 : 0; //support credit based flow control |
nobukuma | 0:003889bc474f | 324 | reply[6] = self->maxframesize; |
nobukuma | 0:003889bc474f | 325 | reply[7] = self->maxframesize>>8; |
nobukuma | 0:003889bc474f | 326 | reply[9] = payload[3]==0xF0 ? r->peer_credits : 0; |
nobukuma | 0:003889bc474f | 327 | printf("Max Frame Size = %d, give %d initial credits\n", self->maxframesize, reply[9]); |
nobukuma | 0:003889bc474f | 328 | rfcomm_send_packet(socket, addr^2, BT_RFCOMM_UIH, 0, reply, sizeof(reply)); |
nobukuma | 0:003889bc474f | 329 | } |
nobukuma | 0:003889bc474f | 330 | break; |
nobukuma | 0:003889bc474f | 331 | case BT_RFCOMM_MSC_CMD: |
nobukuma | 0:003889bc474f | 332 | packet_processed++; |
nobukuma | 0:003889bc474f | 333 | { |
nobukuma | 0:003889bc474f | 334 | printf("Received BT_RFCOMM_MSC_IND\n"); |
nobukuma | 0:003889bc474f | 335 | // fine with this, return the same status and ignore the value, there is no room in the socket to store the value, rfcomm could generate an event |
nobukuma | 0:003889bc474f | 336 | RFCOMMSocket *r = self->find_socket(payload[2]>>2); |
nobukuma | 0:003889bc474f | 337 | if (r==0) break; |
nobukuma | 0:003889bc474f | 338 | unsigned char reply[5]; |
nobukuma | 0:003889bc474f | 339 | memcpy(reply, payload, 5); //keep length, dlci and value(s) |
nobukuma | 0:003889bc474f | 340 | reply[0] = BT_RFCOMM_MSC_RSP; // change command into response |
nobukuma | 0:003889bc474f | 341 | printf("Sending MSC_RSP (%d bytes)\n", length); |
nobukuma | 0:003889bc474f | 342 | rfcomm_send_packet(socket, addr^2, BT_RFCOMM_UIH, 0, reply, (payload[1]>>1)+2); // reply is 4 or 5 bytes |
nobukuma | 0:003889bc474f | 343 | switch (r->State) { |
nobukuma | 0:003889bc474f | 344 | case SocketState_L2CAP_Config_wait: |
nobukuma | 0:003889bc474f | 345 | r->State = SocketState_L2CAP_Config_wait_send; |
nobukuma | 0:003889bc474f | 346 | printf("Sending MSC_CMD\n"); |
nobukuma | 0:003889bc474f | 347 | _bt_rfcomm_send_uih_msc_cmd(socket, initiator, payload[2]>>2, SIGNALS); // ea=1,fc=0,rtc(DSR/DTR)=1,rtr(RTS/CTs)=1,ic(RI)=0,dv(DCD)=1 |
nobukuma | 0:003889bc474f | 348 | r->State = SocketState_L2CAP_Config_wait_rsp; |
nobukuma | 0:003889bc474f | 349 | break; |
nobukuma | 0:003889bc474f | 350 | case SocketState_L2CAP_Config_wait_reqrsp: |
nobukuma | 0:003889bc474f | 351 | r->State = SocketState_L2CAP_Config_wait_rsp; |
nobukuma | 0:003889bc474f | 352 | break; |
nobukuma | 0:003889bc474f | 353 | case SocketState_L2CAP_Config_wait_req: |
nobukuma | 0:003889bc474f | 354 | r->SetState(SocketState_Open); |
nobukuma | 0:003889bc474f | 355 | break; |
nobukuma | 0:003889bc474f | 356 | case SocketState_Open: |
nobukuma | 0:003889bc474f | 357 | //inform port adaptation layer |
nobukuma | 0:003889bc474f | 358 | printf("Received MSC IND in state Open for dlci 0x%02x\n", payload[2]>>2); |
nobukuma | 0:003889bc474f | 359 | break; |
nobukuma | 0:003889bc474f | 360 | default: |
nobukuma | 0:003889bc474f | 361 | printf("Received MSC IND in state %d for dlci 0x%02x\n", r->State, payload[2]>>2); |
nobukuma | 0:003889bc474f | 362 | } |
nobukuma | 0:003889bc474f | 363 | } |
nobukuma | 0:003889bc474f | 364 | break; |
nobukuma | 0:003889bc474f | 365 | case BT_RFCOMM_MSC_RSP: |
nobukuma | 0:003889bc474f | 366 | packet_processed++; |
nobukuma | 0:003889bc474f | 367 | { |
nobukuma | 0:003889bc474f | 368 | RFCOMMSocket *r = self->find_socket(payload[2]>>2); |
nobukuma | 0:003889bc474f | 369 | if (r==0) break; |
nobukuma | 0:003889bc474f | 370 | if (r->State == SocketState_L2CAP_Config_wait_reqrsp) |
nobukuma | 0:003889bc474f | 371 | r->State = SocketState_L2CAP_Config_wait_req; |
nobukuma | 0:003889bc474f | 372 | else if (r->State == SocketState_L2CAP_Config_wait_rsp) |
nobukuma | 0:003889bc474f | 373 | r->SetState(SocketState_Open); |
nobukuma | 0:003889bc474f | 374 | else |
nobukuma | 0:003889bc474f | 375 | printf("Received MSC confirmation in state %d for dlci 0x%02x\n", r->State, payload[2]>>2); |
nobukuma | 0:003889bc474f | 376 | } |
nobukuma | 0:003889bc474f | 377 | break; |
nobukuma | 0:003889bc474f | 378 | case BT_RFCOMM_RPN_CMD: |
nobukuma | 0:003889bc474f | 379 | packet_processed++; |
nobukuma | 0:003889bc474f | 380 | //accept and ignore all settings |
nobukuma | 0:003889bc474f | 381 | unsigned char reply[10]; |
nobukuma | 0:003889bc474f | 382 | memcpy(reply, payload, length); //keep length, dlci and value(s) |
nobukuma | 0:003889bc474f | 383 | reply[0] = BT_RFCOMM_RPN_RSP; // change command into response |
nobukuma | 0:003889bc474f | 384 | printf("Responding to RPN indication (%d bytes)\n", length); |
nobukuma | 0:003889bc474f | 385 | rfcomm_send_packet(socket, addr^2, BT_RFCOMM_UIH, 0, reply, length); |
nobukuma | 0:003889bc474f | 386 | break; |
nobukuma | 0:003889bc474f | 387 | case BT_RFCOMM_RPN_RSP: |
nobukuma | 0:003889bc474f | 388 | packet_processed++; |
nobukuma | 0:003889bc474f | 389 | //ignore a response |
nobukuma | 0:003889bc474f | 390 | printf("Received RPN confirmation\n"); |
nobukuma | 0:003889bc474f | 391 | break; |
nobukuma | 0:003889bc474f | 392 | default: |
nobukuma | 0:003889bc474f | 393 | printf("Unsupported multiplexer frame, type=%02XH\n", data[3]); |
nobukuma | 0:003889bc474f | 394 | } |
nobukuma | 0:003889bc474f | 395 | break; |
nobukuma | 0:003889bc474f | 396 | case BT_RFCOMM_DISC: |
nobukuma | 0:003889bc474f | 397 | printf("Remote site actively disconnected from DLCI0\n"); |
nobukuma | 0:003889bc474f | 398 | rfcomm_send_packet(socket, addr|2, BT_RFCOMM_UA, 0, 0, 0);//confirm disconnection |
nobukuma | 0:003889bc474f | 399 | //intentional fall through |
nobukuma | 0:003889bc474f | 400 | case BT_RFCOMM_DM: |
nobukuma | 0:003889bc474f | 401 | packet_processed++; |
nobukuma | 0:003889bc474f | 402 | printf("Remote side refused connection on DLCI0\n"); |
nobukuma | 0:003889bc474f | 403 | self->_l2cap = Socket_Close(socket); |
nobukuma | 0:003889bc474f | 404 | break; |
nobukuma | 0:003889bc474f | 405 | case BT_RFCOMM_SABM: |
nobukuma | 0:003889bc474f | 406 | packet_processed++; |
nobukuma | 0:003889bc474f | 407 | printf("Remote site is seeking connection on DLCI0\n"); //respond with UA |
nobukuma | 0:003889bc474f | 408 | rfcomm_send_packet(socket, addr|2, BT_RFCOMM_UA, 0, 0, 0);//confirm connection |
nobukuma | 0:003889bc474f | 409 | break; |
nobukuma | 0:003889bc474f | 410 | default: |
nobukuma | 0:003889bc474f | 411 | printf("Unexpected RFCOMM cmd %02XH for address %02XH, length=%d\n", control, addr, length); |
nobukuma | 0:003889bc474f | 412 | } |
nobukuma | 0:003889bc474f | 413 | } else { //data is for one of the serial sockets |
nobukuma | 0:003889bc474f | 414 | RFCOMMSocket *s = 0; |
nobukuma | 0:003889bc474f | 415 | if (control == BT_RFCOMM_SABM) { //req. for conn on this dlci |
nobukuma | 0:003889bc474f | 416 | //cannot call self->rfcomm::find_socket because it has no socket yet |
nobukuma | 0:003889bc474f | 417 | int skt = rfcomm_manager.find_socket(dlci>>1); //find the server socket |
nobukuma | 0:003889bc474f | 418 | s = (RFCOMMSocket*)GetSocketInternal(skt);//the listening socket |
nobukuma | 0:003889bc474f | 419 | if (s) {//move the listening socket to the appropriate rfcomm |
nobukuma | 0:003889bc474f | 420 | int slot = self->find_slot(dlci>>1); |
nobukuma | 0:003889bc474f | 421 | if (slot < 0) { |
nobukuma | 0:003889bc474f | 422 | printf("RFCOMM Channel %d is not free on rfcomm with l2cap socket %d\n", dlci>>1, self->_l2cap); |
nobukuma | 0:003889bc474f | 423 | return; |
nobukuma | 0:003889bc474f | 424 | } |
nobukuma | 0:003889bc474f | 425 | s->serdevice = self; //bind the socket to this refcomm entity |
nobukuma | 0:003889bc474f | 426 | self->sckts[slot] = skt; |
nobukuma | 0:003889bc474f | 427 | rfcomm_manager.remove_socket(skt); |
nobukuma | 0:003889bc474f | 428 | } else { |
nobukuma | 0:003889bc474f | 429 | printf("Couln't find a listening socket for dlci %d\n", dlci); |
nobukuma | 0:003889bc474f | 430 | return; |
nobukuma | 0:003889bc474f | 431 | } |
nobukuma | 0:003889bc474f | 432 | } else |
nobukuma | 0:003889bc474f | 433 | s = self->find_socket(dlci); |
nobukuma | 0:003889bc474f | 434 | if (s==0){ |
nobukuma | 0:003889bc474f | 435 | printf("DLCI %d not found\n", dlci); |
nobukuma | 0:003889bc474f | 436 | return; |
nobukuma | 0:003889bc474f | 437 | } |
nobukuma | 0:003889bc474f | 438 | switch (control) { |
nobukuma | 0:003889bc474f | 439 | case BT_RFCOMM_SABM: |
nobukuma | 0:003889bc474f | 440 | packet_processed++; |
nobukuma | 0:003889bc474f | 441 | rfcomm_send_packet(socket, addr|2, BT_RFCOMM_UA, 0, 0, 0);//confirm connection |
nobukuma | 0:003889bc474f | 442 | s->State = SocketState_L2CAP_Config_wait; //wait for msc cmd |
nobukuma | 0:003889bc474f | 443 | #ifdef TAKE_INITIATIVE |
nobukuma | 0:003889bc474f | 444 | printf("Sending MSC_CMD\n"); |
nobukuma | 0:003889bc474f | 445 | _bt_rfcomm_send_uih_msc_cmd(socket, initiator, dlci, 0x8d); // ea=1,fc=0,rtc(DSR/DTR)=1,rtr(RTS/CTs)=1,ic(RI)=0,dv(DCD)=1 |
nobukuma | 0:003889bc474f | 446 | s->State = SocketState_L2CAP_Config_wait_reqrsp; |
nobukuma | 0:003889bc474f | 447 | #endif |
nobukuma | 0:003889bc474f | 448 | break; |
nobukuma | 0:003889bc474f | 449 | case BT_RFCOMM_UA:// received 2. message BT_RF_COMM_UA |
nobukuma | 0:003889bc474f | 450 | packet_processed++; |
nobukuma | 0:003889bc474f | 451 | if (s->State == SocketState_Closing) { //Confirmation of disconnect |
nobukuma | 0:003889bc474f | 452 | printf("Remote side confirmed disconnect for socket %d\n", s->ID); |
nobukuma | 0:003889bc474f | 453 | s->SetState(SocketState_Closed); |
nobukuma | 0:003889bc474f | 454 | break; |
nobukuma | 0:003889bc474f | 455 | } |
nobukuma | 0:003889bc474f | 456 | printf("Received RFCOMM unnumbered acknowledgement for dlci %u - channel opened\n", dlci); |
nobukuma | 0:003889bc474f | 457 | if (s->State == SocketState_L2CAP_Config_wait) { |
nobukuma | 0:003889bc474f | 458 | printf("Sending MSC CMD\n"); |
nobukuma | 0:003889bc474f | 459 | _bt_rfcomm_send_uih_msc_cmd(socket, initiator, dlci, 0x8d); // ea=1,fc=0,rtc(DSR/DTR)=1,rtr(RTS/CTs)=1,ic(RI)=0,dv(DCD)=1 |
nobukuma | 0:003889bc474f | 460 | s->State = SocketState_L2CAP_Config_wait_reqrsp; |
nobukuma | 0:003889bc474f | 461 | } |
nobukuma | 0:003889bc474f | 462 | break; |
nobukuma | 0:003889bc474f | 463 | case BT_RFCOMM_UIH_PF: //user data with credits |
nobukuma | 0:003889bc474f | 464 | printf("Got %u credits\n", credits); |
nobukuma | 0:003889bc474f | 465 | s->my_credits += credits; |
nobukuma | 0:003889bc474f | 466 | //intentional fall-through |
nobukuma | 0:003889bc474f | 467 | case BT_RFCOMM_UIH: //user data |
nobukuma | 0:003889bc474f | 468 | packet_processed++; |
nobukuma | 0:003889bc474f | 469 | if (DEBUG) { |
nobukuma | 0:003889bc474f | 470 | printf("RX: address %02x, control %02x: ", addr, control); |
nobukuma | 0:003889bc474f | 471 | printHex( payload, length); |
nobukuma | 0:003889bc474f | 472 | } |
nobukuma | 0:003889bc474f | 473 | if (length) { |
nobukuma | 0:003889bc474f | 474 | s->peer_credits--; |
nobukuma | 0:003889bc474f | 475 | s->Recv(payload, length); |
nobukuma | 0:003889bc474f | 476 | } else |
nobukuma | 0:003889bc474f | 477 | printf("Received empty packet\n"); |
nobukuma | 0:003889bc474f | 478 | if (length == 0 || s->peer_credits == 0) {//send credits when peer runs out |
nobukuma | 0:003889bc474f | 479 | //char ini = !(dlci & 1); |
nobukuma | 0:003889bc474f | 480 | unsigned char address = (1 << 0) | (initiator << 1) | (dlci << 2); |
nobukuma | 0:003889bc474f | 481 | printf("send %d credits to dlci %d\n", NR_CREDITS, addr>>2); |
nobukuma | 0:003889bc474f | 482 | rfcomm_send_packet(socket, address, BT_RFCOMM_UIH_PF, NR_CREDITS, NULL, 0); |
nobukuma | 0:003889bc474f | 483 | s->peer_credits += NR_CREDITS; |
nobukuma | 0:003889bc474f | 484 | } |
nobukuma | 0:003889bc474f | 485 | break; |
nobukuma | 0:003889bc474f | 486 | case BT_RFCOMM_DISC: |
nobukuma | 0:003889bc474f | 487 | packet_processed++; |
nobukuma | 0:003889bc474f | 488 | printf("Received DISC IND for dlci %d\n", dlci); |
nobukuma | 0:003889bc474f | 489 | rfcomm_send_packet(socket, addr, BT_RFCOMM_UA, 0, 0, 0);//confirm disconnection |
nobukuma | 0:003889bc474f | 490 | s->SetState(SocketState_Closed); |
nobukuma | 0:003889bc474f | 491 | break; |
nobukuma | 0:003889bc474f | 492 | case BT_RFCOMM_DM: |
nobukuma | 0:003889bc474f | 493 | case BT_RFCOMM_DM_PF: |
nobukuma | 0:003889bc474f | 494 | packet_processed++; |
nobukuma | 0:003889bc474f | 495 | printf("Received DM IND (%02X) for dlci %d\n", control, dlci); |
nobukuma | 0:003889bc474f | 496 | s->SetState(SocketState_Closed); |
nobukuma | 0:003889bc474f | 497 | break; |
nobukuma | 0:003889bc474f | 498 | default: |
nobukuma | 0:003889bc474f | 499 | printf("Unexpected RFCOMM cmd %02XH for address %02XH, length=%d\n", control, addr, length); |
nobukuma | 0:003889bc474f | 500 | } |
nobukuma | 0:003889bc474f | 501 | } |
nobukuma | 0:003889bc474f | 502 | |
nobukuma | 0:003889bc474f | 503 | if (!packet_processed) { |
nobukuma | 0:003889bc474f | 504 | // just dump data for now |
nobukuma | 0:003889bc474f | 505 | printf("??: address %02x, control %02x: ", data[0], data[1]); |
nobukuma | 0:003889bc474f | 506 | printHex( data, len ); |
nobukuma | 0:003889bc474f | 507 | } |
nobukuma | 0:003889bc474f | 508 | printf("\x1B[%dm", 0);//reset terminal colour |
nobukuma | 0:003889bc474f | 509 | } |
nobukuma | 0:003889bc474f | 510 | |
nobukuma | 0:003889bc474f | 511 | //should make the functions below member functions |
nobukuma | 0:003889bc474f | 512 | |
nobukuma | 0:003889bc474f | 513 | /** |
nobukuma | 0:003889bc474f | 514 | * @param credits - only used for RFCOMM flow control in UIH wiht P/F = 1 |
nobukuma | 0:003889bc474f | 515 | */ |
nobukuma | 0:003889bc474f | 516 | /* Questionable optimisation. When OFFSET==8 a buffer is (dynamically) allocated that provides space for the lower |
nobukuma | 0:003889bc474f | 517 | layer headers, this reduces copying to, and allocation of, a lower layer buffer. However, all other layers using |
nobukuma | 0:003889bc474f | 518 | HCI/L2CAP must do the same. |
nobukuma | 0:003889bc474f | 519 | */ |
nobukuma | 0:003889bc474f | 520 | #define OFFSET 8 |
nobukuma | 0:003889bc474f | 521 | |
nobukuma | 0:003889bc474f | 522 | int rfcomm_send_packet(uint16_t source_cid, uint8_t address, uint8_t control, uint8_t credits, const uint8_t *data, uint16_t len) { |
nobukuma | 0:003889bc474f | 523 | |
nobukuma | 0:003889bc474f | 524 | uint16_t pos = OFFSET; |
nobukuma | 0:003889bc474f | 525 | uint8_t crc_fields = 3; |
nobukuma | 0:003889bc474f | 526 | |
nobukuma | 0:003889bc474f | 527 | #if OFFSET == 8 |
nobukuma | 0:003889bc474f | 528 | uint8_t* rfcomm_out_buffer = new uint8_t[OFFSET+len+6]; |
nobukuma | 0:003889bc474f | 529 | #else |
nobukuma | 0:003889bc474f | 530 | static uint8_t rfcomm_out_buffer[MAXFRAMESIZE+6];//seems a bit big as default max framesize is 127 |
nobukuma | 0:003889bc474f | 531 | #endif |
nobukuma | 0:003889bc474f | 532 | rfcomm_out_buffer[pos++] = address; |
nobukuma | 0:003889bc474f | 533 | rfcomm_out_buffer[pos++] = control; |
nobukuma | 0:003889bc474f | 534 | |
nobukuma | 0:003889bc474f | 535 | // length field can be 1 or 2 octets |
nobukuma | 0:003889bc474f | 536 | if (len < 128) { |
nobukuma | 0:003889bc474f | 537 | rfcomm_out_buffer[pos++] = (len << 1)| 1; // bits 0-6 |
nobukuma | 0:003889bc474f | 538 | } else { |
nobukuma | 0:003889bc474f | 539 | rfcomm_out_buffer[pos++] = (len & 0x7f) << 1; // bits 0-6 |
nobukuma | 0:003889bc474f | 540 | rfcomm_out_buffer[pos++] = len >> 7; // bits 7-14 |
nobukuma | 0:003889bc474f | 541 | crc_fields++; |
nobukuma | 0:003889bc474f | 542 | } |
nobukuma | 0:003889bc474f | 543 | |
nobukuma | 0:003889bc474f | 544 | // add credits for UIH frames when PF bit is set |
nobukuma | 0:003889bc474f | 545 | if (control == BT_RFCOMM_UIH_PF) { |
nobukuma | 0:003889bc474f | 546 | rfcomm_out_buffer[pos++] = credits; |
nobukuma | 0:003889bc474f | 547 | } |
nobukuma | 0:003889bc474f | 548 | |
nobukuma | 0:003889bc474f | 549 | // copy actual data |
nobukuma | 0:003889bc474f | 550 | memcpy(&rfcomm_out_buffer[pos], data, len); |
nobukuma | 0:003889bc474f | 551 | pos += len; |
nobukuma | 0:003889bc474f | 552 | |
nobukuma | 0:003889bc474f | 553 | // UIH frames only calc FCS over address + control (5.1.1) |
nobukuma | 0:003889bc474f | 554 | if ((control & 0xef) == BT_RFCOMM_UIH) { |
nobukuma | 0:003889bc474f | 555 | crc_fields = 2; |
nobukuma | 0:003889bc474f | 556 | } |
nobukuma | 0:003889bc474f | 557 | rfcomm_out_buffer[pos++] = crc8_calc(rfcomm_out_buffer+OFFSET, crc_fields); // calc fcs |
nobukuma | 0:003889bc474f | 558 | int retval = Socket_Send( source_cid, rfcomm_out_buffer, pos); |
nobukuma | 0:003889bc474f | 559 | #if OFFSET == 8 |
nobukuma | 0:003889bc474f | 560 | delete[] rfcomm_out_buffer; |
nobukuma | 0:003889bc474f | 561 | #endif |
nobukuma | 0:003889bc474f | 562 | if (retval <= 0) |
nobukuma | 0:003889bc474f | 563 | return retval; |
nobukuma | 0:003889bc474f | 564 | return len;//return the size of the payload |
nobukuma | 0:003889bc474f | 565 | } |
nobukuma | 0:003889bc474f | 566 | |
nobukuma | 0:003889bc474f | 567 | void _bt_rfcomm_send_sabm(uint16_t source_cid, uint8_t initiator, uint8_t dlci) { |
nobukuma | 0:003889bc474f | 568 | uint8_t address = (1 << 0) | (initiator << 1) | (dlci << 2); |
nobukuma | 0:003889bc474f | 569 | rfcomm_send_packet(source_cid, address, BT_RFCOMM_SABM, 0, NULL, 0); |
nobukuma | 0:003889bc474f | 570 | } |
nobukuma | 0:003889bc474f | 571 | |
nobukuma | 0:003889bc474f | 572 | void _bt_rfcomm_send_uih_pn_command(uint16_t source_cid, uint8_t initiator, uint8_t dlci, uint16_t max_frame_size) { |
nobukuma | 0:003889bc474f | 573 | uint8_t payload[10]; |
nobukuma | 0:003889bc474f | 574 | uint8_t address = (1 << 0) | (initiator << 1); // EA and C/R bit set - always server channel 0 |
nobukuma | 0:003889bc474f | 575 | uint8_t pos = 0; |
nobukuma | 0:003889bc474f | 576 | payload[pos++] = BT_RFCOMM_PN_CMD; |
nobukuma | 0:003889bc474f | 577 | payload[pos++] = 8 << 1 | 1; // len |
nobukuma | 0:003889bc474f | 578 | payload[pos++] = dlci; |
nobukuma | 0:003889bc474f | 579 | payload[pos++] = 0xf0; // pre defined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM |
nobukuma | 0:003889bc474f | 580 | payload[pos++] = 0; // priority |
nobukuma | 0:003889bc474f | 581 | payload[pos++] = 0; // max 60 seconds ack |
nobukuma | 0:003889bc474f | 582 | payload[pos++] = max_frame_size & 0xff; // max framesize low |
nobukuma | 0:003889bc474f | 583 | payload[pos++] = max_frame_size >> 8; // max framesize high |
nobukuma | 0:003889bc474f | 584 | payload[pos++] = 0x00; // number of retransmissions |
nobukuma | 0:003889bc474f | 585 | payload[pos++] = INITIAL_CREDITS; // unused error recovery window |
nobukuma | 0:003889bc474f | 586 | rfcomm_send_packet(source_cid, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); |
nobukuma | 0:003889bc474f | 587 | } |
nobukuma | 0:003889bc474f | 588 | |
nobukuma | 0:003889bc474f | 589 | void _bt_rfcomm_send_uih_data(uint16_t source_cid, uint8_t initiator, uint8_t channel, uint8_t *data, uint16_t len) { |
nobukuma | 0:003889bc474f | 590 | uint8_t address = (1 << 0) | (initiator << 1) | (!initiator << 2) | (channel << 3); |
nobukuma | 0:003889bc474f | 591 | rfcomm_send_packet(source_cid, address, BT_RFCOMM_UIH, 0, data, len); |
nobukuma | 0:003889bc474f | 592 | } |
nobukuma | 0:003889bc474f | 593 | |
nobukuma | 0:003889bc474f | 594 | void _bt_rfcomm_send_uih_msc_cmd(uint16_t source_cid, uint8_t initiator, uint8_t dlci, uint8_t signals) { |
nobukuma | 0:003889bc474f | 595 | uint8_t address = (1 << 0) | (initiator << 1); // EA and C/R bit set - always server channel 0 |
nobukuma | 0:003889bc474f | 596 | uint8_t payload[5]; |
nobukuma | 0:003889bc474f | 597 | uint8_t pos = 0; |
nobukuma | 0:003889bc474f | 598 | payload[pos++] = BT_RFCOMM_MSC_CMD; |
nobukuma | 0:003889bc474f | 599 | payload[pos++] = 2 << 1 | 1; // len, should be adapted when adding break byte |
nobukuma | 0:003889bc474f | 600 | payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); //C/R is always 1 |
nobukuma | 0:003889bc474f | 601 | payload[pos++] = signals; |
nobukuma | 0:003889bc474f | 602 | // payload[pos++] = brk; |
nobukuma | 0:003889bc474f | 603 | rfcomm_send_packet(source_cid, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); |
nobukuma | 0:003889bc474f | 604 | } |
nobukuma | 0:003889bc474f | 605 | |
nobukuma | 0:003889bc474f | 606 | void _bt_rfcomm_send_uih_rpn_cmd(uint16_t source_cid, uint8_t initiator, uint8_t dlci, port_settings *val) { |
nobukuma | 0:003889bc474f | 607 | uint8_t address = (1 << 0) | (initiator << 1); // EA and C/R bit set - always server channel 0 |
nobukuma | 0:003889bc474f | 608 | uint8_t payload[sizeof(port_settings)+3]; |
nobukuma | 0:003889bc474f | 609 | uint8_t pos = 0; |
nobukuma | 0:003889bc474f | 610 | payload[pos++] = BT_RFCOMM_RPN_CMD;//type |
nobukuma | 0:003889bc474f | 611 | if (val) { |
nobukuma | 0:003889bc474f | 612 | payload[pos++] = ((1+sizeof(port_settings)) << 1) | 1; // len |
nobukuma | 0:003889bc474f | 613 | payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); |
nobukuma | 0:003889bc474f | 614 | memcpy(payload+pos, (char*)val, sizeof(port_settings)); |
nobukuma | 0:003889bc474f | 615 | pos += sizeof(port_settings); |
nobukuma | 0:003889bc474f | 616 | } else { |
nobukuma | 0:003889bc474f | 617 | payload[pos++] = (1 << 1) | 1; // len |
nobukuma | 0:003889bc474f | 618 | payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); |
nobukuma | 0:003889bc474f | 619 | } |
nobukuma | 0:003889bc474f | 620 | rfcomm_send_packet(source_cid, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); |
nobukuma | 0:003889bc474f | 621 | } |
nobukuma | 0:003889bc474f | 622 | |
nobukuma | 0:003889bc474f | 623 | int set_remote_port_parameters(int socket, port_settings *p) { |
nobukuma | 0:003889bc474f | 624 | RFCOMMSocket* si = (RFCOMMSocket*)GetSocketInternal(socket);//gets the RFCOMM socket |
nobukuma | 0:003889bc474f | 625 | if (!si || si->ID != socket) |
nobukuma | 0:003889bc474f | 626 | return ERR_SOCKET_NOT_FOUND; |
nobukuma | 0:003889bc474f | 627 | return si->serdevice->set_remote_port_parameters(si->dlci, p); |
nobukuma | 0:003889bc474f | 628 | } |
nobukuma | 0:003889bc474f | 629 | |
nobukuma | 0:003889bc474f | 630 | |
nobukuma | 0:003889bc474f | 631 | /* |
nobukuma | 0:003889bc474f | 632 | * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0. |
nobukuma | 0:003889bc474f | 633 | */ |
nobukuma | 0:003889bc474f | 634 | static const uint8_t crc8table[256] = { /* reversed, 8-bit, poly=0x07 */ |
nobukuma | 0:003889bc474f | 635 | 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, |
nobukuma | 0:003889bc474f | 636 | 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, |
nobukuma | 0:003889bc474f | 637 | 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, |
nobukuma | 0:003889bc474f | 638 | 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, |
nobukuma | 0:003889bc474f | 639 | 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, |
nobukuma | 0:003889bc474f | 640 | 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, |
nobukuma | 0:003889bc474f | 641 | 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, |
nobukuma | 0:003889bc474f | 642 | 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, |
nobukuma | 0:003889bc474f | 643 | 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, |
nobukuma | 0:003889bc474f | 644 | 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, |
nobukuma | 0:003889bc474f | 645 | 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, |
nobukuma | 0:003889bc474f | 646 | 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, |
nobukuma | 0:003889bc474f | 647 | 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, |
nobukuma | 0:003889bc474f | 648 | 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, |
nobukuma | 0:003889bc474f | 649 | 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, |
nobukuma | 0:003889bc474f | 650 | 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF |
nobukuma | 0:003889bc474f | 651 | }; |
nobukuma | 0:003889bc474f | 652 | |
nobukuma | 0:003889bc474f | 653 | #define CRC8_INIT 0xFF // Initial FCS value |
nobukuma | 0:003889bc474f | 654 | #define CRC8_OK 0xCF // Good final FCS value |
nobukuma | 0:003889bc474f | 655 | /*-----------------------------------------------------------------------------------*/ |
nobukuma | 0:003889bc474f | 656 | uint8_t crc8(uint8_t *data, uint16_t len) { |
nobukuma | 0:003889bc474f | 657 | uint16_t count; |
nobukuma | 0:003889bc474f | 658 | uint8_t crc = CRC8_INIT; |
nobukuma | 0:003889bc474f | 659 | for (count = 0; count < len; count++) |
nobukuma | 0:003889bc474f | 660 | crc = crc8table[crc ^ data[count]]; |
nobukuma | 0:003889bc474f | 661 | return crc; |
nobukuma | 0:003889bc474f | 662 | } |
nobukuma | 0:003889bc474f | 663 | |
nobukuma | 0:003889bc474f | 664 | /*-----------------------------------------------------------------------------------*/ |
nobukuma | 0:003889bc474f | 665 | uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum) { |
nobukuma | 0:003889bc474f | 666 | uint8_t crc; |
nobukuma | 0:003889bc474f | 667 | |
nobukuma | 0:003889bc474f | 668 | crc = crc8(data, len); |
nobukuma | 0:003889bc474f | 669 | |
nobukuma | 0:003889bc474f | 670 | crc = crc8table[crc ^ check_sum]; |
nobukuma | 0:003889bc474f | 671 | if (crc == CRC8_OK) |
nobukuma | 0:003889bc474f | 672 | return 0; /* Valid */ |
nobukuma | 0:003889bc474f | 673 | else |
nobukuma | 0:003889bc474f | 674 | return 1; /* Failed */ |
nobukuma | 0:003889bc474f | 675 | |
nobukuma | 0:003889bc474f | 676 | } |
nobukuma | 0:003889bc474f | 677 | |
nobukuma | 0:003889bc474f | 678 | /*-----------------------------------------------------------------------------------*/ |
nobukuma | 0:003889bc474f | 679 | uint8_t crc8_calc(uint8_t *data, uint16_t len) { |
nobukuma | 0:003889bc474f | 680 | /* Ones complement */ |
nobukuma | 0:003889bc474f | 681 | return 0xFF - crc8(data, len); |
nobukuma | 0:003889bc474f | 682 | } |