diff --git a/btfmcodec/Makefile b/btfmcodec/Makefile index 966976f5b0..9b41a19b17 100644 --- a/btfmcodec/Makefile +++ b/btfmcodec/Makefile @@ -1,4 +1,4 @@ ccflags-y += -I$(BT_ROOT)/include ccflags-y += -I$(BT_ROOT)/btfmcodec/include -btfmcodec-objs := btfm_codec.o btfm_codec_hw_interface.o btfm_codec_interface.o +btfmcodec-objs := btfm_codec.o btfm_codec_hw_interface.o btfm_codec_interface.o btfm_codec_btadv_interface.o obj-$(CONFIG_BTFM_CODEC) += btfmcodec.o diff --git a/btfmcodec/btfm_codec.c b/btfmcodec/btfm_codec.c index a6e789df23..75da3097ca 100644 --- a/btfmcodec/btfm_codec.c +++ b/btfmcodec/btfm_codec.c @@ -24,7 +24,7 @@ struct btfmcodec_data *btfmcodec; struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE}; struct btfmcodec_char_device *btfmcodec_dev; #define cdev_to_btfmchardev(_cdev) container_of(_cdev, struct btfmcodec_char_device, cdev) -#define MIN_PKT_LEN 0x3 +#define MIN_PKT_LEN 0x9 char *coverttostring(enum btfmcodec_states state) { switch (state) { @@ -63,15 +63,15 @@ static int btfmcodec_dev_open(struct inode *inode, struct file *file) { struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev); struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec; - int active_clients = refcount_read(&btfmcodec_dev->active_clients); + unsigned int active_clients = refcount_read(&btfmcodec_dev->active_clients); btfmcodec->states.current_state = IDLE; /* Just a temp*/ - BTFMCODEC_INFO("File mode :%u", file->f_mode); + btfmcodec->states.next_state = IDLE; BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n", btfmcodec_dev->dev_name, current->comm, task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients)); /* Don't allow a new client if already one is active. */ - if (active_clients > 0) { + if (active_clients > 1) { BTFMCODEC_WARN("%s: Not honoring open as other client is active", __func__); return EACCES; } @@ -95,12 +95,12 @@ static int btfmcodec_dev_release(struct inode *inode, struct file *file) struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev); unsigned long flags; - BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n", + BTFMCODEC_INFO("for %s by %s:%d active_clients[%u]\n", btfmcodec_dev->dev_name, current->comm, task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients)); refcount_dec(&btfmcodec_dev->active_clients); - if (refcount_read(&btfmcodec_dev->active_clients) == 0) { + if (refcount_read(&btfmcodec_dev->active_clients) == 1) { spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags); skb_queue_purge(&btfmcodec_dev->txq); /* Wakeup the device if waiting for the data */ @@ -110,42 +110,93 @@ static int btfmcodec_dev_release(struct inode *inode, struct file *file) skb_queue_purge(&btfmcodec_dev->rxq); } + btfmcodec->states.current_state = IDLE; + btfmcodec->states.next_state = IDLE; return 0; } -btm_opcode get_opcode (struct sk_buff *skb) +btm_opcode STREAM_TO_UINT32 (struct sk_buff *skb) { - return ((skb->data[0]<< 8) | skb->data[1]); + return (skb->data[0] | (skb->data[1] << 8) | + (skb->data[2] << 16) | (skb->data[3] << 24)); } static void btfmcodec_dev_rxwork(struct work_struct *work) { struct btfmcodec_char_device *btfmcodec_dev = container_of(work, struct btfmcodec_char_device, rx_work); struct sk_buff *skb; - struct btm_req *req_pkt; + uint32_t len; + uint8_t status; + int idx; BTFMCODEC_DBG("start"); while ((skb = skb_dequeue(&btfmcodec_dev->rxq))) { - btm_opcode opcode = get_opcode(skb); -// skb_pull(skb, sizeof(btm_opcode)); + btm_opcode opcode = STREAM_TO_UINT32(skb); + skb_pull(skb, sizeof(btm_opcode)); + len = STREAM_TO_UINT32(skb); + skb_pull(skb, sizeof(len)); switch (opcode) { case BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ: - req_pkt = (void *)skb->data; - req_pkt->opcode = opcode; - BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ opcode %4x and len %d", req_pkt->opcode, req_pkt->len); - btfmcodec_dev_enqueue_pkt(btfmcodec_dev, skb->data, skb->len); + idx = BTM_PKT_TYPE_PREPARE_REQ; + BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ"); + if (len == BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN) { + btfmcodec_dev->status[idx] = skb->data[0]; + BTFMCODEC_INFO("prepare wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer); + queue_work(btfmcodec_dev->workqueue, &btfmcodec_dev->wq_prepare_bearer); + } else { + BTFMCODEC_ERR("wrong packet format with len:%d", len); + } break; case BTM_BTFMCODEC_MASTER_CONFIG_RSP: - BTFMCODEC_DBG("BTM_BTFMCODEC_MASTER_CONFIG_RSP"); + idx = BTM_PKT_TYPE_MASTER_CONFIG_RSP; + if (len == BTM_MASTER_CONFIG_RSP_LEN) { + status = skb->data[1]; + if (status == MSG_SUCCESS) + btfmcodec_dev->status[idx] = BTM_RSP_RECV; + else + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } else { + BTFMCODEC_ERR("wrong packet format with len:%d", len); + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } + BTFMCODEC_INFO("Rx BTM_BTFMCODEC_MASTER_CONFIG_RSP status:%d", + status); + wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); break; case BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP: - BTFMCODEC_DBG("BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP"); + idx = BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP; + if (len == BTM_MASTER_CONFIG_RSP_LEN) { + status = skb->data[1]; + if (status == MSG_SUCCESS) + btfmcodec_dev->status[idx] = BTM_RSP_RECV; + else + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } else { + BTFMCODEC_ERR("wrong packet format with len:%d", len); + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } + BTFMCODEC_INFO("Rx BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP status:%d", + status); + wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); break; case BTM_BTFMCODEC_BEARER_SWITCH_IND: - BTFMCODEC_DBG("BTM_BTFMCODEC_BEARER_SWITCH_IND"); + idx = BTM_PKT_TYPE_BEARER_SWITCH_IND; + if (len == BTM_BEARER_SWITCH_IND_LEN) { + status = skb->data[0]; + if (status == MSG_SUCCESS) + btfmcodec_dev->status[idx] = BTM_RSP_RECV; + else + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } else { + BTFMCODEC_ERR("wrong packet format with len:%d", len); + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } + BTFMCODEC_INFO("Rx BTM_BTFMCODEC_BEARER_SWITCH_IND status:%d", + status); + wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); break; default: - BTFMCODEC_ERR("wrong opcode:%04x", opcode); + BTFMCODEC_ERR("wrong opcode:%08x", opcode); } kfree_skb(skb); } @@ -172,7 +223,7 @@ static ssize_t btfmcodec_dev_write(struct file *file, void *kbuf; int ret = 0; - if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) { + if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) { BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current)); return -EINVAL; } else { @@ -219,16 +270,22 @@ static ssize_t btfmcodec_dev_write(struct file *file, free_kbuf: mutex_unlock(&btfmcodec_dev->lock); BTFMCODEC_DBG("finish to %s ret %d\n", btfmcodec_dev->dev_name, ret); - return ret; + return ret < 0 ? ret : count; } -int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, uint8_t *buf, int len) +int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, void *buf, int len) { struct sk_buff *skb; unsigned long flags; + uint8_t *cmd = buf; BTFMCODEC_DBG("start"); spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags); + if (refcount_read(&btfmcodec_dev->active_clients) == 1) { + BTFMCODEC_WARN("no active clients discarding the packet"); + spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags); + return -EINVAL; + } skb = alloc_skb(len, GFP_ATOMIC); if (!skb) { BTFMCODEC_ERR("failed to allocate memory"); @@ -236,7 +293,7 @@ int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, uint8 return -ENOMEM; } - skb_put_data(skb, buf, len); + skb_put_data(skb, cmd, len); skb_queue_tail(&btfmcodec_dev->txq, skb); wake_up_interruptible(&btfmcodec_dev->readq); spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags); @@ -261,7 +318,7 @@ static __poll_t btfmcodec_dev_poll(struct file *file, poll_table *wait) unsigned long flags; BTFMCODEC_DBG("start"); - if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) { + if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) { BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current)); return -EINVAL; } else { @@ -273,7 +330,7 @@ static __poll_t btfmcodec_dev_poll(struct file *file, poll_table *wait) mutex_lock(&btfmcodec_dev->lock); /* recheck if the client has released by the driver */ - if (refcount_read(&btfmcodec_dev->active_clients) == 0) { + if (refcount_read(&btfmcodec_dev->active_clients) == 1) { BTFMCODEC_WARN("port has been closed alreadt"); mutex_unlock(&btfmcodec_dev->lock); return POLLHUP; @@ -312,7 +369,7 @@ static ssize_t btfmcodec_dev_read(struct file *file, int use; BTFMCODEC_DBG("start"); - if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) { + if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) { BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current)); return -EINVAL; } else { @@ -333,7 +390,7 @@ static ssize_t btfmcodec_dev_read(struct file *file, return -ERESTARTSYS; /* We lost the client while waiting */ - if (!refcount_read(&btfmcodec->btfmcodec_dev->active_clients)) + if (refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) return -ENETRESET; spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags); @@ -391,7 +448,7 @@ static ssize_t btfmcodec_attributes_show(struct device *dev, struct device_attribute *attr, char *buf) { -// struct btfmcodec_char_device *btfmcodec_dev = dev_to_btfmcodec(dev); +// struct btfmcodec_get_current_transport *btfmcodec_dev = dev_to_btfmcodec(dev); return 0; } @@ -409,7 +466,7 @@ static int __init btfmcodec_init(void) struct btfmcodec_state_machine *states; struct btfmcodec_char_device *btfmcodec_dev; struct device *dev; - int ret; + int ret, i; BTFMCODEC_INFO("starting up the module"); btfmcodec = kzalloc(sizeof(struct btfmcodec_data), GFP_KERNEL); @@ -457,7 +514,7 @@ static int __init btfmcodec_init(void) // ToDo Rethink of having btfmcodec alone instead of btfmcodec btfmcodec->btfmcodec_dev = btfmcodec_dev; - refcount_set(&btfmcodec_dev->active_clients, 0); + refcount_set(&btfmcodec_dev->active_clients, 1); mutex_init(&btfmcodec_dev->lock); strlcpy(btfmcodec_dev->dev_name, "btfmcodec_dev", DEVICE_NAME_MAX_LEN); device_initialize(dev); @@ -499,6 +556,17 @@ static int __init btfmcodec_init(void) init_waitqueue_head(&btfmcodec_dev->readq); spin_lock_init(&btfmcodec_dev->tx_queue_lock); skb_queue_head_init(&btfmcodec_dev->txq); + INIT_LIST_HEAD(&btfmcodec->config_head); + for (i = 0; i < BTM_PKT_TYPE_MAX; i++) { + init_waitqueue_head(&btfmcodec_dev->rsp_wait_q[i]); + } + mutex_init(&states->state_machine_lock); + btfmcodec_dev->workqueue = alloc_ordered_workqueue("btfmcodec_wq", 0); + if (!btfmcodec_dev->workqueue) { + BTFMCODEC_ERR("btfmcodec_dev Workqueue not initialized properly"); + ret = -ENOMEM; + goto free_device; + } return ret; free_device: diff --git a/btfmcodec/btfm_codec_btadv_interface.c b/btfmcodec/btfm_codec_btadv_interface.c new file mode 100644 index 0000000000..79f0488df6 --- /dev/null +++ b/btfmcodec/btfm_codec_btadv_interface.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 202333 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "btfm_codec.h" +#include "btfm_codec_pkt.h" +#include "btfm_codec_btadv_interface.h" + +void btfmcodec_initiate_hwep_shutdown(struct btfmcodec_char_device *btfmcodec_dev) +{ + wait_queue_head_t *rsp_wait_q = + &btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_HWEP_SHUTDOWN]; + int ret; + uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_HWEP_SHUTDOWN]; + + *status = BTM_WAITING_RSP; + BTFMCODEC_INFO("queuing work to shutdown"); + schedule_work(&btfmcodec_dev->wq_hwep_shutdown); + BTFMCODEC_INFO("waiting here for shutdown"); + ret = wait_event_interruptible_timeout(*rsp_wait_q, + *status != BTM_WAITING_RSP, + msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT)); + + /* Rethink of having a new packet to notify transport switch error */ + if (ret == 0) { + BTFMCODEC_ERR("failed to recevie to complete hwep_shutdown"); + flush_work(&btfmcodec_dev->wq_hwep_shutdown); + } else { + if (*status == BTM_RSP_RECV) { + BTFMCODEC_ERR("sucessfully closed hwep"); + return; + } else if (*status == BTM_FAIL_RESP_RECV) { + BTFMCODEC_ERR("Failed to close hwep"); + return; + } + } +} + +void btfmcodec_move_to_next_state(struct btfmcodec_state_machine *state) +{ + mutex_lock(&state->state_machine_lock); + if (state->current_state == BT_Connecting || + state->current_state == BTADV_AUDIO_Connecting) { + state->current_state += 1; + BTFMCODEC_INFO("moving from %s to %s", + coverttostring(state->current_state -1 ), + coverttostring(state->current_state)); + } else { + BTFMCODEC_ERR("State machine might have gone bad. reseting to default"); + state->current_state = IDLE; + } + + state->prev_state = IDLE; + mutex_unlock(&state->state_machine_lock); +} + +void btfmcodec_revert_current_state(struct btfmcodec_state_machine *state) +{ + mutex_lock(&state->state_machine_lock); + BTFMCODEC_INFO("reverting from %s to %s", coverttostring(state->current_state), + coverttostring(state->prev_state)); + state->current_state = state->prev_state; + state->prev_state = IDLE; + mutex_unlock(&state->state_machine_lock); +} + +void btfmcodec_set_current_state(struct btfmcodec_state_machine *state, + btfmcodec_state current_state) +{ + mutex_lock(&state->state_machine_lock); + BTFMCODEC_INFO("moving from %s to %s", coverttostring(state->current_state), + coverttostring(current_state)); + state->prev_state = state->current_state; + state->current_state = current_state; + mutex_unlock(&state->state_machine_lock); +} + +btfmcodec_state btfmcodec_get_current_transport(struct + btfmcodec_state_machine *state) +{ + btfmcodec_state current_state; + + mutex_lock(&state->state_machine_lock); + current_state = state->current_state; + mutex_unlock(&state->state_machine_lock); + return current_state; +} + +int btfmcodec_frame_transport_switch_ind_pkt(struct btfmcodec_char_device *btfmcodec_dev, + uint8_t active_transport, + uint8_t status) +{ + struct btm_ctrl_pkt rsp; + + rsp.opcode = BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND; + rsp.len = BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN; + rsp.active_transport = active_transport; + rsp.status = status; + return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &rsp, (rsp.len + + BTM_HEADER_LEN)); +} + +int btfmcodec_frame_prepare_bearer_rsp_pkt(struct btfmcodec_char_device *btfmcodec_dev, + uint8_t active_transport, + uint8_t status) +{ + struct btm_ctrl_pkt rsp; + + rsp.opcode = BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP; + rsp.len =BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN; + rsp.active_transport = active_transport; + rsp.status = status; + return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &rsp, (rsp.len + + BTM_HEADER_LEN)); +} + +int btfmcodec_wait_for_bearer_ind(struct btfmcodec_char_device *btfmcodec_dev) +{ + wait_queue_head_t *rsp_wait_q = + &btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_BEARER_SWITCH_IND]; + int ret; + uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_BEARER_SWITCH_IND]; + + *status = BTM_WAITING_RSP; + ret = wait_event_interruptible_timeout(*rsp_wait_q, + *status != BTM_WAITING_RSP, + msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT)); + + if (ret == 0) { + BTFMCODEC_ERR("failed to recevie BTM_BEARER_SWITCH_IND"); + ret = -MSG_INTERNAL_TIMEOUT; + } else { + if (*status == BTM_RSP_RECV) { + ret = 0; + } else if (*status == BTM_FAIL_RESP_RECV) { + BTFMCODEC_ERR("Rx BTM_BEARER_SWITCH_IND with failure status"); + ret = -1; + } + } + + return ret; +} + +int btfmcodec_initiate_hwep_configuration(struct btfmcodec_char_device *btfmcodec_dev) +{ + wait_queue_head_t *rsp_wait_q = + &btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_HWEP_CONFIG]; + uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_HWEP_CONFIG]; + int ret; + + schedule_work(&btfmcodec_dev->wq_hwep_configure); + + ret = wait_event_interruptible_timeout(*rsp_wait_q, + *status != BTM_WAITING_RSP, + msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT)); + + if (ret == 0) { + BTFMCODEC_ERR("failed to recevie complete hwep configure"); + flush_work(&btfmcodec_dev->wq_hwep_configure); + ret = -1; + } else { + if (*status == BTM_RSP_RECV) { + ret = 0; + } else if (*status == BTM_FAIL_RESP_RECV) { + BTFMCODEC_ERR("Failed to close hwep moving back to previous state"); + ret = -1; + } + } + + return ret; +} + +void btfmcodec_configure_hwep(struct btfmcodec_char_device *btfmcodec_dev) +{ + struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec; + struct btfmcodec_state_machine *state = &btfmcodec->states; + int ret; + uint8_t status = MSG_SUCCESS; + + ret = btfmcodec_initiate_hwep_configuration(btfmcodec_dev); + if (ret < 0) { + status = MSG_FAILED_TO_CONFIGURE_HWEP; + /* Move back to BTADV_AUDIO_Connected from BT_Connecting */ + btfmcodec_revert_current_state(state); + } + + ret = btfmcodec_frame_transport_switch_ind_pkt(btfmcodec_dev, + btfmcodec_get_current_transport(state), status); + + if (status != MSG_SUCCESS) + return; + + if (ret < 0) { + ret = btfmcodec_wait_for_bearer_ind(btfmcodec_dev); + if (ret < 0) { + /* Move back to BTADV_AUDIO_Connected for failure cases*/ + BTFMCODEC_ERR("moving back to previous state"); + btfmcodec_revert_current_state(state); + /* close HWEP */ + btfmcodec_initiate_hwep_shutdown(btfmcodec_dev); + if (ret == -MSG_INTERNAL_TIMEOUT) { + btfmcodec_frame_transport_switch_ind_pkt(btfmcodec_dev, BT, + MSG_INTERNAL_TIMEOUT); + } + } else { + /* move from BT_Connecting to BT_Connected */ + btfmcodec_move_to_next_state(state); + } + } else { + /* add code to handle packet errors */ + } +} + +void btfmcodec_prepare_bearer(struct btfmcodec_char_device *btfmcodec_dev, + enum transport_type new_transport) +{ + struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec; + struct btfmcodec_state_machine *state = &btfmcodec->states; + btfmcodec_state current_state; + int ret = -1; + + if (new_transport > (ARRAY_SIZE(transport_type_text))) { + btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev, + (uint8_t) btfmcodec_get_current_transport(state), + MSG_WRONG_TRANSPORT_TYPE); + return; + } + + BTFMCODEC_INFO("Rx to switch from transport type %s to %s", + coverttostring(btfmcodec_get_current_transport(state)), + transport_type_text[new_transport - 1]); + + current_state = btfmcodec_get_current_transport(state); + if (new_transport == BT) { + /* If BT is already active. send +ve ack to BTADV Audio Manager */ + if (current_state == BT_Connected || + current_state == BT_Connecting) { + btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev, + (uint8_t)current_state, MSG_SUCCESS); + return; + } else if (current_state == BTADV_AUDIO_Connected || + current_state == BTADV_AUDIO_Connecting|| + current_state == IDLE) { + if (btfmcodec_is_valid_cache_avb(btfmcodec)) { + BTFMCODEC_INFO("detected BTADV audio Gaming usecase to BT usecase"); + btfmcodec_set_current_state(state, BT_Connecting); + btfmcodec_configure_hwep(btfmcodec_dev); + } else { + if (current_state != IDLE) + BTFMCODEC_INFO("detected BTADV Audio lossless to IDLE"); + BTFMCODEC_INFO("moving to IDLE as no config available"); + btfmcodec_set_current_state(state, IDLE); + btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev, + btfmcodec_get_current_transport(state), + MSG_SUCCESS); + /* No need wait for bearer switch indications as BTFMCODEC + * driver doesn't have configs to configure + */ + } + } + } else if(new_transport == BTADV) { + /* If BTADV audio is already active. send +ve ack to BTADV audio Manager */ + if (current_state == BTADV_AUDIO_Connecting || + current_state == BTADV_AUDIO_Connected) { + btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev, + (uint8_t)current_state, MSG_SUCCESS); + return; + } else { + btfmcodec_set_current_state(state, BTADV_AUDIO_Connecting); + if (btfmcodec_is_valid_cache_avb(btfmcodec)) { + BTFMCODEC_INFO("detected BT to BTADV audio Gaming usecase"); + } else { + BTFMCODEC_INFO("detected IDLE to BTADV audio lossless usecase"); + } + + ret = btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev, + BTADV_AUDIO_Connecting, MSG_SUCCESS); + if (ret < 0) + return; + + /* Wait here to get Bearer switch indication */ + ret = btfmcodec_wait_for_bearer_ind(btfmcodec_dev); + if (ret < 0) { + BTFMCODEC_ERR("moving back to previous state"); + btfmcodec_revert_current_state(state); + if (ret == -MSG_INTERNAL_TIMEOUT) { + btfmcodec_frame_transport_switch_ind_pkt( + btfmcodec_dev, BTADV, + MSG_INTERNAL_TIMEOUT); + } + } else { + btfmcodec_move_to_next_state(state); + } + if (ret < 0) + return; + + if (btfmcodec_is_valid_cache_avb(btfmcodec)) { + BTFMCODEC_INFO("Initiating BT port close..."); + btfmcodec_initiate_hwep_shutdown(btfmcodec_dev); + } + } + } +} + +void btfmcodec_wq_prepare_bearer(struct work_struct *work) +{ + struct btfmcodec_char_device *btfmcodec_dev = container_of(work, + struct btfmcodec_char_device, + wq_prepare_bearer); + int idx = BTM_PKT_TYPE_PREPARE_REQ; + BTFMCODEC_INFO(": with new transport:%d", btfmcodec_dev->status[idx]); + btfmcodec_prepare_bearer(btfmcodec_dev, btfmcodec_dev->status[idx]); +} diff --git a/btfmcodec/btfm_codec_interface.c b/btfmcodec/btfm_codec_interface.c index 5a4aa30f07..be360f34a9 100644 --- a/btfmcodec/btfm_codec_interface.c +++ b/btfmcodec/btfm_codec_interface.c @@ -6,23 +6,116 @@ #include #include "btfm_codec.h" #include "btfm_codec_interface.h" +#include "btfm_codec_pkt.h" +#include "btfm_codec_btadv_interface.h" static struct snd_soc_dai_driver *btfmcodec_dai_info; +uint32_t bits_per_second; + +static int btfm_codec_get_mixer_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = kcontrol->private_data; + struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); + struct hwep_data *hwepinfo = btfmcodec->hwep_info; + struct btfmcodec_state_machine states = btfmcodec->states; + struct snd_kcontrol_new *mixer_ctrl = hwepinfo->mixer_ctrl; + struct snd_ctl_elem_id id = kcontrol->id; + int num_mixer_ctrl = hwepinfo->num_mixer_ctrl; + int i = 0; + + BTFMCODEC_DBG(""); + if (states.current_state != IDLE) { + BTFMCODEC_WARN("Received probe when state is :%s", + coverttostring(states.current_state)); + } else { + for (; i < num_mixer_ctrl ; i++) { + BTFMCODEC_DBG("checking mixer_ctrl:%s and current mixer:%s", + id.name, mixer_ctrl[i].name); + if (!strncmp(id.name, mixer_ctrl[i].name, 64)) { + BTFMCODEC_DBG("Matched"); + mixer_ctrl[i].get(kcontrol, ucontrol); + break; + } + } + if (num_mixer_ctrl == i) + return 0; + } + return 1; +} + + +static int btfmcodec_put_mixer_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = kcontrol->private_data; + struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); + struct hwep_data *hwepinfo = btfmcodec->hwep_info; + struct btfmcodec_state_machine states = btfmcodec->states; + struct snd_kcontrol_new *mixer_ctrl = hwepinfo->mixer_ctrl; + struct snd_ctl_elem_id id = kcontrol->id; + int num_mixer_ctrl = hwepinfo->num_mixer_ctrl; + int i = 0; + + BTFMCODEC_DBG(""); + if (states.current_state != IDLE) { + BTFMCODEC_WARN("Received probe when state is :%s", + coverttostring(states.current_state)); + } else { + for (; i < num_mixer_ctrl ; i++) { + BTFMCODEC_DBG("checking mixer_ctrl:%s and current mixer:%s", + id.name, mixer_ctrl[i].name); + if (!strncmp(id.name, mixer_ctrl[i].name, 64)) { + BTFMCODEC_DBG("Matched"); + mixer_ctrl[i].put(kcontrol, ucontrol); + break; + } + } + if (num_mixer_ctrl == i) + return 0; + } + return 1; +} static int btfmcodec_codec_probe(struct snd_soc_component *codec) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); - struct btfmcodec_state_machine states = btfmcodec->states; + struct btfmcodec_state_machine *state = &btfmcodec->states; struct hwep_data *hwep_info = btfmcodec->hwep_info; + int num_mixer_ctrl = hwep_info->num_mixer_ctrl; BTFMCODEC_DBG(""); // ToDo: check weather probe has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); + if (btfmcodec_get_current_transport(state)!= IDLE) { + BTFMCODEC_WARN("Received probe when state is :%s", + coverttostring(btfmcodec_get_current_transport(state))); } else if (hwep_info->drv && hwep_info->drv->hwep_probe) { - return hwep_info->drv->hwep_probe(codec); + hwep_info->drv->hwep_probe(codec); + /* Register mixer control */ + if (hwep_info->mixer_ctrl && num_mixer_ctrl >= 1) { + struct snd_kcontrol_new *mixer_ctrl; + int i = 0; + mixer_ctrl = (struct snd_kcontrol_new *) + kzalloc(num_mixer_ctrl * + sizeof(struct snd_kcontrol_new), GFP_KERNEL); + if (!mixer_ctrl) { + BTFMCODEC_ERR("failed to register mixer controls"); + goto end; + } + + BTFMCODEC_INFO("Registering %d mixer controls", num_mixer_ctrl); + memcpy(mixer_ctrl, hwep_info->mixer_ctrl, num_mixer_ctrl * sizeof(struct snd_kcontrol_new)); + for (; i< num_mixer_ctrl; i++) { + BTFMCODEC_INFO("name of control:%s", mixer_ctrl[i].name); + mixer_ctrl[i].get = btfm_codec_get_mixer_control; + mixer_ctrl[i].put = btfmcodec_put_mixer_control; + } + snd_soc_add_component_controls(codec, mixer_ctrl, num_mixer_ctrl); + BTFMCODEC_INFO("CODEC address while registering mixer ctrl:%p", codec); + } } +end: // ToDo to add mixer control. return 0; } @@ -30,13 +123,14 @@ static int btfmcodec_codec_probe(struct snd_soc_component *codec) static void btfmcodec_codec_remove(struct snd_soc_component *codec) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); - struct btfmcodec_state_machine states = btfmcodec->states; + struct btfmcodec_state_machine *state = &btfmcodec->states; struct hwep_data *hwep_info = btfmcodec->hwep_info; BTFMCODEC_DBG(""); // ToDo: check whether remove has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); + if (btfmcodec_get_current_transport(state)!= IDLE) { + BTFMCODEC_WARN("Received probe when state is :%s", + coverttostring(btfmcodec_get_current_transport(state))); } else if (hwep_info->drv && hwep_info->drv->hwep_remove) { hwep_info->drv->hwep_remove(codec); } @@ -46,13 +140,14 @@ static int btfmcodec_codec_write(struct snd_soc_component *codec, unsigned int reg, unsigned int value) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); - struct btfmcodec_state_machine states = btfmcodec->states; + struct btfmcodec_state_machine *state = &btfmcodec->states; struct hwep_data *hwep_info = btfmcodec->hwep_info; BTFMCODEC_DBG(""); - // ToDo: check whether remove has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); + // ToDo: check whether write has to allowed when state if different + if (btfmcodec_get_current_transport(state)!= IDLE) { + BTFMCODEC_WARN("Received probe when state is :%s", + coverttostring(btfmcodec_get_current_transport(state))); } else if (hwep_info->drv && hwep_info->drv->hwep_remove) { return hwep_info->drv->hwep_write(codec, reg, value); } @@ -64,13 +159,14 @@ static unsigned int btfmcodec_codec_read(struct snd_soc_component *codec, unsigned int reg) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); - struct btfmcodec_state_machine states = btfmcodec->states; + struct btfmcodec_state_machine *state = &btfmcodec->states; struct hwep_data *hwep_info = btfmcodec->hwep_info; BTFMCODEC_DBG(""); - // ToDo: check whether remove has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); + // ToDo: check whether read has to allowed when state if different + if (btfmcodec_get_current_transport(state)!= IDLE) { + BTFMCODEC_WARN("Received probe when state is :%s", + coverttostring(btfmcodec_get_current_transport(state))); } else if (hwep_info->drv && hwep_info->drv->hwep_read) { return hwep_info->drv->hwep_read(codec, reg); } @@ -92,43 +188,178 @@ static inline void * btfmcodec_get_dai_drvdata(struct hwep_data *hwep_info) return hwep_info->dai_drv; } -static int btfmcodec_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +int btfmcodec_hwep_startup(struct btfmcodec_data *btfmcodec) { - struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); - struct btfmcodec_state_machine states = btfmcodec->states; - struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); - BTFMCODEC_DBG("substream = %s stream = %d dai->name = %s", - substream->name, substream->stream, dai->name); - - // ToDo: check whether statup has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); - } else if (dai && dai_drv->dai_ops && dai_drv->dai_ops->hwep_startup) { + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_startup) { return dai_drv->dai_ops->hwep_startup((void *)btfmcodec->hwep_info); } else { return -1; } +} + +static int btfmcodec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); + struct btfmcodec_state_machine *state = &btfmcodec->states; + + BTFMCODEC_DBG("substream = %s stream = %d dai->name = %s", + substream->name, substream->stream, dai->name); + + if (btfmcodec_get_current_transport(state) != IDLE && + btfmcodec_get_current_transport(state) != BT_Connected) { + BTFMCODEC_DBG("Not allowing as state is:%s", + coverttostring(btfmcodec_get_current_transport(state))); + } else { + return btfmcodec_hwep_startup(btfmcodec); + } return 0; } +int btfmcodec_hwep_shutdown(struct btfmcodec_data *btfmcodec, int id) +{ + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + struct btfmcodec_state_machine *state = &btfmcodec->states; + struct btm_master_shutdown_req shutdown_req; + wait_queue_head_t *rsp_wait_q = + &btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP]; + uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP]; + int ret = 0; + + /* for master configurations failure cases, we don't need to send + * shutdown request + */ + if (btfmcodec_get_current_transport(state) == BT_Connected) { + BTFMCODEC_DBG("sending master shutdown request.."); + shutdown_req.opcode = BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ; + shutdown_req.len = BTM_MASTER_SHUTDOWN_REQ_LEN; + shutdown_req.stream_id = id; + /* See if we need to protect below with lock */ + *status = BTM_WAITING_RSP; + btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &shutdown_req, (shutdown_req.len + + BTM_HEADER_LEN)); + + ret = wait_event_interruptible_timeout(*rsp_wait_q, + (*status) != BTM_WAITING_RSP, + msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT)); + if (ret == 0) { + BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager"); + } else { + if (*status == BTM_RSP_RECV) + ret = 0; + else if (*status == BTM_FAIL_RESP_RECV) + ret = -1; + } + } else { + BTFMCODEC_WARN("Not sending master shutdown request as state is:%s", + coverttostring(btfmcodec_get_current_transport(state))); + } + + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_shutdown) { + dai_drv->dai_ops->hwep_shutdown((void *)btfmcodec->hwep_info, id); + } + + return ret; +} + +void btfmcodec_wq_hwep_shutdown(struct work_struct *work) +{ + struct btfmcodec_char_device *btfmcodec_dev = container_of(work, + struct btfmcodec_char_device, + wq_hwep_shutdown); + struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec; + struct list_head *head = &btfmcodec->config_head; + struct hwep_configurations *hwep_configs = NULL; + int ret = -1; + int idx = BTM_PKT_TYPE_HWEP_SHUTDOWN; + + BTFMCODEC_INFO(" starting shutdown"); + /* Just check if first Rx has to be closed first or + * any order should be ok. + */ + list_for_each_entry(hwep_configs, head, dai_list) { + BTFMCODEC_INFO("shuting down dai id:%d", hwep_configs->stream_id); + ret = btfmcodec_hwep_shutdown(btfmcodec, hwep_configs->stream_id); + if (ret < 0) { + BTFMCODEC_ERR("failed to shutdown master with id", hwep_configs->stream_id); + break; + } + } + + if (ret < 0) + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + else + btfmcodec_dev->status[idx] = BTM_RSP_RECV; + + wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); +} + +static int btfmcodec_delete_configs(struct btfmcodec_data *btfmcodec, uint8_t id) +{ + struct list_head *head = &btfmcodec->config_head; + struct hwep_configurations *hwep_configs; + int ret = -1; + + list_for_each_entry(hwep_configs, head, dai_list) { + if (hwep_configs->stream_id == id) { + BTFMCODEC_INFO("deleting configs with id %d", id); + list_del(&hwep_configs->dai_list); + ret = 1; + break; + } + } + + return ret; +} + static void btfmcodec_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); - struct btfmcodec_state_machine states = btfmcodec->states; - struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); + struct btfmcodec_state_machine *state = &btfmcodec->states; BTFMCODEC_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, dai->id, dai->rate); - // ToDo: check whether statup has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); - } else if (dai && dai_drv->dai_ops && dai_drv->dai_ops->hwep_shutdown) { - dai_drv->dai_ops->hwep_shutdown((void *)btfmcodec->hwep_info, dai->id); + if (btfmcodec_get_current_transport(state) != IDLE && + btfmcodec_get_current_transport(state) != BT_Connected) { + BTFMCODEC_WARN("not allowing shutdown as state is:%s", + coverttostring(btfmcodec_get_current_transport(state))); + /* Delete stored configs */ + btfmcodec_delete_configs(btfmcodec, dai->id); + } else { + /* first master shutdown has to done */ + btfmcodec_hwep_shutdown(btfmcodec, dai->id); + btfmcodec_delete_configs(btfmcodec, dai->id); + if (!btfmcodec_is_valid_cache_avb(btfmcodec)) + btfmcodec_set_current_state(state, IDLE); + else { + BTFMCODEC_WARN("valid stream id is available not updating state\n"); + btfmcodec_set_current_state(state, BT_Connected); + } + } +} + +int btfmcodec_hwep_hw_params (struct btfmcodec_data *btfmcodec, uint32_t bps, + uint32_t direction) +{ + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_hw_params) { + return dai_drv->dai_ops->hwep_hw_params((void *)btfmcodec->hwep_info, + bps, direction); + } else { + return -1; } } @@ -137,55 +368,214 @@ static int btfmcodec_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); - struct btfmcodec_state_machine states = btfmcodec->states; - struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); + struct btfmcodec_state_machine *state = &btfmcodec->states; uint32_t bps = params_width(params); uint32_t direction = substream->stream; BTFMCODEC_DBG("dai->name = %s DAI-ID %x rate %d bps %d num_ch %d", dai->name, dai->id, params_rate(params), params_width(params), params_channels(params)); - // ToDo: check whether hw_params has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); - } else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_hw_params) { - return dai_drv->dai_ops->hwep_hw_params((void *)btfmcodec->hwep_info, bps, direction); + + if (btfmcodec_get_current_transport(state) != IDLE && + btfmcodec_get_current_transport(state) != BT_Connected) { + BTFMCODEC_WARN("caching bps as state is :%s", + coverttostring(btfmcodec_get_current_transport(state))); + bits_per_second = bps; + } else { + return btfmcodec_hwep_hw_params(btfmcodec, bps, direction); + } + + return 0; +} + +bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *btfmcodec) +{ + struct list_head *head = &btfmcodec->config_head; + struct hwep_configurations *hwep_configs; + bool cache_avb = false; + + list_for_each_entry(hwep_configs, head, dai_list) { + cache_avb = true; + break; + } + + return cache_avb; +} + +static int btfmcodec_check_and_cache_configs(struct btfmcodec_data *btfmcodec, + uint32_t sampling_rate, uint32_t direction, + int id, uint8_t codectype) +{ + struct list_head *head = &btfmcodec->config_head; + struct hwep_configurations *hwep_configs; + + list_for_each_entry(hwep_configs, head, dai_list) { + if (hwep_configs->stream_id == id) { + BTFMCODEC_WARN("previous entry for %d is already available", + id); + list_del(&hwep_configs->dai_list); + } + } + + hwep_configs = kzalloc(sizeof(struct hwep_configurations), + GFP_KERNEL); + if (!hwep_configs) { + BTFMCODEC_ERR("failed to allocate memory"); + return -ENOMEM; + } + + hwep_configs->stream_id = id; /* Stream identifier */ + hwep_configs->sample_rate = sampling_rate; + hwep_configs->bit_width = bits_per_second; + hwep_configs->codectype = codectype; + hwep_configs->direction = direction; + + list_add(&hwep_configs->dai_list, head); + BTFMCODEC_INFO("added dai id:%d to list with sampling_rate :%u, direction:%u", id, sampling_rate, direction); + return 1; +} + +static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t id) +{ + struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev; + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct master_hwep_configurations hwep_configs; + struct btm_master_config_req config_reg; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + wait_queue_head_t *rsp_wait_q = + &btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_MASTER_CONFIG_RSP]; + uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_MASTER_CONFIG_RSP]; + int ret = 0; + + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_configs) { + dai_drv->dai_ops->hwep_get_configs((void *)btfmcodec->hwep_info, + &hwep_configs, id); + } else { + BTFMCODEC_ERR("No hwep_get_configs is set by hw ep driver"); + return -1; + } + + BTFMCODEC_INFO("framing packet for %d", id); + config_reg.opcode = BTM_BTFMCODEC_MASTER_CONFIG_REQ; + config_reg.len = BTM_MASTER_CONFIG_REQ_LEN; + config_reg.stream_id = hwep_configs.stream_id; + config_reg.device_id = hwep_configs.device_id; + config_reg.sample_rate = hwep_configs.sample_rate; + config_reg.bit_width = hwep_configs.bit_width; + config_reg.num_channels = hwep_configs.num_channels; + config_reg.channel_num = hwep_configs.chan_num; + config_reg.codec_id = hwep_configs.codectype; + BTFMCODEC_DBG("================================================\n"); + BTFMCODEC_DBG("config_reg.len :%d", config_reg.len); + BTFMCODEC_DBG("config_reg.stream_id :%d", config_reg.stream_id); + BTFMCODEC_DBG("config_reg.device_id :%d", config_reg.device_id); + BTFMCODEC_DBG("config_reg.sample_rate :%d", config_reg.sample_rate); + BTFMCODEC_DBG("config_reg.bit_width :%d", config_reg.bit_width); + BTFMCODEC_DBG("config_reg.num_channels :%d", config_reg.num_channels); + BTFMCODEC_DBG("config_reg.channel_num :%d", config_reg.channel_num); + BTFMCODEC_DBG("config_reg.codec_id :%d", config_reg.codec_id); + BTFMCODEC_DBG("================================================\n"); + /* See if we need to protect below with lock */ + *status = BTM_WAITING_RSP; + btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &config_reg, (config_reg.len + + BTM_HEADER_LEN)); + ret = wait_event_interruptible_timeout(*rsp_wait_q, + (*status) != BTM_WAITING_RSP, + msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT)); + if (ret == 0) { + BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager"); + ret = -ETIMEDOUT; + } else { + if (*status == BTM_RSP_RECV) + return 0; + else if (*status == BTM_FAIL_RESP_RECV) + return -1; + } + + return ret; +} + +int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_rate, + uint32_t direction, int id) +{ + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + struct btfmcodec_state_machine *state = &btfmcodec->states; + + int ret; + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) { + ret = dai_drv->dai_ops->hwep_prepare((void *)hwep_info, sampling_rate, + direction, id); + if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags)) { + ret = btfmcodec_configure_master(btfmcodec, (uint8_t)id); + if (ret < 0) { + BTFMCODEC_ERR("failed to configure master error %d", ret); + /* close slave port and reset the state*/ + btfmcodec_set_current_state(state, IDLE); + /* we don't need to do shutdown, ASOC is doing it */ +// btfmcodec_hwep_shutdown(btfmcodec, id); + } else { + btfmcodec_set_current_state(state, BT_Connected); + } + } } else { return -1; } - return 0; + return ret; } static int btfmcodec_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); - struct btfmcodec_state_machine states = btfmcodec->states; - struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); + struct btfmcodec_state_machine *state = &btfmcodec->states; + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + uint8_t *codectype = dai_drv->dai_ops->hwep_codectype; uint32_t sampling_rate = dai->rate; uint32_t direction = substream->stream; int id = dai->id; int ret; + BTFMCODEC_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d", + dai->name, id, sampling_rate, direction); - BTFMCODEC_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d", dai->name, - id, sampling_rate, direction); - // ToDo: check whether hw_params has to allowed when state if different - if (states.current_state != IDLE) { - BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); - } else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) { - ret = dai_drv->dai_ops->hwep_prepare((void *)btfmcodec->hwep_info, sampling_rate, - direction, id); - if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &btfmcodec->hwep_info->flags)) { - BTFMCODEC_DBG("configuring master now"); + if (btfmcodec_get_current_transport(state) != IDLE && + btfmcodec_get_current_transport(state) != BT_Connected) { + BTFMCODEC_WARN("caching required info as state is:%s", + coverttostring(btfmcodec_get_current_transport(state))); + ret = btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate, direction, + id, *codectype); + } else { + ret = btfmcodec_hwep_prepare(btfmcodec, sampling_rate, direction, id); + if (ret >= 0) { + btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate, direction, + id, *codectype); } + } + + return ret; +} + +int btfmcodec_hwep_set_channel_map(void *hwep_info, unsigned int tx_num, + unsigned int *tx_slot, unsigned int rx_num, + unsigned int *rx_slot) +{ + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_set_channel_map) { + return dai_drv->dai_ops->hwep_set_channel_map(hwep_info, tx_num, + tx_slot, rx_num, + rx_slot); } else { return -1; } - return 0; - return 0; } static int btfmcodec_dai_set_channel_map(struct snd_soc_dai *dai, @@ -194,44 +584,95 @@ static int btfmcodec_dai_set_channel_map(struct snd_soc_dai *dai, { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct btfmcodec_state_machine states = btfmcodec->states; - struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); BTFMCODEC_DBG(""); // ToDo: check whether hw_params has to allowed when state if different if (states.current_state != IDLE) { BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); - } else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_set_channel_map) { - return dai_drv->dai_ops->hwep_set_channel_map((void *)btfmcodec->hwep_info, tx_num, - tx_slot, rx_num, rx_slot); } else { - return -1; + return btfmcodec_hwep_set_channel_map((void *)btfmcodec->hwep_info, tx_num, + tx_slot, rx_num, rx_slot); } return 0; } + +int btfmcodec_hwep_get_channel_map(void *hwep_info, unsigned int *tx_num, + unsigned int *tx_slot, unsigned int *rx_num, + unsigned int *rx_slot, int id) +{ + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_channel_map) { + return dai_drv->dai_ops->hwep_get_channel_map(hwep_info, tx_num, + tx_slot, rx_num, + rx_slot, id); + } else { + return -1; + } +} + static int btfmcodec_dai_get_channel_map(struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); - struct btfmcodec_state_machine states = btfmcodec->states; - struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); +// struct btfmcodec_state_machine states = btfmcodec->states; BTFMCODEC_DBG(""); - // ToDo: check whether hw_params has to allowed when state if different - if (states.current_state != IDLE) { + // ToDo: get_channel_map is not needed for new driver +/* if (states.current_state != IDLE) { BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); - } else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_channel_map) { - return dai_drv->dai_ops->hwep_get_channel_map((void *)btfmcodec->hwep_info, tx_num, - tx_slot, rx_num, rx_slot, dai->id); } else { - return -1; - } +*/ return btfmcodec_hwep_get_channel_map((void *)btfmcodec->hwep_info, + tx_num, tx_slot, rx_num, + rx_slot, dai->id); +// } return 0; } +void btfmcodec_wq_hwep_configure(struct work_struct *work) +{ + struct btfmcodec_char_device *btfmcodec_dev = container_of(work, + struct btfmcodec_char_device, + wq_hwep_configure); + struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec; + struct list_head *head = &btfmcodec->config_head; + struct hwep_configurations *hwep_configs = NULL; + int ret; + int idx = BTM_PKT_TYPE_HWEP_CONFIG; + uint32_t sample_rate, direction; + uint8_t id, bit_width, codectype; + + list_for_each_entry(hwep_configs, head, dai_list) { + id = hwep_configs->stream_id; + sample_rate = hwep_configs->sample_rate; + bit_width = hwep_configs->bit_width; + codectype = hwep_configs->codectype; + direction = hwep_configs->direction; + + BTFMCODEC_INFO("configuring dai id:%d with sampling rate:%d bit_width:%d", id, sample_rate, bit_width); + ret = btfmcodec_hwep_startup(btfmcodec); + if (ret >= 0) + ret = btfmcodec_hwep_hw_params(btfmcodec, bit_width, direction); + if (ret >= 0) + ret = btfmcodec_hwep_prepare(btfmcodec, sample_rate, direction, id); + if (ret < 0) { + BTFMCODEC_ERR("failed to configure hwep", hwep_configs->stream_id); + break; + } + } + + if (ret < 0) + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + else + btfmcodec_dev->status[idx] = BTM_RSP_RECV; + + wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); +} static struct snd_soc_dai_ops btfmcodec_dai_ops = { .startup = btfmcodec_dai_startup, .shutdown = btfmcodec_dai_shutdown, @@ -244,11 +685,13 @@ static struct snd_soc_dai_ops btfmcodec_dai_ops = { int btfm_register_codec(struct hwep_data *hwep_info) { struct btfmcodec_data *btfmcodec; + struct btfmcodec_char_device *btfmcodec_dev; struct device *dev; struct hwep_dai_driver *dai_drv; int i, ret; btfmcodec = btfm_get_btfmcodec(); + btfmcodec_dev = btfmcodec->btfmcodec_dev; dev = &btfmcodec->dev; btfmcodec_dai_info = kzalloc((sizeof(struct snd_soc_dai_driver) * hwep_info->num_dai), GFP_KERNEL); if (!btfmcodec_dai_info) { @@ -269,6 +712,19 @@ int btfm_register_codec(struct hwep_data *hwep_info) BTFMCODEC_INFO("slim bus driver name:%s", dev->driver->name); ret = snd_soc_register_component(dev, &btfmcodec_codec_component, btfmcodec_dai_info, hwep_info->num_dai); + BTFMCODEC_INFO("Dev node address: %p", dev); + BTFMCODEC_INFO("btfmcodec address :%p, btfmcodec"); + BTFMCODEC_INFO("HWEPINFO address:%p", hwep_info); + BTFMCODEC_INFO("btfmcodec_dev INFO address:%p", btfmcodec->btfmcodec_dev); + BTFMCODEC_INFO("before wq_hwep_shutdown:%p", btfmcodec_dev->wq_hwep_shutdown); + BTFMCODEC_INFO("before wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer); + INIT_WORK(&btfmcodec_dev->wq_hwep_shutdown, btfmcodec_wq_hwep_shutdown); + INIT_WORK(&btfmcodec_dev->wq_prepare_bearer, btfmcodec_wq_prepare_bearer); + INIT_WORK(&btfmcodec_dev->wq_hwep_configure, btfmcodec_wq_hwep_configure); + BTFMCODEC_INFO("after wq_hwep_shutdown:%p", btfmcodec_dev->wq_hwep_shutdown); + BTFMCODEC_INFO("after wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer); + BTFMCODEC_INFO("btfmcodec_wq_prepare_bearer:%p", btfmcodec_wq_prepare_bearer); + BTFMCODEC_INFO("btfmcodec_wq_hwep_shutdown:%p", btfmcodec_wq_hwep_shutdown); return ret; } diff --git a/btfmcodec/include/btfm_codec.h b/btfmcodec/include/btfm_codec.h index 46d4d86287..4ed3ecea44 100644 --- a/btfmcodec/include/btfm_codec.h +++ b/btfmcodec/include/btfm_codec.h @@ -20,24 +20,36 @@ #define DEVICE_NAME_MAX_LEN 64 -enum btfmcodec_states { - /*Default state of btfm codec driver */ +typedef enum btfmcodec_states { + /*Default state of kernel proxy driver */ IDLE = 0, - /* When BT is active transport */ - BT_Connected = 1, /* Waiting for BT bearer indication after configuring HW ports */ - BT_Connecting = 2, - /* When BTADV_AUDIO is active transport */ - BTADV_AUDIO_Connected = 3, - /* Waiting for BTADV_AUDIO bearer switch indications */ - BTADV_AUDIO_Connecting = 4 + BT_Connecting = 1, + /* When BT is active transport */ + BT_Connected = 2, + /* Waiting for BTADV Audio bearer switch indications */ + BTADV_AUDIO_Connecting = 3, + /* When BTADV audio is active transport */ + BTADV_AUDIO_Connected = 4 +} btfmcodec_state; + +enum btfm_pkt_type { + BTM_PKT_TYPE_PREPARE_REQ = 0, + BTM_PKT_TYPE_MASTER_CONFIG_RSP, + BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP, + BTM_PKT_TYPE_BEARER_SWITCH_IND, + BTM_PKT_TYPE_HWEP_SHUTDOWN, + BTM_PKT_TYPE_HWEP_CONFIG, + BTM_PKT_TYPE_MAX, }; + char *coverttostring(enum btfmcodec_states); struct btfmcodec_state_machine { - enum btfmcodec_states prev_state; - enum btfmcodec_states current_state; - enum btfmcodec_states next_state; + struct mutex state_machine_lock; + btfmcodec_state prev_state; + btfmcodec_state current_state; + btfmcodec_state next_state; }; struct btfmcodec_char_device { @@ -46,11 +58,17 @@ struct btfmcodec_char_device { struct mutex lock; int reuse_minor; char dev_name[DEVICE_NAME_MAX_LEN]; + struct workqueue_struct *workqueue; struct sk_buff_head rxq; struct work_struct rx_work; + struct work_struct wq_hwep_shutdown; + struct work_struct wq_prepare_bearer; + struct work_struct wq_hwep_configure; wait_queue_head_t readq; spinlock_t tx_queue_lock; struct sk_buff_head txq; + wait_queue_head_t rsp_wait_q[BTM_PKT_TYPE_MAX]; + uint8_t status[BTM_PKT_TYPE_MAX]; void *btfmcodec; }; @@ -59,8 +77,8 @@ struct btfmcodec_data { struct btfmcodec_state_machine states; struct btfmcodec_char_device *btfmcodec_dev; struct hwep_data *hwep_info; + struct list_head config_head; }; -int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *, uint8_t *, int); struct btfmcodec_data *btfm_get_btfmcodec(void); #endif /*__LINUX_BTFM_CODEC_H */ diff --git a/btfmcodec/include/btfm_codec_btadv_interface.h b/btfmcodec/include/btfm_codec_btadv_interface.h new file mode 100644 index 0000000000..b27d2a0503 --- /dev/null +++ b/btfmcodec/include/btfm_codec_btadv_interface.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __LINUX_BTFM_CODEC_BTADV_INTERFACE_H +#define __LINUX_BTFM_CODEC_BTADV_INTERFACE_H + +enum transport_type { + BT = 1, + BTADV, +}; + +static char *transport_type_text[] = {"BT", "BTADV"}; + +void btfmcodec_set_current_state(struct btfmcodec_state_machine *, btfmcodec_state); +void btfmcodec_wq_prepare_bearer(struct work_struct *); +void btfmcodec_wq_hwep_shutdown(struct work_struct *); +void btfmcodec_initiate_hwep_shutdown(struct btfmcodec_char_device *btfmcodec_dev); +btfmcodec_state btfmcodec_get_current_transport(struct btfmcodec_state_machine *state); +#endif /* __LINUX_BTFM_CODEC_BTADV_INTERFACE_H */ diff --git a/btfmcodec/include/btfm_codec_hw_interface.h b/btfmcodec/include/btfm_codec_hw_interface.h index 116b415da9..60163b95ab 100644 --- a/btfmcodec/include/btfm_codec_hw_interface.h +++ b/btfmcodec/include/btfm_codec_hw_interface.h @@ -20,11 +20,32 @@ #define BTADV_AUDIO_MASTER_CONFIG 0 #define DEVICE_NAME_MAX_LEN 64 +struct hwep_configurations { + void *btfmcodec; + uint8_t stream_id; + uint32_t sample_rate; + uint8_t bit_width; + uint8_t codectype; + uint32_t direction; + struct list_head dai_list; +}; + +struct master_hwep_configurations { + uint8_t stream_id; + uint32_t device_id; + uint32_t sample_rate; + uint8_t bit_width; + uint8_t num_channels; + uint8_t chan_num; + uint8_t codectype; + uint16_t direction; +}; struct hwep_comp_drv { - int (*hwep_probe) (struct snd_soc_component *component); - void (*hwep_remove) (struct snd_soc_component *component); - unsigned int (*hwep_read)(struct snd_soc_component *component, unsigned int reg); - int (*hwep_write)(struct snd_soc_component *componentm, unsigned int reg, unsigned int value); + int (*hwep_probe) (struct snd_soc_component *); + void (*hwep_remove) (struct snd_soc_component *); + unsigned int (*hwep_read)(struct snd_soc_component *, unsigned int ); + int (*hwep_write)(struct snd_soc_component *, unsigned int, + unsigned int); }; struct hwep_dai_ops { @@ -36,6 +57,9 @@ struct hwep_dai_ops { unsigned int, unsigned int *); int (*hwep_get_channel_map)(void *, unsigned int *, unsigned int *, unsigned int *, unsigned int *, int); + int (*hwep_get_configs)(void *, struct master_hwep_configurations *, + uint8_t); + uint8_t *hwep_codectype; }; struct hwep_dai_driver { @@ -51,7 +75,9 @@ struct hwep_data { char driver_name [DEVICE_NAME_MAX_LEN]; struct hwep_comp_drv *drv; struct hwep_dai_driver *dai_drv; + struct snd_kcontrol_new *mixer_ctrl; int num_dai; + int num_mixer_ctrl; unsigned long flags; }; diff --git a/btfmcodec/include/btfm_codec_pkt.h b/btfmcodec/include/btfm_codec_pkt.h index 33432e22c7..3084b88de5 100644 --- a/btfmcodec/include/btfm_codec_pkt.h +++ b/btfmcodec/include/btfm_codec_pkt.h @@ -6,44 +6,89 @@ #ifndef __LINUX_BTFM_CODEC_PKT_H #define __LINUX_BTFM_CODEC_PKT_H -typedef uint16_t btm_opcode; +typedef uint32_t btm_opcode; struct btm_req { btm_opcode opcode; - uint8_t len; + uint32_t len; uint8_t *data; -}; +}__attribute__((packed)); struct btm_rsp { btm_opcode opcode; uint8_t status; -}; +}__attribute__((packed)); struct btm_ind { btm_opcode opcode; - uint8_t len; + uint32_t len; uint8_t *data; +}__attribute__((packed)); + +struct btm_ctrl_pkt { + btm_opcode opcode; + uint32_t len; + uint8_t active_transport; + uint8_t status; +}__attribute__((packed)); + +#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ 0x50000000 +#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP 0x50000001 +#define BTM_BTFMCODEC_MASTER_CONFIG_REQ 0x50000002 +#define BTM_BTFMCODEC_MASTER_CONFIG_RSP 0x50000003 +#define BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ 0x50000004 +#define BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP 0x50000005 +#define BTM_BTFMCODEC_BEARER_SWITCH_IND 0x58000001 +#define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND 0x58000002 + +#define BTM_MASTER_CONFIG_REQ_LEN 13 +#define BTM_MASTER_CONFIG_RSP_TIMEOUT 1000 +#define BTM_HEADER_LEN 8 +#define BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN 2 +#define BTM_MASTER_CONFIG_RSP_LEN 2 +#define BTM_MASTER_SHUTDOWN_REQ_LEN 1 +#define BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN 1 +#define BTM_BEARER_SWITCH_IND_LEN 1 + +enum rx_status { + /* Waiting for response */ + BTM_WAITING_RSP, + /* Response recevied */ + BTM_RSP_RECV, + /* Response recevied with failure status*/ + BTM_FAIL_RESP_RECV, +}; +enum btfm_kp_status { + /* KP processed message succesfully */ + MSG_SUCCESS = 0, + /* Error while processing the message */ + MSG_FAILED, + /* Wrong transport type selected by BTADV audio manager */ + MSG_WRONG_TRANSPORT_TYPE, + /* Timeout triggered to receive bearer switch indications*/ + MSG_INTERNAL_TIMEOUT, + MSG_FAILED_TO_CONFIGURE_HWEP, + MSG_FAILED_TO_SHUTDOWN_HWEP, + MSG_ERR_WHILE_SHUTING_DOWN_HWEP, }; - -#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ 0x5000 -#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP 0x5001 -#define BTM_BTFMCODEC_MASTER_CONFIG_REQ 0x5002 -#define BTM_BTFMCODEC_MASTER_CONFIG_RSP 0x5003 -#define BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ 0x5004 -#define BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP 0x5005 -#define BTM_BTFMCODEC_BEARER_SWITCH_IND 0x50C8 -#define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND 0x50C9 - struct btm_master_config_req { btm_opcode opcode; - uint8_t len; - uint8_t stream_identifier; - uint32_t device_identifier; - uint32_t sampling_rate; + uint32_t len; + uint8_t stream_id; + uint32_t device_id; + uint32_t sample_rate; uint8_t bit_width; - uint8_t num_of_channels; + uint8_t num_channels; uint8_t channel_num; uint8_t codec_id; -}; +}__attribute__((packed)); + +struct btm_master_shutdown_req { + btm_opcode opcode; + uint32_t len; + uint8_t stream_id; +}__attribute__((packed)); +int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *, void *, int); +bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *); #endif /* __LINUX_BTFM_CODEC_PKT_H*/ diff --git a/slimbus/Makefile b/slimbus/Makefile index 18aa0a3f45..ae35b58e12 100644 --- a/slimbus/Makefile +++ b/slimbus/Makefile @@ -5,4 +5,4 @@ bt_fm_slim-objs := btfm_slim.o btfm_slim_codec.o btfm_slim_slave.o obj-$(CONFIG_BTFM_SLIM) += bt_fm_slim.o # Below src is for BTFM Driver support based on btfm codec btfm_slim_codec-objs := btfm_slim.o btfm_slim_hw_interface.o btfm_slim_slave.o -obj-$(CONFIG_SLIM_BTFM_CODEC) += btfm_slim_codec.o \ No newline at end of file +obj-$(CONFIG_SLIM_BTFM_CODEC) += btfm_slim_codec.o diff --git a/slimbus/btfm_slim.c b/slimbus/btfm_slim.c index 2178ca7a7e..dbab9dff2b 100644 --- a/slimbus/btfm_slim.c +++ b/slimbus/btfm_slim.c @@ -515,6 +515,67 @@ int btfm_slim_hw_deinit(struct btfmslim *btfmslim) return ret; } +#if IS_ENABLED (CONFIG_BTFM_SLIM) +void btfm_slim_get_hwep_details(struct slim_device *dev, struct btfmslim *btfm_slim) +{ +} +#else +void btfm_slim_get_hwep_details(struct slim_device *slim, struct btfmslim *btfm_slim) +{ + struct device_node *np = slim->dev.of_node; + const __be32 *prop; + struct btfmslim_ch *rx_chs = btfm_slim->rx_chs; + struct btfmslim_ch *tx_chs = btfm_slim->tx_chs; + int len; + + prop = of_get_property(np, "qcom,btslim-address", &len); + if (prop) { + btfm_slim->device_id = be32_to_cpup(&prop[0]); + BTFMSLIM_DBG("hwep slim address define in dt %08x", btfm_slim->device_id); + } else { + BTFMSLIM_ERR("btslim-address is not defined in dt using default address"); + btfm_slim->device_id = 0; + } + + if (!rx_chs || !tx_chs) { + BTFMSLIM_ERR("either rx/tx channels are configured to null"); + return; + } + + prop = of_get_property(np, "qcom,btslimrx-channels", &len); + if (prop) { + /* Check if we need any protection for index */ + rx_chs[0].ch = (uint8_t)be32_to_cpup(&prop[0]); + rx_chs[1].ch = (uint8_t)be32_to_cpup(&prop[1]); + BTFMSLIM_DBG("Rx: id\tname\tport\tch"); + BTFMSLIM_DBG(" %d\t%s\t%d\t%d", rx_chs[0].id, + rx_chs[0].name, rx_chs[0].port, + rx_chs[0].ch); + BTFMSLIM_DBG(" %d\t%s\t%d\t%d", rx_chs[1].id, + rx_chs[1].name, rx_chs[1].port, + rx_chs[1].ch); + } else { + BTFMSLIM_ERR("btslimrx channels are missing in dt using default values"); + } + + prop = of_get_property(np, "qcom,btslimtx-channels", &len); + if (prop) { + /* Check if we need any protection for index */ + tx_chs[0].ch = (uint8_t)be32_to_cpup(&prop[0]); + tx_chs[1].ch = (uint8_t)be32_to_cpup(&prop[1]); + BTFMSLIM_DBG("Tx: id\tname\tport\tch"); + BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs[0].id, + tx_chs[0].name, tx_chs[0].port, + tx_chs[0].ch); + BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs[1].id, + tx_chs[1].name, tx_chs[1].port, + tx_chs[1].ch); + } else { + BTFMSLIM_ERR("btslimtx channels are missing in dt using default values"); + } + +} +#endif static int btfm_slim_status(struct slim_device *sdev, enum slim_device_status status) { @@ -526,6 +587,7 @@ static int btfm_slim_status(struct slim_device *sdev, #if IS_ENABLED(CONFIG_BTFM_SLIM) ret = btfm_slim_register_codec(btfm_slim); #else + btfm_slim_get_hwep_details(sdev, btfm_slim); ret = btfm_slim_register_hw_ep(btfm_slim); #endif if (ret) diff --git a/slimbus/btfm_slim.h b/slimbus/btfm_slim.h index d3e0377dca..393b0ab82a 100644 --- a/slimbus/btfm_slim.h +++ b/slimbus/btfm_slim.h @@ -73,6 +73,9 @@ struct btfmslim { int (*vendor_init)(struct btfmslim *btfmslim); int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num, uint8_t rxport, uint8_t enable); +#if IS_ENABLED(CONFIG_SLIM_BTFM_CODEC) + int device_id; +#endif }; extern int btfm_feedback_ch_setting; diff --git a/slimbus/btfm_slim_hw_interface.c b/slimbus/btfm_slim_hw_interface.c index 67ce3ba41d..bcc7477a56 100644 --- a/slimbus/btfm_slim_hw_interface.c +++ b/slimbus/btfm_slim_hw_interface.c @@ -26,15 +26,16 @@ static int bt_soc_enable_status; int btfm_feedback_ch_setting; +static uint8_t usecase_codec; -static int btfm_slim_codec_write(struct snd_soc_component *codec, +static int btfm_slim_hwep_write(struct snd_soc_component *codec, unsigned int reg, unsigned int value) { BTFMSLIM_DBG(""); return 0; } -static unsigned int btfm_slim_codec_read(struct snd_soc_component *codec, +static unsigned int btfm_slim_hwep_read(struct snd_soc_component *codec, unsigned int reg) { BTFMSLIM_DBG(""); @@ -72,25 +73,39 @@ static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new status_controls[] = { +static int btfm_get_codec_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + BTFMSLIM_DBG("current codec type:%s", codec_text[usecase_codec]); + ucontrol->value.integer.value[0] = usecase_codec; + return 1; +} + +static int btfm_put_codec_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usecase_codec = ucontrol->value.integer.value[0]; + BTFMSLIM_DBG("codec type set to:%s", codec_text[usecase_codec]); + return 1; +} +static struct snd_kcontrol_new status_controls[] = { SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0, - btfm_soc_status_get, - btfm_soc_status_put), + btfm_soc_status_get, btfm_soc_status_put), SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0, btfm_get_feedback_ch_setting, - btfm_put_feedback_ch_setting) + btfm_put_feedback_ch_setting), + SOC_ENUM_EXT("BT codec type", codec_display, + btfm_get_codec_type, btfm_put_codec_type), }; -static int btfm_slim_codec_probe(struct snd_soc_component *codec) +static int btfm_slim_hwep_probe(struct snd_soc_component *codec) { BTFMSLIM_DBG(""); - snd_soc_add_component_controls(codec, status_controls, - ARRAY_SIZE(status_controls)); return 0; } -static void btfm_slim_codec_remove(struct snd_soc_component *codec) +static void btfm_slim_hwep_remove(struct snd_soc_component *codec) { BTFMSLIM_DBG(""); } @@ -163,6 +178,30 @@ static int btfm_slim_dai_hw_params(void *dai, uint32_t bps, return 0; } +void btfm_get_sampling_rate(uint32_t *sampling_rate) +{ + uint8_t codec_types_avb = ARRAY_SIZE(codec_text); + if (usecase_codec > (codec_types_avb - 1)) { + BTFMSLIM_ERR("falling back to use default sampling_rate: %u", + *sampling_rate); + return; + } + + if (*sampling_rate == 44100 || *sampling_rate == 48000) { + if (usecase_codec == LDAC || + usecase_codec == APTX_AD) + *sampling_rate = (*sampling_rate) *2; + } + + if (usecase_codec == LC3_VOICE || + usecase_codec == APTX_AD_SPEECH || + usecase_codec == LC3 || usecase_codec == APTX_AD_QLEA) { + *sampling_rate = 96000; + } + + BTFMSLIM_INFO("current usecase codec type %s and sampling rate:%u khz", + codec_text[usecase_codec], *sampling_rate); +} static int btfm_slim_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id) { struct hwep_data *hwep_info = (struct hwep_data *)dai; @@ -175,6 +214,7 @@ static int btfm_slim_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t dir btfmslim->direction = direction; bt_soc_enable_status = 0; + btfm_get_sampling_rate(&sampling_rate); /* save sample rate */ btfmslim->sample_rate = sampling_rate; @@ -254,7 +294,7 @@ static int btfm_slim_dai_set_channel_map(void *dai, * get channel handler from slimbus driver */ rx_chs->ch = *(uint8_t *)(rx_slot + i); - BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id, + BTFMSLIM_DBG(" %d\t%s\t%d\t%x", rx_chs->id, rx_chs->name, rx_chs->port, rx_chs->ch); } @@ -265,7 +305,7 @@ static int btfm_slim_dai_set_channel_map(void *dai, * get channel handler from slimbus driver */ tx_chs->ch = *(uint8_t *)(tx_slot + i); - BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id, + BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs->id, tx_chs->name, tx_chs->port, tx_chs->ch); } @@ -352,6 +392,45 @@ static int btfm_slim_dai_get_channel_map(void *dai, return 0; } +int btfm_slim_dai_get_configs (void * dai, + struct master_hwep_configurations *hwep_config, + uint8_t id) +{ + struct hwep_data *hwep_info = (struct hwep_data *)dai; + struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev); + struct btfmslim_ch *ch = NULL; + int i = 0; + + BTFMSLIM_DBG(""); + hwep_config->stream_id = id; + hwep_config->device_id = btfmslim->device_id; + hwep_config->sample_rate = btfmslim->sample_rate; + hwep_config->bit_width = (uint8_t)btfmslim->bps; + hwep_config->codectype = usecase_codec; + hwep_config->direction = btfmslim->direction; + + switch (id) { + case BTFM_FM_SLIM_TX: + case BTFM_BT_SCO_SLIM_TX: + ch = btfmslim->tx_chs; + break; + case BTFM_BT_SCO_A2DP_SLIM_RX: + case BTFM_BT_SPLIT_A2DP_SLIM_RX: + ch = btfmslim->rx_chs; + break; + } + + for (; i < id ; i++) { + if (ch[i].id == id) { + BTFMSLIM_DBG("id matched"); + hwep_config->num_channels = 1; + hwep_config->chan_num = ch[i].ch; + break; + } + } + + return 1; +} static struct hwep_dai_ops btfmslim_hw_dai_ops = { .hwep_startup = btfm_slim_dai_startup, .hwep_shutdown = btfm_slim_dai_shutdown, @@ -359,6 +438,8 @@ static struct hwep_dai_ops btfmslim_hw_dai_ops = { .hwep_prepare = btfm_slim_dai_prepare, .hwep_set_channel_map = btfm_slim_dai_set_channel_map, .hwep_get_channel_map = btfm_slim_dai_get_channel_map, + .hwep_get_configs = btfm_slim_dai_get_configs, + .hwep_codectype = &usecase_codec, }; static struct hwep_dai_driver btfmslim_dai_driver[] = { @@ -403,10 +484,10 @@ static struct hwep_dai_driver btfmslim_dai_driver[] = { }; static struct hwep_comp_drv btfmslim_hw_driver = { - .hwep_probe = btfm_slim_codec_probe, - .hwep_remove = btfm_slim_codec_remove, - .hwep_read = btfm_slim_codec_read, - .hwep_write = btfm_slim_codec_write, + .hwep_probe = btfm_slim_hwep_probe, + .hwep_remove = btfm_slim_hwep_remove, + .hwep_read = btfm_slim_hwep_read, + .hwep_write = btfm_slim_hwep_write, }; int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim) @@ -431,6 +512,8 @@ int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim) hwep_info->dai_drv = btfmslim_dai_driver; hwep_info->num_dai = ARRAY_SIZE(btfmslim_dai_driver); hwep_info->num_dai = 2; + hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls); + hwep_info->mixer_ctrl = status_controls; set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags); /* Register to hardware endpoint */ ret = btfmcodec_register_hw_ep(hwep_info); diff --git a/slimbus/btfm_slim_hw_interface.h b/slimbus/btfm_slim_hw_interface.h index 38b480cf6b..f51d52b882 100644 --- a/slimbus/btfm_slim_hw_interface.h +++ b/slimbus/btfm_slim_hw_interface.h @@ -15,4 +15,28 @@ // Todo protect with flags int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim); void btfm_slim_unregister_hwep(void); + +typedef enum Codec { + SBC = 0, + AAC, + LDAC, + APTX, + APTX_HD, + APTX_AD, + LC3, + APTX_AD_SPEECH, + LC3_VOICE, + APTX_AD_QLEA, + APTX_AD_R4, + NO_CODEC +} codectype; + +static char const *codec_text[] = {"CODEC_TYPE_SBC", "CODEC_TYPE_AAC", + "CODEC_TYPE_LDAC", "CODEC_TYPE_APTX", + "CODEC_TYPE_APTX_HD", "CODEC_TYPE_APTX_AD", + "CODEC_TYPE_LC3", "CODEC_TYPE_APTX_AD_SPEECH", + "CODEC_TYPE_LC3_VOICE", "CODEC_TYPE_APTX_AD_QLEA", + "CODEC_TYPE_APTX_AD_R4","CODEC_TYPE_INVALID"}; + +static SOC_ENUM_SINGLE_EXT_DECL(codec_display, codec_text); #endif /*__LINUX_BTFM_SLIM_HW_INTERFACE_H*/