isotest: Add support for creating/synchronizing to multiple BISes
authorIulia Tanasescu <iulia.tanasescu@nxp.com>
Mon, 29 May 2023 06:27:34 +0000 (09:27 +0300)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 13:34:03 +0000 (19:04 +0530)
This adds an additional command line option for the Broadcast exercise,
so that the user can indicate the number of BISes to create as part of a
BIG (Broadcast Source), or the number of BISes to synchronize
to (Broadcast Sink).

For the Broadcast Source exercise, issue the following command,
in order to create a BIG with handle 0x01, associated with the
advertising handle 0x01, with 2 BISes:

    tools/isotest -i hci0 -s 00:00:00:00:00:00 -N 2 -G 1 -T 1

The isotest and btmon logs will look something like this:

isotest[7178]: mgmt socket: fd 3
isotest[7178]: mgmt_set_le: err 0
isotest[7178]: mgmt_set_experimental: err 0
isotest[7179]: Exit
isotest[7178]: Connecting 00:00:00:00:00:00 ...
isotest[7178]: Connected [00:00:00:00:00:00]
isotest[7178]: QoS [BIG 0x01 BIS 0x01 Packing 0x00 Framing 0x00 Encryption 0x00]
isotest[7178]: Input QoS [Interval 10000 us Latency 10 ms SDU 0 PHY 0x00 RTN 2]
isotest[7178]: Output QoS [Interval 10000 us Latency 10 ms SDU 40 PHY 0x02 RTN 2]
isotest[7178]: Connecting 00:00:00:00:00:00 ...
isotest[7178]: Connected [00:00:00:00:00:00]
isotest[7178]: QoS [BIG 0x01 BIS 0x01 Packing 0x00 Framing 0x00 Encryption 0x00]
isotest[7178]: Input QoS [Interval 10000 us Latency 10 ms SDU 0 PHY 0x00 RTN 2]
isotest[7178]: Output QoS [Interval 10000 us Latency 10 ms SDU 40 PHY 0x02 RTN 2]
isotest[7183]: Sending ...
isotest[7183]: Number of packets: 1
isotest[7183]: Socket jitter buffer: 80 buffer
isotest[7183]: [seq 0] 40 bytes buffered 92 (3712 bytes)
isotest[7184]: Sending ...
isotest[7184]: Number of packets: 1
isotest[7184]: Socket jitter buffer: 80 buffer
isotest[7184]: [seq 0] 40 bytes buffered 92 (3712 bytes)
isotest[7178]: Exit

< HCI Command: LE Create Broadcast Isochronous Group (0x08|0x0068) plen 31
        Handle: 0x01
        Advertising Handle: 0x01
        Number of BIS: 2
        SDU Interval: 10000 us (0x002710)
        Maximum SDU size: 40
        Maximum Latency: 10 ms (0x000a)
        RTN: 0x02
        PHY: LE 2M (0x02)
        Packing: Sequential (0x00)
        Framing: Unframed (0x00)
        Encryption: 0x00
        Broadcast Code: 00000000000000000000000000000000

> HCI Event: Command Status (0x0f) plen 4
      LE Create Broadcast Isochronous Group (0x08|0x0068) ncmd 1
        Status: Success (0x00)

> HCI Event: LE Meta Event (0x3e) plen 23
      LE Broadcast Isochronous Group Complete (0x1b)
        Status: Success (0x00)
        Handle: 0x01
        BIG Synchronization Delay: 1974 us (0x0007b6)
        Transport Latency: 1974 us (0x0007b6)
        PHY: LE 2M (0x02)
        NSE: 3
        BN: 1
        PTO: 1
        IRC: 3
        Maximum PDU: 40
        ISO Interval: 10.00 msec (0x0008)
        Connection Handle #0: 10
        Connection Handle #1: 11

< HCI Command: LE Setup Isochronous Data Path (0x08|0x006e) plen 13
        Handle: 10
        Data Path Direction: Input (Host to Controller) (0x00)
        Data Path: HCI (0x00)
        Coding Format: Transparent (0x03)
        Company Codec ID: Ericsson Technology Licensing (0)
        Vendor Codec ID: 0
        Controller Delay: 0 us (0x000000)
        Codec Configuration Length: 0
        Codec Configuration:

> HCI Event: Command Complete (0x0e) plen 6
      LE Setup Isochronous Data Path (0x08|0x006e) ncmd 1
        Status: Success (0x00)
        Handle: 10

< HCI Command: LE Setup Isochronous Data Path (0x08|0x006e) plen 13
        Handle: 11
        Data Path Direction: Input (Host to Controller) (0x00)
        Data Path: HCI (0x00)
        Coding Format: Transparent (0x03)
        Company Codec ID: Ericsson Technology Licensing (0)
        Vendor Codec ID: 0
        Controller Delay: 0 us (0x000000)
        Codec Configuration Length: 0
        Codec Configuration:

> HCI Event: Command Complete (0x0e) plen 6
      LE Setup Isochronous Data Path (0x08|0x006e) ncmd 1
        Status: Success (0x00)
        Handle: 11

< ISO Data TX: Handle 10 flags 0x02 dlen 44

< ISO Data TX: Handle 11 flags 0x02 dlen 44

> HCI Event: Number of Completed Packets (0x13) plen 5
        Num handles: 1
        Handle: 10
        Count: 1

> HCI Event: Number of Completed Packets (0x13) plen 5
        Num handles: 1
        Handle: 11
        Count: 1

For the Broadcast Sink exercise, issue the following command,
in order to synchronize to the BISes created by the source:

    tools/isotest -i hci1 -r 36:13:00:E1:1B:F0 -V le_random -N 2 -G 1

The flow is shown by the isotest log and the filtered btmon snippet below:

isotest[4033]: mgmt socket: fd 3
isotest[4034]: Exit
isotest[4033]: mgmt_set_le: err 0
isotest[4033]: mgmt_set_experimental: err 0
isotest[4033]: Waiting for connection 36:13:00:E1:1B:F0...
isotest[4036]: Connected [36:13:00:E1:1B:F0]
isotest[4036]: QoS [BIG 0x01 BIS 0x00 Packing 0x00 Framing 0x00 Encryption 0x00]
isotest[4036]: Input QoS [Interval 1974 us Latency 10 ms SDU 40 PHY 0x00 RTN 0]
isotest[4036]: Output QoS [Interval 0 us Latency 0 ms SDU 0 PHY 0x00 RTN 0]
isotest[4036]: Receiving ...
isotest[4037]: Connected [36:13:00:E1:1B:F0]
isotest[4037]: QoS [BIG 0x01 BIS 0x00 Packing 0x00 Framing 0x00 Encryption 0x00]
isotest[4037]: Input QoS [Interval 1974 us Latency 10 ms SDU 40 PHY 0x00 RTN 0]
isotest[4037]: Output QoS [Interval 0 us Latency 0 ms SDU 0 PHY 0x00 RTN 0]
isotest[4037]: Receiving ...
isotest[4037]: [seq 0] 280 bytes in 6.48 sec speed 0.34 kb/s
isotest[4036]: [seq 0] 280 bytes in 6.54 sec speed 0.33 kb/s
isotest[4037]: [seq 1] 280 bytes in 7.01 sec speed 0.31 kb/s
isotest[4036]: [seq 1] 280 bytes in 7.02 sec speed 0.31 kb/s
isotest[4037]: [seq 2] 280 bytes in 7.06 sec speed 0.31 kb/s
isotest[4036]: [seq 2] 280 bytes in 7.04 sec speed 0.31 kb/s

< HCI Command: LE Periodic Advertising Create Sync (0x08|0x0044) plen 14
        Options: 0x0000
        Use advertising SID, Advertiser Address Type and address
        Reporting initially enabled
        SID: 0x00
        Adv address type: Random (0x01)
        Adv address: 36:13:00:E1:1B:F0 (Non-Resolvable)
        Skip: 0x0000
        Sync timeout: 163840 msec (0x4000)
        Sync CTE type: 0x0000

> HCI Event: Command Status (0x0f) plen 4
      LE Periodic Advertising Create Sync (0x08|0x0044) ncmd 1
        Status: Success (0x00)

< HCI Command: LE Set Extended Scan Parameters (0x08|0x0041) plen 13
        Own address type: Public (0x00)
        Filter policy: Ignore not in accept list (0x01)
        PHYs: 0x05
        Entry 0: LE 1M
          Type: Passive (0x00)
          Interval: 60.000 msec (0x0060)
          Window: 30.000 msec (0x0030)
        Entry 1: LE Coded
          Type: Passive (0x00)
          Interval: 60.000 msec (0x0060)
          Window: 30.000 msec (0x0030)

> HCI Event: Command Complete (0x0e) plen 4
      LE Set Extended Scan Parameters (0x08|0x0041) ncmd 1
        Status: Success (0x00)

< HCI Command: LE Set Extended Scan Enable (0x08|0x0042) plen 6
        Extended scan: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
        Duration: 0 msec (0x0000)
        Period: 0.00 sec (0x0000)

> HCI Event: Command Complete (0x0e) plen 4
      LE Set Extended Scan Enable (0x08|0x0042) ncmd 1
        Status: Success (0x00)

> HCI Event: LE Meta Event (0x3e) plen 16
      LE Periodic Advertising Sync Established (0x0e)
        Status: Success (0x00)
        Sync handle: 0
        Advertising SID: 0x00
        Advertiser address type: Random (0x01)
        Advertiser address: 36:13:00:E1:1B:F0 (Non-Resolvable)
        Advertiser PHY: LE 2M (0x02)
        Periodic advertising interval: 10.00 msec (0x0008)
        Advertiser clock accuracy: 0x00

> HCI Event: LE Meta Event (0x3e) plen 8
      LE Periodic Advertising Report (0x0f)
        Sync handle: 0
        TX power: 127 dbm (0x7f)
        RSSI: -47 dBm (0xd1)
        CTE Type: No Constant Tone Extension (0xff)
        Data status: Complete
        Data length: 0x00

> HCI Event: LE Meta Event (0x3e) plen 20
      LE Broadcast Isochronous Group Info Advertising Report (0x22)
        Sync Handle: 0x0000
        Number BIS: 2
        NSE: 3
        ISO Interval: 10.00 msec (0x0008)
        BN: 1
        PTO: 1
        IRC: 3
        Maximum PDU: 40
        SDU Interval: 10000 us (0x002710)
        Maximum SDU: 40
        PHY: LE 2M (0x02)
        Framing: Unframed (0x00)
        Encryption: 0x00

< HCI Command: LE Broadcast Isochronous Group Create Sync (0x08|0x006b) plen 26
        BIG Handle: 0x01
        BIG Sync Handle: 0x0000
        Encryption: Unencrypted (0x00)
        Broadcast Code: 00000000000000000000000000000000
        Maximum Number Subevents: 0x00
        Timeout: 163840 ms (0x4000)
        Number of BIS: 2
        BIS ID: 0x01
        BIS ID: 0x02

> HCI Event: LE Meta Event (0x3e) plen 19
      LE Broadcast Isochronous Group Sync Estabilished (0x1d)
        Status: Success (0x00)
        BIG Handle: 0x01
        Transport Latency: 1974 us (0x0007b6)
        NSE: 3
        BN: 1
        PTO: 1
        IRC: 3
        Maximum PDU: 40
        ISO Interval: 10.00 msec (0x0008)
        Connection Handle #0: 10
        Connection Handle #1: 11

< HCI Command: LE Setup Isochronous Data Path (0x08|0x006e) plen 13
        Handle: 10
        Data Path Direction: Output (Controller to Host) (0x01)
        Data Path: HCI (0x00)
        Coding Format: Transparent (0x03)
        Company Codec ID: Ericsson Technology Licensing (0)
        Vendor Codec ID: 0
        Controller Delay: 0 us (0x000000)
        Codec Configuration Length: 0
        Codec Configuration:

> HCI Event: Command Complete (0x0e) plen 6
      LE Setup Isochronous Data Path (0x08|0x006e) ncmd 1
        Status: Success (0x00)
        Handle: 10

< HCI Command: LE Setup Isochronous Data Path (0x08|0x006e) plen 13
        Handle: 11
        Data Path Direction: Output (Controller to Host) (0x01)
        Data Path: HCI (0x00)
        Coding Format: Transparent (0x03)
        Company Codec ID: Ericsson Technology Licensing (0)
        Vendor Codec ID: 0
        Controller Delay: 0 us (0x000000)
        Codec Configuration Length: 0
        Codec Configuration:

> HCI Event: Command Complete (0x0e) plen 6
      LE Setup Isochronous Data Path (0x08|0x006e) ncmd 1
        Status: Success (0x00)
        Handle: 11

> ISO Data RX: Handle 10 flags 0x06 dlen 48

> ISO Data RX: Handle 11 flags 0x06 dlen 48

tools/isotest.c
tools/isotest.rst

index 54e4145..af32daa 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/sockios.h>
 #include <time.h>
 #include <inttypes.h>
+#include <sys/wait.h>
 
 #include "lib/bluetooth.h"
 #include "lib/hci.h"
@@ -48,6 +49,9 @@
 #define SEC_USEC(_t)  (_t  * 1000000L)
 #define TS_USEC(_ts)  (SEC_USEC((_ts)->tv_sec) + NSEC_USEC((_ts)->tv_nsec))
 
+#define DEFAULT_BIG_ID 0x01
+#define DEFAULT_BIS_ID 0x01
+
 /* Test modes */
 enum {
        SEND,
@@ -75,6 +79,8 @@ static bool quiet;
 struct bt_iso_qos *iso_qos;
 static bool inout;
 
+static uint8_t num_bis = 1;
+
 struct lookup_table {
        const char *name;
        int flag;
@@ -319,8 +325,6 @@ static int do_connect(char *peer)
        struct sockaddr_iso addr;
        int sk;
 
-       mgmt_set_experimental();
-
        /* Create socket */
        sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO);
        if (sk < 0) {
@@ -393,6 +397,45 @@ error:
        return -1;
 }
 
+static int *bcast_do_connect_mbis(uint8_t count, char *peer)
+{
+       int *sk;
+       uint8_t sk_cnt = 0;
+
+       sk = malloc(count * sizeof(*sk));
+       if (!sk) {
+               syslog(LOG_ERR, "Can't allocate socket array");
+               return NULL;
+       }
+
+       defer_setup = 1;
+
+       for (int i = 0; i < count; i++) {
+               if (i == count - 1)
+                       defer_setup = 0;
+
+               sk[i] = do_connect(peer);
+               if (sk[i] < 0) {
+                       syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                       strerror(errno), errno);
+
+                       goto error;
+               }
+
+               sk_cnt++;
+       }
+
+       return sk;
+
+error:
+       for (int i = 0; i < sk_cnt; i++)
+               close(sk[i]);
+
+       free(sk);
+       return NULL;
+
+}
+
 static void do_listen(char *filename, void (*handler)(int fd, int sk),
                                                        char *peer)
 {
@@ -434,8 +477,11 @@ static void do_listen(char *filename, void (*handler)(int fd, int sk),
        if (peer) {
                str2ba(peer, &addr->iso_bc->bc_bdaddr);
                addr->iso_bc->bc_bdaddr_type = bdaddr_type;
-               addr->iso_bc->bc_num_bis = 1;
-               addr->iso_bc->bc_bis[0] = 1;
+               addr->iso_bc->bc_num_bis = num_bis;
+
+               for (int i = 0; i < num_bis; i++)
+                       addr->iso_bc->bc_bis[i] = i + 1;
+
                optlen += sizeof(*addr->iso_bc);
        }
 
@@ -587,6 +633,7 @@ static void recv_mode(int fd, int sk)
                                                        strerror(errno), errno);
                                if (errno != ENOTCONN)
                                        return;
+
                                r = 0;
                        }
 
@@ -715,12 +762,66 @@ static int read_file(int fd, ssize_t count, bool rewind)
        return len;
 }
 
-static void do_send(int sk, int fd, struct bt_iso_io_qos *out, uint32_t num,
-                   bool repeat)
+static void do_send(int sk, int fd, char *peer, bool repeat)
 {
        uint32_t seq;
        struct timespec t_start;
-       int len, used;
+       int send_len, used;
+       socklen_t len;
+       struct bt_iso_qos qos;
+       uint32_t num;
+       struct bt_iso_io_qos *out;
+
+       syslog(LOG_INFO, "Sending ...");
+
+       /* Read QoS */
+       if (!strcmp(peer, "00:00:00:00:00:00"))
+               out = &qos.bcast.out;
+       else
+               out = &qos.ucast.out;
+
+       memset(&qos, 0, sizeof(qos));
+       len = sizeof(qos);
+       if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) {
+               syslog(LOG_ERR, "Can't get Output QoS socket option: %s (%d)",
+                               strerror(errno), errno);
+               out->sdu = ISO_DEFAULT_MTU;
+       }
+
+       /* num of packets = latency (ms) / interval (us) */
+       num = (out->latency * 1000 / out->interval);
+
+       syslog(LOG_INFO, "Number of packets: %d", num);
+
+       if (!sndbuf)
+               /* Use socket buffer as a jitter buffer for the entire buffer
+                * latency:
+                * jitter buffer = 2 * (SDU * subevents)
+                */
+               sndbuf = 2 * ((out->latency * 1000 / out->interval) *
+                                                       out->sdu);
+
+       len = sizeof(sndbuf);
+       if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &sndbuf, len) < 0) {
+               syslog(LOG_ERR, "Can't set socket SO_SNDBUF option: %s (%d)",
+                               strerror(errno), errno);
+       }
+
+       syslog(LOG_INFO, "Socket jitter buffer: %d buffer", sndbuf);
+
+       if (sndto.tv_usec) {
+               len = sizeof(sndto);
+               if (setsockopt(sk, SOL_SOCKET, SO_SNDTIMEO, &sndto, len) < 0) {
+                       syslog(LOG_ERR, "Can't set socket SO_SNDTIMEO option: "
+                               "%s (%d)", strerror(errno), errno);
+               } else {
+                       syslog(LOG_INFO, "Socket send timeout: %ld usec",
+                                                       sndto.tv_usec);
+               }
+       }
+
+       for (int i = 6; i < out->sdu; i++)
+               buf[i] = 0x7f;
 
        if (clock_gettime(CLOCK_MONOTONIC, &t_start) < 0) {
                perror("clock_gettime");
@@ -729,17 +830,17 @@ static void do_send(int sk, int fd, struct bt_iso_io_qos *out, uint32_t num,
 
        for (seq = 0; ; seq++) {
                if (fd >= 0) {
-                       len = read_file(fd, out->sdu, repeat);
-                       if (len < 0) {
+                       send_len = read_file(fd, out->sdu, repeat);
+                       if (send_len < 0) {
                                syslog(LOG_ERR, "read failed: %s (%d)",
-                                               strerror(-len), -len);
+                                               strerror(-send_len), -send_len);
                                exit(1);
                        }
                } else
-                       len = out->sdu;
+                       send_len = out->sdu;
 
-               len = send(sk, buf, len, 0);
-               if (len <= 0) {
+               send_len = send(sk, buf, send_len, 0);
+               if (send_len <= 0) {
                        syslog(LOG_ERR, "send failed: %s (%d)",
                                                strerror(errno), errno);
                        exit(1);
@@ -750,7 +851,7 @@ static void do_send(int sk, int fd, struct bt_iso_io_qos *out, uint32_t num,
                if (!quiet)
                        syslog(LOG_INFO,
                                "[seq %d] %d bytes buffered %d (%d bytes)",
-                               seq, len, used / len, used);
+                               seq, send_len, used / send_len, used);
 
                if (seq && !((seq + 1) % num))
                        send_wait(&t_start, num * out->interval);
@@ -759,11 +860,11 @@ static void do_send(int sk, int fd, struct bt_iso_io_qos *out, uint32_t num,
 
 static void send_mode(char *filename, char *peer, int i, bool repeat)
 {
-       struct bt_iso_qos qos;
-       socklen_t len;
        int sk, fd = -1;
-       uint32_t num;
-       struct bt_iso_io_qos *out;
+       int *sk_arr;
+       uint8_t nconn = strcmp(peer, "00:00:00:00:00:00") ? 1 : num_bis;
+
+       mgmt_set_experimental();
 
        if (filename) {
                char altername[PATH_MAX];
@@ -780,6 +881,33 @@ static void send_mode(char *filename, char *peer, int i, bool repeat)
                        fd = open_file(filename);
        }
 
+       if (nconn > 1) {
+               sk_arr = bcast_do_connect_mbis(nconn, peer);
+               if (!sk_arr)
+                       exit(1);
+
+               for (int i = 0; i < nconn; i++) {
+                       if (fork()) {
+                               /* Parent */
+                               continue;
+                       }
+
+                       /* Child */
+                       do_send(sk_arr[i], fd, peer, repeat);
+                       exit(0);
+               }
+
+               /* Wait for children to exit */
+               while (wait(NULL) > 0)
+                       ;
+
+               for (int i = 0; i < nconn; i++)
+                       close(sk_arr[i]);
+
+               free(sk_arr);
+               return;
+       }
+
        sk = do_connect(peer);
        if (sk < 0) {
                syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
@@ -793,62 +921,13 @@ static void send_mode(char *filename, char *peer, int i, bool repeat)
                sleep(abs(defer_setup) - 1);
        }
 
-       syslog(LOG_INFO, "Sending ...");
-
-       /* Read QoS */
-       if (!strcmp(peer, "00:00:00:00:00:00"))
-               out = &qos.bcast.out;
-       else
-               out = &qos.ucast.out;
-
-       memset(&qos, 0, sizeof(qos));
-       len = sizeof(qos);
-       if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) {
-               syslog(LOG_ERR, "Can't get Output QoS socket option: %s (%d)",
-                               strerror(errno), errno);
-               out->sdu = ISO_DEFAULT_MTU;
-       }
-
-       /* num of packets = latency (ms) / interval (us) */
-       num = (out->latency * 1000 / out->interval);
-
-       syslog(LOG_INFO, "Number of packets: %d", num);
-
-       if (!sndbuf)
-               /* Use socket buffer as a jitter buffer for the entire buffer
-                * latency:
-                * jitter buffer = 2 * (SDU * subevents)
-                */
-               sndbuf = 2 * ((out->latency * 1000 / out->interval) *
-                                                       out->sdu);
-
-       len = sizeof(sndbuf);
-       if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &sndbuf, len) < 0) {
-               syslog(LOG_ERR, "Can't set socket SO_SNDBUF option: %s (%d)",
-                               strerror(errno), errno);
-       }
-
-       syslog(LOG_INFO, "Socket jitter buffer: %d buffer", sndbuf);
-
-       if (sndto.tv_usec) {
-               len = sizeof(sndto);
-               if (setsockopt(sk, SOL_SOCKET, SO_SNDTIMEO, &sndto, len) < 0) {
-                       syslog(LOG_ERR, "Can't set socket SO_SNDTIMEO option: "
-                               "%s (%d)", strerror(errno), errno);
-               } else {
-                       syslog(LOG_INFO, "Socket send timeout: %ld usec",
-                                                       sndto.tv_usec);
-               }
-       }
-
-       for (i = 6; i < out->sdu; i++)
-               buf[i] = 0x7f;
-
-       do_send(sk, fd, out, num, repeat);
+       do_send(sk, fd, peer, repeat);
 }
 
 static void reconnect_mode(char *peer)
 {
+       mgmt_set_experimental();
+
        while (1) {
                int sk;
 
@@ -867,6 +946,8 @@ static void reconnect_mode(char *peer)
 
 static void multy_connect_mode(char *peer)
 {
+       mgmt_set_experimental();
+
        while (1) {
                int i, sk;
 
@@ -1000,7 +1081,8 @@ static void usage(void)
                "\t[-B, --preset <value>]\n"
                "\t[-G, --CIG/BIG <value>]\n"
                "\t[-T, --CIS/BIS <value>]\n"
-               "\t[-V, --type <value>] address type (help for list)\n");
+               "\t[-V, --type <value>] address type (help for list)\n"
+               "\t[-N, --nbis <value>] Number of BISes to create/synchronize to\n");
 }
 
 static const struct option main_options[] = {
@@ -1030,6 +1112,7 @@ static const struct option main_options[] = {
        { "CIG/BIG",   required_argument, NULL, 'G'},
        { "CIS/BIS",   required_argument, NULL, 'T'},
        { "type",      required_argument, NULL, 'V'},
+       { "nbis",      required_argument, NULL, 'N'},
        {}
 };
 
@@ -1059,6 +1142,8 @@ int main(int argc, char *argv[])
        char *filename = NULL;
        bool repeat = false;
        unsigned int i;
+       uint8_t nconn = 1;
+       char *peer;
 
        iso_qos = malloc(sizeof(*iso_qos));
        /* Default to 16_2_1 */
@@ -1069,7 +1154,7 @@ int main(int argc, char *argv[])
                int opt;
 
                opt = getopt_long(argc, argv,
-                       "d::cmr::s::nb:i:j:hqt:CV:W:M:S:P:F:I:L:Y:R:B:G:T:e:k:",
+                       "d::cmr::s::nb:i:j:hqt:CV:W:M:S:P:F:I:L:Y:R:B:G:T:e:k:N:",
                        main_options, NULL);
                if (opt < 0)
                        break;
@@ -1235,6 +1320,23 @@ int main(int argc, char *argv[])
                                        exit(1);
                        break;
 
+               case 'N':
+                       if (optarg)
+                               num_bis = atoi(optarg);
+
+                       if (num_bis > 1) {
+                               /* If the user requested multiple BISes,
+                                * make sure that all BISes are bound
+                                * for the same BIG and advertising set
+                                */
+                               if (iso_qos->bcast.big == BT_ISO_QOS_BIG_UNSET)
+                                       iso_qos->bcast.big = DEFAULT_BIG_ID;
+
+                               if (iso_qos->bcast.bis == BT_ISO_QOS_BIS_UNSET)
+                                       iso_qos->bcast.bis = DEFAULT_BIS_ID;
+                       }
+                       break;
+
                /* fall through */
                default:
                        usage();
@@ -1308,10 +1410,46 @@ int main(int argc, char *argv[])
                        break;
 
                case CONNECT:
-                       sk = do_connect(argv[optind + i]);
-                       if (sk < 0)
-                               exit(1);
-                       dump_mode(-1, sk);
+                       peer = argv[optind + i];
+
+                       mgmt_set_experimental();
+
+                       if (!strcmp(peer, "00:00:00:00:00:00"))
+                               nconn = num_bis;
+
+                       if (nconn > 1) {
+                               int *sk_arr =  bcast_do_connect_mbis(nconn,
+                                                               peer);
+
+                               if (!sk_arr)
+                                       exit(1);
+
+                               for (int i = 0; i < nconn; i++) {
+                                       if (fork()) {
+                                               /* Parent */
+                                               continue;
+                                       }
+
+                                       /* Child */
+                                       dump_mode(-1, sk_arr[i]);
+                                       exit(0);
+                               }
+
+                               /* Wait for children to exit */
+                               while (wait(NULL) > 0)
+                                       ;
+
+                               for (int i = 0; i < nconn; i++)
+                                       close(sk_arr[i]);
+
+                               free(sk_arr);
+                       } else {
+                               sk = do_connect(argv[optind + i]);
+                               if (sk < 0)
+                                       exit(1);
+                               dump_mode(-1, sk);
+                       }
+
                        break;
 
                case RECV:
index 124dc71..fc5b3c5 100644 (file)
@@ -172,6 +172,10 @@ OPTIONS
 
 -k, --bcode=<BCODE>  Socket QoS Broadcast Code
 
+-N, --nbis=<NBIS>  Number of BISes to create as part of a
+                   BIG (BIS broadcaster) or to synchronize
+                   to (BIS broadcast receiver)
+
 EXAMPLES
 ========