diff options
Diffstat (limited to 'cras/src/libcras')
-rw-r--r-- | cras/src/libcras/cras_client.c | 4249 | ||||
-rw-r--r-- | cras/src/libcras/cras_client.h | 2008 | ||||
-rw-r--r-- | cras/src/libcras/cras_helpers.c | 159 | ||||
-rw-r--r-- | cras/src/libcras/cras_helpers.h | 95 |
4 files changed, 0 insertions, 6511 deletions
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c deleted file mode 100644 index 8420db1f..00000000 --- a/cras/src/libcras/cras_client.c +++ /dev/null @@ -1,4249 +0,0 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * Basic playback flow: - * cras_client_create - Create new structure and set to defaults. - * cras_client_connect - Connect client to server - sets up server_fd to - * communicate with the audio server. After the client connects, the server - * will send back a message containing the client id. - * cras_client_add_stream - Add a playback or capture stream. Creates a - * client_stream struct and send a file descriptor to server. That file - * descriptor and aud_fd are a pair created from socketpair(). - * client_connected - The server will send a connected message to indicate that - * the client should start receiving audio events from aud_fd. This message - * also specifies the shared memory region to use to share audio samples. - * This region will be shmat'd. - * running - Once the connections are established, the client will listen for - * requests on aud_fd and fill the shm region with the requested number of - * samples. This happens in the aud_cb specified in the stream parameters. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* For ppoll() */ -#endif - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <poll.h> -#include <pthread.h> -#include <stdbool.h> -#include <stdint.h> -#include <sys/eventfd.h> -#include <sys/ipc.h> -#include <sys/mman.h> -#include <sys/param.h> -#include <sys/signal.h> -#include <sys/socket.h> -#include <sys/timerfd.h> -#include <sys/types.h> -#include <sys/un.h> -#include <syslog.h> -#include <unistd.h> - -#include "cras_client.h" -#include "cras_config.h" -#include "cras_file_wait.h" -#include "cras_messages.h" -#include "cras_observer_ops.h" -#include "cras_shm.h" -#include "cras_types.h" -#include "cras_util.h" -#include "utlist.h" - -static const size_t MAX_CMD_MSG_LEN = 256; -static const size_t SERVER_SHUTDOWN_TIMEOUT_US = 500000; -static const size_t SERVER_CONNECT_TIMEOUT_MS = 1000; -static const size_t HOTWORD_FRAME_RATE = 16000; -static const size_t HOTWORD_BLOCK_SIZE = 320; - -/* Commands sent from the user to the running client. */ -enum { CLIENT_STOP, - CLIENT_ADD_STREAM, - CLIENT_REMOVE_STREAM, - CLIENT_SET_STREAM_VOLUME_SCALER, - CLIENT_SERVER_CONNECT, - CLIENT_SERVER_CONNECT_ASYNC, -}; - -struct command_msg { - unsigned len; - unsigned msg_id; - cras_stream_id_t stream_id; -}; - -struct set_stream_volume_command_message { - struct command_msg header; - float volume_scaler; -}; - -/* Adds a stream to the client. - * stream - The stream to add. - * stream_id_out - Filled with the stream id of the new stream. - * dev_idx - Index of the device to attach the newly created stream. - * NO_DEVICE means not to pin the stream to a device. - */ -struct add_stream_command_message { - struct command_msg header; - struct client_stream *stream; - cras_stream_id_t *stream_id_out; - uint32_t dev_idx; -}; - -/* Commands send from a running stream to the client. */ -enum { CLIENT_STREAM_EOF, -}; - -struct stream_msg { - unsigned msg_id; - cras_stream_id_t stream_id; -}; - -enum CRAS_THREAD_STATE { - CRAS_THREAD_STOP, - /* Isn't (shouldn't be) running. */ - CRAS_THREAD_WARMUP, - /* Is started, but not fully functional: waiting - * for resources to be ready for example. */ - CRAS_THREAD_RUNNING, - /* Is running and fully functional. */ -}; - -/* Manage information for a thread. */ -struct thread_state { - pthread_t tid; - enum CRAS_THREAD_STATE state; -}; - -/* Parameters used when setting up a capture or playback stream. See comment - * above cras_client_stream_params_create or libcras_stream_params_set in the - * header for descriptions. */ -struct cras_stream_params { - enum CRAS_STREAM_DIRECTION direction; - size_t buffer_frames; - size_t cb_threshold; - enum CRAS_STREAM_TYPE stream_type; - enum CRAS_CLIENT_TYPE client_type; - uint32_t flags; - uint64_t effects; - void *user_data; - cras_playback_cb_t aud_cb; - cras_unified_cb_t unified_cb; - cras_error_cb_t err_cb; - struct cras_audio_format format; - libcras_stream_cb_t stream_cb; -}; - -/* Represents an attached audio stream. - * id - Unique stream identifier. - * aud_fd - After server connects audio messages come in here. - * direction - playback, capture, or loopback (see CRAS_STREAM_DIRECTION). - * flags - Currently only used for CRAS_INPUT_STREAM_FLAG. - * volume_scaler - Amount to scale the stream by, 0.0 to 1.0. Client could - * change this scaler value before stream actually connected, so we need - * to cache it until shm is prepared and apply it. - * tid - Thread id of the audio thread spawned for this stream. - * running - Audio thread runs while this is non-zero. - * wake_fds - Pipe to wake the audio thread. - * client - The client this stream is attached to. - * config - Audio stream configuration. - * shm - Shared memory used to exchange audio samples with the server. - * prev, next - Form a linked list of streams attached to a client. - */ -struct client_stream { - cras_stream_id_t id; - int aud_fd; /* audio messages from server come in here. */ - enum CRAS_STREAM_DIRECTION direction; - uint32_t flags; - float volume_scaler; - struct thread_state thread; - int wake_fds[2]; /* Pipe to wake the thread */ - struct cras_client *client; - struct cras_stream_params *config; - struct cras_audio_shm *shm; - struct client_stream *prev, *next; -}; - -/* State of the socket. */ -typedef enum cras_socket_state { - CRAS_SOCKET_STATE_DISCONNECTED, - /* Not connected. Also used to cleanup the current connection - * before restarting the connection attempt. */ - CRAS_SOCKET_STATE_WAIT_FOR_SOCKET, - /* Waiting for the socket file to exist. Socket file existence - * is monitored using cras_file_wait. */ - CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE, - /* Waiting for the socket to have something at the other end. */ - CRAS_SOCKET_STATE_FIRST_MESSAGE, - /* Waiting for the first messages from the server and set our - * client ID. */ - CRAS_SOCKET_STATE_CONNECTED, - /* The socket is connected and working. */ - CRAS_SOCKET_STATE_ERROR_DELAY, - /* There was an error during one of the above states. Sleep for - * a bit before continuing. If this state could not be initiated - * then we move to the DISCONNECTED state and notify via the - * connection callback. */ -} cras_socket_state_t; - -/* Represents a client used to communicate with the audio server. - * id - Unique identifier for this client, negative until connected. - * server_fd - Incoming messages from server. - * server_fd_state - State of the server's socket. - * server_event_fd - Eventfd to wait on until a connection is established. - * stream_fds - Pipe for attached streams. - * command_fds - Pipe for user commands to thread. - * command_reply_fds - Pipe for acking/nacking command messages from thread. - * sock_file - Server communication socket file. - * sock_file_wait - Structure used to monitor existence of the socket file. - * sock_file_exists - Set to true when the socket file exists. - * running - The client thread will run while this is non zero. - * next_stream_id - ID to give the next stream. - * stream_start_cond - Condition used during stream startup. - * stream_start_lock - Lock used during stream startup. - * tid - Thread ID of the client thread started by "cras_client_run_thread". - * last_command_result - Passes back the result of the last user command. - * streams - Linked list of streams attached to this client. - * server_state - RO shared memory region holding server state. - * atlog_ro - RO shared memory region holding audio thread log. - * debug_info_callback - Function to call when debug info is received. - * atlog_access_callback - Function to call when atlog RO fd is received. - * get_hotword_models_cb_t - Function to call when hotword models info is ready. - * server_connection_cb - Function to called when a connection state changes. - * server_connection_user_arg - User argument for server_connection_cb. - * thread_priority_cb - Function to call for setting audio thread priority. - * observer_ops - Functions to call when system state changes. - * observer_context - Context passed to client in state change callbacks. - */ -struct cras_client { - int id; - int server_fd; - cras_socket_state_t server_fd_state; - int server_event_fd; - int stream_fds[2]; - int command_fds[2]; - int command_reply_fds[2]; - const char *sock_file; - struct cras_file_wait *sock_file_wait; - bool sock_file_exists; - struct thread_state thread; - cras_stream_id_t next_stream_id; - pthread_cond_t stream_start_cond; - pthread_mutex_t stream_start_lock; - int last_command_result; - struct client_stream *streams; - const struct cras_server_state *server_state; - struct audio_thread_event_log *atlog_ro; - void (*debug_info_callback)(struct cras_client *); - void (*atlog_access_callback)(struct cras_client *); - get_hotword_models_cb_t get_hotword_models_cb; - cras_connection_status_cb_t server_connection_cb; - void *server_connection_user_arg; - cras_thread_priority_cb_t thread_priority_cb; - struct cras_observer_ops observer_ops; - void *observer_context; -}; - -/* - * Holds the client pointer plus internal book keeping. - * - * client - The client - * server_state_rwlock - lock to make the client's server_state thread-safe. - */ -struct client_int { - struct cras_client client; - pthread_rwlock_t server_state_rwlock; -}; - -#define to_client_int(cptr) \ - ((struct client_int *)((char *)cptr - \ - offsetof(struct client_int, client))) - -/* - * Holds the hotword stream format, params, and ID used when waiting for a - * hotword. The structure is created by cras_client_enable_hotword_callback and - * destroyed by cras_client_disable_hotword_callback. - */ -struct cras_hotword_handle { - struct cras_audio_format *format; - struct cras_stream_params *params; - cras_stream_id_t stream_id; - cras_hotword_trigger_cb_t trigger_cb; - cras_hotword_error_cb_t err_cb; - void *user_data; -}; - -struct cras_stream_cb_data { - cras_stream_id_t stream_id; - enum CRAS_STREAM_DIRECTION direction; - uint8_t *buf; - unsigned int frames; - struct timespec sample_ts; - void *user_arg; -}; - -int stream_cb_get_stream_id(struct cras_stream_cb_data *data, - cras_stream_id_t *id) -{ - *id = data->stream_id; - return 0; -} - -int stream_cb_get_buf(struct cras_stream_cb_data *data, uint8_t **buf) -{ - *buf = data->buf; - return 0; -} - -int stream_cb_get_frames(struct cras_stream_cb_data *data, unsigned int *frames) -{ - *frames = data->frames; - return 0; -} - -int stream_cb_get_latency(struct cras_stream_cb_data *data, - struct timespec *latency) -{ - if (data->direction == CRAS_STREAM_INPUT) - cras_client_calc_capture_latency(&data->sample_ts, latency); - else - cras_client_calc_playback_latency(&data->sample_ts, latency); - return 0; -} - -int stream_cb_get_user_arg(struct cras_stream_cb_data *data, void **user_arg) -{ - *user_arg = data->user_arg; - return 0; -} - -struct libcras_stream_cb_data * -libcras_stream_cb_data_create(cras_stream_id_t stream_id, - enum CRAS_STREAM_DIRECTION direction, - uint8_t *buf, unsigned int frames, - struct timespec sample_ts, void *user_arg) -{ - struct libcras_stream_cb_data *data = - (struct libcras_stream_cb_data *)calloc( - 1, sizeof(struct libcras_stream_cb_data)); - if (!data) { - syslog(LOG_ERR, "cras_client: calloc: %s", strerror(errno)); - return NULL; - } - data->data_ = (struct cras_stream_cb_data *)calloc( - 1, sizeof(struct cras_stream_cb_data)); - if (!data->data_) { - syslog(LOG_ERR, "cras_client: calloc: %s", strerror(errno)); - free(data); - return NULL; - } - data->api_version = CRAS_API_VERSION; - data->get_stream_id = stream_cb_get_stream_id; - data->get_buf = stream_cb_get_buf; - data->get_frames = stream_cb_get_frames; - data->get_latency = stream_cb_get_latency; - data->get_user_arg = stream_cb_get_user_arg; - data->data_->stream_id = stream_id; - data->data_->direction = direction; - data->data_->buf = buf; - data->data_->frames = frames; - data->data_->sample_ts = sample_ts; - data->data_->user_arg = user_arg; - return data; -} - -void libcras_stream_cb_data_destroy(struct libcras_stream_cb_data *data) -{ - if (data) - free(data->data_); - free(data); -} - -/* - * Local Helpers - */ - -static int client_thread_rm_stream(struct cras_client *client, - cras_stream_id_t stream_id); -static int handle_message_from_server(struct cras_client *client); -static int reregister_notifications(struct cras_client *client); - -static struct libcras_node_info * -libcras_node_info_create(struct cras_iodev_info *iodev, - struct cras_ionode_info *ionode); - -/* - * Unlock the server_state_rwlock if lock_rc is 0. - * - * Args: - * client - The CRAS client pointer. - * lock_rc - The result of server_state_rdlock or - * server_state_wrlock. - */ -static void server_state_unlock(const struct cras_client *client, int lock_rc) -{ - struct client_int *client_int; - - if (!client) - return; - client_int = to_client_int(client); - if (lock_rc == 0) - pthread_rwlock_unlock(&client_int->server_state_rwlock); -} - -/* - * Lock the server_state_rwlock for reading. - * - * Also checks that the server_state pointer is valid. - * - * Args: - * client - The CRAS client pointer. - * Returns: - * 0 for success, positive error code on error. - * Returns EINVAL if the server state pointer is NULL. - */ -static int server_state_rdlock(const struct cras_client *client) -{ - struct client_int *client_int; - int lock_rc; - - if (!client) - return EINVAL; - client_int = to_client_int(client); - lock_rc = pthread_rwlock_rdlock(&client_int->server_state_rwlock); - if (lock_rc != 0) - return lock_rc; - if (!client->server_state) { - pthread_rwlock_unlock(&client_int->server_state_rwlock); - return EINVAL; - } - return 0; -} - -/* - * Lock the server_state_rwlock for writing. - * - * Args: - * client - The CRAS client pointer. - * Returns: - * 0 for success, positive error code on error. - */ -static int server_state_wrlock(const struct cras_client *client) -{ - struct client_int *client_int; - - if (!client) - return EINVAL; - client_int = to_client_int(client); - return pthread_rwlock_wrlock(&client_int->server_state_rwlock); -} - -/* Get the stream pointer from a stream id. */ -static struct client_stream *stream_from_id(const struct cras_client *client, - unsigned int id) -{ - struct client_stream *out; - - DL_SEARCH_SCALAR(client->streams, out, id, id); - return out; -} - -/* - * Fill a pollfd structure with the current server fd and events. - */ -void server_fill_pollfd(const struct cras_client *client, - struct pollfd *poll_fd) -{ - int events = 0; - - poll_fd->fd = client->server_fd; - switch (client->server_fd_state) { - case CRAS_SOCKET_STATE_DISCONNECTED: - break; - case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET: - case CRAS_SOCKET_STATE_FIRST_MESSAGE: - case CRAS_SOCKET_STATE_CONNECTED: - case CRAS_SOCKET_STATE_ERROR_DELAY: - events = POLLIN; - break; - case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE: - events = POLLOUT; - break; - } - poll_fd->events = events; - poll_fd->revents = 0; -} - -/* - * Change the server_fd_state. - */ -static void server_fd_move_to_state(struct cras_client *client, - cras_socket_state_t state) -{ - if (state == client->server_fd_state) - return; - - client->server_fd_state = state; -} - -/* - * Action to take when in state ERROR_DELAY. - * - * In this state we want to sleep for a few seconds before retrying the - * connection to the audio server. - * - * If server_fd is negative: create a timer and setup server_fd with the - * timer's fd. If server_fd is not negative and there is input, then assume - * that the timer has expired, and restart the connection by moving to - * WAIT_FOR_SOCKET state. - */ -static int error_delay_next_action(struct cras_client *client, int poll_revents) -{ - int rc; - struct itimerspec timeout; - - if (client->server_fd == -1) { - client->server_fd = timerfd_create(CLOCK_MONOTONIC, - TFD_NONBLOCK | TFD_CLOEXEC); - if (client->server_fd == -1) { - rc = -errno; - syslog(LOG_ERR, - "cras_client: Could not create timerfd: %s", - strerror(-rc)); - return rc; - } - - /* Setup a relative timeout of 2 seconds. */ - memset(&timeout, 0, sizeof(timeout)); - timeout.it_value.tv_sec = 2; - rc = timerfd_settime(client->server_fd, 0, &timeout, NULL); - if (rc != 0) { - rc = -errno; - syslog(LOG_ERR, - "cras_client: Could not set timeout: %s", - strerror(-rc)); - return rc; - } - return 0; - } else if ((poll_revents & POLLIN) == 0) { - return 0; - } - - /* Move to the next state: close the timer fd first. */ - close(client->server_fd); - client->server_fd = -1; - server_fd_move_to_state(client, CRAS_SOCKET_STATE_WAIT_FOR_SOCKET); - return 0; -} - -/* - * Action to take when in WAIT_FOR_SOCKET state. - * - * In this state we are waiting for the socket file to exist. The existence of - * the socket file is continually monitored using the cras_file_wait structure - * and a separate fd. When the sock_file_exists boolean is modified, the state - * machine is invoked. - * - * If the socket file exists, then we move to the WAIT_FOR_WRITABLE state. - */ -static void wait_for_socket_next_action(struct cras_client *client) -{ - if (client->sock_file_exists) - server_fd_move_to_state(client, - CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE); -} - -/* - * Action to take when in WAIT_FOR_WRITABLE state. - * - * In this state we are initiating a connection the server and waiting for the - * server to ready for incoming messages. - * - * Create the socket to the server, and wait while a connect request results in - * -EINPROGRESS. Otherwise, we assume that the socket file will be deleted by - * the server and the server_fd_state will be changed in - * sock_file_wait_dispatch(). - */ -static int wait_for_writable_next_action(struct cras_client *client, - int poll_revents) -{ - int rc; - struct sockaddr_un address; - - if (client->server_fd == -1) { - client->server_fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); - if (client->server_fd < 0) { - rc = -errno; - syslog(LOG_ERR, "cras_client: server socket failed: %s", - strerror(-rc)); - return rc; - } - } else if ((poll_revents & POLLOUT) == 0) { - return 0; - } - - /* We make the file descriptor non-blocking when we do connect(), so we - * don't block indefinitely. */ - cras_make_fd_nonblocking(client->server_fd); - - memset(&address, 0, sizeof(struct sockaddr_un)); - address.sun_family = AF_UNIX; - strcpy(address.sun_path, client->sock_file); - rc = connect(client->server_fd, (struct sockaddr *)&address, - sizeof(struct sockaddr_un)); - if (rc != 0) { - rc = -errno; - /* For -EINPROGRESS, we wait for POLLOUT on the server_fd. - * Otherwise CRAS is not running and we assume that the socket - * file will be deleted and recreated. Notification of that will - * happen via the sock_file_wait_dispatch(). */ - if (rc == -ECONNREFUSED) { - /* CRAS is not running, don't log this error and just - * stay in this state waiting sock_file_wait_dispatch() - * to move the state machine. */ - close(client->server_fd); - client->server_fd = -1; - } else if (rc != -EINPROGRESS) { - syslog(LOG_ERR, - "cras_client: server connect failed: %s", - strerror(-rc)); - return rc; - } - return 0; - } - - cras_make_fd_blocking(client->server_fd); - server_fd_move_to_state(client, CRAS_SOCKET_STATE_FIRST_MESSAGE); - return 0; -} - -/* - * Action to take when transitioning to the CONNECTED state. - */ -static int connect_transition_action(struct cras_client *client) -{ - eventfd_t event_value; - int rc; - - rc = reregister_notifications(client); - if (rc < 0) - return rc; - - server_fd_move_to_state(client, CRAS_SOCKET_STATE_CONNECTED); - /* Notify anyone waiting on this state change that we're - * connected. */ - eventfd_read(client->server_event_fd, &event_value); - eventfd_write(client->server_event_fd, 1); - if (client->server_connection_cb) - client->server_connection_cb( - client, CRAS_CONN_STATUS_CONNECTED, - client->server_connection_user_arg); - return 0; -} - -/* - * Action to take when in the FIRST_MESSAGE state. - * - * We are waiting for the first message from the server. When our client ID has - * been set, then we can move to the CONNECTED state. - */ -static int first_message_next_action(struct cras_client *client, - int poll_revents) -{ - int rc; - - if (client->server_fd < 0) - return -EINVAL; - - if ((poll_revents & POLLIN) == 0) - return 0; - - rc = handle_message_from_server(client); - if (rc < 0) { - syslog(LOG_ERR, "handle first message: %s", strerror(-rc)); - } else if (client->id >= 0) { - rc = connect_transition_action(client); - } else { - syslog(LOG_ERR, "did not get ID after first message!"); - rc = -EINVAL; - } - return rc; -} - -/* - * Play nice and shutdown the server socket. - */ -static inline int shutdown_and_close_socket(int sockfd) -{ - int rc; - uint8_t buffer[CRAS_CLIENT_MAX_MSG_SIZE]; - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = SERVER_SHUTDOWN_TIMEOUT_US; - setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); - - rc = shutdown(sockfd, SHUT_WR); - if (rc < 0) - return rc; - /* Wait until the socket is closed by the peer. */ - for (;;) { - rc = recv(sockfd, buffer, sizeof(buffer), 0); - if (rc <= 0) - break; - } - return close(sockfd); -} - -/* - * Action to take when disconnecting from the server. - * - * Clean up the server socket, and the server_state pointer. Move to the next - * logical state. - */ -static void disconnect_transition_action(struct cras_client *client, bool force) -{ - eventfd_t event_value; - cras_socket_state_t old_state = client->server_fd_state; - struct client_stream *s; - int lock_rc; - - /* Stop all playing streams. - * TODO(muirj): Pause and resume streams. */ - DL_FOREACH (client->streams, s) { - s->config->err_cb(client, s->id, -ENOTCONN, - s->config->user_data); - client_thread_rm_stream(client, s->id); - } - - /* Clean up the server_state pointer. */ - lock_rc = server_state_wrlock(client); - if (client->server_state) { - munmap((void *)client->server_state, - sizeof(*client->server_state)); - client->server_state = NULL; - } - server_state_unlock(client, lock_rc); - - /* Our ID is unknown now. */ - client->id = -1; - - /* Clean up the server fd. */ - if (client->server_fd >= 0) { - if (!force) - shutdown_and_close_socket(client->server_fd); - else - close(client->server_fd); - client->server_fd = -1; - } - - /* Reset the server_event_fd value to 0 (and cause subsequent threads - * waiting on the connection to wait). */ - eventfd_read(client->server_event_fd, &event_value); - - switch (old_state) { - case CRAS_SOCKET_STATE_DISCONNECTED: - /* Do nothing: already disconnected. */ - break; - case CRAS_SOCKET_STATE_ERROR_DELAY: - /* We're disconnected and there was a failure to setup - * automatic reconnection, so call the server error - * callback now. */ - server_fd_move_to_state(client, CRAS_SOCKET_STATE_DISCONNECTED); - if (client->server_connection_cb) - client->server_connection_cb( - client, CRAS_CONN_STATUS_FAILED, - client->server_connection_user_arg); - break; - case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET: - case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE: - case CRAS_SOCKET_STATE_FIRST_MESSAGE: - /* We are running this state transition while a connection is - * in progress for an error case. When there is no error, we - * come into this function in the DISCONNECTED state. */ - server_fd_move_to_state(client, CRAS_SOCKET_STATE_ERROR_DELAY); - break; - case CRAS_SOCKET_STATE_CONNECTED: - /* Disconnected from CRAS (for an error), wait for the socket - * file to be (re)created. */ - server_fd_move_to_state(client, - CRAS_SOCKET_STATE_WAIT_FOR_SOCKET); - /* Notify the caller that we aren't connected anymore. */ - if (client->server_connection_cb) - client->server_connection_cb( - client, CRAS_CONN_STATUS_DISCONNECTED, - client->server_connection_user_arg); - break; - } -} - -static int server_fd_dispatch(struct cras_client *client, int poll_revents) -{ - int rc = 0; - cras_socket_state_t old_state; - - if ((poll_revents & POLLHUP) != 0) { - /* Error or disconnect: cleanup and make a state change now. */ - disconnect_transition_action(client, true); - } - old_state = client->server_fd_state; - - switch (client->server_fd_state) { - case CRAS_SOCKET_STATE_DISCONNECTED: - /* Assume that we've taken the necessary actions. */ - return -ENOTCONN; - case CRAS_SOCKET_STATE_ERROR_DELAY: - rc = error_delay_next_action(client, poll_revents); - break; - case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET: - wait_for_socket_next_action(client); - break; - case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE: - rc = wait_for_writable_next_action(client, poll_revents); - break; - case CRAS_SOCKET_STATE_FIRST_MESSAGE: - rc = first_message_next_action(client, poll_revents); - break; - case CRAS_SOCKET_STATE_CONNECTED: - if ((poll_revents & POLLIN) != 0) - rc = handle_message_from_server(client); - break; - } - - if (rc != 0) { - /* If there is an error, then start-over. */ - rc = server_fd_dispatch(client, POLLHUP); - } else if (old_state != client->server_fd_state) { - /* There was a state change, process the new state now. */ - rc = server_fd_dispatch(client, 0); - } - return rc; -} - -/* - * Start connecting to the server if we aren't already. - */ -static int server_connect(struct cras_client *client) -{ - if (client->server_fd_state != CRAS_SOCKET_STATE_DISCONNECTED) - return 0; - /* Start waiting for the server socket to exist. */ - server_fd_move_to_state(client, CRAS_SOCKET_STATE_WAIT_FOR_SOCKET); - return server_fd_dispatch(client, 0); -} - -/* - * Disconnect from the server if we haven't already. - */ -static void server_disconnect(struct cras_client *client) -{ - if (client->server_fd_state == CRAS_SOCKET_STATE_DISCONNECTED) - return; - /* Set the disconnected state first so that the disconnect - * transition doesn't move the server state to ERROR_DELAY. */ - server_fd_move_to_state(client, CRAS_SOCKET_STATE_DISCONNECTED); - disconnect_transition_action(client, false); -} - -/* - * Called when something happens to the socket file. - */ -static void sock_file_wait_callback(void *context, cras_file_wait_event_t event, - const char *filename) -{ - struct cras_client *client = (struct cras_client *)context; - switch (event) { - case CRAS_FILE_WAIT_EVENT_CREATED: - client->sock_file_exists = 1; - switch (client->server_fd_state) { - case CRAS_SOCKET_STATE_DISCONNECTED: - case CRAS_SOCKET_STATE_ERROR_DELAY: - case CRAS_SOCKET_STATE_FIRST_MESSAGE: - case CRAS_SOCKET_STATE_CONNECTED: - break; - case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET: - case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE: - /* The socket file exists. Tell the server state - * machine. */ - server_fd_dispatch(client, 0); - break; - } - break; - case CRAS_FILE_WAIT_EVENT_DELETED: - client->sock_file_exists = 0; - switch (client->server_fd_state) { - case CRAS_SOCKET_STATE_DISCONNECTED: - break; - case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET: - case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE: - case CRAS_SOCKET_STATE_ERROR_DELAY: - case CRAS_SOCKET_STATE_FIRST_MESSAGE: - case CRAS_SOCKET_STATE_CONNECTED: - /* Restart the connection process. */ - server_disconnect(client); - server_connect(client); - break; - } - break; - case CRAS_FILE_WAIT_EVENT_NONE: - break; - } -} - -/* - * Service the sock_file_wait's fd. - * - * If the socket file is deleted, then cause a disconnect from the server. - * Otherwise, start a reconnect depending on the server_fd_state. - */ -static int sock_file_wait_dispatch(struct cras_client *client, int poll_revents) -{ - int rc; - - if ((poll_revents & POLLIN) == 0) - return 0; - - rc = cras_file_wait_dispatch(client->sock_file_wait); - if (rc == -EAGAIN || rc == -EWOULDBLOCK) - rc = 0; - else if (rc != 0) - syslog(LOG_ERR, "cras_file_wait_dispatch: %s", strerror(-rc)); - return rc; -} - -/* - * Waits until we have heard back from the server so that we know we are - * connected. - * - * The connected success/failure message is always the first message the server - * sends. Return non zero if client is connected to the server. A return code - * of zero means that the client is not connected to the server. - */ -static int check_server_connected_wait(struct cras_client *client, - struct timespec *timeout) -{ - int rc = 0; - struct pollfd poll_fd; - - poll_fd.fd = client->server_event_fd; - poll_fd.events = POLLIN; - poll_fd.revents = 0; - - /* The server_event_fd is only read and written by the functions - * that connect to the server. When a connection is established the - * eventfd has a value of 1 and cras_poll will return immediately - * with 1. When there is no connection to the server, then this - * function waits until the timeout has expired or a non-zero value - * is written to the server_event_fd. */ - while (rc == 0) - rc = cras_poll(&poll_fd, 1, timeout, NULL); - return rc > 0; -} - -/* Returns non-zero if the thread is running (not stopped). */ -static inline int thread_is_running(struct thread_state *thread) -{ - return thread->state != CRAS_THREAD_STOP; -} - -/* - * Opens the server socket and connects to it. - * Args: - * client - Client pointer created with cras_client_create(). - * timeout - Connection timeout. - * Returns: - * 0 for success, negative error code on failure. - */ -static int connect_to_server(struct cras_client *client, - struct timespec *timeout, bool use_command_thread) -{ - int rc; - struct pollfd poll_fd[2]; - struct timespec connected_timeout; - - if (!client) - return -EINVAL; - - if (thread_is_running(&client->thread) && use_command_thread) { - rc = cras_client_connect_async(client); - if (rc == 0) { - rc = check_server_connected_wait(client, timeout); - return rc ? 0 : -ESHUTDOWN; - } - } - - connected_timeout.tv_sec = 0; - connected_timeout.tv_nsec = 0; - if (check_server_connected_wait(client, &connected_timeout)) - return 0; - - poll_fd[0].fd = cras_file_wait_get_fd(client->sock_file_wait); - poll_fd[0].events = POLLIN; - - rc = server_connect(client); - while (rc == 0) { - // Wait until we've connected or until there is a timeout. - // Meanwhile handle incoming actions on our fds. - - server_fill_pollfd(client, &(poll_fd[1])); - rc = cras_poll(poll_fd, 2, timeout, NULL); - if (rc <= 0) - continue; - - if (poll_fd[0].revents) { - rc = sock_file_wait_dispatch(client, - poll_fd[0].revents); - continue; - } - - if (poll_fd[1].revents) { - rc = server_fd_dispatch(client, poll_fd[1].revents); - if (rc == 0 && client->server_fd_state == - CRAS_SOCKET_STATE_CONNECTED) - break; - } - } - - if (rc != 0) - syslog(LOG_ERR, "cras_client: Connect server failed: %s", - strerror(-rc)); - - return rc; -} - -static int connect_to_server_wait_retry(struct cras_client *client, - int timeout_ms, bool use_command_thread) -{ - struct timespec timeout_value; - struct timespec *timeout; - - if (timeout_ms < 0) { - timeout = NULL; - } else { - timeout = &timeout_value; - ms_to_timespec(timeout_ms, timeout); - } - - /* If connected, wait for the first message from the server - * indicating it's ready. */ - return connect_to_server(client, timeout, use_command_thread); -} - -/* - * Tries to connect to the server. Waits for the initial message from the - * server. This will happen near instantaneously if the server is already - * running. - */ -static int connect_to_server_wait(struct cras_client *client, - bool use_command_thread) -{ - return connect_to_server_wait_retry(client, SERVER_CONNECT_TIMEOUT_MS, - use_command_thread); -} - -/* - * Audio thread. - */ - -/* Sends a message from the stream to the client to indicate an error. - * If the running stream encounters an error, then it must tell the client - * to stop running it. - */ -static int send_stream_message(const struct client_stream *stream, - unsigned msg_id) -{ - int res; - struct stream_msg msg; - - msg.stream_id = stream->id; - msg.msg_id = msg_id; - res = write(stream->client->stream_fds[1], &msg, sizeof(msg)); - if (res != sizeof(msg)) - return -EPIPE; - - return 0; -} - -/* Blocks until there is data to be read from the read_fd or until woken by an - * incoming "poke" on wake_fd. Up to "len" bytes are read into "buf". */ -static int read_with_wake_fd(int wake_fd, int read_fd, uint8_t *buf, size_t len) -{ - struct pollfd pollfds[2]; - int nread = 0; - int nfds = 1; - int rc; - char tmp; - - pollfds[0].fd = wake_fd; - pollfds[0].events = POLLIN; - if (read_fd >= 0) { - nfds++; - pollfds[1].fd = read_fd; - pollfds[1].events = POLLIN; - } - - rc = poll(pollfds, nfds, -1); - if (rc < 0) - return rc; - if (read_fd >= 0 && pollfds[1].revents & POLLIN) { - nread = read(read_fd, buf, len); - if (nread != (int)len) - return -EIO; - } - if (pollfds[0].revents & POLLIN) { - rc = read(wake_fd, &tmp, 1); - if (rc < 0) - return rc; - } - - return nread; -} -/* Check the availability and configures a capture buffer. - * Args: - * stream - The input stream to configure buffer for. - * captured_frames - To be filled with the pointer to the beginning of - * captured buffer. - * num_frames - Number of captured frames. - * Returns: - * Number of frames available in captured_frames. - */ -static unsigned int config_capture_buf(struct client_stream *stream, - uint8_t **captured_frames, - unsigned int num_frames) -{ - /* Always return the beginning of the read buffer because Chrome expects - * so. */ - *captured_frames = cras_shm_get_read_buffer_base(stream->shm); - - /* Don't ask for more frames than the client desires. */ - if (stream->flags & BULK_AUDIO_OK) - num_frames = MIN(num_frames, stream->config->buffer_frames); - else - num_frames = MIN(num_frames, stream->config->cb_threshold); - - /* If shm readable frames is less than client requests, that means - * overrun has happened in server side. Don't send partial corrupted - * buffer to client. */ - if (cras_shm_get_curr_read_frames(stream->shm) < num_frames) - return 0; - - return num_frames; -} - -static void complete_capture_read_current(struct client_stream *stream, - unsigned int num_frames) -{ - cras_shm_buffer_read_current(stream->shm, num_frames); -} - -static int send_capture_reply(struct client_stream *stream, unsigned int frames, - int err) -{ - struct audio_message aud_msg; - int rc; - - if (!cras_stream_uses_input_hw(stream->direction)) - return 0; - - aud_msg.id = AUDIO_MESSAGE_DATA_CAPTURED; - aud_msg.frames = frames; - aud_msg.error = err; - - rc = write(stream->aud_fd, &aud_msg, sizeof(aud_msg)); - if (rc != sizeof(aud_msg)) - return -EPIPE; - - return 0; -} - -/* For capture streams this handles the message signalling that data is ready to - * be passed to the user of this stream. Calls the audio callback with the new - * samples, and mark them as read. - * Args: - * stream - The stream the message was received for. - * num_frames - The number of captured frames. - * Returns: - * 0, unless there is a fatal error or the client declares enod of file. - */ -static int handle_capture_data_ready(struct client_stream *stream, - unsigned int num_frames) -{ - int frames; - struct cras_stream_params *config; - uint8_t *captured_frames; - struct timespec ts; - int rc = 0; - struct libcras_stream_cb_data *data; - - config = stream->config; - /* If this message is for an output stream, log error and drop it. */ - if (!cras_stream_has_input(stream->direction)) { - syslog(LOG_ERR, "cras_client: Play data to input\n"); - return 0; - } - - num_frames = config_capture_buf(stream, &captured_frames, num_frames); - if (num_frames == 0) - return 0; - - cras_timespec_to_timespec(&ts, &stream->shm->header->ts); - - if (config->stream_cb) { - data = libcras_stream_cb_data_create( - stream->id, stream->direction, captured_frames, - num_frames, ts, config->user_data); - if (!data) - return -errno; - frames = config->stream_cb(data); - libcras_stream_cb_data_destroy(data); - data = NULL; - } else if (config->unified_cb) { - frames = config->unified_cb(stream->client, stream->id, - captured_frames, NULL, num_frames, - &ts, NULL, config->user_data); - } else { - frames = config->aud_cb(stream->client, stream->id, - captured_frames, num_frames, &ts, - config->user_data); - } - if (frames < 0) { - send_stream_message(stream, CLIENT_STREAM_EOF); - rc = frames; - goto reply_captured; - } - if (frames == 0) - return 0; - - complete_capture_read_current(stream, frames); -reply_captured: - return send_capture_reply(stream, frames, rc); -} - -/* Notifies the server that "frames" samples have been written. */ -static int send_playback_reply(struct client_stream *stream, - unsigned int frames, int error) -{ - struct audio_message aud_msg; - int rc; - - if (!cras_stream_uses_output_hw(stream->direction)) - return 0; - - aud_msg.id = AUDIO_MESSAGE_DATA_READY; - aud_msg.frames = frames; - aud_msg.error = error; - - rc = write(stream->aud_fd, &aud_msg, sizeof(aud_msg)); - if (rc != sizeof(aud_msg)) - return -EPIPE; - - return 0; -} - -/* For playback streams when current buffer is empty, this handles the request - * for more samples by calling the audio callback for the thread, and signaling - * the server that the samples have been written. */ -static int handle_playback_request(struct client_stream *stream, - unsigned int num_frames) -{ - uint8_t *buf; - int frames; - int rc = 0; - struct cras_stream_params *config; - struct cras_audio_shm *shm = stream->shm; - struct timespec ts; - struct libcras_stream_cb_data *data; - - config = stream->config; - - /* If this message is for an input stream, log error and drop it. */ - if (stream->direction != CRAS_STREAM_OUTPUT) { - syslog(LOG_ERR, "cras_client: Record data from output\n"); - return 0; - } - - buf = cras_shm_get_write_buffer_base(shm); - - /* Limit the amount of frames to the configured amount. */ - num_frames = MIN(num_frames, config->cb_threshold); - - cras_timespec_to_timespec(&ts, &shm->header->ts); - - /* Get samples from the user */ - if (config->stream_cb) { - data = libcras_stream_cb_data_create(stream->id, - stream->direction, buf, - num_frames, ts, - config->user_data); - if (!data) - return -errno; - frames = config->stream_cb(data); - libcras_stream_cb_data_destroy(data); - data = NULL; - } else if (config->unified_cb) { - frames = config->unified_cb(stream->client, stream->id, NULL, - buf, num_frames, NULL, &ts, - config->user_data); - } else { - frames = config->aud_cb(stream->client, stream->id, buf, - num_frames, &ts, config->user_data); - } - if (frames < 0) { - send_stream_message(stream, CLIENT_STREAM_EOF); - rc = frames; - goto reply_written; - } - - cras_shm_buffer_written_start(shm, frames); - -reply_written: - /* Signal server that data is ready, or that an error has occurred. */ - rc = send_playback_reply(stream, frames, rc); - return rc; -} - -static void audio_thread_set_priority(struct client_stream *stream) -{ - /* Use provided callback to set priority if available. */ - if (stream->client->thread_priority_cb) { - stream->client->thread_priority_cb(stream->client); - return; - } - - /* Try to get RT scheduling, if that fails try to set the nice value. */ - if (cras_set_rt_scheduling(CRAS_CLIENT_RT_THREAD_PRIORITY) || - cras_set_thread_priority(CRAS_CLIENT_RT_THREAD_PRIORITY)) - cras_set_nice_level(CRAS_CLIENT_NICENESS_LEVEL); -} - -/* Listens to the audio socket for messages from the server indicating that - * the stream needs to be serviced. One of these runs per stream. */ -static void *audio_thread(void *arg) -{ - struct client_stream *stream = (struct client_stream *)arg; - int thread_terminated = 0; - struct audio_message aud_msg; - int aud_fd; - int num_read; - - if (arg == NULL) - return (void *)-EIO; - - audio_thread_set_priority(stream); - - /* Notify the control thread that we've started. */ - pthread_mutex_lock(&stream->client->stream_start_lock); - pthread_cond_broadcast(&stream->client->stream_start_cond); - pthread_mutex_unlock(&stream->client->stream_start_lock); - - while (thread_is_running(&stream->thread) && !thread_terminated) { - /* While we are warming up, aud_fd may not be valid and some - * shared memory resources may not yet be available. */ - aud_fd = (stream->thread.state == CRAS_THREAD_WARMUP) ? - -1 : - stream->aud_fd; - num_read = - read_with_wake_fd(stream->wake_fds[0], aud_fd, - (uint8_t *)&aud_msg, sizeof(aud_msg)); - if (num_read < 0) - return (void *)-EIO; - if (num_read == 0) - continue; - - switch (aud_msg.id) { - case AUDIO_MESSAGE_DATA_READY: - thread_terminated = handle_capture_data_ready( - stream, aud_msg.frames); - break; - case AUDIO_MESSAGE_REQUEST_DATA: - thread_terminated = - handle_playback_request(stream, aud_msg.frames); - break; - default: - break; - } - } - - return NULL; -} - -/* Pokes the audio thread so that it can notice if it has been terminated. */ -static int wake_aud_thread(struct client_stream *stream) -{ - int rc; - - rc = write(stream->wake_fds[1], &rc, 1); - if (rc != 1) - return rc; - return 0; -} - -/* Stop the audio thread for the given stream. - * Args: - * stream - Stream for which to stop the audio thread. - * join - When non-zero, attempt to join the audio thread (wait for it to - * complete). - */ -static void stop_aud_thread(struct client_stream *stream, int join) -{ - if (thread_is_running(&stream->thread)) { - stream->thread.state = CRAS_THREAD_STOP; - wake_aud_thread(stream); - if (join) - pthread_join(stream->thread.tid, NULL); - } - - if (stream->wake_fds[0] >= 0) { - close(stream->wake_fds[0]); - close(stream->wake_fds[1]); - stream->wake_fds[0] = -1; - } -} - -/* Start the audio thread for this stream. - * Returns when the thread has started and is waiting. - * Args: - * stream - The stream that needs an audio thread. - * Returns: - * 0 for success, or a negative error code. - */ -static int start_aud_thread(struct client_stream *stream) -{ - int rc; - struct timespec future; - - rc = pipe(stream->wake_fds); - if (rc < 0) { - rc = -errno; - syslog(LOG_ERR, "cras_client: pipe: %s", strerror(-rc)); - return rc; - } - - stream->thread.state = CRAS_THREAD_WARMUP; - - pthread_mutex_lock(&stream->client->stream_start_lock); - rc = pthread_create(&stream->thread.tid, NULL, audio_thread, stream); - if (rc) { - pthread_mutex_unlock(&stream->client->stream_start_lock); - syslog(LOG_ERR, "cras_client: Couldn't create audio stream: %s", - strerror(rc)); - stream->thread.state = CRAS_THREAD_STOP; - stop_aud_thread(stream, 0); - return -rc; - } - - clock_gettime(CLOCK_REALTIME, &future); - future.tv_sec += 2; /* Wait up to two seconds. */ - rc = pthread_cond_timedwait(&stream->client->stream_start_cond, - &stream->client->stream_start_lock, - &future); - pthread_mutex_unlock(&stream->client->stream_start_lock); - if (rc != 0) { - /* Something is very wrong: try to cancel the thread and don't - * wait for it. */ - syslog(LOG_ERR, "cras_client: Client thread not responding: %s", - strerror(rc)); - stop_aud_thread(stream, 0); - return -rc; - } - return 0; -} - -/* - * Client thread. - */ - -/* Gets the update_count of the server state shm region. */ -static inline unsigned -begin_server_state_read(const struct cras_server_state *state) -{ - unsigned count; - - /* Version will be odd when the server is writing. */ - while ((count = *(volatile unsigned *)&state->update_count) & 1) - sched_yield(); - __sync_synchronize(); - return count; -} - -/* Checks if the update count of the server state shm region has changed from - * count. Returns 0 if the count still matches. - */ -static inline int end_server_state_read(const struct cras_server_state *state, - unsigned count) -{ - __sync_synchronize(); - if (count != *(volatile unsigned *)&state->update_count) - return -EAGAIN; - return 0; -} - -/* Release shm areas if references to them are held. */ -static void free_shm(struct client_stream *stream) -{ - cras_audio_shm_destroy(stream->shm); - stream->shm = NULL; -} - -/* Handles the stream connected message from the server. Check if we need a - * format converter, configure the shared memory region, and start the audio - * thread that will handle requests from the server. */ -static int stream_connected(struct client_stream *stream, - const struct cras_client_stream_connected *msg, - const int stream_fds[2], const unsigned int num_fds) -{ - int rc, samples_prot; - unsigned int i; - struct cras_shm_info header_info, samples_info; - - if (msg->err || num_fds != 2) { - syslog(LOG_ERR, "cras_client: Error setting up stream %d\n", - msg->err); - rc = msg->err; - goto err_ret; - } - - rc = cras_shm_info_init_with_fd(stream_fds[0], cras_shm_header_size(), - &header_info); - if (rc < 0) - goto err_ret; - - rc = cras_shm_info_init_with_fd(stream_fds[1], msg->samples_shm_size, - &samples_info); - if (rc < 0) { - cras_shm_info_cleanup(&header_info); - goto err_ret; - } - - if (stream->direction == CRAS_STREAM_OUTPUT) - samples_prot = PROT_WRITE; - else - samples_prot = PROT_READ; - - rc = cras_audio_shm_create(&header_info, &samples_info, samples_prot, - &stream->shm); - if (rc < 0) { - syslog(LOG_ERR, "cras_client: Error configuring shm"); - goto err_ret; - } - cras_shm_copy_shared_config(stream->shm); - cras_shm_set_volume_scaler(stream->shm, stream->volume_scaler); - - stream->thread.state = CRAS_THREAD_RUNNING; - wake_aud_thread(stream); - - close(stream_fds[0]); - close(stream_fds[1]); - return 0; -err_ret: - stop_aud_thread(stream, 1); - for (i = 0; i < num_fds; i++) - close(stream_fds[i]); - free_shm(stream); - return rc; -} - -static int send_connect_message(struct cras_client *client, - struct client_stream *stream, uint32_t dev_idx) -{ - int rc; - struct cras_connect_message serv_msg; - int sock[2] = { -1, -1 }; - - /* Create a socket pair for the server to notify of audio events. */ - rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); - if (rc != 0) { - rc = -errno; - syslog(LOG_ERR, "cras_client: socketpair: %s", strerror(-rc)); - goto fail; - } - - cras_fill_connect_message(&serv_msg, stream->config->direction, - stream->id, stream->config->stream_type, - stream->config->client_type, - stream->config->buffer_frames, - stream->config->cb_threshold, stream->flags, - stream->config->effects, - stream->config->format, dev_idx); - - rc = cras_send_with_fds(client->server_fd, &serv_msg, sizeof(serv_msg), - &sock[1], 1); - if (rc != sizeof(serv_msg)) { - rc = EIO; - syslog(LOG_ERR, - "cras_client: add_stream: Send server message failed."); - goto fail; - } - - stream->aud_fd = sock[0]; - close(sock[1]); - return 0; - -fail: - if (sock[0] != -1) - close(sock[0]); - if (sock[1] != -1) - close(sock[1]); - return rc; -} - -/* Adds a stream to a running client. Checks to make sure that the client is - * attached, waits if it isn't. The stream is prepared on the main thread and - * passed here. */ -static int client_thread_add_stream(struct cras_client *client, - struct client_stream *stream, - cras_stream_id_t *stream_id_out, - uint32_t dev_idx) -{ - int rc; - cras_stream_id_t new_id; - struct client_stream *out; - - if ((stream->flags & HOTWORD_STREAM) == HOTWORD_STREAM) { - int hotword_idx; - hotword_idx = cras_client_get_first_dev_type_idx( - client, CRAS_NODE_TYPE_HOTWORD, CRAS_STREAM_INPUT); - - /* Find the hotword device index. */ - if (dev_idx == NO_DEVICE) { - if (hotword_idx < 0) { - syslog(LOG_ERR, - "cras_client: add_stream: No hotword dev"); - return hotword_idx; - } else { - dev_idx = (uint32_t)hotword_idx; - } - } - /* A known Use case for client to pin hotword stream on a not - * hotword device is to use internal mic for Assistant to work - * on board without usable DSP hotwording. We assume there will - * be only one hotword device exists. */ - else if (dev_idx != (uint32_t)hotword_idx) { - /* Unmask the flag to fallback to normal pinned stream - * on specified device. */ - stream->flags &= ~HOTWORD_STREAM; - } - } - - /* Find an available stream id. */ - do { - new_id = cras_get_stream_id(client->id, client->next_stream_id); - client->next_stream_id++; - DL_SEARCH_SCALAR(client->streams, out, id, new_id); - } while (out != NULL); - - stream->id = new_id; - *stream_id_out = new_id; - stream->client = client; - - /* Start the audio thread. */ - rc = start_aud_thread(stream); - if (rc != 0) - return rc; - - /* Start the thread associated with this stream. */ - /* send a message to the server asking that the stream be started. */ - rc = send_connect_message(client, stream, dev_idx); - if (rc != 0) { - stop_aud_thread(stream, 1); - return rc; - } - - /* Add the stream to the linked list */ - DL_APPEND(client->streams, stream); - - return 0; -} - -/* Removes a stream from a running client from within the running client's - * context. */ -static int client_thread_rm_stream(struct cras_client *client, - cras_stream_id_t stream_id) -{ - struct cras_disconnect_stream_message msg; - struct client_stream *stream = stream_from_id(client, stream_id); - int rc; - - if (stream == NULL) - return 0; - - /* Tell server to remove. */ - if (client->server_fd_state == CRAS_SOCKET_STATE_CONNECTED) { - cras_fill_disconnect_stream_message(&msg, stream_id); - rc = write(client->server_fd, &msg, sizeof(msg)); - if (rc < 0) - syslog(LOG_ERR, - "cras_client: error removing stream from server\n"); - } - - /* And shut down locally. */ - stop_aud_thread(stream, 1); - - free_shm(stream); - - DL_DELETE(client->streams, stream); - if (stream->aud_fd >= 0) - close(stream->aud_fd); - - free(stream->config); - free(stream); - - return 0; -} - -/* Sets the volume scaling factor for a playback or capture stream. */ -static int client_thread_set_stream_volume(struct cras_client *client, - cras_stream_id_t stream_id, - float volume_scaler) -{ - struct client_stream *stream; - - stream = stream_from_id(client, stream_id); - if (stream == NULL || volume_scaler > 1.0 || volume_scaler < 0.0) - return -EINVAL; - - stream->volume_scaler = volume_scaler; - if (stream->shm) - cras_shm_set_volume_scaler(stream->shm, volume_scaler); - - return 0; -} - -/* Attach to the shm region containing the audio thread log. */ -static void attach_atlog_shm(struct cras_client *client, int fd) -{ - client->atlog_ro = (struct audio_thread_event_log *)mmap( - NULL, sizeof(*client->atlog_ro), PROT_READ, MAP_SHARED, fd, 0); - close(fd); -} - -/* Attach to the shm region containing the server state. */ -static int client_attach_shm(struct cras_client *client, int shm_fd) -{ - int lock_rc; - int rc; - - lock_rc = server_state_wrlock(client); - if (client->server_state) { - rc = -EBUSY; - goto error; - } - - client->server_state = (struct cras_server_state *)mmap( - NULL, sizeof(*client->server_state), PROT_READ, MAP_SHARED, - shm_fd, 0); - rc = -errno; - close(shm_fd); - if (client->server_state == (struct cras_server_state *)-1) { - syslog(LOG_ERR, - "cras_client: mmap failed to map shm for client: %s", - strerror(-rc)); - goto error; - } - - if (client->server_state->state_version != CRAS_SERVER_STATE_VERSION) { - munmap((void *)client->server_state, - sizeof(*client->server_state)); - client->server_state = NULL; - rc = -EINVAL; - syslog(LOG_ERR, "cras_client: Unknown server_state version."); - } else { - rc = 0; - } - -error: - server_state_unlock(client, lock_rc); - return rc; -} - -static void cras_client_get_hotword_models_ready(struct cras_client *client, - const char *hotword_models) -{ - if (!client->get_hotword_models_cb) - return; - client->get_hotword_models_cb(client, hotword_models); - client->get_hotword_models_cb = NULL; -} - -/* Handles messages from the cras server. */ -static int handle_message_from_server(struct cras_client *client) -{ - uint8_t buf[CRAS_CLIENT_MAX_MSG_SIZE]; - struct cras_client_message *msg; - int rc = 0; - int nread; - int server_fds[2]; - unsigned int num_fds = 2; - - msg = (struct cras_client_message *)buf; - nread = cras_recv_with_fds(client->server_fd, buf, sizeof(buf), - server_fds, &num_fds); - if (nread < (int)sizeof(msg->length) || (int)msg->length != nread) - return -EIO; - - switch (msg->id) { - case CRAS_CLIENT_CONNECTED: { - struct cras_client_connected *cmsg = - (struct cras_client_connected *)msg; - if (num_fds != 1) - return -EINVAL; - rc = client_attach_shm(client, server_fds[0]); - if (rc) - return rc; - client->id = cmsg->client_id; - - break; - } - case CRAS_CLIENT_STREAM_CONNECTED: { - struct cras_client_stream_connected *cmsg = - (struct cras_client_stream_connected *)msg; - struct client_stream *stream = - stream_from_id(client, cmsg->stream_id); - if (stream == NULL) { - if (num_fds != 2) { - syslog(LOG_ERR, - "cras_client: Error receiving " - "stream 0x%x connected message", - cmsg->stream_id); - return -EINVAL; - } - - /* - * Usually, the fds should be closed in stream_connected - * callback. However, sometimes a stream is removed - * before it is connected. - */ - close(server_fds[0]); - close(server_fds[1]); - break; - } - rc = stream_connected(stream, cmsg, server_fds, num_fds); - if (rc < 0) - stream->config->err_cb(stream->client, stream->id, rc, - stream->config->user_data); - break; - } - case CRAS_CLIENT_AUDIO_DEBUG_INFO_READY: - if (client->debug_info_callback) - client->debug_info_callback(client); - client->debug_info_callback = NULL; - break; - case CRAS_CLIENT_ATLOG_FD_READY: - if (num_fds != 1 || server_fds[0] < 0) - return -EINVAL; - attach_atlog_shm(client, server_fds[0]); - if (client->atlog_access_callback) - client->atlog_access_callback(client); - client->atlog_access_callback = NULL; - break; - case CRAS_CLIENT_GET_HOTWORD_MODELS_READY: { - struct cras_client_get_hotword_models_ready *cmsg = - (struct cras_client_get_hotword_models_ready *)msg; - cras_client_get_hotword_models_ready( - client, (const char *)cmsg->hotword_models); - break; - } - case CRAS_CLIENT_OUTPUT_VOLUME_CHANGED: { - struct cras_client_volume_changed *cmsg = - (struct cras_client_volume_changed *)msg; - if (client->observer_ops.output_volume_changed) - client->observer_ops.output_volume_changed( - client->observer_context, cmsg->volume); - break; - } - case CRAS_CLIENT_OUTPUT_MUTE_CHANGED: { - struct cras_client_mute_changed *cmsg = - (struct cras_client_mute_changed *)msg; - if (client->observer_ops.output_mute_changed) - client->observer_ops.output_mute_changed( - client->observer_context, cmsg->muted, - cmsg->user_muted, cmsg->mute_locked); - break; - } - case CRAS_CLIENT_CAPTURE_GAIN_CHANGED: { - struct cras_client_volume_changed *cmsg = - (struct cras_client_volume_changed *)msg; - if (client->observer_ops.capture_gain_changed) - client->observer_ops.capture_gain_changed( - client->observer_context, cmsg->volume); - break; - } - case CRAS_CLIENT_CAPTURE_MUTE_CHANGED: { - struct cras_client_mute_changed *cmsg = - (struct cras_client_mute_changed *)msg; - if (client->observer_ops.capture_mute_changed) - client->observer_ops.capture_mute_changed( - client->observer_context, cmsg->muted, - cmsg->mute_locked); - break; - } - case CRAS_CLIENT_NODES_CHANGED: { - if (client->observer_ops.nodes_changed) - client->observer_ops.nodes_changed( - client->observer_context); - break; - } - case CRAS_CLIENT_ACTIVE_NODE_CHANGED: { - struct cras_client_active_node_changed *cmsg = - (struct cras_client_active_node_changed *)msg; - enum CRAS_STREAM_DIRECTION direction = - (enum CRAS_STREAM_DIRECTION)cmsg->direction; - if (client->observer_ops.active_node_changed) - client->observer_ops.active_node_changed( - client->observer_context, direction, - cmsg->node_id); - break; - } - case CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED: { - struct cras_client_node_value_changed *cmsg = - (struct cras_client_node_value_changed *)msg; - if (client->observer_ops.output_node_volume_changed) - client->observer_ops.output_node_volume_changed( - client->observer_context, cmsg->node_id, - cmsg->value); - break; - } - case CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED: { - struct cras_client_node_value_changed *cmsg = - (struct cras_client_node_value_changed *)msg; - if (client->observer_ops.node_left_right_swapped_changed) - client->observer_ops.node_left_right_swapped_changed( - client->observer_context, cmsg->node_id, - cmsg->value); - break; - } - case CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED: { - struct cras_client_node_value_changed *cmsg = - (struct cras_client_node_value_changed *)msg; - if (client->observer_ops.input_node_gain_changed) - client->observer_ops.input_node_gain_changed( - client->observer_context, cmsg->node_id, - cmsg->value); - break; - } - case CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED: { - struct cras_client_num_active_streams_changed *cmsg = - (struct cras_client_num_active_streams_changed *)msg; - enum CRAS_STREAM_DIRECTION direction = - (enum CRAS_STREAM_DIRECTION)cmsg->direction; - if (client->observer_ops.num_active_streams_changed) - client->observer_ops.num_active_streams_changed( - client->observer_context, direction, - cmsg->num_active_streams); - break; - } - default: - break; - } - - return 0; -} - -/* Handles messages from streams to this client. */ -static int handle_stream_message(struct cras_client *client, int poll_revents) -{ - struct stream_msg msg; - int rc; - - if ((poll_revents & POLLIN) == 0) - return 0; - - rc = read(client->stream_fds[0], &msg, sizeof(msg)); - if (rc < 0) - syslog(LOG_ERR, "cras_client: Stream read failed %d\n", errno); - /* The only reason a stream sends a message is if it needs to be - * removed. An error on read would mean the same thing so regardless of - * what gets us here, just remove the stream */ - client_thread_rm_stream(client, msg.stream_id); - return 0; -} - -/* Handles messages from users to this client. */ -static int handle_command_message(struct cras_client *client, int poll_revents) -{ - uint8_t buf[MAX_CMD_MSG_LEN]; - struct command_msg *msg = (struct command_msg *)buf; - int rc, to_read; - - if ((poll_revents & POLLIN) == 0) - return 0; - - rc = read(client->command_fds[0], buf, sizeof(msg->len)); - if (rc != sizeof(msg->len) || msg->len > MAX_CMD_MSG_LEN) { - rc = -EIO; - goto cmd_msg_complete; - } - to_read = msg->len - rc; - rc = read(client->command_fds[0], &buf[0] + rc, to_read); - if (rc != to_read) { - rc = -EIO; - goto cmd_msg_complete; - } - - switch (msg->msg_id) { - case CLIENT_STOP: { - struct client_stream *s; - - /* Stop all playing streams */ - DL_FOREACH (client->streams, s) - client_thread_rm_stream(client, s->id); - - /* And stop this client */ - client->thread.state = CRAS_THREAD_STOP; - rc = 0; - break; - } - case CLIENT_ADD_STREAM: { - struct add_stream_command_message *add_msg = - (struct add_stream_command_message *)msg; - rc = client_thread_add_stream(client, add_msg->stream, - add_msg->stream_id_out, - add_msg->dev_idx); - break; - } - case CLIENT_REMOVE_STREAM: - rc = client_thread_rm_stream(client, msg->stream_id); - break; - case CLIENT_SET_STREAM_VOLUME_SCALER: { - struct set_stream_volume_command_message *vol_msg = - (struct set_stream_volume_command_message *)msg; - rc = client_thread_set_stream_volume(client, - vol_msg->header.stream_id, - vol_msg->volume_scaler); - break; - } - case CLIENT_SERVER_CONNECT: - rc = connect_to_server_wait(client, false); - break; - case CLIENT_SERVER_CONNECT_ASYNC: - rc = server_connect(client); - break; - default: - assert(0); - break; - } - -cmd_msg_complete: - /* Wake the waiting main thread with the result of the command. */ - if (write(client->command_reply_fds[1], &rc, sizeof(rc)) != sizeof(rc)) - return -EIO; - return rc; -} - -/* This thread handles non audio sample communication with the audio server. - * The client program will call fucntions below to send messages to this thread - * to add or remove streams or change parameters. - */ -static void *client_thread(void *arg) -{ - struct cras_client *client = (struct cras_client *)arg; - struct pollfd pollfds[4]; - int (*cbs[4])(struct cras_client * client, int poll_revents); - unsigned int num_pollfds, i; - int rc; - - if (arg == NULL) - return (void *)-EINVAL; - - while (thread_is_running(&client->thread)) { - num_pollfds = 0; - - rc = cras_file_wait_get_fd(client->sock_file_wait); - if (rc >= 0) { - cbs[num_pollfds] = sock_file_wait_dispatch; - pollfds[num_pollfds].fd = rc; - pollfds[num_pollfds].events = POLLIN; - pollfds[num_pollfds].revents = 0; - num_pollfds++; - } else - syslog(LOG_ERR, "file wait fd: %d", rc); - if (client->server_fd >= 0) { - cbs[num_pollfds] = server_fd_dispatch; - server_fill_pollfd(client, &(pollfds[num_pollfds])); - num_pollfds++; - } - if (client->command_fds[0] >= 0) { - cbs[num_pollfds] = handle_command_message; - pollfds[num_pollfds].fd = client->command_fds[0]; - pollfds[num_pollfds].events = POLLIN; - pollfds[num_pollfds].revents = 0; - num_pollfds++; - } - if (client->stream_fds[0] >= 0) { - cbs[num_pollfds] = handle_stream_message; - pollfds[num_pollfds].fd = client->stream_fds[0]; - pollfds[num_pollfds].events = POLLIN; - pollfds[num_pollfds].revents = 0; - num_pollfds++; - } - - rc = poll(pollfds, num_pollfds, -1); - if (rc <= 0) - continue; - - for (i = 0; i < num_pollfds; i++) { - /* Only do one at a time, since some messages may - * result in change to other fds. */ - if (pollfds[i].revents) { - cbs[i](client, pollfds[i].revents); - break; - } - } - } - - /* close the command reply pipe. */ - close(client->command_reply_fds[1]); - client->command_reply_fds[1] = -1; - - return NULL; -} - -/* Sends a message to the client thread to complete an action requested by the - * user. Then waits for the action to complete and returns the result. */ -static int send_command_message(struct cras_client *client, - struct command_msg *msg) -{ - int rc, cmd_res; - if (client == NULL || !thread_is_running(&client->thread)) - return -EINVAL; - - rc = write(client->command_fds[1], msg, msg->len); - if (rc != (int)msg->len) - return -EPIPE; - - /* Wait for command to complete. */ - rc = read(client->command_reply_fds[0], &cmd_res, sizeof(cmd_res)); - if (rc != sizeof(cmd_res)) - return -EPIPE; - return cmd_res; -} - -/* Send a simple message to the client thread that holds no data. */ -static int send_simple_cmd_msg(struct cras_client *client, - cras_stream_id_t stream_id, unsigned msg_id) -{ - struct command_msg msg; - - msg.len = sizeof(msg); - msg.stream_id = stream_id; - msg.msg_id = msg_id; - - return send_command_message(client, &msg); -} - -/* Sends the set volume message to the client thread. */ -static int send_stream_volume_command_msg(struct cras_client *client, - cras_stream_id_t stream_id, - float volume_scaler) -{ - struct set_stream_volume_command_message msg; - - msg.header.len = sizeof(msg); - msg.header.stream_id = stream_id; - msg.header.msg_id = CLIENT_SET_STREAM_VOLUME_SCALER; - msg.volume_scaler = volume_scaler; - - return send_command_message(client, &msg.header); -} - -/* Sends a message back to the client and returns the error code. */ -static int write_message_to_server(struct cras_client *client, - const struct cras_server_message *msg) -{ - ssize_t write_rc = -EPIPE; - - if (client->server_fd_state == CRAS_SOCKET_STATE_CONNECTED || - client->server_fd_state == CRAS_SOCKET_STATE_FIRST_MESSAGE) { - write_rc = write(client->server_fd, msg, msg->length); - if (write_rc < 0) - write_rc = -errno; - } - - if (write_rc != (ssize_t)msg->length && - client->server_fd_state != CRAS_SOCKET_STATE_FIRST_MESSAGE) - return -EPIPE; - - if (write_rc < 0) - return write_rc; - else if (write_rc != (ssize_t)msg->length) - return -EIO; - else - return 0; -} - -/* Fills server socket file to connect by client's connection type. */ -static int fill_socket_file(struct cras_client *client, - enum CRAS_CONNECTION_TYPE conn_type) -{ - int rc; - - client->sock_file = - (const char *)calloc(CRAS_MAX_SOCKET_PATH_SIZE, sizeof(char)); - if (client->sock_file == NULL) - return -ENOMEM; - - rc = cras_fill_socket_path(conn_type, (char *)client->sock_file); - if (rc < 0) { - free((void *)client->sock_file); - return rc; - } - return 0; -} - -/* - * Exported Client Interface - */ - -int cras_client_create_with_type(struct cras_client **client, - enum CRAS_CONNECTION_TYPE conn_type) -{ - int rc; - struct client_int *client_int; - pthread_condattr_t cond_attr; - - if (!cras_validate_connection_type(conn_type)) { - syslog(LOG_ERR, "Input connection type is not supported.\n"); - return -EINVAL; - } - - /* Ignore SIGPIPE while using this API. */ - signal(SIGPIPE, SIG_IGN); - - client_int = (struct client_int *)calloc(1, sizeof(*client_int)); - if (!client_int) - return -ENOMEM; - *client = &client_int->client; - (*client)->server_fd = -1; - (*client)->id = -1; - - rc = pthread_rwlock_init(&client_int->server_state_rwlock, NULL); - if (rc != 0) { - syslog(LOG_ERR, "cras_client: Could not init state rwlock."); - rc = -rc; - goto free_client; - } - - rc = pthread_mutex_init(&(*client)->stream_start_lock, NULL); - if (rc != 0) { - syslog(LOG_ERR, "cras_client: Could not init start lock."); - rc = -rc; - goto free_rwlock; - } - - pthread_condattr_init(&cond_attr); - pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); - rc = pthread_cond_init(&(*client)->stream_start_cond, &cond_attr); - pthread_condattr_destroy(&cond_attr); - if (rc != 0) { - syslog(LOG_ERR, "cras_client: Could not init start cond."); - rc = -rc; - goto free_lock; - } - - (*client)->server_event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if ((*client)->server_event_fd < 0) { - syslog(LOG_ERR, "cras_client: Could not setup server eventfd."); - rc = -errno; - goto free_cond; - } - - rc = fill_socket_file((*client), conn_type); - if (rc < 0) { - goto free_server_event_fd; - } - - rc = cras_file_wait_create((*client)->sock_file, - CRAS_FILE_WAIT_FLAG_NONE, - sock_file_wait_callback, *client, - &(*client)->sock_file_wait); - if (rc < 0 && rc != -ENOENT) { - syslog(LOG_ERR, - "cras_client: Could not setup watch for '%s': %s", - (*client)->sock_file, strerror(-rc)); - goto free_error; - } - (*client)->sock_file_exists = (rc == 0); - - /* Pipes used by the main thread and the client thread to send commands - * and replies. */ - rc = pipe((*client)->command_fds); - if (rc < 0) - goto free_error; - /* Pipe used to communicate between the client thread and the audio - * thread. */ - rc = pipe((*client)->stream_fds); - if (rc < 0) { - close((*client)->command_fds[0]); - close((*client)->command_fds[1]); - goto free_error; - } - (*client)->command_reply_fds[0] = -1; - (*client)->command_reply_fds[1] = -1; - - return 0; -free_error: - cras_file_wait_destroy((*client)->sock_file_wait); - free((void *)(*client)->sock_file); -free_server_event_fd: - if ((*client)->server_event_fd >= 0) - close((*client)->server_event_fd); -free_cond: - pthread_cond_destroy(&(*client)->stream_start_cond); -free_lock: - pthread_mutex_destroy(&(*client)->stream_start_lock); -free_rwlock: - pthread_rwlock_destroy(&client_int->server_state_rwlock); -free_client: - *client = NULL; - free(client_int); - return rc; -} - -int cras_client_create(struct cras_client **client) -{ - return cras_client_create_with_type(client, CRAS_CONTROL); -} - -void cras_client_destroy(struct cras_client *client) -{ - struct client_int *client_int; - if (client == NULL) - return; - client_int = to_client_int(client); - client->server_connection_cb = NULL; - cras_client_stop(client); - server_disconnect(client); - close(client->server_event_fd); - close(client->command_fds[0]); - close(client->command_fds[1]); - close(client->stream_fds[0]); - close(client->stream_fds[1]); - cras_file_wait_destroy(client->sock_file_wait); - pthread_rwlock_destroy(&client_int->server_state_rwlock); - free((void *)client->sock_file); - free(client_int); -} - -int cras_client_connect(struct cras_client *client) -{ - return connect_to_server(client, NULL, true); -} - -int cras_client_connect_timeout(struct cras_client *client, - unsigned int timeout_ms) -{ - return connect_to_server_wait_retry(client, timeout_ms, true); -} - -int cras_client_connected_wait(struct cras_client *client) -{ - return send_simple_cmd_msg(client, 0, CLIENT_SERVER_CONNECT); -} - -int cras_client_connect_async(struct cras_client *client) -{ - return send_simple_cmd_msg(client, 0, CLIENT_SERVER_CONNECT_ASYNC); -} - -struct cras_stream_params *cras_client_stream_params_create( - enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames, - size_t cb_threshold, size_t unused, enum CRAS_STREAM_TYPE stream_type, - uint32_t flags, void *user_data, cras_playback_cb_t aud_cb, - cras_error_cb_t err_cb, struct cras_audio_format *format) -{ - struct cras_stream_params *params; - - params = (struct cras_stream_params *)malloc(sizeof(*params)); - if (params == NULL) - return NULL; - - params->direction = direction; - params->buffer_frames = buffer_frames; - params->cb_threshold = cb_threshold; - params->effects = 0; - params->stream_type = stream_type; - params->client_type = CRAS_CLIENT_TYPE_UNKNOWN; - params->flags = flags; - params->user_data = user_data; - params->aud_cb = aud_cb; - params->unified_cb = 0; - params->stream_cb = 0; - params->err_cb = err_cb; - memcpy(&(params->format), format, sizeof(*format)); - return params; -} - -void cras_client_stream_params_set_client_type( - struct cras_stream_params *params, enum CRAS_CLIENT_TYPE client_type) -{ - params->client_type = client_type; -} - -void cras_client_stream_params_enable_aec(struct cras_stream_params *params) -{ - params->effects |= APM_ECHO_CANCELLATION; -} - -void cras_client_stream_params_disable_aec(struct cras_stream_params *params) -{ - params->effects &= ~APM_ECHO_CANCELLATION; -} - -void cras_client_stream_params_enable_ns(struct cras_stream_params *params) -{ - params->effects |= APM_NOISE_SUPRESSION; -} - -void cras_client_stream_params_disable_ns(struct cras_stream_params *params) -{ - params->effects &= ~APM_NOISE_SUPRESSION; -} - -void cras_client_stream_params_enable_agc(struct cras_stream_params *params) -{ - params->effects |= APM_GAIN_CONTROL; -} - -void cras_client_stream_params_disable_agc(struct cras_stream_params *params) -{ - params->effects &= ~APM_GAIN_CONTROL; -} - -void cras_client_stream_params_enable_vad(struct cras_stream_params *params) -{ - params->effects |= APM_VOICE_DETECTION; -} - -void cras_client_stream_params_disable_vad(struct cras_stream_params *params) -{ - params->effects &= ~APM_VOICE_DETECTION; -} - -struct cras_stream_params *cras_client_unified_params_create( - enum CRAS_STREAM_DIRECTION direction, unsigned int block_size, - enum CRAS_STREAM_TYPE stream_type, uint32_t flags, void *user_data, - cras_unified_cb_t unified_cb, cras_error_cb_t err_cb, - struct cras_audio_format *format) -{ - struct cras_stream_params *params; - - params = (struct cras_stream_params *)malloc(sizeof(*params)); - if (params == NULL) - return NULL; - - params->direction = direction; - params->buffer_frames = block_size * 2; - params->cb_threshold = block_size; - params->stream_type = stream_type; - params->client_type = CRAS_CLIENT_TYPE_UNKNOWN; - params->flags = flags; - params->effects = 0; - params->user_data = user_data; - params->aud_cb = 0; - params->unified_cb = unified_cb; - params->stream_cb = 0; - params->err_cb = err_cb; - memcpy(&(params->format), format, sizeof(*format)); - - return params; -} - -void cras_client_stream_params_destroy(struct cras_stream_params *params) -{ - free(params); -} - -static inline int cras_client_send_add_stream_command_message( - struct cras_client *client, uint32_t dev_idx, - cras_stream_id_t *stream_id_out, struct cras_stream_params *config) -{ - struct add_stream_command_message cmd_msg; - struct client_stream *stream; - int rc = 0; - - if (client == NULL || config == NULL || stream_id_out == NULL) - return -EINVAL; - - if (config->stream_cb == NULL && config->aud_cb == NULL && - config->unified_cb == NULL) - return -EINVAL; - - if (config->err_cb == NULL) - return -EINVAL; - - stream = (struct client_stream *)calloc(1, sizeof(*stream)); - if (stream == NULL) { - rc = -ENOMEM; - goto add_failed; - } - stream->config = - (struct cras_stream_params *)malloc(sizeof(*(stream->config))); - if (stream->config == NULL) { - rc = -ENOMEM; - goto add_failed; - } - memcpy(stream->config, config, sizeof(*config)); - stream->aud_fd = -1; - stream->wake_fds[0] = -1; - stream->wake_fds[1] = -1; - stream->direction = config->direction; - stream->flags = config->flags; - - /* Caller might not set this volume scaler after stream created, - * so always initialize it to 1.0f */ - stream->volume_scaler = 1.0f; - - cmd_msg.header.len = sizeof(cmd_msg); - cmd_msg.header.msg_id = CLIENT_ADD_STREAM; - cmd_msg.header.stream_id = stream->id; - cmd_msg.stream = stream; - cmd_msg.stream_id_out = stream_id_out; - cmd_msg.dev_idx = dev_idx; - rc = send_command_message(client, &cmd_msg.header); - if (rc < 0) { - syslog(LOG_ERR, - "cras_client: adding stream failed in thread %d", rc); - goto add_failed; - } - - return 0; - -add_failed: - if (stream) { - if (stream->config) - free(stream->config); - free(stream); - } - return rc; -} - -int cras_client_add_stream(struct cras_client *client, - cras_stream_id_t *stream_id_out, - struct cras_stream_params *config) -{ - return cras_client_send_add_stream_command_message( - client, NO_DEVICE, stream_id_out, config); -} - -int cras_client_add_pinned_stream(struct cras_client *client, uint32_t dev_idx, - cras_stream_id_t *stream_id_out, - struct cras_stream_params *config) -{ - return cras_client_send_add_stream_command_message( - client, dev_idx, stream_id_out, config); -} - -int cras_client_rm_stream(struct cras_client *client, - cras_stream_id_t stream_id) -{ - if (client == NULL) - return -EINVAL; - - return send_simple_cmd_msg(client, stream_id, CLIENT_REMOVE_STREAM); -} - -int cras_client_set_stream_volume(struct cras_client *client, - cras_stream_id_t stream_id, - float volume_scaler) -{ - if (client == NULL) - return -EINVAL; - - return send_stream_volume_command_msg(client, stream_id, volume_scaler); -} - -int cras_client_set_system_volume(struct cras_client *client, size_t volume) -{ - struct cras_set_system_volume msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_system_volume(&msg, volume); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_system_mute(struct cras_client *client, int mute) -{ - struct cras_set_system_mute msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_system_mute(&msg, mute); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_user_mute(struct cras_client *client, int mute) -{ - struct cras_set_system_mute msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_user_mute(&msg, mute); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_system_mute_locked(struct cras_client *client, int locked) -{ - struct cras_set_system_mute msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_system_mute_locked(&msg, locked); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_system_capture_mute(struct cras_client *client, int mute) -{ - struct cras_set_system_mute msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_system_capture_mute(&msg, mute); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_system_capture_mute_locked(struct cras_client *client, - int locked) -{ - struct cras_set_system_mute msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_system_capture_mute_locked(&msg, locked); - return write_message_to_server(client, &msg.header); -} - -size_t cras_client_get_system_volume(const struct cras_client *client) -{ - size_t volume; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - volume = client->server_state->volume; - server_state_unlock(client, lock_rc); - return volume; -} - -long cras_client_get_system_capture_gain(const struct cras_client *client) -{ - long gain; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - gain = client->server_state->capture_gain; - server_state_unlock(client, lock_rc); - return gain; -} - -int cras_client_get_system_muted(const struct cras_client *client) -{ - int muted; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - muted = client->server_state->mute; - server_state_unlock(client, lock_rc); - return muted; -} - -int cras_client_get_user_muted(const struct cras_client *client) -{ - int muted; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - muted = client->server_state->user_mute; - server_state_unlock(client, lock_rc); - return muted; -} - -int cras_client_get_system_capture_muted(const struct cras_client *client) -{ - int muted; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - muted = client->server_state->capture_mute; - server_state_unlock(client, lock_rc); - return muted; -} - -long cras_client_get_system_min_volume(const struct cras_client *client) -{ - long min_volume; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - min_volume = client->server_state->min_volume_dBFS; - server_state_unlock(client, lock_rc); - return min_volume; -} - -long cras_client_get_system_max_volume(const struct cras_client *client) -{ - long max_volume; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - max_volume = client->server_state->max_volume_dBFS; - server_state_unlock(client, lock_rc); - return max_volume; -} - -int cras_client_get_default_output_buffer_size(struct cras_client *client) -{ - int default_output_buffer_size; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return -EINVAL; - - default_output_buffer_size = - client->server_state->default_output_buffer_size; - server_state_unlock(client, lock_rc); - return default_output_buffer_size; -} - -const struct audio_debug_info * -cras_client_get_audio_debug_info(const struct cras_client *client) -{ - const struct audio_debug_info *debug_info; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - debug_info = &client->server_state->audio_debug_info; - server_state_unlock(client, lock_rc); - return debug_info; -} - -const struct main_thread_debug_info * -cras_client_get_main_thread_debug_info(const struct cras_client *client) -{ - const struct main_thread_debug_info *debug_info; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - debug_info = &client->server_state->main_thread_debug_info; - server_state_unlock(client, lock_rc); - return debug_info; -} - -const struct cras_bt_debug_info * -cras_client_get_bt_debug_info(const struct cras_client *client) -{ - const struct cras_bt_debug_info *debug_info; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - debug_info = &client->server_state->bt_debug_info; - server_state_unlock(client, lock_rc); - return debug_info; -} - -const struct cras_audio_thread_snapshot_buffer * -cras_client_get_audio_thread_snapshot_buffer(const struct cras_client *client) -{ - const struct cras_audio_thread_snapshot_buffer *snapshot_buffer; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - snapshot_buffer = &client->server_state->snapshot_buffer; - server_state_unlock(client, lock_rc); - return snapshot_buffer; -} - -unsigned cras_client_get_num_active_streams(const struct cras_client *client, - struct timespec *ts) -{ - unsigned num_streams, version, i; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - -read_active_streams_again: - version = begin_server_state_read(client->server_state); - num_streams = 0; - for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) - num_streams += client->server_state->num_active_streams[i]; - if (ts) { - if (num_streams) - clock_gettime(CLOCK_MONOTONIC_RAW, ts); - else - cras_timespec_to_timespec( - ts, - &client->server_state->last_active_stream_time); - } - if (end_server_state_read(client->server_state, version)) - goto read_active_streams_again; - - server_state_unlock(client, lock_rc); - return num_streams; -} - -int cras_client_run_thread(struct cras_client *client) -{ - int rc; - - if (client == NULL) - return -EINVAL; - if (thread_is_running(&client->thread)) - return 0; - - assert(client->command_reply_fds[0] == -1 && - client->command_reply_fds[1] == -1); - - if (pipe(client->command_reply_fds) < 0) - return -EIO; - client->thread.state = CRAS_THREAD_RUNNING; - rc = pthread_create(&client->thread.tid, NULL, client_thread, client); - if (rc) { - client->thread.state = CRAS_THREAD_STOP; - return -rc; - } - - return 0; -} - -int cras_client_stop(struct cras_client *client) -{ - if (client == NULL) - return -EINVAL; - if (!thread_is_running(&client->thread)) - return 0; - - send_simple_cmd_msg(client, 0, CLIENT_STOP); - pthread_join(client->thread.tid, NULL); - - /* The other end of the reply pipe is closed by the client thread, just - * clost the read end here. */ - close(client->command_reply_fds[0]); - client->command_reply_fds[0] = -1; - - return 0; -} - -void cras_client_set_connection_status_cb( - struct cras_client *client, cras_connection_status_cb_t connection_cb, - void *user_arg) -{ - client->server_connection_cb = connection_cb; - client->server_connection_user_arg = user_arg; -} - -void cras_client_set_thread_priority_cb(struct cras_client *client, - cras_thread_priority_cb_t cb) -{ - client->thread_priority_cb = cb; -} - -int cras_client_get_output_devices(const struct cras_client *client, - struct cras_iodev_info *devs, - struct cras_ionode_info *nodes, - size_t *num_devs, size_t *num_nodes) -{ - const struct cras_server_state *state; - unsigned avail_devs, avail_nodes, version; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return -EINVAL; - state = client->server_state; - -read_outputs_again: - version = begin_server_state_read(state); - avail_devs = MIN(*num_devs, state->num_output_devs); - memcpy(devs, state->output_devs, avail_devs * sizeof(*devs)); - avail_nodes = MIN(*num_nodes, state->num_output_nodes); - memcpy(nodes, state->output_nodes, avail_nodes * sizeof(*nodes)); - if (end_server_state_read(state, version)) - goto read_outputs_again; - server_state_unlock(client, lock_rc); - - *num_devs = avail_devs; - *num_nodes = avail_nodes; - - return 0; -} - -int cras_client_get_input_devices(const struct cras_client *client, - struct cras_iodev_info *devs, - struct cras_ionode_info *nodes, - size_t *num_devs, size_t *num_nodes) -{ - const struct cras_server_state *state; - unsigned avail_devs, avail_nodes, version; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (!client) - return -EINVAL; - state = client->server_state; - -read_inputs_again: - version = begin_server_state_read(state); - avail_devs = MIN(*num_devs, state->num_input_devs); - memcpy(devs, state->input_devs, avail_devs * sizeof(*devs)); - avail_nodes = MIN(*num_nodes, state->num_input_nodes); - memcpy(nodes, state->input_nodes, avail_nodes * sizeof(*nodes)); - if (end_server_state_read(state, version)) - goto read_inputs_again; - server_state_unlock(client, lock_rc); - - *num_devs = avail_devs; - *num_nodes = avail_nodes; - - return 0; -} - -int cras_client_get_attached_clients(const struct cras_client *client, - struct cras_attached_client_info *clients, - size_t max_clients) -{ - const struct cras_server_state *state; - unsigned num, version; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return -EINVAL; - state = client->server_state; - -read_clients_again: - version = begin_server_state_read(state); - num = MIN(max_clients, state->num_attached_clients); - memcpy(clients, state->client_info, num * sizeof(*clients)); - if (end_server_state_read(state, version)) - goto read_clients_again; - server_state_unlock(client, lock_rc); - - return num; -} - -/* Find an output ionode on an iodev with the matching name. - * - * Args: - * dev_name - The prefix of the iodev name. - * node_name - The prefix of the ionode name. - * dev_info - The information about the iodev will be returned here. - * node_info - The information about the ionode will be returned here. - * Returns: - * 0 if successful, -1 if the node cannot be found. - */ -static int cras_client_find_output_node(const struct cras_client *client, - const char *dev_name, - const char *node_name, - struct cras_iodev_info *dev_info, - struct cras_ionode_info *node_info) -{ - size_t ndevs, nnodes; - struct cras_iodev_info *devs = NULL; - struct cras_ionode_info *nodes = NULL; - int rc = -1; - unsigned i, j; - - if (!client || !dev_name || !node_name) - goto quit; - - devs = (struct cras_iodev_info *)malloc(CRAS_MAX_IODEVS * - sizeof(*devs)); - if (!devs) - goto quit; - - nodes = (struct cras_ionode_info *)malloc(CRAS_MAX_IONODES * - sizeof(*nodes)); - if (!nodes) - goto quit; - - ndevs = CRAS_MAX_IODEVS; - nnodes = CRAS_MAX_IONODES; - rc = cras_client_get_output_devices(client, devs, nodes, &ndevs, - &nnodes); - if (rc < 0) - goto quit; - - for (i = 0; i < ndevs; i++) - if (!strncmp(dev_name, devs[i].name, strlen(dev_name))) - goto found_dev; - rc = -1; - goto quit; - -found_dev: - for (j = 0; j < nnodes; j++) - if (nodes[j].iodev_idx == devs[i].idx && - !strncmp(node_name, nodes[j].name, strlen(node_name))) - goto found_node; - rc = -1; - goto quit; - -found_node: - *dev_info = devs[i]; - *node_info = nodes[j]; - rc = 0; - -quit: - free(devs); - free(nodes); - return rc; -} - -int cras_client_get_node_by_id(const struct cras_client *client, int input, - const cras_node_id_t node_id, - struct cras_ionode_info *node_info) -{ - size_t ndevs, nnodes; - struct cras_iodev_info *devs = NULL; - struct cras_ionode_info *nodes = NULL; - int rc = -EINVAL; - unsigned i; - - if (!client || !node_info) { - rc = -EINVAL; - goto quit; - } - - devs = (struct cras_iodev_info *)malloc(CRAS_MAX_IODEVS * - sizeof(*devs)); - if (!devs) { - rc = -ENOMEM; - goto quit; - } - - nodes = (struct cras_ionode_info *)malloc(CRAS_MAX_IONODES * - sizeof(*nodes)); - if (!nodes) { - rc = -ENOMEM; - goto quit; - } - - ndevs = CRAS_MAX_IODEVS; - nnodes = CRAS_MAX_IONODES; - if (input) - rc = cras_client_get_input_devices(client, devs, nodes, &ndevs, - &nnodes); - else - rc = cras_client_get_output_devices(client, devs, nodes, &ndevs, - &nnodes); - if (rc < 0) - goto quit; - - rc = -ENOENT; - for (i = 0; i < nnodes; i++) { - if (node_id == cras_make_node_id(nodes[i].iodev_idx, - nodes[i].ionode_idx)) { - memcpy(node_info, &nodes[i], sizeof(*node_info)); - rc = 0; - break; - } - } - -quit: - free(devs); - free(nodes); - return rc; -} - -int cras_client_output_dev_plugged(const struct cras_client *client, - const char *name) -{ - struct cras_iodev_info dev_info; - struct cras_ionode_info node_info = { 0 }; - - if (cras_client_find_output_node(client, name, "Front Headphone Jack", - &dev_info, &node_info) < 0) - return 0; - - return node_info.plugged; -} - -int cras_client_set_node_attr(struct cras_client *client, - cras_node_id_t node_id, enum ionode_attr attr, - int value) -{ - struct cras_set_node_attr msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_node_attr(&msg, node_id, attr, value); - return write_message_to_server(client, &msg.header); -} - -int cras_client_select_node(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id) -{ - struct cras_select_node msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_select_node(&msg, direction, node_id); - return write_message_to_server(client, &msg.header); -} - -int cras_client_add_active_node(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id) -{ - struct cras_add_active_node msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_add_active_node(&msg, direction, node_id); - return write_message_to_server(client, &msg.header); -} - -int cras_client_rm_active_node(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id) -{ - struct cras_rm_active_node msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_rm_active_node(&msg, direction, node_id); - return write_message_to_server(client, &msg.header); -} - -int cras_client_format_bytes_per_frame(struct cras_audio_format *fmt) -{ - if (fmt == NULL) - return -EINVAL; - - return cras_get_format_bytes(fmt); -} - -int cras_client_calc_playback_latency(const struct timespec *sample_time, - struct timespec *delay) -{ - struct timespec now; - - if (delay == NULL) - return -EINVAL; - - clock_gettime(CLOCK_MONOTONIC_RAW, &now); - - /* for output return time until sample is played (t - now) */ - subtract_timespecs(sample_time, &now, delay); - return 0; -} - -int cras_client_calc_capture_latency(const struct timespec *sample_time, - struct timespec *delay) -{ - struct timespec now; - - if (delay == NULL) - return -EINVAL; - - clock_gettime(CLOCK_MONOTONIC_RAW, &now); - - /* For input want time since sample read (now - t) */ - subtract_timespecs(&now, sample_time, delay); - return 0; -} - -int cras_client_reload_dsp(struct cras_client *client) -{ - struct cras_reload_dsp msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_reload_dsp(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_dump_dsp_info(struct cras_client *client) -{ - struct cras_dump_dsp_info msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_dump_dsp_info(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_update_audio_debug_info( - struct cras_client *client, void (*debug_info_cb)(struct cras_client *)) -{ - struct cras_dump_audio_thread msg; - - if (client == NULL) - return -EINVAL; - - if (client->debug_info_callback != NULL) - return -EINVAL; - client->debug_info_callback = debug_info_cb; - - cras_fill_dump_audio_thread(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_get_atlog_access(struct cras_client *client, - void (*atlog_access_cb)(struct cras_client *)) -{ - struct cras_get_atlog_fd msg; - - if (client == NULL) - return -EINVAL; - - if (client->atlog_access_callback != NULL) - return -EINVAL; - client->atlog_access_callback = atlog_access_cb; - - cras_fill_get_atlog_fd(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_read_atlog(struct cras_client *client, uint64_t *read_idx, - uint64_t *missing, - struct audio_thread_event_log *buf) -{ - struct audio_thread_event_log log; - uint64_t i, sync_write_pos, len = 0; - struct timespec timestamp, last_timestamp; - - if (!client->atlog_ro) - return -EINVAL; - - sync_write_pos = client->atlog_ro->sync_write_pos; - __sync_synchronize(); - memcpy(&log, client->atlog_ro, sizeof(log)); - - if (sync_write_pos <= *read_idx) - return 0; - - *missing = 0; - for (i = sync_write_pos - 1; i >= *read_idx; --i) { - uint64_t pos = i % log.len; - timestamp.tv_sec = log.log[pos].tag_sec & 0x00ffffff; - timestamp.tv_nsec = log.log[pos].nsec; - - if (i != sync_write_pos - 1 && - timespec_after(×tamp, &last_timestamp)) { - if (*read_idx) - *missing = i - *read_idx + 1; - *read_idx = i + 1; - break; - } - last_timestamp = timestamp; - - if (!i) - break; - } - - /* Copies the continuous part of log. */ - if ((sync_write_pos - 1) % log.len < *read_idx % log.len) { - len = log.len - *read_idx % log.len; - memcpy(buf->log, &log.log[*read_idx % log.len], - sizeof(struct audio_thread_event) * len); - memcpy(&buf->log[len], log.log, - sizeof(struct audio_thread_event) * - ((sync_write_pos - 1) % log.len + 1)); - len = sync_write_pos - *read_idx; - } else { - len = sync_write_pos - *read_idx; - memcpy(buf->log, &log.log[*read_idx % log.len], - sizeof(struct audio_thread_event) * len); - } - - *read_idx = sync_write_pos; - return len; -} - -int cras_client_update_main_thread_debug_info( - struct cras_client *client, void (*debug_info_cb)(struct cras_client *)) -{ - struct cras_dump_main msg; - - if (client == NULL) - return -EINVAL; - if (client->debug_info_callback != NULL) - return -EINVAL; - client->debug_info_callback = debug_info_cb; - cras_fill_dump_main(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_update_bt_debug_info( - struct cras_client *client, void (*debug_info_cb)(struct cras_client *)) -{ - struct cras_dump_bt msg; - - if (client == NULL) - return -EINVAL; - - if (client->debug_info_callback != NULL) - return -EINVAL; - client->debug_info_callback = debug_info_cb; - - cras_fill_dump_bt(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_update_audio_thread_snapshots( - struct cras_client *client, void (*debug_info_cb)(struct cras_client *)) -{ - struct cras_dump_snapshots msg; - - if (client == NULL) - return -EINVAL; - - if (client->debug_info_callback != NULL) - return -EINVAL; - client->debug_info_callback = debug_info_cb; - - cras_fill_dump_snapshots(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_get_max_supported_channels(const struct cras_client *client, - cras_node_id_t node_id, - uint32_t *max_channels) -{ - size_t ndevs, nnodes; - struct cras_iodev_info *devs = NULL; - struct cras_ionode_info *nodes = NULL; - int rc = -EINVAL; - unsigned i; - - if (!client) { - rc = -EINVAL; - goto quit; - } - - devs = (struct cras_iodev_info *)malloc(CRAS_MAX_IODEVS * - sizeof(*devs)); - if (!devs) { - rc = -ENOMEM; - goto quit; - } - - nodes = (struct cras_ionode_info *)malloc(CRAS_MAX_IONODES * - sizeof(*nodes)); - if (!nodes) { - rc = -ENOMEM; - goto quit; - } - - ndevs = CRAS_MAX_IODEVS; - nnodes = CRAS_MAX_IONODES; - rc = cras_client_get_output_devices(client, devs, nodes, &ndevs, - &nnodes); - if (rc < 0) - goto quit; - - rc = -ENOENT; - uint32_t iodev_idx; - for (i = 0; i < nnodes; i++) { - if (node_id == cras_make_node_id(nodes[i].iodev_idx, - nodes[i].ionode_idx)) { - iodev_idx = nodes[i].iodev_idx; - rc = 0; - break; - } - } - - if (rc < 0) - goto quit; - - rc = -ENOENT; - for (i = 0; i < ndevs; i++) { - if (iodev_idx == devs[i].idx) { - *max_channels = devs[i].max_supported_channels; - rc = 0; - break; - } - } - -quit: - free(devs); - free(nodes); - return rc; -} - -int cras_client_set_node_volume(struct cras_client *client, - cras_node_id_t node_id, uint8_t volume) -{ - struct cras_set_node_attr msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_node_attr(&msg, node_id, IONODE_ATTR_VOLUME, volume); - return write_message_to_server(client, &msg.header); -} - -int cras_client_swap_node_left_right(struct cras_client *client, - cras_node_id_t node_id, int enable) -{ - struct cras_set_node_attr msg; - - if (client == NULL) - return -EINVAL; - - cras_fill_set_node_attr(&msg, node_id, IONODE_ATTR_SWAP_LEFT_RIGHT, - enable); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_node_capture_gain(struct cras_client *client, - cras_node_id_t node_id, long gain) -{ - struct cras_set_node_attr msg; - - if (client == NULL) - return -EINVAL; - if (gain > INT_MAX || gain < INT_MIN) - return -EINVAL; - - cras_fill_set_node_attr(&msg, node_id, IONODE_ATTR_CAPTURE_GAIN, gain); - return write_message_to_server(client, &msg.header); -} - -int cras_client_add_test_iodev(struct cras_client *client, - enum TEST_IODEV_TYPE type) -{ - struct cras_add_test_dev msg; - - cras_fill_add_test_dev(&msg, type); - return write_message_to_server(client, &msg.header); -} - -int cras_client_test_iodev_command(struct cras_client *client, - unsigned int iodev_idx, - enum CRAS_TEST_IODEV_CMD command, - unsigned int data_len, const uint8_t *data) -{ - struct cras_test_dev_command *msg; - int rc; - - msg = (struct cras_test_dev_command *)malloc(sizeof(*msg) + data_len); - cras_fill_test_dev_command(msg, iodev_idx, command, data_len, data); - rc = write_message_to_server(client, &msg->header); - free(msg); - return rc; -} - -int cras_client_config_global_remix(struct cras_client *client, - unsigned num_channels, float *coefficient) -{ - struct cras_config_global_remix *msg; - int rc; - size_t nchan = (size_t)num_channels; - - msg = (struct cras_config_global_remix *)malloc( - sizeof(*msg) + nchan * nchan * sizeof(*coefficient)); - cras_fill_config_global_remix_command(msg, num_channels, coefficient, - num_channels * num_channels); - rc = write_message_to_server(client, &msg->header); - free(msg); - return rc; -} - -int cras_client_get_first_node_type_idx(const struct cras_client *client, - enum CRAS_NODE_TYPE type, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t *node_id) -{ - const struct cras_server_state *state; - unsigned int version; - unsigned int i; - const struct cras_ionode_info *node_list; - unsigned int num_nodes; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return -EINVAL; - state = client->server_state; - -read_nodes_again: - version = begin_server_state_read(state); - if (direction == CRAS_STREAM_OUTPUT) { - node_list = state->output_nodes; - num_nodes = state->num_output_nodes; - } else { - node_list = state->input_nodes; - num_nodes = state->num_input_nodes; - } - for (i = 0; i < num_nodes; i++) { - if ((enum CRAS_NODE_TYPE)node_list[i].type_enum == type) { - *node_id = cras_make_node_id(node_list[i].iodev_idx, - node_list[i].ionode_idx); - server_state_unlock(client, lock_rc); - return 0; - } - } - if (end_server_state_read(state, version)) - goto read_nodes_again; - server_state_unlock(client, lock_rc); - - return -ENODEV; -} - -int cras_client_get_first_dev_type_idx(const struct cras_client *client, - enum CRAS_NODE_TYPE type, - enum CRAS_STREAM_DIRECTION direction) -{ - cras_node_id_t node_id; - int rc; - - rc = cras_client_get_first_node_type_idx(client, type, direction, - &node_id); - if (rc) - return rc; - - return dev_index_of(node_id); -} - -int cras_client_set_suspend(struct cras_client *client, int suspend) -{ - struct cras_server_message msg; - - cras_fill_suspend_message(&msg, suspend); - return write_message_to_server(client, &msg); -} - -int cras_client_get_hotword_models(struct cras_client *client, - cras_node_id_t node_id, - get_hotword_models_cb_t cb) -{ - struct cras_get_hotword_models msg; - - if (!client) - return -EINVAL; - client->get_hotword_models_cb = cb; - - cras_fill_get_hotword_models_message(&msg, node_id); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_hotword_model(struct cras_client *client, - cras_node_id_t node_id, - const char *model_name) -{ - struct cras_set_hotword_model msg; - - cras_fill_set_hotword_model_message(&msg, node_id, model_name); - return write_message_to_server(client, &msg.header); -} - -int cras_client_set_aec_dump(struct cras_client *client, - cras_stream_id_t stream_id, int start, int fd) -{ - struct cras_set_aec_dump msg; - - cras_fill_set_aec_dump_message(&msg, stream_id, start); - - if (fd != -1) - return cras_send_with_fds(client->server_fd, &msg, sizeof(msg), - &fd, 1); - else - return write_message_to_server(client, &msg.header); -} - -int cras_client_reload_aec_config(struct cras_client *client) -{ - struct cras_reload_aec_config msg; - - cras_fill_reload_aec_config(&msg); - return write_message_to_server(client, &msg.header); -} - -int cras_client_get_aec_supported(struct cras_client *client) -{ - int aec_supported; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return 0; - - aec_supported = client->server_state->aec_supported; - server_state_unlock(client, lock_rc); - return aec_supported; -} - -int cras_client_get_aec_group_id(struct cras_client *client) -{ - int aec_group_id; - int lock_rc; - - lock_rc = server_state_rdlock(client); - if (lock_rc) - return -1; - - aec_group_id = client->server_state->aec_group_id; - server_state_unlock(client, lock_rc); - return aec_group_id; -} - -int cras_client_set_bt_wbs_enabled(struct cras_client *client, bool enabled) -{ - struct cras_set_bt_wbs_enabled msg; - - cras_fill_set_bt_wbs_enabled(&msg, enabled); - return write_message_to_server(client, &msg.header); -} - -void cras_client_set_state_change_callback_context(struct cras_client *client, - void *context) -{ - if (!client) - return; - client->observer_context = context; -} - -static int cras_send_register_notification(struct cras_client *client, - enum CRAS_CLIENT_MESSAGE_ID msg_id, - int do_register) -{ - struct cras_register_notification msg; - int rc; - - /* This library automatically re-registers notifications when - * reconnecting, so we can ignore message send failure due to no - * connection. */ - cras_fill_register_notification_message(&msg, msg_id, do_register); - rc = write_message_to_server(client, &msg.header); - if (rc == -EPIPE) - rc = 0; - return rc; -} - -int cras_client_set_output_volume_changed_callback( - struct cras_client *client, - cras_client_output_volume_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.output_volume_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_OUTPUT_VOLUME_CHANGED, cb != NULL); -} - -int cras_client_set_output_mute_changed_callback( - struct cras_client *client, cras_client_output_mute_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.output_mute_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_OUTPUT_MUTE_CHANGED, cb != NULL); -} - -int cras_client_set_capture_gain_changed_callback( - struct cras_client *client, - cras_client_capture_gain_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.capture_gain_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_CAPTURE_GAIN_CHANGED, cb != NULL); -} - -int cras_client_set_capture_mute_changed_callback( - struct cras_client *client, - cras_client_capture_mute_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.capture_mute_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_CAPTURE_MUTE_CHANGED, cb != NULL); -} - -int cras_client_set_nodes_changed_callback( - struct cras_client *client, cras_client_nodes_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.nodes_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_NODES_CHANGED, cb != NULL); -} - -int cras_client_set_active_node_changed_callback( - struct cras_client *client, cras_client_active_node_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.active_node_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_ACTIVE_NODE_CHANGED, cb != NULL); -} - -int cras_client_set_output_node_volume_changed_callback( - struct cras_client *client, - cras_client_output_node_volume_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.output_node_volume_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED, cb != NULL); -} - -int cras_client_set_node_left_right_swapped_changed_callback( - struct cras_client *client, - cras_client_node_left_right_swapped_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.node_left_right_swapped_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED, - cb != NULL); -} - -int cras_client_set_input_node_gain_changed_callback( - struct cras_client *client, - cras_client_input_node_gain_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.input_node_gain_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED, cb != NULL); -} - -int cras_client_set_num_active_streams_changed_callback( - struct cras_client *client, - cras_client_num_active_streams_changed_callback cb) -{ - if (!client) - return -EINVAL; - client->observer_ops.num_active_streams_changed = cb; - return cras_send_register_notification( - client, CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED, cb != NULL); -} - -static int reregister_notifications(struct cras_client *client) -{ - int rc; - - if (client->observer_ops.output_volume_changed) { - rc = cras_client_set_output_volume_changed_callback( - client, client->observer_ops.output_volume_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.output_mute_changed) { - rc = cras_client_set_output_mute_changed_callback( - client, client->observer_ops.output_mute_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.capture_gain_changed) { - rc = cras_client_set_capture_gain_changed_callback( - client, client->observer_ops.capture_gain_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.capture_mute_changed) { - rc = cras_client_set_capture_mute_changed_callback( - client, client->observer_ops.capture_mute_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.nodes_changed) { - rc = cras_client_set_nodes_changed_callback( - client, client->observer_ops.nodes_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.active_node_changed) { - rc = cras_client_set_active_node_changed_callback( - client, client->observer_ops.active_node_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.output_node_volume_changed) { - rc = cras_client_set_output_node_volume_changed_callback( - client, - client->observer_ops.output_node_volume_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.node_left_right_swapped_changed) { - rc = cras_client_set_node_left_right_swapped_changed_callback( - client, - client->observer_ops.node_left_right_swapped_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.input_node_gain_changed) { - rc = cras_client_set_input_node_gain_changed_callback( - client, client->observer_ops.input_node_gain_changed); - if (rc != 0) - return rc; - } - if (client->observer_ops.num_active_streams_changed) { - rc = cras_client_set_num_active_streams_changed_callback( - client, - client->observer_ops.num_active_streams_changed); - if (rc != 0) - return rc; - } - return 0; -} - -static int hotword_read_cb(struct cras_client *client, - cras_stream_id_t stream_id, - uint8_t *captured_samples, uint8_t *playback_samples, - unsigned int frames, - const struct timespec *captured_time, - const struct timespec *playback_time, void *user_arg) -{ - struct cras_hotword_handle *handle; - - handle = (struct cras_hotword_handle *)user_arg; - if (handle->trigger_cb) - handle->trigger_cb(client, handle, handle->user_data); - - return 0; -} - -static int hotword_err_cb(struct cras_client *client, - cras_stream_id_t stream_id, int error, void *user_arg) -{ - struct cras_hotword_handle *handle; - - handle = (struct cras_hotword_handle *)user_arg; - if (handle->err_cb) - handle->err_cb(client, handle, error, handle->user_data); - - return 0; -} - -int cras_client_enable_hotword_callback(struct cras_client *client, - void *user_data, - cras_hotword_trigger_cb_t trigger_cb, - cras_hotword_error_cb_t err_cb, - struct cras_hotword_handle **handle_out) -{ - struct cras_hotword_handle *handle; - int ret = 0; - - if (!client) - return -EINVAL; - - handle = (struct cras_hotword_handle *)calloc(1, sizeof(*handle)); - if (!handle) - return -ENOMEM; - - handle->format = cras_audio_format_create(SND_PCM_FORMAT_S16_LE, - HOTWORD_FRAME_RATE, 1); - if (!handle->format) { - ret = -ENOMEM; - goto cleanup; - } - - handle->params = cras_client_unified_params_create( - CRAS_STREAM_INPUT, HOTWORD_BLOCK_SIZE, CRAS_STREAM_TYPE_DEFAULT, - HOTWORD_STREAM | TRIGGER_ONLY, (void *)handle, hotword_read_cb, - hotword_err_cb, handle->format); - if (!handle->params) { - ret = -ENOMEM; - goto cleanup_format; - } - - handle->trigger_cb = trigger_cb; - handle->err_cb = err_cb; - handle->user_data = user_data; - - ret = cras_client_add_stream(client, &handle->stream_id, - handle->params); - if (ret) - goto cleanup_params; - - *handle_out = handle; - return 0; - -cleanup_params: - cras_client_stream_params_destroy(handle->params); -cleanup_format: - cras_audio_format_destroy(handle->format); -cleanup: - free(handle); - return ret; -} - -int cras_client_disable_hotword_callback(struct cras_client *client, - struct cras_hotword_handle *handle) -{ - if (!client || !handle) - return -EINVAL; - - cras_client_rm_stream(client, handle->stream_id); - cras_audio_format_destroy(handle->format); - cras_client_stream_params_destroy(handle->params); - free(handle); - return 0; -} - -int get_nodes(struct cras_client *client, enum CRAS_STREAM_DIRECTION direction, - struct libcras_node_info ***nodes, size_t *num) -{ - struct cras_iodev_info iodevs[CRAS_MAX_IODEVS]; - struct cras_ionode_info ionodes[CRAS_MAX_IONODES]; - size_t num_devs = CRAS_MAX_IODEVS, num_nodes = CRAS_MAX_IONODES; - int rc, i, j; - - *num = 0; - if (direction == CRAS_STREAM_INPUT) { - rc = cras_client_get_input_devices(client, iodevs, ionodes, - &num_devs, &num_nodes); - } else { - rc = cras_client_get_output_devices(client, iodevs, ionodes, - &num_devs, &num_nodes); - } - - if (rc < 0) { - syslog(LOG_ERR, "Failed to get devices: %d", rc); - return rc; - } - - *nodes = (struct libcras_node_info **)calloc( - num_nodes, sizeof(struct libcras_node_info *)); - - for (i = 0; i < num_devs; i++) { - for (j = 0; j < num_nodes; j++) { - if (iodevs[i].idx != ionodes[j].iodev_idx) - continue; - (*nodes)[*num] = libcras_node_info_create(&iodevs[i], - &ionodes[j]); - if ((*nodes)[*num] == NULL) { - rc = -errno; - goto clean; - } - (*num)++; - } - } - return 0; -clean: - for (i = 0; i < *num; i++) - libcras_node_info_destroy((*nodes)[i]); - free(*nodes); - *nodes = NULL; - *num = 0; - return rc; -} - -int get_default_output_buffer_size(struct cras_client *client, int *size) -{ - int rc = cras_client_get_default_output_buffer_size(client); - if (rc < 0) - return rc; - *size = rc; - return 0; -} - -int get_aec_group_id(struct cras_client *client, int *id) -{ - int rc = cras_client_get_aec_group_id(client); - if (rc < 0) - return rc; - *id = rc; - return 0; -} - -int get_aec_supported(struct cras_client *client, int *supported) -{ - *supported = cras_client_get_aec_supported(client); - return 0; -} - -int get_system_muted(struct cras_client *client, int *muted) -{ - *muted = cras_client_get_system_muted(client); - return 0; -} - -int get_loopback_dev_idx(struct cras_client *client, int *idx) -{ - int rc = cras_client_get_first_dev_type_idx( - client, CRAS_NODE_TYPE_POST_MIX_PRE_DSP, CRAS_STREAM_INPUT); - if (rc < 0) - return rc; - *idx = rc; - return 0; -} - -struct libcras_client *libcras_client_create() -{ - struct libcras_client *client = (struct libcras_client *)calloc( - 1, sizeof(struct libcras_client)); - if (!client) { - syslog(LOG_ERR, "cras_client: calloc failed"); - return NULL; - } - if (cras_client_create(&client->client_)) { - libcras_client_destroy(client); - return NULL; - } - client->api_version = CRAS_API_VERSION; - client->connect = cras_client_connect; - client->connect_timeout = cras_client_connect_timeout; - client->connected_wait = cras_client_connected_wait; - client->run_thread = cras_client_run_thread; - client->stop = cras_client_stop; - client->add_pinned_stream = cras_client_add_pinned_stream; - client->rm_stream = cras_client_rm_stream; - client->set_stream_volume = cras_client_set_stream_volume; - client->get_nodes = get_nodes; - client->get_default_output_buffer_size = get_default_output_buffer_size; - client->get_aec_group_id = get_aec_group_id; - client->get_aec_supported = get_aec_supported; - client->get_system_muted = get_system_muted; - client->set_system_mute = cras_client_set_system_mute; - client->get_loopback_dev_idx = get_loopback_dev_idx; - return client; -} - -void libcras_client_destroy(struct libcras_client *client) -{ - cras_client_destroy(client->client_); - free(client); -} - -int stream_params_set(struct cras_stream_params *params, - enum CRAS_STREAM_DIRECTION direction, - size_t buffer_frames, size_t cb_threshold, - enum CRAS_STREAM_TYPE stream_type, - enum CRAS_CLIENT_TYPE client_type, uint32_t flags, - void *user_data, libcras_stream_cb_t stream_cb, - cras_error_cb_t err_cb, size_t rate, - snd_pcm_format_t format, size_t num_channels) -{ - params->direction = direction; - params->buffer_frames = buffer_frames; - params->cb_threshold = cb_threshold; - params->stream_type = stream_type; - params->client_type = client_type; - params->flags = flags; - params->user_data = user_data; - params->stream_cb = stream_cb; - params->err_cb = err_cb; - params->format.frame_rate = rate; - params->format.format = format; - params->format.num_channels = num_channels; - return 0; -} - -int stream_params_set_channel_layout(struct cras_stream_params *params, - int length, const int8_t *layout) -{ - if (length != CRAS_CH_MAX) - return -EINVAL; - return cras_audio_format_set_channel_layout(¶ms->format, layout); -} - -struct libcras_stream_params *libcras_stream_params_create() -{ - struct libcras_stream_params *params = - (struct libcras_stream_params *)calloc( - 1, sizeof(struct libcras_stream_params)); - if (!params) { - syslog(LOG_ERR, "cras_client: calloc failed"); - return NULL; - } - params->params_ = (struct cras_stream_params *)calloc( - 1, sizeof(struct cras_stream_params)); - if (params->params_ == NULL) { - syslog(LOG_ERR, "cras_client: calloc failed"); - free(params->params_); - return NULL; - } - params->api_version = CRAS_API_VERSION; - params->set = stream_params_set; - params->set_channel_layout = stream_params_set_channel_layout; - params->enable_aec = cras_client_stream_params_enable_aec; - return params; -} - -void libcras_stream_params_destroy(struct libcras_stream_params *params) -{ - free(params->params_); - free(params); -} - -struct cras_node_info { - uint64_t id; - uint32_t dev_idx; - uint32_t node_idx; - uint32_t max_supported_channels; - bool plugged; - bool active; - char type[CRAS_NODE_TYPE_BUFFER_SIZE]; - char node_name[CRAS_NODE_NAME_BUFFER_SIZE]; - char dev_name[CRAS_IODEV_NAME_BUFFER_SIZE]; -}; - -int cras_node_info_get_id(struct cras_node_info *node, uint64_t *id) -{ - (*id) = node->id; - return 0; -} - -int cras_node_info_get_dev_idx(struct cras_node_info *node, uint32_t *dev_idx) -{ - (*dev_idx) = node->dev_idx; - return 0; -} - -int cras_node_info_get_node_idx(struct cras_node_info *node, uint32_t *node_idx) -{ - (*node_idx) = node->node_idx; - return 0; -} - -int cras_node_info_get_max_supported_channels(struct cras_node_info *node, - uint32_t *max_supported_channels) -{ - (*max_supported_channels) = node->max_supported_channels; - return 0; -} - -int cras_node_info_is_plugged(struct cras_node_info *node, bool *is_plugged) -{ - (*is_plugged) = node->plugged; - return 0; -} - -int cras_node_info_is_active(struct cras_node_info *node, bool *is_active) -{ - (*is_active) = node->active; - return 0; -} - -int cras_node_info_get_type(struct cras_node_info *node, char **type) -{ - (*type) = node->type; - return 0; -} - -int cras_node_info_get_node_name(struct cras_node_info *node, char **node_name) -{ - (*node_name) = node->node_name; - return 0; -} - -int cras_node_info_get_dev_name(struct cras_node_info *node, char **dev_name) -{ - (*dev_name) = node->dev_name; - return 0; -} - -struct libcras_node_info * -libcras_node_info_create(struct cras_iodev_info *iodev, - struct cras_ionode_info *ionode) -{ - struct libcras_node_info *node = (struct libcras_node_info *)calloc( - 1, sizeof(struct libcras_node_info)); - if (!node) { - syslog(LOG_ERR, "cras_client: calloc failed"); - return NULL; - } - node->node_ = (struct cras_node_info *)calloc( - 1, sizeof(struct cras_node_info)); - if (node->node_ == NULL) { - syslog(LOG_ERR, "cras_client: calloc failed"); - free(node); - return NULL; - } - node->api_version = CRAS_API_VERSION; - node->node_->id = - cras_make_node_id(ionode->iodev_idx, ionode->ionode_idx); - node->node_->dev_idx = ionode->iodev_idx; - node->node_->node_idx = ionode->ionode_idx; - node->node_->max_supported_channels = iodev->max_supported_channels; - node->node_->plugged = ionode->plugged; - node->node_->active = ionode->active; - strncpy(node->node_->type, ionode->type, CRAS_NODE_TYPE_BUFFER_SIZE); - node->node_->type[CRAS_NODE_TYPE_BUFFER_SIZE - 1] = '\0'; - strncpy(node->node_->node_name, ionode->name, - CRAS_NODE_NAME_BUFFER_SIZE); - node->node_->node_name[CRAS_NODE_NAME_BUFFER_SIZE - 1] = '\0'; - strncpy(node->node_->dev_name, iodev->name, - CRAS_IODEV_NAME_BUFFER_SIZE); - node->node_->dev_name[CRAS_IODEV_NAME_BUFFER_SIZE - 1] = '\0'; - node->get_id = cras_node_info_get_id; - node->get_dev_idx = cras_node_info_get_dev_idx; - node->get_node_idx = cras_node_info_get_node_idx; - node->get_max_supported_channels = - cras_node_info_get_max_supported_channels; - node->is_plugged = cras_node_info_is_plugged; - node->is_active = cras_node_info_is_active; - node->get_type = cras_node_info_get_type; - node->get_node_name = cras_node_info_get_node_name; - node->get_dev_name = cras_node_info_get_dev_name; - return node; -} - -void libcras_node_info_destroy(struct libcras_node_info *node) -{ - free(node->node_); - free(node); -} - -void libcras_node_info_array_destroy(struct libcras_node_info **nodes, - size_t num) -{ - int i; - for (i = 0; i < num; i++) - libcras_node_info_destroy(nodes[i]); - free(nodes); -} diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h deleted file mode 100644 index f26a0814..00000000 --- a/cras/src/libcras/cras_client.h +++ /dev/null @@ -1,2008 +0,0 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * This API creates multiple threads, one for control, and a thread per audio - * stream. The control thread is used to receive messages and notifications - * from the audio server, and manage the per-stream threads. API calls below - * may send messages to the control thread, or directly to the server. It is - * required that the control thread is running in order to support audio - * streams and notifications from the server. - * - * The API has multiple initialization sequences, but some of those can block - * while waiting for a response from the server. - * - * The following is the non-blocking API initialization sequence: - * cras_client_create() - * cras_client_set_connection_status_cb() (optional) - * cras_client_run_thread() - * cras_client_connect_async() - * - * The connection callback is executed asynchronously from the control thread - * when the connection has been established. The connection callback should be - * used to turn on or off interactions with any API call that communicates with - * the audio server or starts/stops audio streams. The above is implemented by - * cras_helper_create_connect_async(). - * - * The following alternative (deprecated) initialization sequence can ensure - * that the connection is established synchronously. - * - * Just connect to the server (no control thread): - * cras_client_create() - * cras_client_set_server_connection_cb() (optional) - * one of: - * cras_client_connect() (blocks forever) - * or - * cras_client_connect_timeout() (blocks for timeout) - * - * For API calls below that require the control thread to be running: - * cras_client_run_thread(); - * cras_client_connected_wait(); (blocks up to 1 sec.) - * - * The above minus setting the connection callback is implemented within - * cras_helper_create_connect(). - */ - -#ifndef CRAS_CLIENT_H_ -#define CRAS_CLIENT_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdbool.h> -#include <stdint.h> -#include <sys/select.h> - -#include "cras_iodev_info.h" -#include "cras_types.h" -#include "cras_util.h" - -struct cras_client; -struct cras_hotword_handle; -struct cras_stream_params; - -/* Callback for audio received or transmitted. - * Args (All pointer will be valid - except user_arg, that's up to the user): - * client: The client requesting service. - * stream_id - Unique identifier for the stream needing data read/written. - * samples - Read or write samples to/form here. - * frames - Maximum number of frames to read or write. - * sample_time - Playback time for the first sample read/written. - * user_arg - Value passed to add_stream; - * Return: - * Returns the number of frames read or written on success, or a negative - * number if there is a stream-fatal error. Returns EOF when the end of the - * stream is reached. - */ -typedef int (*cras_playback_cb_t)(struct cras_client *client, - cras_stream_id_t stream_id, uint8_t *samples, - size_t frames, - const struct timespec *sample_time, - void *user_arg); - -/* Callback for audio received and/or transmitted. - * Args (All pointer will be valid - except user_arg, that's up to the user): - * client: The client requesting service. - * stream_id - Unique identifier for the stream needing data read/written. - * captured_samples - Read samples form here. - * playback_samples - Read or write samples to here. - * frames - Maximum number of frames to read or write. - * captured_time - Time the first sample was read. - * playback_time - Playback time for the first sample written. - * user_arg - Value passed to add_stream; - * Return: - * Returns the number of frames read or written on success, or a negative - * number if there is a stream-fatal error. Returns EOF when the end of the - * stream is reached. - */ -typedef int (*cras_unified_cb_t)(struct cras_client *client, - cras_stream_id_t stream_id, - uint8_t *captured_samples, - uint8_t *playback_samples, unsigned int frames, - const struct timespec *captured_time, - const struct timespec *playback_time, - void *user_arg); - -/* Callback for handling stream errors. - * Args: - * client - The client created with cras_client_create(). - * stream_id - The ID for this stream. - * error - The error code, - * user_arg - The argument defined in cras_client_*_params_create(). - */ -typedef int (*cras_error_cb_t)(struct cras_client *client, - cras_stream_id_t stream_id, int error, - void *user_arg); - -/* Server connection status. */ -typedef enum cras_connection_status { - CRAS_CONN_STATUS_FAILED, - /* Resource allocation problem. Free resources, and retry the - * connection with cras_client_connect_async(), or (blocking) - * cras_client_connect(). Do not call cras_client_connect(), - * cras_client_connect_timeout(), or cras_client_destroy() - * from the callback. */ - CRAS_CONN_STATUS_DISCONNECTED, - /* The control thread is attempting to reconnect to the - * server in the background. Any attempt to access the - * server will fail or block (see - * cras_client_set_server_message_blocking(). */ - CRAS_CONN_STATUS_CONNECTED, - /* Connection is established. All state change callbacks - * have been re-registered, but audio streams must be - * restarted, and node state data must be updated. */ -} cras_connection_status_t; - -/* Callback for handling server connection status. - * - * See also cras_client_set_connection_status_cb(). Do not call - * cras_client_connect(), cras_client_connect_timeout(), or - * cras_client_destroy() from this callback. - * - * Args: - * client - The client created with cras_client_create(). - * status - The status of the connection to the server. - * user_arg - The argument defined in - * cras_client_set_connection_status_cb(). - */ -typedef void (*cras_connection_status_cb_t)(struct cras_client *client, - cras_connection_status_t status, - void *user_arg); - -/* Callback for setting thread priority. */ -typedef void (*cras_thread_priority_cb_t)(struct cras_client *client); - -/* Callback for handling get hotword models reply. */ -typedef void (*get_hotword_models_cb_t)(struct cras_client *client, - const char *hotword_models); - -/* Callback to wait for a hotword trigger. */ -typedef void (*cras_hotword_trigger_cb_t)(struct cras_client *client, - struct cras_hotword_handle *handle, - void *user_data); - -/* Callback for handling hotword errors. */ -typedef int (*cras_hotword_error_cb_t)(struct cras_client *client, - struct cras_hotword_handle *handle, - int error, void *user_data); - -/* - * Client handling. - */ - -/* Creates a new client. - * Args: - * client - Filled with a pointer to the new client. - * Returns: - * 0 on success (*client is filled with a valid cras_client pointer). - * Negative error code on failure(*client will be NULL). - */ -int cras_client_create(struct cras_client **client); - -/* Creates a new client with given connection type. - * Args: - * client - Filled with a pointer to the new client. - * conn_type - enum CRAS_CONNECTION_TYPE - * - * Returns: - * 0 on success (*client is filled with a valid cras_client pointer). - * Negative error code on failure(*client will be NULL). - */ -int cras_client_create_with_type(struct cras_client **client, - enum CRAS_CONNECTION_TYPE conn_type); - -/* Destroys a client. - * Args: - * client - returned from "cras_client_create". - */ -void cras_client_destroy(struct cras_client *client); - -/* Connects a client to the running server. - * Waits forever (until interrupted or connected). - * Args: - * client - pointer returned from "cras_client_create". - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -int cras_client_connect(struct cras_client *client); - -/* Connects a client to the running server, retries until timeout. - * Args: - * client - pointer returned from "cras_client_create". - * timeout_ms - timeout in milliseconds or negative to wait forever. - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -int cras_client_connect_timeout(struct cras_client *client, - unsigned int timeout_ms); - -/* Begins running the client control thread. - * - * Required for stream operations and other operations noted below. - * - * Args: - * client - the client to start (from cras_client_create). - * Returns: - * 0 on success or if the thread is already running, -EINVAL if the client - * pointer is NULL, or the negative result of pthread_create(). - */ -int cras_client_run_thread(struct cras_client *client); - -/* Stops running a client. - * This function is executed automatically by cras_client_destroy(). - * Args: - * client - the client to stop (from cras_client_create). - * Returns: - * 0 on success or if the thread was already stopped, -EINVAL if the client - * isn't valid. - */ -int cras_client_stop(struct cras_client *client); - -/* Wait up to 1 second for the client thread to complete the server connection. - * - * After cras_client_run_thread() is executed, this function can be used to - * ensure that the connection has been established with the server and ensure - * that any information about the server is up to date. If - * cras_client_run_thread() has not yet been executed, or cras_client_stop() - * was executed and thread isn't running, then this function returns -EINVAL. - * - * Args: - * client - pointer returned from "cras_client_create". - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -int cras_client_connected_wait(struct cras_client *client); - -/* Ask the client control thread to connect to the audio server. - * - * After cras_client_run_thread() is executed, this function can be used - * to ask the control thread to connect to the audio server asynchronously. - * The callback set with cras_client_set_connection_status_cb() will be - * executed when the connection is established. - * - * Args: - * client - The client from cras_client_create(). - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - * -EINVAL if the client pointer is invalid or the control thread is - * not running. - */ -int cras_client_connect_async(struct cras_client *client); - -/* Sets server connection status callback. - * - * See cras_connection_status_t for a description of the connection states - * and appropriate user action. - * - * Args: - * client - The client from cras_client_create. - * connection_cb - The callback function to register. - * user_arg - Pointer that will be passed to the callback. - */ -void cras_client_set_connection_status_cb( - struct cras_client *client, cras_connection_status_cb_t connection_cb, - void *user_arg); - -/* Sets callback for setting thread priority. - * Args: - * client - The client from cras_client_create. - * cb - The thread priority callback. - */ -void cras_client_set_thread_priority_cb(struct cras_client *client, - cras_thread_priority_cb_t cb); - -/* Returns the current list of output devices. - * - * Requires that the connection to the server has been established. - * - * Data is copied and thus can become out of date. This call must be - * re-executed to get updates. - * - * Args: - * client - The client from cras_client_create. - * devs - Array that will be filled with device info. - * nodes - Array that will be filled with node info. - * *num_devs - Maximum number of devices to put in the array. - * *num_nodes - Maximum number of nodes to put in the array. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - * *num_devs is set to the actual number of devices info filled. - * *num_nodes is set to the actual number of nodes info filled. - */ -int cras_client_get_output_devices(const struct cras_client *client, - struct cras_iodev_info *devs, - struct cras_ionode_info *nodes, - size_t *num_devs, size_t *num_nodes); - -/* Returns the current list of input devices. - * - * Requires that the connection to the server has been established. - * - * Data is copied and thus can become out of date. This call must be - * re-executed to get updates. - * - * Args: - * client - The client from cras_client_create. - * devs - Array that will be filled with device info. - * nodes - Array that will be filled with node info. - * *num_devs - Maximum number of devices to put in the array. - * *num_nodes - Maximum number of nodes to put in the array. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - * *num_devs is set to the actual number of devices info filled. - * *num_nodes is set to the actual number of nodes info filled. - */ -int cras_client_get_input_devices(const struct cras_client *client, - struct cras_iodev_info *devs, - struct cras_ionode_info *nodes, - size_t *num_devs, size_t *num_nodes); - -/* Returns the current list of clients attached to the server. - * - * Requires that the connection to the server has been established. - * - * Data is copied and thus can become out of date. This call must be - * re-executed to get updates. - * - * Args: - * client - This client (from cras_client_create). - * clients - Array that will be filled with a list of attached clients. - * max_clients - Maximum number of clients to put in the array. - * Returns: - * The number of attached clients. This may be more that max_clients passed - * in, this indicates that all of the clients wouldn't fit in the provided - * array. - */ -int cras_client_get_attached_clients(const struct cras_client *client, - struct cras_attached_client_info *clients, - size_t max_clients); - -/* Find a node info with the matching node id. - * - * Requires that the connection to the server has been established. - * - * Data is copied and thus can become out of date. This call must be - * re-executed to get updates. - * - * Args: - * client - This client (from cras_client_create). - * input - Non-zero for input nodes, zero for output nodes. - * node_id - The node id to look for. - * node_info - The information about the ionode will be returned here. - * Returns: - * 0 if successful, negative on error; -ENOENT if the node cannot be found. - */ -int cras_client_get_node_by_id(const struct cras_client *client, int input, - const cras_node_id_t node_id, - struct cras_ionode_info *node_info); - -/* Checks if the output device with the given name is currently plugged in. - * - * For internal devices this checks that jack state, for USB devices this will - * always be true if they are present. The name parameter can be the complete - * name or any unique prefix of the name. If the name is not unique the first - * matching name will be checked. - * - * Requires that the connection to the server has been established. - * - * Data is copied and thus can become out of date. This call must be - * re-executed to get updates. - * - * Args: - * client - The client from cras_client_create. - * name - Name of the device to check. - * Returns: - * 1 if the device exists and is plugged, 0 otherwise. - */ -int cras_client_output_dev_plugged(const struct cras_client *client, - const char *name); - -/* Set the value of an attribute of an ionode. - * - * Args: - * client - The client from cras_client_create. - * node_id - The id of the ionode. - * attr - the attribute we want to change. - * value - the value we want to set. - * Returns: - * Returns 0 for success, negative on error (from errno.h). - */ -int cras_client_set_node_attr(struct cras_client *client, - cras_node_id_t node_id, enum ionode_attr attr, - int value); - -/* Select the preferred node for playback/capture. - * - * Args: - * client - The client from cras_client_create. - * direction - The direction of the ionode. - * node_id - The id of the ionode. If node_id is the special value 0, then - * the preference is cleared and cras will choose automatically. - */ -int cras_client_select_node(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id); - -/* Adds an active node for playback/capture. - * - * Args: - * client - The client from cras_client_create. - * direction - The direction of the ionode. - * node_id - The id of the ionode. If there's no node matching given - * id, nothing will happen in CRAS. - */ -int cras_client_add_active_node(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id); - -/* Removes an active node for playback/capture. - * - * Args: - * client - The client from cras_client_create. - * direction - The direction of the ionode. - * node_id - The id of the ionode. If there's no node matching given - * id, nothing will happen in CRAS. - */ -int cras_client_rm_active_node(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id); - -/* Asks the server to reload dsp plugin configuration from the ini file. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - */ -int cras_client_reload_dsp(struct cras_client *client); - -/* Asks the server to dump current dsp information to syslog. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - */ -int cras_client_dump_dsp_info(struct cras_client *client); - -/* Asks the server to dump current audio thread information. - * - * Args: - * client - The client from cras_client_create. - * cb - A function to call when the data is received. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - */ -int cras_client_update_audio_debug_info(struct cras_client *client, - void (*cb)(struct cras_client *)); - -/* Asks the server to dump current main thread information. - * Args: - * client - The client from cras_client_create. - * cb - A function to call when the data is received. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - */ -int cras_client_update_main_thread_debug_info(struct cras_client *client, - void (*cb)(struct cras_client *)); - -/* Asks the server to dump bluetooth debug information. - * Args: - * client - The client from cras_client_create. - * cb - Function to call when debug info is ready. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - */ -int cras_client_update_bt_debug_info(struct cras_client *client, - void (*cb)(struct cras_client *)); - -/* Gets read-only access to audio thread log. Should be called once before - calling cras_client_read_atlog. - * Args: - * client - The client from cras_client_create. - * atlog_access_cb - Function to call after getting atlog access. - * Returns: - * 0 on success, -EINVAL if the client or atlog_access_cb isn't valid. - */ -int cras_client_get_atlog_access(struct cras_client *client, - void (*atlog_access_cb)(struct cras_client *)); - -/* Reads continuous audio thread log into 'buf', starting from 'read_idx'-th log - * till the latest. The number of missing logs within the range will be stored - * in 'missing'. Requires calling cras_client_get_atlog_access() beforehand - * to get access to audio thread log. - * Args: - * client - The client from cras_client_create. - * read_idx - The log number to start reading with. - * missing - The pointer to store the number of missing logs. - * buf - The buffer to which continuous logs will be copied. - * Returns: - * The number of logs copied. < 0 if failed to read audio thread log. - */ -int cras_client_read_atlog(struct cras_client *client, uint64_t *read_idx, - uint64_t *missing, - struct audio_thread_event_log *buf); - -/* Asks the server to dump current audio thread snapshots. - * - * Args: - * client - The client from cras_client_create. - * cb - A function to call when the data is received. - * Returns: - * 0 on success, -EINVAL if the client isn't valid or isn't running. - */ -int cras_client_update_audio_thread_snapshots(struct cras_client *client, - void (*cb)(struct cras_client *)); - -/* Gets the max supported channel count of the output device from node_id. - * Args: - * client - The client from cras_client_create. - * node_id - ID of the node. - * max_channels - Out parameter will be filled with the max supported channel - * count. - * Returns: - * 0 on success, or negative error code on failure. - */ -int cras_client_get_max_supported_channels(const struct cras_client *client, - cras_node_id_t node_id, - uint32_t *max_channels); - -/* - * Stream handling. - */ - -/* Setup stream configuration parameters. - * Args: - * direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT). - * buffer_frames - total number of audio frames to buffer (dictates latency). - * cb_threshold - For playback, call back for more data when the buffer - * reaches this level. For capture, this is ignored (Audio callback will - * be called when buffer_frames have been captured). - * unused - No longer used. - * stream_type - media or talk (currently only support "default"). - * flags - Currently only used for CRAS_INPUT_STREAM_FLAG. - * user_data - Pointer that will be passed to the callback. - * aud_cb - Called when audio is needed(playback) or ready(capture). Allowed - * return EOF to indicate that the stream should terminate. - * err_cb - Called when there is an error with the stream. - * format - The format of the audio stream. Specifies bits per sample, - * number of channels, and sample rate. - */ -struct cras_stream_params *cras_client_stream_params_create( - enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames, - size_t cb_threshold, size_t unused, enum CRAS_STREAM_TYPE stream_type, - uint32_t flags, void *user_data, cras_playback_cb_t aud_cb, - cras_error_cb_t err_cb, struct cras_audio_format *format); - -/* Functions to set the client type on given stream parameter. - * Args: - * params - Stream configuration parameters. - * client_type - A client type. - */ -void cras_client_stream_params_set_client_type( - struct cras_stream_params *params, enum CRAS_CLIENT_TYPE client_type); - -/* Functions to enable or disable specific effect on given stream parameter. - * Args: - * params - Stream configuration parameters. - */ -void cras_client_stream_params_enable_aec(struct cras_stream_params *params); -void cras_client_stream_params_disable_aec(struct cras_stream_params *params); -void cras_client_stream_params_enable_ns(struct cras_stream_params *params); -void cras_client_stream_params_disable_ns(struct cras_stream_params *params); -void cras_client_stream_params_enable_agc(struct cras_stream_params *params); -void cras_client_stream_params_disable_agc(struct cras_stream_params *params); -void cras_client_stream_params_enable_vad(struct cras_stream_params *params); -void cras_client_stream_params_disable_vad(struct cras_stream_params *params); - -/* Setup stream configuration parameters. DEPRECATED. - * TODO(crbug.com/972928): remove this - * Use cras_client_stream_params_create instead. - * Args: - * direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT) or - * loopback(CRAS_STREAM_POST_MIX_PRE_DSP). - * block_size - The number of frames per callback(dictates latency). - * stream_type - media or talk (currently only support "default"). - * flags - None currently used. - * user_data - Pointer that will be passed to the callback. - * unified_cb - Called to request audio data or to notify the client when - * captured audio is available. Though this is a unified_cb, - * only one direction will be used for a stream, depending - * on the 'direction' parameter. - * err_cb - Called when there is an error with the stream. - * format - The format of the audio stream. Specifies bits per sample, - * number of channels, and sample rate. - */ -struct cras_stream_params *cras_client_unified_params_create( - enum CRAS_STREAM_DIRECTION direction, unsigned int block_size, - enum CRAS_STREAM_TYPE stream_type, uint32_t flags, void *user_data, - cras_unified_cb_t unified_cb, cras_error_cb_t err_cb, - struct cras_audio_format *format); - -/* Destroy stream params created with cras_client_stream_params_create. */ -void cras_client_stream_params_destroy(struct cras_stream_params *params); - -/* Creates a new stream and return the stream id or < 0 on error. - * - * Requires execution of cras_client_run_thread(), and an active connection - * to the audio server. - * - * Args: - * client - The client to add the stream to (from cras_client_create). - * stream_id_out - On success will be filled with the new stream id. - * Guaranteed to be set before any callbacks are made. - * config - The cras_stream_params struct specifying the parameters for the - * stream. - * Returns: - * 0 on success, negative error code on failure (from errno.h). - */ -int cras_client_add_stream(struct cras_client *client, - cras_stream_id_t *stream_id_out, - struct cras_stream_params *config); - -/* Creates a pinned stream and return the stream id or < 0 on error. - * - * Requires execution of cras_client_run_thread(), and an active connection - * to the audio server. - * - * Args: - * client - The client to add the stream to (from cras_client_create). - * dev_idx - Index of the device to attach the newly created stream. - * stream_id_out - On success will be filled with the new stream id. - * Guaranteed to be set before any callbacks are made. - * config - The cras_stream_params struct specifying the parameters for the - * stream. - * Returns: - * 0 on success, negative error code on failure (from errno.h). - */ -int cras_client_add_pinned_stream(struct cras_client *client, uint32_t dev_idx, - cras_stream_id_t *stream_id_out, - struct cras_stream_params *config); - -/* Removes a currently playing/capturing stream. - * - * Requires execution of cras_client_run_thread(). - * - * Args: - * client - Client to remove the stream (returned from cras_client_create). - * stream_id - ID returned from cras_client_add_stream to identify the stream - to remove. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -int cras_client_rm_stream(struct cras_client *client, - cras_stream_id_t stream_id); - -/* Sets the volume scaling factor for the given stream. - * - * Requires execution of cras_client_run_thread(). - * - * Args: - * client - Client owning the stream. - * stream_id - ID returned from cras_client_add_stream. - * volume_scaler - 0.0-1.0 the new value to scale this stream by. - */ -int cras_client_set_stream_volume(struct cras_client *client, - cras_stream_id_t stream_id, - float volume_scaler); - -/* - * System level functions. - */ - -/* Sets the volume of the system. - * - * Volume here ranges from 0 to 100, and will be translated to dB based on the - * output-specific volume curve. - * - * Args: - * client - The client from cras_client_create. - * volume - 0-100 the new volume index. - * Returns: - * 0 for success, -EPIPE if there is an I/O error talking to the server, or - * -EINVAL if 'client' is invalid. - */ -int cras_client_set_system_volume(struct cras_client *client, size_t volume); - -/* Sets the mute state of the system. - * - * Args: - * client - The client from cras_client_create. - * mute - 0 is un-mute, 1 is muted. - * Returns: - * 0 for success, -EPIPE if there is an I/O error talking to the server, or - * -EINVAL if 'client' is invalid. - */ -int cras_client_set_system_mute(struct cras_client *client, int mute); - -/* Sets the user mute state of the system. - * - * This is used for mutes caused by user interaction. Like the mute key. - * - * Args: - * client - The client from cras_client_create. - * mute - 0 is un-mute, 1 is muted. - * Returns: - * 0 for success, -EPIPE if there is an I/O error talking to the server, or - * -EINVAL if 'client' is invalid. - */ -int cras_client_set_user_mute(struct cras_client *client, int mute); - -/* Sets the mute locked state of the system. - * - * Changing mute state is impossible when this flag is set to locked. - * - * Args: - * client - The client from cras_client_create. - * locked - 0 is un-locked, 1 is locked. - * Returns: - * 0 for success, -EPIPE if there is an I/O error talking to the server, or - * -EINVAL if 'client' is invalid. - */ -int cras_client_set_system_mute_locked(struct cras_client *client, int locked); - -/* Sets the capture mute state of the system. - * - * Recordings will be muted when this is set. - * - * Args: - * client - The client from cras_client_create. - * mute - 0 is un-mute, 1 is muted. - * Returns: - * 0 for success, -EPIPE if there is an I/O error talking to the server, or - * -EINVAL if 'client' is invalid. - */ -int cras_client_set_system_capture_mute(struct cras_client *client, int mute); - -/* Sets the capture mute locked state of the system. - * - * Changing mute state is impossible when this flag is set to locked. - * - * Args: - * client - The client from cras_client_create. - * locked - 0 is un-locked, 1 is locked. - * Returns: - * 0 for success, -EPIPE if there is an I/O error talking to the server, or - * -EINVAL if 'client' is invalid. - */ -int cras_client_set_system_capture_mute_locked(struct cras_client *client, - int locked); - -/* Gets the current system volume. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * The current system volume between 0 and 100. - */ -size_t cras_client_get_system_volume(const struct cras_client *client); - -/* Gets the current system mute state. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * 0 if not muted, 1 if it is. - */ -int cras_client_get_system_muted(const struct cras_client *client); - -/* Gets the current user mute state. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * 0 if not muted, 1 if it is. - */ -int cras_client_get_user_muted(const struct cras_client *client); - -/* Gets the current system capture mute state. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * 0 if capture is not muted, 1 if it is. - */ -int cras_client_get_system_capture_muted(const struct cras_client *client); - -/* Gets the current minimum system volume. - * Args: - * client - The client from cras_client_create. - * Returns: - * The minimum value for the current output device in dBFS * 100. This is - * the level of attenuation at volume == 1. - */ -long cras_client_get_system_min_volume(const struct cras_client *client); - -/* Gets the current maximum system volume. - * Args: - * client - The client from cras_client_create. - * Returns: - * The maximum value for the current output device in dBFS * 100. This is - * the level of attenuation at volume == 100. - */ -long cras_client_get_system_max_volume(const struct cras_client *client); - -/* Gets the default output buffer size. - * Args: - * client - The client from cras_client_create. - * Returns: - * Default output buffer size in frames. A negative error on failure. - */ -int cras_client_get_default_output_buffer_size(struct cras_client *client); - -/* Gets audio debug info. - * - * Requires that the connection to the server has been established. - * Access to the resulting pointer is not thread-safe. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * A pointer to the debug info. This info is only updated when requested by - * calling cras_client_update_audio_debug_info. - */ -const struct audio_debug_info * -cras_client_get_audio_debug_info(const struct cras_client *client); - -/* Gets bluetooth debug info. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * A pointer to the debug info. This info is updated and requested by - * calling cras_client_update_bt_debug_info. - */ -const struct cras_bt_debug_info * -cras_client_get_bt_debug_info(const struct cras_client *client); - -/* Gets main thread debug info. - * Args: - * client - The client from cras_client_create. - * Returns: - * A pointer to the debug info. This info is updated and requested by - * calling cras_client_update_main_thread_debug_info. - */ -const struct main_thread_debug_info * -cras_client_get_main_thread_debug_info(const struct cras_client *client); - -/* Gets audio thread snapshot buffer. - * - * Requires that the connection to the server has been established. - * Access to the resulting pointer is not thread-safe. - * - * Args: - * client - The client from cras_client_create. - * Returns: - * A pointer to the snapshot buffer. This info is only updated when - * requested by calling cras_client_update_audio_thread_snapshots. - */ -const struct cras_audio_thread_snapshot_buffer * -cras_client_get_audio_thread_snapshot_buffer(const struct cras_client *client); - -/* Gets the number of streams currently attached to the server. - * - * This is the total number of capture and playback streams. If the ts argument - * is not null, then it will be filled with the last time audio was played or - * recorded. ts will be set to the current time if streams are currently - * active. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * ts - Filled with the timestamp of the last stream. - * Returns: - * The number of active streams. - */ -unsigned cras_client_get_num_active_streams(const struct cras_client *client, - struct timespec *ts); - -/* - * Utility functions. - */ - -/* Returns the number of bytes in an audio frame for a stream. - * Args: - * format - The format of the audio stream. Specifies bits per sample, - * number of channels, and sample rate. - * Returns: - * Positive number of bytes in a frame, or a negative error code if fmt is - * NULL. - */ -int cras_client_format_bytes_per_frame(struct cras_audio_format *fmt); - -/* For playback streams, calculates the latency of the next sample written. - * Only valid when called from the audio callback function for the stream - * (aud_cb). - * Args: - * sample_time - The sample time stamp passed in to aud_cb. - * delay - Out parameter will be filled with the latency. - * Returns: - * 0 on success, -EINVAL if delay is NULL. - */ -int cras_client_calc_playback_latency(const struct timespec *sample_time, - struct timespec *delay); - -/* For capture returns the latency of the next frame to be read from the buffer - * (based on when it was captured). Only valid when called from the audio - * callback function for the stream (aud_cb). - * Args: - * sample_time - The sample time stamp passed in to aud_cb. - * delay - Out parameter will be filled with the latency. - * Returns: - * 0 on success, -EINVAL if delay is NULL. - */ -int cras_client_calc_capture_latency(const struct timespec *sample_time, - struct timespec *delay); - -/* Set the volume of the given output node. Only for output nodes. - * - * Args: - * client - The client from cras_client_create. - * node_id - ID of the node. - * volume - New value for node volume. - */ -int cras_client_set_node_volume(struct cras_client *client, - cras_node_id_t node_id, uint8_t volume); - -/* Swap the left and right channel of the given node. - * - * Args: - * client - The client from cras_client_create. - * node_id - ID of the node. - * enable - 1 to enable swap mode, 0 to disable. - */ -int cras_client_swap_node_left_right(struct cras_client *client, - cras_node_id_t node_id, int enable); - -/* Set the capture gain of the given input node. Only for input nodes. - * - * Args: - * client - The client from cras_client_create. - * node_id - ID of the node. - * gain - New capture gain for the node, in range (0, 100) which will - * linearly maps to (-4000, 4000) 100*dBFS. - */ -int cras_client_set_node_capture_gain(struct cras_client *client, - cras_node_id_t node_id, long gain); - -/* Add a test iodev to the iodev list. - * - * Args: - * client - The client from cras_client_create. - * type - The type of test iodev, see cras_types.h - */ -int cras_client_add_test_iodev(struct cras_client *client, - enum TEST_IODEV_TYPE type); - -/* Send a test command to a test iodev. - * - * Args: - * client - The client from cras_client_create. - * iodev_idx - The index of the test iodev. - * command - The command to send. - * data_len - Length of command data. - * data - Command data. - */ -int cras_client_test_iodev_command(struct cras_client *client, - unsigned int iodev_idx, - enum CRAS_TEST_IODEV_CMD command, - unsigned int data_len, const uint8_t *data); - -/* Finds the first node of the given type. - * - * This is used for finding a special hotword node. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * type - The type of device to find. - * direction - Search input or output devices. - * node_id - The found node on success. - * Returns: - * 0 on success, a negative error on failure. - */ -int cras_client_get_first_node_type_idx(const struct cras_client *client, - enum CRAS_NODE_TYPE type, - enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t *node_id); - -/* Finds the first device that contains a node of the given type. - * - * This is used for finding a special hotword device. - * - * Requires that the connection to the server has been established. - * - * Args: - * client - The client from cras_client_create. - * type - The type of device to find. - * direction - Search input or output devices. - * Returns the device index of a negative error on failure. - */ -int cras_client_get_first_dev_type_idx(const struct cras_client *client, - enum CRAS_NODE_TYPE type, - enum CRAS_STREAM_DIRECTION direction); - -/* Sets the suspend state of audio playback and capture. - * - * Set this before putting the system into suspend. - * - * Args: - * client - The client from cras_client_create. - * suspend - Suspend the system if non-zero, otherwise resume. - */ -int cras_client_set_suspend(struct cras_client *client, int suspend); - -/* Configures the global converter for output remixing. - * - * Args: - * client - The client from cras_client_create. - * num_channels - Number of output channels. - * coefficient - Float array representing |num_channels| * |num_channels| - * matrix. Channels of mixed PCM output will be remixed by - * multiplying this matrix. - */ -int cras_client_config_global_remix(struct cras_client *client, - unsigned num_channels, float *coefficient); - -/* Gets the set of supported hotword language models on a node. The supported - * models may differ on different nodes. - * - * Args: - * client - The client from cras_client_create. - * node_id - ID of a hotword input node (CRAS_NODE_TYPE_HOTWORD). - * cb - The function to be called when hotword models are ready. - * Returns: - * 0 on success. - */ -int cras_client_get_hotword_models(struct cras_client *client, - cras_node_id_t node_id, - get_hotword_models_cb_t cb); - -/* Sets the hotword language model on a node. If there are existing streams on - * the hotword input node when this function is called, they need to be closed - * then re-opend for the model change to take effect. - * Args: - * client - The client from cras_client_create. - * node_id - ID of a hotword input node (CRAS_NODE_TYPE_HOTWORD). - * model_name - Name of the model to use, e.g. "en_us". - * Returns: - * 0 on success. - * -EINVAL if client or node_id is invalid. - * -ENOENT if the specified model is not found. - */ -int cras_client_set_hotword_model(struct cras_client *client, - cras_node_id_t node_id, - const char *model_name); - -/* - * Creates a hotword stream and waits for the hotword to trigger. - * - * Args: - * client - The client to add the stream to (from cras_client_create). - * user_data - Pointer that will be passed to the callback. - * trigger_cb - Called when a hotword is triggered. - * err_cb - Called when there is an error with the stream. - * handle_out - On success will be filled with a cras_hotword_handle. - * Returns: - * 0 on success, negative error code on failure (from errno.h). - */ -int cras_client_enable_hotword_callback( - struct cras_client *client, void *user_data, - cras_hotword_trigger_cb_t trigger_cb, cras_hotword_error_cb_t err_cb, - struct cras_hotword_handle **handle_out); - -/* - * Closes a hotword stream that was created by cras_client_wait_for_hotword. - * - * Args: - * client - Client to remove the stream (returned from cras_client_create). - * handle - cras_hotword_handle returned from cras_client_wait_for_hotword. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -int cras_client_disable_hotword_callback(struct cras_client *client, - struct cras_hotword_handle *handle); - -/* Starts or stops the aec dump task on server side. - * Args: - * client - The client from cras_client_create. - * stream_id - The id of the input stream running with aec effect. - * start - True to start APM debugging, otherwise to stop it. - * fd - File descriptor of the file to store aec dump result. - */ -int cras_client_set_aec_dump(struct cras_client *client, - cras_stream_id_t stream_id, int start, int fd); -/* - * Reloads the aec.ini config file on server side. - */ -int cras_client_reload_aec_config(struct cras_client *client); - -/* - * Returns if AEC is supported. - */ -int cras_client_get_aec_supported(struct cras_client *client); - -/* - * Returns the AEC group ID if available. - */ -int cras_client_get_aec_group_id(struct cras_client *client); - -/* - * Sets the flag to enable bluetooth wideband speech in server. - */ -int cras_client_set_bt_wbs_enabled(struct cras_client *client, bool enabled); - -/* Set the context pointer for system state change callbacks. - * Args: - * client - The client from cras_client_create. - * context - The context pointer passed to all callbacks. - */ -void cras_client_set_state_change_callback_context(struct cras_client *client, - void *context); - -/* Output volume change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * volume - The system output volume, ranging from 0 to 100. - */ -typedef void (*cras_client_output_volume_changed_callback)(void *context, - int32_t volume); - -/* Output mute change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * muted - Non-zero when the audio is muted, zero otherwise. - * user_muted - Non-zero when the audio has been muted by the - * user, zero otherwise. - * mute_locked - Non-zero when the mute funcion is locked, - * zero otherwise. - */ -typedef void (*cras_client_output_mute_changed_callback)(void *context, - int muted, - int user_muted, - int mute_locked); - -/* Capture gain change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * gain - The system capture gain, in centi-decibels. - */ -typedef void (*cras_client_capture_gain_changed_callback)(void *context, - int32_t gain); - -/* Capture mute change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * muted - Non-zero when the audio is muted, zero otherwise. - * mute_locked - Non-zero when the mute funcion is locked, - * zero otherwise. - */ -typedef void (*cras_client_capture_mute_changed_callback)(void *context, - int muted, - int mute_locked); - -/* Nodes change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - */ -typedef void (*cras_client_nodes_changed_callback)(void *context); - -/* Active node change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * direction - Indicates the direction of the selected node. - * node_id - The ID of the selected node. Special device ID values - * defined by CRAS_SPECIAL_DEVICE will be used when no other - * device or node is selected or between selections. - */ -typedef void (*cras_client_active_node_changed_callback)( - void *context, enum CRAS_STREAM_DIRECTION direction, - cras_node_id_t node_id); - -/* Output node volume change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * node_id - The ID of the output node. - * volume - The volume for this node with range 0 to 100. - */ -typedef void (*cras_client_output_node_volume_changed_callback)( - void *context, cras_node_id_t node_id, int32_t volume); - -/* Node left right swapped change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * node_id - The ID of the node. - * swapped - Non-zero if the node is left-right swapped, zero otherwise. - */ -typedef void (*cras_client_node_left_right_swapped_changed_callback)( - void *context, cras_node_id_t node_id, int swapped); - -/* Input node gain change callback. - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * node_id - The ID of the input node. - * gain - The gain for this node in centi-decibels. - */ -typedef void (*cras_client_input_node_gain_changed_callback)( - void *context, cras_node_id_t node_id, int32_t gain); - -/* Number of active streams change callback. - * - * Args: - * context - Context pointer set with - * cras_client_set_state_change_callback_context(). - * direction - Indicates the direction of the stream's node. - * num_active_streams - The number of active streams. - */ -typedef void (*cras_client_num_active_streams_changed_callback)( - void *context, enum CRAS_STREAM_DIRECTION direction, - uint32_t num_active_streams); - -/* Set system state information callbacks. - * NOTE: These callbacks are executed from the client control thread. - * Each state change callback is given the context pointer set with - * cras_client_set_state_change_callback_context(). The context pointer is - * NULL by default. - * Args: - * client - The client from cras_client_create. - * cb - The callback, or NULL to disable the call-back. - * Returns: - * 0 for success or negative errno error code on error. - */ -int cras_client_set_output_volume_changed_callback( - struct cras_client *client, - cras_client_output_volume_changed_callback cb); -int cras_client_set_output_mute_changed_callback( - struct cras_client *client, - cras_client_output_mute_changed_callback cb); -int cras_client_set_capture_gain_changed_callback( - struct cras_client *client, - cras_client_capture_gain_changed_callback cb); -int cras_client_set_capture_mute_changed_callback( - struct cras_client *client, - cras_client_capture_mute_changed_callback cb); -int cras_client_set_nodes_changed_callback( - struct cras_client *client, cras_client_nodes_changed_callback cb); -int cras_client_set_active_node_changed_callback( - struct cras_client *client, - cras_client_active_node_changed_callback cb); -int cras_client_set_output_node_volume_changed_callback( - struct cras_client *client, - cras_client_output_node_volume_changed_callback cb); -int cras_client_set_node_left_right_swapped_changed_callback( - struct cras_client *client, - cras_client_node_left_right_swapped_changed_callback cb); -int cras_client_set_input_node_gain_changed_callback( - struct cras_client *client, - cras_client_input_node_gain_changed_callback cb); -int cras_client_set_num_active_streams_changed_callback( - struct cras_client *client, - cras_client_num_active_streams_changed_callback cb); - -/* - * The functions below prefixed with libcras wrap the original CRAS library - * They provide an interface that maps the pointers to the functions above. - * Please add a new function instead of modifying the existing function. - * Here are some rules about how to add a new function: - * 1. Increase the CRAS_API_VERSION by 1. - * 2. Write a new function in cras_client.c. - * 3. Append the corresponding pointer to the structure. Remeber DO NOT change - * the order of functions in the structs. - * 4. Assign the pointer to the new function in cras_client.c. - * 5. Create the inline function in cras_client.h, which is used by clients. - * Remember to add DISABLE_CFI_ICALL on the inline function. - * 6. Add CHECK_VERSION in the inline function. If the api_version is smaller - * than the supported version, this inline function will return -ENOSYS. - */ - -#define CRAS_API_VERSION 1 -#define CHECK_VERSION(object, version) \ - if (object->api_version < version) { \ - return -ENOSYS; \ - } - -/* - * The inline functions use the indirect function call. Therefore, they are - * incompatible with CFI-icall. - */ -#define DISABLE_CFI_ICALL __attribute__((no_sanitize("cfi-icall"))) - -struct libcras_node_info { - int api_version; - struct cras_node_info *node_; - int (*get_id)(struct cras_node_info *node, uint64_t *id); - int (*get_dev_idx)(struct cras_node_info *node, uint32_t *dev_idx); - int (*get_node_idx)(struct cras_node_info *node, uint32_t *node_idx); - int (*get_max_supported_channels)(struct cras_node_info *node, - uint32_t *max_supported_channels); - int (*is_plugged)(struct cras_node_info *node, bool *plugged); - int (*is_active)(struct cras_node_info *node, bool *active); - int (*get_type)(struct cras_node_info *node, char **name); - int (*get_node_name)(struct cras_node_info *node, char **name); - int (*get_dev_name)(struct cras_node_info *node, char **name); -}; - -struct libcras_client { - int api_version; - struct cras_client *client_; - int (*connect)(struct cras_client *client); - int (*connect_timeout)(struct cras_client *client, - unsigned int timeout_ms); - int (*connected_wait)(struct cras_client *client); - int (*run_thread)(struct cras_client *client); - int (*stop)(struct cras_client *client); - int (*add_pinned_stream)(struct cras_client *client, uint32_t dev_idx, - cras_stream_id_t *stream_id_out, - struct cras_stream_params *config); - int (*rm_stream)(struct cras_client *client, - cras_stream_id_t stream_id); - int (*set_stream_volume)(struct cras_client *client, - cras_stream_id_t stream_id, - float volume_scaler); - int (*get_nodes)(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - struct libcras_node_info ***nodes, size_t *num); - int (*get_default_output_buffer_size)(struct cras_client *client, - int *size); - int (*get_aec_group_id)(struct cras_client *client, int *id); - int (*get_aec_supported)(struct cras_client *client, int *supported); - int (*get_system_muted)(struct cras_client *client, int *muted); - int (*set_system_mute)(struct cras_client *client, int mute); - int (*get_loopback_dev_idx)(struct cras_client *client, int *idx); -}; - -struct cras_stream_cb_data; -struct libcras_stream_cb_data { - int api_version; - struct cras_stream_cb_data *data_; - int (*get_stream_id)(struct cras_stream_cb_data *data, - cras_stream_id_t *id); - int (*get_buf)(struct cras_stream_cb_data *data, uint8_t **buf); - int (*get_frames)(struct cras_stream_cb_data *data, - unsigned int *frames); - int (*get_latency)(struct cras_stream_cb_data *data, - struct timespec *latency); - int (*get_user_arg)(struct cras_stream_cb_data *data, void **user_arg); -}; -typedef int (*libcras_stream_cb_t)(struct libcras_stream_cb_data *data); - -struct libcras_stream_params { - int api_version; - struct cras_stream_params *params_; - int (*set)(struct cras_stream_params *params, - enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames, - size_t cb_threshold, enum CRAS_STREAM_TYPE stream_type, - enum CRAS_CLIENT_TYPE client_type, uint32_t flags, - void *user_data, libcras_stream_cb_t stream_cb, - cras_error_cb_t err_cb, size_t rate, snd_pcm_format_t format, - size_t num_channels); - int (*set_channel_layout)(struct cras_stream_params *params, int length, - const int8_t *layout); - void (*enable_aec)(struct cras_stream_params *params); -}; - -/* - * Creates a new client. - * Returns: - * If success, return a valid libcras_client pointer. Otherwise, return - * NULL. - */ -struct libcras_client *libcras_client_create(); - -/* - * Destroys a client. - * Args: - * client - pointer returned from "libcras_client_create". - */ -void libcras_client_destroy(struct libcras_client *client); - -/* - * Connects a client to the running server. - * Waits forever (until interrupted or connected). - * Args: - * client - pointer returned from "libcras_client_create". - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_connect(struct libcras_client *client) -{ - return client->connect(client->client_); -} - -/* - * Connects a client to the running server, retries until timeout. - * Args: - * client - pointer returned from "libcras_client_create". - * timeout_ms - timeout in milliseconds or negative to wait forever. - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_connect_timeout(struct libcras_client *client, - unsigned int timeout_ms) -{ - return client->connect_timeout(client->client_, timeout_ms); -} - -/* - * Wait up to 1 second for the client thread to complete the server connection. - * - * After libcras_client_run_thread() is executed, this function can be - * used to ensure that the connection has been established with the server and - * ensure that any information about the server is up to date. If - * libcras_client_run_thread() has not yet been executed, or - * libcras_client_stop() was executed and thread isn't running, then this - * function returns -EINVAL. - * - * Args: - * client - pointer returned from "libcras_client_create". - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_connected_wait(struct libcras_client *client) -{ - return client->connected_wait(client->client_); -} - -/* - * Begins running the client control thread. - * - * Required for stream operations and other operations noted below. - * - * Args: - * client - pointer returned from "libcras_client_create". - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_run_thread(struct libcras_client *client) -{ - return client->run_thread(client->client_); -} - -/* - * Stops running a client. - * This function is executed automatically by cras_client_destroy(). - * Args: - * client - pointer returned from "libcras_client_create". - * Returns: - * 0 on success or if the thread was already stopped, -EINVAL if the client - * isn't valid. - */ -DISABLE_CFI_ICALL -inline int libcras_client_stop(struct libcras_client *client) -{ - return client->stop(client->client_); -} - -/* - * Creates a pinned stream and return the stream id or < 0 on error. - * - * Requires execution of libcras_client_run_thread(), and an active - * connection to the audio server. - * - * Args: - * client - pointer returned from "libcras_client_create". - * dev_idx - Index of the device to attach the newly created stream. - * stream_id_out - On success will be filled with the new stream id. - * Guaranteed to be set before any callbacks are made. - * params - The pointer specifying the parameters for the stream. - * (returned from libcras_stream_params_create) - * Returns: - * 0 on success, negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_add_pinned_stream( - struct libcras_client *client, uint32_t dev_idx, - cras_stream_id_t *stream_id_out, struct libcras_stream_params *params) -{ - return client->add_pinned_stream(client->client_, dev_idx, - stream_id_out, params->params_); -} - -/* - * Removes a currently playing/capturing stream. - * - * Requires execution of libcras_client_run_thread(). - * - * Args: - * client - pointer returned from "libcras_client_create". - * stream_id - ID returned from libcras_client_add_stream to identify - * the stream to remove. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_rm_stream(struct libcras_client *client, - cras_stream_id_t stream_id) -{ - return client->rm_stream(client->client_, stream_id); -} - -/* - * Sets the volume scaling factor for the given stream. - * - * Requires execution of cras_client_run_thread(). - * - * Args: - * client - pointer returned from "libcras_client_create". - * stream_id - ID returned from libcras_client_add_stream. - * volume_scaler - 0.0-1.0 the new value to scale this stream by. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_set_stream_volume(struct libcras_client *client, - cras_stream_id_t stream_id, - float volume_scaler) -{ - return client->set_stream_volume(client->client_, stream_id, - volume_scaler); -} - -/* - * Gets the current list of audio nodes. - * - * Args: - * client - Pointer returned from "libcras_client_create". - * direction - Input or output. - * nodes - Array that will be filled with libcras_node_info pointers. - * num - Pointer to store the size of the array. - * Returns: - * 0 on success negative error code on failure (from errno.h). - * Remember to call libcras_node_info_array_destroy to free the array. - */ -DISABLE_CFI_ICALL -inline int libcras_client_get_nodes(struct libcras_client *client, - enum CRAS_STREAM_DIRECTION direction, - struct libcras_node_info ***nodes, - size_t *num) -{ - return client->get_nodes(client->client_, direction, nodes, num); -} - -/* - * Gets the default output buffer size. - * Args: - * client - Pointer returned from "libcras_client_create". - * size - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_client_get_default_output_buffer_size(struct libcras_client *client, - int *size) -{ - return client->get_default_output_buffer_size(client->client_, size); -} - -/* - * Gets the AEC group ID. - * Args: - * client - Pointer returned from "libcras_client_create". - * id - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_get_aec_group_id(struct libcras_client *client, - int *id) -{ - return client->get_aec_group_id(client->client_, id); -} - -/* - * Gets whether AEC is supported. - * Args: - * client - Pointer returned from "libcras_client_create". - * supported - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_get_aec_supported(struct libcras_client *client, - int *supported) -{ - return client->get_aec_supported(client->client_, supported); -} - -/* - * Gets whether the system is muted. - * Args: - * client - Pointer returned from "libcras_client_create". - * muted - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_get_system_muted(struct libcras_client *client, - int *muted) -{ - return client->get_aec_group_id(client->client_, muted); -} - -/* - * Mutes or unmutes the system. - * Args: - * client - Pointer returned from "libcras_client_create". - * mute - 1 is to mute and 0 is to unmute. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_set_system_mute(struct libcras_client *client, - int mute) -{ - return client->set_system_mute(client->client_, mute); -} - -/* - * Gets the index of the loopback device. - * Args: - * client - Pointer returned from "libcras_client_create". - * idx - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_client_get_loopback_dev_idx(struct libcras_client *client, - int *idx) -{ - return client->get_loopback_dev_idx(client->client_, idx); -} - -/* - * Creates a new struct to save stream params. - * Returns: - * If success, return a valid libcras_stream_params pointer. Otherwise, - * return NULL. - */ -struct libcras_stream_params *libcras_stream_params_create(); - -/* - * Destroys a stream params instance. - * Args: - * params - The pointer returned from libcras_stream_params_create. - */ -void libcras_stream_params_destroy(struct libcras_stream_params *params); - -/* - * Setup stream configuration parameters. - * Args: - * params - The pointer returned from libcras_stream_params_create. - * direction - Playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT). - * buffer_frames - total number of audio frames to buffer (dictates latency). - * cb_threshold - For playback, call back for more data when the buffer - * reaches this level. For capture, this is ignored (Audio callback will - * be called when buffer_frames have been captured). - * stream_type - Media or talk (currently only support "default"). - * client_type - The client type, like Chrome or CrOSVM. - * flags - Currently only used for CRAS_INPUT_STREAM_FLAG. - * user_data - Pointer that will be passed to the callback. - * stream_cb - The audio callback. Called when audio is needed(playback) or - * ready(capture). - * err_cb - Called when there is an error with the stream. - * rate - The sample rate of the audio stream. - * format - The format of the audio stream. - * num_channels - The number of channels of the audio stream. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_stream_params_set( - struct libcras_stream_params *params, - enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames, - size_t cb_threshold, enum CRAS_STREAM_TYPE stream_type, - enum CRAS_CLIENT_TYPE client_type, uint32_t flags, void *user_data, - libcras_stream_cb_t stream_cb, cras_error_cb_t err_cb, size_t rate, - snd_pcm_format_t format, size_t num_channels) -{ - return params->set(params->params_, direction, buffer_frames, - cb_threshold, stream_type, client_type, flags, - user_data, stream_cb, err_cb, rate, format, - num_channels); -} - -/* - * Sets channel layout on given stream parameter. - * Args: - * params - The pointer returned from libcras_stream_params_create. - * length - The length of the array. - * layout - An integer array representing the position of each channel in - * enum CRAS_CHANNEL. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_stream_params_set_channel_layout(struct libcras_stream_params *params, - int length, const int8_t *layout) -{ - return params->set_channel_layout(params->params_, length, layout); -} - -/* - * Enables AEC on given stream parameter. - * Args: - * params - The pointer returned from libcras_stream_params_create. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_stream_params_enable_aec(struct libcras_stream_params *params) -{ - params->enable_aec(params->params_); - return 0; -} - -/* - * Gets stream id from the callback data. - * Args: - * data - The pointer passed to the callback function. - * id - The pointer to save the stream id. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_stream_cb_data_get_stream_id(struct libcras_stream_cb_data *data, - cras_stream_id_t *id) -{ - return data->get_stream_id(data->data_, id); -} - -/* - * Gets stream buf from the callback data. - * Args: - * data - The pointer passed to the callback function. - * buf - The pointer to save the stream buffer. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_stream_cb_data_get_buf(struct libcras_stream_cb_data *data, - uint8_t **buf) -{ - return data->get_buf(data->data_, buf); -} - -/* - * Gets how many frames to read or play from the callback data. - * Args: - * data - The pointer passed to the callback function. - * frames - The pointer to save the number of frames. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_stream_cb_data_get_frames(struct libcras_stream_cb_data *data, - unsigned int *frames) -{ - return data->get_frames(data->data_, frames); -} - -/* - * Gets the latency from the callback data. - * Args: - * data - The pointer passed to the callback function. - * frames - The timespec pointer to save the latency. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_stream_cb_data_get_latency(struct libcras_stream_cb_data *data, - struct timespec *latency) -{ - return data->get_latency(data->data_, latency); -} - -/* - * Gets the user data from the callback data. - * Args: - * data - The pointer passed to the callback function. - * frames - The pointer to save the user data. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_stream_cb_data_get_usr_arg(struct libcras_stream_cb_data *data, - void **user_arg) -{ - return data->get_user_arg(data->data_, user_arg); -} - -/* - * Destroys a node info instance. - * Args: - * node - The libcras_node_info pointer to destroy. - */ -void libcras_node_info_destroy(struct libcras_node_info *node); - -/* - * Destroys a node info array. - * Args: - * nodes - The libcras_node_info pointer array to destroy. - * num - The size of the array. - */ -void libcras_node_info_array_destroy(struct libcras_node_info **nodes, - size_t num); - -/* - * Gets ID from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * id - The pointer to save ID. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_get_id(struct libcras_node_info *node, - uint64_t *id) -{ - return node->get_id(node->node_, id); -} - -/* - * Gets device index from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * dev_idx - The pointer to the device index. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_get_dev_idx(struct libcras_node_info *node, - uint32_t *dev_idx) -{ - return node->get_dev_idx(node->node_, dev_idx); -} - -/* - * Gets node index from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * node_idx - The pointer to save the node index. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_get_node_idx(struct libcras_node_info *node, - uint32_t *node_idx) -{ - return node->get_node_idx(node->node_, node_idx); -} - -/* - * Gets the max supported channels from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * max_supported_channels - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int -libcras_node_info_get_max_supported_channels(struct libcras_node_info *node, - uint32_t *max_supported_channels) -{ - return node->get_max_supported_channels(node->node_, - max_supported_channels); -} - -/* - * Gets whether the node is plugged from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * plugged - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_is_plugged(struct libcras_node_info *node, - bool *plugged) -{ - return node->is_plugged(node->node_, plugged); -} - -/* - * Gets whether the node is active from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * active - The pointer to save the result. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_is_active(struct libcras_node_info *node, - bool *active) -{ - return node->is_active(node->node_, active); -} - -/* - * Gets device type from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * type - The pointer to save the device type. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_get_type(struct libcras_node_info *node, - char **type) -{ - return node->get_type(node->node_, type); -} - -/* - * Gets device name from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * name - The pointer to save the device name. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_get_node_name(struct libcras_node_info *node, - char **name) -{ - return node->get_node_name(node->node_, name); -} - -/* - * Gets node name from the node info pointer. - * Args: - * node - The node info pointer. (Returned from libcras_client_get_nodes) - * name - The pointer to save the node name. - * Returns: - * 0 on success negative error code on failure (from errno.h). - */ -DISABLE_CFI_ICALL -inline int libcras_node_info_get_dev_name(struct libcras_node_info *node, - char **name) -{ - return node->get_dev_name(node->node_, name); -} - -#ifdef __cplusplus -} -#endif - -#endif /* CRAS_CLIENT_H_ */ diff --git a/cras/src/libcras/cras_helpers.c b/cras/src/libcras/cras_helpers.c deleted file mode 100644 index a2120eae..00000000 --- a/cras/src/libcras/cras_helpers.c +++ /dev/null @@ -1,159 +0,0 @@ -/* Copyright 2015 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <errno.h> -#include <stdint.h> -#include <sys/param.h> - -#include "cras_client.h" -#include "cras_util.h" - -struct buffer_data { - const uint8_t *buffer; - unsigned int offset; - unsigned int frame_bytes; - unsigned int len; -}; - -static int -play_buffer_callback(struct cras_client *client, cras_stream_id_t stream_id, - uint8_t *captured_samples, uint8_t *playback_samples, - unsigned int frames, const struct timespec *captured_time, - const struct timespec *playback_time, void *user_arg) -{ - struct buffer_data *data = (struct buffer_data *)user_arg; - int to_copy = data->len - data->offset; - - if (to_copy <= 0) { - free(user_arg); - return EOF; - } - - to_copy = MIN(to_copy, frames * data->frame_bytes); - - memcpy(playback_samples, data->buffer + data->offset, to_copy); - - data->offset += to_copy; - - return to_copy / data->frame_bytes; -} - -static int play_buffer_error(struct cras_client *client, - cras_stream_id_t stream_id, int error, - void *user_arg) -{ - free(user_arg); - return 0; -} - -int cras_helper_create_connect_async(struct cras_client **client, - cras_connection_status_cb_t connection_cb, - void *user_arg) -{ - int rc; - - rc = cras_client_create(client); - if (rc < 0) - return rc; - - cras_client_set_connection_status_cb(*client, connection_cb, user_arg); - - rc = cras_client_run_thread(*client); - if (rc < 0) - goto client_start_error; - - rc = cras_client_connect_async(*client); - if (rc < 0) - goto client_start_error; - - return 0; - -client_start_error: - cras_client_destroy(*client); - return rc; -} - -int cras_helper_create_connect(struct cras_client **client) -{ - int rc; - - rc = cras_client_create(client); - if (rc < 0) - return rc; - - rc = cras_client_connect(*client); - if (rc < 0) - goto client_start_error; - - rc = cras_client_run_thread(*client); - if (rc < 0) - goto client_start_error; - - rc = cras_client_connected_wait(*client); - if (rc < 0) - goto client_start_error; - - return 0; - -client_start_error: - cras_client_destroy(*client); - return rc; -} - -int cras_helper_add_stream_simple( - struct cras_client *client, enum CRAS_STREAM_DIRECTION direction, - void *user_data, cras_unified_cb_t unified_cb, cras_error_cb_t err_cb, - snd_pcm_format_t format, unsigned int frame_rate, - unsigned int num_channels, int dev_idx, cras_stream_id_t *stream_id_out) -{ - struct cras_audio_format *aud_format; - struct cras_stream_params *params; - int rc; - - aud_format = cras_audio_format_create(format, frame_rate, num_channels); - if (!aud_format) - return -ENOMEM; - - params = cras_client_unified_params_create(CRAS_STREAM_OUTPUT, 2048, - CRAS_STREAM_TYPE_DEFAULT, 0, - user_data, unified_cb, - err_cb, aud_format); - if (!params) { - rc = -ENOMEM; - goto done_add_stream; - } - - if (dev_idx < 0) - dev_idx = NO_DEVICE; - rc = cras_client_add_pinned_stream(client, dev_idx, stream_id_out, - params); - -done_add_stream: - cras_audio_format_destroy(aud_format); - cras_client_stream_params_destroy(params); - return rc; -} - -int cras_helper_play_buffer(struct cras_client *client, const void *buffer, - unsigned int frames, snd_pcm_format_t format, - unsigned int frame_rate, unsigned int num_channels, - int dev_idx) -{ - struct buffer_data *data; - cras_stream_id_t stream_id; - - data = malloc(sizeof(*data)); - - data->buffer = buffer; - data->frame_bytes = num_channels * PCM_FORMAT_WIDTH(format) / 8; - data->offset = 0; - data->len = frames * data->frame_bytes; - - return cras_helper_add_stream_simple(client, CRAS_STREAM_OUTPUT, data, - play_buffer_callback, - play_buffer_error, format, - frame_rate, num_channels, dev_idx, - &stream_id); -} diff --git a/cras/src/libcras/cras_helpers.h b/cras/src/libcras/cras_helpers.h deleted file mode 100644 index 604b2660..00000000 --- a/cras/src/libcras/cras_helpers.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright 2015 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef _CRAS_HELPERS_H -#define _CRAS_HELPERS_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Creates and connects a client to the running server asynchronously. - * - * When the connection has been established the connection_cb is executed - * with the appropriate state. See cras_connection_status_cb_t for more - * information. - * - * Args: - * client - Filled with a pointer to the new client. - * connection_cb - The connection status callback function. - * user_arg - Argument passed to the connection status callback. - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -int cras_helper_create_connect_async(struct cras_client **client, - cras_connection_status_cb_t connection_cb, - void *user_arg); - -/* Creates and connects a client to the running server. - * - * Waits forever (or interrupt) for the server to be available. - * - * Args: - * client - Filled with a pointer to the new client. - * Returns: - * 0 on success, or a negative error code on failure (from errno.h). - */ -int cras_helper_create_connect(struct cras_client **client); - -/* Adds a stream with the given parameters, no flags and a buffer size of 2048 - * Note that the unified_cb parameter is being phased out. - * TODO(crbug.com/972928): convert this from unified_cb. - * Args: - * client - The client to add the stream to (from cras_client_create). - * direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT) or - * loopback(CRAS_STREAM_POST_MIX_PRE_DSP). - * user_data - Pointer that will be passed to the callback. - * unified_cb - Called to request audio data or to notify the client when - * captured audio is available. Though this is a unified_cb, - * only one direction will be used for a stream, depending - * on the 'direction' parameter. - * err_cb - Called when there is an error with the stream. - * format - The type of the samples, ex. S16_LE. - * frame_rate - Sample rate. - * num_channels - Number of channels in the stream, should be 1 or 2 when - * using this API, for > 2 channel streams see cras_client.h. - * dev_idx - Set this to a negative number to play to the default device, if - * positive it is the index of the device to pin the stream to. - * stream_id_out - On success will be filled with the new stream id. - * Guaranteed to be set before any callbacks are made. - * Returns: - * 0 on success, negative error code on failure (from errno.h). - */ -int cras_helper_add_stream_simple(struct cras_client *client, - enum CRAS_STREAM_DIRECTION direction, - void *user_data, cras_unified_cb_t unified_cb, - cras_error_cb_t err_cb, - snd_pcm_format_t format, - unsigned int frame_rate, - unsigned int num_channels, int dev_idx, - cras_stream_id_t *stream_id_out); - -/* Plays the given buffer at a default latency. - * Args: - * client - The client to add the stream to (from cras_client_create). - * buffer - The audio samples. - * num_frames - The size of the buffer in number of samples. - * format - The type of the samples, ex. S16_LE. - * frame_rate - Sample rate. - * num_channels - Number of channels in the stream. - * dev_idx - Set this to a negative number to play to the default device, if - * positive it is the index of the device to pin the stream to. - * Returns: - * 0 on success, negative error code on failure (from errno.h). - */ -int cras_helper_play_buffer(struct cras_client *client, const void *buffer, - unsigned int num_frames, snd_pcm_format_t format, - unsigned int frame_rate, unsigned int num_channels, - int dev_idx); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* _CRAS_HELPERS_H */ |