~vcs-imports/alsa-plugins/trunk

« back to all changes in this revision

Viewing changes to aaf/pcm_aaf.c

  • Committer: Takashi Iwai
  • Author(s): Andre Guedes
  • Date: 2018-12-10 08:42:32 UTC
  • Revision ID: git-v1:027046166694448c3bf2bfc2697221c2c2dcb796
aaf: AVTPDU transmission periodicity

When operating in playback mode (i.e. AVTP talker) the plugin is
expected to transmit the AVTPDUs in a periodical manner. The AVTPDU
period is defined by the number of audio frames per AVTPDU and the
sampling rate (see section 7.7 from AVTP spec [1] for further
information).

To enforce the AVTPDU periodicity, this patch leverages the SO_TXTIME
sockopt recently added to socket interface which enables the userspace
to specify when a given packet should be transmitted. The plugin
configures the transmission time from each AVTPDU so the expected
transmission interval is maintained.

The SO_TXTIME feature works in conjunction with the Earliest TxTime
First (ETF) qdisc. The ETF qdisc sorts packets from multiple sockets by
the earliest transmission time and sends them to the network controller.
It also enables offloading packet transmission to hardware in case the
NIC supports it, providing more time accuracy. For further information
about ETF qdisc, see tc-etf(8). The qdisc can be configured many ways,
in doc/aaf.txt we provide an example.

Below follows some implementation highlights:

The packet transmission time is configured through socket control
message interface so we now use sendmsg() to transmit AVTPDUs, instead
of sendto().

sendmsg() API requires a msghdr struct which is initialized during
device setup time. Strictly speaking, that struct is only required when
operating in playback mode but we initialize it always, no matter if
running in playback or capture mode. This makes hw_params() and
hw_free() callbacks implementation way more simpler, specially on
handling error cases.

[1] 1722-2016 - IEEE Standard for a Transport Protocol for Time-Sensitive
    Applications in Bridged Local Area Networks

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
#include <limits.h>
28
28
#include <linux/if_ether.h>
29
29
#include <linux/if_packet.h>
 
30
#include <linux/net_tstamp.h>
30
31
#include <net/if.h>
31
32
#include <string.h>
32
33
#include <stdbool.h>
71
72
        int pdu_size;
72
73
        uint8_t pdu_seq;
73
74
 
 
75
        struct msghdr *msg;
 
76
        struct cmsghdr *cmsg;
 
77
 
74
78
        uint64_t timer_starttime;
75
79
        uint64_t timer_period;
76
80
        uint64_t timer_expirations;
282
286
        memcpy(&aaf->sk_addr.sll_addr, aaf->addr, ETH_ALEN);
283
287
 
284
288
        if (io->stream == SND_PCM_STREAM_PLAYBACK) {
 
289
                struct sock_txtime txtime_cfg;
 
290
 
285
291
                res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &aaf->prio,
286
292
                                 sizeof(aaf->prio));
287
293
                if (res < 0) {
289
295
                        res = -errno;
290
296
                        goto err;
291
297
                }
 
298
 
 
299
                txtime_cfg.clockid = CLOCK_TAI;
 
300
                txtime_cfg.flags = 0;
 
301
                res = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &txtime_cfg,
 
302
                                sizeof(txtime_cfg));
 
303
                if (res < 0) {
 
304
                        SNDERR("Failed to configure txtime");
 
305
                        res = -errno;
 
306
                        goto err;
 
307
                }
292
308
        } else {
293
309
                struct packet_mreq mreq = { 0 };
294
310
 
447
463
        return res;
448
464
}
449
465
 
 
466
static int aaf_init_msghdr(snd_pcm_aaf_t *aaf)
 
467
{
 
468
        int res;
 
469
        struct iovec *iov;
 
470
        char *control;
 
471
        size_t controllen;
 
472
        struct msghdr *msg;
 
473
        struct cmsghdr *cmsg;
 
474
 
 
475
        iov = malloc(sizeof(struct iovec));
 
476
        if (!iov) {
 
477
                SNDERR("Failed to allocate iovec");
 
478
                return -ENOMEM;
 
479
        }
 
480
 
 
481
        iov->iov_base = aaf->pdu;
 
482
        iov->iov_len = aaf->pdu_size;
 
483
 
 
484
        controllen = CMSG_SPACE(sizeof(__u64));
 
485
        control = malloc(controllen);
 
486
        if (!control) {
 
487
                SNDERR("Failed to allocate control buffer");
 
488
                res = -ENOMEM;
 
489
                goto err_free_iov;
 
490
        }
 
491
 
 
492
        msg = malloc(sizeof(struct msghdr));
 
493
        if (!msg) {
 
494
                SNDERR("Failed to allocate msghdr");
 
495
                res = -ENOMEM;
 
496
                goto err_free_control;
 
497
        }
 
498
 
 
499
        msg->msg_name = &aaf->sk_addr;
 
500
        msg->msg_namelen = sizeof(aaf->sk_addr);
 
501
        msg->msg_iov = iov;
 
502
        msg->msg_iovlen = 1;
 
503
        msg->msg_control = control;
 
504
        msg->msg_controllen = controllen;
 
505
 
 
506
        cmsg = CMSG_FIRSTHDR(msg);
 
507
        cmsg->cmsg_level = SOL_SOCKET;
 
508
        cmsg->cmsg_type = SCM_TXTIME;
 
509
        cmsg->cmsg_len = CMSG_LEN(sizeof(__u64));
 
510
 
 
511
        aaf->msg = msg;
 
512
        aaf->cmsg = cmsg;
 
513
        return 0;
 
514
 
 
515
err_free_control:
 
516
        free(control);
 
517
err_free_iov:
 
518
        free(iov);
 
519
        return res;
 
520
}
 
521
 
450
522
static void aaf_inc_ptr(snd_pcm_uframes_t *ptr, snd_pcm_uframes_t val,
451
523
                        snd_pcm_uframes_t boundary)
452
524
{
565
637
}
566
638
 
567
639
static int aaf_tx_pdu(snd_pcm_aaf_t *aaf, snd_pcm_uframes_t ptr,
568
 
                      uint64_t ptime)
 
640
                      uint64_t ptime, __u64 txtime)
569
641
{
570
642
        int res;
571
643
        ssize_t n;
572
644
        snd_pcm_ioplug_t *io = &aaf->io;
573
645
        struct avtp_stream_pdu *pdu = aaf->pdu;
574
646
 
 
647
        *(__u64 *)CMSG_DATA(aaf->cmsg) = txtime;
 
648
 
575
649
        res = snd_pcm_areas_copy_wrap(aaf->payload_areas, 0,
576
650
                                      aaf->frames_per_pdu,
577
651
                                      aaf->audiobuf_areas,
591
665
        if (res < 0)
592
666
                return res;
593
667
 
594
 
        n = sendto(aaf->sk_fd, aaf->pdu, aaf->pdu_size, 0,
595
 
                   (struct sockaddr *) &aaf->sk_addr,
596
 
                   sizeof(aaf->sk_addr));
 
668
        n = sendmsg(aaf->sk_fd, aaf->msg, 0);
597
669
        if (n < 0 || n != aaf->pdu_size) {
598
670
                SNDERR("Failed to send AAF PDU");
599
671
                return -EIO;
605
677
static int aaf_tx_pdus(snd_pcm_aaf_t *aaf, int pdu_count)
606
678
{
607
679
        int res;
608
 
        uint64_t ptime;
 
680
        uint64_t ptime, txtime;
609
681
        snd_pcm_uframes_t ptr;
610
682
 
611
 
        ptime = aaf_mclk_gettime(aaf) + aaf->mtt + aaf->t_uncertainty;
 
683
        txtime = aaf_mclk_gettime(aaf) + aaf->t_uncertainty;
 
684
        ptime = txtime + aaf->mtt;
612
685
        ptr = aaf->hw_ptr;
613
686
 
614
687
        while (pdu_count--) {
615
 
                res = aaf_tx_pdu(aaf, ptr, ptime);
 
688
                res = aaf_tx_pdu(aaf, ptr, ptime, txtime);
616
689
                if (res < 0)
617
690
                        return res;
618
691
 
 
692
                txtime += aaf->pdu_period;
619
693
                ptime += aaf->pdu_period;
620
694
                ptr += aaf->frames_per_pdu;
621
695
        }
1075
1149
        if (res < 0)
1076
1150
                goto err_free_pdu;
1077
1151
 
 
1152
        res = aaf_init_msghdr(aaf);
 
1153
        if (res < 0)
 
1154
                goto err_free_areas;
 
1155
 
1078
1156
        if (io->period_size % aaf->frames_per_pdu) {
1079
1157
                /* The plugin requires that the period size is multiple of the
1080
1158
                 * configuration frames_per_pdu. Return error if this
1082
1160
                 */
1083
1161
                SNDERR("Period size must be multiple of frames_per_pdu");
1084
1162
                res = -EINVAL;
1085
 
                goto err_free_areas;
 
1163
                goto err_free_msghdr;
1086
1164
        }
1087
1165
 
1088
1166
        aaf->pdu_period = (uint64_t)NSEC_PER_SEC * aaf->frames_per_pdu /
1089
1167
                          io->rate;
1090
1168
        return 0;
1091
1169
 
 
1170
err_free_msghdr:
 
1171
        free(aaf->msg->msg_iov);
 
1172
        free(aaf->msg->msg_control);
 
1173
        free(aaf->msg);
1092
1174
err_free_areas:
1093
1175
        free(aaf->payload_areas);
1094
1176
err_free_pdu:
1108
1190
        close(aaf->timer_fd);
1109
1191
        free(aaf->pdu);
1110
1192
        free(aaf->payload_areas);
 
1193
        free(aaf->msg->msg_iov);
 
1194
        free(aaf->msg->msg_control);
 
1195
        free(aaf->msg);
1111
1196
        return 0;
1112
1197
}
1113
1198