1
+ /*
2
+ * Copyright (c) 2024, COVESA
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions are met:
6
+ *
7
+ * * Redistributions of source code must retain the above copyright notice,
8
+ * this list of conditions and the following disclaimer.
9
+ * * Redistributions in binary form must reproduce the above copyright
10
+ * notice, this list of conditions and the following disclaimer in the
11
+ * documentation and/or other materials provided with the distribution.
12
+ * * Neither the name of COVESA nor the names of its contributors may be
13
+ * used to endorse or promote products derived from this software without
14
+ * specific prior written permission.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ *
27
+ * SPDX-License-Identifier: BSD-3-Clause
28
+ */
29
+
30
+ #include <linux/if_packet.h>
31
+ #include <linux/if.h>
32
+ #include <linux/if_ether.h>
33
+ #include <linux/can.h>
34
+ #include <linux/can/raw.h>
35
+ #include <sys/ioctl.h>
36
+ #include <time.h>
37
+
38
+ #include <arpa/inet.h>
39
+ #include <stdlib.h>
40
+ #include <argp.h>
41
+ #include <string.h>
42
+ #include <unistd.h>
43
+ #include <stdio.h>
44
+
45
+ #include "common/common.h"
46
+ #include "avtp/Udp.h"
47
+ #include "avtp/acf/Ntscf.h"
48
+ #include "avtp/acf/Tscf.h"
49
+ #include "avtp/acf/Gpc.h"
50
+ #include "avtp/CommonHeader.h"
51
+
52
+ #define MAX_PDU_SIZE 1500
53
+ #define MAX_MSG_SIZE 100
54
+ #define STREAM_ID 0xAABBCCDDEEFF0001
55
+ #define ARGPARSE_OPTION_MSG 500
56
+ #define GPC_CODE 256
57
+
58
+ static char ifname [IFNAMSIZ ];
59
+ static uint8_t macaddr [ETH_ALEN ];
60
+ static char message_string [MAX_MSG_SIZE ] = "Hello World!\0" ;
61
+ static uint8_t ip_addr [sizeof (struct in_addr )];
62
+ static uint32_t udp_port = 17220 ;
63
+ static int priority = -1 ;
64
+ static uint8_t seq_num = 0 ;
65
+ static uint32_t udp_seq_num = 0 ;
66
+ static uint8_t use_tscf ;
67
+ static uint8_t use_udp ;
68
+
69
+ static error_t parser (int key , char * arg , struct argp_state * state )
70
+ {
71
+ int res ;
72
+ char ip_addr_str [100 ];
73
+
74
+ switch (key ) {
75
+ case 't' :
76
+ use_tscf = 1 ;
77
+ break ;
78
+ case 'u' :
79
+ use_udp = 1 ;
80
+ break ;
81
+ case ARGPARSE_OPTION_MSG :
82
+ strncpy (message_string , arg , MAX_MSG_SIZE );
83
+ break ;
84
+ case ARGP_KEY_NO_ARGS :
85
+ argp_usage (state );
86
+
87
+ case ARGP_KEY_ARG :
88
+ if (state -> argc < 2 ){
89
+ argp_usage (state );
90
+ }
91
+ if (!use_udp ){
92
+
93
+ strncpy (ifname , arg , sizeof (ifname ) - 1 );
94
+
95
+ if (state -> next < state -> argc )
96
+ {
97
+ res = sscanf (state -> argv [state -> next ], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx" ,
98
+ & macaddr [0 ], & macaddr [1 ], & macaddr [2 ],
99
+ & macaddr [3 ], & macaddr [4 ], & macaddr [5 ]);
100
+ if (res != 6 ) {
101
+ fprintf (stderr , "Invalid MAC address\n\n" );
102
+ argp_usage (state );
103
+ }
104
+ state -> next += 1 ;
105
+ }
106
+
107
+ } else {
108
+ res = sscanf (arg , "%[^:]:%d" , ip_addr_str , & udp_port );
109
+ if (!res ) {
110
+ fprintf (stderr , "Invalid IP address or port\n\n" );
111
+ argp_usage (state );
112
+ }
113
+ res = inet_pton (AF_INET , ip_addr_str , ip_addr );
114
+ if (!res ) {
115
+ fprintf (stderr , "Invalid IP address\n\n" );
116
+ argp_usage (state );
117
+ }
118
+ }
119
+
120
+ break ;
121
+ }
122
+
123
+ return 0 ;
124
+ }
125
+
126
+ static struct argp_option options [] = {
127
+ {"tscf" , 't' , 0 , 0 , "Use TSCF" },
128
+ {"udp" , 'u' , 0 , 0 , "Use UDP" },
129
+ {"message" , ARGPARSE_OPTION_MSG , "MSG_STR" , 0 , "String message to send over IEEE 1722" },
130
+ {"ifname" , 0 , 0 , OPTION_DOC , "Network interface (If Ethernet)" },
131
+ {"dst-mac-address" , 0 , 0 , OPTION_DOC , "Stream destination MAC address (If Ethernet)" },
132
+ {"dst-nw-address:port" , 0 , 0 , OPTION_DOC , "Stream destination network address and port (If UDP)" },
133
+ { 0 }
134
+ };
135
+
136
+ static struct argp argp = { options , parser , 0 , 0 };
137
+
138
+ static int init_cf_pdu (uint8_t * pdu )
139
+ {
140
+ int res ;
141
+ if (use_tscf ) {
142
+ Avtp_Tscf_t * tscf_pdu = (Avtp_Tscf_t * ) pdu ;
143
+ memset (tscf_pdu , 0 , AVTP_TSCF_HEADER_LEN );
144
+ Avtp_Tscf_Init (tscf_pdu );
145
+ Avtp_Tscf_SetField (tscf_pdu , AVTP_TSCF_FIELD_TU , 0U );
146
+ Avtp_Tscf_SetField (tscf_pdu , AVTP_TSCF_FIELD_SEQUENCE_NUM , seq_num ++ );
147
+ Avtp_Tscf_SetField (tscf_pdu , AVTP_TSCF_FIELD_STREAM_ID , STREAM_ID );
148
+ res = AVTP_TSCF_HEADER_LEN ;
149
+ } else {
150
+ Avtp_Ntscf_t * ntscf_pdu = (Avtp_Ntscf_t * ) pdu ;
151
+ memset (ntscf_pdu , 0 , AVTP_NTSCF_HEADER_LEN );
152
+ Avtp_Ntscf_Init (ntscf_pdu );
153
+ Avtp_Ntscf_SetField (ntscf_pdu , AVTP_NTSCF_FIELD_SEQUENCE_NUM , seq_num ++ );
154
+ Avtp_Ntscf_SetField (ntscf_pdu , AVTP_NTSCF_FIELD_STREAM_ID , STREAM_ID );
155
+ res = AVTP_NTSCF_HEADER_LEN ;
156
+ }
157
+ return res ;
158
+ }
159
+
160
+ static int update_pdu_length (uint8_t * pdu , uint64_t length )
161
+ {
162
+ if (use_tscf ) {
163
+ uint64_t payloadLen = length - AVTP_TSCF_HEADER_LEN ;
164
+ Avtp_Tscf_SetField ((Avtp_Tscf_t * )pdu , AVTP_TSCF_FIELD_STREAM_DATA_LENGTH , payloadLen );
165
+ } else {
166
+ uint64_t payloadLen = length - AVTP_NTSCF_HEADER_LEN ;
167
+ Avtp_Ntscf_SetField ((Avtp_Ntscf_t * )pdu , AVTP_NTSCF_FIELD_NTSCF_DATA_LENGTH , payloadLen );
168
+ }
169
+ return 0 ;
170
+ }
171
+
172
+ static int prepare_acf_packet (uint8_t * acf_pdu , uint64_t gpc_code ,
173
+ uint8_t * payload , uint16_t length ) {
174
+
175
+ Avtp_Gpc_t * pdu = (Avtp_Gpc_t * ) acf_pdu ;
176
+
177
+ // Clear bits
178
+ memset (pdu , 0 , AVTP_GPC_HEADER_LEN );
179
+ uint8_t acf_length = (AVTP_GPC_HEADER_LEN + length )/4 ;
180
+ if (length % 4 ) acf_length ++ ;
181
+
182
+ // Prepare ACF PDU for CAN
183
+ Avtp_Gpc_Init (pdu );
184
+ Avtp_Gpc_SetField (pdu , AVTP_GPC_FIELD_GPC_MSG_ID , gpc_code );
185
+ Avtp_Gpc_SetField (pdu , AVTP_GPC_FIELD_ACF_MSG_LENGTH , acf_length );
186
+ memcpy (acf_pdu + AVTP_GPC_HEADER_LEN , payload , length );
187
+ memset (acf_pdu + AVTP_GPC_HEADER_LEN + length , 0 , acf_length * 4 - length );
188
+
189
+ return acf_length * 4 ;
190
+ }
191
+
192
+ static int update_cf_length (uint8_t * cf_pdu , uint64_t length )
193
+ {
194
+ if (use_tscf ) {
195
+ uint64_t payloadLen = length - AVTP_TSCF_HEADER_LEN ;
196
+ Avtp_Tscf_SetField ((Avtp_Tscf_t * )cf_pdu , AVTP_TSCF_FIELD_STREAM_DATA_LENGTH , payloadLen );
197
+ } else {
198
+ uint64_t payloadLen = length - AVTP_NTSCF_HEADER_LEN ;
199
+ Avtp_Ntscf_SetField ((Avtp_Ntscf_t * )cf_pdu , AVTP_NTSCF_FIELD_NTSCF_DATA_LENGTH , payloadLen );
200
+ }
201
+ return 0 ;
202
+ }
203
+
204
+ int main (int argc , char * argv [])
205
+ {
206
+
207
+ int fd , res ;
208
+ struct sockaddr_ll sk_ll_addr ;
209
+ struct sockaddr_in sk_udp_addr ;
210
+ uint8_t pdu [MAX_PDU_SIZE ];
211
+ uint16_t pdu_length , cf_length ;
212
+ uint64_t gpc_code = 0 ;
213
+
214
+ argp_parse (& argp , argc , argv , 0 , NULL , NULL );
215
+
216
+ // Create an appropriate talker socket: UDP or Ethernet raw
217
+ // Setup the socket for sending to the destination
218
+ if (use_udp ) {
219
+ fd = create_talker_socket_udp (priority );
220
+ if (fd < 0 ) return fd ;
221
+
222
+ res = setup_udp_socket_address ((struct in_addr * ) ip_addr ,
223
+ udp_port , & sk_udp_addr );
224
+ } else {
225
+ fd = create_talker_socket (priority );
226
+ if (fd < 0 ) return fd ;
227
+ res = setup_socket_address (fd , ifname , macaddr ,
228
+ ETH_P_TSN , & sk_ll_addr );
229
+ }
230
+ if (res < 0 ) goto err ;
231
+
232
+ // Sending loop
233
+ for (;;) {
234
+
235
+ // Pack into control formats
236
+ uint8_t * cf_pdu ;
237
+ pdu_length = 0 ;
238
+ cf_length = 0 ;
239
+
240
+ // Usage of UDP means the PDU needs an encapsulation number
241
+ if (use_udp ) {
242
+ Avtp_Udp_t * udp_pdu = (Avtp_Udp_t * ) pdu ;
243
+ Avtp_Udp_SetField (udp_pdu , AVTP_UDP_FIELD_ENCAPSULATION_SEQ_NO ,
244
+ udp_seq_num ++ );
245
+ pdu_length += sizeof (Avtp_Udp_t );
246
+ }
247
+
248
+ // Create the CF packet first
249
+ cf_pdu = pdu + pdu_length ;
250
+ res = init_cf_pdu (cf_pdu );
251
+ if (res < 0 )
252
+ goto err ;
253
+ pdu_length += res ;
254
+ cf_length += res ;
255
+
256
+ // Creation of the ACF Packet
257
+ uint8_t * acf_pdu = pdu + pdu_length ;
258
+ res = prepare_acf_packet (acf_pdu , gpc_code ++ , (uint8_t * )message_string ,
259
+ strlen ((char * )message_string ));
260
+ if (res < 0 ) goto err ;
261
+ pdu_length += res ;
262
+ cf_length += res ;
263
+
264
+ res = update_cf_length (cf_pdu , cf_length );
265
+ if (res < 0 )
266
+ goto err ;
267
+
268
+ if (use_udp ) {
269
+ res = sendto (fd , pdu , pdu_length , 0 ,
270
+ (struct sockaddr * ) & sk_udp_addr , sizeof (sk_udp_addr ));
271
+ } else {
272
+ res = sendto (fd , pdu , pdu_length , 0 ,
273
+ (struct sockaddr * ) & sk_ll_addr , sizeof (sk_ll_addr ));
274
+ }
275
+ if (res < 0 ) {
276
+ perror ("Failed to send data" );
277
+ goto err ;
278
+ }
279
+ sleep (1 );
280
+ }
281
+
282
+ err :
283
+ close (fd );
284
+ return 1 ;
285
+
286
+ }
0 commit comments