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