📄 headset.c
字号:
} sdp_list_free(classes, free); return;failed_not_supported: if (p->msg) error_not_supported(dev->conn, p->msg);failed: if (classes) sdp_list_free(classes, free); pending_connect_finalize(dev); headset_set_state(dev, HEADSET_STATE_DISCONNECTED);}static int get_records(struct audio_device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id){ struct headset *hs = device->headset; uuid_t uuid; sdp_uuid16_create(&uuid, hs->search_hfp ? HANDSFREE_SVCLASS_ID : HEADSET_SVCLASS_ID); headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); pending_connect_init(hs, HEADSET_STATE_CONNECTED); if (cb) { unsigned int id; id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data); if (cb_id) *cb_id = id; } return bt_search_service(&device->src, &device->dst, &uuid, get_record_cb, device, NULL);}static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id){ struct headset *hs = dev->headset; char address[18]; int err; if (hs->rfcomm_ch < 0) return get_records(dev, cb, user_data, cb_id); ba2str(&dev->dst, address); debug("%s: Connecting to %s channel %d", dev->path, address, hs->rfcomm_ch); err = bt_rfcomm_connect(&dev->src, &dev->dst, hs->rfcomm_ch, rfcomm_connect_cb, dev); if (err < 0) { error("connect() failed: %s (%d)", strerror(-err), -err); return err; } headset_set_state(dev, HEADSET_STATE_CONNECT_IN_PROGRESS); pending_connect_init(hs, HEADSET_STATE_CONNECTED); if (cb) { unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data); if (cb_id) *cb_id = id; } return 0;}static DBusMessage *hs_stop(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; headset_set_state(device, HEADSET_STATE_CONNECTED); return reply;}static DBusMessage *hs_is_playing(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t playing; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; playing = (hs->state == HEADSET_STATE_PLAYING); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing, DBUS_TYPE_INVALID); return reply;}static DBusMessage *hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; char hs_address[18]; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; if (hs->state == HEADSET_STATE_DISCONNECTED) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); headset_set_state(device, HEADSET_STATE_DISCONNECTED); ba2str(&device->dst, hs_address); info("Disconnected from %s, %s", hs_address, device->path); return reply;}static DBusMessage *hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; DBusMessage *reply; dbus_bool_t connected; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; connected = (device->headset->state >= HEADSET_STATE_CONNECTED); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); return reply;}static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; int err; if (hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS) return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "Connect in Progress"); else if (hs->state > HEADSET_STATE_CONNECT_IN_PROGRESS) return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyConnected", "Already Connected"); if (hs->hfp_handle && !ag.telephony_ready) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady", "Telephony subsystem not ready"); if (!manager_allow_headset_connection(&device->src)) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAllowed", "Too many connected devices"); err = rfcomm_connect(device, NULL, NULL, NULL); if (err < 0) return g_dbus_create_error(msg, ERROR_INTERFACE ".ConnectAttemptFailed", "Connect Attempt Failed"); hs->auto_dc = FALSE; hs->pending->msg = dbus_message_ref(msg); return NULL;}static DBusMessage *hs_ring(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; int err; if (hs->state < HEADSET_STATE_CONNECTED) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; if (ag.ring_timer) { debug("IndicateCall received when already indicating"); goto done; } err = headset_send(hs, "\r\nRING\r\n"); if (err < 0) { dbus_message_unref(reply); return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", strerror(-err)); } ring_timer_cb(NULL); ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb, NULL);done: return reply;}static DBusMessage *hs_cancel_call(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; if (ag.ring_timer) { g_source_remove(ag.ring_timer); ag.ring_timer = 0; } else debug("Got CancelCall method call but no call is active"); return reply;}static DBusMessage *hs_play(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; int err; if (sco_hci) { error("Refusing Headset.Play() because SCO HCI routing " "is enabled"); return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", "Operation not Available"); } switch (hs->state) { case HEADSET_STATE_DISCONNECTED: case HEADSET_STATE_CONNECT_IN_PROGRESS: return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); case HEADSET_STATE_PLAY_IN_PROGRESS: if (hs->pending && hs->pending->msg == NULL) { hs->pending->msg = dbus_message_ref(msg); return NULL; } return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "Play in Progress"); case HEADSET_STATE_PLAYING: return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyConnected", "Device Already Connected"); case HEADSET_STATE_CONNECTED: default: break; } err = sco_connect(device, NULL, NULL, NULL); if (err < 0) return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", strerror(-err)); hs->pending->msg = dbus_message_ref(msg); return NULL;}static DBusMessage *hs_get_speaker_gain(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; if (hs->state < HEADSET_STATE_CONNECTED || hs->sp_gain < 0) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", "Operation not Available"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; gain = (dbus_uint16_t) hs->sp_gain; dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); return reply;}static DBusMessage *hs_get_mic_gain(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; if (hs->state < HEADSET_STATE_CONNECTED || hs->mic_gain < 0) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", "Operation not Available"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; gain = (dbus_uint16_t) hs->mic_gain; dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); return reply;}static DBusMessage *hs_set_gain(DBusConnection *conn, DBusMessage *msg, void *data, uint16_t gain, char type){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; int err; if (hs->state < HEADSET_STATE_CONNECTED) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); if (gain > 15) return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArgument", "Must be less than or equal to 15"); reply = dbus_message_new_method_return(msg); if (!reply) return NULL; if (hs->state != HEADSET_STATE_PLAYING) goto done; err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain); if (err < 0) { dbus_message_unref(reply); return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", strerror(-err)); }done: if (type == HEADSET_GAIN_SPEAKER) { hs->sp_gain = gain; g_dbus_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, "SpeakerGainChanged", DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); } else { hs->mic_gain = gain; g_dbus_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, "MicrophoneGainChanged", DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); } return reply;}static DBusMessage *hs_set_speaker_gain(DBusConnection *conn, DBusMessage *msg, void *data){ uint16_t gain; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)) return NULL; return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_SPEAKER);}static DBusMessage *hs_set_mic_gain(DBusConnection *conn, DBusMessage *msg, void *data){ uint16_t gain; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)) return NULL; return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_MICROPHONE);}static DBusMessage *hs_get_properties(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; gboolean value; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); /* Playing */ value = (device->headset->state == HEADSET_STATE_PLAYING); dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value); /* Connected */ value = (device->headset->state >= HEADSET_STATE_CONNECTED); dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value); if (!value) goto done; /* SpeakerGain */ dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16, &device->headset->sp_gain); /* MicrophoneGain */ dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16, &device->headset->mic_gain);done: dbus_message_iter_close_container(&iter, &dict); return reply;}static DBusMessage *hs_set_property(DBusConnection *conn, DBusMessage *msg, void *data){ const char *property; DBusMessageIter iter; DBusMessageIter sub; uint16_t gain; if (!dbus_message_iter_init(msg, &iter)) return invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return invalid_args(msg); dbus_message_iter_recurse(&iter, &sub); if (g_str_equal("SpeakerGain", property)) { if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16) return invalid_args(msg); dbus_message_iter_get_basic(&sub, &gain); return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_SPEAKER); } else if (g_str_equal("MicrophoneGain", property)) { if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16) return invalid_args(msg); dbus_message_iter_get_basic(&sub, &gain); return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_MICROPHONE); } return invalid_args(msg);}static GDBusMethodTable headset_methods[] = { { "Connect", "", "", hs_connect, G_DBUS_METHOD_FLAG_ASYNC }, { "Disconnect", "", "", hs_disconnect }, { "IsConnected", "", "b", hs_is_connected }, { "IndicateCall", "", "", hs_ring }, { "CancelCall", "", "", hs_cancel_call }, { "Play", "", "", hs_play, G_DBUS_METHOD_FLAG_ASYNC }, { "Stop", "", "", hs_stop }, { "IsPlaying", "", "b", hs_is_playing, G_DBUS_METHOD_FLAG_DEPRECATED }, { "GetSpeakerGain", "", "q", hs_get_speaker_gain, G_DBUS_METHOD_FLAG_DEPRECATED }, { "GetMicrophoneGain", "", "q", hs_get_mic_gain, G_DBUS_METHOD_FLAG_DEPRECATED }, { "SetSpeakerGain", "q", "", hs_set_speaker_gain, G_DBUS_METHOD_FLAG_DEPRECATED }, { "SetMicrophoneGain", "q", "", hs_set_mic_gain, G_DBUS_METHOD_FLAG_DEPRECATED }, { "GetProperties", "", "a{sv}",hs_get_properties }, { "SetProperty", "sv", "", hs_set_property }, { NULL, NULL, NULL, NULL }};static GDBusSignalTable headset_signals[] = { { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, { "AnswerRequested", "" }, { "Stopped", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, { "Playing", "", G_DBUS_SIGNAL_FLAG_DEPRECATED }, { "SpeakerGainChanged", "q", G_DBUS_SIGNAL_FLAG_DEPRECATED }, { "MicrophoneGainChanged", "q", G_DBUS_SIGNAL_FLAG_DEPRECATED }, { "CallTerminated", "" }, { "PropertyChanged", "sv" }, { NULL, NULL }};static void headset_set_channel(struct headset *headset, const sdp_record_t *record, uint16_t svc){ int ch; sdp_list_t *protos; if (sdp_get_access_protos(record, &protos) < 0) { error("Unable to get access protos from headset record"); return; } 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); if (ch > 0) { headset->rfcomm_ch = ch; debug("Discovered %s service on RFCOMM channel %d", svc == HEADSET_SVCLASS_ID ? "Headset" : "Handsfree", ch); } else error("Unable to get RFCOMM channel from Headset record");}void headset_update(struct audio_device *dev, uint16_t svc, const char *uuidstr){ struct headset *headset = dev->headset; const sdp_record_t *record; record = btd_device_get_record(dev->btd_dev, uuidstr); if (!record) return; switch (svc) { case HANDSFREE_SVCLASS_ID: if (headset->hfp_handle && (headset->hfp_handle != record->handle)) { error("More than one HFP record found on device"); return; } headset->hfp_handle = record->handle; break; case HEADSET_SVCLASS_ID: if (headset->hsp_handle && (headset->hsp_handle != record->handle)) { error("More than one HSP record found on device"); return; } headset->hsp_handle = record->handle; /* Ignore this record if we already have access to HFP */ if (headset->hfp_handle) return; break; default: debug("Invalid record passed to headset_update"); return; } headset_set_channel(headset, record, svc);}static void headset_free(struct audio_device *dev){ struct headset *hs = dev->headset; if (hs->dc_timer) { g_source_remove(hs->dc_timer); hs->dc_timer = 0; } if (hs->sco) { g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); } if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); g_io_channel_unref(hs->rfcomm); } g_free(hs); dev->headset = NULL;}static void path_unregister(void *data){ struct audio_device *dev = data; struct headset *hs = dev->headset; if (hs->state > HEADSET_STATE_DISCONNECTED) { debug("Headset unregistered while device was connected!");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -