summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Hall <christopher.s.hall@intel.com>2013-12-31 12:07:01 -0800
committerChristopher Hall <christopher.s.hall@intel.com>2013-12-31 12:07:01 -0800
commitf2d6bd3c1871507641baf5a3a69fd1665f27b010 (patch)
tree7b76ab934833c40372647b43a257137ef49c8766
parentee150d866dab0449cf2817d6d2dbd364865d6279 (diff)
parent9a6961ea29ec89052f142e3d1312f04c2547b9a9 (diff)
downloadOpen-AVB-f2d6bd3c1871507641baf5a3a69fd1665f27b010.tar.gz
Merge branch 'open-avb-next' of https://github.com/intel-ethernet/Open-AVB into open-avb-next
-rw-r--r--README.rst6
-rw-r--r--daemons/gptp/README.rst2
-rw-r--r--daemons/gptp/common/avbts_message.hpp6
-rw-r--r--daemons/gptp/common/avbts_port.hpp7
-rw-r--r--daemons/gptp/common/ieee1588.hpp68
-rw-r--r--daemons/gptp/common/ieee1588port.cpp10
-rw-r--r--daemons/gptp/common/ptp_message.cpp20
-rw-r--r--daemons/mrpd/mmrp.c20
-rw-r--r--daemons/mrpd/mmrp.h1
-rw-r--r--daemons/mrpd/msrp.c100
-rw-r--r--daemons/mrpd/msrp.h1
-rw-r--r--daemons/mrpd/mvrp.c22
-rw-r--r--daemons/mrpd/mvrp.h5
-rw-r--r--examples/jackd-listener/Makefile5
-rw-r--r--examples/jackd-listener/jack_listener.c535
-rw-r--r--examples/jackd-talker/Makefile24
-rw-r--r--examples/jackd-talker/defines.h9
-rw-r--r--examples/jackd-talker/jack.c133
-rw-r--r--examples/jackd-talker/jack.h10
-rwxr-xr-xexamples/jackd-talker/jackd_talker.c1155
-rw-r--r--examples/mrp_client/Makefile7
-rw-r--r--examples/mrp_client/mrpValidate.c433
-rw-r--r--examples/simple_listener/simple_listener.c15
-rwxr-xr-xexamples/simple_talker/simple_talker.c41
-rw-r--r--kmod/igb/startup.sh1
25 files changed, 2555 insertions, 81 deletions
diff --git a/README.rst b/README.rst
index 1d57aa2d..111492ec 100644
--- a/README.rst
+++ b/README.rst
@@ -63,9 +63,13 @@ devices based on their standardized management properties.
+ https://github.com/jdkoftinoff/jdksavdecc-c
+AudioScience has created a 1722.1 C++ controller library which builds on jdkadvecc-c.
+
++ https://github.com/audioscience/avdecc-lib
+
XMOS
----
XMOS is a semiconductor company providing a reference design for AVB
-endpoints in pro audio and automotive. Our source code is open source
+endpoints in pro audio and automotive. XMOS endpoint source code is open source
and available on Github - https://github.com/xcore/sw_avb
diff --git a/daemons/gptp/README.rst b/daemons/gptp/README.rst
index 8f90e65f..374a2215 100644
--- a/daemons/gptp/README.rst
+++ b/daemons/gptp/README.rst
@@ -46,6 +46,8 @@ To execute, run
such as
./daemon_cl eth0
+The daemon creates a shared memory segment with the 'ptp' group. Some distributions may not have this group installed. The apparent effect is client applications will segfault after attempting to connect and use the shared memory segment. One suggested workaround is to execute 'sudo groupadd ptp' or similar to fix this issue.
+
Windows Version
+++++++++++++++
diff --git a/daemons/gptp/common/avbts_message.hpp b/daemons/gptp/common/avbts_message.hpp
index 10a39f52..5786bb80 100644
--- a/daemons/gptp/common/avbts_message.hpp
+++ b/daemons/gptp/common/avbts_message.hpp
@@ -106,12 +106,14 @@
#define PTP_PDELAY_FOLLOWUP_REQ_CLOCK_ID(x) x+10
#define PTP_PDELAY_FOLLOWUP_REQ_PORT_ID(x) x+18
-#define PTP_LI_61_BYTE 0
+#define PTP_LI_61_BYTE 1
#define PTP_LI_61_BIT 0
-#define PTP_LI_59_BYTE 0
+#define PTP_LI_59_BYTE 1
#define PTP_LI_59_BIT 1
#define PTP_ASSIST_BYTE 0
#define PTP_ASSIST_BIT 1
+#define PTP_PTPTIMESCALE_BYTE 1
+#define PTP_PTPTIMESCALE_BIT 3
enum MessageType {
SYNC_MESSAGE = 0,
diff --git a/daemons/gptp/common/avbts_port.hpp b/daemons/gptp/common/avbts_port.hpp
index 5dc46ab3..913d572c 100644
--- a/daemons/gptp/common/avbts_port.hpp
+++ b/daemons/gptp/common/avbts_port.hpp
@@ -53,8 +53,8 @@
#define OTHER_MULTICAST GPTP_MULTICAST
#define PDELAY_RESP_RECEIPT_TIMEOUT_MULTIPLIER 3
-#define SYNC_RECEIPT_TIMEOUT_MULTIPLIER 10
-#define ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER 10
+#define SYNC_RECEIPT_TIMEOUT_MULTIPLIER 3
+#define ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER 3
typedef enum {
PTP_MASTER,
@@ -239,6 +239,9 @@ class IEEE1588Port {
fprintf(stderr, "AsCapable: %s\n",
ascap == true ? "Enabled" : "Disabled");
}
+ if(!ascap){
+ _peer_offset_init = false;
+ }
asCapable = ascap;
}
diff --git a/daemons/gptp/common/ieee1588.hpp b/daemons/gptp/common/ieee1588.hpp
index d39ae1b0..27ce7229 100644
--- a/daemons/gptp/common/ieee1588.hpp
+++ b/daemons/gptp/common/ieee1588.hpp
@@ -155,46 +155,44 @@ static inline uint64_t bswap_64(uint64_t in)
}
#define NS_PER_SECOND 1000000000
-#define LS_SEC_MAX 0xFFFFFFFF
+#define LS_SEC_MAX 0xFFFFFFFFull
static inline void TIMESTAMP_SUB_NS( Timestamp &ts, uint64_t ns ) {
- bool borrow;
-
- if( (ns % NS_PER_SECOND) > ts.nanoseconds ) {
- borrow = true;
- ts.nanoseconds = (NS_PER_SECOND + ts.nanoseconds) - (ns % NS_PER_SECOND);
- } else {
- borrow = false;
- ts.nanoseconds = ts.nanoseconds - (ns % NS_PER_SECOND );
- }
- while( borrow || (ns/NS_PER_SECOND > 0) ) {
- if( ts.seconds_ls != 0 ) {
- --ts.seconds_ls;
- } else {
- --ts.seconds_ms;
- ts.seconds_ls = LS_SEC_MAX;
- }
- if( borrow ) borrow = false;
- else ns -= NS_PER_SECOND;
- }
- return;
+ uint64_t secs = (uint64_t)ts.seconds_ls | ((uint64_t)ts.seconds_ms) << 32;
+ uint64_t nanos = (uint64_t)ts.nanoseconds;
+
+ secs -= ns / NS_PER_SECOND;
+ ns = ns % NS_PER_SECOND;
+
+ if(ns > nanos)
+ { //borrow
+ nanos += NS_PER_SECOND;
+ --secs;
+ }
+
+ nanos -= ns;
+
+ ts.seconds_ms = (uint16_t)(secs >> 32);
+ ts.seconds_ls = (uint32_t)(secs & LS_SEC_MAX);
+ ts.nanoseconds = (uint32_t)nanos;
}
static inline void TIMESTAMP_ADD_NS( Timestamp &ts, uint64_t ns ) {
- uint32_t tmp;
- bool carry = false;
-
- tmp = ts.nanoseconds;
- ts.nanoseconds += ns % NS_PER_SECOND;
- if( ts.nanoseconds < tmp ) carry = true;
-
- while( carry || (ns/NS_PER_SECOND) >= 1 ) {
- ts.seconds_ls = (ts.seconds_ls == LS_SEC_MAX) ? 0 : ts.seconds_ls + 1;
- if( ts.seconds_ls == 0 ) ++ts.seconds_ms;
- if( carry ) carry = false;
- else ns -= NS_PER_SECOND;
- }
- return;
+ uint64_t secs = (uint64_t)ts.seconds_ls | ((uint64_t)ts.seconds_ms) << 32;
+ uint64_t nanos = (uint64_t)ts.nanoseconds;
+
+ secs += ns / NS_PER_SECOND;
+ nanos += ns % NS_PER_SECOND;
+
+ if(nanos > NS_PER_SECOND)
+ { //carry
+ nanos -= NS_PER_SECOND;
+ ++secs;
+ }
+
+ ts.seconds_ms = (uint16_t)(secs >> 32);
+ ts.seconds_ls = (uint32_t)(secs & LS_SEC_MAX);
+ ts.nanoseconds = (uint32_t)nanos;
}
#define XPTPD_ERROR(fmt,...) fprintf( stderr, "ERROR at %u in %s: " fmt "\n", __LINE__, __FILE__ ,## __VA_ARGS__)
diff --git a/daemons/gptp/common/ieee1588port.cpp b/daemons/gptp/common/ieee1588port.cpp
index 27ad6b69..5b84c380 100644
--- a/daemons/gptp/common/ieee1588port.cpp
+++ b/daemons/gptp/common/ieee1588port.cpp
@@ -392,7 +392,7 @@ void IEEE1588Port::processEvent(Event e)
} else if( port_state == PTP_MASTER ) {
becomeMaster( false );
} else {
- e3 = SYNC_RECEIPT_TIMEOUT_EXPIRES;
+ //e3 = SYNC_RECEIPT_TIMEOUT_EXPIRES;
e4 = ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES;
interval3 = (unsigned long long)
(SYNC_RECEIPT_TIMEOUT_MULTIPLIER*
@@ -891,13 +891,15 @@ void IEEE1588Port::becomeMaster( bool annc ) {
}
void IEEE1588Port::becomeSlave( bool restart_syntonization ) {
- port_state = PTP_SLAVE;
clock->deleteEventTimer( this, ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES );
clock->deleteEventTimer( this, SYNC_INTERVAL_TIMEOUT_EXPIRES );
- clock->addEventTimer
+
+ port_state = PTP_SLAVE;
+
+ /*clock->addEventTimer
( this, SYNC_RECEIPT_TIMEOUT_EXPIRES,
(SYNC_RECEIPT_TIMEOUT_MULTIPLIER*
- (unsigned long long)(pow((double)2,getSyncInterval())*1000000000.0)));
+ (unsigned long long)(pow((double)2,getSyncInterval())*1000000000.0)));*/
clock->addEventTimer
(this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES,
(ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER*
diff --git a/daemons/gptp/common/ptp_message.cpp b/daemons/gptp/common/ptp_message.cpp
index aa22c6d6..1febf4e9 100644
--- a/daemons/gptp/common/ptp_message.cpp
+++ b/daemons/gptp/common/ptp_message.cpp
@@ -51,6 +51,7 @@ PTPMessageCommon::PTPMessageCommon(IEEE1588Port * port)
domainNumber = port->getClock()->getDomain();
// Set flags as necessary
memset(flags, 0, PTP_FLAGS_LENGTH);
+ flags[PTP_PTPTIMESCALE_BYTE] |= (0x1 << PTP_PTPTIMESCALE_BIT);
correctionField = 0;
_gc = false;
sourcePortIdentity = new PortIdentity();
@@ -72,6 +73,7 @@ PTPMessageCommon *buildPTPMessage
PTPMessageCommon *msg = NULL;
MessageType messageType;
unsigned char tspec_msg_t = 0;
+ unsigned char transportSpecific = 0;
uint16_t sequenceId;
PortIdentity *sourcePortIdentity;
@@ -96,6 +98,7 @@ PTPMessageCommon *buildPTPMessage
buf + PTP_COMMON_HDR_TRANSSPEC_MSGTYPE(PTP_COMMON_HDR_OFFSET),
sizeof(tspec_msg_t));
messageType = (MessageType) (tspec_msg_t & 0xF);
+ transportSpecific = (tspec_msg_t >> 4) & 0x0F;
sourcePortIdentity = new PortIdentity
((uint8_t *)
@@ -148,6 +151,11 @@ PTPMessageCommon *buildPTPMessage
}
+ if (transportSpecific!=1) {
+ XPTPD_INFO("*** Received message with unsupported transportSpecific type=%d",transportSpecific);
+ return NULL;
+ }
+
switch (messageType) {
case SYNC_MESSAGE:
@@ -741,9 +749,13 @@ void PTPMessageAnnounce::processMessage(IEEE1588Port * port)
port->getClock()->deleteEventTimer(port,
ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES);
+ if( stepsRemoved >= 255 ) goto bail;
// Add message to the list
port->addQualifiedAnnounce(this);
+ port->getClock()->addEventTimer(port, STATE_CHANGE_EVENT, 16000000);
+
+bail:
port->getClock()->addEventTimer(port, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES,
(unsigned long long)
(ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER *
@@ -751,7 +763,6 @@ void PTPMessageAnnounce::processMessage(IEEE1588Port * port)
((double)2,
port->getAnnounceInterval()) *
1000000000.0)));
- port->getClock()->addEventTimer(port, STATE_CHANGE_EVENT, 16000000);
}
void PTPMessageSync::processMessage(IEEE1588Port * port)
@@ -1209,7 +1220,7 @@ void PTPMessagePathDelayReq::sendPort(IEEE1588Port * port,
PTPMessagePathDelayResp::PTPMessagePathDelayResp(IEEE1588Port * port) :
PTPMessageCommon(port)
{
- logMeanMessageInterval = 0;
+ logMeanMessageInterval = 0x7F;
control = MESSAGE_OTHER;
messageType = PATH_DELAY_RESP_MESSAGE;
versionPTP = GPTP_VERSION;
@@ -1317,7 +1328,7 @@ void PTPMessagePathDelayResp::getRequestingPortIdentity
PTPMessagePathDelayRespFollowUp::PTPMessagePathDelayRespFollowUp
(IEEE1588Port * port) : PTPMessageCommon (port)
{
- logMeanMessageInterval = 0;
+ logMeanMessageInterval = 0x7F;
control = MESSAGE_OTHER;
messageType = PATH_DELAY_FOLLOWUP_MESSAGE;
versionPTP = GPTP_VERSION;
@@ -1437,8 +1448,6 @@ void PTPMessagePathDelayRespFollowUp::processMessage(IEEE1588Port * port)
goto abort;
}
- port->setAsCapable( true );
-
link_delay =
((response_rx_timestamp.seconds_ms * 1LL -
@@ -1493,6 +1502,7 @@ void PTPMessagePathDelayRespFollowUp::processMessage(IEEE1588Port * port)
theirs_elapsed += link_delay;
rate_offset = ((FrequencyRatio) mine_elapsed)/theirs_elapsed;
port->setPeerRateOffset(rate_offset);
+ port->setAsCapable( true );
}
}
port->setLinkDelay( link_delay );
diff --git a/daemons/mrpd/mmrp.c b/daemons/mrpd/mmrp.c
index 53ea2ee0..b087d43d 100644
--- a/daemons/mrpd/mmrp.c
+++ b/daemons/mrpd/mmrp.c
@@ -217,7 +217,9 @@ int mmrp_event(int event, struct mmrp_attribute *rattrib)
mrp_lvatimer_fsm(&(MMRP_db->mrp_db), MRP_EVENT_LVATIMER);
+ MMRP_db->send_empty_LeaveAll_flag = 1;
mmrp_txpdu();
+ MMRP_db->send_empty_LeaveAll_flag = 0;
break;
case MRP_EVENT_RLA:
mrp_jointimer_start(&(MMRP_db->mrp_db));
@@ -749,6 +751,7 @@ mmrp_emit_svcvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
mrpdu_message_t *mrpdu_msg;
unsigned char *mrpdu_msg_ptr = msgbuf;
unsigned char *mrpdu_msg_eof = msgbuf_eof;
+ unsigned int attrib_found_flag = 0;
/* need at least 6 bytes for a single vector */
if (mrpdu_msg_ptr > (mrpdu_msg_eof - 6))
@@ -769,6 +772,7 @@ mmrp_emit_svcvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
continue;
}
+ attrib_found_flag = 1;
if (0 == attrib->applicant.tx) {
attrib = attrib->next;
continue;
@@ -937,6 +941,22 @@ mmrp_emit_svcvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
}
+ /*
+ * If no attributes are declared, send a LeaveAll with an all 0
+ * FirstValue, Number of Values set to 0 and not attribute event.
+ */
+ if ((0 == attrib_found_flag) && MMRP_db->send_empty_LeaveAll_flag) {
+
+ mrpdu_vectorptr->VectorHeader = MRPDU_VECT_NUMVALUES(0) |
+ MRPDU_VECT_LVA(0xFFFF);
+ mrpdu_vectorptr->VectorHeader =
+ htons(mrpdu_vectorptr->VectorHeader);
+
+ mrpdu_msg_ptr =
+ &(mrpdu_vectorptr->FirstValue_VectorEvents[mrpdu_msg->AttributeLength]);
+ mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
+ }
+
if (mrpdu_vectorptr == (mrpdu_vectorattrib_t *) mrpdu_msg->Data) {
*bytes_used = 0;
return 0;
diff --git a/daemons/mrpd/mmrp.h b/daemons/mrpd/mmrp.h
index d67a2714..92fd224c 100644
--- a/daemons/mrpd/mmrp.h
+++ b/daemons/mrpd/mmrp.h
@@ -65,6 +65,7 @@ struct mmrp_attribute {
struct mmrp_database {
struct mrp_database mrp_db;
struct mmrp_attribute *attrib_list;
+ int send_empty_LeaveAll_flag;
};
int mmrp_init(int mmrp_enable);
diff --git a/daemons/mrpd/msrp.c b/daemons/mrpd/msrp.c
index 3405d598..3306e6f1 100644
--- a/daemons/mrpd/msrp.c
+++ b/daemons/mrpd/msrp.c
@@ -283,7 +283,9 @@ int msrp_event(int event, struct msrp_attribute *rattrib)
mrp_lvatimer_fsm(&(MSRP_db->mrp_db), MRP_EVENT_LVATIMER);
+ MSRP_db->send_empty_LeaveAll_flag = 1;
msrp_txpdu();
+ MSRP_db->send_empty_LeaveAll_flag = 0;
break;
case MRP_EVENT_RLA:
mrp_jointimer_start(&(MSRP_db->mrp_db));
@@ -1487,6 +1489,7 @@ msrp_emit_domainvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
mrpdu_message_t *mrpdu_msg;
unsigned char *mrpdu_msg_ptr = msgbuf;
unsigned char *mrpdu_msg_eof = msgbuf_eof;
+ unsigned int attrib_found_flag = 0;
/* need at least 5 bytes for a single vector */
if (mrpdu_msg_ptr > (mrpdu_msg_eof - 5))
@@ -1507,6 +1510,7 @@ msrp_emit_domainvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
continue;
}
+ attrib_found_flag = 1;
if (0 == attrib->applicant.tx) {
attrib = attrib->next;
continue;
@@ -1690,6 +1694,22 @@ msrp_emit_domainvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
}
+ /*
+ * If no attributes are declared, send a LeaveAll with an all 0
+ * FirstValue, Number of Values set to 0 and not attribute event.
+ */
+ if ((0 == attrib_found_flag) && MSRP_db->send_empty_LeaveAll_flag) {
+
+ mrpdu_vectorptr->VectorHeader = MRPDU_VECT_NUMVALUES(0) |
+ MRPDU_VECT_LVA(0xFFFF);
+ mrpdu_vectorptr->VectorHeader =
+ htons(mrpdu_vectorptr->VectorHeader);
+
+ mrpdu_msg_ptr =
+ &(mrpdu_vectorptr->FirstValue_VectorEvents[mrpdu_msg->AttributeLength]);
+ mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
+ }
+
if (mrpdu_vectorptr == (mrpdu_vectorattrib_t *) & (mrpdu_msg->Data[2])) {
*bytes_used = 0;
return 0;
@@ -1721,8 +1741,8 @@ msrp_emit_domainvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
}
int
-msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
- int *bytes_used, int lva)
+msrp_emit_talkervectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
+ int *bytes_used, int lva, unsigned int type)
{
mrpdu_vectorattrib_t *mrpdu_vectorptr;
uint16_t numvalues;
@@ -1738,9 +1758,21 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
unsigned char *mrpdu_msg_ptr = msgbuf;
unsigned char *mrpdu_msg_eof = msgbuf_eof;
int mac_eq;
+ unsigned int attrib_found_flag = 0;
+ unsigned int vector_size = 0;
+ unsigned int attrib_len = 0;
+
+ /* MSRP_TALKER_ADV_TYPE */
+ vector_size = 28;
+ attrib_len = 25;
+ if (MSRP_TALKER_FAILED_TYPE == type) {
+ /* talker failed */
+ vector_size += 9;
+ attrib_len += 9;
+ }
/* need at least 28 bytes for a single vector */
- if (mrpdu_msg_ptr > (mrpdu_msg_eof - 28))
+ if (mrpdu_msg_ptr > (mrpdu_msg_eof - vector_size))
goto oops;
/*
@@ -1751,8 +1783,8 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
* not those we know about as a listener ...
*/
mrpdu_msg = (mrpdu_message_t *) mrpdu_msg_ptr;
- mrpdu_msg->AttributeType = MSRP_TALKER_ADV_TYPE;
- mrpdu_msg->AttributeLength = 25;
+ mrpdu_msg->AttributeType = type;
+ mrpdu_msg->AttributeLength = attrib_len;
attrib = MSRP_db->attrib_list;
@@ -1760,7 +1792,7 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
while ((mrpdu_msg_ptr < (mrpdu_msg_eof - 2)) && (NULL != attrib)) {
- if (MSRP_TALKER_ADV_TYPE != attrib->type) {
+ if (type != attrib->type) {
attrib = attrib->next;
continue;
}
@@ -1770,6 +1802,7 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
continue;
}
#endif
+ attrib_found_flag = 1;
if (0 == attrib->applicant.tx) {
attrib = attrib->next;
continue;
@@ -1821,6 +1854,13 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
mrpdu_vectorptr->FirstValue_VectorEvents[24] =
attrib->attribute.talk_listen.AccumulatedLatency;
+ if (MSRP_TALKER_FAILED_TYPE == type) {
+ memcpy(&mrpdu_vectorptr->FirstValue_VectorEvents[25],
+ attrib->attribute.talk_listen.FailureInformation.BridgeID, 8);
+ mrpdu_vectorptr->FirstValue_VectorEvents[33] =
+ attrib->attribute.talk_listen.FailureInformation.FailureCode;
+ }
+
switch (attrib->applicant.sndmsg) {
case MRP_SND_IN:
/*
@@ -1866,11 +1906,11 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
* which also need to be transmitted
*/
- vectidx = 25;
+ vectidx = attrib_len;
vattrib = attrib->next;
while (NULL != vattrib) {
- if (MSRP_TALKER_ADV_TYPE != vattrib->type)
+ if (type != vattrib->type)
break;
if (0 == vattrib->applicant.tx)
@@ -1978,6 +2018,22 @@ msrp_emit_talkvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
}
+ /*
+ * If no attributes are declared, send a LeaveAll with an all 0
+ * FirstValue, Number of Values set to 0 and not attribute event.
+ */
+ if ((0 == attrib_found_flag) && MSRP_db->send_empty_LeaveAll_flag) {
+
+ mrpdu_vectorptr->VectorHeader = MRPDU_VECT_NUMVALUES(0) |
+ MRPDU_VECT_LVA(0xFFFF);
+ mrpdu_vectorptr->VectorHeader =
+ htons(mrpdu_vectorptr->VectorHeader);
+
+ mrpdu_msg_ptr =
+ &(mrpdu_vectorptr->FirstValue_VectorEvents[mrpdu_msg->AttributeLength]);
+ mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
+ }
+
if (mrpdu_vectorptr == (mrpdu_vectorattrib_t *) & (mrpdu_msg->Data[2])) {
*bytes_used = 0;
return 0;
@@ -2023,6 +2079,7 @@ msrp_emit_listenvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
uint8_t streamid_firstval[8];
struct msrp_attribute *attrib, *vattrib;
int mac_eq;
+ unsigned int attrib_found_flag = 0;
/* need at least 13 bytes for a single vector */
if (mrpdu_msg_ptr > (mrpdu_msg_eof - 13))
@@ -2057,6 +2114,7 @@ msrp_emit_listenvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
continue;
}
+ attrib_found_flag = 1;
if (0 == attrib->applicant.tx) {
attrib = attrib->next;
continue;
@@ -2311,6 +2369,23 @@ msrp_emit_listenvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
}
+ /*
+ * If no attributes are declared, send a LeaveAll with an all 0
+ * FirstValue, Number of Values set to 0 and not attribute event.
+ */
+ if ((0 == attrib_found_flag) && MSRP_db->send_empty_LeaveAll_flag) {
+
+ mrpdu_vectorptr->VectorHeader = MRPDU_VECT_NUMVALUES(0) |
+ MRPDU_VECT_LVA(0xFFFF);
+ mrpdu_vectorptr->VectorHeader =
+ htons(mrpdu_vectorptr->VectorHeader);
+
+ mrpdu_msg_ptr =
+ &(mrpdu_vectorptr->FirstValue_VectorEvents[mrpdu_msg->AttributeLength]);
+ mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
+ }
+
+
if (mrpdu_vectorptr == (mrpdu_vectorattrib_t *) & (mrpdu_msg->Data[2])) {
if (listen_declare)
free(listen_declare);
@@ -2355,6 +2430,7 @@ int msrp_txpdu(void)
msgbuf = (unsigned char *)malloc(MAX_FRAME_SIZE);
if (NULL == msgbuf)
return -1;
+ memset(msgbuf, 0, MAX_FRAME_SIZE);
msgbuf_len = 0;
msgbuf_wrptr = msgbuf;
@@ -2379,7 +2455,13 @@ int msrp_txpdu(void)
MSRP_db->mrp_db.lva.tx = 0;
}
- rc = msrp_emit_talkvectors(mrpdu_msg_ptr, mrpdu_msg_eof, &bytes, lva);
+ rc = msrp_emit_talkervectors(mrpdu_msg_ptr, mrpdu_msg_eof, &bytes, lva, MSRP_TALKER_ADV_TYPE);
+ if (-1 == rc)
+ goto out;
+
+ mrpdu_msg_ptr += bytes;
+
+ rc = msrp_emit_talkervectors(mrpdu_msg_ptr, mrpdu_msg_eof, &bytes, lva, MSRP_TALKER_FAILED_TYPE);
if (-1 == rc)
goto out;
diff --git a/daemons/mrpd/msrp.h b/daemons/mrpd/msrp.h
index 6e370057..6e549075 100644
--- a/daemons/mrpd/msrp.h
+++ b/daemons/mrpd/msrp.h
@@ -153,6 +153,7 @@ struct msrp_attribute {
struct msrp_database {
struct mrp_database mrp_db;
struct msrp_attribute *attrib_list;
+ int send_empty_LeaveAll_flag;
};
int msrp_init(int msrp_enable);
diff --git a/daemons/mrpd/mvrp.c b/daemons/mrpd/mvrp.c
index fdad327c..78803baf 100644
--- a/daemons/mrpd/mvrp.c
+++ b/daemons/mrpd/mvrp.c
@@ -167,7 +167,9 @@ int mvrp_event(int event, struct mvrp_attribute *rattrib)
mrp_lvatimer_fsm(&(MVRP_db->mrp_db), MRP_EVENT_LVATIMER);
+ MVRP_db->send_empty_LeaveAll_flag = 1;
mvrp_txpdu();
+ MVRP_db->send_empty_LeaveAll_flag = 0;
break;
case MRP_EVENT_RLA:
mrp_jointimer_start(&(MVRP_db->mrp_db));
@@ -583,6 +585,7 @@ mvrp_emit_vidvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
uint16_t vid_firstval;
struct mvrp_attribute *attrib, *vattrib;
mrpdu_message_t *mrpdu_msg;
+ unsigned int attrib_found_flag = 0;
unsigned char *mrpdu_msg_ptr = msgbuf;
unsigned char *mrpdu_msg_eof = msgbuf_eof;
@@ -601,6 +604,7 @@ mvrp_emit_vidvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
while ((mrpdu_msg_ptr < (mrpdu_msg_eof - 2)) && (NULL != attrib)) {
+ attrib_found_flag = 1;
if (0 == attrib->applicant.tx) {
attrib = attrib->next;
continue;
@@ -770,6 +774,23 @@ mvrp_emit_vidvectors(unsigned char *msgbuf, unsigned char *msgbuf_eof,
mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
}
+ /*
+ * If no attributes are declared, send a LeaveAll with an all 0
+ * FirstValue, Number of Values set to 0 and not attribute event.
+ */
+ if ((0 == attrib_found_flag) && MVRP_db->send_empty_LeaveAll_flag) {
+
+ mrpdu_vectorptr->VectorHeader = MRPDU_VECT_NUMVALUES(0) |
+ MRPDU_VECT_LVA(0xFFFF);
+ mrpdu_vectorptr->VectorHeader =
+ htons(mrpdu_vectorptr->VectorHeader);
+
+ mrpdu_msg_ptr =
+ &(mrpdu_vectorptr->FirstValue_VectorEvents[mrpdu_msg->AttributeLength]);
+ mrpdu_vectorptr = (mrpdu_vectorattrib_t *) mrpdu_msg_ptr;
+ }
+
+
if (mrpdu_vectorptr == (mrpdu_vectorattrib_t *) mrpdu_msg->Data) {
*bytes_used = 0;
return 0;
@@ -804,6 +825,7 @@ int mvrp_txpdu(void)
msgbuf = (unsigned char *)malloc(MAX_FRAME_SIZE);
if (NULL == msgbuf)
return -1;
+ memset(msgbuf, 0, MAX_FRAME_SIZE);
msgbuf_len = 0;
msgbuf_wrptr = msgbuf;
diff --git a/daemons/mrpd/mvrp.h b/daemons/mrpd/mvrp.h
index 1096adb8..41cdcbff 100644
--- a/daemons/mrpd/mvrp.h
+++ b/daemons/mrpd/mvrp.h
@@ -40,8 +40,9 @@ struct mvrp_attribute {
};
struct mvrp_database {
- struct mrp_database mrp_db;
- struct mvrp_attribute *attrib_list;
+ struct mrp_database mrp_db;
+ struct mvrp_attribute *attrib_list;
+ int send_empty_LeaveAll_flag;
};
#define MVRP_ETYPE 0x88F5
diff --git a/examples/jackd-listener/Makefile b/examples/jackd-listener/Makefile
new file mode 100644
index 00000000..9ca00c40
--- /dev/null
+++ b/examples/jackd-listener/Makefile
@@ -0,0 +1,5 @@
+CC = gcc
+LDLIBS = -std=gnu99 -lpcap -lsndfile -pthread -ljack
+
+all: jack_listener.c
+ $(CC) -o jack_listener jack_listener.c $(LDLIBS) $(LDFLAGS)
diff --git a/examples/jackd-listener/jack_listener.c b/examples/jackd-listener/jack_listener.c
new file mode 100644
index 00000000..495ba09c
--- /dev/null
+++ b/examples/jackd-listener/jack_listener.c
@@ -0,0 +1,535 @@
+/*
+Copyright (c) 2013 Katja Rohloff <katja.rohloff@uni-jena.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <pcap/pcap.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <jack/weakmacros.h>
+#include <jack/thread.h>
+
+#include <sndfile.h>
+
+#define LIBSND
+
+#define MRPD_PORT_DEFAULT 7500
+
+#define ETHERNET_HEADER_SIZE 18
+#define SEVENTEEN22_HEADER_PART1_SIZE 4
+#define STREAM_ID_SIZE 8
+#define SEVENTEEN22_HEADER_PART2_SIZE 10
+#define SIX1883_HEADER_SIZE 10
+#define HEADER_SIZE ETHERNET_HEADER_SIZE + SEVENTEEN22_HEADER_PART1_SIZE + STREAM_ID_SIZE + SEVENTEEN22_HEADER_PART2_SIZE + SIX1883_HEADER_SIZE
+
+#define SAMPLES_PER_SECOND 48000
+#define SAMPLES_PER_FRAME 6
+#define CHANNELS 2
+#define SAMPLE_SIZE 4
+
+#define DEFAULT_RINGBUFFER_SIZE 32768
+
+#define MAX_SAMPLE_VALUE ((1U << ((sizeof(int32_t)*8)-1))-1)
+
+struct ethernet_header{
+ u_char dst[6];
+ u_char src[6];
+ u_char stuff[4];
+ u_char type[2];
+};
+
+// global
+unsigned char stream_id[STREAM_ID_SIZE];
+volatile int talker = 0;
+int control_socket;
+pcap_t* handle;
+u_char ETHER_TYPE[] = { 0x22, 0xf0 };
+SNDFILE* snd_file;
+
+static jack_port_t** outputports;
+static jack_default_audio_sample_t** out;
+jack_ringbuffer_t* ringbuffer;
+
+jack_client_t* client;
+
+volatile int ready = 0;
+
+static void help()
+{
+ fprintf(stderr, "\n"
+ "Usage: listener [-h] -i interface -f file_name.wav"
+ "\n"
+ "Options:\n"
+ " -h show this message\n"
+ " -i specify interface for AVB connection\n"
+ "\n" "%s" "\n");
+ exit(1);
+}
+
+int send_msg(char *data, int data_len)
+{
+ int rc;
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(MRPD_PORT_DEFAULT);
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ inet_aton("127.0.0.1", &addr.sin_addr);
+
+ rc = sendto(control_socket, data, data_len, 0, (struct sockaddr*)&addr, (socklen_t)sizeof(addr));
+ return rc;
+}
+
+int send_process(char type)
+{
+ int rc;
+ char* msgbuf = malloc(1500);
+
+ if (NULL == msgbuf) {
+ return -1;
+ }
+
+ memset(msgbuf, 0, 1500);
+
+ switch(type) {
+ case 'M':
+ sprintf(msgbuf, "BYE");
+ break;
+ case 'D':
+ sprintf(msgbuf, "S+D:C=6,P=3,V=0002");
+ break;
+ case 'R':
+ sprintf(msgbuf, "S+L:L=%02x%02x%02x%02x%02x%02x%02x%02x, D=2",
+ stream_id[0], stream_id[1],
+ stream_id[2], stream_id[3],
+ stream_id[4], stream_id[5],
+ stream_id[6], stream_id[7]);
+ break;
+ case 'L':
+ sprintf(msgbuf, "S-L:L=%02x%02x%02x%02x%02x%02x%02x%02x, D=3",
+ stream_id[0], stream_id[1],
+ stream_id[2], stream_id[3],
+ stream_id[4], stream_id[5],
+ stream_id[6], stream_id[7]);
+ break;
+ default:
+ return -1;
+ }
+
+ rc = send_msg(msgbuf, 1500);
+
+ free(msgbuf);
+ return rc;
+}
+
+int create_socket()
+{
+ struct sockaddr_in addr;
+
+ control_socket = socket(AF_INET, SOCK_DGRAM, 0);
+
+ /** POSIX: fd 0,1,2 are reserved */
+ if (2 > control_socket) {
+ close(control_socket);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(0);
+
+ if(0 > (bind(control_socket, (struct sockaddr*)&addr, sizeof(addr)))) {
+ fprintf(stderr, "Could not bind socket.\n");
+ close(control_socket);
+ return -1;
+ }
+
+ return 0;
+}
+
+int recvmsg_process(char *buf, int buflen)
+{
+ uint32_t id;
+ int l = 0;
+
+ if ('S' == buf[l++] && 'N' == buf[l++] && 'E' == buf[l++] && 'T' == buf[++l]) {
+
+ while ('S' != buf[l++]);
+ l++;
+
+ for(int j = 0; j < 8 ; l+=2, j++) {
+ sscanf(&buf[l],"%02x",&id);
+ stream_id[j] = (unsigned char)id;
+ }
+ talker = 1;
+ }
+
+ return 0;
+}
+
+int recv_msg()
+{
+ int bytes = 0;
+ char* msgbuf = malloc(2000);
+
+ if (NULL == msgbuf) {
+ return -1;
+ }
+
+ memset(msgbuf, 0, 2000);
+
+ bytes = recv(control_socket, msgbuf, 2000, 0);
+
+ if (bytes <= -1){
+ free(msgbuf);
+ return -1;
+ }
+
+ return recvmsg_process(msgbuf, bytes);
+}
+
+int await_talker()
+{
+ while (0 == talker) {
+ recv_msg();
+ }
+
+ return 0;
+}
+
+void shutdown_all(int sig)
+{
+ fprintf(stdout,"Leaving...\n");
+
+ if (0 != talker) {
+ send_process('L'); /** send leave */
+ }
+
+ send_process('M'); /** mrp disconnect */
+ close(control_socket);
+
+ if (NULL != handle) {
+ pcap_breakloop(handle);
+ pcap_close(handle);
+ }
+
+#ifdef LIBSND
+ if (NULL != snd_file) {
+ sf_write_sync(snd_file);
+ sf_close(snd_file);
+ }
+#endif
+
+ if (NULL != client) {
+ fprintf(stdout, "jack\n");
+ jack_client_close(client);
+ jack_ringbuffer_free(ringbuffer);
+ }
+
+ exit(0);
+}
+
+void pcap_callback(u_char* args, const struct pcap_pkthdr* packet_header, const u_char* packet)
+{
+ unsigned char* test_stream_id;
+ struct ethernet_header* eth_header;
+
+ uint32_t* mybuf;
+ uint32_t frame[CHANNELS];
+ jack_default_audio_sample_t jackframe[CHANNELS];
+
+ int cnt;
+ static int total;
+
+ eth_header = (struct ethernet_header*)(packet);
+
+ if (0 != memcmp(ETHER_TYPE,eth_header->type,sizeof(eth_header->type))) {
+ return;
+ }
+
+ test_stream_id = (unsigned char*)(packet + ETHERNET_HEADER_SIZE + SEVENTEEN22_HEADER_PART1_SIZE);
+ if (0 != memcmp(test_stream_id, stream_id, STREAM_ID_SIZE)) {
+ return;
+ }
+
+ mybuf = (uint32_t*) (packet + HEADER_SIZE);
+
+ for(int i = 0; i < SAMPLES_PER_FRAME * CHANNELS; i+=CHANNELS) {
+
+ memcpy(&frame[0], &mybuf[i], sizeof(frame));
+
+ for(int j = 0; j < CHANNELS; j++) {
+
+ frame[j] = ntohl(frame[j]); /* convert to host-byte order */
+ frame[j] &= 0x00ffffff; /* ignore leading label */
+ frame[j] <<= 8; /* left-align remaining PCM-24 sample */
+
+ jackframe[j] = ((int32_t)frame[j])/(float)(MAX_SAMPLE_VALUE);
+ }
+
+ if ((cnt = jack_ringbuffer_write_space(ringbuffer)) >= SAMPLE_SIZE * CHANNELS) {
+ jack_ringbuffer_write(ringbuffer, (void*)&jackframe[0], SAMPLE_SIZE * CHANNELS);
+
+ } else {
+ fprintf(stdout, "Only %i bytes available after %i samples.\n", cnt, total);
+ }
+
+ if (jack_ringbuffer_write_space(ringbuffer) <= SAMPLE_SIZE * CHANNELS * DEFAULT_RINGBUFFER_SIZE / 4) {
+ /** Ringbuffer has only 25% or less write space available, it's time to tell jackd
+ to read some data. */
+ ready = 1;
+ }
+
+#ifdef LIBSND
+ sf_writef_float(snd_file, jackframe, 1);
+#endif
+ }
+}
+
+static int process_jack(jack_nframes_t nframes, void* arg)
+{
+ int cnt;
+
+ if (!ready) {
+ return 0;
+ }
+
+ for(int i = 0; i < CHANNELS; i++) {
+ out[i] = jack_port_get_buffer(outputports[i], nframes);
+ }
+
+ for(size_t i = 0; i < nframes; i++) {
+
+ if (jack_ringbuffer_read_space(ringbuffer) >= SAMPLE_SIZE * CHANNELS) {
+
+ for(int j = 0; j < CHANNELS; j++){
+ jack_ringbuffer_read (ringbuffer, (char*)(out[j]+i), SAMPLE_SIZE);
+ }
+
+ } else {
+ printf ("underrun\n");
+ ready = 0;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+void jack_shutdown(void* arg)
+{
+ printf("JACK shutdown\n");
+ shutdown_all(0);
+}
+
+jack_client_t* init_jack(void)
+{
+ const char* client_name = "simple_listener";
+ const char* server_name = NULL;
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+
+ client = jack_client_open (client_name, options, &status, server_name);
+
+ if (NULL == client) {
+ fprintf (stderr, "jack_client_open() failed\n ");
+ shutdown_all(0);
+ exit (1);
+ }
+
+ if (status & JackServerStarted) {
+ fprintf (stderr, "JACK server started\n");
+ }
+
+ if (status & JackNameNotUnique) {
+ client_name = jack_get_client_name(client);
+ fprintf (stderr, "unique name `%s' assigned\n", client_name);
+ }
+
+ jack_set_process_callback(client, process_jack, 0);
+ jack_on_shutdown(client, jack_shutdown, 0);
+
+ outputports = (jack_port_t**) malloc (CHANNELS * sizeof (jack_port_t*));
+ out = (jack_default_audio_sample_t**) malloc (CHANNELS * sizeof (jack_default_audio_sample_t*));
+ ringbuffer = jack_ringbuffer_create (SAMPLE_SIZE * DEFAULT_RINGBUFFER_SIZE * CHANNELS);
+ jack_ringbuffer_mlock(ringbuffer);
+
+ memset(out, 0, sizeof (jack_default_audio_sample_t*)*CHANNELS);
+ memset(ringbuffer->buf, 0, ringbuffer->size);
+
+ for(int i = 0; i < CHANNELS; i++) {
+
+ char* portName;
+ if (asprintf(&portName, "output%d", i) < 0) {
+ fprintf(stderr, "could not create portname for port %d\n", i);
+ shutdown_all(0);
+ exit(1);
+ }
+
+ outputports[i] = jack_port_register (client, portName, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (NULL == outputports[i]) {
+ fprintf (stderr, "cannot register output port \"%d\"!\n", i);
+ shutdown_all(0);
+ exit (1);
+ }
+ }
+
+ const char** ports;
+ if (jack_activate (client)) {
+ fprintf (stderr, "cannot activate client\n");
+ shutdown_all(0);
+ exit(1);
+ }
+
+ ports = jack_get_ports(client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
+ if(NULL == ports) {
+ fprintf (stderr, "no physical playback ports\n");
+ shutdown_all(0);
+ exit(1);
+ }
+
+ int i = 0;
+ while(i < CHANNELS && NULL != ports[i]) {
+ if (jack_connect(client, jack_port_name(outputports[i]), ports[i]))
+ fprintf (stderr, "cannot connect output ports\n");
+ i++;
+ }
+
+ free(ports);
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ char* dev = NULL;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct bpf_program comp_filter_exp; /** The compiled filter expression */
+ char filter_exp[] = "ether dst 91:E0:F0:00:0e:80"; /** The filter expression */
+ struct pcap_pkthdr header; /** header pcap gives us */
+ const u_char* packet; /** actual packet */
+
+ signal(SIGINT, shutdown_all);
+
+ int c;
+ while((c = getopt(argc, argv, "hi:")) > 0)
+ {
+ switch (c)
+ {
+ case 'h':
+ help();
+ break;
+ case 'i':
+ dev = strdup(optarg);
+ break;
+ default:
+ fprintf(stderr, "Unrecognized option!\n");
+ }
+ }
+
+ if (NULL == dev) {
+ help();
+ }
+
+ if (create_socket()) {
+ fprintf(stderr, "Socket creation failed.\n");
+ return (errno);
+ }
+
+ send_process('D'); /** report domain status */
+
+ init_jack();
+
+ fprintf(stdout,"Waiting for talker...\n");
+ await_talker();
+
+ send_process('R'); /** send_ready */
+
+#ifdef LIBSND
+ char* filename = "listener.wav";
+ SF_INFO* sf_info = (SF_INFO*)malloc(sizeof(SF_INFO));
+
+ memset(sf_info, 0, sizeof(SF_INFO));
+
+ sf_info->samplerate = SAMPLES_PER_SECOND;
+ sf_info->channels = CHANNELS;
+ sf_info->format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
+
+ if (0 == sf_format_check(sf_info)) {
+ fprintf(stderr, "Wrong format.\n");
+ shutdown_all(0);
+ return -1;
+ }
+
+ if (NULL == (snd_file = sf_open(filename, SFM_WRITE, sf_info))) {
+ fprintf(stderr, "Could not create file %s.\n", filename);
+ shutdown_all(0);
+ return -1;
+ }
+#endif
+
+ /** session, get session handler */
+ handle = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf);
+ if (NULL == handle) {
+ fprintf(stderr, "Could not open device %s: %s.\n", dev, errbuf);
+ shutdown_all(0);
+ return -1;
+ }
+
+ /** compile and apply filter */
+ if (-1 == pcap_compile(handle, &comp_filter_exp, filter_exp, 0, PCAP_NETMASK_UNKNOWN)) {
+ fprintf(stderr, "Could not parse filter %s: %s.\n", filter_exp, pcap_geterr(handle));
+ shutdown_all(0);
+ return -1;
+ }
+
+ if (-1 == pcap_setfilter(handle, &comp_filter_exp)) {
+ fprintf(stderr, "Could not install filter %s: %s.\n", filter_exp, pcap_geterr(handle));
+ shutdown_all(0);
+ return -1;
+ }
+
+ /** loop forever and call callback-function for every received packet */
+ pcap_loop(handle, -1, pcap_callback, NULL);
+
+ usleep(-1);
+ return 0;
+}
diff --git a/examples/jackd-talker/Makefile b/examples/jackd-talker/Makefile
new file mode 100644
index 00000000..16d27934
--- /dev/null
+++ b/examples/jackd-talker/Makefile
@@ -0,0 +1,24 @@
+OPT=-O2 -g
+CFLAGS=$(OPT) -Wall -W -Wno-parentheses -std=gnu99
+
+CC=gcc
+INCFLAGS=-I../../lib/igb
+LDLIBS=-ligb -lpci -lz -lrt -lm -pthread -ljack -lsndfile
+LDFLAGS=-L../../lib/igb
+
+all: jackd_talker
+
+jackd_talker: jackd_talker.o jack.o
+
+jack.o: jack.c jack.h defines.h
+ $(CC) $(CFLAGS) -c jack.c
+
+jackd_talker.o: jackd_talker.c defines.h jack.h
+ gcc -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) jackd_talker.c
+
+%: %.o
+ $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+clean:
+ rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"` jackd_talker
+
diff --git a/examples/jackd-talker/defines.h b/examples/jackd-talker/defines.h
new file mode 100644
index 00000000..9e29c66a
--- /dev/null
+++ b/examples/jackd-talker/defines.h
@@ -0,0 +1,9 @@
+#ifndef _DEFINES_H
+#define _DEFINES_H
+
+
+#define SAMPLES_PER_FRAME 6
+#define SAMPLE_SIZE 4
+#define CHANNELS 2
+
+#endif /* _DEFINES_H */
diff --git a/examples/jackd-talker/jack.c b/examples/jackd-talker/jack.c
new file mode 100644
index 00000000..1c1eb0f5
--- /dev/null
+++ b/examples/jackd-talker/jack.c
@@ -0,0 +1,133 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include "jack.h"
+#include "defines.h"
+
+extern volatile int halt_tx;
+extern volatile int listeners;
+extern volatile int unleash_jack;
+
+
+static jack_port_t** inputports;
+static jack_default_audio_sample_t** in;
+jack_ringbuffer_t* ringbuffer;
+
+pthread_mutex_t threadLock = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t dataReady = PTHREAD_COND_INITIALIZER;
+
+void stop_jack(jack_client_t* client)
+{
+ jack_client_close(client);
+ jack_ringbuffer_free(ringbuffer);
+}
+
+static int process(jack_nframes_t nframes, void* arg)
+{
+ int cnt;
+ static int total;
+
+ /* Do nothing until we're ready to begin. */
+ if (!unleash_jack) {
+ printf ("nothing to do\n");
+ return 0;
+ }
+
+ for(int i = 0; i < CHANNELS; i++) {
+ in[i] = jack_port_get_buffer(inputports[i], nframes);
+ }
+
+ for (size_t i = 0; i < nframes; i++) {
+ for(int j = 0; j < CHANNELS; j++) {
+ total++;
+ if ((cnt = jack_ringbuffer_write_space(ringbuffer)) >= SAMPLE_SIZE) {
+ jack_ringbuffer_write(ringbuffer,
+ (void*) (in[j]+i), SAMPLE_SIZE);
+ if (total % 5000 == 0) {
+ printf ("Available writespace: %i\n", cnt);
+ }
+ } else {
+ printf ("Only %i bytes available after %i samples\n",
+ cnt, total);
+ halt_tx = 1;
+ }
+ }
+ }
+
+ if (0 == pthread_mutex_trylock(&threadLock))
+ {
+ pthread_cond_signal(&dataReady);
+ pthread_mutex_unlock(&threadLock);
+ }
+
+ return 0;
+}
+
+void jack_shutdown(void* arg)
+{
+ printf("JACK shutdown\n");
+ halt_tx = 1;
+}
+
+jack_client_t* init_jack(void)
+{
+ jack_client_t *client;
+ const char *client_name = "simple_talker";
+ const char *server_name = NULL;
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+
+ client = jack_client_open (client_name, options, &status, server_name);
+
+ if (NULL == client) {
+ fprintf (stderr, "jack_client_open() failed\n ");
+ exit (1);
+ }
+ if (status & JackServerStarted) {
+ fprintf (stderr, "JACK server started\n");
+ }
+ if (status & JackNameNotUnique) {
+ client_name = jack_get_client_name(client);
+ fprintf (stderr, "unique name `%s' assigned\n", client_name);
+ }
+
+ jack_set_process_callback(client, process, 0);
+ jack_on_shutdown(client, jack_shutdown, 0);
+
+ if (jack_activate (client))
+ fprintf (stderr, "cannot activate client");
+
+ inputports = (jack_port_t**) malloc (CHANNELS * sizeof (jack_port_t*));
+ in = (jack_default_audio_sample_t**) malloc (CHANNELS * sizeof (jack_default_audio_sample_t*));
+ ringbuffer = jack_ringbuffer_create (SAMPLE_SIZE * DEFAULT_RINGBUFFER_SIZE * CHANNELS);
+ jack_ringbuffer_mlock(ringbuffer);
+
+ memset(in, 0, sizeof (jack_default_audio_sample_t*)*CHANNELS);
+ memset(ringbuffer->buf, 0, ringbuffer->size);
+
+ for(int i = 0; i < CHANNELS; i++)
+ {
+ char* portName;
+ if (asprintf(&portName, "input%d", i) < 0)
+ {
+ fprintf(stderr, "Could not create portname for port %d", i);
+ exit(1);
+ }
+
+ inputports[i] = jack_port_register (client, portName,
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+ if (NULL == inputports[i])
+ {
+ fprintf (stderr, "cannot register input port \"%d\"!\n", i);
+ jack_client_close (client);
+ exit (1);
+ }
+ }
+
+ return client;
+}
diff --git a/examples/jackd-talker/jack.h b/examples/jackd-talker/jack.h
new file mode 100644
index 00000000..c78dc1ab
--- /dev/null
+++ b/examples/jackd-talker/jack.h
@@ -0,0 +1,10 @@
+#ifndef _AVB_JACK_H
+#define _AVB_JACK_H
+
+#define DEFAULT_RINGBUFFER_SIZE 32768
+
+/* Prototypes */
+jack_client_t* init_jack(void);
+void stop_jack(jack_client_t* client);
+
+#endif /* _AVB_JACK_H */
diff --git a/examples/jackd-talker/jackd_talker.c b/examples/jackd-talker/jackd_talker.c
new file mode 100755
index 00000000..9fb83660
--- /dev/null
+++ b/examples/jackd-talker/jackd_talker.c
@@ -0,0 +1,1155 @@
+/******************************************************************************
+
+ Copyright (c) 2012, Intel Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <pci/pci.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <netpacket/packet.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/ethernet.h>
+#include <sys/un.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "igb.h"
+#include "mrpd.h"
+#include "mrp.h"
+#include "msrp.h"
+#include <math.h>
+#include <endian.h>
+#include <stdint.h>
+
+#include <jack/weakmacros.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <jack/thread.h>
+#include "jack.h"
+#include "defines.h"
+
+typedef struct {
+ int64_t ml_phoffset;
+ int64_t ls_phoffset;
+ long double ml_freqoffset;
+ long double ls_freqoffset;
+ int64_t local_time;
+} gPtpTimeData;
+
+
+#define SHM_SIZE 4*8 + sizeof(pthread_mutex_t) /* 3 - 64 bit and 2 - 32 bits */
+#define SHM_NAME "/ptp"
+
+#define MAX_SAMPLE_VALUE ((1U << ((sizeof(int32_t)*8)-1))-1)
+
+#define SRC_CHANNELS (2)
+#define SAMPLES_PER_SECOND (48000)
+#define FREQUENCY (480)
+#define SAMPLES_PER_CYCLE (SAMPLES_PER_SECOND/FREQUENCY)
+#define GAIN (.5)
+
+//1722 header
+#define ETH_TYPE 0x22F0
+
+#define CD_SUBTYPE 0x02 /* for simple audio format */
+#define SV_VER_MR_RS_GV_TV 0x81
+#define RS_TU 0x00
+#define SAMPLE_FORMAT_NON_INTR_FLOAT 0x02
+#define NOMINAL_SAMPLE_RATE 0x09
+#define LINEAR_SAMPLE_MSB 0x20
+unsigned char GATEWAY_INFO[] =
+ { SAMPLE_FORMAT_NON_INTR_FLOAT, 0, NOMINAL_SAMPLE_RATE, LINEAR_SAMPLE_MSB };
+
+#define PAYLOAD_SIZE SAMPLE_SIZE*SAMPLES_PER_FRAME*CHANNELS /* 6*4 * 2 channels = 48 bytes */
+
+#define IGB_BIND_NAMESZ 24
+
+#define XMIT_DELAY (200000000) /* us */
+#define RENDER_DELAY (XMIT_DELAY+2000000) /* us */
+typedef enum { false = 0, true = 1 } bool;
+typedef struct __attribute__ ((packed)) {
+ uint64_t subtype:7;
+ uint64_t cd_indicator:1;
+ uint64_t timestamp_valid:1;
+ uint64_t gateway_valid:1;
+ uint64_t reserved0:1;
+ uint64_t reset:1;
+ uint64_t version:3;
+ uint64_t sid_valid:1;
+ uint64_t seq_number:8;
+ uint64_t timestamp_uncertain:1;
+ uint64_t reserved1:7;
+ uint64_t stream_id;
+ uint64_t timestamp:32;
+ uint64_t gateway_info:32;
+ uint64_t length:16;
+} seventeen22_header;
+
+/* 61883 CIP with SYT Field */
+typedef struct {
+ uint16_t packet_channel:6;
+ uint16_t format_tag:2;
+ uint16_t app_control:4;
+ uint16_t packet_tcode:4;
+ uint16_t source_id:6;
+ uint16_t reserved0:2;
+ uint16_t data_block_size:8;
+ uint16_t reserved1:2;
+ uint16_t source_packet_header:1;
+ uint16_t quadlet_padding_count:3;
+ uint16_t fraction_number:2;
+ uint16_t data_block_continuity:8;
+ uint16_t format_id:6;
+ uint16_t eoh:2;
+ uint16_t format_dependent_field:8;
+ uint16_t syt;
+} six1883_header;
+
+typedef struct {
+ uint8_t label;
+ uint8_t value[3];
+} six1883_sample;
+
+/* global variables */
+int control_socket = -1;
+device_t igb_dev;
+volatile int halt_tx = 0;
+volatile int listeners = 0;
+volatile int unleash_jack = 0;
+volatile int mrp_okay;
+volatile int mrp_error = 0;;
+volatile int domain_a_valid = 0;
+int domain_class_a_id;
+int domain_class_a_priority;
+int domain_class_a_vid;
+volatile int domain_b_valid = 0;
+int domain_class_b_id;
+int domain_class_b_priority;
+int domain_class_b_vid;
+
+pthread_t packetizer_id;
+
+/* evil global variables
+ * FIXME: move to thread argument struct */
+u_int64_t last_time;
+int seqnum;
+uint32_t time_stamp;
+seventeen22_header *header0;
+six1883_header *header1;
+struct igb_packet *tmp_packet;
+struct igb_packet *free_packets;
+
+
+#define VERSION_STR "1.0"
+static const char *version_str = "simple_talker v" VERSION_STR "\n"
+ "Copyright (c) 2012, Intel Corporation\n";
+
+#define MRPD_PORT_DEFAULT 7500
+static inline uint64_t ST_rdtsc(void)
+{
+ uint64_t ret;
+ unsigned c, d;
+ asm volatile ("rdtsc":"=a" (c), "=d"(d));
+ ret = d;
+ ret <<= 32;
+ ret |= c;
+ return ret;
+}
+
+static int shm_fd = -1;
+static char *memory_offset_buffer = NULL;
+int gptpinit(void)
+{
+ shm_fd = shm_open(SHM_NAME, O_RDWR, 0);
+ if (shm_fd == -1) {
+ perror("shm_open()");
+ return false;
+ }
+ memory_offset_buffer =
+ (char *)mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+ shm_fd, 0);
+ if (memory_offset_buffer == (char *)-1) {
+ perror("mmap()");
+ memory_offset_buffer = NULL;
+ shm_unlink(SHM_NAME);
+ return false;
+ }
+ return true;
+}
+
+void gptpdeinit(void)
+{
+ if (memory_offset_buffer != NULL) {
+ munmap(memory_offset_buffer, SHM_SIZE);
+ }
+ if (shm_fd != -1) {
+ close(shm_fd);
+ }
+}
+
+int gptpscaling(gPtpTimeData * td)
+{
+ pthread_mutex_lock((pthread_mutex_t *) memory_offset_buffer);
+ memcpy(td, memory_offset_buffer + sizeof(pthread_mutex_t), sizeof(*td));
+ pthread_mutex_unlock((pthread_mutex_t *) memory_offset_buffer);
+
+ fprintf(stderr, "ml_phoffset = %lld, ls_phoffset = %lld\n",
+ td->ml_phoffset, td->ls_phoffset);
+ fprintf(stderr, "ml_freqffset = %Lf, ls_freqoffset = %Lf\n",
+ td->ml_freqoffset, td->ls_freqoffset);
+
+ return true;
+}
+
+int mrp_join_listener(uint8_t * streamid);
+int send_mrp_msg(char *notify_data, int notify_len)
+{
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(MRPD_PORT_DEFAULT);
+ inet_aton("127.0.0.1", &addr.sin_addr);
+ addr_len = sizeof(addr);
+ if (control_socket != -1)
+ return (sendto
+ (control_socket, notify_data, notify_len, 0,
+ (struct sockaddr *)&addr, addr_len));
+
+ else
+ return (0);
+}
+
+int mrp_connect()
+{
+ struct sockaddr_in addr;
+ int sock_fd = -1;
+ sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock_fd < 0)
+ goto out;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(MRPD_PORT_DEFAULT);
+ inet_aton("127.0.0.1", &addr.sin_addr);
+ memset(&addr, 0, sizeof(addr));
+ control_socket = sock_fd;
+ return (0);
+ out: if (sock_fd != -1)
+ close(sock_fd);
+ sock_fd = -1;
+ return (-1);
+}
+
+int mrp_disconnect()
+{
+ char *msgbuf;
+ int rc;
+ msgbuf = malloc(64);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 64);
+ sprintf(msgbuf, "BYE");
+ mrp_okay = 0;
+ rc = send_mrp_msg(msgbuf, 1500);
+
+ /* rc = recv_mrp_okay(); */
+ free(msgbuf);
+ return rc;
+}
+
+int recv_mrp_okay()
+{
+ while ((mrp_okay == 0) && (mrp_error == 0))
+ usleep(20000);
+ return 0;
+}
+
+int mrp_register_domain(int *class_id, int *priority, u_int16_t * vid)
+{
+ char *msgbuf;
+ int rc;
+ msgbuf = malloc(64);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 64);
+ sprintf(msgbuf, "S+D:C=%d,P=%d,V=%04x", *class_id, *priority, *vid);
+ mrp_okay = 0;
+ rc = send_mrp_msg(msgbuf, 1500);
+
+ /* rc = recv_mrp_okay(); */
+ free(msgbuf);
+ return rc;
+}
+
+int mrp_get_domain(int *class_a_id, int *a_priority, u_int16_t * a_vid,
+ int *class_b_id, int *b_priority, u_int16_t * b_vid)
+{
+ char *msgbuf;
+
+ /* we may not get a notification if we are joining late,
+ * so query for what is already there ...
+ */
+ msgbuf = malloc(64);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 64);
+ sprintf(msgbuf, "S??");
+ send_mrp_msg(msgbuf, 64);
+ free(msgbuf);
+ while (!halt_tx && (domain_a_valid == 0) && (domain_b_valid == 0))
+ usleep(20000);
+ *class_a_id = 0;
+ *a_priority = 0;
+ *a_vid = 0;
+ *class_b_id = 0;
+ *b_priority = 0;
+ *b_vid = 0;
+ if (domain_a_valid) {
+ *class_a_id = domain_class_a_id;
+ *a_priority = domain_class_a_priority;
+ *a_vid = domain_class_a_vid;
+ }
+ if (domain_b_valid) {
+ *class_b_id = domain_class_b_id;
+ *b_priority = domain_class_b_priority;
+ *b_vid = domain_class_b_vid;
+ }
+ return (0);
+}
+unsigned char monitor_stream_id[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+int mrp_await_listener(unsigned char *streamid)
+{
+ char *msgbuf;
+ memcpy(monitor_stream_id, streamid, sizeof(monitor_stream_id));
+ msgbuf = malloc(64);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 64);
+ sprintf(msgbuf, "S??");
+ send_mrp_msg(msgbuf, 64);
+ free(msgbuf);
+
+ /* either already there ... or need to wait ... */
+ while (!halt_tx && (listeners == 0))
+ usleep(20000);
+ return (0);
+}
+
+int process_mrp_msg(char *buf, int buflen)
+{
+
+ /*
+ * 1st character indicates application
+ * [MVS] - MAC, VLAN or STREAM
+ */
+ unsigned int id;
+ unsigned int priority;
+ unsigned int vid;
+ int i, j, k;
+ unsigned int substate;
+ unsigned char recovered_streamid[8];
+ k = 0;
+ next_line:if (k >= buflen)
+ return (0);
+ switch (buf[k]) {
+ case 'E':
+ printf("%s from mrpd\n", buf);
+ fflush(stdout);
+ mrp_error = 1;
+ break;
+ case 'O':
+ mrp_okay = 1;
+ break;
+ case 'M':
+ case 'V':
+ printf("%s unhandled from mrpd\n", buf);
+ fflush(stdout);
+
+ /* unhandled for now */
+ break;
+ case 'L':
+
+ /* parse a listener attribute - see if it matches our monitor_stream_id */
+ i = k;
+ while (buf[i] != 'D')
+ i++;
+ i += 2; /* skip the ':' */
+ sscanf(&(buf[i]), "%d", &substate);
+ while (buf[i] != 'S')
+ i++;
+ i += 2; /* skip the ':' */
+ for (j = 0; j < 8; j++) {
+ sscanf(&(buf[i + 2 * j]), "%02x", &id);
+ recovered_streamid[j] = (unsigned char)id;
+ } printf
+ ("FOUND STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ",
+ recovered_streamid[0], recovered_streamid[1],
+ recovered_streamid[2], recovered_streamid[3],
+ recovered_streamid[4], recovered_streamid[5],
+ recovered_streamid[6], recovered_streamid[7]);
+ switch (substate) {
+ case 0:
+ printf("with state ignore\n");
+ break;
+ case 1:
+ printf("with state askfailed\n");
+ break;
+ case 2:
+ printf("with state ready\n");
+ break;
+ case 3:
+ printf("with state readyfail\n");
+ break;
+ default:
+ printf("with state UNKNOWN (%d)\n", substate);
+ break;
+ }
+ if (substate > MSRP_LISTENER_ASKFAILED) {
+ if (memcmp
+ (recovered_streamid, monitor_stream_id,
+ sizeof(recovered_streamid)) == 0) {
+ listeners = 1;
+ printf("added listener\n");
+ }
+ }
+ fflush(stdout);
+
+ /* try to find a newline ... */
+ while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0'))
+ i++;
+ if (i == buflen)
+ return (0);
+ if (buf[i] == '\0')
+ return (0);
+ i++;
+ k = i;
+ goto next_line;
+ break;
+ case 'D':
+ i = k + 4;
+
+ /* save the domain attribute */
+ sscanf(&(buf[i]), "%d", &id);
+ while (buf[i] != 'P')
+ i++;
+ i += 2; /* skip the ':' */
+ sscanf(&(buf[i]), "%d", &priority);
+ while (buf[i] != 'V')
+ i++;
+ i += 2; /* skip the ':' */
+ sscanf(&(buf[i]), "%x", &vid);
+ if (id == 6) {
+ domain_class_a_id = id;
+ domain_class_a_priority = priority;
+ domain_class_a_vid = vid;
+ domain_a_valid = 1;
+ } else {
+ domain_class_b_id = id;
+ domain_class_b_priority = priority;
+ domain_class_b_vid = vid;
+ domain_b_valid = 1;
+ }
+ while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0'))
+ i++;
+ if ((i == buflen) || (buf[i] == '\0'))
+ return (0);
+ i++;
+ k = i;
+ goto next_line;
+ break;
+ case 'T':
+
+ /* as simple_talker we don't care about other talkers */
+ i = k;
+ while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0'))
+ i++;
+ if (i == buflen)
+ return (0);
+ if (buf[i] == '\0')
+ return (0);
+ i++;
+ k = i;
+ goto next_line;
+ break;
+ case 'S':
+
+ /* handle the leave/join events */
+ switch (buf[k + 4]) {
+ case 'L':
+ i = k + 5;
+ while (buf[i] != 'D')
+ i++;
+ i += 2; /* skip the ':' */
+ sscanf(&(buf[i]), "%d", &substate);
+ while (buf[i] != 'S')
+ i++;
+ i += 2; /* skip the ':' */
+ for (j = 0; j < 8; j++) {
+ sscanf(&(buf[i + 2 * j]), "%02x", &id);
+ recovered_streamid[j] = (unsigned char)id;
+ } printf
+ ("EVENT on STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ",
+ recovered_streamid[0], recovered_streamid[1],
+ recovered_streamid[2], recovered_streamid[3],
+ recovered_streamid[4], recovered_streamid[5],
+ recovered_streamid[6], recovered_streamid[7]);
+ switch (substate) {
+ case 0:
+ printf("with state ignore\n");
+ break;
+ case 1:
+ printf("with state askfailed\n");
+ break;
+ case 2:
+ printf("with state ready\n");
+ break;
+ case 3:
+ printf("with state readyfail\n");
+ break;
+ default:
+ printf("with state UNKNOWN (%d)\n", substate);
+ break;
+ }
+ switch (buf[k + 1]) {
+ case 'L':
+ printf("got a leave indication\n");
+ if (memcmp
+ (recovered_streamid, monitor_stream_id,
+ sizeof(recovered_streamid)) == 0) {
+ listeners = 0;
+ printf("listener left\n");
+ }
+ break;
+ case 'J':
+ case 'N':
+ printf("got a new/join indication\n");
+ if (substate > MSRP_LISTENER_ASKFAILED) {
+ if (memcmp
+ (recovered_streamid,
+ monitor_stream_id,
+ sizeof(recovered_streamid)) == 0)
+ listeners = 1;
+ }
+ break;
+ }
+
+ /* only care about listeners ... */
+ default:
+ return (0);
+ break;
+ }
+ break;
+ case '\0':
+ break;
+ }
+ return (0);
+}
+
+void *mrp_monitor_thread(void *arg)
+{
+ char *msgbuf;
+ struct sockaddr_in client_addr;
+ struct msghdr msg;
+ struct iovec iov;
+ int bytes = 0;
+ struct pollfd fds;
+ int rc;
+ if (NULL == arg)
+ rc = 0;
+
+ else
+ rc = 1;
+ msgbuf = (char *)malloc(MAX_MRPD_CMDSZ);
+ if (NULL == msgbuf)
+ return NULL;
+ while (!halt_tx) {
+ fds.fd = control_socket;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ rc = poll(&fds, 1, 100);
+ if (rc < 0) {
+ free(msgbuf);
+ pthread_exit(NULL);
+ }
+ if (rc == 0)
+ continue;
+ if ((fds.revents & POLLIN) == 0) {
+ free(msgbuf);
+ pthread_exit(NULL);
+ }
+ memset(&msg, 0, sizeof(msg));
+ memset(&client_addr, 0, sizeof(client_addr));
+ memset(msgbuf, 0, MAX_MRPD_CMDSZ);
+ iov.iov_len = MAX_MRPD_CMDSZ;
+ iov.iov_base = msgbuf;
+ msg.msg_name = &client_addr;
+ msg.msg_namelen = sizeof(client_addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ bytes = recvmsg(control_socket, &msg, 0);
+ if (bytes < 0)
+ continue;
+ process_mrp_msg(msgbuf, bytes);
+ }
+ free(msgbuf);
+ pthread_exit(NULL);
+}
+
+pthread_t monitor_thread;
+pthread_attr_t monitor_attr;
+int mrp_monitor()
+{
+ pthread_attr_init(&monitor_attr);
+ pthread_create(&monitor_thread, NULL, mrp_monitor_thread, NULL);
+ return (0);
+}
+
+int mrp_join_listener(uint8_t * streamid)
+{
+ char *msgbuf;
+ int rc;
+ msgbuf = malloc(1500);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 1500);
+ sprintf(msgbuf, "S+L:S=%02X%02X%02X%02X%02X%02X%02X%02X"
+ ",D=2", streamid[0], streamid[1], streamid[2], streamid[3],
+ streamid[4], streamid[5], streamid[6], streamid[7]);
+ mrp_okay = 0;
+ rc = send_mrp_msg(msgbuf, 1500);
+
+ /* rc = recv_mrp_okay(); */
+ free(msgbuf);
+ return rc;
+}
+
+int
+mrp_advertise_stream(uint8_t * streamid,
+ uint8_t * destaddr,
+ u_int16_t vlan,
+ int pktsz, int interval, int priority, int latency)
+{
+ char *msgbuf;
+ int rc;
+ msgbuf = malloc(1500);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 1500);
+ sprintf(msgbuf, "S++:S=%02X%02X%02X%02X%02X%02X%02X%02X"
+ ",A=%02X%02X%02X%02X%02X%02X"
+ ",V=%04X"
+ ",Z=%d"
+ ",I=%d"
+ ",P=%d"
+ ",L=%d", streamid[0], streamid[1], streamid[2],
+ streamid[3], streamid[4], streamid[5], streamid[6],
+ streamid[7], destaddr[0], destaddr[1], destaddr[2],
+ destaddr[3], destaddr[4], destaddr[5], vlan, pktsz,
+ interval, priority << 5, latency);
+ mrp_okay = 0;
+ rc = send_mrp_msg(msgbuf, 1500);
+
+ /* rc = recv_mrp_okay(); */
+ free(msgbuf);
+ return rc;
+}
+
+int
+mrp_unadvertise_stream(uint8_t * streamid,
+ uint8_t * destaddr,
+ u_int16_t vlan,
+ int pktsz, int interval, int priority, int latency)
+{
+ char *msgbuf;
+ int rc;
+ msgbuf = malloc(1500);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 1500);
+ sprintf(msgbuf, "S--:S=%02X%02X%02X%02X%02X%02X%02X%02X"
+ ",A=%02X%02X%02X%02X%02X%02X"
+ ",V=%04X"
+ ",Z=%d"
+ ",I=%d"
+ ",P=%d"
+ ",L=%d", streamid[0], streamid[1], streamid[2],
+ streamid[3], streamid[4], streamid[5], streamid[6],
+ streamid[7], destaddr[0], destaddr[1], destaddr[2],
+ destaddr[3], destaddr[4], destaddr[5], vlan, pktsz,
+ interval, priority << 5, latency);
+ mrp_okay = 0;
+ rc = send_mrp_msg(msgbuf, 1500);
+
+ /* rc = recv_mrp_okay(); */
+ free(msgbuf);
+ return rc;
+}
+
+void sigint_handler(int signum)
+{
+ printf("got SIGINT\n");
+ halt_tx = signum;
+ unleash_jack = 0;
+}
+
+int pci_connect()
+{
+ struct pci_access *pacc;
+ struct pci_dev *dev;
+ int err;
+ char devpath[IGB_BIND_NAMESZ];
+ memset(&igb_dev, 0, sizeof(device_t));
+ pacc = pci_alloc();
+ pci_init(pacc);
+ pci_scan_bus(pacc);
+ for (dev = pacc->devices; dev; dev = dev->next) {
+ pci_fill_info(dev,
+ PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS);
+ igb_dev.pci_vendor_id = dev->vendor_id;
+ igb_dev.pci_device_id = dev->device_id;
+ igb_dev.domain = dev->domain;
+ igb_dev.bus = dev->bus;
+ igb_dev.dev = dev->dev;
+ igb_dev.func = dev->func;
+ snprintf(devpath, IGB_BIND_NAMESZ, "%04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->dev, dev->func);
+ err = igb_probe(&igb_dev);
+ if (err) {
+ continue;
+ }
+ printf("attaching to %s\n", devpath);
+ err = igb_attach(devpath, &igb_dev);
+ if (err) {
+ printf("attach failed! (%s)\n", strerror(errno));
+ continue;
+ }
+ goto out;
+ }
+ pci_cleanup(pacc);
+ return ENXIO;
+ out: pci_cleanup(pacc);
+ return 0;
+}
+
+unsigned char STATION_ADDR[] = { 0, 0, 0, 0, 0, 0 };
+unsigned char STREAM_ID[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/* IEEE 1722 reserved address */
+unsigned char DEST_ADDR[] = { 0x91, 0xE0, 0xF0, 0x00, 0x0e, 0x80 };
+
+int get_mac_address(char *interface)
+{
+ struct ifreq if_request;
+ int lsock;
+ int rc;
+ lsock = socket(PF_PACKET, SOCK_RAW, htons(0x800));
+ if (lsock < 0)
+ return -1;
+ memset(&if_request, 0, sizeof(if_request));
+ strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name));
+ rc = ioctl(lsock, SIOCGIFHWADDR, &if_request);
+ if (rc < 0) {
+ close(lsock);
+ return -1;
+ }
+ memcpy(STATION_ADDR, if_request.ifr_hwaddr.sa_data,
+ sizeof(STATION_ADDR));
+ close(lsock);
+ return 0;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "\n"
+ "usage: simple_talker [-h] -i interface-name"
+ "\n"
+ "options:\n"
+ " -h show this message\n"
+ " -i specify interface for AVB connection\n"
+ "\n" "%s" "\n", version_str);
+ exit(1);
+}
+
+#define PACKET_IPG (125000) /* (1) packet every 125 usec */
+
+
+
+static void* packetizer_thread(void *arg) {
+ struct igb_packet *cleaned_packets;
+ six1883_sample *sample;
+ unsigned total_samples = 0;
+ int err;
+ int i;
+
+ const size_t bytes_to_read = CHANNELS * SAMPLES_PER_FRAME *
+ SAMPLE_SIZE;
+ jack_default_audio_sample_t* framebuf = malloc (bytes_to_read);
+ extern jack_ringbuffer_t* ringbuffer;
+ extern pthread_mutex_t threadLock;
+ extern pthread_cond_t dataReady;
+
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+ pthread_mutex_lock(&threadLock);
+
+ while (listeners && !halt_tx) {
+ pthread_cond_wait(&dataReady, &threadLock);
+
+ while ((jack_ringbuffer_read_space(ringbuffer) >= bytes_to_read)) {
+
+ tmp_packet = free_packets;
+ if (NULL == tmp_packet)
+ goto cleanup;
+ header0 =
+ (seventeen22_header *) (((char *)tmp_packet->vaddr) + 18);
+ header1 = (six1883_header *) (header0 + 1);
+ free_packets = tmp_packet->next;
+
+ /* unfortuntely unless this thread is at rtprio
+ * you get pre-empted between fetching the time
+ * and programming the packet and get a late packet
+ */
+ tmp_packet->attime = last_time + PACKET_IPG;
+ last_time += PACKET_IPG;
+
+ jack_ringbuffer_read (ringbuffer, (char*)&framebuf[0], bytes_to_read);
+
+ header0->seq_number = seqnum++;
+ if (seqnum % 4 == 0)
+ header0->timestamp_valid = 0;
+
+ else
+ header0->timestamp_valid = 1;
+
+ time_stamp = htonl(time_stamp);
+ header0->timestamp = time_stamp;
+ time_stamp = ntohl(time_stamp);
+ time_stamp += PACKET_IPG;
+ header1->data_block_continuity = total_samples;
+ total_samples += SAMPLES_PER_FRAME*CHANNELS;
+ sample =
+ (six1883_sample *) (((char *)tmp_packet->vaddr) +
+ (18 + sizeof(seventeen22_header) +
+ sizeof(six1883_header)));
+
+ for (i = 0; i < SAMPLES_PER_FRAME * CHANNELS; ++i) {
+ uint32_t tmp = htonl(MAX_SAMPLE_VALUE * framebuf[i]);
+ sample[i].label = 0x40;
+ memcpy(&(sample[i].value), &(tmp),
+ sizeof(sample[i].value));
+ }
+
+ err = igb_xmit(&igb_dev, 0, tmp_packet);
+
+ if (!err) {
+ continue;
+ }
+
+ if (ENOSPC == err) {
+
+ /* put back for now */
+ tmp_packet->next = free_packets;
+ free_packets = tmp_packet;
+ }
+
+cleanup: igb_clean(&igb_dev, &cleaned_packets);
+ i = 0;
+ while (cleaned_packets) {
+ i++;
+ tmp_packet = cleaned_packets;
+ cleaned_packets = cleaned_packets->next;
+ tmp_packet->next = free_packets;
+ free_packets = tmp_packet;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static void run_packetizer(void)
+{
+ jack_acquire_real_time_scheduling(packetizer_id, 70);
+ unleash_jack = 1;
+ pthread_join (packetizer_id, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned i;
+ int err;
+ struct igb_dma_alloc a_page;
+ struct igb_packet a_packet;
+ int c;
+ int rc = 0;
+ char *interface = NULL;
+ int class_a_id = 0;
+ int a_priority = 0;
+ u_int16_t a_vid = 0;
+#ifdef DOMAIN_QUERY
+ int class_b_id = 0;
+ int b_priority = 0;
+ u_int16_t b_vid = 0;
+#endif
+ gPtpTimeData td;
+ uint64_t now_local, now_8021as;
+ uint64_t update_8021as;
+ unsigned delta_8021as, delta_local;
+ long double ml_ratio;
+
+ jack_client_t* _jackclient;
+
+ for (;;) {
+ c = getopt(argc, argv, "hi:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ if (interface) {
+ printf
+ ("only one interface per daemon is supported\n");
+ usage();
+ }
+ interface = strdup(optarg);
+ break;
+ }
+ }
+ if (optind < argc)
+ usage();
+ if (NULL == interface) {
+ usage();
+ }
+ rc = mrp_connect();
+ if (rc) {
+ printf("socket creation failed\n");
+ return (errno);
+ }
+ err = pci_connect();
+ if (err) {
+ printf("connect failed (%s) - are you running as root?\n",
+ strerror(errno));
+ return (errno);
+ }
+ err = igb_init(&igb_dev);
+ if (err) {
+ printf("init failed (%s) - is the driver really loaded?\n",
+ strerror(errno));
+ return (errno);
+ }
+ err = igb_dma_malloc_page(&igb_dev, &a_page);
+ if (err) {
+ printf("malloc failed (%s) - out of memory?\n",
+ strerror(errno));
+ return (errno);
+ }
+ signal(SIGINT, sigint_handler);
+ rc = get_mac_address(interface);
+ if (rc) {
+ printf("failed to open interface\n");
+ usage();
+ }
+
+ mrp_monitor();
+#ifdef DOMAIN_QUERY
+ /*
+ * should use mrp_get_domain() above but this is a simplification
+ */
+#endif
+ domain_a_valid = 1;
+ class_a_id = MSRP_SR_CLASS_A;
+ a_priority = MSRP_SR_CLASS_A_PRIO;
+ a_vid = 2;
+ printf("detected domain Class A PRIO=%d VID=%04x...\n", a_priority,
+ a_vid);
+
+#define PKT_SZ 100
+
+ mrp_register_domain(&class_a_id, &a_priority, &a_vid);
+ igb_set_class_bandwidth(&igb_dev, PACKET_IPG / 125000, 0, PKT_SZ - 22,
+ 0);
+
+ memset(STREAM_ID, 0, sizeof(STREAM_ID));
+ memcpy(STREAM_ID, STATION_ADDR, sizeof(STATION_ADDR));
+
+ a_packet.dmatime = a_packet.attime = a_packet.flags = 0;
+ a_packet.map.paddr = a_page.dma_paddr;
+ a_packet.map.mmap_size = a_page.mmap_size;
+ a_packet.offset = 0;
+ a_packet.vaddr = a_page.dma_vaddr + a_packet.offset;
+ a_packet.len = PKT_SZ;
+ free_packets = NULL;
+ seqnum = 0;
+
+ /* divide the dma page into buffers for packets */
+ for (i = 1; i < ((a_page.mmap_size) / PKT_SZ); i++) {
+ tmp_packet = malloc(sizeof(struct igb_packet));
+ if (NULL == tmp_packet) {
+ printf("failed to allocate igb_packet memory!\n");
+ return (errno);
+ }
+ *tmp_packet = a_packet;
+ tmp_packet->offset = (i * PKT_SZ);
+ tmp_packet->vaddr += tmp_packet->offset;
+ tmp_packet->next = free_packets;
+ memset(tmp_packet->vaddr, 0, PKT_SZ); /* MAC header at least */
+ memcpy(tmp_packet->vaddr, DEST_ADDR, sizeof(DEST_ADDR));
+ memcpy(tmp_packet->vaddr + 6, STATION_ADDR,
+ sizeof(STATION_ADDR));
+
+ /* Q-tag */
+ ((char *)tmp_packet->vaddr)[12] = 0x81;
+ ((char *)tmp_packet->vaddr)[13] = 0x00;
+ ((char *)tmp_packet->vaddr)[14] =
+ ((a_priority << 13 | a_vid)) >> 8;
+ ((char *)tmp_packet->vaddr)[15] =
+ ((a_priority << 13 | a_vid)) & 0xFF;
+ ((char *)tmp_packet->vaddr)[16] = 0x22; /* 1722 eth type */
+ ((char *)tmp_packet->vaddr)[17] = 0xF0;
+
+ /* 1722 header update + payload */
+ header0 =
+ (seventeen22_header *) (((char *)tmp_packet->vaddr) + 18);
+ header0->cd_indicator = 0;
+ header0->subtype = 0;
+ header0->sid_valid = 1;
+ header0->version = 0;
+ header0->reset = 0;
+ header0->reserved0 = 0;
+ header0->gateway_valid = 0;
+ header0->reserved1 = 0;
+ header0->timestamp_uncertain = 0;
+ memset(&(header0->stream_id), 0, sizeof(header0->stream_id));
+ memcpy(&(header0->stream_id), STATION_ADDR,
+ sizeof(STATION_ADDR));
+ header0->length = htons(32);
+ header1 = (six1883_header *) (header0 + 1);
+ header1->format_tag = 1;
+ header1->packet_channel = 0x1F;
+ header1->packet_tcode = 0xA;
+ header1->app_control = 0x0;
+ header1->reserved0 = 0;
+ header1->source_id = 0x3F;
+ header1->data_block_size = 1;
+ header1->fraction_number = 0;
+ header1->quadlet_padding_count = 0;
+ header1->source_packet_header = 0;
+ header1->reserved1 = 0;
+ header1->eoh = 0x2;
+ header1->format_id = 0x10;
+ header1->format_dependent_field = 0x02;
+ header1->syt = 0xFFFF;
+ tmp_packet->len =
+ 18 + sizeof(seventeen22_header) + sizeof(six1883_header) +
+ (SAMPLES_PER_FRAME * CHANNELS * sizeof(six1883_sample));
+ free_packets = tmp_packet;
+ }
+
+ /*
+ * subtract 16 bytes for the MAC header/Q-tag - pktsz is limited to the
+ * data payload of the ethernet frame .
+ *
+ * IPG is scaled to the Class (A) observation interval of packets per 125 usec
+ */
+
+ _jackclient = init_jack();
+
+ fprintf(stderr, "advertising stream ...\n");
+ mrp_advertise_stream(STREAM_ID, DEST_ADDR, a_vid, PKT_SZ - 16,
+ PACKET_IPG / 125000, a_priority, 3900);
+ fprintf(stderr, "awaiting a listener ...\n");
+ mrp_await_listener(STREAM_ID);
+ printf("got a listener ...\n");
+ halt_tx = 0;
+
+ gptpinit();
+ gptpscaling(&td);
+
+ if( igb_get_wallclock( &igb_dev, &now_local, NULL ) != 0 ) {
+ fprintf( stderr, "Failed to get wallclock time\n" );
+ return -1;
+ }
+ update_8021as = td.local_time - td.ml_phoffset;
+ delta_local = (unsigned)(now_local - td.local_time);
+ delta_8021as = (unsigned)(td.ml_freqoffset * delta_local);
+ now_8021as = update_8021as + delta_8021as;
+
+ last_time = now_local + XMIT_DELAY;
+ time_stamp = now_8021as + RENDER_DELAY;
+
+ rc = nice(-20);
+
+ pthread_create (&packetizer_id, NULL, packetizer_thread, NULL);
+ run_packetizer();
+
+ rc = nice(0);
+
+ stop_jack(_jackclient);
+
+ if (halt_tx == 0)
+ printf("listener left ...\n");
+ halt_tx = 1;
+
+ mrp_unadvertise_stream(STREAM_ID, DEST_ADDR, a_vid, PKT_SZ - 16,
+ PACKET_IPG / 125000, a_priority, 3900);
+
+ igb_set_class_bandwidth(&igb_dev, 0, 0, 0, 0); /* disable Qav */
+
+ rc = mrp_disconnect();
+
+ igb_dma_free_page(&igb_dev, &a_page);
+
+ err = igb_detach(&igb_dev);
+
+ pthread_exit(NULL);
+
+ return (0);
+}
diff --git a/examples/mrp_client/Makefile b/examples/mrp_client/Makefile
index 47f4a0b0..a973454a 100644
--- a/examples/mrp_client/Makefile
+++ b/examples/mrp_client/Makefile
@@ -10,18 +10,23 @@ endif
#LDLIBS=-ligb -lpci -lz -pthread
#LDFLAGS=-L../../lib/igb
-all: mrpq mrpl
+all: mrpq mrpl mrpValidate
mrpl: mrpl.o mrpdclient.o
mrpq: mrpq.o mrpdclient.o
+mrpValidate: mrpValidate.o mrpdclient.o
+
mrpl.o: mrpl.c
$(CC) -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) mrpl.c
mrpq.o: mrpq.c
$(CC) -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) mrpq.c
+mrpValidate.o: mrpValidate.c
+ $(CC) -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) mrpValidate.c
+
mrpdclient.o: mrpdclient.c
$(CC) -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) mrpdclient.c
diff --git a/examples/mrp_client/mrpValidate.c b/examples/mrp_client/mrpValidate.c
new file mode 100644
index 00000000..c4ac0174
--- /dev/null
+++ b/examples/mrp_client/mrpValidate.c
@@ -0,0 +1,433 @@
+/******************************************************************************
+
+ Copyright (c) 2013, Harman International
+ Copyright (c) 2012, Intel Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/* This utility is designed to do three things:
+ * 1. Allow you to very simply exercise the MRP daemon between two
+ * end stations with or without an AVB Bridge between them.
+ * 2. Monitor the MxRPDUs sent by a third party device.
+ * 3. Provide an example of how to use the Open-AVB MRP daemon
+ * command interface.
+ *
+ * To run this program load the MRP daemon on two PCs. Run one in
+ * Monitor (mon) mode, and issue M++, V++, S++, etc commands on the
+ * other PC. Wireshark can also be used to verify that MRPDUs are
+ * formatted correctly for the various MxRP applications.
+ *
+ * Possible future enhancements:
+ * 1. Allow the various parameters to be modified (e.g.: change the
+ * STREAM_ID, VLAN_ID, etc).
+ * 2. Timestamp the Monitor output, including elapsed time between
+ * commands of the same type (e.g.: MMRP to next MMRP command).
+ * That way a user could validate that the periodic timer for
+ * MMRP is working correctly, or that the MSRP LeaveAll timer
+ * is working, or .... This would also allow the user to verify
+ * the timers of a third party device or operating correctly.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "mrpd.h"
+#include "mrpdclient.h"
+
+/* global variables */
+#define VERSION_STR "0.0"
+static const char *version_str =
+ "mrpValidate v" VERSION_STR "\n"
+ "Copyright (c) 2013, Harman International\n";
+int done = 0;
+char *msgbuf;
+
+// Various parameters used by MMRP, MVRP and MSRP
+// (Note: Defined here in an effort to make it easier to
+// understand examples below when it comes time
+// to build commands in real code!)
+#define STREAM_DA "010203040506"
+#define STREAM_ID "DEADBEEFBADFCA11"
+#define VLAN_ID "0002"
+#define TSPEC_MAX_FRAME_SIZE "576"
+#define TSPEC_MAX_FRAME_INTERVAL "8000"
+#define PRIORITY_AND_RANK "96"
+#define ACCUMULATED_LATENCY "1000"
+#define SR_CLASS_ID "6"
+#define SR_CLASS_PRIORITY "3"
+
+int
+process_ctl_msg(char *buf, int buflen)
+{
+ /* Unused parameters */
+ (void)buflen;
+
+ if (buf[1] == ':') {
+ printf("?? RESP:\n%s", buf);
+ } else {
+ printf("MRPD ---> %s", buf);
+ }
+ fflush(stdout);
+ return(0);
+}
+
+
+void
+displayStatusMsgs(void) {
+ int status;
+
+ status = 0;
+ while (status >= 0) {
+ status = mrpdclient_recv(process_ctl_msg);
+ }
+}
+
+
+//-------------------------------------------
+// MMRP functions:
+// M++ Declare a MAC Address w/New
+// M+? Declare a MAC Address w/JoinMt
+// M-- Withdraw a MAC Address w/Lv
+// M?? Display the database of MAC Addresses
+//-------------------------------------------
+int
+fnMplusplus(void) {
+#define M_PLUS_PLUS "M++:M=" STREAM_DA
+ return (mprdclient_sendto(M_PLUS_PLUS, sizeof(M_PLUS_PLUS)));
+}
+
+int
+fnMplusquestion(void) {
+#define M_PLUS_QUESTION "M+?:M=" STREAM_DA
+ return (mprdclient_sendto(M_PLUS_QUESTION, sizeof(M_PLUS_QUESTION)));
+}
+
+int
+fnMminusminus(void) {
+#define M_MINUS_MINUS "M--:M=" STREAM_DA
+ return (mprdclient_sendto(M_MINUS_MINUS, sizeof(M_MINUS_MINUS)));
+}
+
+int
+fnMquestionquestion(void) {
+#define M_QUESTION_QUESTION "M??"
+ mprdclient_sendto(M_QUESTION_QUESTION, sizeof(M_QUESTION_QUESTION));
+
+ displayStatusMsgs();
+ return 0;
+}
+
+
+//-------------------------------------------
+// MVRP functions:
+// V++ Declare a VLAN Join w/New
+// V+? Declare a VLAN Join w/JoinMt
+// V-- Withdraw a VLAN Join w/Lv
+// V?? Display the database of VLAN Joins
+//-------------------------------------------
+int
+fnVplusplus(void) {
+#define V_PLUS_PLUS "V++:I=" VLAN_ID
+ return (mprdclient_sendto(V_PLUS_PLUS, sizeof(V_PLUS_PLUS)));
+}
+
+int
+fnVplusquestion(void) {
+#define V_PLUS_QUESTION "V+?:I=" VLAN_ID
+ return (mprdclient_sendto(V_PLUS_QUESTION, sizeof(V_PLUS_QUESTION)));
+}
+
+int
+fnVminusminus(void) {
+#define V_MINUS_MINUS "V--:I=" VLAN_ID
+ return (mprdclient_sendto(V_MINUS_MINUS, sizeof(V_MINUS_MINUS)));
+}
+
+int
+fnVquestionquestion(void) {
+#define V_QUESTION_QUESTION "V??"
+ mprdclient_sendto(V_QUESTION_QUESTION, sizeof(V_QUESTION_QUESTION));
+
+ displayStatusMsgs();
+ return 0;
+}
+
+
+/********************************************
+ * MSRP Talker functions:
+ * S++ Declare a Talker Advertise for Stream w/New
+ * S+? Declare a Talker Advertise for Stream w/JoinMt
+ * S-- Withdraw a Talker Advertise for Stream w/Lv
+ * S?? Display the database of Talker Advertise for Stream IDs
+ ********************************************/
+int
+fnSTplusplus(void) {
+#define ST_PLUS_PLUS "S++:S=" STREAM_ID \
+ ",A=" STREAM_DA \
+ ",V=" VLAN_ID \
+ ",Z=" TSPEC_MAX_FRAME_SIZE \
+ ",I=" TSPEC_MAX_FRAME_INTERVAL \
+ ",P=" PRIORITY_AND_RANK \
+ ",L=" ACCUMULATED_LATENCY
+ return (mprdclient_sendto(ST_PLUS_PLUS, sizeof(ST_PLUS_PLUS)));
+}
+
+int
+fnSTplusquestion(void) {
+#define ST_PLUS_QUESTION "S+?:S=" STREAM_ID \
+ ",A=" STREAM_DA \
+ ",V=" VLAN_ID \
+ ",Z=" TSPEC_MAX_FRAME_SIZE \
+ ",I=" TSPEC_MAX_FRAME_INTERVAL \
+ ",P=" PRIORITY_AND_RANK \
+ ",L=" ACCUMULATED_LATENCY
+ return (mprdclient_sendto(ST_PLUS_QUESTION, sizeof(ST_PLUS_QUESTION)));
+}
+
+int
+fnSTminusminus(void) {
+#define ST_MINUS_MINUS "S--:S=" STREAM_ID
+ return (mprdclient_sendto(ST_MINUS_MINUS, sizeof(ST_MINUS_MINUS)));
+}
+
+int
+fnSquestionquestion(void) {
+#define S_QUESTION_QUESTION "S??"
+ mprdclient_sendto(S_QUESTION_QUESTION, sizeof(S_QUESTION_QUESTION));
+
+ displayStatusMsgs();
+ return 0;
+}
+
+
+//-------------------------------------------
+// MSRP Listener functions:
+// S+L Declare a Listener Ready for Stream w/New
+// S-L Withdraw a Listener Ready for Stream w/Lv
+//-------------------------------------------
+int
+fnSLplusplus(void) {
+#define SL_PLUS_PLUS "S+L:L=" STREAM_ID ",D=2"
+ return (mprdclient_sendto(SL_PLUS_PLUS, sizeof(SL_PLUS_PLUS)));
+}
+
+int
+fnSLminusminus(void) {
+#define SL_MINUS_MINUS "S-L:L=" STREAM_ID
+ return (mprdclient_sendto(SL_MINUS_MINUS, sizeof(SL_MINUS_MINUS)));
+}
+
+
+//-------------------------------------------
+// MSRP Domain functions:
+// S+D Declare a Domain w/New
+// S-D Withdraw a Domain w/Lv
+//-------------------------------------------
+int
+fnSDplusplus(void) {
+#define SD_PLUS_PLUS "S+D:C=" SR_CLASS_ID \
+ ",P=" SR_CLASS_PRIORITY \
+ ",V=" VLAN_ID
+ return (mprdclient_sendto(SD_PLUS_PLUS, sizeof(SD_PLUS_PLUS)));
+}
+
+int
+fnSDminusminus(void) {
+#define SD_MINUS_MINUS "S-D:C=" SR_CLASS_ID \
+ ",P=" SR_CLASS_PRIORITY \
+ ",V=" VLAN_ID
+ return (mprdclient_sendto(SD_MINUS_MINUS, sizeof(SD_MINUS_MINUS)));
+}
+
+
+int fnExit(void) {
+ done = 1;
+ return 0;
+}
+
+int
+dump_ctl_msg(char *buf, int buflen)
+{
+ /* Unused parameters */
+ (void)buflen;
+
+ printf("%s", buf);
+ fflush(stdout);
+ return(0);
+}
+
+int fnMonitor(void) {
+ /* First we need to have the mrpd daemon put this application in the
+ * list of attached clients that wish to receive reports when any MMRP,
+ * MSRP or MVRP attributes are received.
+ *
+ * Note that any M*, S* or V* command will put this application in the
+ * mrpd attached clients list. However, the M??, S?? and V?? do not
+ * cause any MRP attribute declarations or registrations, and are
+ * therefore the preferred method for doing this for monitoring.
+ */
+ mprdclient_sendto(M_QUESTION_QUESTION, sizeof(M_QUESTION_QUESTION));
+ mprdclient_sendto(S_QUESTION_QUESTION, sizeof(S_QUESTION_QUESTION));
+ mprdclient_sendto(V_QUESTION_QUESTION, sizeof(V_QUESTION_QUESTION));
+
+ /* Now just wait for any MRPDUs to be received... */
+ while (1) {
+ mrpdclient_recv(dump_ctl_msg);
+ }
+
+ return 0;
+}
+
+//-------------------------------------------
+// Menu system
+//-------------------------------------------
+struct menu_option {
+ char *option;
+ char *desc;
+ int (*fn)(void);
+};
+
+struct menu_option menu[] = {
+ {"M++", "Declare an MMRP MAC Address (NEW)", fnMplusplus},
+ {"M+?", "Declare an MMRP MAC Address (JOIN)", fnMplusquestion},
+ {"M--", "Withdraw an MMRP MAC Address (LEAVE)", fnMminusminus},
+ {"M??", "Dump the MMRP MAC Address table", fnMquestionquestion},
+ {" ", "", 0},
+ {"V++", "Declare a VLAN ID (NEW)", fnVplusplus},
+ {"V+?", "Declare a VLAN ID (JOIN)", fnVplusquestion},
+ {"V--", "Withdraw a VLAN ID (LEAVE)", fnVminusminus},
+ {"V??", "Dump the VLAN ID table", fnVquestionquestion},
+ {" ", "", 0},
+ {"S++", "Declare a Talker Advertise (NEW)", fnSTplusplus},
+ {"S+?", "Declare a Talker Advertise (JOIN)", fnSTplusquestion},
+ {"S--", "Withdraw a Talker Advertise (LEAVE)", fnSTminusminus},
+ {"S??", "Dump the Talker Advertise table", fnSquestionquestion},
+ {" ", "", 0},
+ {"S+L", "Declare a Listener Ready (NEW)", fnSLplusplus},
+ {"S-L", "Withdraw a Listener Ready (LEAVE)", fnSLminusminus},
+ {" ", "", 0},
+ {"S+D", "Declare a Domain (NEW)", fnSDplusplus},
+ {"S-D", "Withdraw a Domain (LEAVE)", fnSDminusminus},
+ {" ", "", 0},
+ {"mon", "Monitor MRP notifications", fnMonitor},
+ {" ", "", 0},
+ {"exit", "Exit the program", fnExit},
+ {0, 0, 0}
+};
+
+void
+displayMenu(void) {
+ int i;
+
+ printf("\n\nSelect from the following options:\n");
+ for (i=0; 0 != menu[i].option; i++) {
+ if ('\0' == menu[i].desc[0]) {
+ printf("\n");
+ } else {
+ printf("%5s - %s\n", menu[i].option, menu[i].desc);
+ }
+ }
+ printf("Note: StreamID=" STREAM_ID ", StreamDA=" STREAM_DA
+ ", VLAN ID=" VLAN_ID "\n");
+}
+
+int
+runMenuFunc(char *option) {
+ int i;
+ int rc = -1;
+
+ for (i=0; 0 != menu[i].option; i++) {
+ if (0 == strcmp(option, menu[i].option)) {
+ rc = menu[i].fn();
+ break;
+ }
+ }
+ if (NULL == menu[i].fn) {
+ printf("ERROR: '%s' is not a listed option\n\n", option);
+
+ }
+ return rc;
+}
+
+//-------------------------------------------
+
+int
+main(int argc, char *argv[]) {
+ char option[10];
+ char saved[10];
+ int rc = 0;
+
+ (void) argc;
+ (void) argv;
+ (void) rc;
+
+ printf("%s\n", version_str);
+
+ msgbuf = malloc(1500);
+ if (NULL == msgbuf) {
+ printf("memory allocation error - exiting\n");
+ return -1;
+ }
+
+ rc = mrpdclient_init(MRPD_PORT_DEFAULT);
+ if (rc) {
+ printf("init failed\n");
+ goto out;
+ }
+
+ saved[0]='\0';
+ while (!done) {
+ displayMenu();
+ printf("Option to run? ");
+ if (NULL == fgets(option, sizeof(option), stdin)) {
+ done = 1;
+ printf("\n");
+ continue;
+ }
+ option[strlen(option)-1] = '\0'; // remove assumed LF
+ if ('\0' == option[0]) {
+ // Repeat last cmd if nothing entered
+ strncpy(option, saved, sizeof(option));
+ }
+
+ rc = runMenuFunc(option);
+ if (rc >= 0) {
+ // Save last command if valid
+ strncpy(saved, option, sizeof(option));
+ }
+ }
+ rc = mprdclient_close();
+
+out:
+ return rc;
+}
diff --git a/examples/simple_listener/simple_listener.c b/examples/simple_listener/simple_listener.c
index 959cf9a0..25abe042 100644
--- a/examples/simple_listener/simple_listener.c
+++ b/examples/simple_listener/simple_listener.c
@@ -214,6 +214,20 @@ int recv_msg()
}
+int join_vlan()
+{
+ int rc;
+ char *msgbuf = malloc(1500);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 1500);
+ sprintf(msgbuf, "V++:I=0002");
+ rc = send_msg(msgbuf, 1500);
+
+ free(msgbuf);
+ return rc;
+}
+
int await_talker()
{
while (0 == talker)
@@ -375,6 +389,7 @@ int main(int argc, char *argv[])
}
report_domain_status();
+ join_vlan();
fprintf(stdout,"Waiting for talker...\n");
await_talker();
diff --git a/examples/simple_talker/simple_talker.c b/examples/simple_talker/simple_talker.c
index 835dcbdd..7520ef9f 100755
--- a/examples/simple_talker/simple_talker.c
+++ b/examples/simple_talker/simple_talker.c
@@ -784,6 +784,21 @@ int mrp_monitor()
return (0);
}
+int mrp_join_vlan()
+{
+ char *msgbuf;
+ int rc;
+ msgbuf = malloc(1500);
+ if (NULL == msgbuf)
+ return -1;
+ memset(msgbuf, 0, 1500);
+ sprintf(msgbuf, "V++:I=0002");
+ rc = send_mrp_msg(msgbuf, 1500);
+
+ free(msgbuf);
+ return rc;
+}
+
int mrp_join_listener(uint8_t * streamid)
{
char *msgbuf;
@@ -999,13 +1014,14 @@ int main(int argc, char *argv[])
IP_RTP_Header *l4_headers;
IP_PseudoHeader pseudo_hdr;
- unsigned l4_local_address;
+ unsigned l4_local_address = 0;
int sd;
+ struct sockaddr_in local;
+ struct ifreq if_request;
uint64_t now_local, now_8021as;
uint64_t update_8021as;
unsigned delta_8021as, delta_local;
- long double ml_ratio;
uint8_t dest_addr[6];
size_t packet_size;
@@ -1070,8 +1086,6 @@ int main(int argc, char *argv[])
if( transport == 2 ) {
memcpy( dest_addr, L2_DEST_ADDR, sizeof(dest_addr));
} else {
- struct sockaddr_in local;;
- struct ifreq if_request;
memset( &local, 0, sizeof( local ));
local.sin_family = PF_INET;
local.sin_addr.s_addr = htonl( INADDR_ANY );
@@ -1117,11 +1131,12 @@ int main(int argc, char *argv[])
#define PKT_SZ 100
mrp_register_domain(&class_a_id, &a_priority, &a_vid);
+ mrp_join_vlan();
+
if( transport == 2 ) {
igb_set_class_bandwidth
(&igb_dev, 125000/L2_PACKET_IPG, 0, PKT_SZ - 22, 0);
} else {
- /* 1 is the wrong value, but API doesn't allow fractional values */
igb_set_class_bandwidth
(&igb_dev, 1, 0,
sizeof(*l4_headers)+L4_SAMPLES_PER_FRAME*CHANNELS*2, 0);
@@ -1307,8 +1322,6 @@ int main(int argc, char *argv[])
last_time = now_local + XMIT_DELAY;
time_stamp = now_8021as + RENDER_DELAY;
- printf( "now(local) = %lu\n", now_local );
-
rc = nice(-20);
while (listeners && !halt_tx) {
@@ -1367,7 +1380,6 @@ int main(int argc, char *argv[])
l4_headers->timestamp = rtp_timestamp;
tmp_packet->attime = last_time + L4_PACKET_IPG;
- printf( "@time = %lu\n", tmp_packet->attime );
last_time += L4_PACKET_IPG;
l4_headers->nanoseconds = time_stamp/1000000000;
@@ -1406,18 +1418,7 @@ int main(int argc, char *argv[])
}
err = igb_xmit(&igb_dev, 0, tmp_packet);
- err = 0;
- // Dump packet contents
- printf( "PTP Timestamp: %lu(%lx)\n", time_stamp, time_stamp );
- printf( "Start Dump ======\n" );
- for( i = 46; i < 70; ++i ) {
- if( i % 24 == 23 ) {
- printf( "%02hhx\n", ((uint8_t *)tmp_packet->vaddr)[i] );
- } else {
- printf( "%02hhx ", ((uint8_t *)tmp_packet->vaddr)[i] );
- }
- }
- printf( "\nEnd Dump ======\n" );
+
if (!err) {
continue;
}
diff --git a/kmod/igb/startup.sh b/kmod/igb/startup.sh
index b8f87b6b..ae71e373 100644
--- a/kmod/igb/startup.sh
+++ b/kmod/igb/startup.sh
@@ -1,5 +1,6 @@
#!/bin/bash
+# Network interface name (e.g. Fedora=p2p1, Ubuntu=eth2, OpenSUSE=ens01)
INTERFACE=p2p1
export INTERFACE