/* * Copyright 2004-2013, Haiku, Inc. All RightsReserved. * Copyright 2002/03, Thomas Kurschel. All rights reserved. * * Distributed under the terms of the MIT License. */ #ifndef _SCSI_PERIPH_H #define _SCSI_PERIPH_H /*! Use this module to minimize work required to write a SCSI peripheral driver. It takes care of: - error handling - medium changes (including restarting the medium) - detection of medium capacity */ #include #include #include // cookie issued by module per device typedef struct scsi_periph_device_info *scsi_periph_device; // cookie issued by module per file handle typedef struct scsi_periph_handle_info *scsi_periph_handle; // "standardized" error code to simplify handling of scsi errors typedef enum { err_act_ok, // executed successfully err_act_retry, // failed, retry 3 times err_act_fail, // failed, don't retry err_act_many_retries, // failed, retry multiple times (currently 10 times) err_act_start, // devices requires a "start" command err_act_invalid_req // request is invalid } err_act; // packed scsi command result typedef struct err_res { status_t error_code : 32; // Be error code uint32 action : 8; // err_act code } err_res; #define MK_ERROR( aaction, code ) ({ \ err_res _res = {.error_code = (code), .action = (aaction) }; \ _res; \ }) // cookie issued by driver to identify itself //typedef struct periph_info *periph_cookie; // cookie issued by driver per device typedef struct periph_device_info *periph_device_cookie; // cookie issued by driver per file handle typedef struct periph_handle_info *periph_handle_cookie; typedef struct IOOperation io_operation; // callbacks to be provided by peripheral driver typedef struct scsi_periph_callbacks { // *** block devices *** // informs of new size of medium // (set to NULL if not a block device) void (*set_capacity)(periph_device_cookie cookie, uint64 capacity, uint32 blockSize, uint32 physicalBlockSize); // *** removable devices // called when media got changed (can be NULL if medium is not changable) // (you don't need to call periph->media_changed, but it's doesn't if you do) // ccb - request at your disposal void (*media_changed)(periph_device_cookie cookie, scsi_ccb *request); } scsi_periph_callbacks; typedef struct scsi_block_range { // values are in blocks uint64 lba; uint64 size; } scsi_block_range; // functions provided by this module typedef struct scsi_periph_interface { module_info info; // *** init/cleanup *** // preferred_ccb_size - preferred command size; if zero, the shortest is used status_t (*register_device)(periph_device_cookie cookie, scsi_periph_callbacks *callbacks, scsi_device scsiDevice, scsi_device_interface *scsi, device_node *node, bool removable, int preferredCcbSize, scsi_periph_device *driver); status_t (*unregister_device)(scsi_periph_device driver); // *** basic command execution *** // exec command, retrying on problems status_t (*safe_exec)(scsi_periph_device periphCookie, scsi_ccb *request); // exec simple command status_t (*simple_exec)(scsi_periph_device device, void *cdb, uint8 cdbLength, void *data, size_t dataLength, int ccbFlags); // *** file handling *** // to be called when a new file is opened status_t (*handle_open)(scsi_periph_device device, periph_handle_cookie periph_handle, scsi_periph_handle *_handle); // to be called when a file is closed status_t (*handle_close)(scsi_periph_handle handle); // to be called when a file is freed status_t (*handle_free)(scsi_periph_handle handle); // *** default implementation for block devices *** status_t (*read_write)(scsi_periph_device_info *device, scsi_ccb *request, uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount, bool isWrite, size_t* _bytesTransferred); status_t (*io)(scsi_periph_device device, io_operation *operation, size_t *_bytesTransferred); // block ioctls status_t (*ioctl)(scsi_periph_handle handle, int op, void *buffer, size_t length); // check medium capacity (calls set_capacity callback on success) // request - ccb for this device; is used to talk to device status_t (*check_capacity)(scsi_periph_device device, scsi_ccb *request); // synchronizes (flush) the device cache err_res (*synchronize_cache)(scsi_periph_device device, scsi_ccb *request); status_t (*trim_device)(scsi_periph_device_info *device, scsi_ccb *request, scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks); // *** removable media *** // to be called when a medium change is detected to block subsequent commands void (*media_changed)(scsi_periph_device device); // convert result of *request to err_res err_res (*check_error)(scsi_periph_device device, scsi_ccb *request); // send start or stop command to device // withLoadEject = true - include loading/ejecting, // false - only do allow/deny err_res (*send_start_stop)(scsi_periph_device device, scsi_ccb *request, bool start, bool withLoadEject); // checks media status and waits for device to become ready // returns: B_OK, B_DEV_MEDIA_CHANGE_REQUESTED, B_NO_MEMORY or // pending error reported by handle_get_error status_t (*get_media_status)(scsi_periph_handle handle); // compose device name consisting of prefix and path/target/LUN // (result must be freed by caller) char *(*compose_device_name)(device_node *device_node, const char *prefix); } scsi_periph_interface; #define SCSI_PERIPH_MODULE_NAME "generic/scsi_periph/v1" #endif /* _SCSI_PERIPH_H */