hfp.c (1d81c7fe685e46ecabc2566e0d735e259106f522) hfp.c (94a27792279a0f8d9dcb16416be8a05c5b9cc58d)
1/*
2 * Copyright (C) 2014 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright

--- 822 unchanged lines hidden (view full) ---

831 remove_hfp_connection_context(hfp_connection);
832 break;
833
834 default:
835 break;
836 }
837}
838// translates command string into hfp_command_t CMD
1/*
2 * Copyright (C) 2014 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright

--- 822 unchanged lines hidden (view full) ---

831 remove_hfp_connection_context(hfp_connection);
832 break;
833
834 default:
835 break;
836 }
837}
838// translates command string into hfp_command_t CMD
839static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
840 int offset = isHandsFree ? 0 : 2;
841
839
842 if (strncmp(line_buffer+offset, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0){
843 return HFP_CMD_CALL_PHONE_NUMBER;
844 }
840typedef struct {
841 const char * command;
842 hfp_command_t command_id;
843} hfp_command_entry_t;
845
844
846 if (strncmp(line_buffer+offset, HFP_LIST_CURRENT_CALLS, strlen(HFP_LIST_CURRENT_CALLS)) == 0){
847 return HFP_CMD_LIST_CURRENT_CALLS;
848 }
845static hfp_command_entry_t hfp_ag_commmand_table[] = {
846 { "AT+BIND=", HFP_CMD_LIST_GENERIC_STATUS_INDICATORS },
847 { "AT+BIND=?", HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS },
848 { "AT+BIND?", HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE },
849 { "AT+BTRH=", HFP_CMD_RESPONSE_AND_HOLD_COMMAND},
850 { "AT+BTRH?", HFP_CMD_RESPONSE_AND_HOLD_QUERY},
851 { "AT+CHLD=", HFP_CMD_CALL_HOLD },
852 { "AT+CHLD=?", HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES },
853 { "AT+CIND=?", HFP_CMD_RETRIEVE_AG_INDICATORS},
854 { "AT+CIND?", HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS },
855 { "AT+COPS=", HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT },
856 { "AT+COPS?", HFP_CMD_QUERY_OPERATOR_SELECTION_NAME },
857 { "ATA", HFP_CMD_CALL_ANSWERED },
858};
849
859
850 if (strncmp(line_buffer+offset, HFP_SUBSCRIBER_NUMBER_INFORMATION, strlen(HFP_SUBSCRIBER_NUMBER_INFORMATION)) == 0){
851 return HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION;
852 }
860static hfp_command_entry_t hfp_hf_commmand_table[] = {
861 { "+BIND:", HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS },
862 { "+BTRH:", HFP_CMD_RESPONSE_AND_HOLD_STATUS },
863 { "+CHLD:", HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES },
864 { "+COPS:", HFP_CMD_QUERY_OPERATOR_SELECTION_NAME },
865};
853
866
854 if (strncmp(line_buffer+offset, HFP_PHONE_NUMBER_FOR_VOICE_TAG, strlen(HFP_PHONE_NUMBER_FOR_VOICE_TAG)) == 0){
855 if (isHandsFree) return HFP_CMD_AG_SENT_PHONE_NUMBER;
856 return HFP_CMD_HF_REQUEST_PHONE_NUMBER;
857 }
867typedef struct {
868 const char * command;
869 hfp_command_t hf_command_id;
870 hfp_command_t ag_command_id;
871} hfp_command_table_t;
858
872
859 if (strncmp(line_buffer+offset, HFP_TRANSMIT_DTMF_CODES, strlen(HFP_TRANSMIT_DTMF_CODES)) == 0){
860 return HFP_CMD_TRANSMIT_DTMF_CODES;
861 }
873static hfp_command_table_t hfp_command_table[] = {
874 { "+BAC", HFP_CMD_AVAILABLE_CODECS, HFP_CMD_AVAILABLE_CODECS},
875 { "+BCC", HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP, HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP},
876 { "+BCS", HFP_CMD_AG_SUGGESTED_CODEC, HFP_CMD_HF_CONFIRMED_CODEC},
877 { "+BIA", HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE}, // +BIA:<enabled>,,<enabled>,,,<enabled>
878 { "+BIEV", HFP_CMD_HF_INDICATOR_STATUS, HFP_CMD_HF_INDICATOR_STATUS},
879 { "+BINP", HFP_CMD_AG_SENT_PHONE_NUMBER, HFP_CMD_HF_REQUEST_PHONE_NUMBER},
880 { "+BLDN", HFP_CMD_REDIAL_LAST_NUMBER, HFP_CMD_REDIAL_LAST_NUMBER},
881 { "+BRSF", HFP_CMD_SUPPORTED_FEATURES, HFP_CMD_SUPPORTED_FEATURES},
882 { "+BSIR", HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING, HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING},
883 { "+BVRA", HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION, HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION }, // EC (Echo CAnceling), NR (Noise Reduction)
884 { "+CCWA", HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE, HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION},
885 { "+CHUP", HFP_CMD_HANG_UP_CALL, HFP_CMD_HANG_UP_CALL},
886 { "+CIEV", HFP_CMD_TRANSFER_AG_INDICATOR_STATUS, HFP_CMD_TRANSFER_AG_INDICATOR_STATUS},
887 { "+CLCC", HFP_CMD_LIST_CURRENT_CALLS, HFP_CMD_LIST_CURRENT_CALLS},
888 { "+CLIP", HFP_CMD_AG_SENT_CLIP_INFORMATION, HFP_CMD_ENABLE_CLIP},
889 { "+CME ERROR", HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR, HFP_CMD_NONE},
890 { "+CMEE", HFP_CMD_NONE, HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR},
891 { "+CMER", HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE, HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE},
892 { "+CNUM", HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION, HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION},
893 { "+NREC", HFP_CMD_TURN_OFF_EC_AND_NR, HFP_CMD_TURN_OFF_EC_AND_NR }, // EC (Echo CAnceling), NR (Noise Reduction)}
894 { "+VGM", HFP_CMD_SET_MICROPHONE_GAIN, HFP_CMD_SET_MICROPHONE_GAIN},
895 { "+VGS", HFP_CMD_SET_SPEAKER_GAIN, HFP_CMD_SET_SPEAKER_GAIN},
896 { "+VTS", HFP_CMD_TRANSMIT_DTMF_CODES, HFP_CMD_TRANSMIT_DTMF_CODES},
897 { "ERROR", HFP_CMD_ERROR, HFP_CMD_ERROR},
898 { "NOP", HFP_CMD_NONE, HFP_CMD_NONE}, // dummy commmand used by unit tests
899 { "OK", HFP_CMD_OK, HFP_CMD_NONE},
900 { "RING", HFP_CMD_RING, HFP_CMD_RING},
901};
862
902
863 if (strncmp(line_buffer+offset, HFP_SET_MICROPHONE_GAIN, strlen(HFP_SET_MICROPHONE_GAIN)) == 0){
864 return HFP_CMD_SET_MICROPHONE_GAIN;
865 }
903static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
866
904
867 if (strncmp(line_buffer+offset, HFP_SET_SPEAKER_GAIN, strlen(HFP_SET_SPEAKER_GAIN)) == 0){
868 return HFP_CMD_SET_SPEAKER_GAIN;
869 }
870
871 if (strncmp(line_buffer+offset, HFP_ACTIVATE_VOICE_RECOGNITION, strlen(HFP_ACTIVATE_VOICE_RECOGNITION)) == 0){
872 if (isHandsFree) return HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION;
873 return HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION;
874 }
875
876 if (strncmp(line_buffer+offset, HFP_TURN_OFF_EC_AND_NR, strlen(HFP_TURN_OFF_EC_AND_NR)) == 0){
877 return HFP_CMD_TURN_OFF_EC_AND_NR;
878 }
879
880 if (strncmp(line_buffer, HFP_ANSWER_CALL, strlen(HFP_ANSWER_CALL)) == 0){
881 return HFP_CMD_CALL_ANSWERED;
882 }
883
884 if (strncmp(line_buffer, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0){
905 // note: if parser in CMD_HEADER state would treats digits and maybe '+' as separator, match on "ATD" would work.
906 // prefix match on 'ATD', AG only
907 if ((isHandsFree == 0) && (strncmp(line_buffer, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0)){
885 return HFP_CMD_CALL_PHONE_NUMBER;
886 }
887
908 return HFP_CMD_CALL_PHONE_NUMBER;
909 }
910
888 if (strncmp(line_buffer+offset, HFP_REDIAL_LAST_NUMBER, strlen(HFP_REDIAL_LAST_NUMBER)) == 0){
889 return HFP_CMD_REDIAL_LAST_NUMBER;
890 }
911 uint16_t offset = isHandsFree ? 0 : 2;
891
912
892 if (strncmp(line_buffer+offset, HFP_CHANGE_IN_BAND_RING_TONE_SETTING, strlen(HFP_CHANGE_IN_BAND_RING_TONE_SETTING)) == 0){
893 return HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING;
894 }
913 uint16_t i;
914 uint16_t num_entries;
895
915
896 if (strncmp(line_buffer+offset, HFP_HANG_UP_CALL, strlen(HFP_HANG_UP_CALL)) == 0){
897 return HFP_CMD_HANG_UP_CALL;
916 // role-based table lookup
917 hfp_command_entry_t * table;
918 if (isHandsFree == 0){
919 table = hfp_ag_commmand_table;
920 num_entries = sizeof(hfp_ag_commmand_table) / sizeof(hfp_command_entry_t);
921 } else {
922 table = hfp_hf_commmand_table;
923 num_entries = sizeof(hfp_hf_commmand_table) / sizeof(hfp_command_entry_t);
898 }
924 }
899
900 if (strncmp(line_buffer+offset, HFP_ERROR, strlen(HFP_ERROR)) == 0){
901 return HFP_CMD_ERROR;
902 }
903
904 if (strncmp(line_buffer+offset, HFP_RING, strlen(HFP_RING)) == 0){
905 return HFP_CMD_RING;
906 }
907
908 if (isHandsFree && (strncmp(line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0)){
909 return HFP_CMD_OK;
910 }
911
912 if (strncmp(line_buffer+offset, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){
913 return HFP_CMD_SUPPORTED_FEATURES;
914 }
915
916 if (strncmp(line_buffer+offset, HFP_TRANSFER_HF_INDICATOR_STATUS, strlen(HFP_TRANSFER_HF_INDICATOR_STATUS)) == 0){
917 return HFP_CMD_HF_INDICATOR_STATUS;
918 }
919
920 if (strncmp(line_buffer+offset, HFP_RESPONSE_AND_HOLD, strlen(HFP_RESPONSE_AND_HOLD)) == 0){
921 if (strncmp(line_buffer+strlen(HFP_RESPONSE_AND_HOLD)+offset, "?", 1) == 0){
922 return HFP_CMD_RESPONSE_AND_HOLD_QUERY;
925 for (i=0;i<num_entries;i++) {
926 hfp_command_entry_t *entry = &table[i];
927 int match = strcmp(line_buffer, entry->command);
928 if (match == 0){
929 return entry->command_id;
923 }
930 }
924 if (strncmp(line_buffer+strlen(HFP_RESPONSE_AND_HOLD)+offset, "=", 1) == 0){
925 return HFP_CMD_RESPONSE_AND_HOLD_COMMAND;
926 }
927 return HFP_CMD_RESPONSE_AND_HOLD_STATUS;
928 }
929
931 }
932
930 if (strncmp(line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){
931 if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){
932 return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
933 // combined table lookup
934 num_entries = sizeof(hfp_command_table) / sizeof(hfp_command_table_t);
935 for (i=0;i<num_entries;i++){
936 hfp_command_table_t * entry = &hfp_command_table[i];
937 bool match = strncmp(line_buffer+offset, entry->command, strlen(entry->command)) == 0;
938 if (match){
939 if (isHandsFree == 1){
940 if (entry->hf_command_id != HFP_CMD_NONE) return entry->hf_command_id;
941 } else {
942 if (entry->ag_command_id != HFP_CMD_NONE) return entry->ag_command_id;
943 }
933 }
944 }
934
935 if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "=?", 2) == 0){
936 return HFP_CMD_RETRIEVE_AG_INDICATORS;
937 }
938 }
939
945 }
946
940 if (strncmp(line_buffer+offset, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){
941 return HFP_CMD_AVAILABLE_CODECS;
942 }
943
944 if (strncmp(line_buffer+offset, HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, strlen(HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS)) == 0){
945 return HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE;
946 }
947
948 if (strncmp(line_buffer+offset, HFP_ENABLE_CLIP, strlen(HFP_ENABLE_CLIP)) == 0){
949 if (isHandsFree) return HFP_CMD_AG_SENT_CLIP_INFORMATION;
950 return HFP_CMD_ENABLE_CLIP;
951 }
952
953 if (strncmp(line_buffer+offset, HFP_ENABLE_CALL_WAITING_NOTIFICATION, strlen(HFP_ENABLE_CALL_WAITING_NOTIFICATION)) == 0){
954 if (isHandsFree) return HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE;
955 return HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION;
956 }
957
958 if (strncmp(line_buffer+offset, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){
959
960 if (isHandsFree) return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES;
961
962 if (strncmp(line_buffer+strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)+offset, "=?", 2) == 0){
963 return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES;
964 }
965 if (strncmp(line_buffer+strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)+offset, "=", 1) == 0){
966 return HFP_CMD_CALL_HOLD;
967 }
968
947 if (strncmp(line_buffer+offset, "AT+", 3) == 0){
969 return HFP_CMD_UNKNOWN;
948 return HFP_CMD_UNKNOWN;
970 }
971
972 if (strncmp(line_buffer+offset, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){
973 if (isHandsFree) {
974 return HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS;
975 }
976 if (strncmp(line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=?", 2) == 0){
977 return HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS;
978 }
979 if (strncmp(line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=", 1) == 0){
980 return HFP_CMD_LIST_GENERIC_STATUS_INDICATORS;
981 }
982 return HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE;
983 }
984
985 if (strncmp(line_buffer+offset, HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS, strlen(HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS)) == 0){
986 return HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE;
987 }
988
989
990 if (strncmp(line_buffer+offset, HFP_QUERY_OPERATOR_SELECTION, strlen(HFP_QUERY_OPERATOR_SELECTION)) == 0){
991 if (strncmp(line_buffer+strlen(HFP_QUERY_OPERATOR_SELECTION)+offset, "=", 1) == 0){
992 return HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT;
993 }
994 return HFP_CMD_QUERY_OPERATOR_SELECTION_NAME;
995 }
996
949 }
950
997 if (strncmp(line_buffer+offset, HFP_TRANSFER_AG_INDICATOR_STATUS, strlen(HFP_TRANSFER_AG_INDICATOR_STATUS)) == 0){
998 return HFP_CMD_TRANSFER_AG_INDICATOR_STATUS;
999 }
1000
1001 if (isHandsFree && (strncmp(line_buffer+offset, HFP_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0)){
1002 return HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR;
1003 }
1004
1005 if (!isHandsFree && (strncmp(line_buffer+offset, HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0)){
1006 return HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR;
1007 }
1008
1009 if (strncmp(line_buffer+offset, HFP_TRIGGER_CODEC_CONNECTION_SETUP, strlen(HFP_TRIGGER_CODEC_CONNECTION_SETUP)) == 0){
1010 return HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP;
1011 }
1012
1013 if (strncmp(line_buffer+offset, HFP_CONFIRM_COMMON_CODEC, strlen(HFP_CONFIRM_COMMON_CODEC)) == 0){
1014 if (isHandsFree){
1015 return HFP_CMD_AG_SUGGESTED_CODEC;
1016 } else {
1017 return HFP_CMD_HF_CONFIRMED_CODEC;
1018 }
1019 }
1020
1021 if (strncmp(line_buffer+offset, "AT+", 3) == 0){
1022 return HFP_CMD_UNKNOWN;
1023 }
1024
1025 if (strncmp(line_buffer+offset, "+", 1) == 0){
1026 return HFP_CMD_UNKNOWN;
1027 }
951 if (strncmp(line_buffer+offset, "+", 1) == 0){
952 return HFP_CMD_UNKNOWN;
953 }
1028
1029 if (strncmp(line_buffer+offset, "NOP", 3) == 0){
1030 return HFP_CMD_NONE;
1031 }
1032
954
1033 return HFP_CMD_NONE;
1034}
1035
1036static void hfp_parser_store_byte(hfp_connection_t * hfp_connection, uint8_t byte){
1037 if ((hfp_connection->line_size + 1 ) >= HFP_MAX_INDICATOR_DESC_SIZE) return;
1038 hfp_connection->line_buffer[hfp_connection->line_size++] = byte;
1039 hfp_connection->line_buffer[hfp_connection->line_size] = 0;
1040}
1041static int hfp_parser_is_buffer_empty(hfp_connection_t * hfp_connection){
1042 return hfp_connection->line_size == 0;
1043}
1044
1045static int hfp_parser_is_end_of_line(uint8_t byte){
1046 return (byte == '\n') || (byte == '\r');
1047}
1048
955 return HFP_CMD_NONE;
956}
957
958static void hfp_parser_store_byte(hfp_connection_t * hfp_connection, uint8_t byte){
959 if ((hfp_connection->line_size + 1 ) >= HFP_MAX_INDICATOR_DESC_SIZE) return;
960 hfp_connection->line_buffer[hfp_connection->line_size++] = byte;
961 hfp_connection->line_buffer[hfp_connection->line_size] = 0;
962}
963static int hfp_parser_is_buffer_empty(hfp_connection_t * hfp_connection){
964 return hfp_connection->line_size == 0;
965}
966
967static int hfp_parser_is_end_of_line(uint8_t byte){
968 return (byte == '\n') || (byte == '\r');
969}
970
1049void hfp_parser_reset_line_buffer(hfp_connection_t *hfp_connection) {
971static void hfp_parser_reset_line_buffer(hfp_connection_t *hfp_connection) {
1050 hfp_connection->line_size = 0;
1051 hfp_connection->line_buffer[0] = 0;
1052}
1053
1054static void hfp_parser_store_if_token(hfp_connection_t * hfp_connection, uint8_t byte){
1055 switch (byte){
1056 case ',':
1057 case ';':

--- 614 unchanged lines hidden ---
972 hfp_connection->line_size = 0;
973 hfp_connection->line_buffer[0] = 0;
974}
975
976static void hfp_parser_store_if_token(hfp_connection_t * hfp_connection, uint8_t byte){
977 switch (byte){
978 case ',':
979 case ';':

--- 614 unchanged lines hidden ---