Make the SliMP3 dissector a "new-style" dissector, so it rejects packets
[obnox/wireshark/wip.git] / epan / dissectors / 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$
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
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 <string.h>
36 #include <ctype.h>
37 #include <epan/packet.h>
38
39 #include "isprint.h"
40
41 static int proto_slimp3 = -1;
42 static int hf_slimp3_opcode = -1;
43 static int hf_slimp3_ir = -1;
44 static int hf_slimp3_display = -1;
45 static int hf_slimp3_control = -1;
46 static int hf_slimp3_hello = -1;
47 static int hf_slimp3_i2c = -1;
48 static int hf_slimp3_data_request = -1;
49 static int hf_slimp3_data = -1;
50 static int hf_slimp3_discover_request = -1;
51 static int hf_slimp3_discover_response = -1;
52 static int hf_slimp3_data_ack = -1;
53
54 static gint ett_slimp3 = -1;
55
56 static dissector_handle_t slimp3_handle;
57
58 #define UDP_PORT_SLIMP3_V1    1069
59 #define UDP_PORT_SLIMP3_V2    3483
60
61 #define SLIMP3_IR       'i'
62 #define SLIMP3_CONTROL  's'
63 #define SLIMP3_HELLO    'h'
64 #define SLIMP3_DATA     'm'
65 #define SLIMP3_DATA_REQ 'r'
66 #define SLIMP3_DISPLAY  'l'
67 #define SLIMP3_I2C      '2'
68 #define SLIMP3_DISC_REQ 'd'
69 #define SLIMP3_DISC_RSP 'D'
70 #define SLIMP3_DATA_ACK 'a'
71
72 static const value_string slimp3_opcode_vals[] = {
73   { SLIMP3_IR,       "Infrared Remote Code" },
74   { SLIMP3_CONTROL,  "Stream Control" },
75   { SLIMP3_DATA,     "MPEG Data" },
76   { SLIMP3_DATA_REQ, "Data Request" },
77   { SLIMP3_HELLO,    "Hello" },
78   { SLIMP3_DISPLAY,  "Display" },
79   { SLIMP3_I2C,      "I2C" },
80   { SLIMP3_DISC_REQ, "Discovery Request" },
81   { SLIMP3_DISC_RSP, "Discovery Response" },
82   { SLIMP3_DATA_ACK, "Ack" },
83   { 0,               NULL }
84 };
85
86 /* IR remote control types */
87 static const value_string slimp3_ir_types[] = {
88     { 0x02, "SLIMP3" },
89     { 0xff, "JVC DVD Player" },
90
91     { 0, NULL }
92 };
93
94 /* IR codes for the custom SLIMP3 remote control */
95 static const value_string slimp3_ir_codes_slimp3[] = {
96     { 0x76899867, "0" },
97     { 0x7689f00f, "1" },
98     { 0x768908f7, "2" },
99     { 0x76898877, "3" },
100     { 0x768948b7, "4" },
101     { 0x7689c837, "5" },
102     { 0x768928d7, "6" },
103     { 0x7689a857, "7" },
104     { 0x76896897, "8" },
105     { 0x7689e817, "9" },
106     { 0x7689b04f, "arrow_down" },
107     { 0x7689906f, "arrow_left" },
108     { 0x7689d02f, "arrow_right" },
109     { 0x7689e01f, "arrow_up" },
110     { 0x768900ff, "voldown" },
111     { 0x7689807f, "volup" },
112     { 0x768940bf, "power" },
113     { 0x7689c03f, "rew" },
114     { 0x768920df, "pause" },
115     { 0x7689a05f, "fwd" },
116     { 0x7689609f, "add" },
117     { 0x768910ef, "play" },
118     { 0x768958a7, "search" },
119     { 0x7689d827, "shuffle" },
120     { 0x768938c7, "repeat" },
121     { 0x7689b847, "sleep" },
122     { 0x76897887, "now_playing" },
123     { 0x7689f807, "size" },
124     { 0x768904fb, "brightness" },
125
126     { 0,      NULL }
127 };
128
129 /* IR codes for the JVC remote control */
130 static const value_string slimp3_ir_codes_jvc[] = {
131     { 0xf786, "One" },
132     { 0xf746, "Two" },
133     { 0xf7c6, "Three" },
134     { 0xf726, "Four" },
135     { 0xf7a6, "Five" },
136     { 0xf766, "Six" },
137     { 0xf7e6, "Seven" },
138     { 0xf716, "Eight" },
139     { 0xf796, "Nine" },
140     { 0xf776, "Ten" },
141
142     { 0xf7f6, "Picture-In-Picture" },
143     /* { 0xf7XX, "Enter" }, */
144     { 0xf70e, "Back" },
145     { 0xf732, "Play" },
146     { 0xf76e, "Forward" },
147     { 0xf743, "Record" },
148     { 0xf7c2, "Stop" },
149     { 0xf7b2, "Pause" },
150     /* { 0xf7XX, "TV/Video" }, */
151     { 0xf703, "Display" },
152     { 0xf7b3, "Sleep" },
153     { 0xf7b6, "Guide" },
154     { 0xf70b, "Up" },
155     { 0xf74b, "Left" },
156     { 0xf7cb, "Right" },
157     { 0xf78b, "Down" },
158     { 0xf783, "Menu" },
159     { 0xf72b, "OK" },
160     { 0xf778, "Volume Up" },
161     { 0xf7f8, "Volume Down" },
162     { 0xf70d, "Channel Up" },
163     { 0xf78d, "Channel Down" },
164     /* { 0xf7XX, "Mute" },  */
165     { 0xf7ab, "Recall" },
166     { 0xf702, "Power" },
167
168     { 0,      NULL }
169 };
170
171
172 static const value_string slimp3_display_commands[] = {
173     {  0x1, "Clear Display"},
174     {  0x2, "Cursor to 1st Line Home"},
175
176     {  0x4, "Mode: Decrement Address, Shift Cursor"},
177     {  0x5, "Mode: Decrement Address, Shift Display"},
178     {  0x6, "Mode: Increment Address, Shift Cursor"},
179     {  0x7, "Mode: Increment Address, Shift Display"},
180
181     {  0x8, "Display Off"},
182     {  0xd, "Display On, With Blinking"},
183     {  0xe, "Display On, With Cursor"},
184     {  0xf, "Display On, With Cursor And Blinking"},
185
186     { 0x10, "Move Cursor Left"},
187     { 0x14, "Move Cursor Right"},
188     { 0x18, "Shift Display Left"},
189     { 0x1b, "Shift Display Right"},
190
191     { 0x30, "Set (8-bit)"},
192     { 0x20, "Set (4-bit)"},
193
194     { 0xa0, "Cursor to Top Right"},
195     { 0xc0, "Cursor to 2nd Line Home"},
196
197     {    0, NULL},
198 };
199
200 static const value_string slimp3_display_fset8[] = {
201     { 0x0, "Brightness 100%"},
202     { 0x1, "Brightness 75%"},
203     { 0x2, "Brightness 50%"},
204     { 0x3, "Brightness 25%"},
205
206     {   0, NULL },
207 };
208
209 static const value_string slimp3_stream_control[] = {
210     { 1, "Reset buffer, Start New Stream"},
211     { 2, "Pause Playback"},
212     { 4, "Resume Playback"},
213     {   0, NULL },
214 };
215
216
217 static const value_string slimp3_mpg_control[] = {
218     { 0, "Go"},           /* Run the decoder */
219     { 1, "Stop"},         /* Halt decoder but don't reset rptr */
220     { 3, "Reset"},        /* Halt decoder and reset rptr */
221
222     { 0, NULL }
223 };
224
225 #define MAX_LCD_STR_LEN 128
226 static int
227 dissect_slimp3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
228 {
229     const char          *opcode_str;
230     proto_tree          *slimp3_tree = NULL;
231     proto_item          *ti = NULL;
232     gint                i1;
233     gint                offset = 0;
234     guint16             opcode;
235     guchar              lcd_char;
236     char                lcd_str[MAX_LCD_STR_LEN + 1];
237     int                 to_server = FALSE;
238     int                 old_protocol = FALSE;
239     address             tmp_addr;
240     gboolean            in_str;
241     int                 lcd_strlen;
242
243     /*
244      * If it doesn't begin with a known opcode, reject it, so that
245      * traffic that happens to be do or from one of our ports
246      * doesn't get misidentified as SliMP3 traffic.
247      */
248     if (!tvb_bytes_exist(tvb, offset, 1))
249         return 0;       /* not even an opcode */
250     opcode = tvb_get_guint8(tvb, offset);
251     opcode_str = match_strval(opcode, slimp3_opcode_vals);
252     if (opcode_str == NULL)
253         return 0;
254
255     if (check_col(pinfo->cinfo, COL_PROTOCOL))
256         col_set_str(pinfo->cinfo, COL_PROTOCOL, "SliMP3");
257
258     if (check_col(pinfo->cinfo, COL_INFO)) {
259
260         col_add_fstr(pinfo->cinfo, COL_INFO, "%s", opcode_str);
261
262     }
263
264     if (tree) {
265
266         ti = proto_tree_add_item(tree, proto_slimp3, tvb, offset, -1, FALSE);
267         slimp3_tree = proto_item_add_subtree(ti, ett_slimp3);
268
269         proto_tree_add_uint(slimp3_tree, hf_slimp3_opcode, tvb,
270                             offset, 1, opcode);
271     }
272
273     /* The new protocol (v1.3 and later) uses an IANA-assigned port number.
274     * It usually uses the same number for both sizes of the conversation, so
275     * the port numbers can't always be used to determine client and server.
276     * The new protocol places the clients MAC address in the packet, so that
277     * is used to identify packets originating at the client.
278     */
279     if ((pinfo->destport == UDP_PORT_SLIMP3_V2) && (pinfo->srcport == UDP_PORT_SLIMP3_V2)) {
280         SET_ADDRESS(&tmp_addr, AT_ETHER, 6, tvb_get_ptr(tvb, offset+12, 6));
281         to_server = ADDRESSES_EQUAL(&tmp_addr, &pinfo->dl_src);
282     }
283     else if (pinfo->destport == UDP_PORT_SLIMP3_V2) {
284         to_server = TRUE;
285     }
286     else if (pinfo->srcport == UDP_PORT_SLIMP3_V2) {
287         to_server = FALSE;
288     }
289     if (pinfo->destport == UDP_PORT_SLIMP3_V1) {
290         to_server = TRUE;
291         old_protocol = TRUE;
292     }
293     else if (pinfo->srcport == UDP_PORT_SLIMP3_V1) {
294         to_server = FALSE;
295         old_protocol = TRUE;
296     }
297
298     switch (opcode) {
299
300     case SLIMP3_IR:
301         /* IR code
302         *
303         * [0]        'i' as in "IR"
304         * [1]        0x00
305         * [2..5]     player's time since startup in ticks @625 KHz
306         * [6]        IR code id, ff=JVC, 02=SLIMP3
307         * [7]        number of meaningful bits - 16 for JVC, 32 for SLIMP3
308         * [8..11]    the 32-bit IR code
309         * [12..17]   reserved
310         */
311         if (tree) {
312             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_ir, tvb, offset+8, 4, FALSE);
313
314             i1 = tvb_get_ntohl(tvb, offset+2);
315             proto_tree_add_text(slimp3_tree, tvb, offset+2, 4, "Uptime: %u sec (%u ticks)",
316                                 i1/625000, i1);
317             i1 = tvb_get_guint8(tvb, offset+6);
318             proto_tree_add_text(slimp3_tree, tvb, offset+6, 1, "Code identifier: 0x%0x: %s",
319                                 i1, val_to_str(i1, slimp3_ir_types, "Unknown"));
320             proto_tree_add_text(slimp3_tree, tvb, offset+7, 1, "Code bits: %d",
321                                 tvb_get_guint8(tvb, offset+7));
322
323             i1 = tvb_get_ntohl(tvb, offset+8);
324             /* Check the code to figure out which remote is being used. */
325             if (tvb_get_guint8(tvb, offset+6) == 0x02 &&
326                 tvb_get_guint8(tvb, offset+7) == 32) {
327                 /* This is the custom SLIMP3 remote. */
328                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4,
329                                     "Infrared Code: %s: 0x%0x",
330                                     val_to_str(i1, slimp3_ir_codes_slimp3, "Unknown"), i1);
331             }
332             else if (tvb_get_guint8(tvb, offset+6) == 0xff &&
333                      tvb_get_guint8(tvb, offset+7) == 16) {
334                 /* This is a JVC DVD player remote */
335                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4,
336                                     "Infrared Code: %s: 0x%0x",
337                                     val_to_str(i1, slimp3_ir_codes_jvc, "Unknown"), i1);
338             } else {
339                 /* Unknown code; just write it */
340                 proto_tree_add_text(slimp3_tree, tvb, offset+8, 4, "Infrared Code: 0x%0x", i1);
341             }
342         }
343         if (check_col(pinfo->cinfo, COL_INFO)) {
344             i1 = tvb_get_ntohl(tvb, offset+8);
345             if (tvb_get_guint8(tvb, offset+6) == 0x02 &&
346                 tvb_get_guint8(tvb, offset+7) == 32) {
347                 col_append_fstr(pinfo->cinfo, COL_INFO, ", SLIMP3: %s",
348                                 val_to_str(i1, slimp3_ir_codes_slimp3, "Unknown (0x%0x)"));
349             }
350             else if (tvb_get_guint8(tvb, offset+6) == 0xff &&
351                      tvb_get_guint8(tvb, offset+7) == 16) {
352                 col_append_fstr(pinfo->cinfo, COL_INFO, ", JVC: %s",
353                                 val_to_str(i1, slimp3_ir_codes_jvc, "Unknown (0x%0x)"));
354             } else {
355                 /* Unknown code; just write it */
356                 col_append_fstr(pinfo->cinfo, COL_INFO, ", 0x%0x", i1);
357             }
358         }
359         break;
360
361     case SLIMP3_DISPLAY:
362         if (tree) {
363
364             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_display,
365                                        tvb, offset, 1, FALSE);
366
367             /* Loop through the commands */
368             i1 = 18;
369             in_str = FALSE;
370             lcd_strlen = 0;
371             while (i1 < tvb_reported_length_remaining(tvb, offset)) {
372                 switch(tvb_get_guint8(tvb, offset + i1)) {
373                 case 0:
374                     in_str = FALSE;
375                     lcd_strlen = 0;
376                     proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2,
377                                         "Delay (%d ms)", tvb_get_guint8(tvb, offset + i1 + 1));
378                     i1 += 2;
379                     break;
380                 case 3:
381                     lcd_char = tvb_get_guint8(tvb, offset + i1 + 1);
382                     if (!isprint(lcd_char)) lcd_char = '.';
383                     if (ti && in_str) {
384                         lcd_strlen += 2;
385                         proto_item_append_text(ti, "%c", lcd_char);
386                         proto_item_set_len(ti, lcd_strlen);
387                     } else {
388                         ti = proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2,
389                                                  "String: %c", lcd_char);
390                         in_str = TRUE;
391                         lcd_strlen = 2;
392                     }
393                     i1 += 2;
394                     break;
395
396                 case 2:
397                     in_str = FALSE;
398                     lcd_strlen = 0;
399                     ti = proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2,
400                                              "Command: %s",
401                                              val_to_str(tvb_get_guint8(tvb, offset + i1 + 1),
402                                                         slimp3_display_commands,
403                                                         "Unknown (0x%0x)"));
404                     if ((tvb_get_guint8(tvb, offset + i1 + 1) & 0xf0) == 0x30) {
405                         proto_item_append_text(ti, ": %s",
406                                                val_to_str(tvb_get_guint8(tvb, offset + i1 + 2),
407                                                           slimp3_display_fset8,
408                                                           "Unknown (0x%0x)"));
409                         i1 += 2;
410                     }
411                     i1 += 2;
412                     break;
413
414                 default:
415                     proto_tree_add_text(slimp3_tree, tvb, offset + i1, 2,
416                                         "Unknown 0x%0x, 0x%0x",
417                                         tvb_get_guint8(tvb, offset + i1),
418                                         tvb_get_guint8(tvb, offset + i1 + 1));
419                     i1 += 2;
420                     break;
421                 }
422             }
423         }
424
425         if (check_col(pinfo->cinfo, COL_INFO)) {
426             i1 = 18;
427             lcd_strlen = 0;
428             while (tvb_offset_exists(tvb, offset + i1) && 
429                     lcd_strlen < MAX_LCD_STR_LEN) {
430                 switch (tvb_get_guint8(tvb, offset + i1)) {
431
432                 case 0:
433                     lcd_str[lcd_strlen++] = '.';
434                     break;
435
436                 case 2:
437                     lcd_str[lcd_strlen++] = '|';
438                     if (tvb_offset_exists(tvb, offset + i1 + 1) &&
439                           (tvb_get_guint8(tvb, offset + i1 + 1) & 0xf0) == 0x30)
440                         i1 += 2;
441                     break;
442
443                 case 3:
444                     if (tvb_offset_exists(tvb, offset + i1 + 1)) {
445                         if (lcd_strlen < 1 ||
446                               lcd_str[lcd_strlen-1] != ' ' ||
447                               tvb_get_guint8(tvb, offset + i1 + 1) != ' ') {
448                             lcd_char = tvb_get_guint8(tvb, offset + i1 + 1);
449                             lcd_str[lcd_strlen++] = isprint(lcd_char) ? lcd_char : '.';
450                         }
451                     }
452                 }
453                 i1 += 2;
454             }
455             lcd_str[lcd_strlen] = '\0';
456             if (lcd_strlen > 0)
457                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", lcd_str);
458         }
459
460         break;
461
462     case SLIMP3_CONTROL:
463         if (tree) {
464             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_control,
465                                        tvb, offset+1, 1, FALSE);
466             proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Command: %s",
467                                 val_to_str(tvb_get_guint8(tvb, offset+1),
468                                            slimp3_stream_control, "Unknown (0x%0x)"));
469         }
470
471         if (check_col(pinfo->cinfo, COL_INFO)) {
472             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
473                             val_to_str(tvb_get_guint8(tvb, offset+1),
474                                        slimp3_stream_control, "Unknown (0x%0x)"));
475         }
476         break;
477
478     case SLIMP3_HELLO:
479         if (tree) {
480             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_hello,
481                                        tvb, offset+1, 1, FALSE);
482             if (to_server) {
483                 guint8 fw_ver = 0;
484                 /* Hello response; client->server */
485                 proto_tree_add_text(slimp3_tree, tvb, offset, 1, "Hello Response (Client --> Server)");
486                 proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Device ID: %d",
487                                     tvb_get_guint8(tvb, offset+1));
488                 fw_ver = tvb_get_guint8(tvb, offset+2);
489                 proto_tree_add_text(slimp3_tree, tvb, offset+2, 1, "Firmware Revision: %d.%d (0x%0x)",
490                                     fw_ver>>4, fw_ver & 0xf, fw_ver);
491             } else {
492                 /* Hello request; server->client */
493                 proto_tree_add_text(slimp3_tree, tvb, offset, 1, "Hello Request (Server --> Client)");
494             }
495         }
496         break;
497
498     case SLIMP3_I2C:
499         if (tree) {
500             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_i2c,
501                                        tvb, offset, 1, FALSE);
502             if (to_server) {
503                 /* Hello response; client->server */
504                 proto_tree_add_text(slimp3_tree, tvb, offset, -1,
505                                     "I2C Response (Client --> Server)");
506             } else {
507                 /* Hello request; server->client */
508                 proto_tree_add_text(slimp3_tree, tvb, offset, -1,
509                                     "I2C Request (Server --> Client)");
510             }
511         }
512
513         if (check_col(pinfo->cinfo, COL_INFO)) {
514             if (to_server) {
515                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Response");
516             } else {
517                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Request");
518             }
519         }
520         break;
521
522     case SLIMP3_DATA_REQ:
523         if (tree) {
524             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data_request,
525                                        tvb, offset, 1, FALSE);
526             proto_tree_add_text(slimp3_tree, tvb, offset+2, 2,
527                                 "Requested offset: %d bytes.",
528                                 tvb_get_ntohs(tvb, offset+2)*2);
529         }
530
531         if (check_col(pinfo->cinfo, COL_INFO)) {
532             col_append_fstr(pinfo->cinfo, COL_INFO, ", Offset: %d bytes",
533                             tvb_get_ntohs(tvb, offset+2)*2);
534         }
535         break;
536
537     case SLIMP3_DATA:
538         /* MPEG data (v1.3 and later)
539         *
540         *  [0]       'm'
541         *  [1..5]    reserved
542         *  [6..7]    Write pointer (in words)
543         *  [8..9]    reserved
544         *  [10..11]  Sequence number
545         *  [12..17]  reserved
546         *  [18..]    MPEG data
547         */
548         if (tree) {
549             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data,
550                                        tvb, offset, 1, FALSE);
551             if (old_protocol) {
552                 proto_tree_add_text(slimp3_tree, tvb, offset, -1,
553                                     "Length: %d bytes", 
554                                     tvb_reported_length_remaining(tvb, offset+18));
555                 proto_tree_add_text(slimp3_tree, tvb, offset+2, 2,
556                                     "Buffer offset: %d bytes.",
557                                     tvb_get_ntohs(tvb, offset+2) * 2);
558             }
559             else {
560                 proto_tree_add_text(slimp3_tree, tvb, offset+1, 1, "Command: %s",
561                                     val_to_str(tvb_get_guint8(tvb, offset+1),
562                                                slimp3_mpg_control, "Unknown (0x%0x)"));
563                 proto_tree_add_text(slimp3_tree, tvb, offset, -1,
564                                     "Length: %d bytes", 
565                                     tvb_reported_length_remaining(tvb, offset+18));
566                 proto_tree_add_text(slimp3_tree, tvb, offset+6, 2,
567                                     "Write Pointer: %d",
568                                     tvb_get_ntohs(tvb, offset+6) * 2);
569                 proto_tree_add_text(slimp3_tree, tvb, offset+10, 2,
570                                     "Sequence: %d",
571                                     tvb_get_ntohs(tvb, offset+10));
572             }
573         }
574
575         if (check_col(pinfo->cinfo, COL_INFO)) {
576             if (old_protocol) {
577                 col_append_fstr(pinfo->cinfo, COL_INFO, 
578                                 ", Length: %d bytes, Offset: %d bytes.",
579                                 tvb_reported_length_remaining(tvb, offset+18),
580                                 tvb_get_ntohs(tvb, offset+2) * 2);
581             }
582             else {
583                 col_append_fstr(pinfo->cinfo, COL_INFO, 
584                                 ", %s, %d bytes at %d, Sequence: %d",
585                                 val_to_str(tvb_get_guint8(tvb, offset+1),
586                                            slimp3_mpg_control, "Unknown (0x%0x)"),
587                                 tvb_reported_length_remaining(tvb, offset+18),
588                                 tvb_get_ntohs(tvb, offset+6) * 2,
589                                 tvb_get_ntohs(tvb, offset+10));
590             }
591         }
592         break;
593
594     case SLIMP3_DISC_REQ:
595         if (tree) {
596             guint8 fw_ver;
597             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_discover_request,
598                                        tvb, offset, 1, FALSE);
599             proto_tree_add_text(slimp3_tree, tvb, offset+1, 1,
600                                 "Device ID: %d.", tvb_get_guint8(tvb, offset+1));
601             fw_ver = tvb_get_guint8(tvb, offset+2);
602             proto_tree_add_text(slimp3_tree, tvb, offset+2, 1, "Firmware Revision: %d.%d (0x%0x)",
603                                 fw_ver>>4, fw_ver & 0xf, fw_ver);
604         }
605
606         if (check_col(pinfo->cinfo, COL_INFO)) {
607             guint8 fw_ver = tvb_get_guint8(tvb, offset+2);
608             col_append_fstr(pinfo->cinfo, COL_INFO, ", Device ID: %d. Firmware: %d.%d",
609                             tvb_get_guint8(tvb, offset+1), fw_ver>>4, fw_ver & 0xf);
610         }
611         break;
612
613     case SLIMP3_DISC_RSP:
614         if (tree) {
615             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_discover_response,
616                                        tvb, offset, 1, FALSE);
617             proto_tree_add_text(slimp3_tree, tvb, offset+2, 4,
618                                 "Server Address: %s.",
619                                 ip_to_str(tvb_get_ptr(tvb, offset+2, 4)));
620             proto_tree_add_text(slimp3_tree, tvb, offset+6, 2,
621                                 "Server Port: %d", tvb_get_ntohs(tvb, offset + 6));
622         }
623
624         if (check_col(pinfo->cinfo, COL_INFO)) {
625             col_append_fstr(pinfo->cinfo, COL_INFO, ", Server Address: %s. Server Port: %d",
626                             ip_to_str(tvb_get_ptr(tvb, offset+2, 4)),
627                             tvb_get_ntohs(tvb, offset + 6));
628         }
629         break;
630
631     case SLIMP3_DATA_ACK:
632         /* Acknowledge MPEG data
633         *
634         *  [0]       'a'
635         *  [1..5]    
636         *  [6..7]    Write pointer (in words)
637         *  [8..9]    Read pointer (in words)
638         *  [10..11]  Sequence number
639         *  [12..17]  client MAC address (v1.3 and later)
640         */
641         if (tree) {
642             proto_tree_add_item_hidden(slimp3_tree, hf_slimp3_data_ack,
643                                        tvb, offset, 1, FALSE);
644             proto_tree_add_text(slimp3_tree, tvb, offset+6, 2,
645                                 "Write Pointer: %d",
646                                 tvb_get_ntohs(tvb, offset+6) * 2);
647             proto_tree_add_text(slimp3_tree, tvb, offset+8, 2,
648                                 "Read Pointer: %d",
649                                 tvb_get_ntohs(tvb, offset+8) * 2);
650             proto_tree_add_text(slimp3_tree, tvb, offset+10, 2,
651                                 "Sequence: %d",
652                                 tvb_get_ntohs(tvb, offset+10));
653         }
654         if (check_col(pinfo->cinfo, COL_INFO)) {
655             col_append_fstr(pinfo->cinfo, COL_INFO, ", Sequence: %d",
656                             tvb_get_ntohs(tvb, offset+10));
657         }
658         break;
659
660     default:
661         if (tree) {
662             proto_tree_add_text(slimp3_tree, tvb, offset, -1,
663                                 "Data (%d bytes)", tvb_reported_length_remaining(tvb, offset));
664         }
665         break;
666
667     }
668     return tvb_length(tvb);
669 }
670
671 void
672 proto_register_slimp3(void)
673 {
674   static hf_register_info hf[] = {
675     { &hf_slimp3_opcode,
676       { "Opcode",             "slimp3.opcode",
677         FT_UINT8, BASE_DEC, VALS(slimp3_opcode_vals), 0x0,
678         "SLIMP3 message type", HFILL }},
679
680     { &hf_slimp3_ir,
681       { "Infrared",           "slimp3.ir",
682         FT_UINT32, BASE_HEX, NULL, 0x0,
683         "SLIMP3 Infrared command", HFILL }},
684
685     { &hf_slimp3_control,
686       { "Control Packet",   "slimp3.control",
687         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
688         "SLIMP3 control", HFILL }},
689
690     { &hf_slimp3_display,
691       { "Display",                    "slimp3.display",
692         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
693         "SLIMP3 display", HFILL }},
694
695     { &hf_slimp3_hello,
696       { "Hello",                      "slimp3.hello",
697         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
698         "SLIMP3 hello", HFILL }},
699
700     { &hf_slimp3_i2c,
701       { "I2C",                "slimp3.i2c",
702         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
703         "SLIMP3 I2C", HFILL }},
704
705     { &hf_slimp3_data,
706       { "Data",              "slimp3.data",
707         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
708         "SLIMP3 Data", HFILL }},
709
710     { &hf_slimp3_data_request,
711       { "Data Request",      "slimp3.data_req",
712         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
713         "SLIMP3 Data Request", HFILL }},
714
715     { &hf_slimp3_discover_request,
716       { "Discovery Request", "slimp3.discovery_req",
717         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
718         "SLIMP3 Discovery Request", HFILL }},
719
720     { &hf_slimp3_discover_response,
721       { "Discovery Response", "slimp3.discovery_response",
722         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
723         "SLIMP3 Discovery Response", HFILL }},
724
725     { &hf_slimp3_data_ack,
726       { "Data Ack",      "slimp3.data_ack",
727         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
728         "SLIMP3 Data Ack", HFILL }},
729
730   };
731   static gint *ett[] = {
732     &ett_slimp3,
733   };
734
735   proto_slimp3 = proto_register_protocol("SliMP3 Communication Protocol",
736                                        "SliMP3", "slimp3");
737   proto_register_field_array(proto_slimp3, hf, array_length(hf));
738   proto_register_subtree_array(ett, array_length(ett));
739
740   slimp3_handle = new_create_dissector_handle(dissect_slimp3, proto_slimp3);
741 }
742
743 void
744 proto_reg_handoff_slimp3(void)
745 {
746   dissector_add("udp.port", UDP_PORT_SLIMP3_V1, slimp3_handle);
747   dissector_add("udp.port", UDP_PORT_SLIMP3_V2, slimp3_handle);
748 }