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