/src/wireshark/epan/dissectors/packet-usbms-uasp.c
Line | Count | Source |
1 | | /* packet-usbms-uasp.c |
2 | | * Routines for USB Attached SCSI dissection |
3 | | * Copyright 2021, Aidan MacDonald <amachronic@protonmail.com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include <config.h> |
13 | | |
14 | | #include <epan/packet.h> |
15 | | #include "packet-usb.h" |
16 | | #include "packet-scsi.h" |
17 | | |
18 | | void proto_register_uasp(void); |
19 | | void proto_reg_handoff_uasp(void); |
20 | | |
21 | 32 | #define IF_PROTOCOL_UAS 0x62 |
22 | | |
23 | | static dissector_handle_t uasp_descriptor_handle; |
24 | | static dissector_handle_t uasp_bulk_handle; |
25 | | |
26 | | static int proto_uasp; |
27 | | |
28 | | static int hf_pipe_usage_descr_pipe_id; |
29 | | static int hf_uas_iu_id; |
30 | | static int hf_uas_tag; |
31 | | static int hf_uas_cmd_command_priority; |
32 | | static int hf_uas_cmd_task_attribute; |
33 | | static int hf_uas_cmd_additional_cdb_length; |
34 | | static int hf_uas_sense_status_qualifier; |
35 | | static int hf_uas_sense_status; |
36 | | static int hf_uas_sense_length; |
37 | | static int hf_uas_response_additional_info; |
38 | | static int hf_uas_response_code; |
39 | | static int hf_uas_taskmgmt_function; |
40 | | static int hf_uas_taskmgmt_tag_of_managed_task; |
41 | | static int hf_uas_tag_started_frame; |
42 | | static int hf_uas_tag_completed_frame; |
43 | | static int hf_uas_tag_read_ready_frame; |
44 | | static int hf_uas_tag_write_ready_frame; |
45 | | static int hf_uas_tag_data_recv_frame; |
46 | | static int hf_uas_tag_data_sent_frame; |
47 | | |
48 | | static int ett_uasp; |
49 | | static int ett_uasp_desc; |
50 | | |
51 | 0 | #define DT_PIPE_USAGE 0x24 |
52 | | |
53 | | static const value_string uasp_descriptor_type_vals[] = { |
54 | | {DT_PIPE_USAGE, "Pipe Usage"}, |
55 | | {0, NULL} |
56 | | }; |
57 | | |
58 | | static value_string_ext uasp_descriptor_type_vals_ext = |
59 | | VALUE_STRING_EXT_INIT(uasp_descriptor_type_vals); |
60 | | |
61 | 0 | #define COMMAND_PIPE_ID 0x01 |
62 | 0 | #define STATUS_PIPE_ID 0x02 |
63 | 0 | #define DATA_IN_PIPE_ID 0x03 |
64 | 0 | #define DATA_OUT_PIPE_ID 0x04 |
65 | | |
66 | | static const value_string uasp_pipe_id_vals[] = { |
67 | | {COMMAND_PIPE_ID, "Command"}, |
68 | | {STATUS_PIPE_ID, "Status"}, |
69 | | {DATA_IN_PIPE_ID, "Data-In"}, |
70 | | {DATA_OUT_PIPE_ID, "Data-Out"}, |
71 | | {0, NULL} |
72 | | }; |
73 | | |
74 | 0 | #define COMMAND_IU_ID 0x01 |
75 | 0 | #define SENSE_IU_ID 0x03 |
76 | 0 | #define RESPONSE_IU_ID 0x04 |
77 | 0 | #define TASK_MGMT_IU_ID 0x05 |
78 | 0 | #define READ_READY_IU_ID 0x06 |
79 | 0 | #define WRITE_READY_IU_ID 0x07 |
80 | | |
81 | | static const value_string uasp_iu_id_vals[] = { |
82 | | {COMMAND_IU_ID, "Command IU"}, |
83 | | {SENSE_IU_ID, "Sense IU"}, |
84 | | {RESPONSE_IU_ID, "Response IU"}, |
85 | | {TASK_MGMT_IU_ID, "Task Management IU"}, |
86 | | {READ_READY_IU_ID, "Read Ready IU"}, |
87 | | {WRITE_READY_IU_ID, "Write Ready IU"}, |
88 | | {0, NULL}, |
89 | | }; |
90 | | |
91 | | typedef struct _uasp_itlq_nexus_t { |
92 | | uint16_t tag; /* tag for this ITLQ nexus */ |
93 | | uint32_t started_frame; /* when tag was first seen */ |
94 | | uint32_t completed_frame; /* when tag was completed */ |
95 | | uint32_t read_ready_frame; /* when read ready was issued for tag */ |
96 | | uint32_t write_ready_frame; /* when write ready was issued for tag */ |
97 | | uint32_t data_recv_frame; /* when read data was received for tag */ |
98 | | uint32_t data_sent_frame; /* when write data was sent for tag */ |
99 | | itl_nexus_t* itl; |
100 | | itlq_nexus_t itlq; |
101 | | } uasp_itlq_nexus_t; |
102 | | |
103 | | typedef struct _uasp_conv_info_t { |
104 | | /* for keeping track of what endpoint is used for what */ |
105 | | uint8_t command_endpoint; |
106 | | uint8_t status_endpoint; |
107 | | uint8_t data_in_endpoint; |
108 | | uint8_t data_out_endpoint; |
109 | | |
110 | | /* tag of each read/write ready IU; indexed by pinfo->num */ |
111 | | wmem_tree_t* read_ready; |
112 | | wmem_tree_t* write_ready; |
113 | | |
114 | | /* ITL nexus; indexed by LUN */ |
115 | | wmem_tree_t* itl; |
116 | | |
117 | | /* UASP ITLQ nexus per command; multi part key |
118 | | * [0] = UAS tag |
119 | | * [1] = pinfo->num */ |
120 | | wmem_tree_t* itlq; |
121 | | } uasp_conv_info_t; |
122 | | |
123 | | static uasp_conv_info_t* |
124 | | get_uasp_conv_info(usb_conv_info_t *usb_conv_info) |
125 | 0 | { |
126 | 0 | uasp_conv_info_t *uasp_conv_info = (uasp_conv_info_t *)usb_conv_info->class_data; |
127 | |
|
128 | 0 | if (!uasp_conv_info) { |
129 | 0 | uasp_conv_info = wmem_new(wmem_file_scope(), uasp_conv_info_t); |
130 | 0 | uasp_conv_info->command_endpoint = 0; |
131 | 0 | uasp_conv_info->status_endpoint = 0; |
132 | 0 | uasp_conv_info->data_in_endpoint = 0; |
133 | 0 | uasp_conv_info->data_out_endpoint = 0; |
134 | 0 | uasp_conv_info->read_ready = wmem_tree_new(wmem_file_scope()); |
135 | 0 | uasp_conv_info->write_ready = wmem_tree_new(wmem_file_scope()); |
136 | 0 | uasp_conv_info->itl = wmem_tree_new(wmem_file_scope()); |
137 | 0 | uasp_conv_info->itlq = wmem_tree_new(wmem_file_scope()); |
138 | |
|
139 | 0 | usb_conv_info->class_data = uasp_conv_info; |
140 | 0 | usb_conv_info->class_data_type = USB_CONV_MASS_STORAGE_UASP; |
141 | 0 | } else if (usb_conv_info->class_data_type != USB_CONV_MASS_STORAGE_UASP) { |
142 | 0 | return NULL; |
143 | 0 | } |
144 | | |
145 | 0 | return uasp_conv_info; |
146 | 0 | } |
147 | | |
148 | | static uint16_t |
149 | | get_scsi_lun(tvbuff_t* tvb, int offset) |
150 | 0 | { |
151 | 0 | uint16_t lun; |
152 | | |
153 | | /* Copied from packet-iscsi.c - not really correct but good enough... */ |
154 | 0 | if (tvb_get_uint8(tvb, offset) & 0x40) { |
155 | | /* volume set addressing */ |
156 | 0 | lun = tvb_get_uint8(tvb, offset) & 0x3f; |
157 | 0 | lun <<= 8; |
158 | 0 | lun |= tvb_get_uint8(tvb,offset + 1); |
159 | 0 | } else { |
160 | 0 | lun = tvb_get_uint8(tvb, offset + 1); |
161 | 0 | } |
162 | |
|
163 | 0 | return lun; |
164 | 0 | } |
165 | | |
166 | | static uasp_itlq_nexus_t* |
167 | | create_itlq_nexus(packet_info *pinfo, uasp_conv_info_t *uasp_conv_info, uint16_t lun, uint16_t tag) |
168 | 0 | { |
169 | 0 | wmem_tree_key_t key[3]; |
170 | 0 | uint32_t tag32 = tag; |
171 | 0 | itl_nexus_t *itl; |
172 | 0 | uasp_itlq_nexus_t *uitlq; |
173 | | |
174 | | /* ensure ITL nexus exists */ |
175 | 0 | itl = (itl_nexus_t *)wmem_tree_lookup32(uasp_conv_info->itl, lun); |
176 | 0 | if(!itl) { |
177 | 0 | itl = wmem_new(wmem_file_scope(), itl_nexus_t); |
178 | 0 | itl->cmdset = 0xff; |
179 | 0 | itl->conversation = NULL; |
180 | 0 | wmem_tree_insert32(uasp_conv_info->itl, lun, itl); |
181 | 0 | } |
182 | | |
183 | | /* ensure ITLQ nexus exists */ |
184 | 0 | key[0].length = 1; |
185 | 0 | key[0].key = &tag32; |
186 | 0 | key[1].length = 1; |
187 | 0 | key[1].key = &pinfo->num; |
188 | 0 | key[2].length = 0; |
189 | |
|
190 | 0 | uitlq = (uasp_itlq_nexus_t *)wmem_tree_lookup32_array(uasp_conv_info->itlq, key); |
191 | 0 | if(!uitlq) { |
192 | 0 | uitlq = wmem_new(wmem_file_scope(), uasp_itlq_nexus_t); |
193 | 0 | uitlq->tag = tag; |
194 | 0 | uitlq->started_frame = pinfo->num; |
195 | 0 | uitlq->completed_frame = 0; |
196 | 0 | uitlq->read_ready_frame = 0; |
197 | 0 | uitlq->write_ready_frame = 0; |
198 | 0 | uitlq->data_sent_frame = 0; |
199 | 0 | uitlq->data_recv_frame = 0; |
200 | 0 | uitlq->itl = itl; |
201 | 0 | uitlq->itlq.lun = lun; |
202 | 0 | uitlq->itlq.scsi_opcode = 0xffff; |
203 | 0 | uitlq->itlq.task_flags = 0; |
204 | 0 | uitlq->itlq.data_length = 0; |
205 | 0 | uitlq->itlq.bidir_data_length = 0; |
206 | 0 | uitlq->itlq.fc_time = pinfo->abs_ts; |
207 | 0 | uitlq->itlq.first_exchange_frame = pinfo->num; |
208 | 0 | uitlq->itlq.last_exchange_frame = 0; |
209 | 0 | uitlq->itlq.flags = 0; |
210 | 0 | uitlq->itlq.alloc_len = 0; |
211 | 0 | uitlq->itlq.extra_data = NULL; |
212 | |
|
213 | 0 | wmem_tree_insert32_array(uasp_conv_info->itlq, key, uitlq); |
214 | 0 | } |
215 | |
|
216 | 0 | return uitlq; |
217 | 0 | } |
218 | | |
219 | | static uasp_itlq_nexus_t* |
220 | | get_itlq_nexus(packet_info* pinfo, uasp_conv_info_t *uasp_conv_info, uint16_t tag) |
221 | 0 | { |
222 | 0 | uint32_t tag32 = tag; |
223 | 0 | wmem_tree_key_t key[3]; |
224 | 0 | uasp_itlq_nexus_t *uitlq; |
225 | |
|
226 | 0 | key[0].length = 1; |
227 | 0 | key[0].key = &tag32; |
228 | 0 | key[1].length = 1; |
229 | 0 | key[1].key = &pinfo->num; |
230 | 0 | key[2].length = 0; |
231 | |
|
232 | 0 | uitlq = (uasp_itlq_nexus_t *)wmem_tree_lookup32_array_le(uasp_conv_info->itlq, key); |
233 | 0 | if(!uitlq || uitlq->tag != tag) |
234 | 0 | return NULL; |
235 | | |
236 | 0 | return uitlq; |
237 | 0 | } |
238 | | |
239 | | static void |
240 | | create_ready_iu(wmem_tree_t* tree, packet_info* pinfo, uint16_t tag) |
241 | 0 | { |
242 | 0 | wmem_tree_insert32(tree, pinfo->num, GUINT_TO_POINTER(tag)); |
243 | 0 | } |
244 | | |
245 | | static uint16_t |
246 | | get_ready_iu(wmem_tree_t* tree, packet_info* pinfo) |
247 | 0 | { |
248 | 0 | return GPOINTER_TO_UINT(wmem_tree_lookup32_le(tree, pinfo->num)); |
249 | 0 | } |
250 | | |
251 | 0 | #define DATA_WRITE (-1) |
252 | 0 | #define DATA_READ (-2) |
253 | | |
254 | | static void |
255 | | add_uasp_tag_links(tvbuff_t *tvb, proto_tree *uasp_tree, uasp_itlq_nexus_t *uitlq, int kind) |
256 | 0 | { |
257 | 0 | proto_item *ti; |
258 | |
|
259 | 0 | if (!uitlq) |
260 | 0 | return; |
261 | | |
262 | 0 | if (uitlq->started_frame && kind != COMMAND_IU_ID && kind != TASK_MGMT_IU_ID) { |
263 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag_started_frame, tvb, 0, 0, uitlq->started_frame); |
264 | 0 | proto_item_set_generated(ti); |
265 | 0 | } |
266 | |
|
267 | 0 | if (uitlq->read_ready_frame && kind != READ_READY_IU_ID) { |
268 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag_read_ready_frame, tvb, 0, 0, uitlq->read_ready_frame); |
269 | 0 | proto_item_set_generated(ti); |
270 | 0 | } |
271 | |
|
272 | 0 | if (uitlq->write_ready_frame && kind != WRITE_READY_IU_ID) { |
273 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag_write_ready_frame, tvb, 0, 0, uitlq->write_ready_frame); |
274 | 0 | proto_item_set_generated(ti); |
275 | 0 | } |
276 | |
|
277 | 0 | if (uitlq->data_recv_frame && kind != DATA_READ) { |
278 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag_data_recv_frame, tvb, 0, 0, uitlq->data_recv_frame); |
279 | 0 | proto_item_set_generated(ti); |
280 | 0 | } |
281 | |
|
282 | 0 | if (uitlq->data_sent_frame && kind != DATA_WRITE) { |
283 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag_data_sent_frame, tvb, 0, 0, uitlq->data_sent_frame); |
284 | 0 | proto_item_set_generated(ti); |
285 | 0 | } |
286 | |
|
287 | 0 | if (uitlq->completed_frame && kind != SENSE_IU_ID && kind != RESPONSE_IU_ID) { |
288 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag_completed_frame, tvb, 0, 0, uitlq->completed_frame); |
289 | 0 | proto_item_set_generated(ti); |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | static int |
294 | | dissect_uasp_iu(tvbuff_t *tvb, packet_info *pinfo, |
295 | | proto_tree *parent_tree, proto_tree *uasp_tree, |
296 | | urb_info_t *urb _U_, uasp_conv_info_t *uasp_conv_info) |
297 | 0 | { |
298 | 0 | uint8_t iu_id; |
299 | 0 | uint8_t status; |
300 | 0 | uint16_t tag; |
301 | 0 | uint16_t lun; |
302 | 0 | uasp_itlq_nexus_t *uitlq = NULL; |
303 | 0 | int rlen; |
304 | 0 | tvbuff_t *cdb_tvb; |
305 | | |
306 | | /* an IU header is 4 bytes */ |
307 | 0 | if (tvb_reported_length(tvb) < 4) |
308 | 0 | return 0; |
309 | | |
310 | 0 | iu_id = tvb_get_uint8(tvb, 0); |
311 | 0 | tag = tvb_get_ntohs(tvb, 2); |
312 | |
|
313 | 0 | col_add_str(pinfo->cinfo, COL_INFO, |
314 | 0 | val_to_str(pinfo->pool, iu_id, uasp_iu_id_vals, "Unknown IU [0x%02x]")); |
315 | |
|
316 | 0 | proto_tree_add_item(uasp_tree, hf_uas_iu_id, tvb, 0, 1, ENC_NA); |
317 | 0 | proto_tree_add_item(uasp_tree, hf_uas_tag, tvb, 2, 2, ENC_BIG_ENDIAN); |
318 | |
|
319 | 0 | switch(iu_id) { |
320 | 0 | case COMMAND_IU_ID: |
321 | 0 | proto_tree_add_item(uasp_tree, hf_uas_cmd_command_priority, tvb, 4, 1, ENC_NA); |
322 | 0 | proto_tree_add_item(uasp_tree, hf_uas_cmd_task_attribute, tvb, 4, 1, ENC_NA); |
323 | 0 | proto_tree_add_item(uasp_tree, hf_uas_cmd_additional_cdb_length, tvb, 6, 1, ENC_NA); |
324 | 0 | dissect_scsi_lun(uasp_tree, tvb, 8); |
325 | |
|
326 | 0 | lun = get_scsi_lun(tvb, 8); |
327 | 0 | uitlq = create_itlq_nexus(pinfo, uasp_conv_info, lun, tag); |
328 | |
|
329 | 0 | rlen = 16 + tvb_get_uint8(tvb, 6); |
330 | 0 | if (rlen) { |
331 | 0 | cdb_tvb = tvb_new_subset_length(tvb, 16, rlen); |
332 | 0 | dissect_scsi_cdb(cdb_tvb, pinfo, parent_tree, SCSI_DEV_UNKNOWN, |
333 | 0 | &uitlq->itlq, uitlq->itl); |
334 | 0 | } |
335 | |
|
336 | 0 | break; |
337 | | |
338 | 0 | case SENSE_IU_ID: |
339 | 0 | proto_tree_add_item(uasp_tree, hf_uas_sense_status_qualifier, tvb, 4, 2, ENC_BIG_ENDIAN); |
340 | 0 | proto_tree_add_item(uasp_tree, hf_uas_sense_status, tvb, 6, 1, ENC_NA); |
341 | 0 | proto_tree_add_item(uasp_tree, hf_uas_sense_length, tvb, 14, 2, ENC_BIG_ENDIAN); |
342 | |
|
343 | 0 | uitlq = get_itlq_nexus(pinfo, uasp_conv_info, tag); |
344 | 0 | if (uitlq) { |
345 | 0 | uitlq->completed_frame = pinfo->num; |
346 | 0 | uitlq->itlq.last_exchange_frame = pinfo->num; |
347 | |
|
348 | 0 | status = tvb_get_uint8(tvb, 6); |
349 | 0 | dissect_scsi_rsp(tvb, pinfo, parent_tree, &uitlq->itlq, uitlq->itl, status); |
350 | | |
351 | | /* dissect sense info, if any */ |
352 | 0 | rlen = tvb_get_ntohs(tvb, 14); |
353 | 0 | if (rlen) { |
354 | 0 | dissect_scsi_snsinfo(tvb, pinfo, parent_tree, 16, rlen, &uitlq->itlq, uitlq->itl); |
355 | 0 | } |
356 | 0 | } |
357 | |
|
358 | 0 | break; |
359 | | |
360 | 0 | case RESPONSE_IU_ID: |
361 | 0 | proto_tree_add_item(uasp_tree, hf_uas_response_additional_info, tvb, 4, 3, ENC_BIG_ENDIAN); |
362 | 0 | proto_tree_add_item(uasp_tree, hf_uas_response_code, tvb, 7, 1, ENC_NA); |
363 | 0 | break; |
364 | | |
365 | 0 | case TASK_MGMT_IU_ID: |
366 | 0 | proto_tree_add_item(uasp_tree, hf_uas_taskmgmt_function, tvb, 4, 1, ENC_NA); |
367 | 0 | proto_tree_add_item(uasp_tree, hf_uas_taskmgmt_tag_of_managed_task, tvb, 6, 2, ENC_BIG_ENDIAN); |
368 | 0 | dissect_scsi_lun(uasp_tree, tvb, 8); |
369 | 0 | break; |
370 | | |
371 | 0 | case READ_READY_IU_ID: |
372 | 0 | uitlq = get_itlq_nexus(pinfo, uasp_conv_info, tag); |
373 | 0 | if (uitlq) |
374 | 0 | uitlq->read_ready_frame = pinfo->num; |
375 | |
|
376 | 0 | create_ready_iu(uasp_conv_info->read_ready, pinfo, tag); |
377 | 0 | break; |
378 | | |
379 | 0 | case WRITE_READY_IU_ID: |
380 | 0 | uitlq = get_itlq_nexus(pinfo, uasp_conv_info, tag); |
381 | 0 | if (uitlq) |
382 | 0 | uitlq->write_ready_frame = pinfo->num; |
383 | |
|
384 | 0 | create_ready_iu(uasp_conv_info->write_ready, pinfo, tag); |
385 | 0 | break; |
386 | 0 | } |
387 | | |
388 | 0 | add_uasp_tag_links(tvb, uasp_tree, uitlq, iu_id); |
389 | |
|
390 | 0 | return tvb_captured_length(tvb); |
391 | 0 | } |
392 | | |
393 | | static int |
394 | | dissect_uasp_data(tvbuff_t *tvb, packet_info *pinfo, |
395 | | proto_tree *parent_tree, proto_tree *uasp_tree, |
396 | | urb_info_t *urb, uasp_conv_info_t *uasp_conv_info) |
397 | 0 | { |
398 | 0 | proto_item *ti; |
399 | 0 | uint16_t tag; |
400 | 0 | uasp_itlq_nexus_t *uitlq; |
401 | 0 | bool is_request; |
402 | |
|
403 | 0 | is_request = (urb->direction == P2P_DIR_SENT) ? true : false; |
404 | | |
405 | | /* TODO - fetch tag from USB 3.0 Bulk Streams. |
406 | | * |
407 | | * It seems Wireshark doesn't track the stream ID so we can't yet |
408 | | * dissect UASP over USB 3.0 traffic. (The Linux kernel doesn't even |
409 | | * export an URB's stream ID, so OS support for this might be spotty |
410 | | * or even non-existent...) |
411 | | */ |
412 | 0 | if (is_request) |
413 | 0 | tag = get_ready_iu(uasp_conv_info->write_ready, pinfo); |
414 | 0 | else |
415 | 0 | tag = get_ready_iu(uasp_conv_info->read_ready, pinfo); |
416 | | |
417 | | /* add tag to tree */ |
418 | 0 | ti = proto_tree_add_uint(uasp_tree, hf_uas_tag, tvb, 0, 0, tag); |
419 | 0 | proto_item_set_generated(ti); |
420 | |
|
421 | 0 | uitlq = get_itlq_nexus(pinfo, uasp_conv_info, tag); |
422 | 0 | if (uitlq) { |
423 | 0 | if (is_request) |
424 | 0 | uitlq->data_sent_frame = pinfo->num; |
425 | 0 | else |
426 | 0 | uitlq->data_recv_frame = pinfo->num; |
427 | |
|
428 | 0 | add_uasp_tag_links(tvb, uasp_tree, uitlq, is_request ? DATA_WRITE : DATA_READ); |
429 | 0 | dissect_scsi_payload(tvb, pinfo, parent_tree, is_request, &uitlq->itlq, uitlq->itl, 0); |
430 | 0 | } |
431 | |
|
432 | 0 | return tvb_captured_length(tvb); |
433 | 0 | } |
434 | | |
435 | | static int |
436 | | dissect_uasp_bulk(tvbuff_t *tvb, |
437 | | packet_info *pinfo, |
438 | | proto_tree *parent_tree, |
439 | | void *data) |
440 | 0 | { |
441 | 0 | typedef int(*uasp_dissector_t)(tvbuff_t *, packet_info *, proto_tree *, |
442 | 0 | proto_tree *, urb_info_t *, uasp_conv_info_t *); |
443 | |
|
444 | 0 | proto_tree *uasp_tree; |
445 | 0 | proto_item *ti; |
446 | 0 | uasp_dissector_t dissector = NULL; |
447 | 0 | uint8_t endpoint; |
448 | 0 | urb_info_t *urb = (urb_info_t *)data; |
449 | 0 | uasp_conv_info_t *uasp_conv_info; |
450 | |
|
451 | 0 | if (!urb) |
452 | 0 | return 0; |
453 | | |
454 | 0 | uasp_conv_info = get_uasp_conv_info(urb->conv); |
455 | 0 | if (!uasp_conv_info) |
456 | 0 | return 0; |
457 | | |
458 | 0 | endpoint = urb->endpoint; |
459 | 0 | if (endpoint == uasp_conv_info->command_endpoint || |
460 | 0 | endpoint == uasp_conv_info->status_endpoint) |
461 | 0 | dissector = dissect_uasp_iu; |
462 | 0 | else if (endpoint == uasp_conv_info->data_in_endpoint || |
463 | 0 | endpoint == uasp_conv_info->data_out_endpoint) |
464 | 0 | dissector = dissect_uasp_data; |
465 | 0 | else |
466 | 0 | return 0; |
467 | | |
468 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "UASP"); |
469 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
470 | |
|
471 | 0 | ti = proto_tree_add_protocol_format(parent_tree, proto_uasp, tvb, 0, -1, |
472 | 0 | "USB Attached SCSI"); |
473 | 0 | uasp_tree = proto_item_add_subtree(ti, ett_uasp); |
474 | |
|
475 | 0 | return dissector(tvb, pinfo, parent_tree, uasp_tree, urb, uasp_conv_info); |
476 | 0 | } |
477 | | |
478 | | static int |
479 | | dissect_uasp_descriptor(tvbuff_t *tvb, |
480 | | packet_info *pinfo _U_, |
481 | | proto_tree *parent_tree, |
482 | | void *data _U_) |
483 | 0 | { |
484 | 0 | uint8_t desc_type; |
485 | 0 | uint8_t desc_len; |
486 | 0 | proto_tree *desc_tree; |
487 | 0 | proto_tree *desc_tree_item; |
488 | 0 | urb_info_t *urb = (urb_info_t *)data; |
489 | 0 | usb_trans_info_t *usb_trans_info = NULL; |
490 | 0 | uasp_conv_info_t *uasp_conv_info; |
491 | |
|
492 | 0 | if (urb) |
493 | 0 | usb_trans_info = urb->usb_trans_info; |
494 | | |
495 | | /* Descriptor must have a length and type field. */ |
496 | 0 | if (tvb_reported_length(tvb) < 2) |
497 | 0 | return 0; |
498 | | |
499 | 0 | desc_len = tvb_get_uint8(tvb, 0); |
500 | 0 | desc_type = tvb_get_uint8(tvb, 1); |
501 | |
|
502 | 0 | if (desc_type != DT_PIPE_USAGE) |
503 | 0 | return 0; |
504 | | |
505 | 0 | desc_tree = proto_tree_add_subtree(parent_tree, tvb, 0, desc_len, |
506 | 0 | ett_uasp_desc, &desc_tree_item, |
507 | 0 | "UAS PIPE USAGE DESCRIPTOR"); |
508 | |
|
509 | 0 | dissect_usb_descriptor_header(desc_tree, tvb, 0, |
510 | 0 | &uasp_descriptor_type_vals_ext); |
511 | 0 | proto_tree_add_item(desc_tree, hf_pipe_usage_descr_pipe_id, |
512 | 0 | tvb, 2, 1, ENC_NA); |
513 | | |
514 | | /* The pipe usage descriptor should follow the endpoint descriptor |
515 | | * of the endpoint it applies to. Keep track of the pipe ID for the |
516 | | * endpoint so the bulk dissector can distinguish between commands |
517 | | * and data reliably */ |
518 | 0 | if (!pinfo->fd->visited && usb_trans_info && usb_trans_info->interface_info) { |
519 | 0 | uint8_t endpoint = usb_trans_info->interface_endpoint; |
520 | 0 | uint8_t pipe_id = tvb_get_uint8(tvb, 2); |
521 | |
|
522 | 0 | uasp_conv_info = get_uasp_conv_info(usb_trans_info->interface_info); |
523 | 0 | if (uasp_conv_info) { |
524 | 0 | switch (pipe_id) { |
525 | 0 | case COMMAND_PIPE_ID: |
526 | 0 | uasp_conv_info->command_endpoint = endpoint; |
527 | 0 | break; |
528 | 0 | case STATUS_PIPE_ID: |
529 | 0 | uasp_conv_info->status_endpoint = endpoint; |
530 | 0 | break; |
531 | 0 | case DATA_IN_PIPE_ID: |
532 | 0 | uasp_conv_info->data_in_endpoint = endpoint; |
533 | 0 | break; |
534 | 0 | case DATA_OUT_PIPE_ID: |
535 | 0 | uasp_conv_info->data_out_endpoint = endpoint; |
536 | 0 | break; |
537 | 0 | } |
538 | 0 | } |
539 | 0 | } |
540 | | |
541 | 0 | return desc_len; |
542 | 0 | } |
543 | | |
544 | | void |
545 | | proto_register_uasp(void) |
546 | 16 | { |
547 | 16 | static hf_register_info hf[] = { |
548 | 16 | { &hf_pipe_usage_descr_pipe_id, |
549 | 16 | { "bPipeID", "uasp.pipe_usage.bPipeID", |
550 | 16 | FT_UINT8, BASE_HEX, VALS(uasp_pipe_id_vals), 0x00, NULL, HFILL } }, |
551 | | |
552 | 16 | { &hf_uas_iu_id, |
553 | 16 | { "IU ID", "uasp.iu_id", |
554 | 16 | FT_UINT8, BASE_HEX, VALS(uasp_iu_id_vals), 0x00, NULL, HFILL } }, |
555 | 16 | { &hf_uas_tag, |
556 | 16 | { "Tag", "uasp.tag", |
557 | 16 | FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL } }, |
558 | | |
559 | 16 | { &hf_uas_cmd_command_priority, |
560 | 16 | { "Command Priority", "uasp.command.priority", |
561 | 16 | FT_UINT8, BASE_DEC, NULL, 0x78, NULL, HFILL } }, |
562 | 16 | { &hf_uas_cmd_task_attribute, |
563 | 16 | { "Task Attribute", "uasp.command.task_attr", |
564 | 16 | FT_UINT8, BASE_HEX, NULL, 0x07, NULL, HFILL } }, |
565 | 16 | { &hf_uas_cmd_additional_cdb_length, |
566 | 16 | { "Additional CDB Length", "uasp.command.add_cdb_length", |
567 | 16 | FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } }, |
568 | | |
569 | 16 | { &hf_uas_sense_status_qualifier, |
570 | 16 | { "Status Qualifier", "uasp.sense.status_qualifier", |
571 | 16 | FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, |
572 | 16 | { &hf_uas_sense_status, |
573 | 16 | { "Status", "uasp.sense.status", |
574 | 16 | FT_UINT8, BASE_DEC, VALS(scsi_status_val), 0x00, NULL, HFILL } }, |
575 | 16 | { &hf_uas_sense_length, |
576 | 16 | { "Length", "uasp.sense.length", |
577 | 16 | FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, |
578 | | |
579 | 16 | { &hf_uas_response_additional_info, |
580 | 16 | { "Additional Response Info", "uasp.response.add_info", |
581 | 16 | FT_UINT24, BASE_HEX, NULL, 0x00, NULL, HFILL } }, |
582 | 16 | { &hf_uas_response_code, |
583 | 16 | { "Response Code", "uasp.response.code", |
584 | 16 | FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } }, |
585 | | |
586 | 16 | { &hf_uas_taskmgmt_function, |
587 | 16 | { "Task Management Function", "uasp.task_mgmt.function", |
588 | 16 | FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } }, |
589 | 16 | { &hf_uas_taskmgmt_tag_of_managed_task, |
590 | 16 | { "Tag of Managed Task", "uasp.task_mgmt.managed_tag", |
591 | 16 | FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL } }, |
592 | | |
593 | 16 | { &hf_uas_tag_started_frame, |
594 | 16 | { "Tag started in", "uasp.tag_started_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, |
595 | 16 | "The command with this tag was started in this frame", HFILL } }, |
596 | 16 | { &hf_uas_tag_completed_frame, |
597 | 16 | { "Tag completed in", "uasp.tag_completed_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, |
598 | 16 | "The command with this tag was completed in this frame", HFILL } }, |
599 | 16 | { &hf_uas_tag_read_ready_frame, |
600 | 16 | { "Tag read ready in", "uasp.tag_read_ready_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, |
601 | 16 | "The request data for the tag became ready in this frame", HFILL } }, |
602 | 16 | { &hf_uas_tag_write_ready_frame, |
603 | 16 | { "Tag write ready in", "uasp.tag_write_ready_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, |
604 | 16 | "The request data for the tag became ready in this frame", HFILL } }, |
605 | 16 | { &hf_uas_tag_data_recv_frame, |
606 | 16 | { "Tag data received in", "uasp.tag_data_recv_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, |
607 | 16 | "The response data for the tag was transmitted in this frame", HFILL } }, |
608 | 16 | { &hf_uas_tag_data_sent_frame, |
609 | 16 | { "Tag data sent in", "uasp.tag_data_sent_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, |
610 | 16 | "The request data for the tag was transmitted in this frame", HFILL } }, |
611 | 16 | }; |
612 | | |
613 | 16 | static int *uasp_ett[] = { |
614 | 16 | &ett_uasp, |
615 | 16 | &ett_uasp_desc, |
616 | 16 | }; |
617 | | |
618 | 16 | proto_uasp = proto_register_protocol("USB Attached SCSI", "UASP", "uasp"); |
619 | 16 | proto_register_field_array(proto_uasp, hf, array_length(hf)); |
620 | 16 | proto_register_subtree_array(uasp_ett, array_length(uasp_ett)); |
621 | | |
622 | 16 | uasp_descriptor_handle = register_dissector("uasp", dissect_uasp_descriptor, proto_uasp); |
623 | 16 | uasp_bulk_handle = register_dissector("uasp.bulk", dissect_uasp_bulk, proto_uasp); |
624 | 16 | } |
625 | | |
626 | | void |
627 | | proto_reg_handoff_uasp(void) |
628 | 16 | { |
629 | 16 | dissector_add_uint("usbms.descriptor", IF_PROTOCOL_UAS, uasp_descriptor_handle); |
630 | 16 | dissector_add_uint("usbms.bulk", IF_PROTOCOL_UAS, uasp_bulk_handle); |
631 | 16 | } |