Skip to content

Idea for single core HCI optimization

Michele Sardo edited this page Mar 17, 2024 · 6 revisions

HCI on single core devices

Problem

Single core device implementing both host and controller part, needs to implement HCI communication originally designed for wired connection. This adds a lot of redundancy and inefficiency both in memory usage and performance.

Draft proposal to replace HCI in single core devices

A good place to inspect to review how the host interface the controller is the file hci_core.c. Some observations are:

  • Despite the fact that the HCI is inherently asynchronous, most of the call are synchronous and uses bt_hci_cmd_send_sync
  • We can consider as a (typical?) example of synchronous call, the function hci_le_read_phy
static int hci_le_read_phy(struct bt_conn *conn)
{
	struct bt_hci_cp_le_read_phy *cp;
	struct bt_hci_rp_le_read_phy *rp;
	struct net_buf *buf, *rsp;
	int err;

	buf = bt_hci_cmd_create(BT_HCI_OP_LE_READ_PHY, sizeof(*cp));
	if (!buf) {
		return -ENOBUFS;
	}

	cp = net_buf_add(buf, sizeof(*cp));
	cp->handle = sys_cpu_to_le16(conn->handle);

	err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_PHY, buf, &rsp);
	if (err) {
		return err;
	}

	rp = (void *)rsp->data;
	conn->le.phy.tx_phy = bt_get_phy(rp->tx_phy);
	conn->le.phy.rx_phy = bt_get_phy(rp->rx_phy);
	net_buf_unref(rsp);

	return 0;
}

The steps performed are the following:

  1. call bt_hci_cmd_create to allocate buffer and check success
  2. add space for parameters via net_buf_add_mem, if any
  3. fill parameters taking care of endianess (weird for a single core!), if any
  4. call bt_hci_cmd_send_sync returning error and possible reply

A possible generalization of this method could be to define a controller API for each controller command as follow: int hci_op_le_read_phy(struct bt_hci_cp_le_set_phy *cp, struct bt_hci_rp_le_set_phy *rp);, standard naming convention to be defined.

This kind of API should be used to handle both single host+controller as well as separated host and controller.

Example of implementation for separated host and controller.

int hci_op_le_read_phy(struct bt_hci_cp_le_set_phy *cp, struct bt_hci_rp_le_set_phy *rp)
{
  buf = bt_hci_cmd_create();
  // copy param from cp to buf
  bt_fill_cmd_params(buf, cp);
  err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_PHY, buf, &rsp);
  bt_fill_rsp_params(buf, rp);
  return rp->status; # extracted from response
}

Example of implementation for single core host and controller.

int hci_op_le_read_phy(struct bt_hci_cp_le_set_phy *cp, struct bt_hci_rp_le_set_phy *rp)
{
  // Call matching API in the controller library (typically provided by SoC), after rearranging parameters ?
  // If we consider Zephyr controller, the call can be as follow
  return le_read_phy(cp, rp); // Indeed this is not that simple since net buf are used, need to be reviewed
}

Open points/todo

  • cp and rp parameters have dynamic allocation via net_buf, with the new proposal the caller should define them in its own stack.
  • Related to previous point, how variable length params are treated ? E.g. bt_hci_cp_host_num_completed_packets, are always last param with zero size array [0] ?
  • when reply is present, is it possible to ignore it passing rsp=NULL ? Is it a useful use case ?
  • list asynchronous cases by checking calls to bt_hci_cmd_send and define a strategy for them.
  • do we need to do something regarding events ?
  • What about acl,iso traffic ?
  • What about classic bluetooth (EDR), anything to do ?