void sngisdn_trace_q931(char* str, uint8_t* data, uint32_t data_len) { uint32_t str_len; uint8_t prot_disc, callRefFlag; uint16_t lenCallRef, c, i; uint8_t current_codeset = 0; str_len = 0; /* Decode Protocol Discrimator */ prot_disc = (uint8_t)data[0]; str_len += sprintf(&str[str_len], " Prot Disc:%s (0x%02x)\n", get_code_2_str(prot_disc, dcodQ931ProtDiscTable), prot_disc); /* Decode Call Reference */ lenCallRef = (uint8_t) (data[1] & 0x0F); str_len += sprintf(&str[str_len], " Call Ref:"); c=2; callRefFlag = get_bits(data[c], 8,8); for(i=0; i<(2*lenCallRef);i++) { if(i==0) { str_len += sprintf(&str[str_len], "%s%s", get_code_2_str((uint8_t)(data[c] & 0x70), dcodQ931CallRefHiTable), get_code_2_str((uint8_t)(data[c] & 0x0F), dcodQ931CallRefLoTable)); } else { str_len += sprintf(&str[str_len], "%s%s", get_code_2_str((uint8_t)(data[c] & 0xF0), dcodQ931CallRefHiTable), get_code_2_str((uint8_t)(data[c] & 0x0F), dcodQ931CallRefLoTable)); } i=i+1; c=c+1; } str_len += sprintf(&str[str_len], " (%s side)\n", callRefFlag?"Destination":"Originating"); /* Decode message type */ str_len+= sprintf(&str[str_len], " Type:%s (0x%x)\n", get_code_2_str((int)(data[2+lenCallRef] & 0xFF), dcodQ931MsgTypeTable), (int)(data[2+lenCallRef] & 0xFF)); /* go through rest of data and look for important info */ for(i=3+lenCallRef; i < data_len; i++) { switch (data[i] & 0xF8) { case Q931_LOCKING_SHIFT: current_codeset = (data[i] & 0x7); str_len+= sprintf(&str[str_len], "Codeset shift to %d (locking)\n", current_codeset); continue; case Q931_NON_LOCKING_SHIFT: current_codeset = (data[i] & 0x7); str_len+= sprintf(&str[str_len], "Codeset shift to %d (non-locking)\n", current_codeset); continue; } i+= sngisdn_decode_ie(str, &str_len, current_codeset, data, i); } print_hex_dump(str, &str_len, (uint8_t*) data, 0, data_len); return; }
static ftdm_status_t sngisdn_map_call(sngisdn_span_data_t *signal_data, sngisdn_frame_info_t frame_info, ftdm_channel_t **found) { sngisdn_chan_data_t *sngisdn_info; ftdm_channel_t *ftdmchan = NULL; ftdm_iterator_t *chaniter = NULL; ftdm_iterator_t *curr = NULL; ftdm_status_t status = FTDM_FAIL; uint8_t outbound_call = 0; if ((!frame_info.call_ref_flag && frame_info.dir == FTDM_TRACE_DIR_OUTGOING) || (frame_info.call_ref_flag && frame_info.dir == FTDM_TRACE_DIR_INCOMING)) { /* If this is an outgoing frame and this frame was sent by the originating side of the call (frame_info.call_ref_flag == 0), then this is an outbound call */ outbound_call = 1; } else { outbound_call = 0; } switch (frame_info.msgtype) { case PROT_Q931_MSGTYPE_SETUP: /* We initiated this outgoing call try to match the call reference with our internal call-id*/ if (!frame_info.bchan_no) { /* We were not able to determine the bchannel on this call, so we will not be able to match it anyway */ status = FTDM_FAIL; } chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL); for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) { ftdmchan = (ftdm_channel_t*)(ftdm_iterator_current(curr)); ftdm_channel_lock(ftdmchan); if (outbound_call) { sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data; if (sngisdn_info && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { if (ftdmchan->caller_data.call_id && ftdmchan->physical_chan_id == frame_info.bchan_no) { sngisdn_info->call_ref = frame_info.call_ref; *found = ftdmchan; status = FTDM_SUCCESS; } } } else { if (ftdmchan->physical_chan_id == frame_info.bchan_no) { *found = ftdmchan; status = FTDM_SUCCESS; } } ftdm_channel_unlock(ftdmchan); } ftdm_iterator_free(chaniter); break; case PROT_Q931_MSGTYPE_ALERTING: case PROT_Q931_MSGTYPE_PROCEEDING: case PROT_Q931_MSGTYPE_PROGRESS: case PROT_Q931_MSGTYPE_CONNECT: case PROT_Q931_MSGTYPE_SETUP_ACK: case PROT_Q931_MSGTYPE_CONNECT_ACK: case PROT_Q931_MSGTYPE_USER_INFO: case PROT_Q931_MSGTYPE_DISCONNECT: case PROT_Q931_MSGTYPE_RELEASE: case PROT_Q931_MSGTYPE_RELEASE_ACK: case PROT_Q931_MSGTYPE_RELEASE_COMPLETE: case PROT_Q931_MSGTYPE_FACILITY: case PROT_Q931_MSGTYPE_NOTIFY: case PROT_Q931_MSGTYPE_STATUS_ENQUIRY: case PROT_Q931_MSGTYPE_INFORMATION: case PROT_Q931_MSGTYPE_STATUS: /* Look for an outbound call on that span and and try to match the call-id */ chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL); for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) { ftdmchan = (ftdm_channel_t*)(ftdm_iterator_current(curr)); ftdm_channel_lock(ftdmchan); sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data; if (outbound_call) { if (sngisdn_info && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { if (sngisdn_info->call_ref == frame_info.call_ref) { *found = ftdmchan; status = FTDM_SUCCESS; } } } else { if (sngisdn_info && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { if (sngisdn_info->call_ref && sngisdn_info->call_ref == frame_info.call_ref) { *found = ftdmchan; status = FTDM_SUCCESS; } } } ftdm_channel_unlock(ftdmchan); } ftdm_iterator_free(chaniter); break; default: /* This frame is not call specific, ignore */ break; } if (status == FTDM_SUCCESS) { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Mapped %s with Call Ref:%04x to call-id:%d\n", get_code_2_str(frame_info.msgtype, dcodQ931MsgTypeTable), frame_info.call_ref, (*found)->caller_data.call_id); } else { /* We could not map this frame to a call-id */ ftdm_log(FTDM_LOG_DEBUG, "Failed to map %s with Call Ref:%04x to local call\n", get_code_2_str(frame_info.msgtype, dcodQ931MsgTypeTable), frame_info.call_ref); } return status; }
uint32_t sngisdn_decode_ie(char *str, uint32_t *str_len, uint8_t current_codeset, uint8_t *data, uint16_t index_start) { unsigned char* ieData; uint8_t ieId; uint32_t len = 0; int index_end; ieData = (unsigned char*) &data[index_start]; ieId = OCTET(1); len = OCTET(2); index_end = index_start+len+1; *str_len += sprintf(&str[*str_len], " %s:", get_code_2_str(data[index_start], dcodQ931IEIDTable)); switch(ieId) { case PROT_Q931_IE_BEARER_CAP: { uint8_t codingStandard, infTransferCap, transferMode, infTransferRate, usrL1Prot; codingStandard = get_bits(OCTET(3),6,7); infTransferCap = get_bits(OCTET(3),1,5); transferMode = get_bits(OCTET(4),6,7); infTransferRate = get_bits(OCTET(4),1,5); usrL1Prot = get_bits(OCTET(5),1,5); *str_len+= sprintf(&str[*str_len], "Coding:%s(%d) TransferCap:%s(%d) TransferRate:%s(%d) L1Prot:%s(%d)\n", get_code_2_str(codingStandard, dcodQ931BcCodingStandardTable), codingStandard, get_code_2_str(infTransferCap, dcodQ931BcInfTransferCapTable), infTransferCap, get_code_2_str(infTransferRate, dcodQ931BcInfTransferRateTable), infTransferRate, get_code_2_str(usrL1Prot, dcodQ931BcusrL1ProtTable), usrL1Prot); } break; case PROT_Q931_IE_CAUSE: { uint8_t codingStandard, location, cause,diagOct = 5; codingStandard = get_bits(OCTET(3),6,7); location = get_bits(OCTET(3),1,4); cause = get_bits(OCTET(4),1,7); *str_len+= sprintf(&str[*str_len], "coding:%s(%d) location:%s(%d) val:%s(%d)\n", get_code_2_str(codingStandard, dcodQ931BcCodingStandardTable), codingStandard, get_code_2_str(location,dcodQ931IelocationTable), location, get_code_2_str(cause, dcodQ931CauseCodeTable), cause); switch(cause) { case PROT_Q931_RELEASE_CAUSE_IE_NOT_EXIST: while(diagOct++ < len) { *str_len+= sprintf(&str[*str_len], " %d:IE %s(0x%02x)\n", diagOct, get_code_2_str(OCTET(diagOct), dcodQ931IEIDTable), OCTET(diagOct)); } break; case PROT_Q931_RELEASE_CAUSE_WRONG_CALL_STATE: while(diagOct++ < len) { *str_len+= sprintf(&str[*str_len], " %d:Message %s(0x%02x)\n", diagOct, get_code_2_str(OCTET(diagOct), dcodQ931MsgTypeTable), OCTET(diagOct)); } break; case PROT_Q931_RECOVERY_ON_TIMER_EXPIRE: *str_len+= sprintf(&str[*str_len], " Timer T\n"); while(diagOct++ < len) { if(OCTET(diagOct) >= ' ' && OCTET(diagOct) < 0x7f) { *str_len+= sprintf(&str[*str_len], "%c", OCTET(diagOct)); } else { *str_len+= sprintf(&str[*str_len], "."); } } break; default: while(diagOct++ < len) { *str_len+= sprintf(&str[*str_len], " %d: 0x%02x\n", diagOct, OCTET(diagOct)); } break; } } break; case PROT_Q931_IE_CHANNEL_ID: { uint8_t infoChannelSelection=0; uint8_t prefExclusive=0; uint8_t ifaceIdPresent=0; uint8_t ifaceIdentifier = 0; /* octet_3_1 */ uint8_t chanType=0, numberMap=0, codingStandard=0; uint8_t channelNo = 0; infoChannelSelection = get_bits(OCTET(3),1,2); prefExclusive = get_bits(OCTET(3),4,4); ifaceIdPresent = get_bits(OCTET(3),7,7); if (ifaceIdPresent) { ifaceIdentifier= get_bits(OCTET(4),1,7); chanType = get_bits(OCTET(5),1,4); numberMap = get_bits(OCTET(5),5,5); codingStandard = get_bits(OCTET(5),6,7); channelNo = get_bits(OCTET(6),1,7); } else { chanType = get_bits(OCTET(4),1,4); numberMap = get_bits(OCTET(4),5,5); codingStandard = get_bits(OCTET(4),6,7); channelNo = get_bits(OCTET(5),1,7); } if (numberMap) { *str_len+= sprintf(&str[*str_len], " MAP:%s ", get_code_2_str(infoChannelSelection, dcodQ931InfoChannelSelTable)); } else { *str_len+= sprintf(&str[*str_len], "No:%d ", channelNo); } *str_len+= sprintf(&str[*str_len], "Type:%s(%d) %s ", get_code_2_str(chanType,dcodQ931ChanTypeTable), chanType, (numberMap)? "Map":""); *str_len+= sprintf(&str[*str_len], "%s/%s \n", (prefExclusive)? "Exclusive":"Preferred", (ifaceIdPresent)? "Explicit":"Implicit"); } break; case PROT_Q931_IE_CALLING_PARTY_NUMBER: { uint8_t plan, type, screening = 0, presentation = 0, callingNumOct, j; uint8_t screeningEnabled = 0, presentationEnabled = 0; char callingNumDigits[32]; memset(callingNumDigits, 0, sizeof(callingNumDigits)); plan = get_bits(OCTET(3),1,4); type = get_bits(OCTET(3),5,7); if(!get_bits(OCTET(3),8,8)) { screening = get_bits(OCTET(4),1,2); presentation = get_bits(OCTET(4),6,7); screeningEnabled = 1; presentationEnabled = 1; callingNumOct = 4; } else { callingNumOct = 3; } if(len >= sizeof(callingNumDigits)) { len = sizeof(callingNumDigits)-1; } j = 0; while(callingNumOct++ <= len+1) { callingNumDigits[j++]=ia5[get_bits(OCTET(callingNumOct),1,4)][get_bits(OCTET(callingNumOct),5,8)]; } callingNumDigits[j]='\0'; *str_len+= sprintf(&str[*str_len], "%s(l:%d) plan:%s(%d) type:%s(%d)", callingNumDigits, j, get_code_2_str(plan, dcodQ931NumberingPlanTable), plan, get_code_2_str(type, dcodQ931TypeofNumberTable), type); if (presentationEnabled||screeningEnabled) { *str_len+= sprintf(&str[*str_len], "scr:%s(%d) pres:%s(%d)\n", get_code_2_str(screening, dcodQ931ScreeningTable), screening, get_code_2_str(presentation, dcodQ931PresentationTable), presentation); } else { *str_len+= sprintf(&str[*str_len], "\n"); } } break; case PROT_Q931_IE_CALLED_PARTY_NUMBER: { uint8_t plan, type, calledNumOct,j; char calledNumDigits[32]; memset(calledNumDigits, 0, sizeof(calledNumDigits)); plan = get_bits(OCTET(3),1,4); type = get_bits(OCTET(3),5,7); if(len >= sizeof(calledNumDigits)) { len = sizeof(calledNumDigits)-1; } calledNumOct = 3; j = 0; while(calledNumOct++ <= len+1) { calledNumDigits[j++]=ia5[get_bits(OCTET(calledNumOct),1,4)][get_bits(OCTET(calledNumOct),5,8)]; } calledNumDigits[j]='\0'; *str_len+= sprintf(&str[*str_len], "%s(l:%d) plan:%s(%d) type:%s(%d)\n", calledNumDigits, j, get_code_2_str(plan, dcodQ931NumberingPlanTable), plan, get_code_2_str(type, dcodQ931TypeofNumberTable), type); } break; case PROT_Q931_IE_REDIRECTING_NUMBER: //rdnis { uint8_t plan, type, screening = 0, presentation = 0, reason = 0, rdnisOct,j; uint8_t screeningEnabled = 0, presentationEnabled = 0, reasonEnabled = 0; char rdnis_string[32]; memset(rdnis_string, 0, sizeof(rdnis_string)); rdnisOct = 5; plan = get_bits(OCTET(3),1,4); type = get_bits(OCTET(3),5,7); if(!get_bits(OCTET(3),8,8)) { //Oct 3a exists rdnisOct++; screening = get_bits(OCTET(4),1,2); presentation = get_bits(OCTET(4),6,7); screeningEnabled = 1; presentationEnabled = 1; if (!get_bits(OCTET(4),8,8)) { //Oct 3b exists rdnisOct++; reason = get_bits(OCTET(5),1,4); reasonEnabled = 1; } } if(len >= sizeof(rdnis_string)) { len = sizeof(rdnis_string)-1; } j = 0; while(rdnisOct++ <= len+1) { rdnis_string[j++]=ia5[get_bits(OCTET(rdnisOct),1,4)][get_bits(OCTET(rdnisOct),5,8)]; } rdnis_string[j]='\0'; *str_len+= sprintf(&str[*str_len], "%s(l:%d) plan:%s(%d) type:%s(%d)", rdnis_string, j, get_code_2_str(plan, dcodQ931NumberingPlanTable), plan, get_code_2_str(type, dcodQ931TypeofNumberTable), type); if(presentationEnabled || screeningEnabled) { *str_len+= sprintf(&str[*str_len], "scr:%s(%d) pres:%s(%d)", get_code_2_str(screening, dcodQ931ScreeningTable), screening, get_code_2_str(presentation, dcodQ931PresentationTable), presentation); } if(reasonEnabled) { *str_len+= sprintf(&str[*str_len], "reason:%s(%d)", get_code_2_str(reason, dcodQ931ReasonTable), reason); } *str_len+= sprintf(&str[*str_len], "\n"); } break; case PROT_Q931_IE_USER_USER: { uint8_t protDiscr = 0x00, j, uui_stringOct; char uui_string[32]; memset(uui_string, 0, sizeof(uui_string)); protDiscr = OCTET(3); uui_stringOct = 3; if (protDiscr != 0x04) { /* Non-IA5 */ *str_len+= sprintf(&str[*str_len], "%s (0x%02x)\n", get_code_2_str(protDiscr, dcodQ931UuiProtDiscrTable), protDiscr); } else { j = 0; if(len >= sizeof(uui_string)) { len = sizeof(uui_string)-1; } while(uui_stringOct++ <= len+1) { uui_string[j++]=ia5[get_bits(OCTET(uui_stringOct),1,4)][get_bits(OCTET(uui_stringOct),5,8)]; } uui_string[j]='\0'; *str_len+= sprintf(&str[*str_len], " %s (0x%02x) <%s>\n", get_code_2_str(protDiscr, dcodQ931UuiProtDiscrTable), protDiscr, uui_string); } } break; case PROT_Q931_IE_DISPLAY: { uint8_t displayStrOct=2, j; char displayStr[82]; memset(displayStr, 0, sizeof(displayStr)); if(get_bits(OCTET(3),8,8)) { displayStrOct++; } j = 0; if(len >= sizeof(displayStr)) { len = sizeof(displayStr)-1; } while(displayStrOct++ <= len+1) { displayStr[j++]=ia5[get_bits(OCTET(displayStrOct),1,4)][get_bits(OCTET(displayStrOct),5,8)]; } displayStr[j]='\0'; *str_len+= sprintf(&str[*str_len], "%s(l:%d)\n", displayStr, len); } break; case PROT_Q931_IE_RESTART_IND: { uint8_t indClass; indClass = get_bits(OCTET(3),1,3); *str_len+= sprintf(&str[*str_len], "class:%s(%d)\n", get_code_2_str(indClass,dcodQ931RestartIndClassTable), indClass); } break; case PROT_Q931_IE_PROGRESS_IND: { uint8_t codingStandard, location, progressDescr; codingStandard = get_bits(OCTET(3),6,7); location = get_bits(OCTET(3),1,4); progressDescr = get_bits(OCTET(4),1,7); *str_len+= sprintf(&str[*str_len], "coding:%s(%d) location:%s(%d) descr:%s(%d)\n", get_code_2_str(codingStandard,dcodQ931BcCodingStandardTable), codingStandard, get_code_2_str(location,dcodQ931IelocationTable), location, get_code_2_str(progressDescr,dcodQ931IeprogressDescrTable), progressDescr); } break; case PROT_Q931_IE_KEYPAD_FACILITY: { uint8_t keypadFacilityStrOct = 3, j; char keypadFacilityStr[82]; memset(keypadFacilityStr, 0, sizeof(keypadFacilityStr)); j = 0; if(len >= sizeof(keypadFacilityStr)) { len = sizeof(keypadFacilityStr)-1; } while(keypadFacilityStrOct++ < len+1) { keypadFacilityStr[j++]=ia5[get_bits(OCTET(keypadFacilityStrOct),1,4)][get_bits(OCTET(keypadFacilityStrOct),5,8)]; } keypadFacilityStr[j]='\0'; *str_len+= sprintf(&str[*str_len], " digits:%s(l:%d)\n", keypadFacilityStr, len); } break; case PROT_Q931_IE_FACILITY: { uint8_t protProfile; protProfile = get_bits(OCTET(3),1,5); *str_len+= sprintf(&str[*str_len], "Prot profile:%s(%d)\n", get_code_2_str(protProfile,dcodQ931IeFacilityProtProfileTable), protProfile); } break; case PROT_Q931_IE_GENERIC_DIGITS: { uint8_t encoding,type; int value = 0; encoding = get_bits(OCTET(3),6,8); type = get_bits(OCTET(3),1,5); *str_len+= sprintf(&str[*str_len], "encoding:%s(%d) type:%s(%d) ", get_code_2_str(encoding,dcodQ931GenDigitsEncodingTable), encoding, get_code_2_str(encoding,dcodQ931GenDigitsTypeTable), type); if (len > 1) { uint32_t j=0; while(++j < len) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ { uint8_t byte = OCTET(j+3); value = (get_bits(byte,1,4)*10) + get_bits(byte,5,8) + (value*10); } break; case 2: /* IA 5 */ value = value*10 + OCTET(j+3)-'0'; *str_len+= sprintf(&str[*str_len], "%c", OCTET(j+3)); break; case 3: /* Don't know how to decode binary encoding yet */ *str_len+= sprintf(&str[*str_len], "Binary encoded"); break; } } *str_len+= sprintf(&str[*str_len], " "); switch(type) { case 4: /* info digits */ *str_len+= sprintf(&str[*str_len], "ani2:%s(%d)", get_code_2_str(value,dcodQ931LineInfoTable), value); break; case 5: /* Callid */ *str_len+= sprintf(&str[*str_len], "Caller ID not implemented\n"); break; } } *str_len+= sprintf(&str[*str_len], "\n"); print_hex_dump(str, str_len, (uint8_t*) data, index_start, index_end); } break; case PROT_Q931_IE_SENDING_COMPLETE: /* No need to decode sending complete IE, as no additional info is available except that sending is done */ /* This is a single octet IE */ *str_len+= sprintf(&str[*str_len], "\n"); return 0; break; case PROT_Q931_IE_CALLED_PARTY_SUBADDRESS: { uint8_t type; uint8_t currentOct, j=0; char calling_subaddr_string[82]; memset(calling_subaddr_string, 0, sizeof(calling_subaddr_string)); type = get_bits(OCTET(3),5,7); currentOct = 3; while(currentOct++ <= len+1) { calling_subaddr_string[j++]=ia5[get_bits(OCTET(currentOct),1,4)][get_bits(OCTET(currentOct),5,8)]; } calling_subaddr_string[j++]='\0'; *str_len += sprintf(&str[*str_len], "%s (l:%d) type:%s(%d) \n", calling_subaddr_string, (j-1), get_code_2_str(type, dcodQ931TypeOfSubaddressTable), type); } break; case PROT_Q931_IE_REDIRECTION_NUMBER: case PROT_Q931_IE_NOTIFICATION_IND: case PROT_Q931_IE_DATE_TIME: case PROT_Q931_IE_INFORMATION_REQUEST: case PROT_Q931_IE_SIGNAL: case PROT_Q931_IE_SWITCHOOK: case PROT_Q931_IE_FEATURE_ACT: case PROT_Q931_IE_FEATURE_IND: case PROT_Q931_IE_INFORMATION_RATE: case PROT_Q931_IE_END_TO_END_TRANSIT_DELAY: case PROT_Q931_IE_TRANSIT_DELAY_SELECT_IND: case PROT_Q931_IE_PACKET_LAYER_BINARY_PARAMS: case PROT_Q931_IE_PACKET_LAYER_WINDOW_SIZE: case PROT_Q931_IE_PACKET_LAYER_SIZE: case PROT_Q931_IE_TRANSIT_NETWORK_SELECTION: case PROT_Q931_IE_LOW_LAYER_COMPAT: case PROT_Q931_IE_HIGH_LAYER_COMPAT: case PROT_Q931_IE_ESCAPE_FOR_EXTENSION: case PROT_Q931_IE_CALL_IDENTITY: case PROT_Q931_IE_CALL_STATE: case PROT_Q931_IE_SEGMENTED_MESSAGE: case PROT_Q931_IE_NETWORK_SPF_FACILITY: case PROT_Q931_IE_CALLING_PARTY_SUBADDRESS: default: { *str_len += sprintf(&str[*str_len], "Undecoded"); print_hex_dump((char*)str, str_len, data, index_start, index_end); } break; } return len+1; }
void sngisdn_decode_q921(char* str, uint8_t* data, uint32_t data_len) { int str_len; uint32_t i; uint8_t sapi, cr, ea, tei, ns, nr, pf, p, cmd; uint8_t frame_format = 0; str_len = 0; if(data_len >= 2) { switch ((int)data[2] & 0x03) { case 0: case 2: frame_format = I_FRAME; break; case 1: frame_format = S_FRAME; break; case 3: frame_format = U_FRAME; break; } } str_len+= sprintf(&str[str_len], " format: %s\n", get_code_2_str(frame_format, dcodQ921FrameFormatTable)); for(i=0; i < data_len; i++) { switch(i) { case 0: // Octet 2 sapi = (uint8_t)((data[i]>>2) & 0x3F); cr = (uint8_t)((data[i]>>1) & 0x1); ea = (uint8_t)(data[i] & 0x1); str_len+= sprintf(&str[str_len], " sapi: %03d c/r: %01d ea: %01d\n", sapi, cr, ea); break; case 1: tei = (uint8_t)((data[i]>>1) & 0x7F); ea = (uint8_t)(data[i] & 0x1); str_len+= sprintf(&str[str_len], " tei: %03d ea: %01d\n", tei, ea); break; case 2: switch(frame_format) { case I_FRAME: ns = (uint8_t)((data[i]>>1) & 0x7F); nr = (uint8_t)((data[i+1]>>1) & 0x7F); p = (uint8_t)(data[i+1] & 0x01); str_len+= sprintf(&str[str_len], " n(s): %03d\n n(r): %03d p: %01d\n", ns, nr, p); break; case S_FRAME: nr = (uint8_t)((data[i+1]>>1) & 0x7F); pf = (uint8_t)(data[i+1] & 0x01); str_len+= sprintf(&str[str_len], " n(r): %03d p/f: %01d\n", nr, pf); cmd = (uint8_t)((data[i]>>2) & 0x03); str_len+= sprintf(&str[str_len], " cmd: %s\n", get_code_2_str(cmd, dcodQ921SupervisoryCmdTable)); break; case U_FRAME: pf = (uint8_t)((data[i]>>4) & 0x01); str_len+= sprintf(&str[str_len], " p/f: %01d\n", pf); cmd = (uint8_t)((data[i]>>2) & 0x03); cmd |= (uint8_t)((data[i]>>5) & 0x07); str_len+= sprintf(&str[str_len], " cmd: %s\n", get_code_2_str(cmd, dcodQ921UnnumberedCmdTable)); break; } break; } } return; }