Use Ashok's IEEE-float-to-long code as the basis for
[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.4 2002/02/02 02:59:23 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_tree  *el_tree = NULL;
175     proto_item  *ti = NULL;
176     conversation_t  *conversation;
177     gint                i1;
178     gint                offset = 0;
179     guint16             opcode;
180     guint16         error;
181     guint32      i;
182     guint8       subcode;
183     char addc_str[101];
184     char *addc_strp;
185     
186
187     if (check_col(pinfo->cinfo, COL_PROTOCOL))
188         col_set_str(pinfo->cinfo, COL_PROTOCOL, "SliMP3");
189
190     opcode = tvb_get_guint8(tvb, offset);
191
192     if (check_col(pinfo->cinfo, COL_INFO)) {
193
194         col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
195                      val_to_str(opcode, slimp3_opcode_vals, "Unknown (%c)"));
196
197     }
198
199     addc_strp = addc_str;
200     if (tree) {
201
202         ti = proto_tree_add_item(tree, proto_slimp3, tvb, offset, -1, FALSE);
203         slimp3_tree = proto_item_add_subtree(ti, ett_slimp3);
204
205         proto_tree_add_uint(slimp3_tree, hf_slimp3_opcode, tvb,
206                             offset, 1, opcode);
207     }
208     switch (opcode) {
209
210     case SLIMP3_IR:
211         if (tree) {
212             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_ir, tvb, offset+8, 4, FALSE);
213
214             i1 = tvb_get_ntohl(tvb, offset+2);
215             proto_tree_add_text(slimp3_tree, tvb, offset+2, 4, "Uptime: %u sec (%u ticks)", 
216                                 i1/625000, i1);
217             proto_tree_add_text(slimp3_tree, tvb, offset+6, 1, "Code identifier: 0x%0x: %s", 
218                                 tvb_get_guint8(tvb, offset+6), 
219                                 tvb_get_guint8(tvb, offset+6)==0xff ? "JVC DVD Player" : "Unknown");
220             proto_tree_add_text(slimp3_tree, tvb, offset+7, 1, "Code bits: %d",
221                                 tvb_get_guint8(tvb, offset+7));
222
223             i1 = tvb_get_ntohl(tvb, offset+8);
224             /* Is this a standard JVC remote code? */
225             if (tvb_get_guint8(tvb, offset+6) == 0xff && 
226                 tvb_get_guint8(tvb, offset+7) == 16) {
227
228                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4, 
229                                     "Infrared Code: %s: 0x%0x", 
230                                     val_to_str(i1, slimp3_ir_codes_jvc, "Unknown"),
231                                     tvb_get_ntohl(tvb, offset+8));
232             } else {
233                 /* Unknown code; just write it */
234                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4, "Infrared Code: 0x%0x", 
235                                     tvb_get_ntohl(tvb, offset+8));
236             }
237         }
238         if (check_col(pinfo->cinfo, COL_INFO)) {
239             i1 = tvb_get_ntohl(tvb, offset+8);
240             if (tvb_get_guint8(tvb, offset+6) == 0xff && 
241                 tvb_get_guint8(tvb, offset+7) == 16) {
242                 col_append_fstr(pinfo->cinfo, COL_INFO, ", JVC: %s", 
243                                 val_to_str(i1, slimp3_ir_codes_jvc, "Unknown (0x%0x)"));
244             } else {
245                 /* Unknown code; just write it */
246                 col_append_fstr(pinfo->cinfo, COL_INFO, ", 0x%0x", i1);
247             }
248         }
249         break;
250
251     case SLIMP3_DISPLAY:
252         if (tree) {
253             int in_str;
254
255             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_display,
256                                        tvb, offset, 1, FALSE);
257
258             /* Loop through the commands */
259             i1 = 18;
260             in_str = 0;
261             while (i1 < tvb_reported_length_remaining(tvb, offset)) {
262                 switch(tvb_get_guint8(tvb, offset + i1)) {
263                 case 0:
264                     in_str = 0; 
265                     proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
266                                         "Delay (%d ms)", tvb_get_guint8(tvb, offset + i1 + 1));
267                     i1 += 2;
268                     break;
269                 case 3:
270                     if (ti && in_str) {
271                         proto_item_append_text(ti, "%c", 
272                                                tvb_get_guint8(tvb, offset + i1 + 1));
273                     } else {
274                         ti = proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
275                                                  "String: %c", 
276                                                  tvb_get_guint8(tvb, offset + i1 + 1));
277                         in_str = 1;
278                     }
279                     i1 += 2;
280                     break;
281
282                 case 2:
283                     in_str = 0; 
284                     ti = proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
285                                              "Command: %s", 
286                                              val_to_str(tvb_get_guint8(tvb, offset + i1 + 1), 
287                                                         slimp3_display_commands, 
288                                                         "Unknown (0x%0x)"));
289                     if ((tvb_get_guint8(tvb, offset + i1 + 1) & 0xf0) == 0x30) { 
290                         proto_item_append_text(ti, ": %s", 
291                                                val_to_str(tvb_get_guint8(tvb, offset + i1 + 2),
292                                                           slimp3_display_fset8, 
293                                                           "Unknown (0x%0x)"));
294                         i1 += 2 ;
295                     }
296                     i1 += 2;
297                     break;
298                         
299                 default:
300                     proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2, 
301                                         "Unknown 0x%0x, 0x%0x", 
302                                         tvb_get_guint8(tvb, offset + i1),
303                                         tvb_get_guint8(tvb, offset + i1 + 1));
304                     i1 += 2;
305                     break;
306                 }
307             }
308         }
309
310         if (check_col(pinfo->cinfo, COL_INFO)) {
311             i1 = 18;
312             addc_strp = addc_str;
313             while (tvb_offset_exists(tvb, offset + i1)) {
314                 switch (tvb_get_guint8(tvb, offset + i1)) {
315
316                 case 0:
317                     *addc_strp++ = '.';
318                     break;
319
320                 case 2:
321                     *addc_strp++ = '|'; 
322                     if (tvb_offset_exists(tvb, offset + i1 + 1) &&
323                           (tvb_get_guint8(tvb, offset + i1 + 1) & 0xf0) == 0x30)
324                         i1 += 2;
325                     break;
326
327                 case 3:
328                     if (tvb_offset_exists(tvb, offset + i1 + 1)) {
329                         if (addc_strp == addc_str || 
330                               *(addc_strp-1) != ' ' ||
331                               tvb_get_guint8(tvb, offset + i1 + 1) != ' ')
332                             *addc_strp++ = tvb_get_guint8(tvb, offset + i1 + 1);
333                     }
334                 }
335                 i1 += 2;
336             }
337             *addc_strp = 0;
338             if (addc_strp - addc_str > 0)
339                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", addc_str);
340         }
341
342         break;
343
344     case SLIMP3_CONTROL:
345         if (tree) {
346             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_control,
347                                        tvb, offset+1, 1, FALSE);
348             proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Command: %s",
349                                 val_to_str(tvb_get_guint8(tvb, offset+1), 
350                                            slimp3_stream_control, "Unknown (0x%0x)"));
351         }
352
353         if (check_col(pinfo->cinfo, COL_INFO)) {
354             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", 
355                             val_to_str(tvb_get_guint8(tvb, offset+1), 
356                                        slimp3_stream_control, "Unknown (0x%0x)"));
357         }
358         break;
359
360     case SLIMP3_HELLO:
361         if (tree) {
362             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_hello,
363                                        tvb, offset+1, 1, FALSE);
364             if (pinfo->destport == UDP_PORT_SLIMP3) {
365                 guint8 fw_ver = 0;
366                 /* Hello response; client->server */
367                 proto_tree_add_text(slimp3_tree, tvb, offset, 1, "Hello Response (Client --> Server)");
368                 proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Device ID: %d", 
369                                     tvb_get_guint8(tvb, offset+1));
370                 fw_ver = tvb_get_guint8(tvb, offset+2);
371                 proto_tree_add_text(slimp3_tree, tvb, offset+2, 1, "Firmware Revision: %d.%d (0x%0x)", 
372                                     fw_ver>>4, fw_ver & 0xf, fw_ver);
373             } else {
374                 /* Hello request; server->client */
375                 proto_tree_add_text(slimp3_tree, tvb, offset, 1, "Hello Request (Server --> Client)");
376             }
377         }
378         break;
379
380     case SLIMP3_I2C:
381         if (tree) {
382             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_i2c,
383                                        tvb, offset, 1, FALSE);
384             if (pinfo->destport == UDP_PORT_SLIMP3) {
385                 /* Hello response; client->server */
386                 proto_tree_add_text(slimp3_tree, tvb, offset, -1, 
387                                     "I2C Response (Client --> Server)");
388             } else {
389                 /* Hello request; server->client */
390                 proto_tree_add_text(slimp3_tree, tvb, offset, -1, 
391                                     "I2C Request (Server --> Client)");
392             }
393         }
394
395         if (check_col(pinfo->cinfo, COL_INFO)) {
396             if (pinfo->destport == UDP_PORT_SLIMP3) {
397                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Response");
398             } else {
399                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Request");
400             }
401         }
402         break;
403
404     case SLIMP3_DATA_REQ:
405         if (tree) {
406             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data_request,
407                                        tvb, offset, 1, FALSE);
408             proto_tree_add_text(slimp3_tree, tvb, offset+2, 2, 
409                                 "Requested offset: %d bytes.", 
410                                 tvb_get_ntohs(tvb, offset+2)*2);
411         }
412
413         if (check_col(pinfo->cinfo, COL_INFO)) {
414             col_append_fstr(pinfo->cinfo, COL_INFO, ", Offset: %d bytes",
415                             tvb_get_ntohs(tvb, offset+2)*2);
416         }
417         break;
418
419     case SLIMP3_DATA:
420         if (tree) {
421             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data,
422                                        tvb, offset, 1, FALSE);
423             proto_tree_add_text(slimp3_tree, tvb, offset, -1, 
424                                 "Length: %d bytes", tvb_reported_length_remaining(tvb, offset+18));
425             proto_tree_add_text(slimp3_tree, tvb, offset+2, 2, 
426                                 "Buffer offset: %d bytes.", 
427                                 tvb_get_ntohs(tvb, offset+2) * 2);
428         }
429
430         if (check_col(pinfo->cinfo, COL_INFO)) {
431             col_append_fstr(pinfo->cinfo, COL_INFO, ", Length: %d bytes, Offset: %d bytes.",
432                             tvb_reported_length_remaining(tvb, offset+18),
433                             tvb_get_ntohs(tvb, offset+2) * 2);
434         }
435         break;
436
437     case SLIMP3_DISC_REQ:
438         if (tree) {
439             guint8 fw_ver;
440             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_discover_request,
441                                        tvb, offset, 1, FALSE);
442             proto_tree_add_text(slimp3_tree, tvb, offset+1, 1,
443                                 "Device ID: %d.", tvb_get_guint8(tvb, offset+1));
444             fw_ver = tvb_get_guint8(tvb, offset+2);
445             proto_tree_add_text(slimp3_tree, tvb, offset+2, 1, "Firmware Revision: %d.%d (0x%0x)", 
446                                 fw_ver>>4, fw_ver & 0xf, fw_ver);
447         }
448
449         if (check_col(pinfo->cinfo, COL_INFO)) {
450             guint8 fw_ver = tvb_get_guint8(tvb, offset+2);
451             col_append_fstr(pinfo->cinfo, COL_INFO, ", Device ID: %d. Firmware: %d.%d",
452                             tvb_get_guint8(tvb, offset+1), fw_ver>>4, fw_ver & 0xf);
453         }
454         break;
455
456     case SLIMP3_DISC_RSP:
457         if (tree) {
458             guint8 fw_ver;
459             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_discover_response,
460                                        tvb, offset, 1, FALSE);
461             proto_tree_add_text(slimp3_tree, tvb, offset+2, 4,
462                                 "Server Address: %s.", 
463                                 ip_to_str(tvb_get_ptr(tvb, offset+2, 4)));
464             proto_tree_add_text(slimp3_tree, tvb, offset+6, 2, 
465                                 "Server Port: %d", tvb_get_ntohs(tvb, offset + 6));
466         }
467
468         if (check_col(pinfo->cinfo, COL_INFO)) {
469             guint8 fw_ver = tvb_get_guint8(tvb, offset+2);
470             col_append_fstr(pinfo->cinfo, COL_INFO, ", Server Address: %s. Server Port: %d", 
471                             ip_to_str(tvb_get_ptr(tvb, offset+2, 4)),
472                             tvb_get_ntohs(tvb, offset + 6));
473         }
474         break;
475
476     default:
477         if (tree) {
478             proto_tree_add_text(slimp3_tree, tvb, offset, -1,
479                                 "Data (%d bytes)", tvb_reported_length_remaining(tvb, offset));
480         }
481         break;
482
483     }
484 }
485
486 void
487 proto_register_slimp3(void)
488 {
489   static hf_register_info hf[] = {
490     { &hf_slimp3_opcode,
491       { "Opcode",             "slimp3.opcode",
492         FT_UINT8, BASE_DEC, VALS(slimp3_opcode_vals), 0x0,
493         "SLIMP3 message type", HFILL }},
494
495     { &hf_slimp3_ir,
496       { "Infrared",           "slimp3.ir",
497         FT_UINT32, BASE_HEX, NULL, 0x0,
498         "SLIMP3 Infrared command", HFILL }},
499
500     { &hf_slimp3_control,
501       { "Control Packet",   "slimp3.control",
502         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
503         "SLIMP3 control", HFILL }},
504
505     { &hf_slimp3_display,
506       { "Display",                    "slimp3.display",
507         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
508         "SLIMP3 display", HFILL }},
509
510     { &hf_slimp3_hello,
511       { "Hello",                      "slimp3.hello",
512         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
513         "SLIMP3 hello", HFILL }},
514
515     { &hf_slimp3_i2c,
516       { "I2C",                "slimp3.i2c",
517         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
518         "SLIMP3 I2C", HFILL }},
519
520     { &hf_slimp3_data,
521       { "Data",              "slimp3.data",
522         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
523         "SLIMP3 Data", HFILL }},
524
525     { &hf_slimp3_data_request,
526       { "Data Request",      "slimp3.data_req",
527         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
528         "SLIMP3 Data Request", HFILL }},
529
530     { &hf_slimp3_discover_request,
531       { "Discovery Request", "slimp3.discovery_req",
532         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
533         "SLIMP3 Discovery Request", HFILL }},
534
535     { &hf_slimp3_discover_response,
536       { "Discovery Response", "slimp3.discovery_response",
537         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
538         "SLIMP3 Discovery Response", HFILL }},
539
540   };
541   static gint *ett[] = {
542     &ett_slimp3,
543   };
544
545   proto_slimp3 = proto_register_protocol("SliMP3 Communication Protocol",
546                                        "SliMP3", "slimp3");
547   proto_register_field_array(proto_slimp3, hf, array_length(hf));
548   proto_register_subtree_array(ett, array_length(ett));
549
550   slimp3_handle = create_dissector_handle(dissect_slimp3, proto_slimp3);
551 }
552
553 void
554 proto_reg_handoff_slimp3(void)
555 {
556   dissector_add("udp.port", UDP_PORT_SLIMP3, slimp3_handle);
557 }