📄 headset.c
字号:
return 0;}static int event_reporting(struct audio_device *dev, const char *buf){ char **tokens; /* <mode>, <keyp>, <disp>, <ind>, <bfr> */ if (strlen(buf) < 13) return -EINVAL; tokens = g_strsplit(&buf[8], ",", 5); if (g_strv_length(tokens) < 4) { g_strfreev(tokens); return -EINVAL; } ag.er_mode = atoi(tokens[0]); ag.er_ind = atoi(tokens[3]); g_strfreev(tokens); tokens = NULL; debug("Event reporting (CMER): mode=%d, ind=%d", ag.er_mode, ag.er_ind); switch (ag.er_ind) { case 0: case 1: telephony_event_reporting_req(dev, ag.er_ind); break; default: return -EINVAL; } return 0;}static int call_hold(struct audio_device *dev, const char *buf){ struct headset *hs = dev->headset; int err; if (strlen(buf) < 9) return -EINVAL; if (buf[8] != '?') { telephony_call_hold_req(dev, &buf[8]); return 0; } err = headset_send(hs, "\r\n+CHLD: (%s)\r\n", ag.chld); if (err < 0) return err; err = headset_send(hs, "\r\nOK\r\n"); if (err < 0) return err; if (hs->state != HEADSET_STATE_CONNECT_IN_PROGRESS) return 0; hfp_slc_complete(dev); return 0;}static int button_press(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; g_dbus_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); if (ag.ring_timer) { g_source_remove(ag.ring_timer); ag.ring_timer = 0; } return headset_send(hs, "\r\nOK\r\n");}int telephony_answer_call_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int answer_call(struct audio_device *device, const char *buf){ if (ag.ring_timer) { g_source_remove(ag.ring_timer); ag.ring_timer = 0; } if (ag.number) { g_free(ag.number); ag.number = NULL; } telephony_answer_call_req(device); return 0;}int telephony_terminate_call_rsp(void *telephony_device, cme_error_t err){ struct audio_device *device = telephony_device; struct headset *hs = device->headset; if (err != CME_ERROR_NONE) return telephony_generic_rsp(telephony_device, err); g_dbus_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "CallTerminated", DBUS_TYPE_INVALID); return headset_send(hs, "\r\nOK\r\n");}static int terminate_call(struct audio_device *device, const char *buf){ if (ag.number) { g_free(ag.number); ag.number = NULL; } if (ag.ring_timer) { g_source_remove(ag.ring_timer); ag.ring_timer = 0; } telephony_terminate_call_req(device); return 0;}static int cli_notification(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; if (strlen(buf) < 9) return -EINVAL; hs->cli_active = buf[8] == '1' ? TRUE : FALSE; return headset_send(hs, "\r\nOK\r\n");}int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int response_and_hold(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; if (strlen(buf) < 8) return -EINVAL; if (buf[7] == '=') { telephony_response_and_hold_req(device, atoi(&buf[8]) < 0); return 0; } if (ag.rh >= 0) headset_send(hs, "\r\n+BTRH: %d\r\n", ag.rh); return headset_send(hs, "\r\nOK\r\n", ag.rh);}int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int last_dialed_number(struct audio_device *device, const char *buf){ telephony_last_dialed_number_req(device); return 0;}int telephony_dial_number_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int dial_number(struct audio_device *device, const char *buf){ char number[BUF_SIZE]; size_t buf_len; buf_len = strlen(buf); if (buf[buf_len - 1] != ';') { debug("Rejecting non-voice call dial request"); return -EINVAL; } memset(number, 0, sizeof(number)); strncpy(number, &buf[3], buf_len - 4); telephony_dial_number_req(device, number); return 0;}static int signal_gain_setting(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; const char *property; const char *name; dbus_uint16_t gain; if (strlen(buf) < 8) { error("Too short string for Gain setting"); return -EINVAL; } gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10); if (gain > 15) { error("Invalid gain value received: %u", gain); return -EINVAL; } switch (buf[5]) { case HEADSET_GAIN_SPEAKER: if (hs->sp_gain == gain) goto ok; name = "SpeakerGainChanged"; property = "SpeakerGain"; hs->sp_gain = gain; break; case HEADSET_GAIN_MICROPHONE: if (hs->mic_gain == gain) goto ok; name = "MicrophoneGainChanged"; property = "MicrophoneGain"; hs->mic_gain = gain; break; default: error("Unknown gain setting"); return -EINVAL; } g_dbus_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); emit_property_changed(device->conn, device->path, AUDIO_HEADSET_INTERFACE, property, DBUS_TYPE_UINT16, &gain);ok: return headset_send(hs, "\r\nOK\r\n");}int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int dtmf_tone(struct audio_device *device, const char *buf){ if (strlen(buf) < 8) { error("Too short string for DTMF tone"); return -EINVAL; } telephony_transmit_dtmf_req(device, buf[7]); return 0;}int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int subscriber_number(struct audio_device *device, const char *buf){ telephony_subscriber_number_req(device); return 0;}int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}static int list_current_calls(struct audio_device *device, const char *buf){ telephony_list_current_calls_req(device); return 0;}static int extended_errors(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; if (strlen(buf) < 9) return -EINVAL; if (buf[8] == '1') { hs->cme_enabled = TRUE; debug("CME errors enabled for headset %p", hs); } else { hs->cme_enabled = FALSE; debug("CME errors disabled for headset %p", hs); } return headset_send(hs, "\r\nOK\r\n");}static int call_waiting_notify(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; if (strlen(buf) < 9) return -EINVAL; if (buf[8] == '1') { hs->cwa_enabled = TRUE; debug("Call waiting notifiaction enabled for headset %p", hs); } else { hs->cwa_enabled = FALSE; debug("Call waiting notification disabled for headset %p", hs); } return headset_send(hs, "\r\nOK\r\n");}int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}int telephony_call_hold_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}int telephony_disable_nr_and_ec_rsp(void *telephony_device, cme_error_t err){ return telephony_generic_rsp(telephony_device, err);}int telephony_operator_selection_ind(int mode, const char *oper){ if (!active_devices) return -ENODEV; send_foreach_headset(active_devices, hfp_cmp, "\r\n+COPS: %d,0,%s\r\n", mode, oper); return 0;}static int operator_selection(struct audio_device *device, const char *buf){ struct headset *hs = device->headset; if (strlen(buf) < 8) return -EINVAL; switch (buf[7]) { case '?': telephony_operator_selection_req(device); break; case '=': return headset_send(hs, "\r\nOK\r\n"); default: return -EINVAL; } return 0;}static int disable_nr_and_ec(struct audio_device *device, const char *buf){ if (strlen(buf) < 9) return -EINVAL; telephony_disable_nr_and_ec_req(device); return 0;}static struct event event_callbacks[] = { { "ATA", answer_call }, { "ATD", dial_number }, { "AT+VG", signal_gain_setting }, { "AT+BRSF", supported_features }, { "AT+CIND", report_indicators }, { "AT+CMER", event_reporting }, { "AT+CHLD", call_hold }, { "AT+CHUP", terminate_call }, { "AT+CKPD", button_press }, { "AT+CLIP", cli_notification }, { "AT+BTRH", response_and_hold }, { "AT+BLDN", last_dialed_number }, { "AT+VTS", dtmf_tone }, { "AT+CNUM", subscriber_number }, { "AT+CLCC", list_current_calls }, { "AT+CMEE", extended_errors }, { "AT+CCWA", call_waiting_notify }, { "AT+COPS", operator_selection }, { "AT+NREC", disable_nr_and_ec }, { 0 }};static int handle_event(struct audio_device *device, const char *buf){ struct event *ev; debug("Received %s", buf); for (ev = event_callbacks; ev->cmd; ev++) { if (!strncmp(buf, ev->cmd, strlen(ev->cmd))) return ev->callback(device, buf); } return -EINVAL;}static void close_sco(struct audio_device *device){ struct headset *hs = device->headset; if (hs->sco) { g_source_remove(hs->sco_id); hs->sco_id = 0; g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; }}static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct audio_device *device){ struct headset *hs; unsigned char buf[BUF_SIZE]; char *cr; gsize bytes_read = 0; gsize free_space; int err; off_t cmd_len; if (cond & G_IO_NVAL) return FALSE; hs = device->headset; if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; if (g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, &bytes_read) != G_IO_ERROR_NONE) return TRUE; free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; if (free_space < bytes_read) { /* Very likely that the HS is sending us garbage so * just ignore the data and disconnect */ error("Too much data to fit incomming buffer"); goto failed; } memcpy(&hs->buf[hs->data_start], buf, bytes_read); hs->data_length += bytes_read; /* Make sure the data is null terminated so we can use string * functions */ hs->buf[hs->data_start + hs->data_length] = '\0'; cr = strchr(&hs->buf[hs->data_start], '\r'); if (!cr) return TRUE; cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; *cr = '\0'; err = handle_event(device, &hs->buf[hs->data_start]); if (err == -EINVAL) { error("Badly formated or unrecognized command: %s", &hs->buf[hs->data_start]); err = headset_send(hs, "\r\nERROR\r\n"); } else if (err < 0) error("Error handling command %s: %s (%d)", &hs->buf[hs->data_start], strerror(-err), -err); hs->data_start += cmd_len; hs->data_length -= cmd_len; if (!hs->data_length) hs->data_start = 0; return TRUE;failed: headset_set_state(device, HEADSET_STATE_DISCONNECTED); return FALSE;}static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, struct audio_device *device){ struct headset *hs; if (cond & G_IO_NVAL) return FALSE; hs = device->headset; error("Audio connection got disconnected"); headset_set_state(device, HEADSET_STATE_CONNECTED); return FALSE;}static void rfcomm_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer user_data){ struct audio_device *dev = user_data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; char hs_address[18]; if (err < 0) { error("connect(): %s (%d)", strerror(-err), -err); goto failed; } ba2str(&dev->dst, hs_address); hs->rfcomm = chan; p->io = NULL; if (server_is_enabled(&dev->src, HANDSFREE_SVCLASS_ID) && hs->hfp_handle != 0) hs->hfp_active = TRUE; else hs->hfp_active = FALSE; g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, (GIOFunc) rfcomm_io_cb, dev); debug("%s: Connected to %s", dev->path, hs_address); /* In HFP mode wait for Service Level Connection */ if (hs->hfp_active) return; headset_set_state(dev, HEADSET_STATE_CONNECTED); if (p->target_state == HEADSET_STATE_PLAYING) { p->err = sco_connect(dev, NULL, NULL, NULL); if (p->err < 0) goto failed; return; } if (p->msg) { DBusMessage *reply = dbus_message_new_method_return(p->msg); g_dbus_send_message(dev->conn, reply); } pending_connect_finalize(dev); return;failed: if (p->msg) error_connection_attempt_failed(dev->conn, p->msg, p->err); pending_connect_finalize(dev); if (hs->rfcomm) headset_set_state(dev, HEADSET_STATE_CONNECTED); else headset_set_state(dev, HEADSET_STATE_DISCONNECTED);}static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data){ struct audio_device *dev = user_data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; int ch = -1; sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; if (err < 0) { error("Unable to get service record: %s (%d)", strerror(-err), -err); goto failed_not_supported; } if (!recs || !recs->data) { error("No records found"); goto failed_not_supported; } record = recs->data; if (sdp_get_service_classes(record, &classes) < 0) { error("Unable to get service classes from record"); goto failed_not_supported; } memcpy(&uuid, classes->data, sizeof(uuid)); if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16) { error("Not a 16 bit UUID"); goto failed_not_supported; } if (hs->search_hfp) { if (uuid.value.uuid16 != HANDSFREE_SVCLASS_ID) { error("Service record didn't contain the HFP UUID"); goto failed_not_supported; } hs->hfp_handle = record->handle; } else { if (uuid.value.uuid16 != HEADSET_SVCLASS_ID) { error("Service record didn't contain the HSP UUID"); goto failed_not_supported; } hs->hsp_handle = record->handle; } if (!sdp_get_access_protos(record, &protos)) { ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); protos = NULL; } if (ch == -1) { error("Unable to extract RFCOMM channel from service record"); goto failed_not_supported; } hs->rfcomm_ch = ch; err = rfcomm_connect(dev, NULL, NULL, NULL); if (err < 0) { error("Unable to connect: %s (%s)", strerror(-err), -err); p->err = -err; error_connection_attempt_failed(dev->conn, p->msg, p->err); goto failed;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -