@@ -1119,6 +1119,81 @@ int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
return 0;
}
+int hci_send_acl_data(int dd, uint16_t handle, uint8_t dlen, struct signal_hdr *sh, struct signal_payload_hdr *plh, void *pl)
+{
+ uint8_t type = HCI_ACLDATA_PKT;
+ hci_acl_hdr ha;
+ struct iovec iv[5];
+ int ivn;
+
+ ha.handle = handle;
+ ha.dlen = dlen;
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = &ha;
+ iv[1].iov_len = HCI_ACL_HDR_SIZE;
+ ivn = 2;
+
+ printf("\nACL Packet details[handle:%x, length:%d]\n", ha.handle, ha.dlen);
+
+ if (dlen) {
+ iv[2].iov_base = sh;
+ iv[2].iov_len = 4; //HCI_SIGNAL_HDR_SIZE;
+ ivn = 3;
+ printf("\nACL signal command details[length:%d, cid:%d]\n", sh->len, sh->cid);
+ if(sh->len > 0) {
+ iv[3].iov_base = plh;
+ iv[3].iov_len = 4; //HCI_SIGNAL_PAYLOAD_HDR_SIZE;
+ ivn = 4;
+ if(plh->len > 0) {
+ iv[4].iov_base = pl;
+ iv[4].iov_len = plh->len;
+ ivn = 5;
+ }
+ }
+ }
+
+ while (writev(dd, iv, ivn) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ return -1;
+ }
+ return 0;
+}
+
+int hci_signal_le_con_param_update_req(int dd, uint16_t handle, uint16_t interval_min,
+ uint16_t interval_max, uint16_t slave_latency,
+ uint16_t timeout_multiplier)
+{
+ struct signal_hdr sh;
+ struct signal_payload_hdr pl;
+ struct le_con_param_update_req ur;
+
+ uint16_t length = 0x0010;
+
+ memset(&sh, 0, sizeof(sh));
+ memset(&pl, 0, sizeof(pl));
+ memset(&ur, 0, sizeof(ur));
+
+ sh.len = HCI_SIGNAL_LE_CON_PARAM_UPDATE_REQ_SIZE;
+ sh.cid = HCI_LE_CHANNEL_ID;
+
+ pl.code = LE_CON_PARAM_UPDATE_REQ_CODE;
+ pl.id = 0x77;
+ pl.len = LE_CON_PARAM_UPDATE_LEN;
+
+ ur.interval_min = interval_min;
+ ur.interval_max = interval_max;
+ ur.slave_latency = slave_latency;
+ ur.timeout_multiplier = timeout_multiplier;
+
+ if (hci_send_acl_data(dd, handle, length, &sh, &pl, &ur) < 0)
+ return -1;
+
+ return 0;
+}
+
int hci_send_req(int dd, struct hci_request *r, int to)
{
unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
@@ -35,12 +35,41 @@ struct hci_version {
uint16_t lmp_subver;
};
+struct hci_acl_hdr {
+ uint16_t handle;
+ uint16_t len;
+};
+
+struct signal_hdr {
+ uint16_t len;
+ uint16_t cid;
+};
+
+struct signal_payload_hdr {
+ uint8_t code;
+ uint8_t id;
+ uint16_t len;
+};
+
+struct le_con_param_update_req {
+ uint16_t interval_min;
+ uint16_t interval_max;
+ uint16_t slave_latency;
+ uint16_t timeout_multiplier;
+};
+#define HCI_SIGNAL_LE_CON_PARAM_UPDATE_REQ_SIZE 0x000C
+#define HCI_LE_CHANNEL_ID 0x0005
+#define LE_CON_PARAM_UPDATE_REQ_CODE 0x12
+#define LE_CON_PARAM_UPDATE_LEN 0x0008
+
int hci_open_dev(int dev_id);
int hci_close_dev(int dd);
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
+int hci_send_acl_data(int dd, uint16_t handle, uint8_t dlen, struct signal_hdr *sh, struct signal_payload_hdr *plh, void *pl);
int hci_send_req(int dd, struct hci_request *req, int timeout);
int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
+int hci_signal_le_con_param_update_req(int dd, uint16_t handle, uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency, uint16_t timeout_multiplier);
int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to);
int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
@@ -3369,6 +3369,95 @@ static void cmd_lecup(int dev_id, int argc, char **argv)
hci_close_dev(dd);
}
+static const char *acl_lecup_help =
+ "Usage:\n"
+ "\tacllecup <handle> <min> <max> <latency> <timeout>\n"
+ "\tOptions:\n"
+ "\t -H, --handle <0xXXXX> LE connection handle\n"
+ "\t -m, --min <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -M, --max <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -l, --latency <range> Slave latency. Range: 0x0000 to 0x03E8\n"
+ "\t -t, --timeout <time> N * 10ms. Range: 0x000A to 0x0C80\n"
+ "\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
+ "\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
+
+static void cmd_acl_lecup(int dev_id, int argc, char **argv)
+{
+ uint16_t handle = 0, min, max, latency, timeout;
+ int opt, dd, base;
+ int options = 0;
+
+ /* Aleatory valid values */
+ min = 0x0C8;
+ max = 0x0960;
+ latency = 0x0007;
+ timeout = 0x0C80;
+
+ for_each_opt(opt, lecup_options, NULL) {
+ if (optarg && strncasecmp("0x", optarg, 2) == 0)
+ base = 16;
+ else
+ base = 10;
+
+ switch (opt) {
+ case 'H':
+ handle = strtoul(optarg, NULL, base);
+ break;
+ case 'm':
+ min = strtoul(optarg, NULL, base);
+ break;
+ case 'M':
+ max = strtoul(optarg, NULL, base);
+ break;
+ case 'l':
+ latency = strtoul(optarg, NULL, base);
+ break;
+ case 't':
+ timeout = strtoul(optarg, NULL, base);
+ break;
+ default:
+ printf("%s", acl_lecup_help);
+ return;
+ }
+ options = 1;
+ }
+
+ if (options == 0) {
+ helper_arg(5, 5, &argc, &argv, acl_lecup_help);
+
+ handle = strtoul(argv[0], NULL, 0);
+ min = strtoul(argv[1], NULL, 0);
+ max = strtoul(argv[2], NULL, 0);
+ latency = strtoul(argv[3], NULL, 0);
+ timeout = strtoul(argv[4], NULL, 0);
+ }
+
+ if (handle == 0 || handle > 0x0EFF) {
+ printf("%s", acl_lecup_help);
+ return;
+ }
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ fprintf(stderr, "HCI device open failed\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "Signal LE Connection Update Request: %d %d %d %d %d\n",
+ handle, min, max, latency, timeout);
+ if (hci_signal_le_con_param_update_req(dd, htobs(handle), htobs(min), htobs(max),
+ htobs(latency), htobs(timeout)) < 0) {
+ int err = -errno;
+ fprintf(stderr, "Could not change connection params: %s(%d)\n",
+ strerror(-err), -err);
+ }
+
+ hci_close_dev(dd);
+}
+
static struct {
char *cmd;
void (*func)(int dev_id, int argc, char **argv);
@@ -3417,6 +3506,7 @@ static struct {
{ "lecc", cmd_lecc, "Create a LE Connection" },
{ "ledc", cmd_ledc, "Disconnect a LE Connection" },
{ "lecup", cmd_lecup, "LE Connection Update" },
+ { "acllecup", cmd_acl_lecup, "LE ACL Connection Param Update Req" },
{ NULL, NULL, 0 }
};