Skip to content

Commit 7155114

Browse files
committed
PcapngImportFilter: Initial LINK_TYPE_SOCKETCAN support
1 parent 70f35c4 commit 7155114

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

scopeprotocols/PcapngImportFilter.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ void PcapngImportFilter::OnFileNameChanged()
172172

173173
switch(m_linkType)
174174
{
175+
case LINK_TYPE_SOCKETCAN:
176+
LoadSocketCAN(fp);
177+
break;
178+
175179
case LINK_TYPE_LINUX_COOKED:
176180
//Linux cooked encapsulation is special: we don't know the output data format initially
177181
//and there can be a mix of several which we don't currently implement!
@@ -185,6 +189,219 @@ void PcapngImportFilter::OnFileNameChanged()
185189
fclose(fp);
186190
}
187191

192+
//TODO: this shares a lot in common with LoadCANLinuxCooked, how can we share more?
193+
bool PcapngImportFilter::LoadSocketCAN(FILE* fp)
194+
{
195+
LogTrace("Loading SocketCAN packets\n");
196+
LogIndenter li;
197+
198+
//Create output waveform
199+
auto cap = new CANWaveform;
200+
cap->m_timescale = 1;
201+
cap->m_triggerPhase = 0;
202+
cap->PrepareForCpuAccess();
203+
SetData(cap, 0);
204+
205+
bool first = true;
206+
int64_t baseTimestamp = 0;
207+
208+
//Calculate length of a single bit on the bus
209+
int64_t baud = m_parameters[m_datarate].GetIntVal();
210+
int64_t ui = FS_PER_SECOND / baud;
211+
212+
uint32_t blocktype;
213+
uint32_t blocklen;
214+
int64_t tend = 0;
215+
while(!feof(fp))
216+
{
217+
auto blockstart = ftell(fp);
218+
219+
if(1 != fread(&blocktype, sizeof(blocktype), 1, fp))
220+
return false;
221+
if(1 != fread(&blocklen, sizeof(blocklen), 1, fp))
222+
return false;
223+
224+
//Should be an EPB, ignore anything else
225+
switch(blocktype)
226+
{
227+
case 5:
228+
LogTrace("Found Block Statistics (%d bytes)\n", blocklen);
229+
fseek(fp, blockstart + blocklen, SEEK_SET);
230+
continue;
231+
232+
case 6:
233+
//LogTrace("Found EPB (%d bytes)\n", blocklen);
234+
break;
235+
236+
default:
237+
//unknown type, wut?
238+
LogWarning("unknown block type %d\n", blocktype);
239+
fseek(fp, blockstart + blocklen, SEEK_SET);
240+
continue;
241+
}
242+
LogIndenter li2;
243+
244+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
245+
// PCAPNG EPB headers
246+
247+
//For now, ignore interface number since we don't support mixed captures or multiple output streams yet
248+
uint32_t ifacenum;
249+
if(1 != fread(&ifacenum, sizeof(ifacenum), 1, fp))
250+
return false;
251+
//LogTrace("Interface %u\n", ifacenum);
252+
253+
//Timestamp
254+
uint32_t tstamp[2];
255+
if(2 != fread(tstamp, sizeof(uint32_t), 2, fp))
256+
return false;
257+
258+
//Convert from packed format in native units to a single 64-bit integer
259+
int64_t stamp = tstamp[0];
260+
stamp = (stamp << 32) | tstamp[1];
261+
262+
//If this is the FIRST packet in the capture, it's the base timestamp and we measure offsets from that
263+
if(first)
264+
{
265+
baseTimestamp = stamp;
266+
stamp = 0;
267+
first = false;
268+
269+
//Convert base timestamp to seconds and fs
270+
int64_t ticks_per_fs = FS_PER_SECOND / m_timestampScale;
271+
cap->m_startTimestamp = baseTimestamp / ticks_per_fs;
272+
cap->m_startFemtoseconds = m_timestampScale * (baseTimestamp % ticks_per_fs);
273+
}
274+
275+
//Not first, use relative offset
276+
else
277+
stamp -= baseTimestamp;
278+
279+
//Convert from native units to fs
280+
stamp *= m_timestampScale;
281+
282+
//Actual as-captured packet length
283+
uint32_t packlen;
284+
if(1 != fread(&packlen, sizeof(packlen), 1, fp))
285+
return false;
286+
if(packlen < 16)
287+
{
288+
LogWarning("Invalid packet length %d (should be >= 16 to allow room for cooked headers)\n", packlen);
289+
fseek(fp, blockstart + blocklen, SEEK_SET);
290+
continue;
291+
}
292+
293+
//Original packet length (might have been truncated, but ignore this)
294+
uint32_t origlen;
295+
if(1 != fread(&origlen, sizeof(origlen), 1, fp))
296+
return false;
297+
298+
//Timestamps sometimes have some jitter due to USB dongles combining several into one transaction,
299+
//without logging actual arrival timestamps. So they can appear to be coming at too high a baud rate.
300+
//Fudge the timestamp if it claims to have come before the previous frame ended
301+
if(stamp < tend)
302+
stamp = tend;
303+
304+
//Read CAN ID (32 bit on wire)
305+
uint32_t id;
306+
if(1 != fread(&id, sizeof(id), 1, fp))
307+
return false;
308+
id = ntohl(id);
309+
310+
//Read frame length
311+
uint8_t nbytes;
312+
if(1 != fread(&nbytes, sizeof(nbytes), 1, fp))
313+
return false;
314+
if(nbytes > 8)
315+
{
316+
LogWarning("Invalid DLC %d (should be <= 8)\n", nbytes);
317+
fseek(fp, blockstart + blocklen, SEEK_SET);
318+
continue;
319+
}
320+
321+
//Skip 3 bytes of FD flags / reserved before the payload
322+
fseek(fp, 3, SEEK_CUR);
323+
324+
//Read payload
325+
uint8_t data[8];
326+
if(nbytes != fread(data, 1, nbytes, fp))
327+
return false;
328+
329+
//Extract header bits (packed in with ID)
330+
bool ext = (id & 0x80000000);
331+
bool rtr = (id & 0x40000000);
332+
bool err = (id & 0x20000000);
333+
id &= 0x1fffffff;
334+
bool fd = false;//(proto == 0x0d);
335+
336+
//Add timeline samples
337+
cap->m_offsets.push_back(stamp);
338+
cap->m_durations.push_back(ui);
339+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_SOF, 0));
340+
341+
cap->m_offsets.push_back(stamp + ui);
342+
cap->m_durations.push_back(31 * ui);
343+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_ID, id));
344+
345+
cap->m_offsets.push_back(stamp + 32*ui);
346+
cap->m_durations.push_back(ui);
347+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_RTR, rtr));
348+
349+
cap->m_offsets.push_back(stamp + 33*ui);
350+
cap->m_durations.push_back(ui);
351+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_FD, fd));
352+
353+
cap->m_offsets.push_back(stamp + 34*ui);
354+
cap->m_durations.push_back(ui);
355+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_R0, 0));
356+
357+
cap->m_offsets.push_back(stamp + 35*ui);
358+
cap->m_durations.push_back(ui*4);
359+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_DLC, nbytes));
360+
361+
//Data
362+
for(size_t i=0; i<nbytes; i++)
363+
{
364+
cap->m_offsets.push_back(stamp + 39*ui + i*8*ui);
365+
cap->m_durations.push_back(ui*8);
366+
cap->m_samples.push_back(CANSymbol(CANSymbol::TYPE_DATA, data[i]));
367+
}
368+
369+
tend = stamp + 39*ui + nbytes*8*ui;
370+
371+
//CRC TODO
372+
//CRC delim TODO
373+
//ACK TODO
374+
//ACK delim TODO
375+
376+
//Add the packet
377+
//Fake the duration for now: assume 8 bytes payload, extended format, and no stuffing
378+
//Leave format/type/ack blank, this doesn't seem to be saved in this capture format
379+
auto pack = new Packet;
380+
if(err)
381+
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_ERROR];
382+
else if(rtr)
383+
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_DATA_READ];
384+
else
385+
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_DATA_WRITE];
386+
pack->m_headers["Format"] = ext ? "EXT" : "BASE";
387+
pack->m_headers["ID"] = to_string_hex(id);
388+
pack->m_headers["Mode"] = fd ? "CAN-FD" : "CAN";
389+
pack->m_headers["Len"] = to_string(nbytes);
390+
if(err)
391+
pack->m_headers["Format"] = "ERR";
392+
for(size_t i=0; i<nbytes; i++)
393+
pack->m_data.push_back(data[i]);
394+
pack->m_offset = stamp;
395+
pack->m_len = 128 * ui;
396+
m_packets.push_back(pack);
397+
398+
//End of the EPB, skip any unread contents
399+
fseek(fp, blockstart + blocklen, SEEK_SET);
400+
}
401+
402+
return true;
403+
}
404+
188405
bool PcapngImportFilter::LoadLinuxCooked(FILE* fp)
189406
{
190407
LogTrace("Loading Linux cooked format packets\n");
@@ -526,6 +743,11 @@ bool PcapngImportFilter::ReadIDB(FILE* fp)
526743
m_linkType = LINK_TYPE_CAN;
527744
break;
528745

746+
case 227:
747+
LogTrace("SocketCAN data\n");
748+
m_linkType = LINK_TYPE_SOCKETCAN;
749+
break;
750+
529751
default:
530752
LogWarning("PcapNG contains unknown type data %d\n", linktype);
531753
m_linkType = LINK_TYPE_UNKNOWN;

scopeprotocols/PcapngImportFilter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,15 @@ class PcapngImportFilter : public PacketDecoder
6161

6262
bool LoadLinuxCooked(FILE* fp);
6363
bool LoadCANLinuxCooked(FILE* fp);
64+
bool LoadSocketCAN(FILE* fp);
6465

6566
enum LinkType
6667
{
6768
LINK_TYPE_ETHERNET,
6869
LINK_TYPE_CAN,
6970
LINK_TYPE_USB,
7071
LINK_TYPE_LINUX_COOKED,
72+
LINK_TYPE_SOCKETCAN,
7173
LINK_TYPE_UNKNOWN
7274
} m_linkType;
7375

0 commit comments

Comments
 (0)