Obscure more email addresses.
[obnox/wireshark/wip.git] / packet-fcp.c
1 /* packet-fcp.c
2  * Routines for Fibre Channel Protocol for SCSI (FCP)
3  * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com>
4  *
5  * $Id: packet-fcp.c,v 1.2 2003/01/31 03:17:46 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED"
12  * is a dissector file; if you just copied this from README.developer,
13  * don't bother with the "Copied from" - you don't even need to put
14  * in a "Copied from" if you copied an existing dissector, especially
15  * if the bulk of the code in the new dissector is your code)
16  * 
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * as published by the Free Software Foundation; either version 2
20  * of the License, or (at your option) any later version.
21  * 
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * 
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
42 #endif
43
44 #ifdef HAVE_NETINET_IN_H
45 # include <netinet/in.h>
46 #endif
47
48 #include <glib.h>
49
50 #ifdef NEED_SNPRINTF_H
51 # include "snprintf.h"
52 #endif
53
54 #include "prefs.h"
55 #include <epan/packet.h>
56 #include <epan/conversation.h>
57 #include "etypes.h"
58 #include "packet-fc.h"
59 #include "packet-fcp.h"
60 #include "packet-scsi.h"
61
62 /* Initialize the protocol and registered fields */
63 static int proto_fcp         = -1;
64 static int hf_fcp_multilun   = -1;
65 static int hf_fcp_singlelun  = -1;
66 static int hf_fcp_crn        = -1;
67 static int hf_fcp_taskattr   = -1;
68 static int hf_fcp_taskmgmt   = -1;
69 static int hf_fcp_addlcdblen = -1;
70 static int hf_fcp_rddata     = -1;
71 static int hf_fcp_wrdata     = -1;
72 static int hf_fcp_dl         = -1;
73 static int hf_fcp_data_ro    = -1;
74 static int hf_fcp_burstlen   = -1;
75 static int hf_fcp_rspflags   = -1;
76 static int hf_fcp_resid      = -1;
77 static int hf_fcp_snslen     = -1;
78 static int hf_fcp_rsplen     = -1;
79 static int hf_fcp_rspcode    = -1;
80 static int hf_fcp_scsistatus = -1;
81 static int hf_fcp_type = -1;
82
83
84 /* Initialize the subtree pointers */
85 static gint ett_fcp = -1;
86 static dissector_table_t fcp_dissector;
87 static dissector_handle_t data_handle;
88
89 typedef struct _fcp_conv_key {
90     guint32 conv_idx;
91 } fcp_conv_key_t;
92
93 typedef struct _fcp_conv_data {
94     guint32 fcp_dl;
95     gint32 fcp_lun;
96     guint32 abs_secs;
97     guint32 abs_usecs;
98 } fcp_conv_data_t;
99
100 GHashTable *fcp_req_hash = NULL;
101 GMemChunk *fcp_req_keys = NULL;
102 GMemChunk *fcp_req_vals = NULL;
103 guint32 fcp_init_count = 25;
104
105 /*
106  * Hash Functions
107  */
108 static gint
109 fcp_equal(gconstpointer v, gconstpointer w)
110 {
111   fcp_conv_key_t *v1 = (fcp_conv_key_t *)v;
112   fcp_conv_key_t *v2 = (fcp_conv_key_t *)w;
113
114   return (v1->conv_idx == v2->conv_idx);
115 }
116
117 static guint
118 fcp_hash (gconstpointer v)
119 {
120         fcp_conv_key_t *key = (fcp_conv_key_t *)v;
121         guint val;
122
123         val = key->conv_idx;
124
125         return val;
126 }
127
128 /*
129  * Protocol initialization
130  */
131 static void
132 fcp_init_protocol(void)
133 {
134     if (fcp_req_keys)
135         g_mem_chunk_destroy(fcp_req_keys);
136     if (fcp_req_vals)
137         g_mem_chunk_destroy(fcp_req_vals);
138     if (fcp_req_hash)
139         g_hash_table_destroy(fcp_req_hash);
140
141     fcp_req_hash = g_hash_table_new(fcp_hash, fcp_equal);
142     fcp_req_keys = g_mem_chunk_new("fcp_req_keys",
143                                    sizeof(fcp_conv_key_t),
144                                    fcp_init_count * sizeof(fcp_conv_key_t),
145                                    G_ALLOC_AND_FREE);
146     fcp_req_vals = g_mem_chunk_new("fcp_req_vals",
147                                    sizeof(fcp_conv_data_t),
148                                    fcp_init_count * sizeof(fcp_conv_data_t),
149                                    G_ALLOC_AND_FREE);
150 }
151
152 static gchar *
153 task_mgmt_flags_to_str (guint8 flags, gchar *str)
154 {
155     int stroff = 0;
156     
157     if (str == NULL)
158         return str;
159
160     *str = '\0';
161     
162     if (flags & 0x80) {
163         strcpy (str, "Obsolete, ");
164         stroff += 10;
165     }
166
167     if (flags & 0x40) {
168         strcpy (&str[stroff], "Clear ACA, ");
169         stroff += 11;
170     }
171
172     if (flags & 0x20) {
173         strcpy (&str[stroff], "Target Reset, ");
174         stroff += 14;
175     }
176
177     if (flags & 0x10) {
178         strcpy (&str[stroff], "LU Reset, ");
179         stroff += 10;
180     }
181
182     if (flags & 0x08) {
183         strcpy (&str[stroff], "Rsvd, ");
184         stroff += 6;
185     }
186
187     if (flags & 0x04) {
188         strcpy (&str[stroff], "Clear Task Set, ");
189         stroff += 16;
190     }
191
192     if (flags & 0x02) {
193         strcpy (&str[stroff], "Abort Task Set");
194         stroff += 14;
195     }
196
197     return (str);
198 }
199
200 static gchar *
201 rspflags_to_str (guint8 flags, gchar *str)
202 {
203     int stroff = 0;
204
205     if (str == NULL)
206         return (str);
207
208     *str = '\0';
209     
210     if (flags & 0x10) {
211         strcpy (str, "FCP_CONF_REQ | ");
212         stroff += 15;
213     }
214     if (flags & 0x08) {
215         strcpy (&str[stroff], "FCP_RESID_UNDER | ");
216         stroff += 18;
217     }
218     if (flags & 0x04) {
219         strcpy (&str[stroff], "FCP_RESID_OVER | ");
220         stroff += 17;
221     }
222     if (flags & 0x02) {
223         strcpy (&str[stroff], "FCP_SNS_LEN_VLD | ");
224         stroff += 18;
225     }
226     if (flags & 0x01) {
227         strcpy (&str[stroff], "FCP_RSP_LEN_VLD | ");
228     }
229
230     return (str);
231 }
232
233 /* Code to actually dissect the packets */
234 static void
235 dissect_fcp_cmnd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
236 {
237     int offset = 0;
238     int len,
239         add_len = 0;
240     gchar str[128];
241     guint8 flags, lun0;
242     proto_item *ti;
243     proto_tree *fcp_tree = NULL;
244     conversation_t *conversation;
245     fcp_conv_data_t *cdata;
246     fcp_conv_key_t ckey, *req_key;
247     scsi_task_id_t task_key;
248
249     /* Determine the length of the FCP part of the packet */
250     flags = tvb_get_guint8 (tvb, offset+10);
251     if (flags) {
252         add_len = tvb_get_guint8 (tvb, offset+11) & 0x7C;
253         add_len = add_len >> 2;
254         
255         len = FCP_DEF_CMND_LEN + add_len;
256     }
257     else {
258         len = FCP_DEF_CMND_LEN;
259     }
260
261     /* We track the conversation to determine how many bytes is required */
262     /* by the data that is sent back or sent next by the initiator as part */
263     /* of this command. The state is destroyed in the response dissector */
264     
265     conversation = find_conversation (&pinfo->src, &pinfo->dst,
266                                       pinfo->ptype, pinfo->oxid,
267                                       pinfo->rxid, NO_PORT2);
268     if (!conversation) {
269         conversation = conversation_new (&pinfo->src, &pinfo->dst,
270                                          pinfo->ptype, pinfo->oxid,
271                                          pinfo->rxid, NO_PORT2);
272     }
273     
274     ckey.conv_idx = conversation->index;
275     task_key.conv_id = conversation->index;
276     task_key.task_id = conversation->index;
277     pinfo->private_data = (void *)&task_key;
278     
279     cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
280                                                     &ckey);
281     if (cdata) {
282         /* Since we never free the memory used by an exchange, this maybe a
283          * case of another request using the same exchange as a previous
284          * req. 
285          */
286         cdata->fcp_dl = tvb_get_ntohl (tvb, offset+12+16+add_len);
287         cdata->abs_usecs = pinfo->fd->abs_usecs;
288         cdata->abs_secs = pinfo->fd->abs_secs;
289     }
290     else {
291         req_key = g_mem_chunk_alloc (fcp_req_keys);
292         req_key->conv_idx = conversation->index;
293         
294         cdata = g_mem_chunk_alloc (fcp_req_vals);
295         cdata->fcp_dl = tvb_get_ntohl (tvb, offset+12+16+add_len);
296         cdata->abs_usecs = pinfo->fd->abs_usecs;
297         cdata->abs_secs = pinfo->fd->abs_secs;
298         
299         g_hash_table_insert (fcp_req_hash, req_key, cdata);
300     }
301     
302     dissect_scsi_cdb (tvb, pinfo, fcp_tree, offset+12, 16+add_len,
303                       SCSI_DEV_UNKNOWN);
304
305     if (tree) {
306         ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, len,
307                                              "FCP_CMND");
308         fcp_tree = proto_item_add_subtree (ti, ett_fcp);
309         proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0);
310                                     
311         lun0 = tvb_get_guint8 (tvb, offset);
312
313         /* Display single-level LUNs in decimal for clarity */
314         /* I'm taking a shortcut here by assuming that if the first byte of the
315          * LUN field is 0, it is a single-level LUN. This is not true. For a
316          * real single-level LUN, all 8 bytes except byte 1 must be 0.
317          */
318         if (lun0) {
319             cdata->fcp_lun = -1;
320             proto_tree_add_item (fcp_tree, hf_fcp_multilun, tvb, offset, 8, 0);
321         }
322         else {
323             cdata->fcp_lun = tvb_get_guint8 (tvb, offset+1);
324             proto_tree_add_item (fcp_tree, hf_fcp_singlelun, tvb, offset+1,
325                                  1, 0);
326         }
327
328         proto_tree_add_item (fcp_tree, hf_fcp_crn, tvb, offset+8, 1, 0);
329         proto_tree_add_item (fcp_tree, hf_fcp_taskattr, tvb, offset+9, 1, 0);
330         proto_tree_add_uint_format (fcp_tree, hf_fcp_taskmgmt, tvb, offset+10,
331                                     1, flags,
332                                     "Task Management Flags: 0x%x (%s)",
333                                     flags,
334                                     task_mgmt_flags_to_str (flags, str));
335         proto_tree_add_item (fcp_tree, hf_fcp_addlcdblen, tvb, offset+11, 1, 0);
336         proto_tree_add_item (fcp_tree, hf_fcp_rddata, tvb, offset+11, 1, 0);
337         proto_tree_add_item (fcp_tree, hf_fcp_wrdata, tvb, offset+11, 1, 0);
338         dissect_scsi_cdb (tvb, pinfo, tree, offset+12, 16+add_len,
339                           SCSI_DEV_UNKNOWN);
340         proto_tree_add_item (fcp_tree, hf_fcp_dl, tvb, offset+12+16+add_len,
341                              4, 0);
342     }
343 }
344
345 static void
346 dissect_fcp_data (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
347 {
348     conversation_t *conversation;
349     fcp_conv_data_t *cdata = NULL;
350     fcp_conv_key_t ckey;
351     proto_item *ti;
352     proto_tree *fcp_tree;
353     scsi_task_id_t task_key;
354
355     /* Retrieve conversation state to determine expected payload */
356     conversation = find_conversation (&pinfo->src, &pinfo->dst,
357                                       pinfo->ptype, pinfo->oxid,
358                                       pinfo->rxid, NO_PORT2);
359     if (conversation) {
360         ckey.conv_idx = conversation->index;
361     
362         cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
363                                                         &ckey);
364         task_key.conv_id = conversation->index;
365         task_key.task_id = conversation->index;
366         pinfo->private_data = (void *)&task_key;
367     }
368     else {
369         pinfo->private_data = NULL;
370     }
371     if (cdata) {
372         ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, 0,
373                                              "FCP_DATA");
374         fcp_tree = proto_item_add_subtree (ti, ett_fcp);
375
376         if (cdata->fcp_lun >= 0)
377             proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb,
378                                         0, 0, cdata->fcp_lun);
379
380         dissect_scsi_payload (tvb, pinfo, tree, 0, FALSE, cdata->fcp_dl);
381     }
382     else {
383         dissect_scsi_payload (tvb, pinfo, tree, 0, FALSE, 0);
384     }
385 }
386
387 static void
388 dissect_fcp_rsp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
389 {
390     guint32 offset = 0,
391         del_usecs = 0;
392     guint len = 0,
393           add_len = 0,
394           rsplen = 0;
395     gchar str[128];
396     guint8 flags;
397     proto_item *ti;
398     proto_tree *fcp_tree;
399     guint8 status;
400     conversation_t *conversation;
401     fcp_conv_data_t *cdata = NULL;
402     fcp_conv_key_t ckey;
403     scsi_task_id_t task_key;
404
405     status = tvb_get_guint8 (tvb, offset+11);
406     
407     if (check_col (pinfo->cinfo, COL_INFO)) {
408         col_append_fstr (pinfo->cinfo, COL_INFO, " , %s",
409                          val_to_str (status, scsi_status_val, "0x%x"));
410     }
411
412     /* Response marks the end of the conversation. So destroy state */
413     conversation = find_conversation (&pinfo->src, &pinfo->dst,
414                                       pinfo->ptype, pinfo->oxid,
415                                       pinfo->rxid, NO_PORT2);
416     if (conversation) {
417         ckey.conv_idx = conversation->index;
418     
419         cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
420                                                         &ckey);
421         task_key.conv_id = task_key.task_id = conversation->index;
422         pinfo->private_data = (void *)&task_key;
423     }
424     
425     if (tree) {
426         /* Determine the length of the FCP part of the packet */
427         len = FCP_DEF_RSP_LEN;
428
429         flags = tvb_get_guint8 (tvb, offset+10);
430         if (flags & 0x2) {
431             add_len = tvb_get_ntohl (tvb, offset+20);
432             len += add_len;
433         }
434         if (flags & 0x1) {
435             add_len = tvb_get_ntohl (tvb, offset+16);
436             len += add_len;
437         }
438
439         ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, len,
440                                              "FCP_RSP");
441         fcp_tree = proto_item_add_subtree (ti, ett_fcp);
442         proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0);
443
444         if (cdata) {
445             del_usecs = (pinfo->fd->abs_secs - cdata->abs_secs)* 1000000 +
446                 (pinfo->fd->abs_usecs - cdata->abs_usecs);
447             if (del_usecs > 1000)
448                 proto_tree_add_text (fcp_tree, tvb, offset, 0,
449                                      "Cmd Response Time: %d msecs",
450                                      del_usecs/1000);
451             else
452                 proto_tree_add_text (fcp_tree, tvb, offset, 0,
453                                      "Cmd Response Time: %d usecs",
454                                      del_usecs);
455             if (cdata->fcp_lun >= 0)
456                 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb,
457                                             offset, 0, cdata->fcp_lun);
458         }
459         proto_tree_add_uint_format (fcp_tree, hf_fcp_rspflags, tvb, offset+10,
460                                     1, flags, "Flags: 0x%02x (%s)", flags,
461                                     rspflags_to_str (flags, str));
462         proto_tree_add_item (fcp_tree, hf_fcp_scsistatus, tvb, offset+11, 1, 0);
463         if (flags & 0xC)
464             proto_tree_add_item (fcp_tree, hf_fcp_resid, tvb, offset+12, 4, 0);
465         if (flags & 0x2)
466             proto_tree_add_item (fcp_tree, hf_fcp_snslen, tvb, offset+16, 4, 0);
467         if (flags & 0x1) {
468             rsplen = tvb_get_ntohl (tvb, offset+20);
469             proto_tree_add_item (fcp_tree, hf_fcp_rsplen, tvb, offset+20, 4, 0);
470             proto_tree_add_item (fcp_tree, hf_fcp_rspcode, tvb, offset+27, 1,
471                                  0);
472         }
473         if (flags & 0x2) {
474             dissect_scsi_snsinfo (tvb, pinfo, tree, offset+24+rsplen,
475                                   tvb_get_ntohl (tvb, offset+16));
476         }
477         if (cdata) {
478             g_mem_chunk_free (fcp_req_vals, cdata);
479             g_hash_table_remove (fcp_req_hash, &ckey);
480         }
481     }
482 }
483
484 static void
485 dissect_fcp_xfer_rdy (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
486 {
487     int offset = 0;
488     proto_item *ti;
489     proto_tree *fcp_tree;
490     guint del_usecs;
491
492     conversation_t *conversation;
493     fcp_conv_data_t *cdata = NULL;
494     fcp_conv_key_t ckey, *req_key;
495
496     /* Retrieve conversation state to determine expected payload */
497     conversation = find_conversation (&pinfo->src, &pinfo->dst,
498                                       pinfo->ptype, pinfo->oxid,
499                                       pinfo->rxid, NO_PORT2);
500     if (!conversation) {
501         conversation = conversation_new (&pinfo->src, &pinfo->dst,
502                                          pinfo->ptype, pinfo->oxid,
503                                          pinfo->rxid, NO_PORT2);
504     }
505     
506     if (conversation) {
507         ckey.conv_idx = conversation->index;
508     
509         cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash,
510                                                         &ckey);
511         if (cdata != NULL) {
512             cdata->fcp_dl = tvb_get_ntohl (tvb, offset+4);
513         }
514         else {
515             req_key = g_mem_chunk_alloc (fcp_req_keys);
516             req_key->conv_idx = conversation->index;
517             
518             cdata = g_mem_chunk_alloc (fcp_req_vals);
519             cdata->fcp_dl = tvb_get_ntohl (tvb, offset+4);
520             cdata->fcp_lun = -1;
521             
522             g_hash_table_insert (fcp_req_hash, req_key, cdata);
523         }
524     }
525
526     if (tree) {
527         ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, 12,
528                                              "FCP_XFER_RDY");
529         fcp_tree = proto_item_add_subtree (ti, ett_fcp);
530         proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0);
531
532         if (cdata) {
533             del_usecs = (pinfo->fd->abs_secs - cdata->abs_secs)* 1000000 +
534                 (pinfo->fd->abs_usecs - cdata->abs_usecs);
535             if (del_usecs > 1000)
536                 proto_tree_add_text (fcp_tree, tvb, offset, 0,
537                                      "Cmd Response Time: %d msecs",
538                                      del_usecs/1000);
539             else
540                 proto_tree_add_text (fcp_tree, tvb, offset, 0,
541                                      "Cmd Response Time: %d usecs",
542                                      del_usecs);
543             if (cdata->fcp_lun >= 0)
544                 proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb,
545                                             offset, 0, cdata->fcp_lun);
546         }
547         proto_tree_add_item (fcp_tree, hf_fcp_data_ro, tvb, offset, 4, 0);
548         proto_tree_add_item (fcp_tree, hf_fcp_burstlen, tvb, offset+4, 4, 0);
549     }
550 }
551
552 static void
553 dissect_fcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
554 {
555
556 /* Set up structures needed to add the protocol subtree and manage it */
557     guint8 r_ctl;
558
559     /* Make entries in Protocol column and Info column on summary display */
560     if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
561         col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCP");
562
563     r_ctl = pinfo->r_ctl;
564
565     r_ctl &= 0xF;
566
567     if (check_col (pinfo->cinfo, COL_INFO)) {
568         col_set_str (pinfo->cinfo, COL_INFO, val_to_str (r_ctl, fcp_iu_val,
569                                                       "0x%x"));
570     }
571     
572     switch (r_ctl) {
573     case FCP_IU_DATA:
574         dissect_fcp_data (tvb, pinfo, tree);
575         break;
576     case FCP_IU_CONFIRM:
577         /* Nothing to be done here */
578         break;
579     case FCP_IU_XFER_RDY:
580         dissect_fcp_xfer_rdy (tvb, pinfo, tree);
581         break;
582     case FCP_IU_CMD:
583         dissect_fcp_cmnd (tvb, pinfo, tree);
584         break;
585     case FCP_IU_RSP:
586         dissect_fcp_rsp (tvb, pinfo, tree);
587         break;
588     default:
589         call_dissector (data_handle, tvb, pinfo, tree);
590         break;
591     }
592 }
593
594 /* Register the protocol with Ethereal */
595
596 /* this format is require because a script is used to build the C function
597    that calls all the protocol registration.
598 */
599
600 void
601 proto_register_fcp (void)
602 {                 
603
604     /* Setup list of header fields  See Section 1.6.1 for details*/
605     static hf_register_info hf[] = {
606         { &hf_fcp_type,
607           {"Field to branch off to SCSI", "fcp.type", FT_UINT8, BASE_HEX, NULL,
608            0x0, "", HFILL}},
609         {&hf_fcp_multilun,
610          {"Multi-Level LUN", "fcp.multilun", FT_BYTES, BASE_HEX, NULL, 0x0,
611           "", HFILL}},
612         { &hf_fcp_singlelun,
613           {"LUN", "fcp.lun", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
614         { &hf_fcp_crn,
615           {"Command Ref Num", "fcp.crn", FT_UINT8, BASE_DEC, NULL, 0x0, "",
616            HFILL}},
617         { &hf_fcp_taskattr,
618           {"Task Attribute", "fcp.taskattr", FT_UINT8, BASE_HEX,
619            VALS (fcp_task_attr_val), 0x7, "", HFILL}},
620         { &hf_fcp_taskmgmt,
621           {"Task Management Flags", "fcp.taskmgmt", FT_UINT8, BASE_HEX, NULL,
622            0x0, "", HFILL}},
623         { &hf_fcp_addlcdblen,
624           {"Additional CDB Length", "fcp.addlcdblen", FT_UINT8, BASE_DEC, NULL,
625            0xFC, "", HFILL}},
626         { &hf_fcp_rddata,
627           {"RDDATA", "fcp.rddata", FT_BOOLEAN, 8, NULL, 0x02, "", HFILL}},
628         { &hf_fcp_wrdata,
629           {"WRDATA", "fcp.wrdata", FT_BOOLEAN, 8, NULL, 0x01, "", HFILL}},
630         { &hf_fcp_dl,
631           {"FCP_DL", "fcp.dl", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
632         { &hf_fcp_data_ro,
633           {"FCP_DATA_RO", "fcp.data_ro", FT_UINT32, BASE_DEC, NULL, 0x0, "",
634            HFILL}},
635         { &hf_fcp_burstlen,
636           {"Burst Length", "fcp.burstlen", FT_UINT32, BASE_DEC, NULL, 0x0, "",
637            HFILL}},
638         { &hf_fcp_rspflags,
639           {"FCP_RSP Flags", "fcp.rspflags", FT_UINT8, BASE_HEX, NULL, 0x0, "",
640            HFILL}},
641         { &hf_fcp_resid,
642           {"FCP_RESID", "fcp.resid", FT_UINT32, BASE_DEC, NULL, 0x0, "",
643            HFILL}},
644         { &hf_fcp_snslen,
645           {"FCP_SNS_LEN", "fcp.snslen", FT_UINT32, BASE_DEC, NULL, 0x0, "",
646            HFILL}},
647         { &hf_fcp_rsplen,
648           {"FCP_RSP_LEN", "fcp.rsplen", FT_UINT32, BASE_DEC, NULL, 0x0, "",
649            HFILL}},
650         { &hf_fcp_rspcode,
651           {"RSP_CODE", "fcp.rspcode", FT_UINT8, BASE_HEX,
652            VALS (fcp_rsp_code_val), 0x0, "", HFILL}},
653         { &hf_fcp_scsistatus,
654           {"SCSI Status", "fcp.status", FT_UINT8, BASE_HEX,
655            VALS (scsi_status_val), 0x0, "", HFILL}},
656     };
657
658     /* Setup protocol subtree array */
659     static gint *ett[] = {
660         &ett_fcp,
661     };
662
663     /* Register the protocol name and description */
664     proto_fcp = proto_register_protocol("Fibre Channel Protocol for SCSI",
665                                         "FCP", "fcp");
666
667     /* Required function calls to register the header fields and subtrees used */
668     proto_register_field_array(proto_fcp, hf, array_length(hf));
669     proto_register_subtree_array(ett, array_length(ett));
670     fcp_dissector = register_dissector_table ("fcp.type", "FCP Type", FT_UINT8,
671                                               BASE_HEX);
672     register_init_routine (&fcp_init_protocol);
673 }
674
675 /* If this dissector uses sub-dissector registration add a registration routine.
676    This format is required because a script is used to find these routines and
677    create the code that calls these routines.
678 */
679 void
680 proto_reg_handoff_fcp (void)
681 {
682     dissector_handle_t fcp_handle;
683
684     fcp_handle = create_dissector_handle (dissect_fcp, proto_fcp);
685     dissector_add("fc.ftype", FC_FTYPE_SCSI, fcp_handle);
686
687     data_handle = find_dissector ("data");
688 }
689
690