1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers |
4 | * Copyright (C) 2002-2006 Kristian Høgsberg |
5 | */ |
6 | |
7 | #include <byteswap.h> |
8 | #include <endian.h> |
9 | #include <fcntl.h> |
10 | #include <linux/firewire-constants.h> |
11 | #include <poll.h> |
12 | #include <popt.h> |
13 | #include <signal.h> |
14 | #include <stdio.h> |
15 | #include <stdlib.h> |
16 | #include <string.h> |
17 | #include <sys/ioctl.h> |
18 | #include <sys/time.h> |
19 | #include <termios.h> |
20 | #include <unistd.h> |
21 | |
22 | #include "list.h" |
23 | #include "nosy-dump.h" |
24 | #include "nosy-user.h" |
25 | |
26 | enum { |
27 | PACKET_FIELD_DETAIL = 0x01, |
28 | PACKET_FIELD_DATA_LENGTH = 0x02, |
29 | /* Marks the fields we print in transaction view. */ |
30 | PACKET_FIELD_TRANSACTION = 0x04, |
31 | }; |
32 | |
33 | static void print_packet(uint32_t *data, size_t length); |
34 | static void decode_link_packet(struct link_packet *packet, size_t length, |
35 | int include_flags, int exclude_flags); |
36 | static int run = 1; |
37 | sig_t sys_sigint_handler; |
38 | |
39 | static char *option_nosy_device = "/dev/nosy" ; |
40 | static char *option_view = "packet" ; |
41 | static char *option_output; |
42 | static char *option_input; |
43 | static int option_hex; |
44 | static int option_iso; |
45 | static int option_cycle_start; |
46 | static int option_version; |
47 | static int option_verbose; |
48 | |
49 | enum { |
50 | VIEW_TRANSACTION, |
51 | VIEW_PACKET, |
52 | VIEW_STATS, |
53 | }; |
54 | |
55 | static const struct poptOption options[] = { |
56 | { |
57 | .longName = "device" , |
58 | .shortName = 'd', |
59 | .argInfo = POPT_ARG_STRING, |
60 | .arg = &option_nosy_device, |
61 | .descrip = "Path to nosy device." , |
62 | .argDescrip = "DEVICE" |
63 | }, |
64 | { |
65 | .longName = "view" , |
66 | .argInfo = POPT_ARG_STRING, |
67 | .arg = &option_view, |
68 | .descrip = "Specify view of bus traffic: packet, transaction or stats." , |
69 | .argDescrip = "VIEW" |
70 | }, |
71 | { |
72 | .longName = "hex" , |
73 | .shortName = 'x', |
74 | .argInfo = POPT_ARG_NONE, |
75 | .arg = &option_hex, |
76 | .descrip = "Print each packet in hex." , |
77 | }, |
78 | { |
79 | .longName = "iso" , |
80 | .argInfo = POPT_ARG_NONE, |
81 | .arg = &option_iso, |
82 | .descrip = "Print iso packets." , |
83 | }, |
84 | { |
85 | .longName = "cycle-start" , |
86 | .argInfo = POPT_ARG_NONE, |
87 | .arg = &option_cycle_start, |
88 | .descrip = "Print cycle start packets." , |
89 | }, |
90 | { |
91 | .longName = "verbose" , |
92 | .shortName = 'v', |
93 | .argInfo = POPT_ARG_NONE, |
94 | .arg = &option_verbose, |
95 | .descrip = "Verbose packet view." , |
96 | }, |
97 | { |
98 | .longName = "output" , |
99 | .shortName = 'o', |
100 | .argInfo = POPT_ARG_STRING, |
101 | .arg = &option_output, |
102 | .descrip = "Log to output file." , |
103 | .argDescrip = "FILENAME" |
104 | }, |
105 | { |
106 | .longName = "input" , |
107 | .shortName = 'i', |
108 | .argInfo = POPT_ARG_STRING, |
109 | .arg = &option_input, |
110 | .descrip = "Decode log from file." , |
111 | .argDescrip = "FILENAME" |
112 | }, |
113 | { |
114 | .longName = "version" , |
115 | .argInfo = POPT_ARG_NONE, |
116 | .arg = &option_version, |
117 | .descrip = "Specify print version info." , |
118 | }, |
119 | POPT_AUTOHELP |
120 | POPT_TABLEEND |
121 | }; |
122 | |
123 | /* Allow all ^C except the first to interrupt the program in the usual way. */ |
124 | static void |
125 | sigint_handler(int signal_num) |
126 | { |
127 | if (run == 1) { |
128 | run = 0; |
129 | signal(SIGINT, SIG_DFL); |
130 | } |
131 | } |
132 | |
133 | static struct subaction * |
134 | subaction_create(uint32_t *data, size_t length) |
135 | { |
136 | struct subaction *sa; |
137 | |
138 | /* we put the ack in the subaction struct for easy access. */ |
139 | sa = malloc(sizeof *sa - sizeof sa->packet + length); |
140 | if (!sa) |
141 | exit(EXIT_FAILURE); |
142 | sa->ack = data[length / 4 - 1]; |
143 | sa->length = length; |
144 | memcpy(&sa->packet, data, length); |
145 | |
146 | return sa; |
147 | } |
148 | |
149 | static void |
150 | subaction_destroy(struct subaction *sa) |
151 | { |
152 | free(sa); |
153 | } |
154 | |
155 | static struct list pending_transaction_list = { |
156 | &pending_transaction_list, &pending_transaction_list |
157 | }; |
158 | |
159 | static struct link_transaction * |
160 | link_transaction_lookup(int request_node, int response_node, int tlabel) |
161 | { |
162 | struct link_transaction *t; |
163 | |
164 | list_for_each_entry(t, &pending_transaction_list, link) { |
165 | if (t->request_node == request_node && |
166 | t->response_node == response_node && |
167 | t->tlabel == tlabel) |
168 | return t; |
169 | } |
170 | |
171 | t = malloc(sizeof *t); |
172 | if (!t) |
173 | exit(EXIT_FAILURE); |
174 | t->request_node = request_node; |
175 | t->response_node = response_node; |
176 | t->tlabel = tlabel; |
177 | list_init(list: &t->request_list); |
178 | list_init(list: &t->response_list); |
179 | |
180 | list_append(list: &pending_transaction_list, new_link: &t->link); |
181 | |
182 | return t; |
183 | } |
184 | |
185 | static void |
186 | link_transaction_destroy(struct link_transaction *t) |
187 | { |
188 | struct subaction *sa; |
189 | |
190 | while (!list_empty(list: &t->request_list)) { |
191 | sa = list_head(&t->request_list, struct subaction, link); |
192 | list_remove(link: &sa->link); |
193 | subaction_destroy(sa); |
194 | } |
195 | while (!list_empty(list: &t->response_list)) { |
196 | sa = list_head(&t->response_list, struct subaction, link); |
197 | list_remove(link: &sa->link); |
198 | subaction_destroy(sa); |
199 | } |
200 | free(t); |
201 | } |
202 | |
203 | struct protocol_decoder { |
204 | const char *name; |
205 | int (*decode)(struct link_transaction *t); |
206 | }; |
207 | |
208 | static const struct protocol_decoder protocol_decoders[] = { |
209 | { "FCP" , decode_fcp } |
210 | }; |
211 | |
212 | static void |
213 | handle_transaction(struct link_transaction *t) |
214 | { |
215 | struct subaction *sa; |
216 | int i; |
217 | |
218 | if (!t->request) { |
219 | printf("BUG in handle_transaction\n" ); |
220 | return; |
221 | } |
222 | |
223 | for (i = 0; i < array_length(protocol_decoders); i++) |
224 | if (protocol_decoders[i].decode(t)) |
225 | break; |
226 | |
227 | /* HACK: decode only fcp right now. */ |
228 | return; |
229 | |
230 | decode_link_packet(&t->request->packet, t->request->length, |
231 | PACKET_FIELD_TRANSACTION, 0); |
232 | if (t->response) |
233 | decode_link_packet(&t->response->packet, t->request->length, |
234 | PACKET_FIELD_TRANSACTION, 0); |
235 | else |
236 | printf("[no response]" ); |
237 | |
238 | if (option_verbose) { |
239 | list_for_each_entry(sa, &t->request_list, link) |
240 | print_packet((uint32_t *) &sa->packet, sa->length); |
241 | list_for_each_entry(sa, &t->response_list, link) |
242 | print_packet((uint32_t *) &sa->packet, sa->length); |
243 | } |
244 | printf("\r\n" ); |
245 | |
246 | link_transaction_destroy(t); |
247 | } |
248 | |
249 | static void |
250 | clear_pending_transaction_list(void) |
251 | { |
252 | struct link_transaction *t; |
253 | |
254 | while (!list_empty(list: &pending_transaction_list)) { |
255 | t = list_head(&pending_transaction_list, |
256 | struct link_transaction, link); |
257 | list_remove(link: &t->link); |
258 | link_transaction_destroy(t); |
259 | /* print unfinished transactions */ |
260 | } |
261 | } |
262 | |
263 | static const char * const tcode_names[] = { |
264 | [0x0] = "write_quadlet_request" , [0x6] = "read_quadlet_response" , |
265 | [0x1] = "write_block_request" , [0x7] = "read_block_response" , |
266 | [0x2] = "write_response" , [0x8] = "cycle_start" , |
267 | [0x3] = "reserved" , [0x9] = "lock_request" , |
268 | [0x4] = "read_quadlet_request" , [0xa] = "iso_data" , |
269 | [0x5] = "read_block_request" , [0xb] = "lock_response" , |
270 | }; |
271 | |
272 | static const char * const ack_names[] = { |
273 | [0x0] = "no ack" , [0x8] = "reserved (0x08)" , |
274 | [0x1] = "ack_complete" , [0x9] = "reserved (0x09)" , |
275 | [0x2] = "ack_pending" , [0xa] = "reserved (0x0a)" , |
276 | [0x3] = "reserved (0x03)" , [0xb] = "reserved (0x0b)" , |
277 | [0x4] = "ack_busy_x" , [0xc] = "reserved (0x0c)" , |
278 | [0x5] = "ack_busy_a" , [0xd] = "ack_data_error" , |
279 | [0x6] = "ack_busy_b" , [0xe] = "ack_type_error" , |
280 | [0x7] = "reserved (0x07)" , [0xf] = "reserved (0x0f)" , |
281 | }; |
282 | |
283 | static const char * const rcode_names[] = { |
284 | [0x0] = "complete" , [0x4] = "conflict_error" , |
285 | [0x1] = "reserved (0x01)" , [0x5] = "data_error" , |
286 | [0x2] = "reserved (0x02)" , [0x6] = "type_error" , |
287 | [0x3] = "reserved (0x03)" , [0x7] = "address_error" , |
288 | }; |
289 | |
290 | static const char * const retry_names[] = { |
291 | [0x0] = "retry_1" , |
292 | [0x1] = "retry_x" , |
293 | [0x2] = "retry_a" , |
294 | [0x3] = "retry_b" , |
295 | }; |
296 | |
297 | enum { |
298 | PACKET_RESERVED, |
299 | PACKET_REQUEST, |
300 | PACKET_RESPONSE, |
301 | PACKET_OTHER, |
302 | }; |
303 | |
304 | struct packet_info { |
305 | const char *name; |
306 | int type; |
307 | int response_tcode; |
308 | const struct packet_field *fields; |
309 | int field_count; |
310 | }; |
311 | |
312 | struct packet_field { |
313 | const char *name; /* Short name for field. */ |
314 | int offset; /* Location of field, specified in bits; */ |
315 | /* negative means from end of packet. */ |
316 | int width; /* Width of field, 0 means use data_length. */ |
317 | int flags; /* Show options. */ |
318 | const char * const *value_names; |
319 | }; |
320 | |
321 | #define COMMON_REQUEST_FIELDS \ |
322 | { "dest", 0, 16, PACKET_FIELD_TRANSACTION }, \ |
323 | { "tl", 16, 6 }, \ |
324 | { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ |
325 | { "tcode", 24, 4, PACKET_FIELD_TRANSACTION, tcode_names }, \ |
326 | { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ |
327 | { "src", 32, 16, PACKET_FIELD_TRANSACTION }, \ |
328 | { "offs", 48, 48, PACKET_FIELD_TRANSACTION } |
329 | |
330 | #define COMMON_RESPONSE_FIELDS \ |
331 | { "dest", 0, 16 }, \ |
332 | { "tl", 16, 6 }, \ |
333 | { "rt", 22, 2, PACKET_FIELD_DETAIL, retry_names }, \ |
334 | { "tcode", 24, 4, 0, tcode_names }, \ |
335 | { "pri", 28, 4, PACKET_FIELD_DETAIL }, \ |
336 | { "src", 32, 16 }, \ |
337 | { "rcode", 48, 4, PACKET_FIELD_TRANSACTION, rcode_names } |
338 | |
339 | static const struct packet_field read_quadlet_request_fields[] = { |
340 | COMMON_REQUEST_FIELDS, |
341 | { "crc" , 96, 32, PACKET_FIELD_DETAIL }, |
342 | { "ack" , 156, 4, 0, ack_names }, |
343 | }; |
344 | |
345 | static const struct packet_field read_quadlet_response_fields[] = { |
346 | COMMON_RESPONSE_FIELDS, |
347 | { "data" , 96, 32, PACKET_FIELD_TRANSACTION }, |
348 | { "crc" , 128, 32, PACKET_FIELD_DETAIL }, |
349 | { "ack" , 188, 4, 0, ack_names }, |
350 | }; |
351 | |
352 | static const struct packet_field read_block_request_fields[] = { |
353 | COMMON_REQUEST_FIELDS, |
354 | { "data_length" , 96, 16, PACKET_FIELD_TRANSACTION }, |
355 | { "extended_tcode" , 112, 16 }, |
356 | { "crc" , 128, 32, PACKET_FIELD_DETAIL }, |
357 | { "ack" , 188, 4, 0, ack_names }, |
358 | }; |
359 | |
360 | static const struct packet_field block_response_fields[] = { |
361 | COMMON_RESPONSE_FIELDS, |
362 | { "data_length" , 96, 16, PACKET_FIELD_DATA_LENGTH }, |
363 | { "extended_tcode" , 112, 16 }, |
364 | { "crc" , 128, 32, PACKET_FIELD_DETAIL }, |
365 | { "data" , 160, 0, PACKET_FIELD_TRANSACTION }, |
366 | { "crc" , -64, 32, PACKET_FIELD_DETAIL }, |
367 | { "ack" , -4, 4, 0, ack_names }, |
368 | }; |
369 | |
370 | static const struct packet_field write_quadlet_request_fields[] = { |
371 | COMMON_REQUEST_FIELDS, |
372 | { "data" , 96, 32, PACKET_FIELD_TRANSACTION }, |
373 | { "ack" , -4, 4, 0, ack_names }, |
374 | }; |
375 | |
376 | static const struct packet_field block_request_fields[] = { |
377 | COMMON_REQUEST_FIELDS, |
378 | { "data_length" , 96, 16, PACKET_FIELD_DATA_LENGTH | PACKET_FIELD_TRANSACTION }, |
379 | { "extended_tcode" , 112, 16, PACKET_FIELD_TRANSACTION }, |
380 | { "crc" , 128, 32, PACKET_FIELD_DETAIL }, |
381 | { "data" , 160, 0, PACKET_FIELD_TRANSACTION }, |
382 | { "crc" , -64, 32, PACKET_FIELD_DETAIL }, |
383 | { "ack" , -4, 4, 0, ack_names }, |
384 | }; |
385 | |
386 | static const struct packet_field write_response_fields[] = { |
387 | COMMON_RESPONSE_FIELDS, |
388 | { "reserved" , 64, 32, PACKET_FIELD_DETAIL }, |
389 | { "ack" , -4, 4, 0, ack_names }, |
390 | }; |
391 | |
392 | static const struct packet_field iso_data_fields[] = { |
393 | { "data_length" , 0, 16, PACKET_FIELD_DATA_LENGTH }, |
394 | { "tag" , 16, 2 }, |
395 | { "channel" , 18, 6 }, |
396 | { "tcode" , 24, 4, 0, tcode_names }, |
397 | { "sy" , 28, 4 }, |
398 | { "crc" , 32, 32, PACKET_FIELD_DETAIL }, |
399 | { "data" , 64, 0 }, |
400 | { "crc" , -64, 32, PACKET_FIELD_DETAIL }, |
401 | { "ack" , -4, 4, 0, ack_names }, |
402 | }; |
403 | |
404 | static const struct packet_info packet_info[] = { |
405 | { |
406 | .name = "write_quadlet_request" , |
407 | .type = PACKET_REQUEST, |
408 | .response_tcode = TCODE_WRITE_RESPONSE, |
409 | .fields = write_quadlet_request_fields, |
410 | .field_count = array_length(write_quadlet_request_fields) |
411 | }, |
412 | { |
413 | .name = "write_block_request" , |
414 | .type = PACKET_REQUEST, |
415 | .response_tcode = TCODE_WRITE_RESPONSE, |
416 | .fields = block_request_fields, |
417 | .field_count = array_length(block_request_fields) |
418 | }, |
419 | { |
420 | .name = "write_response" , |
421 | .type = PACKET_RESPONSE, |
422 | .fields = write_response_fields, |
423 | .field_count = array_length(write_response_fields) |
424 | }, |
425 | { |
426 | .name = "reserved" , |
427 | .type = PACKET_RESERVED, |
428 | }, |
429 | { |
430 | .name = "read_quadlet_request" , |
431 | .type = PACKET_REQUEST, |
432 | .response_tcode = TCODE_READ_QUADLET_RESPONSE, |
433 | .fields = read_quadlet_request_fields, |
434 | .field_count = array_length(read_quadlet_request_fields) |
435 | }, |
436 | { |
437 | .name = "read_block_request" , |
438 | .type = PACKET_REQUEST, |
439 | .response_tcode = TCODE_READ_BLOCK_RESPONSE, |
440 | .fields = read_block_request_fields, |
441 | .field_count = array_length(read_block_request_fields) |
442 | }, |
443 | { |
444 | .name = "read_quadlet_response" , |
445 | .type = PACKET_RESPONSE, |
446 | .fields = read_quadlet_response_fields, |
447 | .field_count = array_length(read_quadlet_response_fields) |
448 | }, |
449 | { |
450 | .name = "read_block_response" , |
451 | .type = PACKET_RESPONSE, |
452 | .fields = block_response_fields, |
453 | .field_count = array_length(block_response_fields) |
454 | }, |
455 | { |
456 | .name = "cycle_start" , |
457 | .type = PACKET_OTHER, |
458 | .fields = write_quadlet_request_fields, |
459 | .field_count = array_length(write_quadlet_request_fields) |
460 | }, |
461 | { |
462 | .name = "lock_request" , |
463 | .type = PACKET_REQUEST, |
464 | .fields = block_request_fields, |
465 | .field_count = array_length(block_request_fields) |
466 | }, |
467 | { |
468 | .name = "iso_data" , |
469 | .type = PACKET_OTHER, |
470 | .fields = iso_data_fields, |
471 | .field_count = array_length(iso_data_fields) |
472 | }, |
473 | { |
474 | .name = "lock_response" , |
475 | .type = PACKET_RESPONSE, |
476 | .fields = block_response_fields, |
477 | .field_count = array_length(block_response_fields) |
478 | }, |
479 | }; |
480 | |
481 | static int |
482 | handle_request_packet(uint32_t *data, size_t length) |
483 | { |
484 | struct link_packet *p = (struct link_packet *) data; |
485 | struct subaction *sa, *prev; |
486 | struct link_transaction *t; |
487 | |
488 | t = link_transaction_lookup(request_node: p->common.source, response_node: p->common.destination, |
489 | tlabel: p->common.tlabel); |
490 | sa = subaction_create(data, length); |
491 | t->request = sa; |
492 | |
493 | if (!list_empty(list: &t->request_list)) { |
494 | prev = list_tail(&t->request_list, |
495 | struct subaction, link); |
496 | |
497 | if (!ACK_BUSY(prev->ack)) { |
498 | /* |
499 | * error, we should only see ack_busy_* before the |
500 | * ack_pending/ack_complete -- this is an ack_pending |
501 | * instead (ack_complete would have finished the |
502 | * transaction). |
503 | */ |
504 | } |
505 | |
506 | if (prev->packet.common.tcode != sa->packet.common.tcode || |
507 | prev->packet.common.tlabel != sa->packet.common.tlabel) { |
508 | /* memcmp() ? */ |
509 | /* error, these should match for retries. */ |
510 | } |
511 | } |
512 | |
513 | list_append(list: &t->request_list, new_link: &sa->link); |
514 | |
515 | switch (sa->ack) { |
516 | case ACK_COMPLETE: |
517 | if (p->common.tcode != TCODE_WRITE_QUADLET_REQUEST && |
518 | p->common.tcode != TCODE_WRITE_BLOCK_REQUEST) |
519 | /* error, unified transactions only allowed for write */; |
520 | list_remove(link: &t->link); |
521 | handle_transaction(t); |
522 | break; |
523 | |
524 | case ACK_NO_ACK: |
525 | case ACK_DATA_ERROR: |
526 | case ACK_TYPE_ERROR: |
527 | list_remove(link: &t->link); |
528 | handle_transaction(t); |
529 | break; |
530 | |
531 | case ACK_PENDING: |
532 | /* request subaction phase over, wait for response. */ |
533 | break; |
534 | |
535 | case ACK_BUSY_X: |
536 | case ACK_BUSY_A: |
537 | case ACK_BUSY_B: |
538 | /* ok, wait for retry. */ |
539 | /* check that retry protocol is respected. */ |
540 | break; |
541 | } |
542 | |
543 | return 1; |
544 | } |
545 | |
546 | static int |
547 | handle_response_packet(uint32_t *data, size_t length) |
548 | { |
549 | struct link_packet *p = (struct link_packet *) data; |
550 | struct subaction *sa, *prev; |
551 | struct link_transaction *t; |
552 | |
553 | t = link_transaction_lookup(request_node: p->common.destination, response_node: p->common.source, |
554 | tlabel: p->common.tlabel); |
555 | if (list_empty(list: &t->request_list)) { |
556 | /* unsolicited response */ |
557 | } |
558 | |
559 | sa = subaction_create(data, length); |
560 | t->response = sa; |
561 | |
562 | if (!list_empty(list: &t->response_list)) { |
563 | prev = list_tail(&t->response_list, struct subaction, link); |
564 | |
565 | if (!ACK_BUSY(prev->ack)) { |
566 | /* |
567 | * error, we should only see ack_busy_* before the |
568 | * ack_pending/ack_complete |
569 | */ |
570 | } |
571 | |
572 | if (prev->packet.common.tcode != sa->packet.common.tcode || |
573 | prev->packet.common.tlabel != sa->packet.common.tlabel) { |
574 | /* use memcmp() instead? */ |
575 | /* error, these should match for retries. */ |
576 | } |
577 | } else { |
578 | prev = list_tail(&t->request_list, struct subaction, link); |
579 | if (prev->ack != ACK_PENDING) { |
580 | /* |
581 | * error, should not get response unless last request got |
582 | * ack_pending. |
583 | */ |
584 | } |
585 | |
586 | if (packet_info[prev->packet.common.tcode].response_tcode != |
587 | sa->packet.common.tcode) { |
588 | /* error, tcode mismatch */ |
589 | } |
590 | } |
591 | |
592 | list_append(list: &t->response_list, new_link: &sa->link); |
593 | |
594 | switch (sa->ack) { |
595 | case ACK_COMPLETE: |
596 | case ACK_NO_ACK: |
597 | case ACK_DATA_ERROR: |
598 | case ACK_TYPE_ERROR: |
599 | list_remove(link: &t->link); |
600 | handle_transaction(t); |
601 | /* transaction complete, remove t from pending list. */ |
602 | break; |
603 | |
604 | case ACK_PENDING: |
605 | /* error for responses. */ |
606 | break; |
607 | |
608 | case ACK_BUSY_X: |
609 | case ACK_BUSY_A: |
610 | case ACK_BUSY_B: |
611 | /* no problem, wait for next retry */ |
612 | break; |
613 | } |
614 | |
615 | return 1; |
616 | } |
617 | |
618 | static int |
619 | handle_packet(uint32_t *data, size_t length) |
620 | { |
621 | if (length == 0) { |
622 | printf("bus reset\r\n" ); |
623 | clear_pending_transaction_list(); |
624 | } else if (length > sizeof(struct phy_packet)) { |
625 | struct link_packet *p = (struct link_packet *) data; |
626 | |
627 | switch (packet_info[p->common.tcode].type) { |
628 | case PACKET_REQUEST: |
629 | return handle_request_packet(data, length); |
630 | |
631 | case PACKET_RESPONSE: |
632 | return handle_response_packet(data, length); |
633 | |
634 | case PACKET_OTHER: |
635 | case PACKET_RESERVED: |
636 | return 0; |
637 | } |
638 | } |
639 | |
640 | return 1; |
641 | } |
642 | |
643 | static unsigned int |
644 | get_bits(struct link_packet *packet, int offset, int width) |
645 | { |
646 | uint32_t *data = (uint32_t *) packet; |
647 | uint32_t index, shift, mask; |
648 | |
649 | index = offset / 32 + 1; |
650 | shift = 32 - (offset & 31) - width; |
651 | mask = width == 32 ? ~0 : (1 << width) - 1; |
652 | |
653 | return (data[index] >> shift) & mask; |
654 | } |
655 | |
656 | #if __BYTE_ORDER == __LITTLE_ENDIAN |
657 | #define byte_index(i) ((i) ^ 3) |
658 | #elif __BYTE_ORDER == __BIG_ENDIAN |
659 | #define byte_index(i) (i) |
660 | #else |
661 | #error unsupported byte order. |
662 | #endif |
663 | |
664 | static void |
665 | dump_data(unsigned char *data, int length) |
666 | { |
667 | int i, print_length; |
668 | |
669 | if (length > 128) |
670 | print_length = 128; |
671 | else |
672 | print_length = length; |
673 | |
674 | for (i = 0; i < print_length; i++) |
675 | printf("%s%02hhx" , |
676 | (i % 4 == 0 && i != 0) ? " " : "" , |
677 | data[byte_index(i)]); |
678 | |
679 | if (print_length < length) |
680 | printf(" (%d more bytes)" , length - print_length); |
681 | } |
682 | |
683 | static void |
684 | decode_link_packet(struct link_packet *packet, size_t length, |
685 | int include_flags, int exclude_flags) |
686 | { |
687 | const struct packet_info *pi; |
688 | int data_length = 0; |
689 | int i; |
690 | |
691 | pi = &packet_info[packet->common.tcode]; |
692 | |
693 | for (i = 0; i < pi->field_count; i++) { |
694 | const struct packet_field *f = &pi->fields[i]; |
695 | int offset; |
696 | |
697 | if (f->flags & exclude_flags) |
698 | continue; |
699 | if (include_flags && !(f->flags & include_flags)) |
700 | continue; |
701 | |
702 | if (f->offset < 0) |
703 | offset = length * 8 + f->offset - 32; |
704 | else |
705 | offset = f->offset; |
706 | |
707 | if (f->value_names != NULL) { |
708 | uint32_t bits; |
709 | |
710 | bits = get_bits(packet, offset, f->width); |
711 | printf("%s" , f->value_names[bits]); |
712 | } else if (f->width == 0) { |
713 | printf("%s=[" , f->name); |
714 | dump_data(data: (unsigned char *) packet + (offset / 8 + 4), length: data_length); |
715 | printf("]" ); |
716 | } else { |
717 | unsigned long long bits; |
718 | int high_width, low_width; |
719 | |
720 | if ((offset & ~31) != ((offset + f->width - 1) & ~31)) { |
721 | /* Bit field spans quadlet boundary. */ |
722 | high_width = ((offset + 31) & ~31) - offset; |
723 | low_width = f->width - high_width; |
724 | |
725 | bits = get_bits(packet, offset, width: high_width); |
726 | bits = (bits << low_width) | |
727 | get_bits(packet, offset: offset + high_width, width: low_width); |
728 | } else { |
729 | bits = get_bits(packet, offset, width: f->width); |
730 | } |
731 | |
732 | printf("%s=0x%0*llx" , f->name, (f->width + 3) / 4, bits); |
733 | |
734 | if (f->flags & PACKET_FIELD_DATA_LENGTH) |
735 | data_length = bits; |
736 | } |
737 | |
738 | if (i < pi->field_count - 1) |
739 | printf(", " ); |
740 | } |
741 | } |
742 | |
743 | static void |
744 | print_packet(uint32_t *data, size_t length) |
745 | { |
746 | int i; |
747 | |
748 | printf("%6u " , data[0]); |
749 | |
750 | if (length == 4) { |
751 | printf("bus reset" ); |
752 | } else if (length < sizeof(struct phy_packet)) { |
753 | printf("short packet: " ); |
754 | for (i = 1; i < length / 4; i++) |
755 | printf("%s%08x" , i == 0 ? "[" : " " , data[i]); |
756 | printf("]" ); |
757 | |
758 | } else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) { |
759 | struct phy_packet *pp = (struct phy_packet *) data; |
760 | |
761 | /* phy packet are 3 quadlets: the 1 quadlet payload, |
762 | * the bitwise inverse of the payload and the snoop |
763 | * mode ack */ |
764 | |
765 | switch (pp->common.identifier) { |
766 | case PHY_PACKET_CONFIGURATION: |
767 | if (!pp->phy_config.set_root && !pp->phy_config.set_gap_count) { |
768 | printf("ext phy config: phy_id=%02x" , pp->phy_config.root_id); |
769 | } else { |
770 | printf("phy config:" ); |
771 | if (pp->phy_config.set_root) |
772 | printf(" set_root_id=%02x" , pp->phy_config.root_id); |
773 | if (pp->phy_config.set_gap_count) |
774 | printf(" set_gap_count=%d" , pp->phy_config.gap_count); |
775 | } |
776 | break; |
777 | |
778 | case PHY_PACKET_LINK_ON: |
779 | printf("link-on packet, phy_id=%02x" , pp->link_on.phy_id); |
780 | break; |
781 | |
782 | case PHY_PACKET_SELF_ID: |
783 | if (pp->self_id.extended) { |
784 | printf("extended self id: phy_id=%02x, seq=%d" , |
785 | pp->ext_self_id.phy_id, pp->ext_self_id.sequence); |
786 | } else { |
787 | static const char * const speed_names[] = { |
788 | "S100" , "S200" , "S400" , "BETA" |
789 | }; |
790 | printf("self id: phy_id=%02x, link %s, gap_count=%d, speed=%s%s%s" , |
791 | pp->self_id.phy_id, |
792 | (pp->self_id.link_active ? "active" : "not active" ), |
793 | pp->self_id.gap_count, |
794 | speed_names[pp->self_id.phy_speed], |
795 | (pp->self_id.contender ? ", irm contender" : "" ), |
796 | (pp->self_id.initiated_reset ? ", initiator" : "" )); |
797 | } |
798 | break; |
799 | default: |
800 | printf("unknown phy packet: " ); |
801 | for (i = 1; i < length / 4; i++) |
802 | printf("%s%08x" , i == 0 ? "[" : " " , data[i]); |
803 | printf("]" ); |
804 | break; |
805 | } |
806 | } else { |
807 | struct link_packet *packet = (struct link_packet *) data; |
808 | |
809 | decode_link_packet(packet, length, 0, |
810 | option_verbose ? 0 : PACKET_FIELD_DETAIL); |
811 | } |
812 | |
813 | if (option_hex) { |
814 | printf(" [" ); |
815 | dump_data(data: (unsigned char *) data + 4, length: length - 4); |
816 | printf("]" ); |
817 | } |
818 | |
819 | printf("\r\n" ); |
820 | } |
821 | |
822 | #define HIDE_CURSOR "\033[?25l" |
823 | #define SHOW_CURSOR "\033[?25h" |
824 | #define CLEAR "\033[H\033[2J" |
825 | |
826 | static void |
827 | print_stats(uint32_t *data, size_t length) |
828 | { |
829 | static int bus_reset_count, short_packet_count, phy_packet_count; |
830 | static int tcode_count[16]; |
831 | static struct timeval last_update; |
832 | struct timeval now; |
833 | int i; |
834 | |
835 | if (length == 0) |
836 | bus_reset_count++; |
837 | else if (length < sizeof(struct phy_packet)) |
838 | short_packet_count++; |
839 | else if (length == sizeof(struct phy_packet) && data[1] == ~data[2]) |
840 | phy_packet_count++; |
841 | else { |
842 | struct link_packet *packet = (struct link_packet *) data; |
843 | tcode_count[packet->common.tcode]++; |
844 | } |
845 | |
846 | gettimeofday(&now, NULL); |
847 | if (now.tv_sec <= last_update.tv_sec && |
848 | now.tv_usec < last_update.tv_usec + 500000) |
849 | return; |
850 | |
851 | last_update = now; |
852 | printf(CLEAR HIDE_CURSOR |
853 | " bus resets : %8d\n" |
854 | " short packets : %8d\n" |
855 | " phy packets : %8d\n" , |
856 | bus_reset_count, short_packet_count, phy_packet_count); |
857 | |
858 | for (i = 0; i < array_length(packet_info); i++) |
859 | if (packet_info[i].type != PACKET_RESERVED) |
860 | printf(" %-24s: %8d\n" , packet_info[i].name, tcode_count[i]); |
861 | printf(SHOW_CURSOR "\n" ); |
862 | } |
863 | |
864 | static struct termios saved_attributes; |
865 | |
866 | static void |
867 | reset_input_mode(void) |
868 | { |
869 | tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes); |
870 | } |
871 | |
872 | static void |
873 | set_input_mode(void) |
874 | { |
875 | struct termios tattr; |
876 | |
877 | /* Make sure stdin is a terminal. */ |
878 | if (!isatty(STDIN_FILENO)) { |
879 | fprintf(stderr, "Not a terminal.\n" ); |
880 | exit(EXIT_FAILURE); |
881 | } |
882 | |
883 | /* Save the terminal attributes so we can restore them later. */ |
884 | tcgetattr(STDIN_FILENO, &saved_attributes); |
885 | atexit(reset_input_mode); |
886 | |
887 | /* Set the funny terminal modes. */ |
888 | tcgetattr(STDIN_FILENO, &tattr); |
889 | tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ |
890 | tattr.c_cc[VMIN] = 1; |
891 | tattr.c_cc[VTIME] = 0; |
892 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); |
893 | } |
894 | |
895 | int main(int argc, const char *argv[]) |
896 | { |
897 | uint32_t buf[128 * 1024]; |
898 | uint32_t filter; |
899 | int length, retval, view; |
900 | int fd = -1; |
901 | FILE *output = NULL, *input = NULL; |
902 | poptContext con; |
903 | char c; |
904 | struct pollfd pollfds[2]; |
905 | |
906 | sys_sigint_handler = signal(SIGINT, sigint_handler); |
907 | |
908 | con = poptGetContext(NULL, argc, argv, options, 0); |
909 | retval = poptGetNextOpt(con); |
910 | if (retval < -1) { |
911 | poptPrintUsage(con, stdout, 0); |
912 | return -1; |
913 | } |
914 | |
915 | if (option_version) { |
916 | printf("dump tool for nosy sniffer, version %s\n" , VERSION); |
917 | return 0; |
918 | } |
919 | |
920 | if (__BYTE_ORDER != __LITTLE_ENDIAN) |
921 | fprintf(stderr, "warning: nosy has only been tested on little " |
922 | "endian machines\n" ); |
923 | |
924 | if (option_input != NULL) { |
925 | input = fopen(option_input, "r" ); |
926 | if (input == NULL) { |
927 | fprintf(stderr, "Could not open %s, %m\n" , option_input); |
928 | return -1; |
929 | } |
930 | } else { |
931 | fd = open(option_nosy_device, O_RDWR); |
932 | if (fd < 0) { |
933 | fprintf(stderr, "Could not open %s, %m\n" , option_nosy_device); |
934 | return -1; |
935 | } |
936 | set_input_mode(); |
937 | } |
938 | |
939 | if (strcmp(option_view, "transaction" ) == 0) |
940 | view = VIEW_TRANSACTION; |
941 | else if (strcmp(option_view, "stats" ) == 0) |
942 | view = VIEW_STATS; |
943 | else |
944 | view = VIEW_PACKET; |
945 | |
946 | if (option_output) { |
947 | output = fopen(option_output, "w" ); |
948 | if (output == NULL) { |
949 | fprintf(stderr, "Could not open %s, %m\n" , option_output); |
950 | return -1; |
951 | } |
952 | } |
953 | |
954 | setvbuf(stdout, NULL, _IOLBF, BUFSIZ); |
955 | |
956 | filter = ~0; |
957 | if (!option_iso) |
958 | filter &= ~(1 << TCODE_STREAM_DATA); |
959 | if (!option_cycle_start) |
960 | filter &= ~(1 << TCODE_CYCLE_START); |
961 | if (view == VIEW_STATS) |
962 | filter = ~(1 << TCODE_CYCLE_START); |
963 | |
964 | ioctl(fd, NOSY_IOC_FILTER, filter); |
965 | |
966 | ioctl(fd, NOSY_IOC_START); |
967 | |
968 | pollfds[0].fd = fd; |
969 | pollfds[0].events = POLLIN; |
970 | pollfds[1].fd = STDIN_FILENO; |
971 | pollfds[1].events = POLLIN; |
972 | |
973 | while (run) { |
974 | if (input != NULL) { |
975 | if (fread(&length, sizeof length, 1, input) != 1) |
976 | return 0; |
977 | fread(buf, 1, length, input); |
978 | } else { |
979 | poll(pollfds, 2, -1); |
980 | if (pollfds[1].revents) { |
981 | read(STDIN_FILENO, &c, sizeof c); |
982 | switch (c) { |
983 | case 'q': |
984 | if (output != NULL) |
985 | fclose(output); |
986 | return 0; |
987 | } |
988 | } |
989 | |
990 | if (pollfds[0].revents) |
991 | length = read(fd, buf, sizeof buf); |
992 | else |
993 | continue; |
994 | } |
995 | |
996 | if (output != NULL) { |
997 | fwrite(&length, sizeof length, 1, output); |
998 | fwrite(buf, 1, length, output); |
999 | } |
1000 | |
1001 | switch (view) { |
1002 | case VIEW_TRANSACTION: |
1003 | handle_packet(buf, length); |
1004 | break; |
1005 | case VIEW_PACKET: |
1006 | print_packet(buf, length); |
1007 | break; |
1008 | case VIEW_STATS: |
1009 | print_stats(buf, length); |
1010 | break; |
1011 | } |
1012 | } |
1013 | |
1014 | if (output != NULL) |
1015 | fclose(output); |
1016 | |
1017 | close(fd); |
1018 | |
1019 | poptFreeContext(con); |
1020 | |
1021 | return 0; |
1022 | } |
1023 | |