2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Copyright 2002-03, Thomas Kurschel. All rights reserved.
5 * Distributed under the terms of the MIT License.
9 //! Everything doing the real input/output stuff.
12 #include "scsi_periph_int.h"
20 inquiry(scsi_periph_device_info
*device
, scsi_inquiry
*inquiry
)
22 const scsi_res_inquiry
*device_inquiry
= NULL
;
25 if (gDeviceManager
->get_attr_raw(device
->node
, SCSI_DEVICE_INQUIRY_ITEM
,
26 (const void **)&device_inquiry
, &inquiryLength
, true) != B_OK
)
29 memcpy(inquiry
, device_inquiry
, min_c(inquiryLength
, sizeof(scsi_inquiry
)));
35 vpd_page_inquiry(scsi_periph_device_info
*device
, uint8 page
, void* data
,
40 scsi_ccb
* ccb
= device
->scsi
->alloc_ccb(device
->scsi_device
);
44 scsi_cmd_inquiry
*cmd
= (scsi_cmd_inquiry
*)ccb
->cdb
;
45 memset(cmd
, 0, sizeof(scsi_cmd_inquiry
));
46 cmd
->opcode
= SCSI_OP_INQUIRY
;
47 cmd
->lun
= ccb
->target_lun
;
49 cmd
->page_code
= page
;
50 cmd
->allocation_length
= length
;
52 ccb
->flags
= SCSI_DIR_IN
;
53 ccb
->cdb_length
= sizeof(scsi_cmd_inquiry
);
56 ccb
->timeout
= device
->std_timeout
;
58 ccb
->data
= (uint8
*)data
;
60 ccb
->data_length
= length
;
62 status_t status
= periph_safe_exec(device
, ccb
);
64 device
->scsi
->free_ccb(ccb
);
71 vpd_page_get(scsi_periph_device_info
*device
, uint8 page
, void* data
,
76 status_t status
= vpd_page_inquiry(device
, 0, data
, length
);
78 return status
; // or B_BAD_VALUE
83 scsi_page_list
*list_data
= (scsi_page_list
*)data
;
84 int page_length
= min_c(list_data
->page_length
, length
-
85 offsetof(scsi_page_list
, pages
));
86 for (int i
= 0; i
< page_length
; i
++) {
87 if (list_data
->pages
[i
] == page
)
88 return vpd_page_inquiry(device
, page
, data
, length
);
91 // TODO buffer might be not big enough
99 prevent_allow(scsi_periph_device_info
*device
, bool prevent
)
101 scsi_cmd_prevent_allow cmd
;
105 memset(&cmd
, 0, sizeof(cmd
));
106 cmd
.opcode
= SCSI_OP_PREVENT_ALLOW
;
107 cmd
.prevent
= prevent
;
109 return periph_simple_exec(device
, (uint8
*)&cmd
, sizeof(cmd
), NULL
, 0,
114 /*! Keep this in sync with scsi_raw driver!!! */
116 raw_command(scsi_periph_device_info
*device
, raw_device_command
*cmd
)
122 request
= device
->scsi
->alloc_ccb(device
->scsi_device
);
128 if (cmd
->flags
& B_RAW_DEVICE_DATA_IN
)
129 request
->flags
|= SCSI_DIR_IN
;
130 else if (cmd
->data_length
)
131 request
->flags
|= SCSI_DIR_OUT
;
133 request
->flags
|= SCSI_DIR_NONE
;
135 request
->data
= (uint8
*)cmd
->data
;
136 request
->sg_list
= NULL
;
137 request
->data_length
= cmd
->data_length
;
139 request
->timeout
= cmd
->timeout
;
141 memcpy(request
->cdb
, cmd
->command
, SCSI_MAX_CDB_SIZE
);
142 request
->cdb_length
= cmd
->command_length
;
144 device
->scsi
->sync_io(request
);
146 // TBD: should we call standard error handler here, or may the
147 // actions done there (like starting the unit) confuse the application?
149 cmd
->cam_status
= request
->subsys_status
;
150 cmd
->scsi_status
= request
->device_status
;
152 if ((request
->subsys_status
& SCSI_AUTOSNS_VALID
) != 0 && cmd
->sense_data
) {
153 memcpy(cmd
->sense_data
, request
->sense
, min_c(cmd
->sense_data_length
,
154 (size_t)SCSI_MAX_SENSE_SIZE
- request
->sense_resid
));
157 if ((cmd
->flags
& B_RAW_DEVICE_REPORT_RESIDUAL
) != 0) {
158 // this is a bit strange, see Be's sample code where I pinched this from;
159 // normally, residual means "number of unused bytes left"
160 // but here, we have to return "number of used bytes", which is the opposite
161 cmd
->data_length
= cmd
->data_length
- request
->data_resid
;
162 cmd
->sense_data_length
= SCSI_MAX_SENSE_SIZE
- request
->sense_resid
;
165 device
->scsi
->free_ccb(request
);
171 /*! Universal read/write function */
173 read_write(scsi_periph_device_info
*device
, scsi_ccb
*request
,
174 io_operation
*operation
, uint64 offset
, size_t originalNumBlocks
,
175 physical_entry
* vecs
, size_t vecCount
, bool isWrite
,
176 size_t* _bytesTransferred
)
178 uint32 blockSize
= device
->block_size
;
179 size_t numBlocks
= originalNumBlocks
;
186 bool isReadWrite10
= false;
188 request
->flags
= isWrite
? SCSI_DIR_OUT
: SCSI_DIR_IN
;
190 // io_operations are generated by a DMAResource and thus contain DMA
191 // safe physical vectors
192 if (operation
!= NULL
)
193 request
->flags
|= SCSI_DMA_SAFE
;
195 // make sure we avoid 10 byte commands if they aren't supported
196 if (!device
->rw10_enabled
|| device
->preferred_ccb_size
== 6) {
197 // restricting transfer is OK - the block manager will
198 // take care of transferring the rest
199 if (numBlocks
> 0x100)
202 // no way to break the 21 bit address limit
203 if (offset
> 0x200000)
206 // don't allow transfer cross the 24 bit address limit
207 // (I'm not sure whether this is allowed, but this way we
208 // are sure to not ask for trouble)
209 if (offset
< 0x100000)
210 numBlocks
= min_c(numBlocks
, 0x100000 - pos
);
213 numBytes
= numBlocks
* blockSize
;
214 if (numBlocks
!= originalNumBlocks
)
215 panic("I/O operation would need to be cut.");
217 request
->data
= NULL
;
218 request
->sg_list
= vecs
;
219 request
->data_length
= numBytes
;
220 request
->sg_count
= vecCount
;
221 request
->io_operation
= operation
;
223 request
->timeout
= device
->std_timeout
;
224 // see whether daemon instructed us to post an ordered command;
225 // reset flag after read
226 SHOW_FLOW(3, "flag=%x, next_tag=%x, ordered: %s",
227 (int)request
->flags
, (int)device
->next_tag_action
,
228 (request
->flags
& SCSI_ORDERED_QTAG
) != 0 ? "yes" : "no");
230 // use shortest commands whenever possible
231 if (offset
+ numBlocks
< 0x200000LL
&& numBlocks
<= 0x100) {
232 scsi_cmd_rw_6
*cmd
= (scsi_cmd_rw_6
*)request
->cdb
;
234 isReadWrite10
= false;
236 memset(cmd
, 0, sizeof(*cmd
));
237 cmd
->opcode
= isWrite
? SCSI_OP_WRITE_6
: SCSI_OP_READ_6
;
238 cmd
->high_lba
= (pos
>> 16) & 0x1f;
239 cmd
->mid_lba
= (pos
>> 8) & 0xff;
240 cmd
->low_lba
= pos
& 0xff;
241 cmd
->length
= numBlocks
;
243 request
->cdb_length
= sizeof(*cmd
);
244 } else if (offset
+ numBlocks
< 0x100000000LL
&& numBlocks
<= 0x10000) {
245 scsi_cmd_rw_10
*cmd
= (scsi_cmd_rw_10
*)request
->cdb
;
247 isReadWrite10
= true;
249 memset(cmd
, 0, sizeof(*cmd
));
250 cmd
->opcode
= isWrite
? SCSI_OP_WRITE_10
: SCSI_OP_READ_10
;
251 cmd
->relative_address
= 0;
252 cmd
->force_unit_access
= 0;
253 cmd
->disable_page_out
= 0;
254 cmd
->lba
= B_HOST_TO_BENDIAN_INT32(pos
);
255 cmd
->length
= B_HOST_TO_BENDIAN_INT16(numBlocks
);
257 request
->cdb_length
= sizeof(*cmd
);
258 } else if (offset
+ numBlocks
< 0x100000000LL
&& numBlocks
<= 0x10000000) {
259 scsi_cmd_rw_12
*cmd
= (scsi_cmd_rw_12
*)request
->cdb
;
261 memset(cmd
, 0, sizeof(*cmd
));
262 cmd
->opcode
= isWrite
? SCSI_OP_WRITE_12
: SCSI_OP_READ_12
;
263 cmd
->relative_address
= 0;
264 cmd
->force_unit_access
= 0;
265 cmd
->disable_page_out
= 0;
266 cmd
->lba
= B_HOST_TO_BENDIAN_INT32(pos
);
267 cmd
->length
= B_HOST_TO_BENDIAN_INT32(numBlocks
);
269 request
->cdb_length
= sizeof(*cmd
);
271 scsi_cmd_rw_16
*cmd
= (scsi_cmd_rw_16
*)request
->cdb
;
273 memset(cmd
, 0, sizeof(*cmd
));
274 cmd
->opcode
= isWrite
? SCSI_OP_WRITE_16
: SCSI_OP_READ_16
;
275 cmd
->force_unit_access_non_volatile
= 0;
276 cmd
->force_unit_access
= 0;
277 cmd
->disable_page_out
= 0;
278 cmd
->lba
= B_HOST_TO_BENDIAN_INT64(offset
);
279 cmd
->length
= B_HOST_TO_BENDIAN_INT32(numBlocks
);
281 request
->cdb_length
= sizeof(*cmd
);
284 // TODO: last chance to detect errors that occured during concurrent accesses
285 //status_t status = handle->pending_error;
286 //if (status != B_OK)
289 device
->scsi
->async_io(request
);
291 acquire_sem(request
->completion_sem
);
293 // ask generic peripheral layer what to do now
294 res
= periph_check_error(device
, request
);
296 // TODO: bytes might have been transferred even in the error case!
297 switch (res
.action
) {
299 *_bytesTransferred
= numBytes
- request
->data_resid
;
303 res
= periph_send_start_stop(device
, request
, 1,
305 if (res
.action
== err_act_ok
)
306 res
.action
= err_act_retry
;
309 case err_act_invalid_req
:
310 // if this was a 10 byte command, the device probably doesn't
311 // support them, so disable them and retry
313 atomic_and(&device
->rw10_enabled
, 0);
314 res
.action
= err_act_retry
;
316 res
.action
= err_act_fail
;
319 } while ((res
.action
== err_act_retry
&& retries
++ < 3)
320 || (res
.action
== err_act_many_retries
&& retries
++ < 30));
322 // peripheral layer only created "read" error, so we have to
323 // map them to "write" errors if this was a write request
324 if (res
.error_code
== B_DEV_READ_ERROR
&& isWrite
)
325 return B_DEV_WRITE_ERROR
;
327 return res
.error_code
;
331 // #pragma mark - public functions
335 periph_ioctl(scsi_periph_handle_info
*handle
, int op
, void *buffer
,
339 case B_GET_MEDIA_STATUS
:
341 status_t status
= B_OK
;
343 if (handle
->device
->removable
)
344 status
= periph_get_media_status(handle
);
346 SHOW_FLOW(2, "%s", strerror(status
));
348 *(status_t
*)buffer
= status
;
352 case B_GET_DEVICE_NAME
:
354 // TODO: this should be written as an attribute to the node
355 // Try driver further up first
356 if (handle
->device
->scsi
->ioctl
!= NULL
) {
357 status_t status
= handle
->device
->scsi
->ioctl(
358 handle
->device
->scsi_device
, op
, buffer
, length
);
363 // If that fails, get SCSI vendor/product
365 if (gDeviceManager
->get_attr_string(handle
->device
->node
,
366 SCSI_DEVICE_VENDOR_ITEM
, &vendor
, true) == B_OK
) {
367 char name
[B_FILE_NAME_LENGTH
];
368 strlcpy(name
, vendor
, sizeof(name
));
371 if (gDeviceManager
->get_attr_string(handle
->device
->node
,
372 SCSI_DEVICE_PRODUCT_ITEM
, &product
, true) == B_OK
) {
373 strlcat(name
, " ", sizeof(name
));
374 strlcat(name
, product
, sizeof(name
));
377 return user_strlcpy((char*)buffer
, name
, length
) >= 0
378 ? B_OK
: B_BAD_ADDRESS
;
384 return inquiry(handle
->device
, (scsi_inquiry
*)buffer
);
386 case B_SCSI_PREVENT_ALLOW
:
387 return prevent_allow(handle
->device
, *(bool *)buffer
);
389 case B_RAW_DEVICE_COMMAND
:
390 return raw_command(handle
->device
, (raw_device_command
*)buffer
);
393 if (handle
->device
->scsi
->ioctl
!= NULL
) {
394 return handle
->device
->scsi
->ioctl(handle
->device
->scsi_device
,
398 SHOW_ERROR(4, "Unknown ioctl: %x", op
);
399 return B_DEV_INVALID_IOCTL
;
404 /*! Kernel daemon - once in a minute, it sets a flag so that the next command
405 is executed ordered; this way, we avoid starvation of SCSI commands inside
406 the SCSI queuing system - the ordered command waits for all previous
407 commands and thus no command can starve longer then a minute
410 periph_sync_queue_daemon(void *arg
, int iteration
)
412 scsi_periph_device_info
*device
= (scsi_periph_device_info
*)arg
;
414 SHOW_FLOW0(3, "Setting ordered flag for next R/W access");
415 atomic_or(&device
->next_tag_action
, SCSI_ORDERED_QTAG
);
420 periph_read_write(scsi_periph_device_info
*device
, scsi_ccb
*request
,
421 uint64 offset
, size_t numBlocks
, physical_entry
* vecs
, size_t vecCount
,
422 bool isWrite
, size_t* _bytesTransferred
)
424 return read_write(device
, request
, NULL
, offset
, numBlocks
, vecs
, vecCount
,
425 isWrite
, _bytesTransferred
);
430 periph_io(scsi_periph_device_info
*device
, io_operation
*operation
,
431 size_t* _bytesTransferred
)
433 const uint32 blockSize
= device
->block_size
;
437 // don't test rw10_enabled restrictions - this flag may get changed
438 scsi_ccb
*request
= device
->scsi
->alloc_ccb(device
->scsi_device
);
442 status_t status
= read_write(device
, request
, operation
,
443 operation
->Offset() / blockSize
, operation
->Length() / blockSize
,
444 (physical_entry
*)operation
->Vecs(), operation
->VecCount(),
445 operation
->IsWrite(), _bytesTransferred
);
447 device
->scsi
->free_ccb(request
);