📄 headset.c
字号:
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(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); dbus_connection_send(dev->conn, reply, NULL); dbus_message_unref(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; } sdp_list_free(classes, free); sdp_record_free(record); return;failed_not_supported: if (p->msg) { error_not_supported(dev->conn, p->msg); dbus_message_unref(p->msg); p->msg = NULL; }failed: if (classes) sdp_list_free(classes, free); if (record) sdp_record_free(record); 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"); 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 gboolean ring_timer_cb(gpointer data){ struct audio_device *device = data; struct headset *hs = device->headset; int err; err = headset_send(hs, "\r\nRING\r\n"); if (err < 0) error("Error while sending RING: %s (%d)", strerror(-err), -err); if (hs->cli_active && hs->ph_number) { err = headset_send(hs, "\r\n+CLIP:\"%s\",%d\r\n", hs->ph_number, hs->type); if (err < 0) error("Error while sending CLIP: %s (%d)", strerror(-err), -err); } return TRUE;}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 (hs->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)); } if (hs->cli_active && hs->ph_number) { err = headset_send(hs, "\r\n+CLIP:\"%s\",%d\r\n", hs->ph_number, hs->type); if (err < 0) { dbus_message_unref(reply); return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", strerror(-err)); } } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device);done: return reply;}static DBusMessage *hs_cancel_ringing(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 (!hs->ring_timer) { debug("Got CancelRinging method call but ringing is not in progress"); goto done; } g_source_remove(hs->ring_timer); hs->ring_timer = 0;done: if (hs->hfp_active) { int err; /*+CIEV: (callsetup = 0)*/ err = headset_send(hs, "\r\n+CIEV:3,0\r\n"); if (err < 0) { dbus_message_unref(reply); return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", strerror(-err)); } } 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: 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, char type){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; int err; if (hs->state < HEADSET_STATE_CONNECTED) return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected", "Device not Connected"); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)) return NULL; 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){ return hs_set_gain(conn, msg, data, HEADSET_GAIN_SPEAKER);}static DBusMessage *hs_set_mic_gain(DBusConnection *conn, DBusMessage *msg, void *data){ return hs_set_gain(conn, msg, data, HEADSET_GAIN_MICROPHONE);}static DBusMessage *hf_setup_call(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; const char *value; int err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -