Note that pre-0.6 libpcap didn't handle HP-UX as well as 0.6 and later
[obnox/wireshark/wip.git] / packet-slimp3.c
1 /* packet-slimp3.c
2  * Routines for SliMP3 protocol dissection
3  *
4  * Ashok Narayanan <ashokn@cisco.com>
5  *
6  * Adds support for the data packet protocol for the SliMP3
7  * See www.slimdevices.com for details.
8  *
9  * $Id: packet-slimp3.c,v 1.5 2002/04/29 08:20:09 guy Exp $
10  *
11  * Ethereal - Network traffic analyzer
12  * By Gerald Combs <gerald@ethereal.com>
13  * Copyright 1998 Gerald Combs
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * 
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #include <glib.h>
43 #include <epan/packet.h>
44 #include <epan/conversation.h>
45
46 static int proto_slimp3 = -1;
47 static int hf_slimp3_opcode = -1;
48 static int hf_slimp3_ir = -1;
49 static int hf_slimp3_display = -1;
50 static int hf_slimp3_control = -1;
51 static int hf_slimp3_hello = -1;
52 static int hf_slimp3_i2c = -1;
53 static int hf_slimp3_data_request = -1;
54 static int hf_slimp3_data = -1;
55 static int hf_slimp3_discover_request = -1;
56 static int hf_slimp3_discover_response = -1;
57
58 static gint ett_slimp3 = -1;
59
60 static dissector_handle_t slimp3_handle;
61
62 #define UDP_PORT_SLIMP3    1069
63
64 #define SLIMP3_IR       'i'
65 #define SLIMP3_CONTROL  's'
66 #define SLIMP3_HELLO    'h'
67 #define SLIMP3_DATA     'm'
68 #define SLIMP3_DATA_REQ 'r'
69 #define SLIMP3_DISPLAY  'l'
70 #define SLIMP3_I2C      '2'
71 #define SLIMP3_DISC_REQ 'd'
72 #define SLIMP3_DISC_RSP 'D'
73
74 static const value_string slimp3_opcode_vals[] = {
75   { SLIMP3_IR,       "Infrared Remote Code" },
76   { SLIMP3_CONTROL,  "Stream Control" },
77   { SLIMP3_DATA,     "MPEG Data" },
78   { SLIMP3_DATA_REQ, "Data Request" },
79   { SLIMP3_HELLO,    "Hello" },
80   { SLIMP3_DISPLAY,  "Display" },
81   { SLIMP3_I2C,      "I2C" },
82   { SLIMP3_DISC_REQ, "Discovery Request" },
83   { SLIMP3_DISC_RSP, "Discovery Response" },
84   { 0,               NULL }
85 };
86
87 static const value_string slimp3_ir_codes_jvc[] = {
88     { 0xf786, "One" }, 
89     { 0xf746, "Two" }, 
90     { 0xf7c6, "Three" }, 
91     { 0xf726, "Four" }, 
92     { 0xf7a6, "Five" }, 
93     { 0xf766, "Six" }, 
94     { 0xf7e6, "Seven" }, 
95     { 0xf716, "Eight" }, 
96     { 0xf796, "Nine" }, 
97     { 0xf776, "Ten" }, 
98
99     { 0xf7f6, "Picture-In-Picture" }, 
100     /* { 0xf7XX, "Enter" }, */
101     { 0xf70e, "Back" }, 
102     { 0xf732, "Play" }, 
103     { 0xf76e, "Forward" }, 
104     { 0xf743, "Record" }, 
105     { 0xf7c2, "Stop" }, 
106     { 0xf7b2, "Pause" }, 
107     /* { 0xf7XX, "TV/Video" }, */
108     { 0xf703, "Display" }, 
109     { 0xf7b3, "Sleep" }, 
110     { 0xf7b6, "Guide" }, 
111     { 0xf70b, "Up" }, 
112     { 0xf74b, "Left" }, 
113     { 0xf7cb, "Right" }, 
114     { 0xf78b, "Down" }, 
115     { 0xf783, "Menu" }, 
116     { 0xf72b, "OK" }, 
117     { 0xf778, "Volume Up" }, 
118     { 0xf7f8, "Volume Down" }, 
119     { 0xf70d, "Channel Up" }, 
120     { 0xf78d, "Channel Down" }, 
121     /* { 0xf7XX, "Mute" },  */
122     { 0xf7ab, "Recall" }, 
123     { 0xf702, "Power" }, 
124 };
125
126 static const value_string slimp3_display_commands[] = {
127     {  0x1, "Clear Display"},
128     {  0x2, "Cursor to 1st Line Home"},
129
130     {  0x4, "Mode: Decrement Address, Shift Cursor"},
131     {  0x5, "Mode: Decrement Address, Shift Display"},
132     {  0x6, "Mode: Increment Address, Shift Cursor"},
133     {  0x7, "Mode: Increment Address, Shift Display"},
134     
135     {  0x8, "Display Off"},
136     {  0xd, "Display On, With Blinking"},
137     {  0xe, "Display On, With Cursor"},
138     {  0xf, "Display On, With Cursor And Blinking"},
139
140     { 0x10, "Move Cursor Left"},
141     { 0x14, "Move Cursor Right"},
142     { 0x18, "Shift Display Left"},
143     { 0x1b, "Shift Display Right"},
144
145     { 0x30, "Set (8-bit)"},
146     { 0x20, "Set (4-bit)"},
147
148     { 0xa0, "Cursor to Top Right"},
149     { 0xc0, "Cursor to 2nd Line Home"},
150
151     {    0, NULL},
152 };
153
154 static const value_string slimp3_display_fset8[] = {
155     { 0x0, "Brightness 100%"},
156     { 0x1, "Brightness 75%"},
157     { 0x2, "Brightness 50%"},
158     { 0x3, "Brightness 25%"},
159
160     {   0, NULL },
161 };
162
163 static const value_string slimp3_stream_control[] = {
164     { 1, "Reset buffer, Start New Stream"},
165     { 2, "Pause Playback"},
166     { 4, "Resume Playback"},
167     {   0, NULL },
168 };
169
170 static void
171 dissect_slimp3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
172 {
173     proto_tree  *slimp3_tree = NULL;
174     proto_item  *ti = NULL;
175     gint                i1;
176     gint                offset = 0;
177     guint16             opcode;
178     char addc_str[101];
179     char *addc_strp;
180     
181
182     if (check_col(pinfo->cinfo, COL_PROTOCOL))
183         col_set_str(pinfo->cinfo, COL_PROTOCOL, "SliMP3");
184
185     opcode = tvb_get_guint8(tvb, offset);
186
187     if (check_col(pinfo->cinfo, COL_INFO)) {
188
189         col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
190                      val_to_str(opcode, slimp3_opcode_vals, "Unknown (%c)"));
191
192     }
193
194     addc_strp = addc_str;
195     if (tree) {
196
197         ti = proto_tree_add_item(tree, proto_slimp3, tvb, offset, -1, FALSE);
198         slimp3_tree = proto_item_add_subtree(ti, ett_slimp3);
199
200         proto_tree_add_uint(slimp3_tree, hf_slimp3_opcode, tvb,
201                             offset, 1, opcode);
202     }
203     switch (opcode) {
204
205     case SLIMP3_IR:
206         if (tree) {
207             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_ir, tvb, offset+8, 4, FALSE);
208
209             i1 = tvb_get_ntohl(tvb, offset+2);
210             proto_tree_add_text(slimp3_tree, tvb, offset+2, 4, "Uptime: %u sec (%u ticks)", 
211                                 i1/625000, i1);
212             proto_tree_add_text(slimp3_tree, tvb, offset+6, 1, "Code identifier: 0x%0x: %s", 
213                                 tvb_get_guint8(tvb, offset+6), 
214                                 tvb_get_guint8(tvb, offset+6)==0xff ? "JVC DVD Player" : "Unknown");
215             proto_tree_add_text(slimp3_tree, tvb, offset+7, 1, "Code bits: %d",
216                                 tvb_get_guint8(tvb, offset+7));
217
218             i1 = tvb_get_ntohl(tvb, offset+8);
219             /* Is this a standard JVC remote code? */
220             if (tvb_get_guint8(tvb, offset+6) == 0xff && 
221                 tvb_get_guint8(tvb, offset+7) == 16) {
222
223                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4, 
224                                     "Infrared Code: %s: 0x%0x", 
225                                     val_to_str(i1, slimp3_ir_codes_jvc, "Unknown"),
226                                     tvb_get_ntohl(tvb, offset+8));
227             } else {
228                 /* Unknown code; just write it */
229                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4, "Infrared Code: 0x%0x", 
230                                     tvb_get_ntohl(tvb, offset+8));
231             }
232         }
233         if (check_col(pinfo->cinfo, COL_INFO)) {
234             i1 = tvb_get_ntohl(tvb, offset+8);
235             if (tvb_get_guint8(tvb, offset+6) == 0xff && 
236                 tvb_get_guint8(tvb, offset+7) == 16) {
237                 col_append_fstr(pinfo->cinfo, COL_INFO, ", JVC: %s", 
238                                 val_to_str(i1, slimp3_ir_codes_jvc, "Unknown (0x%0x)"));
239             } else {
240                 /* Unknown code; just write it */
241                 col_append_fstr(pinfo->cinfo, COL_INFO, ", 0x%0x", i1);
242             }
243         }
244         break;
245
246     case SLIMP3_DISPLAY:
247         if (tree) {
248             int in_str;
249
250             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_display,
251                                        tvb, offset, 1, FALSE);
252
253             /* Loop through the commands */
254             i1 = 18;
255             in_str = 0;
256             while (i1 < tvb_reported_length_remaining(tvb, offset)) {
257                 switch(tvb_get_guint8(tvb, offset + i1)) {
258                 case 0:
259                     in_str = 0; 
260                     proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
261                                         "Delay (%d ms)", tvb_get_guint8(tvb, offset + i1 + 1));
262                     i1 += 2;
263                     break;
264                 case 3:
265                     if (ti && in_str) {
266                         proto_item_append_text(ti, "%c", 
267                                                tvb_get_guint8(tvb, offset + i1 + 1));
268                     } else {
269                         ti = proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
270                                                  "String: %c", 
271                                                  tvb_get_guint8(tvb, offset + i1 + 1));
272                         in_str = 1;
273                     }
274                     i1 += 2;
275                     break;
276
277                 case 2:
278                     in_str = 0; 
279                     ti = proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
280                                              "Command: %s", 
281                                              val_to_str(tvb_get_guint8(tvb, offset + i1 + 1), 
282                                                         slimp3_display_commands, 
283                                                         "Unknown (0x%0x)"));
284                     if ((tvb_get_guint8(tvb, offset + i1 + 1) & 0xf0) == 0x30) { 
285                         proto_item_append_text(ti, ": %s", 
286                                                val_to_str(tvb_get_guint8(tvb, offset + i1 + 2),
287                                                           slimp3_display_fset8, 
288                                                           "Unknown (0x%0x)"));
289                         i1 += 2 ;
290                     }
291                     i1 += 2;
292                     break;
293                         
294                 default:
295                     proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
296                                         "Unknown 0x%0x, 0x%0x", 
297                                         tvb_get_guint8(tvb, offset + i1),
298                                         tvb_get_guint8(tvb, offset + i1 + 1));
299                     i1 += 2;
300                     break;
301                 }
302             }
303         }
304
305         if (check_col(pinfo->cinfo, COL_INFO)) {
306             i1 = 18;
307             addc_strp = addc_str;
308             while (tvb_offset_exists(tvb, offset + i1)) {
309                 switch (tvb_get_guint8(tvb, offset + i1)) {
310
311                 case 0:
312                     *addc_strp++ = '.';
313                     break;
314
315                 case 2:
316                     *addc_strp++ = '|'; 
317                     if (tvb_offset_exists(tvb, offset + i1 + 1) &&
318                           (tvb_get_guint8(tvb, offset + i1 + 1) & 0xf0) == 0x30)
319                         i1 += 2;
320                     break;
321
322                 case 3:
323                     if (tvb_offset_exists(tvb, offset + i1 + 1)) {
324                         if (addc_strp == addc_str || 
325                               *(addc_strp-1) != ' ' ||
326                               tvb_get_guint8(tvb, offset + i1 + 1) != ' ')
327                             *addc_strp++ = tvb_get_guint8(tvb, offset + i1 + 1);
328                     }
329                 }
330                 i1 += 2;
331             }
332             *addc_strp = 0;
333             if (addc_strp - addc_str > 0)
334                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", addc_str);
335         }
336
337         break;
338
339     case SLIMP3_CONTROL:
340         if (tree) {
341             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_control,
342                                        tvb, offset+1, 1, FALSE);
343             proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Command: %s",
344                                 val_to_str(tvb_get_guint8(tvb, offset+1), 
345                                            slimp3_stream_control, "Unknown (0x%0x)"));
346         }
347
348         if (check_col(pinfo->cinfo, COL_INFO)) {
349             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", 
350                             val_to_str(tvb_get_guint8(tvb, offset+1), 
351                                        slimp3_stream_control, "Unknown (0x%0x)"));
352         }
353         break;
354
355     case SLIMP3_HELLO:
356         if (tree) {
357             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_hello,
358                                        tvb, offset+1, 1, FALSE);
359             if (pinfo->destport == UDP_PORT_SLIMP3) {
360                 guint8 fw_ver = 0;
361                 /* Hello response; client->server */
362                 proto_tree_add_text(slimp3_tree, tvb, offset, 1, "Hello Response (Client --> Server)");
363                 proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Device ID: %d", 
364                                     tvb_get_guint8(tvb, offset+1));
365                 fw_ver = tvb_get_guint8(tvb, offset+2);
366                 proto_tree_add_text(slimp3_tree, tvb, offset+2, 1, "Firmware Revision: %d.%d (0x%0x)", 
367                                     fw_ver>>4, fw_ver & 0xf, fw_ver);
368             } else {
369                 /* Hello request; server->client */
370                 proto_tree_add_text(slimp3_tree, tvb, offset, 1, "Hello Request (Server --> Client)");
371             }
372         }
373         break;
374
375     case SLIMP3_I2C:
376         if (tree) {
377             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_i2c,
378                                        tvb, offset, 1, FALSE);
379             if (pinfo->destport == UDP_PORT_SLIMP3) {
380                 /* Hello response; client->server */
381                 proto_tree_add_text(slimp3_tree, tvb, offset, -1, 
382                                     "I2C Response (Client --> Server)");
383             } else {
384                 /* Hello request; server->client */
385                 proto_tree_add_text(slimp3_tree, tvb, offset, -1, 
386                                     "I2C Request (Server --> Client)");
387             }
388         }
389
390         if (check_col(pinfo->cinfo, COL_INFO)) {
391             if (pinfo->destport == UDP_PORT_SLIMP3) {
392                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Response");
393             } else {
394                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Request");
395             }
396         }
397         break;
398
399     case SLIMP3_DATA_REQ:
400         if (tree) {
401             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data_request,
402                                        tvb, offset, 1, FALSE);
403             proto_tree_add_text(slimp3_tree, tvb, offset+2, 2, 
404                                 "Requested offset: %d bytes.", 
405                                 tvb_get_ntohs(tvb, offset+2)*2);
406         }
407
408         if (check_col(pinfo->cinfo, COL_INFO)) {
409             col_append_fstr(pinfo->cinfo, COL_INFO, ", Offset: %d bytes",
410                             tvb_get_ntohs(tvb, offset+2)*2);
411         }
412         break;
413
414     case SLIMP3_DATA:
415         if (tree) {
416             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data,
417                                        tvb, offset, 1, FALSE);
418             proto_tree_add_text(slimp3_tree, tvb, offset, -1, 
419                                 "Length: %d bytes", tvb_reported_length_remaining(tvb, offset+18));
420             proto_tree_add_text(slimp3_tree, tvb, offset+2, 2, 
421                                 "Buffer offset: %d bytes.", 
422                                 tvb_get_ntohs(tvb, offset+2) * 2);
423         }
424
425         if (check_col(pinfo->cinfo, COL_INFO)) {
426             col_append_fstr(pinfo->cinfo, COL_INFO, ", Length: %d bytes, Offset: %d bytes.",
427                             tvb_reported_length_remaining(tvb, offset+18),
428                             tvb_get_ntohs(tvb, offset+2) * 2);
429         }
430         break;
431
432     case SLIMP3_DISC_REQ:
433         if (tree) {
434             guint8 fw_ver;
435             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_discover_request,
436                                        tvb, offset, 1, FALSE);
437             proto_tree_add_text(slimp3_tree, tvb, offset+1, 1,
438                                 "Device ID: %d.", tvb_get_guint8(tvb, offset+1));
439             fw_ver = tvb_get_guint8(tvb, offset+2);
440             proto_tree_add_text(slimp3_tree, tvb, offset+2, 1, "Firmware Revision: %d.%d (0x%0x)", 
441                                 fw_ver>>4, fw_ver & 0xf, fw_ver);
442         }
443
444         if (check_col(pinfo->cinfo, COL_INFO)) {
445             guint8 fw_ver = tvb_get_guint8(tvb, offset+2);
446             col_append_fstr(pinfo->cinfo, COL_INFO, ", Device ID: %d. Firmware: %d.%d",
447                             tvb_get_guint8(tvb, offset+1), fw_ver>>4, fw_ver & 0xf);
448         }
449         break;
450
451     case SLIMP3_DISC_RSP:
452         if (tree) {
453             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_discover_response,
454                                        tvb, offset, 1, FALSE);
455             proto_tree_add_text(slimp3_tree, tvb, offset+2, 4,
456                                 "Server Address: %s.", 
457                                 ip_to_str(tvb_get_ptr(tvb, offset+2, 4)));
458             proto_tree_add_text(slimp3_tree, tvb, offset+6, 2, 
459                                 "Server Port: %d", tvb_get_ntohs(tvb, offset + 6));
460         }
461
462         if (check_col(pinfo->cinfo, COL_INFO)) {
463             col_append_fstr(pinfo->cinfo, COL_INFO, ", Server Address: %s. Server Port: %d", 
464                             ip_to_str(tvb_get_ptr(tvb, offset+2, 4)),
465                             tvb_get_ntohs(tvb, offset + 6));
466         }
467         break;
468
469     default:
470         if (tree) {
471             proto_tree_add_text(slimp3_tree, tvb, offset, -1,
472                                 "Data (%d bytes)", tvb_reported_length_remaining(tvb, offset));
473         }
474         break;
475
476     }
477 }
478
479 void
480 proto_register_slimp3(void)
481 {
482   static hf_register_info hf[] = {
483     { &hf_slimp3_opcode,
484       { "Opcode",             "slimp3.opcode",
485         FT_UINT8, BASE_DEC, VALS(slimp3_opcode_vals), 0x0,
486         "SLIMP3 message type", HFILL }},
487
488     { &hf_slimp3_ir,
489       { "Infrared",           "slimp3.ir",
490         FT_UINT32, BASE_HEX, NULL, 0x0,
491         "SLIMP3 Infrared command", HFILL }},
492
493     { &hf_slimp3_control,
494       { "Control Packet",   "slimp3.control",
495         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
496         "SLIMP3 control", HFILL }},
497
498     { &hf_slimp3_display,
499       { "Display",                    "slimp3.display",
500         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
501         "SLIMP3 display", HFILL }},
502
503     { &hf_slimp3_hello,
504       { "Hello",                      "slimp3.hello",
505         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
506         "SLIMP3 hello", HFILL }},
507
508     { &hf_slimp3_i2c,
509       { "I2C",                "slimp3.i2c",
510         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
511         "SLIMP3 I2C", HFILL }},
512
513     { &hf_slimp3_data,
514       { "Data",              "slimp3.data",
515         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
516         "SLIMP3 Data", HFILL }},
517
518     { &hf_slimp3_data_request,
519       { "Data Request",      "slimp3.data_req",
520         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
521         "SLIMP3 Data Request", HFILL }},
522
523     { &hf_slimp3_discover_request,
524       { "Discovery Request", "slimp3.discovery_req",
525         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
526         "SLIMP3 Discovery Request", HFILL }},
527
528     { &hf_slimp3_discover_response,
529       { "Discovery Response", "slimp3.discovery_response",
530         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
531         "SLIMP3 Discovery Response", HFILL }},
532
533   };
534   static gint *ett[] = {
535     &ett_slimp3,
536   };
537
538   proto_slimp3 = proto_register_protocol("SliMP3 Communication Protocol",
539                                        "SliMP3", "slimp3");
540   proto_register_field_array(proto_slimp3, hf, array_length(hf));
541   proto_register_subtree_array(ett, array_length(ett));
542
543   slimp3_handle = create_dissector_handle(dissect_slimp3, proto_slimp3);
544 }
545
546 void
547 proto_reg_handoff_slimp3(void)
548 {
549   dissector_add("udp.port", UDP_PORT_SLIMP3, slimp3_handle);
550 }