Index: apps/diskmode/usb.c |
— | — | @@ -34,6 +34,7 @@ |
35 | 35 | static union usb_endpoint_number usb_inep = { .number = 0, .direction = USB_ENDPOINT_DIRECTION_IN };
|
36 | 36 | static int maxpacket = 512;
|
37 | 37 | static struct wakeup mainloop_wakeup;
|
| 38 | +static char* error = NULL;
|
38 | 39 |
|
39 | 40 |
|
40 | 41 | static const struct usb_devicedescriptor usb_devicedescriptor =
|
— | — | @@ -204,7 +205,7 @@ |
205 | 206 |
|
206 | 207 | void handle_xfer_complete(const struct usb_instance* data, int ifnum, int epnum, int bytesleft)
|
207 | 208 | {
|
208 | | - ums_xfer_complete(epnum & 0x80, bytesleft);
|
| 209 | + ums_xfer_complete(epnum, bytesleft);
|
209 | 210 | }
|
210 | 211 |
|
211 | 212 |
|
— | — | @@ -234,7 +235,6 @@ |
235 | 236 | static struct usb_endpoint usb_c1_i0_a0_ep1in =
|
236 | 237 | {
|
237 | 238 | .number = { .number = 0, .direction = USB_ENDPOINT_DIRECTION_IN },
|
238 | | - .ctrl_request = handle_ep_ctrl_request,
|
239 | 239 | .xfer_complete = handle_xfer_complete,
|
240 | 240 | .timeout = handle_timeout,
|
241 | 241 | };
|
— | — | @@ -329,6 +329,8 @@ |
330 | 330 | ums_handle_async();
|
331 | 331 |
|
332 | 332 | usbmanager_uninstall_custom();
|
| 333 | +
|
| 334 | + if (error) panic(PANIC_KILLTHREAD, error);
|
333 | 335 | }
|
334 | 336 |
|
335 | 337 |
|
— | — | @@ -350,9 +352,21 @@ |
351 | 353 | }
|
352 | 354 |
|
353 | 355 |
|
354 | | -void usb_stall()
|
| 356 | +void usb_stall_in()
|
355 | 357 | {
|
356 | | - usb_set_stall(usb_handle, usb_outep, true);
|
357 | 358 | usb_set_stall(usb_handle, usb_inep, true);
|
358 | 359 | }
|
359 | 360 |
|
| 361 | +
|
| 362 | +void usb_stall_out()
|
| 363 | +{
|
| 364 | + usb_set_stall(usb_handle, usb_outep, true);
|
| 365 | +}
|
| 366 | +
|
| 367 | +
|
| 368 | +void fail(char* errormsg)
|
| 369 | +{
|
| 370 | + error = errormsg;
|
| 371 | + ums_ejected = true;
|
| 372 | + wakeup_signal(&mainloop_wakeup);
|
| 373 | +}
|
Index: apps/diskmode/usb.h |
— | — | @@ -36,7 +36,9 @@ |
37 | 37 | extern void enqueue_async();
|
38 | 38 | extern void usb_transmit(void* buffer, uint32_t len);
|
39 | 39 | extern void usb_receive(void* buffer, uint32_t len);
|
40 | | -extern void usb_stall();
|
| 40 | +extern void usb_stall_in();
|
| 41 | +extern void usb_stall_out();
|
| 42 | +extern void fail(char* errormsg);
|
41 | 43 |
|
42 | 44 | #endif
|
43 | 45 |
|
Index: apps/diskmode/ums.c |
— | — | @@ -26,6 +26,7 @@ |
27 | 27 | #include "ums.h"
|
28 | 28 | #include "scsi.h"
|
29 | 29 | #include "usb.h"
|
| 30 | +#include "../../emcore/trunk/target/ipodclassic/storage_ata-target.h"
|
30 | 31 |
|
31 | 32 |
|
32 | 33 | #define UMS_BUFSIZE 65536
|
— | — | @@ -40,6 +41,8 @@ |
41 | 42 | unsigned int tag;
|
42 | 43 | unsigned int lun;
|
43 | 44 | unsigned int last_result;
|
| 45 | + unsigned int bytes_pending;
|
| 46 | + bool data_direction;
|
44 | 47 | } cur_cmd;
|
45 | 48 |
|
46 | 49 |
|
— | — | @@ -52,8 +55,38 @@ |
53 | 56 | } cur_sense_data;
|
54 | 57 |
|
55 | 58 |
|
56 | | -struct __attribute__((packed,aligned(32))) command_block_wrapper
|
| 59 | +static enum
|
57 | 60 | {
|
| 61 | + SAT_PENDING_NONE = 0,
|
| 62 | + SAT_PENDING_SRST,
|
| 63 | + SAT_PENDING_HRST,
|
| 64 | + SAT_PENDING_CMD,
|
| 65 | + SAT_PENDING_READ_CDB,
|
| 66 | +} sat_pending = SAT_PENDING_NONE;
|
| 67 | +static struct ata_raw_cmd_t sat_command;
|
| 68 | +static struct __attribute__((packed)) sat_response_information
|
| 69 | +{
|
| 70 | + uint8_t descriptor_code;
|
| 71 | + uint8_t additional_length;
|
| 72 | + uint8_t extend;
|
| 73 | + uint8_t error;
|
| 74 | + uint8_t sector_count_h;
|
| 75 | + uint8_t sector_count_l;
|
| 76 | + uint8_t lba_low_h;
|
| 77 | + uint8_t lba_low_l;
|
| 78 | + uint8_t lba_mid_h;
|
| 79 | + uint8_t lba_mid_l;
|
| 80 | + uint8_t lba_high_h;
|
| 81 | + uint8_t lba_high_l;
|
| 82 | + uint8_t device;
|
| 83 | + uint8_t status;
|
| 84 | +} sat_response_information;
|
| 85 | +static bool have_sat_response_information = false;
|
| 86 | +static bool sat_check;
|
| 87 | +
|
| 88 | +
|
| 89 | +struct __attribute__((packed)) command_block_wrapper
|
| 90 | +{
|
58 | 91 | unsigned int signature;
|
59 | 92 | unsigned int tag;
|
60 | 93 | unsigned int data_transfer_length;
|
— | — | @@ -78,7 +111,15 @@ |
79 | 112 | struct inquiry_data inquiry;
|
80 | 113 | struct capacity capacity_data;
|
81 | 114 | struct format_capacity format_capacity_data;
|
82 | | - struct sense_data sense_data;
|
| 115 | + struct sense_data_fixed sense_data_fixed;
|
| 116 | + struct
|
| 117 | + {
|
| 118 | + struct sense_data_descr header;
|
| 119 | + union
|
| 120 | + {
|
| 121 | + struct sat_response_information sat_response;
|
| 122 | + } info;
|
| 123 | + } sense_data_descr;
|
83 | 124 | struct mode_sense_data_6 ms_data_6;
|
84 | 125 | struct mode_sense_data_10 ms_data_10;
|
85 | 126 | struct report_lun_data lun_data;
|
— | — | @@ -91,9 +132,10 @@ |
92 | 133 | WAITING_FOR_COMMAND,
|
93 | 134 | SENDING_BLOCKS,
|
94 | 135 | SENDING_RESULT,
|
95 | | - SENDING_FAILED_RESULT,
|
96 | 136 | RECEIVING_BLOCKS,
|
97 | | - WAITING_FOR_CSW_COMPLETION
|
| 137 | + WAITING_FOR_CSW_COMPLETION,
|
| 138 | + RECEIVING_SAT_WRITE,
|
| 139 | + PROCESSING,
|
98 | 140 | } state = WAITING_FOR_COMMAND;
|
99 | 141 |
|
100 | 142 | static uint32_t length;
|
— | — | @@ -135,9 +177,15 @@ |
136 | 178 |
|
137 | 179 | static void send_csw(int status)
|
138 | 180 | {
|
| 181 | + if (cur_cmd.bytes_pending)
|
| 182 | + {
|
| 183 | + if (cur_cmd.data_direction) usb_stall_in();
|
| 184 | + else usb_stall_out();
|
| 185 | + }
|
| 186 | +
|
139 | 187 | tb.csw.signature = 0x53425355;
|
140 | 188 | tb.csw.tag = cur_cmd.tag;
|
141 | | - tb.csw.data_residue = 0;
|
| 189 | + tb.csw.data_residue = cur_cmd.bytes_pending;
|
142 | 190 | tb.csw.status = status;
|
143 | 191 |
|
144 | 192 | state = WAITING_FOR_CSW_COMPLETION;
|
— | — | @@ -155,32 +203,66 @@ |
156 | 204 |
|
157 | 205 | static void receive_block_data(void* data, int size)
|
158 | 206 | {
|
159 | | - length = size;
|
160 | | - usb_receive(data, size);
|
161 | | - state = RECEIVING_BLOCKS;
|
| 207 | + if (cur_cmd.data_direction) fail("diskmode: Attempting to receive data for IN command!");
|
| 208 | + else if (size > cur_cmd.bytes_pending) fail("diskmode: Receive overrun!");
|
| 209 | + else
|
| 210 | + {
|
| 211 | + cur_cmd.bytes_pending -= size;
|
| 212 | + length = size;
|
| 213 | + usb_receive(data, size);
|
| 214 | + state = RECEIVING_BLOCKS;
|
| 215 | + }
|
162 | 216 | }
|
163 | 217 |
|
164 | 218 |
|
| 219 | +static void receive_sat_write(void* data, int size)
|
| 220 | +{
|
| 221 | + if (cur_cmd.data_direction) fail("diskmode: Attempting to receive data for SAT IN command!");
|
| 222 | + else if (size > cur_cmd.bytes_pending) fail("diskmode: Receive SAT overrun!");
|
| 223 | + else
|
| 224 | + {
|
| 225 | + cur_cmd.bytes_pending -= size;
|
| 226 | + length = size;
|
| 227 | + usb_receive(data, size);
|
| 228 | + state = RECEIVING_SAT_WRITE;
|
| 229 | + }
|
| 230 | +}
|
| 231 | +
|
| 232 | +
|
165 | 233 | static void send_block_data(void* data, int size)
|
166 | 234 | {
|
167 | | - length = size;
|
168 | | - usb_transmit(data, size);
|
169 | | - state = SENDING_BLOCKS;
|
| 235 | + if (!cur_cmd.data_direction) fail("diskmode: Attempting to send data for OUT command!");
|
| 236 | + else if (size > cur_cmd.bytes_pending) fail("diskmode: Send block overrun!");
|
| 237 | + else
|
| 238 | + {
|
| 239 | + cur_cmd.bytes_pending -= size;
|
| 240 | + length = size;
|
| 241 | + usb_transmit(data, size);
|
| 242 | + state = SENDING_BLOCKS;
|
| 243 | + }
|
170 | 244 | }
|
171 | 245 |
|
172 | 246 |
|
173 | 247 | static void send_command_result(void* data, int size)
|
174 | 248 | {
|
175 | | - length = size;
|
176 | | - usb_transmit(data, size);
|
177 | | - state = SENDING_RESULT;
|
| 249 | + if (!cur_cmd.data_direction) fail("diskmode: Attempting to send result for OUT command!");
|
| 250 | + else if (size > cur_cmd.bytes_pending) fail("diskmode: Send result overrun!");
|
| 251 | + else
|
| 252 | + {
|
| 253 | + cur_cmd.bytes_pending -= size;
|
| 254 | + length = size;
|
| 255 | + usb_transmit(data, size);
|
| 256 | + state = SENDING_RESULT;
|
| 257 | + }
|
178 | 258 | }
|
179 | 259 |
|
180 | 260 |
|
181 | | -static void send_command_failed_result()
|
| 261 | +static void send_command_failed_result(unsigned char key, unsigned char asc, unsigned char ascq)
|
182 | 262 | {
|
183 | | - usb_transmit(NULL, 0);
|
184 | | - state = SENDING_FAILED_RESULT;
|
| 263 | + send_csw(1);
|
| 264 | + cur_sense_data.sense_key = key;
|
| 265 | + cur_sense_data.asc = asc;
|
| 266 | + cur_sense_data.ascq = ascq;
|
185 | 267 | }
|
186 | 268 |
|
187 | 269 |
|
— | — | @@ -255,24 +337,22 @@ |
256 | 338 |
|
257 | 339 | if (cbw->signature != 0x43425355)
|
258 | 340 | {
|
259 | | - usb_stall();
|
| 341 | + usb_stall_in();
|
| 342 | + usb_stall_out();
|
260 | 343 | return;
|
261 | 344 | }
|
262 | 345 | cur_cmd.tag = cbw->tag;
|
263 | 346 | cur_cmd.lun = cbw->lun;
|
264 | 347 | cur_cmd.cur_cmd = cbw->command_block[0];
|
| 348 | + cur_cmd.bytes_pending = cbw->data_transfer_length;
|
| 349 | + if (cbw->flags & 0x80) cur_cmd.data_direction = 1;
|
| 350 | + else cur_cmd.data_direction = 0;
|
265 | 351 |
|
266 | 352 | switch (cbw->command_block[0])
|
267 | 353 | {
|
268 | 354 | case SCSI_TEST_UNIT_READY:
|
269 | 355 | if (!ums_ejected) send_csw(0);
|
270 | | - else
|
271 | | - {
|
272 | | - send_csw(1);
|
273 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
274 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
275 | | - cur_sense_data.ascq = 0;
|
276 | | - }
|
| 356 | + else send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
277 | 357 | break;
|
278 | 358 |
|
279 | 359 | case SCSI_REPORT_LUNS:
|
— | — | @@ -291,29 +371,40 @@ |
292 | 372 |
|
293 | 373 | case SCSI_REQUEST_SENSE:
|
294 | 374 | {
|
295 | | - tb.sense_data.ResponseCode = 0x70;
|
296 | | - tb.sense_data.Obsolete = 0;
|
297 | | - tb.sense_data.fei_sensekey = cur_sense_data.sense_key & 0x0f;
|
298 | | - tb.sense_data.Information = cur_sense_data.information;
|
299 | | - tb.sense_data.AdditionalSenseLength = 10;
|
300 | | - tb.sense_data.CommandSpecificInformation = 0;
|
301 | | - tb.sense_data.AdditionalSenseCode = cur_sense_data.asc;
|
302 | | - tb.sense_data.AdditionalSenseCodeQualifier = cur_sense_data.ascq;
|
303 | | - tb.sense_data.FieldReplaceableUnitCode = 0;
|
304 | | - tb.sense_data.SKSV = 0;
|
305 | | - tb.sense_data.SenseKeySpecific = 0;
|
306 | | - send_command_result(&tb.sense_data, MIN(sizeof(tb.sense_data), length));
|
| 375 | + if (have_sat_response_information)
|
| 376 | + {
|
| 377 | + memset(&tb.sense_data_descr.header, 0, sizeof(tb.sense_data_descr.header));
|
| 378 | + tb.sense_data_descr.header.ResponseCode = 0x72;
|
| 379 | + tb.sense_data_descr.header.fei_sensekey = cur_sense_data.sense_key & 0x0f;
|
| 380 | + tb.sense_data_descr.header.AdditionalSenseCode = cur_sense_data.asc;
|
| 381 | + tb.sense_data_descr.header.AdditionalSenseCodeQualifier = cur_sense_data.ascq;
|
| 382 | + tb.sense_data_descr.header.AdditionalSenseLength = sizeof(sat_response_information);
|
| 383 | + memcpy(&tb.sense_data_descr.info.sat_response, &sat_response_information, sizeof(sat_response_information));
|
| 384 | + send_command_result(&tb.sense_data_descr, MIN(sizeof(tb.sense_data_descr.header) + sizeof(sat_response_information), length));
|
| 385 | + }
|
| 386 | + else
|
| 387 | + {
|
| 388 | + tb.sense_data_fixed.ResponseCode = 0x70;
|
| 389 | + tb.sense_data_fixed.Obsolete = 0;
|
| 390 | + tb.sense_data_fixed.fei_sensekey = cur_sense_data.sense_key & 0x0f;
|
| 391 | + tb.sense_data_fixed.Information = cur_sense_data.information;
|
| 392 | + tb.sense_data_fixed.AdditionalSenseLength = 10;
|
| 393 | + tb.sense_data_fixed.CommandSpecificInformation = 0;
|
| 394 | + tb.sense_data_fixed.AdditionalSenseCode = cur_sense_data.asc;
|
| 395 | + tb.sense_data_fixed.AdditionalSenseCodeQualifier = cur_sense_data.ascq;
|
| 396 | + tb.sense_data_fixed.FieldReplaceableUnitCode = 0;
|
| 397 | + tb.sense_data_fixed.SKSV = 0;
|
| 398 | + tb.sense_data_fixed.SenseKeySpecific = 0;
|
| 399 | + send_command_result(&tb.sense_data_fixed, MIN(sizeof(tb.sense_data_fixed), length));
|
| 400 | + }
|
307 | 401 | break;
|
308 | 402 | }
|
309 | 403 |
|
310 | 404 | case SCSI_MODE_SENSE_10:
|
311 | 405 | {
|
312 | | - if (ums_ejected)
|
| 406 | + if (ums_ejected)
|
313 | 407 | {
|
314 | | - send_command_failed_result();
|
315 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
316 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
317 | | - cur_sense_data.ascq = 0;
|
| 408 | + send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
318 | 409 | break;
|
319 | 410 | }
|
320 | 411 | unsigned char page_code = cbw->command_block[2] & 0x3f;
|
— | — | @@ -342,10 +433,7 @@ |
343 | 434 | send_command_result(&tb.ms_data_10, MIN(sizeof(tb.ms_data_10), length));
|
344 | 435 | break;
|
345 | 436 | default:
|
346 | | - send_command_failed_result();
|
347 | | - cur_sense_data.sense_key = SENSE_ILLEGAL_REQUEST;
|
348 | | - cur_sense_data.asc = ASC_INVALID_FIELD_IN_CBD;
|
349 | | - cur_sense_data.ascq = 0;
|
| 437 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CBD, 0);
|
350 | 438 | break;
|
351 | 439 | }
|
352 | 440 | break;
|
— | — | @@ -355,10 +443,7 @@ |
356 | 444 | {
|
357 | 445 | if (ums_ejected)
|
358 | 446 | {
|
359 | | - send_command_failed_result();
|
360 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
361 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
362 | | - cur_sense_data.ascq = 0;
|
| 447 | + send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
363 | 448 | break;
|
364 | 449 | }
|
365 | 450 | unsigned char page_code = cbw->command_block[2] & 0x3f;
|
— | — | @@ -389,10 +474,7 @@ |
390 | 475 | send_command_result(&tb.ms_data_6, MIN(sizeof(tb.ms_data_6), length));
|
391 | 476 | break;
|
392 | 477 | default:
|
393 | | - send_command_failed_result();
|
394 | | - cur_sense_data.sense_key = SENSE_ILLEGAL_REQUEST;
|
395 | | - cur_sense_data.asc = ASC_INVALID_FIELD_IN_CBD;
|
396 | | - cur_sense_data.ascq = 0;
|
| 478 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CBD, 0);
|
397 | 479 | break;
|
398 | 480 | }
|
399 | 481 | break;
|
— | — | @@ -419,13 +501,7 @@ |
420 | 502 | tb.format_capacity_data.block_size |= swap32(SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA);
|
421 | 503 | send_command_result(&tb.format_capacity_data, MIN(sizeof(tb.format_capacity_data), length));
|
422 | 504 | }
|
423 | | - else
|
424 | | - {
|
425 | | - send_command_failed_result();
|
426 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
427 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
428 | | - cur_sense_data.ascq = 0;
|
429 | | - }
|
| 505 | + else send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
430 | 506 | break;
|
431 | 507 | }
|
432 | 508 |
|
— | — | @@ -437,13 +513,7 @@ |
438 | 514 | tb.capacity_data.block_size = swap32(storage_info.sector_size);
|
439 | 515 | send_command_result(&tb.capacity_data, MIN(sizeof(tb.capacity_data), length));
|
440 | 516 | }
|
441 | | - else
|
442 | | - {
|
443 | | - send_command_failed_result();
|
444 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
445 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
446 | | - cur_sense_data.ascq = 0;
|
447 | | - }
|
| 517 | + else send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
448 | 518 | break;
|
449 | 519 | }
|
450 | 520 |
|
— | — | @@ -450,10 +520,7 @@ |
451 | 521 | case SCSI_READ_10:
|
452 | 522 | if (ums_ejected)
|
453 | 523 | {
|
454 | | - send_command_failed_result();
|
455 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
456 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
457 | | - cur_sense_data.ascq = 0;
|
| 524 | + send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
458 | 525 | break;
|
459 | 526 | }
|
460 | 527 | cur_cmd.sector = (cbw->command_block[2] << 24 | cbw->command_block[3] << 16
|
— | — | @@ -462,12 +529,7 @@ |
463 | 530 | cur_cmd.orig_count = cur_cmd.count;
|
464 | 531 |
|
465 | 532 | if ((cur_cmd.sector + cur_cmd.count) > storage_info.num_sectors)
|
466 | | - {
|
467 | | - send_csw(1);
|
468 | | - cur_sense_data.sense_key = SENSE_ILLEGAL_REQUEST;
|
469 | | - cur_sense_data.asc = ASC_LBA_OUT_OF_RANGE;
|
470 | | - cur_sense_data.ascq = 0;
|
471 | | - }
|
| 533 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_LBA_OUT_OF_RANGE, 0);
|
472 | 534 | else if (!cur_cmd.count) send_csw(0);
|
473 | 535 | else send_and_read_next();
|
474 | 536 | break;
|
— | — | @@ -475,10 +537,7 @@ |
476 | 538 | case SCSI_WRITE_10:
|
477 | 539 | if (ums_ejected)
|
478 | 540 | {
|
479 | | - send_command_failed_result();
|
480 | | - cur_sense_data.sense_key = SENSE_NOT_READY;
|
481 | | - cur_sense_data.asc = ASC_MEDIUM_NOT_PRESENT;
|
482 | | - cur_sense_data.ascq = 0;
|
| 541 | + send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
483 | 542 | break;
|
484 | 543 | }
|
485 | 544 | cur_cmd.sector = (cbw->command_block[2] << 24 | cbw->command_block[3] << 16
|
— | — | @@ -487,12 +546,7 @@ |
488 | 547 | cur_cmd.orig_count = cur_cmd.count;
|
489 | 548 |
|
490 | 549 | if ((cur_cmd.sector + cur_cmd.count) > storage_info.num_sectors)
|
491 | | - {
|
492 | | - send_csw(1);
|
493 | | - cur_sense_data.sense_key = SENSE_ILLEGAL_REQUEST;
|
494 | | - cur_sense_data.asc = ASC_LBA_OUT_OF_RANGE;
|
495 | | - cur_sense_data.ascq = 0;
|
496 | | - }
|
| 550 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_LBA_OUT_OF_RANGE, 0);
|
497 | 551 | else if (!cur_cmd.count) send_csw(0);
|
498 | 552 | else
|
499 | 553 | receive_block_data(umsbuf[1][!writebuf_current],
|
— | — | @@ -501,14 +555,138 @@ |
502 | 556 |
|
503 | 557 | case SCSI_WRITE_BUFFER:
|
504 | 558 | break;
|
505 | | -
|
| 559 | +
|
| 560 | + case SCSI_ATA_PASSTHROUGH_12:
|
| 561 | + case SCSI_ATA_PASSTHROUGH_16:
|
| 562 | + {
|
| 563 | + if (!storage_info.driverinfo || get_platform_id() != 0x4c435049)
|
| 564 | + {
|
| 565 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_INVALID_COMMAND, 0);
|
| 566 | + break;
|
| 567 | + }
|
| 568 | + int cmd = cbw->command_block[0];
|
| 569 | + int multi = cbw->command_block[1] >> 5;
|
| 570 | + int protocol = (cbw->command_block[1] >> 1) & 0xf;
|
| 571 | + int extend = 0;
|
| 572 | + int offline = cbw->command_block[2] >> 6;
|
| 573 | + int check = (cbw->command_block[2] >> 5) & 1;
|
| 574 | + int type = (cbw->command_block[2] >> 4) & 1;
|
| 575 | + int dir = (cbw->command_block[2] >> 3) & 1;
|
| 576 | + int block = (cbw->command_block[2] >> 2) & 1;
|
| 577 | + int len = cbw->command_block[2] & 3;
|
| 578 | + int features = cbw->command_block[3];
|
| 579 | + int count = cbw->command_block[4];
|
| 580 | + int lbal = cbw->command_block[5];
|
| 581 | + int lbam = cbw->command_block[6];
|
| 582 | + int lbah = cbw->command_block[7];
|
| 583 | + int device = cbw->command_block[8] & ~0x10;
|
| 584 | + int command = cbw->command_block[9];
|
| 585 | + int control = cbw->command_block[11];
|
| 586 | + if (cmd == SCSI_ATA_PASSTHROUGH_16)
|
| 587 | + {
|
| 588 | + extend = cbw->command_block[1] & 1;
|
| 589 | + features = (extend ? (cbw->command_block[3] << 8) : 0) | cbw->command_block[4];
|
| 590 | + count = (extend ? (cbw->command_block[5] << 8) : 0) | cbw->command_block[6];
|
| 591 | + lbal = (extend ? (cbw->command_block[7] << 8) : 0) | cbw->command_block[8];
|
| 592 | + lbam = (extend ? (cbw->command_block[9] << 8) : 0) | cbw->command_block[10];
|
| 593 | + lbah = (extend ? (cbw->command_block[11] << 8) : 0) | cbw->command_block[12];
|
| 594 | + device = cbw->command_block[13] & ~0x10;
|
| 595 | + command = cbw->command_block[14];
|
| 596 | + control = cbw->command_block[15];
|
| 597 | + }
|
| 598 | + sat_command.lba48 = extend;
|
| 599 | + sat_check = check;
|
| 600 | + int delay = (2 << offline) - 2;
|
| 601 | + int bytes = 0;
|
| 602 | + switch (len)
|
| 603 | + {
|
| 604 | + case 1: bytes = features; break;
|
| 605 | + case 2: bytes = count; break;
|
| 606 | + case 3: bytes = length; break;
|
| 607 | + }
|
| 608 | + int blocks = 1;
|
| 609 | + if (block)
|
| 610 | + {
|
| 611 | + blocks = bytes;
|
| 612 | + bytes = type ? storage_info.sector_size : 512;
|
| 613 | + }
|
| 614 | + int invalid = blocks * bytes > UMS_BUFSIZE;
|
| 615 | + int dma = 0;
|
| 616 | + switch (protocol)
|
| 617 | + {
|
| 618 | + case SAT_HARD_RESET:
|
| 619 | + sat_pending = SAT_PENDING_HRST;
|
| 620 | + enqueue_async();
|
| 621 | + return;
|
| 622 | + case SAT_SOFT_RESET:
|
| 623 | + sat_pending = SAT_PENDING_SRST;
|
| 624 | + enqueue_async();
|
| 625 | + return;
|
| 626 | + case SAT_NON_DATA:
|
| 627 | + if (len) invalid = 1;
|
| 628 | + break;
|
| 629 | + case SAT_PIO_DATA_IN:
|
| 630 | + if (!len || !dir) invalid = 1;
|
| 631 | + break;
|
| 632 | + case SAT_PIO_DATA_OUT:
|
| 633 | + if (!len || dir) invalid = 1;
|
| 634 | + break;
|
| 635 | + case SAT_UDMA_DATA_IN:
|
| 636 | + if (!len || !dir) invalid = 1;
|
| 637 | + dma = 1;
|
| 638 | + break;
|
| 639 | + case SAT_UDMA_DATA_OUT:
|
| 640 | + if (!len || dir) invalid = 1;
|
| 641 | + dma = 1;
|
| 642 | + break;
|
| 643 | + case SAT_RETURN_RESPONSE:
|
| 644 | + sat_pending = SAT_PENDING_READ_CDB;
|
| 645 | + enqueue_async();
|
| 646 | + return;
|
| 647 | + //case SAT_NON_DATA_RESET:
|
| 648 | + //case SAT_DMA:
|
| 649 | + //case SAT_DMA_QUEUED:
|
| 650 | + //case SAT_FPDMA:
|
| 651 | + //case SAT_DIAGNOSTIC:
|
| 652 | + default:
|
| 653 | + invalid = 1;
|
| 654 | + break;
|
| 655 | + }
|
| 656 | + if (invalid)
|
| 657 | + {
|
| 658 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CBD, 0);
|
| 659 | + return;
|
| 660 | + }
|
| 661 | + readbuf_count[!readbuf_current] = 0;
|
| 662 | + void* buffer = umsbuf[0][!readbuf_current];
|
| 663 | + sat_command.transfer = !!len;
|
| 664 | + sat_command.send = !dir;
|
| 665 | + sat_command.dma = dma;
|
| 666 | + sat_command.delay = delay * 1000000;
|
| 667 | + sat_command.buffer = buffer;
|
| 668 | + sat_command.size = blocks;
|
| 669 | + sat_command.blksize = bytes;
|
| 670 | + sat_command.feature = features;
|
| 671 | + sat_command.count = count;
|
| 672 | + sat_command.lba_low = lbal;
|
| 673 | + sat_command.lba_mid = lbam;
|
| 674 | + sat_command.lba_high = lbah;
|
| 675 | + sat_command.device = device;
|
| 676 | + sat_command.command = command;
|
| 677 | + if (len && !dir) receive_sat_write(buffer, bytes * blocks);
|
| 678 | + else
|
| 679 | + {
|
| 680 | + sat_pending = SAT_PENDING_CMD;
|
| 681 | + enqueue_async();
|
| 682 | + }
|
| 683 | + break;
|
| 684 | + }
|
| 685 | +
|
506 | 686 | default:
|
507 | | - send_csw(1);
|
508 | | - cur_sense_data.sense_key = SENSE_ILLEGAL_REQUEST;
|
509 | | - cur_sense_data.asc = ASC_INVALID_COMMAND;
|
510 | | - cur_sense_data.ascq = 0;
|
| 687 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_INVALID_COMMAND, 0);
|
511 | 688 | break;
|
512 | 689 | }
|
| 690 | + have_sat_response_information = false;
|
513 | 691 | }
|
514 | 692 |
|
515 | 693 |
|
— | — | @@ -554,10 +732,8 @@ |
555 | 733 | {
|
556 | 734 | if (IS_ERR(write_rc))
|
557 | 735 | {
|
558 | | - send_csw(1);
|
559 | | - cur_sense_data.sense_key = SENSE_MEDIUM_ERROR;
|
560 | | - cur_sense_data.asc = ASC_WRITE_ERROR;
|
561 | | - cur_sense_data.ascq = 0;
|
| 736 | + send_command_failed_result(SENSE_MEDIUM_ERROR, ASC_WRITE_ERROR, 0);
|
| 737 | + return;
|
562 | 738 | }
|
563 | 739 | send_csw(0);
|
564 | 740 | write_rc = 0;
|
— | — | @@ -569,34 +745,46 @@ |
570 | 746 | void ums_xfer_complete(bool in, int bytesleft)
|
571 | 747 | {
|
572 | 748 | length -= bytesleft;
|
573 | | - switch (state)
|
574 | | - {
|
575 | | - case RECEIVING_BLOCKS:
|
576 | | - if (length != storage_info.sector_size * cur_cmd.count && length != UMS_BUFSIZE) break;
|
577 | | - update_readcache();
|
578 | | - if (!writebuf_busy) writebuf_push();
|
579 | | - else writebuf_overrun = true;
|
580 | | - enqueue_async();
|
581 | | - break;
|
582 | | - case WAITING_FOR_CSW_COMPLETION:
|
583 | | - state = WAITING_FOR_COMMAND;
|
584 | | - listen();
|
585 | | - break;
|
586 | | - case WAITING_FOR_COMMAND:
|
587 | | - invalidate_dcache(&cmdbuf, sizeof(cmdbuf)); // Who pulls this into a cache line!?
|
588 | | - handle_scsi(&cmdbuf.cbw);
|
589 | | - break;
|
590 | | - case SENDING_RESULT:
|
591 | | - send_csw(0);
|
592 | | - break;
|
593 | | - case SENDING_FAILED_RESULT:
|
594 | | - send_csw(1);
|
595 | | - break;
|
596 | | - case SENDING_BLOCKS:
|
597 | | - if (!cur_cmd.count) send_csw(0);
|
598 | | - else send_and_read_next();
|
599 | | - break;
|
600 | | - }
|
| 749 | + if (in)
|
| 750 | + switch (state)
|
| 751 | + {
|
| 752 | + case WAITING_FOR_CSW_COMPLETION:
|
| 753 | + state = WAITING_FOR_COMMAND;
|
| 754 | + listen();
|
| 755 | + break;
|
| 756 | + case SENDING_RESULT:
|
| 757 | + send_csw(0);
|
| 758 | + break;
|
| 759 | + case SENDING_BLOCKS:
|
| 760 | + if (!cur_cmd.count) send_csw(0);
|
| 761 | + else send_and_read_next();
|
| 762 | + break;
|
| 763 | + default:
|
| 764 | + fail("diskmode: Got IN completion in invalid state!");
|
| 765 | + }
|
| 766 | + else
|
| 767 | + switch (state)
|
| 768 | + {
|
| 769 | + case RECEIVING_BLOCKS:
|
| 770 | + if (length != storage_info.sector_size * cur_cmd.count && length != UMS_BUFSIZE) break;
|
| 771 | + update_readcache();
|
| 772 | + if (!writebuf_busy) writebuf_push();
|
| 773 | + else writebuf_overrun = true;
|
| 774 | + enqueue_async();
|
| 775 | + break;
|
| 776 | + case WAITING_FOR_COMMAND:
|
| 777 | + state = PROCESSING;
|
| 778 | + invalidate_dcache(&cmdbuf, sizeof(cmdbuf)); // Who pulls this into a cache line!?
|
| 779 | + handle_scsi(&cmdbuf.cbw);
|
| 780 | + break;
|
| 781 | + case RECEIVING_SAT_WRITE:
|
| 782 | + state = PROCESSING;
|
| 783 | + sat_pending = SAT_PENDING_CMD;
|
| 784 | + enqueue_async();
|
| 785 | + break;
|
| 786 | + default:
|
| 787 | + fail("diskmode: Got OUT completion in invalid state!");
|
| 788 | + }
|
601 | 789 | }
|
602 | 790 |
|
603 | 791 | void ums_handle_async()
|
— | — | @@ -625,13 +813,103 @@ |
626 | 814 | {
|
627 | 815 | read_blocked = false;
|
628 | 816 | if (readbuf_count[readbuf_current] < 0)
|
| 817 | + send_command_failed_result(SENSE_MEDIUM_ERROR, ASC_READ_ERROR, 0);
|
| 818 | + else send_and_read_next();
|
| 819 | + }
|
| 820 | +
|
| 821 | + struct ata_target_driverinfo* drv = storage_info.driverinfo;
|
| 822 | + switch (sat_pending)
|
| 823 | + {
|
| 824 | + case SAT_PENDING_SRST:
|
629 | 825 | {
|
630 | | - send_csw(1);
|
631 | | - cur_sense_data.sense_key = SENSE_MEDIUM_ERROR;
|
632 | | - cur_sense_data.asc = ASC_READ_ERROR;
|
633 | | - cur_sense_data.ascq = 0;
|
| 826 | + sat_pending = SAT_PENDING_NONE;
|
| 827 | + if (IS_ERR(drv->soft_reset()))
|
| 828 | + send_command_failed_result(SENSE_HARDWARE_ERROR, ASC_UNSUCCESSFUL_SOFT_RESET, 0);
|
| 829 | + else send_csw(0);
|
| 830 | + break;
|
634 | 831 | }
|
635 | | - else send_and_read_next();
|
| 832 | + case SAT_PENDING_HRST:
|
| 833 | + {
|
| 834 | + sat_pending = SAT_PENDING_NONE;
|
| 835 | + if (IS_ERR(drv->hard_reset()))
|
| 836 | + send_command_failed_result(SENSE_HARDWARE_ERROR, ASC_UNSUCCESSFUL_SOFT_RESET, 0);
|
| 837 | + else send_csw(0);
|
| 838 | + break;
|
| 839 | + }
|
| 840 | + case SAT_PENDING_CMD:
|
| 841 | + {
|
| 842 | +int cmd = sat_command.command;
|
| 843 | + int datasize = sat_command.size * sat_command.blksize;
|
| 844 | + void* buffer = sat_command.buffer;
|
| 845 | + int rc = drv->raw_cmd(&sat_command);
|
| 846 | + sat_response_information.descriptor_code = 9;
|
| 847 | + sat_response_information.additional_length = 12;
|
| 848 | + sat_response_information.extend = sat_command.lba48;
|
| 849 | + sat_response_information.error = sat_command.feature;
|
| 850 | + sat_response_information.sector_count_h = sat_command.count >> 8;
|
| 851 | + sat_response_information.sector_count_l = sat_command.count & 0xff;
|
| 852 | + sat_response_information.lba_low_h = sat_command.lba_low >> 8;
|
| 853 | + sat_response_information.lba_low_l = sat_command.lba_low & 0xff;
|
| 854 | + sat_response_information.lba_mid_h = sat_command.lba_mid >> 8;
|
| 855 | + sat_response_information.lba_mid_l = sat_command.lba_mid & 0xff;
|
| 856 | + sat_response_information.lba_high_h = sat_command.lba_high >> 8;
|
| 857 | + sat_response_information.lba_high_l = sat_command.lba_high & 0xff;
|
| 858 | + sat_response_information.device = sat_command.device;
|
| 859 | + sat_response_information.status = sat_command.command;
|
| 860 | + have_sat_response_information = true;
|
| 861 | + sat_pending = SAT_PENDING_NONE;
|
| 862 | + if (IS_ERR(rc) || (sat_command.command & 0x21))
|
| 863 | + {
|
| 864 | + if (sat_command.command & 0x20)
|
| 865 | + send_command_failed_result(SENSE_HARDWARE_ERROR, ASC_INTERNAL_TARGET_FAILURE, 0);
|
| 866 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x01))
|
| 867 | + send_command_failed_result(SENSE_MEDIUM_ERROR, ASC_ADDRESS_MARK_NOT_FOUND, 0);
|
| 868 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x02))
|
| 869 | + send_command_failed_result(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT, 0);
|
| 870 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x08))
|
| 871 | + send_command_failed_result(SENSE_UNIT_ATTENTION, ASC_OPERATOR_REQUEST, ASCQ_MEDIUM_REMOVAL_REQUEST);
|
| 872 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x10))
|
| 873 | + send_command_failed_result(SENSE_ILLEGAL_REQUEST, ASC_LBA_OUT_OF_RANGE, 0);
|
| 874 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x20))
|
| 875 | + send_command_failed_result(SENSE_UNIT_ATTENTION, ASC_MEDIUM_MAY_HAVE_CHANGED, 0);
|
| 876 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x40) && datasize && !sat_command.send)
|
| 877 | + send_command_failed_result(SENSE_MEDIUM_ERROR, ASC_READ_ERROR, 0);
|
| 878 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x40))
|
| 879 | + send_command_failed_result(SENSE_DATA_PROTECT, ASC_WRITE_PROTECTED, 0);
|
| 880 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x80))
|
| 881 | + send_command_failed_result(SENSE_ABORTED_COMMAND, ASC_SCSI_PARITY_ERROR, ASCQ_UICRC_ERROR_DETECTED);
|
| 882 | + else if ((sat_command.command & 0x01) && (sat_command.feature & 0x04))
|
| 883 | + send_command_failed_result(SENSE_ABORTED_COMMAND, 0, 0);
|
| 884 | + else send_command_failed_result(SENSE_HARDWARE_ERROR, ASC_INTERNAL_TARGET_FAILURE, 0);
|
| 885 | + }
|
| 886 | + else if (sat_check)
|
| 887 | + send_command_failed_result(SENSE_SOFT_ERROR, ASC_RECOVERED_ERROR, ASCQ_SAT_INFO_AVAILABLE);
|
| 888 | + else if (datasize) send_command_result(buffer, datasize);
|
| 889 | + else send_csw(0);
|
| 890 | + break;
|
| 891 | + }
|
| 892 | + case SAT_PENDING_READ_CDB:
|
| 893 | + {
|
| 894 | + int rc = drv->read_taskfile(&sat_command);
|
| 895 | + sat_response_information.descriptor_code = 9;
|
| 896 | + sat_response_information.additional_length = 12;
|
| 897 | + sat_response_information.extend = sat_command.lba48;
|
| 898 | + sat_response_information.error = sat_command.feature;
|
| 899 | + sat_response_information.sector_count_h = sat_command.count >> 8;
|
| 900 | + sat_response_information.sector_count_l = sat_command.count & 0xff;
|
| 901 | + sat_response_information.lba_low_h = sat_command.lba_low >> 8;
|
| 902 | + sat_response_information.lba_low_l = sat_command.lba_low & 0xff;
|
| 903 | + sat_response_information.lba_mid_h = sat_command.lba_mid >> 8;
|
| 904 | + sat_response_information.lba_mid_l = sat_command.lba_mid & 0xff;
|
| 905 | + sat_response_information.lba_high_h = sat_command.lba_high >> 8;
|
| 906 | + sat_response_information.lba_high_l = sat_command.lba_high & 0xff;
|
| 907 | + sat_response_information.device = sat_command.device;
|
| 908 | + sat_response_information.status = sat_command.command;
|
| 909 | + sat_pending = SAT_PENDING_NONE;
|
| 910 | + if (IS_ERR(rc)) send_command_failed_result(SENSE_HARDWARE_ERROR, ASC_INTERNAL_TARGET_FAILURE, 0);
|
| 911 | + else send_command_result(&sat_response_information, sizeof(sat_response_information));
|
| 912 | + break;
|
| 913 | + }
|
636 | 914 | }
|
637 | 915 | }
|
638 | 916 |
|
Index: apps/diskmode/scsi.h |
— | — | @@ -41,11 +41,17 @@ |
42 | 42 | #define SCSI_START_STOP_UNIT 0x1b
|
43 | 43 | #define SCSI_REPORT_LUNS 0xa0
|
44 | 44 | #define SCSI_WRITE_BUFFER 0x3b
|
| 45 | +#define SCSI_ATA_PASSTHROUGH_12 0xa1
|
| 46 | +#define SCSI_ATA_PASSTHROUGH_16 0x85
|
45 | 47 |
|
| 48 | +#define SENSE_SOFT_ERROR 0x01
|
46 | 49 | #define SENSE_NOT_READY 0x02
|
47 | 50 | #define SENSE_MEDIUM_ERROR 0x03
|
| 51 | +#define SENSE_HARDWARE_ERROR 0x04
|
48 | 52 | #define SENSE_ILLEGAL_REQUEST 0x05
|
49 | 53 | #define SENSE_UNIT_ATTENTION 0x06
|
| 54 | +#define SENSE_DATA_PROTECT 0x07
|
| 55 | +#define SENSE_ABORTED_COMMAND 0x0b
|
50 | 56 |
|
51 | 57 | #define ASC_MEDIUM_NOT_PRESENT 0x3a
|
52 | 58 | #define ASC_INVALID_FIELD_IN_CBD 0x24
|
— | — | @@ -54,9 +60,34 @@ |
55 | 61 | #define ASC_READ_ERROR 0x11
|
56 | 62 | #define ASC_NOT_READY 0x04
|
57 | 63 | #define ASC_INVALID_COMMAND 0x20
|
| 64 | +#define ASC_INTERNAL_TARGET_FAILURE 0x44
|
| 65 | +#define ASC_ADDRESS_MARK_NOT_FOUND 0x13
|
| 66 | +#define ASC_OPERATOR_REQUEST 0x5a
|
| 67 | +#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28
|
| 68 | +#define ASC_SCSI_PARITY_ERROR 0x47
|
| 69 | +#define ASC_WRITE_PROTECTED 0x27
|
| 70 | +#define ASC_RECOVERED_ERROR 0x00
|
| 71 | +#define ASC_UNSUCCESSFUL_SOFT_RESET 0x46
|
58 | 72 |
|
59 | 73 | #define ASCQ_BECOMING_READY 0x01
|
| 74 | +#define ASCQ_MEDIUM_REMOVAL_REQUEST 0x01
|
| 75 | +#define ASCQ_UICRC_ERROR_DETECTED 0x03
|
| 76 | +#define ASCQ_SAT_INFO_AVAILABLE 0x1d
|
60 | 77 |
|
| 78 | +#define SAT_HARD_RESET 0x0
|
| 79 | +#define SAT_SOFT_RESET 0x1
|
| 80 | +#define SAT_NON_DATA 0x3
|
| 81 | +#define SAT_PIO_DATA_IN 0x4
|
| 82 | +#define SAT_PIO_DATA_OUT 0x5
|
| 83 | +#define SAT_DMA 0x6
|
| 84 | +#define SAT_DMA_QUEUED 0x7
|
| 85 | +#define SAT_DIAGNOSTIC 0x8
|
| 86 | +#define SAT_NON_DATA_RESET 0x9
|
| 87 | +#define SAT_UDMA_DATA_IN 0xa
|
| 88 | +#define SAT_UDMA_DATA_OUT 0xb
|
| 89 | +#define SAT_FPDMA 0xc
|
| 90 | +#define SAT_RETURN_RESPONSE 0xf
|
| 91 | +
|
61 | 92 | #define DIRECT_ACCESS_DEVICE 0x00
|
62 | 93 | #define DEVICE_REMOVABLE 0x80
|
63 | 94 |
|
— | — | @@ -84,7 +115,7 @@ |
85 | 116 | unsigned char luns[1][8];
|
86 | 117 | };
|
87 | 118 |
|
88 | | -struct __attribute__((packed)) sense_data
|
| 119 | +struct __attribute__((packed)) sense_data_fixed
|
89 | 120 | {
|
90 | 121 | unsigned char ResponseCode;
|
91 | 122 | unsigned char Obsolete;
|
— | — | @@ -99,6 +130,16 @@ |
100 | 131 | unsigned short SenseKeySpecific;
|
101 | 132 | };
|
102 | 133 |
|
| 134 | +struct __attribute__((packed)) sense_data_descr
|
| 135 | +{
|
| 136 | + unsigned char ResponseCode;
|
| 137 | + unsigned char fei_sensekey;
|
| 138 | + unsigned char AdditionalSenseCode;
|
| 139 | + unsigned char AdditionalSenseCodeQualifier;
|
| 140 | + unsigned char Reserved[3];
|
| 141 | + unsigned char AdditionalSenseLength;
|
| 142 | +};
|
| 143 | +
|
103 | 144 | struct __attribute__((packed)) mode_sense_bdesc_longlba
|
104 | 145 | {
|
105 | 146 | unsigned char num_blocks[8];
|