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