text: /* hfp_hf.c - Hands free Profile - Handsfree side handling */
  
  /*
   * Copyright (c) 2015-2016 Intel Corporation
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  #include <zephyr.h>
  #include <errno.h>
  #include <atomic.h>
  #include <misc/byteorder.h>
  #include <misc/util.h>
  #include <misc/printk.h>
  
  #include <bluetooth/log.h>
  #include <bluetooth/conn.h>
  #include <bluetooth/rfcomm.h>
  #include <bluetooth/hfp_hf.h>
  
  #include "l2cap_internal.h"
  #include "rfcomm_internal.h"
  #include "at.h"
  #include "hfp_internal.h"
  
  #if !defined(CONFIG_BLUETOOTH_DEBUG_HFP_HF)
  #undef BT_DBG
  #define BT_DBG(fmt, ...)
  #endif
  
  struct bt_hfp_hf_cb *bt_hf;
  
  static struct k_fifo hf_fifo;
  static NET_BUF_POOL(hf_pool, CONFIG_BLUETOOTH_MAX_CONN + 1,
  		    BT_RFCOMM_BUF_SIZE(BLUETOOTH_HF_CLIENT_MAX_PDU),
  		    &hf_fifo, NULL, BT_BUF_USER_DATA_MIN);
  
  static struct bt_hfp_hf bt_hfp_hf_pool[CONFIG_BLUETOOTH_MAX_CONN];
  
  void hf_slc_error(struct at_client *hf_at)
  {
  	BT_ERR("SLC error: disconnecting");
  }
  
  int hfp_hf_send_cmd(struct bt_hfp_hf *hf, at_resp_cb_t resp,
  		    at_finish_cb_t finish, const char *format, ...)
  {
  	struct net_buf *buf;
  	va_list vargs;
  	int ret;
  
  	/* register the callbacks */
  	at_register(&hf->at, resp, finish);
  
  	buf = bt_rfcomm_create_pdu(&hf_fifo);
  	if (!buf) {
  		BT_ERR("No Buffers!");
  		return -ENOMEM;
  	}
  
  	va_start(vargs, format);
  	ret = vsnprintk(buf->data, (net_buf_tailroom(buf) - 1), format, vargs);
  	if (ret < 0) {
  		BT_ERR("Unable to format variable arguments");
  		return ret;
  	}
  	va_end(vargs);
  
  	net_buf_add(buf, ret);
  	net_buf_add_u8(buf, '\r');
  
  	ret = bt_rfcomm_dlc_send(&hf->rfcomm_dlc, buf);
  	if (ret < 0) {
  		BT_ERR("Rfcomm send error :(%d)", ret);
  		return ret;
  	}
  
  	return 0;
  }
  
  int brsf_handle(struct at_client *hf_at)
  {
  	struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
  	uint32_t val;
  	int err;
  
  	err = at_get_number(hf_at->buf, &val);
  	if (err < 0) {
  		BT_ERR("Error getting value");
  		return err;
  	}
  
  	hf->ag_features = val;
  
  	return 0;
  }
  
  int brsf_resp(struct at_client *hf_at, struct net_buf *buf)
  {
  	int err;
  
  	BT_DBG("");
  
  	err = at_parse_cmd_input(hf_at, buf, "BRSF", brsf_handle);
  	if (err < 0) {
  		/* Returning negative value is avoided before SLC connection
  		 * established.
  		 */
  		BT_ERR("Error parsing CMD input");
  		hf_slc_error(hf_at);
  	}
  
  	return 0;
  }
  
  int brsf_finish(struct at_client *hf_at, struct net_buf *buf,
  		enum at_result result)
  {
  	if (result != AT_RESULT_OK) {
  		BT_ERR("SLC Connection ERROR in response");
  		hf_slc_error(hf_at);
  		return -EINVAL;
  	}
  
  	/* Continue with SLC creation */
  	return 0;
  }
  
  int hf_slc_establish(struct bt_hfp_hf *hf)
  {
  	int err;
  
  	BT_DBG("");
  
  	err = hfp_hf_send_cmd(hf, brsf_resp, brsf_finish, "AT+BRSF=%u",
  			      hf->hf_features);
  	if (err < 0) {
  		hf_slc_error(&hf->at);
  		return err;
  	}
  
  	return 0;
  }
  
  static void hfp_hf_connected(struct bt_rfcomm_dlc *dlc)
  {
  	struct bt_hfp_hf *hf = CONTAINER_OF(dlc, struct bt_hfp_hf, rfcomm_dlc);
  
  	BT_DBG("hf connected");
  
  	BT_ASSERT(hf);
  	hf_slc_establish(hf);
  }
  
  static void hfp_hf_disconnected(struct bt_rfcomm_dlc *dlc)
  {
  	BT_DBG("hf disconnected!");
  }
  
  static void hfp_hf_recv(struct bt_rfcomm_dlc *dlc, struct net_buf *buf)
  {
  	struct bt_hfp_hf *hf = CONTAINER_OF(dlc, struct bt_hfp_hf, rfcomm_dlc);
  
  	if (at_parse_input(&hf->at, buf) < 0) {
  		BT_ERR("Parsing failed");
  	}
  }
  
  static int bt_hfp_hf_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc)
  {
  	int i;
  	static struct bt_rfcomm_dlc_ops ops = {
  		.connected = hfp_hf_connected,
  		.disconnected = hfp_hf_disconnected,
  		.recv = hfp_hf_recv,
  	};
  
  	BT_DBG("conn %p", conn);
  
  	for (i = 0; i < ARRAY_SIZE(bt_hfp_hf_pool); i++) {
  		struct bt_hfp_hf *hf = &bt_hfp_hf_pool[i];
  
  		if (hf->rfcomm_dlc.session) {
  			continue;
  		}
  
  		hf->at.buf = hf->hf_buffer;
  		hf->at.buf_max_len = HF_MAX_BUF_LEN;
  
  		hf->rfcomm_dlc.ops = &ops;
  		hf->rfcomm_dlc.mtu = BLUETOOTH_HFP_MAX_MTU;
  
  		*dlc = &hf->rfcomm_dlc;
  
  		/* Set the supported features*/
  		hf->hf_features = BT_HFP_HF_SUPPORTED_FEATURES;
  
  		return 0;
  	}
  
  	BT_ERR("Unable to establish HF connection (%p)", conn);
  
  	return -ENOMEM;
  }
  
  static void hfp_hf_init(void)
  {
  	static struct bt_rfcomm_server chan = {
  		.channel = BT_RFCOMM_CHAN_HFP_HF,
  		.accept = bt_hfp_hf_accept,
  	};
  
  	net_buf_pool_init(hf_pool);
  
  	bt_rfcomm_server_register(&chan);
  }
  
  int bt_hfp_hf_register(struct bt_hfp_hf_cb *cb)
  {
  	if (!cb) {
  		return -EINVAL;
  	}
  
  	if (bt_hf) {
  		return -EALREADY;
  	}
  
  	bt_hf = cb;
  
  	hfp_hf_init();
  
  	return 0;
  }
  