📄 headset.c
字号:
}static int cli_notification(struct 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");}static int signal_gain_setting(struct device *device, const char *buf){ struct headset *hs = device->headset; 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"; hs->sp_gain = gain; break; case HEADSET_GAIN_MICROPHONE: if (hs->mic_gain == gain) goto ok; name = "MicrophoneGainChanged"; hs->mic_gain = gain; break; default: error("Unknown gain setting"); return -EINVAL; } dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID);ok: return headset_send(hs, "\r\nOK\r\n");}static struct event event_callbacks[] = { { "ATA", answer_call }, { "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", answer_call }, { "AT+CLIP", cli_notification }, { 0 }};static int handle_event(struct 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 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 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 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 gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct device *dev){ struct headset *hs; struct pending_connect *p; char hs_address[18]; int sk, ret; socklen_t len; if (cond & G_IO_NVAL) return FALSE; hs = dev->headset; p = hs->pending; sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { p->err = errno; error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), p->err); goto failed; } if (ret != 0) { p->err = ret; error("connect(): %s (%d)", strerror(ret), ret); 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 FALSE; 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 FALSE; } if (p->msg) { DBusMessage *reply = dbus_message_new_method_return(p->msg); send_message_and_unref(dev->conn, reply); } pending_connect_finalize(dev); return FALSE;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); return FALSE;}static void get_record_reply(DBusPendingCall *call, void *data){ DBusMessage *reply; DBusError derr; uint8_t *array; int array_len, record_len, err = EIO, ch = -1; sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; struct device *dev = data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceRecord failed: %s", derr.message); dbus_error_free(&derr); goto failed_not_supported; } dbus_error_init(&derr); if (!dbus_message_get_args(reply, &derr, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, DBUS_TYPE_INVALID)) { error("Unable to get args from GetRecordReply: %s", derr.message); dbus_error_free(&derr); goto failed_not_supported; } if (!array) { error("get_record_reply: Unable to get handle array from reply"); goto failed_not_supported; } record = sdp_extract_pdu(array, &record_len); if (!record) { error("Unable to extract service record from reply"); goto failed_not_supported; } if (record_len != array_len) debug("warning: array len (%d) != record len (%d)", array_len, record_len); 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); dbus_message_unref(reply); device_finish_sdp_transaction(dev); 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); if (reply) dbus_message_unref(reply); pending_connect_finalize(dev); headset_set_state(dev, HEADSET_STATE_DISCONNECTED); device_finish_sdp_transaction(dev);}static void get_handles_reply(DBusPendingCall *call, void *data){ DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; struct device *dev = data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; char address[18], *addr_ptr = address; dbus_uint32_t *array = NULL; dbus_uint32_t handle; int array_len; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceHandles failed: %s", derr.message); if (p->msg) { if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) error_connection_attempt_failed(dev->conn, p->msg, EHOSTDOWN); else error_not_supported(dev->conn, p->msg); } dbus_error_free(&derr); goto failed; } dbus_error_init(&derr); if (!dbus_message_get_args(reply, &derr, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, DBUS_TYPE_INVALID)) { error("Unable to get args from reply: %s", derr.message); dbus_error_free(&derr); if (p->msg) error_not_supported(dev->conn, p->msg); goto failed; } if (!array) { error("get_handles_reply: Unable to get handle array from reply"); if (p->msg) error_not_supported(dev->conn, p->msg); goto failed; } if (array_len < 1) { if (hs->search_hfp) { debug("No record handles found for hfp"); hs->search_hfp = FALSE; get_handles(dev, NULL, NULL, NULL); dbus_message_unref(reply); return; } debug("No record handles found for hsp"); if (p->msg) error_not_supported(dev->conn, p->msg); goto failed; } if (array_len > 1) debug("Multiple records found. Using the first one."); msg = dbus_message_new_method_call("org.bluez", dev->adapter_path, "org.bluez.Adapter", "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); if (p->msg) error_out_of_memory(dev->conn, p->msg); goto failed; } ba2str(&dev->dst, address); handle = array[0]; dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID); if (!dbus_connection_send_with_reply(dev->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); if (p->msg) error_connection_attempt_failed(dev->conn, p->msg, EIO); goto failed; } dbus_pending_call_set_notify(pending, get_record_reply, dev, NULL); dbus_pending_call_unref(pending); dbus_message_unref(msg); dbus_message_unref(reply); return;failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); p->err = EIO; pending_connect_finalize(dev); headset_set_state(dev, HEADSET_STATE_DISCONNECTED);}static int get_handles(struct device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id){ DBusPendingCall *pending; struct headset *hs = device->headset; const char *hs_svc; const char *addr_ptr; char hs_address[18]; DBusMessage *msg; msg = dbus_message_new_method_call("org.bluez", device->adapter_path, "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { error("Could not create a new dbus message"); return -ENOMEM; } if (hs->search_hfp) hs_svc = "hfp"; else hs_svc = "hsp"; ba2str(&device->dst, hs_address); addr_ptr = hs_address; dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); dbus_message_unref(msg); return -EIO; } 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; } dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); if (hs->pending) hs->pending->call = pending; else dbus_pending_call_unref(pending);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -