root/headers/private/drivers/scsi_periph.h
/*
 * 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 <bus/SCSI.h>
#include <scsi_cmds.h>
#include <Drivers.h>

// 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 */