// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2014 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include "hal-utils.h" #include "hal-log.h" #include "hal.h" #include "hal-msg.h" #include "ipc-common.h" #include "hal-ipc.h" static const btrc_callbacks_t *cbs = NULL; static bool interface_ready(void) { return cbs != NULL; } static void handle_remote_features(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_remote_features *ev = buf; if (cbs->remote_features_cb) cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr), ev->features); } static void handle_get_play_status(void *buf, uint16_t len, int fd) { if (cbs->get_play_status_cb) cbs->get_play_status_cb(); } static void handle_list_player_attrs(void *buf, uint16_t len, int fd) { if (cbs->list_player_app_attr_cb) cbs->list_player_app_attr_cb(); } static void handle_list_player_values(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_list_player_values *ev = buf; if (cbs->list_player_app_values_cb) cbs->list_player_app_values_cb(ev->attr); } static void handle_get_player_values(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_player_values *ev = buf; btrc_player_attr_t attrs[4]; int i; if (!cbs->get_player_app_value_cb) return; /* Convert uint8_t array to btrc_player_attr_t array */ for (i = 0; i < ev->number; i++) attrs[i] = ev->attrs[i]; cbs->get_player_app_value_cb(ev->number, attrs); } static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_player_attrs_text *ev = buf; btrc_player_attr_t attrs[4]; int i; if (!cbs->get_player_app_attrs_text_cb) return; /* Convert uint8_t array to btrc_player_attr_t array */ for (i = 0; i < ev->number; i++) attrs[i] = ev->attrs[i]; cbs->get_player_app_attrs_text_cb(ev->number, attrs); } static void handle_get_player_values_text(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_player_values_text *ev = buf; if (cbs->get_player_app_values_text_cb) cbs->get_player_app_values_text_cb(ev->attr, ev->number, ev->values); } static void handle_set_player_value(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_set_player_values *ev = buf; struct hal_avrcp_player_attr_value *attrs; btrc_player_settings_t values; int i; if (!cbs->set_player_app_value_cb) return; attrs = (struct hal_avrcp_player_attr_value *) ev->attrs; /* Convert to btrc_player_settings_t */ values.num_attr = ev->number; for (i = 0; i < ev->number; i++) { values.attr_ids[i] = attrs[i].attr; values.attr_values[i] = attrs[i].value; } cbs->set_player_app_value_cb(&values); } static void handle_get_element_attrs(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_get_element_attrs *ev = buf; btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS]; int i; if (!cbs->get_element_attr_cb) return; /* Convert uint8_t array to btrc_media_attr_t array */ for (i = 0; i < ev->number; i++) attrs[i] = ev->attrs[i]; cbs->get_element_attr_cb(ev->number, attrs); } static void handle_register_notification(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_register_notification *ev = buf; if (cbs->register_notification_cb) cbs->register_notification_cb(ev->event, ev->param); } static void handle_volume_changed(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_volume_changed *ev = buf; if (cbs->volume_change_cb) cbs->volume_change_cb(ev->volume, ev->type); } static void handle_passthrough_cmd(void *buf, uint16_t len, int fd) { struct hal_ev_avrcp_passthrough_cmd *ev = buf; if (cbs->passthrough_cmd_cb) cbs->passthrough_cmd_cb(ev->id, ev->state); } /* * handlers will be called from notification thread context, * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ static const struct hal_ipc_handler ev_handlers[] = { /* HAL_EV_AVRCP_REMOTE_FEATURES */ { handle_remote_features, false, sizeof(struct hal_ev_avrcp_remote_features) }, /* HAL_EV_AVRCP_GET_PLAY_STATUS */ { handle_get_play_status, false, 0 }, /* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */ { handle_list_player_attrs, false, 0 }, /* HAL_EV_AVRCP_LIST_PLAYER_VALUES */ { handle_list_player_values, false, sizeof(struct hal_ev_avrcp_list_player_values) }, /* HAL_EV_AVRCP_GET_PLAYER_VALUES */ { handle_get_player_values, true, sizeof(struct hal_ev_avrcp_get_player_values) }, /* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */ { handle_get_player_attrs_text, true, sizeof(struct hal_ev_avrcp_get_player_attrs_text) }, /* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */ { handle_get_player_values_text, true, sizeof(struct hal_ev_avrcp_get_player_values_text) }, /* HAL_EV_AVRCP_SET_PLAYER_VALUES */ { handle_set_player_value, true, sizeof(struct hal_ev_avrcp_set_player_values) }, /* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */ { handle_get_element_attrs, true, sizeof(struct hal_ev_avrcp_get_element_attrs) }, /* HAL_EV_AVRCP_REGISTER_NOTIFICATION */ { handle_register_notification, false, sizeof(struct hal_ev_avrcp_register_notification) }, /* HAL_EV_AVRCP_VOLUME_CHANGED */ { handle_volume_changed, false, sizeof(struct hal_ev_avrcp_volume_changed) }, /* HAL_EV_AVRCP_PASSTHROUGH_CMD */ { handle_passthrough_cmd, false, sizeof(struct hal_ev_avrcp_passthrough_cmd) }, }; static bt_status_t init(btrc_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; int ret; DBG(""); if (interface_ready()) return BT_STATUS_DONE; cbs = callbacks; hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers, sizeof(ev_handlers) / sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_AVRCP; cmd.mode = HAL_MODE_DEFAULT; cmd.max_clients = 1; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); if (ret != BT_STATUS_SUCCESS) { cbs = NULL; hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); } return ret; } static bt_status_t get_play_status_rsp(btrc_play_status_t status, uint32_t song_len, uint32_t song_pos) { struct hal_cmd_avrcp_get_play_status cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.status = status; cmd.duration = song_len; cmd.position = song_pos; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t list_player_app_attr_rsp(int num_attr, btrc_player_attr_t *p_attrs) { char buf[IPC_MTU]; struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_attr < 0) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd) + num_attr; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->number = num_attr; memcpy(cmd->attrs, p_attrs, num_attr); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_LIST_PLAYER_ATTRS, len, cmd, NULL, NULL, NULL); } static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals) { char buf[IPC_MTU]; struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_val < 0) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd) + num_val; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->number = num_val; memcpy(cmd->values, p_vals, num_val); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_LIST_PLAYER_VALUES, len, cmd, NULL, NULL, NULL); } static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf; size_t len, attrs_len; int i; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (!p_vals) return BT_STATUS_PARM_INVALID; attrs_len = p_vals->num_attr * sizeof(struct hal_avrcp_player_attr_value); len = sizeof(*cmd) + attrs_len; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->number = p_vals->num_attr; for (i = 0; i < p_vals->num_attr; i++) { cmd->attrs[i].attr = p_vals->attr_ids[i]; cmd->attrs[i].value = p_vals->attr_values[i]; } return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_ATTRS, len, cmd, NULL, NULL, NULL); } static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len) { struct hal_avrcp_player_setting_text *value = (void *) ptr; size_t attr_len = sizeof(*value); if (attr_len + *len > IPC_MTU) return 0; value->id = id; value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN); *len += attr_len; if (value->len + *len > IPC_MTU) value->len = IPC_MTU - *len; memcpy(value->text, text, value->len); *len += value->len; return attr_len + value->len; } static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr, btrc_player_setting_text_t *p_attrs, size_t *len) { int i; for (i = 0; i < num_attr && *len < IPC_MTU; i++) { int ret; ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len); if (ret == 0) break; ptr += ret; } return i; } static bt_status_t get_player_app_attr_text_rsp(int num_attr, btrc_player_setting_text_t *p_attrs) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf; uint8_t *ptr; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd); ptr = (uint8_t *) &cmd->attrs[0]; cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, len, cmd, NULL, NULL, NULL); } static bt_status_t get_player_app_value_text_rsp(int num_val, btrc_player_setting_text_t *p_vals) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf; uint8_t *ptr; size_t len; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; if (num_val < 0) return BT_STATUS_PARM_INVALID; len = sizeof(*cmd); ptr = (uint8_t *) &cmd->values[0]; cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, len, cmd, NULL, NULL, NULL); } static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr, btrc_element_attr_val_t *p_attrs, size_t *len) { int i; for (i = 0; i < num_attr && *len < IPC_MTU; i++) { int ret; ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len); if (ret == 0) break; ptr += ret; } return i; } static bt_status_t get_element_attr_rsp(uint8_t num_attr, btrc_element_attr_val_t *p_attrs) { char buf[IPC_MTU]; struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; size_t len; uint8_t *ptr; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; len = sizeof(*cmd); ptr = (uint8_t *) &cmd->values[0]; cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len); return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, len, cmd, NULL, NULL, NULL); } static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status) { struct hal_cmd_avrcp_set_player_attrs_value cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.status = rsp_status; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t play_status_changed_rsp(btrc_notification_type_t type, btrc_play_status_t *play_status) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; size_t len; cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED; cmd->type = type; cmd->len = 1; memcpy(cmd->data, play_status, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t track_change_rsp(btrc_notification_type_t type, btrc_uid_t *track) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; size_t len; cmd->event = BTRC_EVT_TRACK_CHANGE; cmd->type = type; cmd->len = sizeof(*track); memcpy(cmd->data, track, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t track_reached_end_rsp(btrc_notification_type_t type) { struct hal_cmd_avrcp_register_notification cmd; cmd.event = BTRC_EVT_TRACK_REACHED_END; cmd.type = type; cmd.len = 0; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t track_reached_start_rsp(btrc_notification_type_t type) { struct hal_cmd_avrcp_register_notification cmd; cmd.event = BTRC_EVT_TRACK_REACHED_START; cmd.type = type; cmd.len = 0; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, sizeof(cmd), &cmd, NULL, NULL, NULL); } static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type, uint32_t *song_pos) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; size_t len; cmd->event = BTRC_EVT_PLAY_POS_CHANGED; cmd->type = type; cmd->len = sizeof(*song_pos); memcpy(cmd->data, song_pos, cmd->len); len = sizeof(*cmd) + cmd->len; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t settings_changed_rsp(btrc_notification_type_t type, btrc_player_settings_t *player_setting) { char buf[IPC_MTU]; struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; struct hal_avrcp_player_attr_value *attrs; size_t len, param_len; int i; param_len = player_setting->num_attr * sizeof(*attrs); len = sizeof(*cmd) + param_len; if (len > IPC_MTU) return BT_STATUS_PARM_INVALID; cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED; cmd->type = type; cmd->len = param_len; attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0]; for (i = 0; i < player_setting->num_attr; i++) { attrs[i].attr = player_setting->attr_ids[i]; attrs[i].value = player_setting->attr_values[i]; } return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_REGISTER_NOTIFICATION, len, cmd, NULL, NULL, NULL); } static bt_status_t register_notification_rsp(btrc_event_id_t event_id, btrc_notification_type_t type, btrc_register_notification_t *p_param) { DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; switch (event_id) { case BTRC_EVT_PLAY_STATUS_CHANGED: return play_status_changed_rsp(type, &p_param->play_status); case BTRC_EVT_TRACK_CHANGE: return track_change_rsp(type, &p_param->track); case BTRC_EVT_TRACK_REACHED_END: return track_reached_end_rsp(type); case BTRC_EVT_TRACK_REACHED_START: return track_reached_start_rsp(type); case BTRC_EVT_PLAY_POS_CHANGED: return play_pos_changed_rsp(type, &p_param->song_pos); case BTRC_EVT_APP_SETTINGS_CHANGED: return settings_changed_rsp(type, &p_param->player_setting); default: return BT_STATUS_PARM_INVALID; } } static bt_status_t set_volume(uint8_t volume) { struct hal_cmd_avrcp_set_volume cmd; DBG(""); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd.value = volume; return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, sizeof(cmd), &cmd, NULL, NULL, NULL); } static void cleanup(void) { struct hal_cmd_unregister_module cmd; DBG(""); if (!interface_ready()) return; cmd.service_id = HAL_SERVICE_ID_AVRCP; hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); cbs = NULL; } static btrc_interface_t iface = { .size = sizeof(iface), .init = init, .get_play_status_rsp = get_play_status_rsp, .list_player_app_attr_rsp = list_player_app_attr_rsp, .list_player_app_value_rsp = list_player_app_value_rsp, .get_player_app_value_rsp = get_player_app_value_rsp, .get_player_app_attr_text_rsp = get_player_app_attr_text_rsp, .get_player_app_value_text_rsp = get_player_app_value_text_rsp, .get_element_attr_rsp = get_element_attr_rsp, .set_player_app_value_rsp = set_player_app_value_rsp, .register_notification_rsp = register_notification_rsp, .set_volume = set_volume, .cleanup = cleanup }; btrc_interface_t *bt_get_avrcp_interface(void) { return &iface; }