diff options
Diffstat (limited to 'sys/cam')
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 65 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 51 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_sa.c | 2464 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_sa.h | 714 |
4 files changed, 3119 insertions, 175 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index d4a61f0..dc1d96d 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -7443,6 +7443,71 @@ scsi_persistent_reserve_out(struct ccb_scsiio *csio, uint32_t retries, scsi_cmd->opcode = PERSISTENT_RES_OUT; scsi_cmd->action = service_action; scsi_cmd->scope_type = scope | res_type; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, + tag_action, + /*data_ptr*/data_ptr, + /*dxfer_len*/dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_security_protocol_in(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint32_t security_protocol, + uint32_t security_protocol_specific, int byte4, + uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, + int timeout) +{ + struct scsi_security_protocol_in *scsi_cmd; + + scsi_cmd = (struct scsi_security_protocol_in *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = SECURITY_PROTOCOL_IN; + + scsi_cmd->security_protocol = security_protocol; + scsi_ulto2b(security_protocol_specific, + scsi_cmd->security_protocol_specific); + scsi_cmd->byte4 = byte4; + scsi_ulto4b(dxfer_len, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_security_protocol_out(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint32_t security_protocol, + uint32_t security_protocol_specific, int byte4, + uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, + int timeout) +{ + struct scsi_security_protocol_out *scsi_cmd; + + scsi_cmd = (struct scsi_security_protocol_out *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = SECURITY_PROTOCOL_OUT; + + scsi_cmd->security_protocol = security_protocol; + scsi_ulto2b(security_protocol_specific, + scsi_cmd->security_protocol_specific); + scsi_cmd->byte4 = byte4; scsi_ulto4b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 46d0bfc..f70b094 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -1758,6 +1758,7 @@ struct ata_pass_16 { #define SERVICE_ACTION_IN 0x9E #define REPORT_LUNS 0xA0 #define ATA_PASS_12 0xA1 +#define SECURITY_PROTOCOL_IN 0xA2 #define MAINTENANCE_IN 0xA3 #define MAINTENANCE_OUT 0xA4 #define MOVE_MEDIUM 0xA5 @@ -1765,6 +1766,7 @@ struct ata_pass_16 { #define WRITE_12 0xAA #define WRITE_VERIFY_12 0xAE #define VERIFY_12 0xAF +#define SECURITY_PROTOCOL_OUT 0xB5 #define READ_ELEMENT_STATUS 0xB8 #define READ_CD 0xBE @@ -2702,6 +2704,41 @@ struct scsi_target_group_data_extended { struct scsi_target_port_group_descriptor groups[]; }; +struct scsi_security_protocol_in +{ + uint8_t opcode; + uint8_t security_protocol; +#define SPI_PROT_INFORMATION 0x00 +#define SPI_PROT_CBCS 0x07 +#define SPI_PROT_TAPE_DATA_ENC 0x20 +#define SPI_PROT_DATA_ENC_CONFIG 0x21 +#define SPI_PROT_SA_CREATE_CAP 0x40 +#define SPI_PROT_IKEV2_SCSI 0x41 +#define SPI_PROT_JEDEC_UFS 0xEC +#define SPI_PROT_SDCARD_TFSSS 0xED +#define SPI_PROT_AUTH_HOST_TRANSIENT 0xEE +#define SPI_PROT_ATA_DEVICE_PASSWORD 0xEF + uint8_t security_protocol_specific[2]; + uint8_t byte4; +#define SPI_INC_512 0x80 + uint8_t reserved1; + uint8_t length[4]; + uint8_t reserved2; + uint8_t control; +}; + +struct scsi_security_protocol_out +{ + uint8_t opcode; + uint8_t security_protocol; + uint8_t security_protocol_specific[2]; + uint8_t byte4; +#define SPO_INC_512 0x80 + uint8_t reserved1; + uint8_t length[4]; + uint8_t reserved2; + uint8_t control; +}; typedef enum { SSD_TYPE_NONE, @@ -3623,6 +3660,20 @@ void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t timeout); +void scsi_security_protocol_in(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint32_t security_protocol, + uint32_t security_protocol_specific, int byte4, + uint8_t *data_ptr, uint32_t dxfer_len, + int sense_len, int timeout); + +void scsi_security_protocol_out(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *,union ccb *), + uint8_t tag_action, uint32_t security_protocol, + uint32_t security_protocol_specific, int byte4, + uint8_t *data_ptr, uint32_t dxfer_len, + int sense_len, int timeout); + void scsi_persistent_reserve_in(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *,union ccb *), uint8_t tag_action, int service_action, diff --git a/sys/cam/scsi/scsi_sa.c b/sys/cam/scsi/scsi_sa.c index 591e843..0480854 100644 --- a/sys/cam/scsi/scsi_sa.c +++ b/sys/cam/scsi/scsi_sa.c @@ -2,6 +2,7 @@ * Implementation of SCSI Sequential Access Peripheral driver for CAM. * * Copyright (c) 1999, 2000 Matthew Jacob + * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/mtio.h> #ifdef _KERNEL #include <sys/conf.h> +#include <sys/sbuf.h> #include <sys/sysctl.h> #include <sys/taskqueue.h> #endif @@ -69,7 +71,7 @@ __FBSDID("$FreeBSD$"); #include <opt_sa.h> #ifndef SA_IO_TIMEOUT -#define SA_IO_TIMEOUT 4 +#define SA_IO_TIMEOUT 32 #endif #ifndef SA_SPACE_TIMEOUT #define SA_SPACE_TIMEOUT 1 * 60 @@ -80,6 +82,9 @@ __FBSDID("$FreeBSD$"); #ifndef SA_ERASE_TIMEOUT #define SA_ERASE_TIMEOUT 4 * 60 #endif +#ifndef SA_REP_DENSITY_TIMEOUT +#define SA_REP_DENSITY_TIMEOUT 90 +#endif #define SCSIOP_TIMEOUT (60 * 1000) /* not an option */ @@ -87,6 +92,7 @@ __FBSDID("$FreeBSD$"); #define REWIND_TIMEOUT (SA_REWIND_TIMEOUT * 60 * 1000) #define ERASE_TIMEOUT (SA_ERASE_TIMEOUT * 60 * 1000) #define SPACE_TIMEOUT (SA_SPACE_TIMEOUT * 60 * 1000) +#define REP_DENSITY_TIMEOUT (SA_REP_DENSITY_TIMEOUT * 60 * 1000) /* * Additional options that can be set for config: SA_1FM_AT_EOT @@ -133,7 +139,12 @@ typedef enum { SA_FLAG_COMP_ENABLED = 0x0400, SA_FLAG_COMP_SUPP = 0x0800, SA_FLAG_COMP_UNSUPP = 0x1000, - SA_FLAG_TAPE_FROZEN = 0x2000 + SA_FLAG_TAPE_FROZEN = 0x2000, + SA_FLAG_PROTECT_SUPP = 0x4000, + + SA_FLAG_COMPRESSION = (SA_FLAG_COMP_SUPP|SA_FLAG_COMP_ENABLED| + SA_FLAG_COMP_UNSUPP), + SA_FLAG_SCTX_INIT = 0x8000 } sa_flags; typedef enum { @@ -143,27 +154,30 @@ typedef enum { } sa_mode; typedef enum { - SA_PARAM_NONE = 0x00, - SA_PARAM_BLOCKSIZE = 0x01, - SA_PARAM_DENSITY = 0x02, - SA_PARAM_COMPRESSION = 0x04, - SA_PARAM_BUFF_MODE = 0x08, - SA_PARAM_NUMBLOCKS = 0x10, - SA_PARAM_WP = 0x20, - SA_PARAM_SPEED = 0x40, - SA_PARAM_ALL = 0x7f + SA_PARAM_NONE = 0x000, + SA_PARAM_BLOCKSIZE = 0x001, + SA_PARAM_DENSITY = 0x002, + SA_PARAM_COMPRESSION = 0x004, + SA_PARAM_BUFF_MODE = 0x008, + SA_PARAM_NUMBLOCKS = 0x010, + SA_PARAM_WP = 0x020, + SA_PARAM_SPEED = 0x040, + SA_PARAM_DENSITY_EXT = 0x080, + SA_PARAM_LBP = 0x100, + SA_PARAM_ALL = 0x1ff } sa_params; typedef enum { - SA_QUIRK_NONE = 0x00, - SA_QUIRK_NOCOMP = 0x01, /* Can't deal with compression at all */ - SA_QUIRK_FIXED = 0x02, /* Force fixed mode */ - SA_QUIRK_VARIABLE = 0x04, /* Force variable mode */ - SA_QUIRK_2FM = 0x08, /* Needs Two File Marks at EOD */ - SA_QUIRK_1FM = 0x10, /* No more than 1 File Mark at EOD */ - SA_QUIRK_NODREAD = 0x20, /* Don't try and dummy read density */ - SA_QUIRK_NO_MODESEL = 0x40, /* Don't do mode select at all */ - SA_QUIRK_NO_CPAGE = 0x80 /* Don't use DEVICE COMPRESSION page */ + SA_QUIRK_NONE = 0x000, + SA_QUIRK_NOCOMP = 0x001, /* Can't deal with compression at all*/ + SA_QUIRK_FIXED = 0x002, /* Force fixed mode */ + SA_QUIRK_VARIABLE = 0x004, /* Force variable mode */ + SA_QUIRK_2FM = 0x008, /* Needs Two File Marks at EOD */ + SA_QUIRK_1FM = 0x010, /* No more than 1 File Mark at EOD */ + SA_QUIRK_NODREAD = 0x020, /* Don't try and dummy read density */ + SA_QUIRK_NO_MODESEL = 0x040, /* Don't do mode select at all */ + SA_QUIRK_NO_CPAGE = 0x080, /* Don't use DEVICE COMPRESSION page */ + SA_QUIRK_NO_LONG_POS = 0x100 /* No long position information */ } sa_quirks; #define SA_QUIRK_BIT_STRING \ @@ -175,10 +189,10 @@ typedef enum { "\0051FM" \ "\006NODREAD" \ "\007NO_MODESEL" \ - "\010NO_CPAGE" + "\010NO_CPAGE" \ + "\011NO_LONG_POS" #define SAMODE(z) (dev2unit(z) & 0x3) -#define SADENSITY(z) ((dev2unit(z) >> 2) & 0x3) #define SA_IS_CTRL(z) (dev2unit(z) & (1 << 4)) #define SA_NOT_CTLDEV 0 @@ -187,29 +201,134 @@ typedef enum { #define SA_ATYPE_R 0 #define SA_ATYPE_NR 1 #define SA_ATYPE_ER 2 +#define SA_NUM_ATYPES 3 -#define SAMINOR(ctl, mode, access) \ - ((ctl << 4) | (mode << 2) | (access & 0x3)) +#define SAMINOR(ctl, access) \ + ((ctl << 4) | (access & 0x3)) -#define SA_NUM_MODES 4 struct sa_devs { struct cdev *ctl_dev; - struct sa_mode_devs { - struct cdev *r_dev; - struct cdev *nr_dev; - struct cdev *er_dev; - } mode_devs[SA_NUM_MODES]; + struct cdev *r_dev; + struct cdev *nr_dev; + struct cdev *er_dev; +}; + +#define SASBADDBASE(sb, indent, data, xfmt, name, type, xsize, desc) \ + sbuf_printf(sb, "%*s<%s type=\"%s\" size=\"%zd\" " \ + "fmt=\"%s\" desc=\"%s\">" #xfmt "</%s>\n", indent, "", \ + #name, #type, xsize, #xfmt, desc ? desc : "", data, #name); + +#define SASBADDINT(sb, indent, data, fmt, name) \ + SASBADDBASE(sb, indent, data, fmt, name, int, sizeof(data), \ + NULL) + +#define SASBADDINTDESC(sb, indent, data, fmt, name, desc) \ + SASBADDBASE(sb, indent, data, fmt, name, int, sizeof(data), \ + desc) + +#define SASBADDUINT(sb, indent, data, fmt, name) \ + SASBADDBASE(sb, indent, data, fmt, name, uint, sizeof(data), \ + NULL) + +#define SASBADDUINTDESC(sb, indent, data, fmt, name, desc) \ + SASBADDBASE(sb, indent, data, fmt, name, uint, sizeof(data), \ + desc) + +#define SASBADDFIXEDSTR(sb, indent, data, fmt, name) \ + SASBADDBASE(sb, indent, data, fmt, name, str, sizeof(data), \ + NULL) + +#define SASBADDFIXEDSTRDESC(sb, indent, data, fmt, name, desc) \ + SASBADDBASE(sb, indent, data, fmt, name, str, sizeof(data), \ + desc) + +#define SASBADDVARSTR(sb, indent, data, fmt, name, maxlen) \ + SASBADDBASE(sb, indent, data, fmt, name, str, maxlen, NULL) + +#define SASBADDVARSTRDESC(sb, indent, data, fmt, name, maxlen, desc) \ + SASBADDBASE(sb, indent, data, fmt, name, str, maxlen, desc) + +#define SASBADDNODE(sb, indent, name) { \ + sbuf_printf(sb, "%*s<%s type=\"%s\">\n", indent, "", #name, \ + "node"); \ + indent += 2; \ +} + +#define SASBADDNODENUM(sb, indent, name, num) { \ + sbuf_printf(sb, "%*s<%s type=\"%s\" num=\"%d\">\n", indent, "", \ + #name, "node", num); \ + indent += 2; \ +} + +#define SASBENDNODE(sb, indent, name) { \ + indent -= 2; \ + sbuf_printf(sb, "%*s</%s>\n", indent, "", #name); \ +} + +#define SA_DENSITY_TYPES 4 + +struct sa_prot_state { + int initialized; + uint32_t prot_method; + uint32_t pi_length; + uint32_t lbp_w; + uint32_t lbp_r; + uint32_t rbdp; +}; + +struct sa_prot_info { + struct sa_prot_state cur_prot_state; + struct sa_prot_state pending_prot_state; }; +/* + * A table mapping protection parameters to their types and values. + */ +struct sa_prot_map { + char *name; + mt_param_set_type param_type; + off_t offset; + uint32_t min_val; + uint32_t max_val; + uint32_t *value; +} sa_prot_table[] = { + { "prot_method", MT_PARAM_SET_UNSIGNED, + __offsetof(struct sa_prot_state, prot_method), + /*min_val*/ 0, /*max_val*/ 255, NULL }, + { "pi_length", MT_PARAM_SET_UNSIGNED, + __offsetof(struct sa_prot_state, pi_length), + /*min_val*/ 0, /*max_val*/ SA_CTRL_DP_PI_LENGTH_MASK, NULL }, + { "lbp_w", MT_PARAM_SET_UNSIGNED, + __offsetof(struct sa_prot_state, lbp_w), + /*min_val*/ 0, /*max_val*/ 1, NULL }, + { "lbp_r", MT_PARAM_SET_UNSIGNED, + __offsetof(struct sa_prot_state, lbp_r), + /*min_val*/ 0, /*max_val*/ 1, NULL }, + { "rbdp", MT_PARAM_SET_UNSIGNED, + __offsetof(struct sa_prot_state, rbdp), + /*min_val*/ 0, /*max_val*/ 1, NULL } +}; + +#define SA_NUM_PROT_ENTS sizeof(sa_prot_table)/sizeof(sa_prot_table[0]) + +#define SA_PROT_ENABLED(softc) ((softc->flags & SA_FLAG_PROTECT_SUPP) \ + && (softc->prot_info.cur_prot_state.initialized != 0) \ + && (softc->prot_info.cur_prot_state.prot_method != 0)) + +#define SA_PROT_LEN(softc) softc->prot_info.cur_prot_state.pi_length + struct sa_softc { sa_state state; sa_flags flags; sa_quirks quirks; u_int si_flags; + struct cam_periph *periph; struct bio_queue_head bio_queue; int queue_count; struct devstat *device_stats; struct sa_devs devs; + int open_count; + int num_devs_to_destroy; int blk_gran; int blk_mask; int blk_shift; @@ -231,12 +350,37 @@ struct sa_softc { int filemarks; union ccb saved_ccb; int last_resid_was_io; + uint8_t density_type_bits[SA_DENSITY_TYPES]; + int density_info_valid[SA_DENSITY_TYPES]; + uint8_t density_info[SA_DENSITY_TYPES][SRDS_MAX_LENGTH]; + + struct sa_prot_info prot_info; + + int sili; + int eot_warn; /* - * Relative to BOT Location. + * Current position information. -1 means that the given value is + * unknown. fileno and blkno are always calculated. blkno is + * relative to the previous file mark. rep_fileno and rep_blkno + * are as reported by the drive, if it supports the long form + * report for the READ POSITION command. rep_blkno is relative to + * the beginning of the partition. + * + * bop means that the drive is at the beginning of the partition. + * eop means that the drive is between early warning and end of + * partition, inside the current partition. + * bpew means that the position is in a PEWZ (Programmable Early + * Warning Zone) */ - daddr_t fileno; - daddr_t blkno; + daddr_t partition; /* Absolute from BOT */ + daddr_t fileno; /* Relative to beginning of partition */ + daddr_t blkno; /* Relative to last file mark */ + daddr_t rep_blkno; /* Relative to beginning of partition */ + daddr_t rep_fileno; /* Relative to beginning of partition */ + int bop; /* Beginning of Partition */ + int eop; /* End of Partition */ + int bpew; /* Beyond Programmable Early Warning */ /* * Latched Error Info @@ -403,16 +547,42 @@ static int sagetparams(struct cam_periph *periph, u_int8_t *write_protect, u_int8_t *speed, int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm, - sa_comp_t *comp_page); + sa_comp_t *comp_page, + struct scsi_control_data_prot_subpage + *prot_page, int dp_size, + int prot_changeable); +static int sasetprot(struct cam_periph *periph, + struct sa_prot_state *new_prot); static int sasetparams(struct cam_periph *periph, sa_params params_to_set, u_int32_t blocksize, u_int8_t density, u_int32_t comp_algorithm, u_int32_t sense_flags); +static int sasetsili(struct cam_periph *periph, + struct mtparamset *ps, int num_params); +static int saseteotwarn(struct cam_periph *periph, + struct mtparamset *ps, int num_params); +static void safillprot(struct sa_softc *softc, int *indent, + struct sbuf *sb); +static void sapopulateprots(struct sa_prot_state *cur_state, + struct sa_prot_map *new_table, + int table_ents); +static struct sa_prot_map *safindprotent(char *name, struct sa_prot_map *table, + int table_ents); +static int sasetprotents(struct cam_periph *periph, + struct mtparamset *ps, int num_params); +static struct sa_param_ent *safindparament(struct mtparamset *ps); +static int saparamsetlist(struct cam_periph *periph, + struct mtsetlist *list, int need_copy); +static int saextget(struct cdev *dev, struct cam_periph *periph, + struct sbuf *sb, struct mtextget *g); +static int saparamget(struct sa_softc *softc, struct sbuf *sb); static void saprevent(struct cam_periph *periph, int action); static int sarewind(struct cam_periph *periph); static int saspace(struct cam_periph *periph, int count, scsi_space_code code); +static void sadevgonecb(void *arg); +static void sasetupdev(struct sa_softc *softc, struct cdev *dev); static int samount(struct cam_periph *, int, struct cdev *); static int saretension(struct cam_periph *periph); static int sareservereleaseunit(struct cam_periph *periph, @@ -420,9 +590,16 @@ static int sareservereleaseunit(struct cam_periph *periph, static int saloadunload(struct cam_periph *periph, int load); static int saerase(struct cam_periph *periph, int longerase); static int sawritefilemarks(struct cam_periph *periph, - int nmarks, int setmarks); + int nmarks, int setmarks, int immed); +static int sagetpos(struct cam_periph *periph); static int sardpos(struct cam_periph *periph, int, u_int32_t *); -static int sasetpos(struct cam_periph *periph, int, u_int32_t *); +static int sasetpos(struct cam_periph *periph, int, + struct mtlocate *); +static void safilldenstypesb(struct sbuf *sb, int *indent, + uint8_t *buf, int buf_len, + int is_density); +static void safilldensitysb(struct sa_softc *softc, int *indent, + struct sbuf *sb); #ifndef SA_DEFAULT_IO_SPLIT @@ -464,7 +641,7 @@ static struct cdevsw sa_cdevsw = { .d_ioctl = saioctl, .d_strategy = sastrategy, .d_name = "sa", - .d_flags = D_TAPE, + .d_flags = D_TAPE | D_TRACKCLOSE, }; static int @@ -488,6 +665,7 @@ saopen(struct cdev *dev, int flags, int fmt, struct thread *td) if (SA_IS_CTRL(dev)) { softc->ctrl_mode = 1; + softc->open_count++; cam_periph_unlock(periph); return (0); } @@ -519,6 +697,7 @@ saopen(struct cdev *dev, int flags, int fmt, struct thread *td) if (error && (flags & O_NONBLOCK)) { softc->flags |= SA_FLAG_OPEN; softc->open_pending_mount = 1; + softc->open_count++; cam_periph_unhold(periph); cam_periph_unlock(periph); return (0); @@ -534,6 +713,7 @@ saopen(struct cdev *dev, int flags, int fmt, struct thread *td) saprevent(periph, PR_PREVENT); softc->flags |= SA_FLAG_OPEN; + softc->open_count++; cam_periph_unhold(periph); cam_periph_unlock(periph); @@ -545,7 +725,7 @@ saclose(struct cdev *dev, int flag, int fmt, struct thread *td) { struct cam_periph *periph; struct sa_softc *softc; - int mode, error, writing, tmp; + int mode, error, writing, tmp, i; int closedbits = SA_FLAG_OPEN; mode = SAMODE(dev); @@ -564,6 +744,7 @@ saclose(struct cdev *dev, int flag, int fmt, struct thread *td) softc->open_rdonly = 0; if (SA_IS_CTRL(dev)) { softc->ctrl_mode = 0; + softc->open_count--; cam_periph_unlock(periph); cam_periph_release(periph); return (0); @@ -572,6 +753,7 @@ saclose(struct cdev *dev, int flag, int fmt, struct thread *td) if (softc->open_pending_mount) { softc->flags &= ~SA_FLAG_OPEN; softc->open_pending_mount = 0; + softc->open_count--; cam_periph_unlock(periph); cam_periph_release(periph); return (0); @@ -676,6 +858,16 @@ saclose(struct cdev *dev, int flag, int fmt, struct thread *td) * And we are no longer open for business. */ softc->flags &= ~closedbits; + softc->open_count--; + + /* + * Invalidate any density information that depends on having tape + * media in the drive. + */ + for (i = 0; i < SA_DENSITY_TYPES; i++) { + if (softc->density_type_bits[i] & SRDS_MEDIA) + softc->density_info_valid[i] = 0; + } /* * Inform users if tape state if frozen.... @@ -824,6 +1016,480 @@ sastrategy(struct bio *bp) return; } +static int +sasetsili(struct cam_periph *periph, struct mtparamset *ps, int num_params) +{ + uint32_t sili_blocksize; + struct sa_softc *softc; + int error; + + error = 0; + softc = (struct sa_softc *)periph->softc; + + if (ps->value_type != MT_PARAM_SET_SIGNED) { + snprintf(ps->error_str, sizeof(ps->error_str), + "sili is a signed parameter"); + goto bailout; + } + if ((ps->value.value_signed < 0) + || (ps->value.value_signed > 1)) { + snprintf(ps->error_str, sizeof(ps->error_str), + "invalid sili value %jd", (intmax_t)ps->value.value_signed); + goto bailout_error; + } + /* + * We only set the SILI flag in variable block + * mode. You'll get a check condition in fixed + * block mode if things don't line up in any case. + */ + if (softc->flags & SA_FLAG_FIXED) { + snprintf(ps->error_str, sizeof(ps->error_str), + "can't set sili bit in fixed block mode"); + goto bailout_error; + } + if (softc->sili == ps->value.value_signed) + goto bailout; + + if (ps->value.value_signed == 1) + sili_blocksize = 4; + else + sili_blocksize = 0; + + error = sasetparams(periph, SA_PARAM_BLOCKSIZE, + sili_blocksize, 0, 0, SF_QUIET_IR); + if (error != 0) { + snprintf(ps->error_str, sizeof(ps->error_str), + "sasetparams() returned error %d", error); + goto bailout_error; + } + + softc->sili = ps->value.value_signed; + +bailout: + ps->status = MT_PARAM_STATUS_OK; + return (error); + +bailout_error: + ps->status = MT_PARAM_STATUS_ERROR; + if (error == 0) + error = EINVAL; + + return (error); +} + +static int +saseteotwarn(struct cam_periph *periph, struct mtparamset *ps, int num_params) +{ + struct sa_softc *softc; + int error; + + error = 0; + softc = (struct sa_softc *)periph->softc; + + if (ps->value_type != MT_PARAM_SET_SIGNED) { + snprintf(ps->error_str, sizeof(ps->error_str), + "eot_warn is a signed parameter"); + ps->status = MT_PARAM_STATUS_ERROR; + goto bailout; + } + if ((ps->value.value_signed < 0) + || (ps->value.value_signed > 1)) { + snprintf(ps->error_str, sizeof(ps->error_str), + "invalid eot_warn value %jd\n", + (intmax_t)ps->value.value_signed); + ps->status = MT_PARAM_STATUS_ERROR; + goto bailout; + } + softc->eot_warn = ps->value.value_signed; + ps->status = MT_PARAM_STATUS_OK; +bailout: + if (ps->status != MT_PARAM_STATUS_OK) + error = EINVAL; + + return (error); +} + + +static void +safillprot(struct sa_softc *softc, int *indent, struct sbuf *sb) +{ + int tmpint; + + SASBADDNODE(sb, *indent, protection); + if (softc->flags & SA_FLAG_PROTECT_SUPP) + tmpint = 1; + else + tmpint = 0; + SASBADDINTDESC(sb, *indent, tmpint, %d, protection_supported, + "Set to 1 if protection information is supported"); + + if ((tmpint != 0) + && (softc->prot_info.cur_prot_state.initialized != 0)) { + struct sa_prot_state *prot; + + prot = &softc->prot_info.cur_prot_state; + + SASBADDUINTDESC(sb, *indent, prot->prot_method, %u, + prot_method, "Current Protection Method"); + SASBADDUINTDESC(sb, *indent, prot->pi_length, %u, + pi_length, "Length of Protection Information"); + SASBADDUINTDESC(sb, *indent, prot->lbp_w, %u, + lbp_w, "Check Protection on Writes"); + SASBADDUINTDESC(sb, *indent, prot->lbp_r, %u, + lbp_r, "Check and Include Protection on Reads"); + SASBADDUINTDESC(sb, *indent, prot->rbdp, %u, + rbdp, "Transfer Protection Information for RECOVER " + "BUFFERED DATA command"); + } + SASBENDNODE(sb, *indent, protection); +} + +static void +sapopulateprots(struct sa_prot_state *cur_state, struct sa_prot_map *new_table, + int table_ents) +{ + int i; + + bcopy(sa_prot_table, new_table, min(table_ents * sizeof(*new_table), + sizeof(sa_prot_table))); + + table_ents = min(table_ents, SA_NUM_PROT_ENTS); + + for (i = 0; i < table_ents; i++) + new_table[i].value = (uint32_t *)((uint8_t *)cur_state + + new_table[i].offset); + + return; +} + +static struct sa_prot_map * +safindprotent(char *name, struct sa_prot_map *table, int table_ents) +{ + char *prot_name = "protection."; + int i, prot_len; + + prot_len = strlen(prot_name); + + /* + * This shouldn't happen, but we check just in case. + */ + if (strncmp(name, prot_name, prot_len) != 0) + goto bailout; + + for (i = 0; i < table_ents; i++) { + if (strcmp(&name[prot_len], table[i].name) != 0) + continue; + return (&table[i]); + } +bailout: + return (NULL); +} + +static int +sasetprotents(struct cam_periph *periph, struct mtparamset *ps, int num_params) +{ + struct sa_softc *softc; + struct sa_prot_map prot_ents[SA_NUM_PROT_ENTS]; + struct sa_prot_state new_state; + int error; + int i; + + softc = (struct sa_softc *)periph->softc; + error = 0; + + /* + * Make sure that this tape drive supports protection information. + * Otherwise we can't set anything. + */ + if ((softc->flags & SA_FLAG_PROTECT_SUPP) == 0) { + snprintf(ps[0].error_str, sizeof(ps[0].error_str), + "Protection information is not supported for this device"); + ps[0].status = MT_PARAM_STATUS_ERROR; + goto bailout; + } + + /* + * We can't operate with physio(9) splitting enabled, because there + * is no way to insure (especially in variable block mode) that + * what the user writes (with a checksum block at the end) will + * make it into the sa(4) driver intact. + */ + if ((softc->si_flags & SI_NOSPLIT) == 0) { + snprintf(ps[0].error_str, sizeof(ps[0].error_str), + "Protection information cannot be enabled with I/O " + "splitting"); + ps[0].status = MT_PARAM_STATUS_ERROR; + goto bailout; + } + + /* + * Take the current cached protection state and use that as the + * basis for our new entries. + */ + bcopy(&softc->prot_info.cur_prot_state, &new_state, sizeof(new_state)); + + /* + * Populate the table mapping property names to pointers into the + * state structure. + */ + sapopulateprots(&new_state, prot_ents, SA_NUM_PROT_ENTS); + + /* + * For each parameter the user passed in, make sure the name, type + * and value are valid. + */ + for (i = 0; i < num_params; i++) { + struct sa_prot_map *ent; + + ent = safindprotent(ps[i].value_name, prot_ents, + SA_NUM_PROT_ENTS); + if (ent == NULL) { + ps[i].status = MT_PARAM_STATUS_ERROR; + snprintf(ps[i].error_str, sizeof(ps[i].error_str), + "Invalid protection entry name %s", + ps[i].value_name); + error = EINVAL; + goto bailout; + } + if (ent->param_type != ps[i].value_type) { + ps[i].status = MT_PARAM_STATUS_ERROR; + snprintf(ps[i].error_str, sizeof(ps[i].error_str), + "Supplied type %d does not match actual type %d", + ps[i].value_type, ent->param_type); + error = EINVAL; + goto bailout; + } + if ((ps[i].value.value_unsigned < ent->min_val) + || (ps[i].value.value_unsigned > ent->max_val)) { + ps[i].status = MT_PARAM_STATUS_ERROR; + snprintf(ps[i].error_str, sizeof(ps[i].error_str), + "Value %ju is outside valid range %u - %u", + (uintmax_t)ps[i].value.value_unsigned, ent->min_val, + ent->max_val); + error = EINVAL; + goto bailout; + } + *(ent->value) = ps[i].value.value_unsigned; + } + + /* + * Actually send the protection settings to the drive. + */ + error = sasetprot(periph, &new_state); + if (error != 0) { + for (i = 0; i < num_params; i++) { + ps[i].status = MT_PARAM_STATUS_ERROR; + snprintf(ps[i].error_str, sizeof(ps[i].error_str), + "Unable to set parameter, see dmesg(8)"); + } + goto bailout; + } + + /* + * Let the user know that his settings were stored successfully. + */ + for (i = 0; i < num_params; i++) + ps[i].status = MT_PARAM_STATUS_OK; + +bailout: + return (error); +} +/* + * Entry handlers generally only handle a single entry. Node handlers will + * handle a contiguous range of parameters to set in a single call. + */ +typedef enum { + SA_PARAM_TYPE_ENTRY, + SA_PARAM_TYPE_NODE +} sa_param_type; + +struct sa_param_ent { + char *name; + sa_param_type param_type; + int (*set_func)(struct cam_periph *periph, struct mtparamset *ps, + int num_params); +} sa_param_table[] = { + {"sili", SA_PARAM_TYPE_ENTRY, sasetsili }, + {"eot_warn", SA_PARAM_TYPE_ENTRY, saseteotwarn }, + {"protection.", SA_PARAM_TYPE_NODE, sasetprotents } +}; + +static struct sa_param_ent * +safindparament(struct mtparamset *ps) +{ + unsigned int i; + + for (i = 0; i < sizeof(sa_param_table) /sizeof(sa_param_table[0]); i++){ + /* + * For entries, we compare all of the characters. For + * nodes, we only compare the first N characters. The node + * handler will decode the rest. + */ + if (sa_param_table[i].param_type == SA_PARAM_TYPE_ENTRY) { + if (strcmp(ps->value_name, sa_param_table[i].name) != 0) + continue; + } else { + if (strncmp(ps->value_name, sa_param_table[i].name, + strlen(sa_param_table[i].name)) != 0) + continue; + } + return (&sa_param_table[i]); + } + + return (NULL); +} + +/* + * Go through a list of parameters, coalescing contiguous parameters with + * the same parent node into a single call to a set_func. + */ +static int +saparamsetlist(struct cam_periph *periph, struct mtsetlist *list, + int need_copy) +{ + int i, contig_ents; + int error; + struct mtparamset *params, *first; + struct sa_param_ent *first_ent; + + error = 0; + params = NULL; + + if (list->num_params == 0) + /* Nothing to do */ + goto bailout; + + /* + * Verify that the user has the correct structure size. + */ + if ((list->num_params * sizeof(struct mtparamset)) != + list->param_len) { + xpt_print(periph->path, "%s: length of params %d != " + "sizeof(struct mtparamset) %zd * num_params %d\n", + __func__, list->param_len, sizeof(struct mtparamset), + list->num_params); + error = EINVAL; + goto bailout; + } + + if (need_copy != 0) { + /* + * XXX KDM will dropping the lock cause an issue here? + */ + cam_periph_unlock(periph); + params = malloc(list->param_len, M_SCSISA, M_WAITOK | M_ZERO); + error = copyin(list->params, params, list->param_len); + cam_periph_lock(periph); + + if (error != 0) + goto bailout; + } else { + params = list->params; + } + + contig_ents = 0; + first = NULL; + first_ent = NULL; + for (i = 0; i < list->num_params; i++) { + struct sa_param_ent *ent; + + ent = safindparament(¶ms[i]); + if (ent == NULL) { + snprintf(params[i].error_str, + sizeof(params[i].error_str), + "%s: cannot find parameter %s", __func__, + params[i].value_name); + params[i].status = MT_PARAM_STATUS_ERROR; + break; + } + + if (first != NULL) { + if (first_ent == ent) { + /* + * We're still in a contiguous list of + * parameters that can be handled by one + * node handler. + */ + contig_ents++; + continue; + } else { + error = first_ent->set_func(periph, first, + contig_ents); + first = NULL; + first_ent = NULL; + contig_ents = 0; + if (error != 0) { + error = 0; + break; + } + } + } + if (ent->param_type == SA_PARAM_TYPE_NODE) { + first = ¶ms[i]; + first_ent = ent; + contig_ents = 1; + } else { + error = ent->set_func(periph, ¶ms[i], 1); + if (error != 0) { + error = 0; + break; + } + } + } + if (first != NULL) + first_ent->set_func(periph, first, contig_ents); + +bailout: + if (need_copy != 0) { + if (error != EFAULT) { + cam_periph_unlock(periph); + copyout(params, list->params, list->param_len); + cam_periph_lock(periph); + } + free(params, M_SCSISA); + } + return (error); +} + +static int +sagetparams_common(struct cdev *dev, struct cam_periph *periph) +{ + struct sa_softc *softc; + u_int8_t write_protect; + int comp_enabled, comp_supported, error; + + softc = (struct sa_softc *)periph->softc; + + if (softc->open_pending_mount) + return (0); + + /* The control device may issue getparams() if there are no opens. */ + if (SA_IS_CTRL(dev) && (softc->flags & SA_FLAG_OPEN) != 0) + return (0); + + error = sagetparams(periph, SA_PARAM_ALL, &softc->media_blksize, + &softc->media_density, &softc->media_numblks, &softc->buffer_mode, + &write_protect, &softc->speed, &comp_supported, &comp_enabled, + &softc->comp_algorithm, NULL, NULL, 0, 0); + if (error) + return (error); + if (write_protect) + softc->flags |= SA_FLAG_TAPE_WP; + else + softc->flags &= ~SA_FLAG_TAPE_WP; + softc->flags &= ~SA_FLAG_COMPRESSION; + if (comp_supported) { + if (softc->saved_comp_algorithm == 0) + softc->saved_comp_algorithm = + softc->comp_algorithm; + softc->flags |= SA_FLAG_COMP_SUPP; + if (comp_enabled) + softc->flags |= SA_FLAG_COMP_ENABLED; + } else + softc->flags |= SA_FLAG_COMP_UNSUPP; + + return (0); +} #define PENDING_MOUNT_CHECK(softc, periph, dev) \ if (softc->open_pending_mount) { \ @@ -868,6 +1534,9 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) switch (cmd) { case MTIOCGETEOTMODEL: case MTIOCGET: + case MTIOCEXTGET: + case MTIOCPARAMGET: + case MTIOCRBLIM: break; case MTIOCERRSTAT: /* @@ -939,36 +1608,9 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { struct mtget *g = (struct mtget *)arg; - /* - * If this isn't the control mode device, actually go out - * and ask the drive again what it's set to. - */ - if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) { - u_int8_t write_protect; - int comp_enabled, comp_supported; - error = sagetparams(periph, SA_PARAM_ALL, - &softc->media_blksize, &softc->media_density, - &softc->media_numblks, &softc->buffer_mode, - &write_protect, &softc->speed, &comp_supported, - &comp_enabled, &softc->comp_algorithm, NULL); - if (error) - break; - if (write_protect) - softc->flags |= SA_FLAG_TAPE_WP; - else - softc->flags &= ~SA_FLAG_TAPE_WP; - softc->flags &= ~(SA_FLAG_COMP_SUPP| - SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP); - if (comp_supported) { - if (softc->saved_comp_algorithm == 0) - softc->saved_comp_algorithm = - softc->comp_algorithm; - softc->flags |= SA_FLAG_COMP_SUPP; - if (comp_enabled) - softc->flags |= SA_FLAG_COMP_ENABLED; - } else - softc->flags |= SA_FLAG_COMP_UNSUPP; - } + error = sagetparams_common(dev, periph); + if (error) + break; bzero(g, sizeof(struct mtget)); g->mt_type = MT_ISAR; if (softc->flags & SA_FLAG_COMP_UNSUPP) { @@ -1020,6 +1662,84 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) error = 0; break; } + case MTIOCEXTGET: + case MTIOCPARAMGET: + { + struct mtextget *g = (struct mtextget *)arg; + char *tmpstr2; + struct sbuf *sb; + + /* + * Report drive status using an XML format. + */ + + /* + * XXX KDM will dropping the lock cause any problems here? + */ + cam_periph_unlock(periph); + sb = sbuf_new(NULL, NULL, g->alloc_len, SBUF_FIXEDLEN); + if (sb == NULL) { + g->status = MT_EXT_GET_ERROR; + snprintf(g->error_str, sizeof(g->error_str), + "Unable to allocate %d bytes for status info", + g->alloc_len); + cam_periph_lock(periph); + goto extget_bailout; + } + cam_periph_lock(periph); + + if (cmd == MTIOCEXTGET) + error = saextget(dev, periph, sb, g); + else + error = saparamget(softc, sb); + + if (error != 0) + goto extget_bailout; + + error = sbuf_finish(sb); + if (error == ENOMEM) { + g->status = MT_EXT_GET_NEED_MORE_SPACE; + error = 0; + } else if (error != 0) { + g->status = MT_EXT_GET_ERROR; + snprintf(g->error_str, sizeof(g->error_str), + "Error %d returned from sbuf_finish()", error); + } else + g->status = MT_EXT_GET_OK; + + error = 0; + tmpstr2 = sbuf_data(sb); + g->fill_len = strlen(tmpstr2) + 1; + cam_periph_unlock(periph); + + error = copyout(tmpstr2, g->status_xml, g->fill_len); + + cam_periph_lock(periph); + +extget_bailout: + sbuf_delete(sb); + break; + } + case MTIOCPARAMSET: + { + struct mtsetlist list; + struct mtparamset *ps = (struct mtparamset *)arg; + + bzero(&list, sizeof(list)); + list.num_params = 1; + list.param_len = sizeof(*ps); + list.params = ps; + + error = saparamsetlist(periph, &list, /*need_copy*/ 0); + break; + } + case MTIOCSETLIST: + { + struct mtsetlist *list = (struct mtsetlist *)arg; + + error = saparamsetlist(periph, list, /*need_copy*/ 1); + break; + } case MTIOCERRSTAT: { struct scsi_tape_errors *sep = @@ -1040,7 +1760,7 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb, sizeof (sep->ctl_cdb)); - if ((SA_IS_CTRL(dev) == 0 && softc->open_pending_mount) || + if ((SA_IS_CTRL(dev) == 0 && !softc->open_pending_mount) || didlockperiph) bzero((caddr_t) &softc->errinfo, sizeof (softc->errinfo)); @@ -1067,13 +1787,17 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) /* * We don't need to clear the SA_FLAG_TAPE_WRITTEN * flag because by keeping track of filemarks - * we have last written we know ehether or not + * we have last written we know whether or not * we need to write more when we close the device. */ - error = sawritefilemarks(periph, count, FALSE); + error = sawritefilemarks(periph, count, FALSE, FALSE); + break; + case MTWEOFI: + /* write an end-of-file marker without waiting */ + error = sawritefilemarks(periph, count, FALSE, TRUE); break; case MTWSS: /* write a setmark */ - error = sawritefilemarks(periph, count, TRUE); + error = sawritefilemarks(periph, count, TRUE, FALSE); break; case MTBSR: /* backward space record */ case MTFSR: /* forward space record */ @@ -1198,6 +1922,9 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) } break; + case MTLOAD: + error = saloadunload(periph, TRUE); + break; case MTNOP: /* no operation, sets status only */ case MTCACHE: /* enable controller cache */ case MTNOCACHE: /* disable controller cache */ @@ -1208,6 +1935,13 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) PENDING_MOUNT_CHECK(softc, periph, dev); + if ((softc->sili != 0) + && (count != 0)) { + xpt_print(periph->path, "Can't enter fixed " + "block mode with SILI enabled\n"); + error = EINVAL; + break; + } error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count, 0, 0, 0); if (error == 0) { @@ -1293,12 +2027,29 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) error = sardpos(periph, 1, (u_int32_t *) arg); break; case MTIOCSLOCATE: + case MTIOCHLOCATE: { + struct mtlocate locate_info; + int hard; + + bzero(&locate_info, sizeof(locate_info)); + locate_info.logical_id = *((uint32_t *)arg); + if (cmd == MTIOCSLOCATE) + hard = 0; + else + hard = 1; + PENDING_MOUNT_CHECK(softc, periph, dev); - error = sasetpos(periph, 0, (u_int32_t *) arg); + + error = sasetpos(periph, hard, &locate_info); break; - case MTIOCHLOCATE: + } + case MTIOCEXTLOCATE: PENDING_MOUNT_CHECK(softc, periph, dev); - error = sasetpos(periph, 1, (u_int32_t *) arg); + error = sasetpos(periph, /*hard*/ 0, (struct mtlocate *)arg); + softc->flags &= + ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN); + softc->flags &= ~SA_FLAG_ERR_PENDING; + softc->filemarks = 0; break; case MTIOCGETEOTMODEL: error = 0; @@ -1324,6 +2075,16 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) break; } break; + case MTIOCRBLIM: { + struct mtrblim *rblim; + + rblim = (struct mtrblim *)arg; + + rblim->granularity = softc->blk_gran; + rblim->min_block_length = softc->min_blk; + rblim->max_block_length = softc->max_blk; + break; + } default: error = cam_periph_ioctl(periph, cmd, arg, saerror); break; @@ -1338,8 +2099,14 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) case MTIOCRDHPOS: case MTIOCSLOCATE: case MTIOCHLOCATE: + /* + * XXX KDM look at this. + */ softc->fileno = (daddr_t) -1; softc->blkno = (daddr_t) -1; + softc->rep_blkno = (daddr_t) -1; + softc->rep_fileno = (daddr_t) -1; + softc->partition = (daddr_t) -1; softc->flags &= ~SA_FLAG_TAPE_FROZEN; xpt_print(periph->path, "tape state now unfrozen.\n"); @@ -1372,6 +2139,51 @@ sainit(void) } static void +sadevgonecb(void *arg) +{ + struct cam_periph *periph; + struct mtx *mtx; + struct sa_softc *softc; + + periph = (struct cam_periph *)arg; + softc = (struct sa_softc *)periph->softc; + + mtx = cam_periph_mtx(periph); + mtx_lock(mtx); + + softc->num_devs_to_destroy--; + if (softc->num_devs_to_destroy == 0) { + int i; + + /* + * When we have gotten all of our callbacks, we will get + * no more close calls from devfs. So if we have any + * dangling opens, we need to release the reference held + * for that particular context. + */ + for (i = 0; i < softc->open_count; i++) + cam_periph_release_locked(periph); + + softc->open_count = 0; + + /* + * Release the reference held for devfs, all of our + * instances are gone now. + */ + cam_periph_release_locked(periph); + } + + /* + * We reference the lock directly here, instead of using + * cam_periph_unlock(). The reason is that the final call to + * cam_periph_release_locked() above could result in the periph + * getting freed. If that is the case, dereferencing the periph + * with a cam_periph_unlock() call would cause a page fault. + */ + mtx_unlock(mtx); +} + +static void saoninvalidate(struct cam_periph *periph) { struct sa_softc *softc; @@ -1392,25 +2204,34 @@ saoninvalidate(struct cam_periph *periph) */ bioq_flush(&softc->bio_queue, NULL, ENXIO); softc->queue_count = 0; + + /* + * Tell devfs that all of our devices have gone away, and ask for a + * callback when it has cleaned up its state. + */ + destroy_dev_sched_cb(softc->devs.ctl_dev, sadevgonecb, periph); + destroy_dev_sched_cb(softc->devs.r_dev, sadevgonecb, periph); + destroy_dev_sched_cb(softc->devs.nr_dev, sadevgonecb, periph); + destroy_dev_sched_cb(softc->devs.er_dev, sadevgonecb, periph); } static void sacleanup(struct cam_periph *periph) { struct sa_softc *softc; - int i; softc = (struct sa_softc *)periph->softc; - devstat_remove_entry(softc->device_stats); cam_periph_unlock(periph); - destroy_dev(softc->devs.ctl_dev); - for (i = 0; i < SA_NUM_MODES; i++) { - destroy_dev(softc->devs.mode_devs[i].r_dev); - destroy_dev(softc->devs.mode_devs[i].nr_dev); - destroy_dev(softc->devs.mode_devs[i].er_dev); - } + + if ((softc->flags & SA_FLAG_SCTX_INIT) != 0 + && sysctl_ctx_free(&softc->sysctl_ctx) != 0) + xpt_print(periph->path, "can't remove sysctl context\n"); + cam_periph_lock(periph); + + devstat_remove_entry(softc->device_stats); + free(softc, M_SCSISA); } @@ -1460,6 +2281,21 @@ saasync(void *callback_arg, u_int32_t code, } static void +sasetupdev(struct sa_softc *softc, struct cdev *dev) +{ + dev->si_drv1 = softc->periph; + dev->si_iosize_max = softc->maxio; + dev->si_flags |= softc->si_flags; + /* + * Keep a count of how many non-alias devices we have created, + * so we can make sure we clean them all up on shutdown. Aliases + * are cleaned up when we destroy the device they're an alias for. + */ + if ((dev->si_flags & SI_ALIAS) == 0) + softc->num_devs_to_destroy++; +} + +static void sasysctlinit(void *context, int pending) { struct cam_periph *periph; @@ -1479,6 +2315,7 @@ sasysctlinit(void *context, int pending) snprintf(tmpstr2, sizeof(tmpstr2), "%u", periph->unit_number); sysctl_ctx_init(&softc->sysctl_ctx); + softc->flags |= SA_FLAG_SCTX_INIT; softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kern_cam_sa), OID_AUTO, tmpstr2, CTLFLAG_RD, 0, tmpstr); @@ -1510,7 +2347,6 @@ saregister(struct cam_periph *periph, void *arg) struct ccb_pathinq cpi; caddr_t match; char tmpstr[80]; - int i; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) { @@ -1529,8 +2365,15 @@ saregister(struct cam_periph *periph, void *arg) softc->state = SA_STATE_NORMAL; softc->fileno = (daddr_t) -1; softc->blkno = (daddr_t) -1; + softc->rep_fileno = (daddr_t) -1; + softc->rep_blkno = (daddr_t) -1; + softc->partition = (daddr_t) -1; + softc->bop = -1; + softc->eop = -1; + softc->bpew = -1; bioq_init(&softc->bio_queue); + softc->periph = periph; periph->softc = softc; /* @@ -1548,6 +2391,41 @@ saregister(struct cam_periph *periph, void *arg) } else softc->quirks = SA_QUIRK_NONE; + /* + * Long format data for READ POSITION was introduced in SSC, which + * was after SCSI-2. (Roughly equivalent to SCSI-3.) If the drive + * reports that it is SCSI-2 or older, it is unlikely to support + * long position data, but it might. Some drives from that era + * claim to be SCSI-2, but do support long position information. + * So, instead of immediately disabling long position information + * for SCSI-2 devices, we'll try one pass through sagetpos(), and + * then disable long position information if we get an error. + */ + if (cgd->inq_data.version <= SCSI_REV_CCS) + softc->quirks |= SA_QUIRK_NO_LONG_POS; + + if (cgd->inq_data.spc3_flags & SPC3_SID_PROTECT) { + struct ccb_dev_advinfo cdai; + struct scsi_vpd_extended_inquiry_data ext_inq; + + bzero(&ext_inq, sizeof(ext_inq)); + + xpt_setup_ccb(&cdai.ccb_h, periph->path, CAM_PRIORITY_NORMAL); + + cdai.ccb_h.func_code = XPT_DEV_ADVINFO; + cdai.flags = CDAI_FLAG_NONE; + cdai.buftype = CDAI_TYPE_EXT_INQ; + cdai.bufsiz = sizeof(ext_inq); + cdai.buf = (uint8_t *)&ext_inq; + xpt_action((union ccb *)&cdai); + + if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); + if ((cdai.ccb_h.status == CAM_REQ_CMP) + && (ext_inq.flags1 & SVPD_EID_SA_SPT_LBP)) + softc->flags |= SA_FLAG_PROTECT_SUPP; + } + bzero(&cpi, sizeof(cpi)); xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; @@ -1614,66 +2492,44 @@ saregister(struct cam_periph *periph, void *arg) if (cpi.hba_misc & PIM_UNMAPPED) softc->si_flags |= SI_UNMAPPED; + /* + * Acquire a reference to the periph before we create the devfs + * instances for it. We'll release this reference once the devfs + * instances have been freed. + */ + if (cam_periph_acquire(periph) != CAM_REQ_CMP) { + xpt_print(periph->path, "%s: lost periph during " + "registration!\n", __func__); + cam_periph_lock(periph); + return (CAM_REQ_CMP_ERR); + } + softc->devs.ctl_dev = make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV, - 0, SA_ATYPE_R), UID_ROOT, GID_OPERATOR, + SA_ATYPE_R), UID_ROOT, GID_OPERATOR, 0660, "%s%d.ctl", periph->periph_name, periph->unit_number); - softc->devs.ctl_dev->si_drv1 = periph; - softc->devs.ctl_dev->si_iosize_max = softc->maxio; - softc->devs.ctl_dev->si_flags |= softc->si_flags; - - for (i = 0; i < SA_NUM_MODES; i++) { - - softc->devs.mode_devs[i].r_dev = make_dev(&sa_cdevsw, - SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_R), - UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d", - periph->periph_name, periph->unit_number, i); - softc->devs.mode_devs[i].r_dev->si_drv1 = periph; - softc->devs.mode_devs[i].r_dev->si_iosize_max = softc->maxio; - softc->devs.mode_devs[i].r_dev->si_flags |= softc->si_flags; - - softc->devs.mode_devs[i].nr_dev = make_dev(&sa_cdevsw, - SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_NR), - UID_ROOT, GID_OPERATOR, 0660, "n%s%d.%d", - periph->periph_name, periph->unit_number, i); - softc->devs.mode_devs[i].nr_dev->si_drv1 = periph; - softc->devs.mode_devs[i].nr_dev->si_iosize_max = softc->maxio; - softc->devs.mode_devs[i].nr_dev->si_flags |= softc->si_flags; - - softc->devs.mode_devs[i].er_dev = make_dev(&sa_cdevsw, - SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_ER), - UID_ROOT, GID_OPERATOR, 0660, "e%s%d.%d", - periph->periph_name, periph->unit_number, i); - softc->devs.mode_devs[i].er_dev->si_drv1 = periph; - softc->devs.mode_devs[i].er_dev->si_iosize_max = softc->maxio; - softc->devs.mode_devs[i].er_dev->si_flags |= softc->si_flags; + sasetupdev(softc, softc->devs.ctl_dev); - /* - * Make the (well known) aliases for the first mode. - */ - if (i == 0) { - struct cdev *alias; + softc->devs.r_dev = make_dev(&sa_cdevsw, SAMINOR(SA_NOT_CTLDEV, + SA_ATYPE_R), UID_ROOT, GID_OPERATOR, + 0660, "%s%d", periph->periph_name, periph->unit_number); + sasetupdev(softc, softc->devs.r_dev); - alias = make_dev_alias(softc->devs.mode_devs[i].r_dev, - "%s%d", periph->periph_name, periph->unit_number); - alias->si_drv1 = periph; - alias->si_iosize_max = softc->maxio; - alias->si_flags |= softc->si_flags; + softc->devs.nr_dev = make_dev(&sa_cdevsw, SAMINOR(SA_NOT_CTLDEV, + SA_ATYPE_NR), UID_ROOT, GID_OPERATOR, + 0660, "n%s%d", periph->periph_name, periph->unit_number); + sasetupdev(softc, softc->devs.nr_dev); - alias = make_dev_alias(softc->devs.mode_devs[i].nr_dev, - "n%s%d", periph->periph_name, periph->unit_number); - alias->si_drv1 = periph; - alias->si_iosize_max = softc->maxio; - alias->si_flags |= softc->si_flags; + softc->devs.er_dev = make_dev(&sa_cdevsw, SAMINOR(SA_NOT_CTLDEV, + SA_ATYPE_ER), UID_ROOT, GID_OPERATOR, + 0660, "e%s%d", periph->periph_name, periph->unit_number); + sasetupdev(softc, softc->devs.er_dev); - alias = make_dev_alias(softc->devs.mode_devs[i].er_dev, - "e%s%d", periph->periph_name, periph->unit_number); - alias->si_drv1 = periph; - alias->si_iosize_max = softc->maxio; - alias->si_flags |= softc->si_flags; - } - } cam_periph_lock(periph); + softc->density_type_bits[0] = 0; + softc->density_type_bits[1] = SRDS_MEDIA; + softc->density_type_bits[2] = SRDS_MEDIUM_TYPE; + softc->density_type_bits[3] = SRDS_MEDIUM_TYPE | SRDS_MEDIA; /* * Bump the peripheral refcount for the sysctl thread, in case we * get invalidated before the thread has a chance to run. @@ -1724,10 +2580,33 @@ again: done_bp = bp; if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) { /* - * We now just clear errors in this case - * and let the residual be the notifier. + * We have two different behaviors for + * writes when we hit either Early Warning + * or the PEWZ (Programmable Early Warning + * Zone). The default behavior is that + * for all writes that are currently + * queued after the write where we saw the + * early warning, we will return the write + * with the residual equal to the count. + * i.e. tell the application that 0 bytes + * were written. + * + * The alternate behavior, which is enabled + * when eot_warn is set, is that in + * addition to setting the residual equal + * to the count, we will set the error + * to ENOSPC. + * + * In either case, once queued writes are + * cleared out, we clear the error flag + * (see below) and the application is free to + * attempt to write more. */ - bp->bio_error = 0; + if (softc->eot_warn != 0) { + bp->bio_flags |= BIO_ERROR; + bp->bio_error = ENOSPC; + } else + bp->bio_error = 0; } else if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) { /* * This can only happen if we're reading @@ -1764,13 +2643,13 @@ again: bioq_remove(&softc->bio_queue, bp); softc->queue_count--; + length = bp->bio_bcount; + if ((softc->flags & SA_FLAG_FIXED) != 0) { if (softc->blk_shift != 0) { - length = - bp->bio_bcount >> softc->blk_shift; + length = length >> softc->blk_shift; } else if (softc->media_blksize != 0) { - length = bp->bio_bcount / - softc->media_blksize; + length = length / softc->media_blksize; } else { bp->bio_error = EIO; xpt_print(periph->path, "zero blocksize" @@ -1785,7 +2664,6 @@ again: "write")); #endif } else { - length = bp->bio_bcount; #if 0 CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO, ("issuing a %d variable byte %s\n", @@ -1813,6 +2691,19 @@ again: * would be to issue, e.g., 64KB reads and occasionally * have to do deal with 512 byte or 1KB intermediate * records. + * + * That said, though, we now support setting the + * SILI bit on reads, and we set the blocksize to 4 + * bytes when we do that. This gives us + * compatibility with software that wants this, + * although the only real difference between that + * and not setting the SILI bit on reads is that we + * won't get a check condition on reads where our + * request size is larger than the block on tape. + * That probably only makes a real difference in + * non-packetized SCSI, where you have to go back + * to the drive to request sense and thus incur + * more latency. */ softc->dsreg = (bp->bio_cmd == BIO_READ)? MTIO_DSREG_RD : MTIO_DSREG_WR; @@ -1820,7 +2711,7 @@ again: MSG_SIMPLE_Q_TAG, (bp->bio_cmd == BIO_READ ? SCSI_RW_READ : SCSI_RW_WRITE) | ((bp->bio_flags & BIO_UNMAPPED) != 0 ? - SCSI_RW_BIO : 0), FALSE, + SCSI_RW_BIO : 0), softc->sili, (softc->flags & SA_FLAG_FIXED) != 0, length, (bp->bio_flags & BIO_UNMAPPED) != 0 ? (void *)bp : bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE, @@ -2006,8 +2897,7 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) * Clear out old state. */ softc->flags &= ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN| - SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED| - SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP); + SA_FLAG_ERR_PENDING|SA_FLAG_COMPRESSION); softc->filemarks = 0; /* @@ -2116,7 +3006,7 @@ samount(struct cam_periph *periph, int oflags, struct cdev *dev) &softc->buffer_mode, &write_protect, &softc->speed, &comp_supported, &comp_enabled, &softc->comp_algorithm, - NULL); + NULL, NULL, 0, 0); if (error != 0) { /* @@ -2363,6 +3253,8 @@ exit: softc->dsreg = MTIO_DSREG_NIL; } else { softc->fileno = softc->blkno = 0; + softc->rep_fileno = softc->rep_blkno = -1; + softc->partition = 0; softc->dsreg = MTIO_DSREG_REST; } #ifdef SA_1FM_AT_EOD @@ -2422,7 +3314,7 @@ sacheckeod(struct cam_periph *periph) markswanted = samarkswanted(periph); if (markswanted > 0) { - error = sawritefilemarks(periph, markswanted, FALSE); + error = sawritefilemarks(periph, markswanted, FALSE, FALSE); } else { error = 0; } @@ -2642,7 +3534,8 @@ sagetparams(struct cam_periph *periph, sa_params params_to_get, u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks, int *buff_mode, u_int8_t *write_protect, u_int8_t *speed, int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm, - sa_comp_t *tcs) + sa_comp_t *tcs, struct scsi_control_data_prot_subpage *prot_page, + int dp_size, int prot_changeable) { union ccb *ccb; void *mode_buffer; @@ -2800,6 +3693,151 @@ retry: bcopy(ntcs, tcs, sizeof (sa_comp_t)); } + if ((params_to_get & SA_PARAM_DENSITY_EXT) + && (softc->scsi_rev >= SCSI_REV_SPC)) { + int i; + + for (i = 0; i < SA_DENSITY_TYPES; i++) { + scsi_report_density_support(&ccb->csio, + /*retries*/ 1, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*media*/ softc->density_type_bits[i] & SRDS_MEDIA, + /*medium_type*/ softc->density_type_bits[i] & + SRDS_MEDIUM_TYPE, + /*data_ptr*/ softc->density_info[i], + /*length*/ sizeof(softc->density_info[i]), + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ REP_DENSITY_TIMEOUT); + error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, + softc->device_stats); + status = ccb->ccb_h.status & CAM_STATUS_MASK; + + /* + * Some tape drives won't support this command at + * all, but hopefully we'll minimize that with the + * check for SPC or greater support above. If they + * don't support the default report (neither the + * MEDIA or MEDIUM_TYPE bits set), then there is + * really no point in continuing on to look for + * other reports. + */ + if ((error != 0) + || (status != CAM_REQ_CMP)) { + error = 0; + softc->density_info_valid[i] = 0; + if (softc->density_type_bits[i] == 0) + break; + else + continue; + } + softc->density_info_valid[i] = ccb->csio.dxfer_len - + ccb->csio.resid; + } + } + + /* + * Get logical block protection parameters if the drive supports it. + */ + if ((params_to_get & SA_PARAM_LBP) + && (softc->flags & SA_FLAG_PROTECT_SUPP)) { + struct scsi_mode_header_10 *mode10_hdr; + struct scsi_control_data_prot_subpage *dp_page; + struct scsi_mode_sense_10 *cdb; + struct sa_prot_state *prot; + int dp_len, returned_len; + + if (dp_size == 0) + dp_size = sizeof(*dp_page); + + dp_len = sizeof(*mode10_hdr) + dp_size; + mode10_hdr = malloc(dp_len, M_SCSISA, M_NOWAIT | M_ZERO); + if (mode10_hdr == NULL) { + error = ENOMEM; + goto sagetparamsexit; + } + + scsi_mode_sense_len(&ccb->csio, + /*retries*/ 5, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*dbd*/ TRUE, + /*page_code*/ (prot_changeable == 0) ? + SMS_PAGE_CTRL_CURRENT : + SMS_PAGE_CTRL_CHANGEABLE, + /*page*/ SMS_CONTROL_MODE_PAGE, + /*param_buf*/ (uint8_t *)mode10_hdr, + /*param_len*/ dp_len, + /*minimum_cmd_size*/ 10, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ SCSIOP_TIMEOUT); + /* + * XXX KDM we need to be able to set the subpage in the + * fill function. + */ + cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes; + cdb->subpage = SA_CTRL_DP_SUBPAGE_CODE; + + error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT, + softc->device_stats); + if (error != 0) { + free(mode10_hdr, M_SCSISA); + goto sagetparamsexit; + } + + status = ccb->ccb_h.status & CAM_STATUS_MASK; + if (status != CAM_REQ_CMP) { + error = EINVAL; + free(mode10_hdr, M_SCSISA); + goto sagetparamsexit; + } + + /* + * The returned data length at least has to be long enough + * for us to look at length in the mode page header. + */ + returned_len = ccb->csio.dxfer_len - ccb->csio.resid; + if (returned_len < sizeof(mode10_hdr->data_length)) { + error = EINVAL; + free(mode10_hdr, M_SCSISA); + goto sagetparamsexit; + } + + returned_len = min(returned_len, + sizeof(mode10_hdr->data_length) + + scsi_2btoul(mode10_hdr->data_length)); + + dp_page = (struct scsi_control_data_prot_subpage *) + &mode10_hdr[1]; + + /* + * We also have to have enough data to include the prot_bits + * in the subpage. + */ + if (returned_len < (sizeof(*mode10_hdr) + + __offsetof(struct scsi_control_data_prot_subpage, prot_bits) + + sizeof(dp_page->prot_bits))) { + error = EINVAL; + free(mode10_hdr, M_SCSISA); + goto sagetparamsexit; + } + + prot = &softc->prot_info.cur_prot_state; + prot->prot_method = dp_page->prot_method; + prot->pi_length = dp_page->pi_length & + SA_CTRL_DP_PI_LENGTH_MASK; + prot->lbp_w = (dp_page->prot_bits & SA_CTRL_DP_LBP_W) ? 1 :0; + prot->lbp_r = (dp_page->prot_bits & SA_CTRL_DP_LBP_R) ? 1 :0; + prot->rbdp = (dp_page->prot_bits & SA_CTRL_DP_RBDP) ? 1 :0; + prot->initialized = 1; + + if (prot_page != NULL) + bcopy(dp_page, prot_page, min(sizeof(*prot_page), + sizeof(*dp_page))); + + free(mode10_hdr, M_SCSISA); + } + if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) { int idx; char *xyz = mode_buffer; @@ -2818,6 +3856,177 @@ sagetparamsexit: } /* + * Set protection information to the pending protection information stored + * in the softc. + */ +static int +sasetprot(struct cam_periph *periph, struct sa_prot_state *new_prot) +{ + struct sa_softc *softc; + struct scsi_control_data_prot_subpage *dp_page, *dp_changeable; + struct scsi_mode_header_10 *mode10_hdr, *mode10_changeable; + union ccb *ccb; + uint8_t current_speed; + size_t dp_size, dp_page_length; + int dp_len, buff_mode; + int error; + + softc = (struct sa_softc *)periph->softc; + mode10_hdr = NULL; + mode10_changeable = NULL; + ccb = NULL; + + /* + * Start off with the size set to the actual length of the page + * that we have defined. + */ + dp_size = sizeof(*dp_changeable); + dp_page_length = dp_size - + __offsetof(struct scsi_control_data_prot_subpage, prot_method); + +retry_length: + + dp_len = sizeof(*mode10_changeable) + dp_size; + mode10_changeable = malloc(dp_len, M_SCSISA, M_NOWAIT | M_ZERO); + if (mode10_changeable == NULL) { + error = ENOMEM; + goto bailout; + } + + dp_changeable = + (struct scsi_control_data_prot_subpage *)&mode10_changeable[1]; + + /* + * First get the data protection page changeable parameters mask. + * We need to know which parameters the drive supports changing. + * We also need to know what the drive claims that its page length + * is. The reason is that IBM drives in particular are very picky + * about the page length. They want it (the length set in the + * page structure itself) to be 28 bytes, and they want the + * parameter list length specified in the mode select header to be + * 40 bytes. So, to work with IBM drives as well as any other tape + * drive, find out what the drive claims the page length is, and + * make sure that we match that. + */ + error = sagetparams(periph, SA_PARAM_SPEED | SA_PARAM_LBP, + NULL, NULL, NULL, &buff_mode, NULL, ¤t_speed, NULL, NULL, + NULL, NULL, dp_changeable, dp_size, /*prot_changeable*/ 1); + if (error != 0) + goto bailout; + + if (scsi_2btoul(dp_changeable->length) > dp_page_length) { + dp_page_length = scsi_2btoul(dp_changeable->length); + dp_size = dp_page_length + + __offsetof(struct scsi_control_data_prot_subpage, + prot_method); + free(mode10_changeable, M_SCSISA); + mode10_changeable = NULL; + goto retry_length; + } + + mode10_hdr = malloc(dp_len, M_SCSISA, M_NOWAIT | M_ZERO); + if (mode10_hdr == NULL) { + error = ENOMEM; + goto bailout; + } + + dp_page = (struct scsi_control_data_prot_subpage *)&mode10_hdr[1]; + + /* + * Now grab the actual current settings in the page. + */ + error = sagetparams(periph, SA_PARAM_SPEED | SA_PARAM_LBP, + NULL, NULL, NULL, &buff_mode, NULL, ¤t_speed, NULL, NULL, + NULL, NULL, dp_page, dp_size, /*prot_changeable*/ 0); + if (error != 0) + goto bailout; + + /* These two fields need to be 0 for MODE SELECT */ + scsi_ulto2b(0, mode10_hdr->data_length); + mode10_hdr->medium_type = 0; + /* We are not including a block descriptor */ + scsi_ulto2b(0, mode10_hdr->blk_desc_len); + + mode10_hdr->dev_spec = current_speed; + /* if set, set single-initiator buffering mode */ + if (softc->buffer_mode == SMH_SA_BUF_MODE_SIBUF) { + mode10_hdr->dev_spec |= SMH_SA_BUF_MODE_SIBUF; + } + + /* + * For each field, make sure that the drive allows changing it + * before bringing in the user's setting. + */ + if (dp_changeable->prot_method != 0) + dp_page->prot_method = new_prot->prot_method; + + if (dp_changeable->pi_length & SA_CTRL_DP_PI_LENGTH_MASK) { + dp_page->pi_length &= ~SA_CTRL_DP_PI_LENGTH_MASK; + dp_page->pi_length |= (new_prot->pi_length & + SA_CTRL_DP_PI_LENGTH_MASK); + } + if (dp_changeable->prot_bits & SA_CTRL_DP_LBP_W) { + if (new_prot->lbp_w) + dp_page->prot_bits |= SA_CTRL_DP_LBP_W; + else + dp_page->prot_bits &= ~SA_CTRL_DP_LBP_W; + } + + if (dp_changeable->prot_bits & SA_CTRL_DP_LBP_R) { + if (new_prot->lbp_r) + dp_page->prot_bits |= SA_CTRL_DP_LBP_R; + else + dp_page->prot_bits &= ~SA_CTRL_DP_LBP_R; + } + + if (dp_changeable->prot_bits & SA_CTRL_DP_RBDP) { + if (new_prot->rbdp) + dp_page->prot_bits |= SA_CTRL_DP_RBDP; + else + dp_page->prot_bits &= ~SA_CTRL_DP_RBDP; + } + + ccb = cam_periph_getccb(periph, 1); + + scsi_mode_select_len(&ccb->csio, + /*retries*/ 5, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*scsi_page_fmt*/ TRUE, + /*save_pages*/ FALSE, + /*param_buf*/ (uint8_t *)mode10_hdr, + /*param_len*/ dp_len, + /*minimum_cmd_size*/ 10, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ SCSIOP_TIMEOUT); + + error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); + if (error != 0) + goto bailout; + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + error = EINVAL; + goto bailout; + } + + /* + * The operation was successful. We could just copy the settings + * the user requested, but just in case the drive ignored some of + * our settings, let's ask for status again. + */ + error = sagetparams(periph, SA_PARAM_SPEED | SA_PARAM_LBP, + NULL, NULL, NULL, &buff_mode, NULL, ¤t_speed, NULL, NULL, + NULL, NULL, dp_page, dp_size, 0); + +bailout: + if (ccb != NULL) + xpt_release_ccb(ccb); + free(mode10_hdr, M_SCSISA); + free(mode10_changeable, M_SCSISA); + return (error); +} + +/* * The purpose of this function is to set one of four different parameters * for a tape drive: * - blocksize @@ -2869,7 +4078,7 @@ sasetparams(struct cam_periph *periph, sa_params params_to_set, params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED, ¤t_blocksize, ¤t_density, NULL, &buff_mode, NULL, ¤t_speed, &comp_supported, &comp_enabled, - ¤t_calg, ccomp); + ¤t_calg, ccomp, NULL, 0, 0); if (error != 0) { free(ccomp, M_SCSISA); @@ -3148,6 +4357,197 @@ retry: return (error); } +static int +saextget(struct cdev *dev, struct cam_periph *periph, struct sbuf *sb, + struct mtextget *g) +{ + int indent, error; + char tmpstr[80]; + struct sa_softc *softc; + int tmpint; + uint32_t maxio_tmp; + struct ccb_getdev cgd; + + softc = (struct sa_softc *)periph->softc; + + error = 0; + + error = sagetparams_common(dev, periph); + if (error) + goto extget_bailout; + if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) + sagetpos(periph); + + indent = 0; + SASBADDNODE(sb, indent, mtextget); + /* + * Basic CAM peripheral information. + */ + SASBADDVARSTR(sb, indent, periph->periph_name, %s, periph_name, + strlen(periph->periph_name) + 1); + SASBADDUINT(sb, indent, periph->unit_number, %u, unit_number); + xpt_setup_ccb(&cgd.ccb_h, + periph->path, + CAM_PRIORITY_NORMAL); + cgd.ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)&cgd); + if ((cgd.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + g->status = MT_EXT_GET_ERROR; + snprintf(g->error_str, sizeof(g->error_str), + "Error %#x returned for XPT_GDEV_TYPE CCB", + cgd.ccb_h.status); + goto extget_bailout; + } + + cam_strvis(tmpstr, cgd.inq_data.vendor, + sizeof(cgd.inq_data.vendor), sizeof(tmpstr)); + SASBADDVARSTRDESC(sb, indent, tmpstr, %s, vendor, + sizeof(cgd.inq_data.vendor) + 1, "SCSI Vendor ID"); + + cam_strvis(tmpstr, cgd.inq_data.product, + sizeof(cgd.inq_data.product), sizeof(tmpstr)); + SASBADDVARSTRDESC(sb, indent, tmpstr, %s, product, + sizeof(cgd.inq_data.product) + 1, "SCSI Product ID"); + + cam_strvis(tmpstr, cgd.inq_data.revision, + sizeof(cgd.inq_data.revision), sizeof(tmpstr)); + SASBADDVARSTRDESC(sb, indent, tmpstr, %s, revision, + sizeof(cgd.inq_data.revision) + 1, "SCSI Revision"); + + if (cgd.serial_num_len > 0) { + char *tmpstr2; + size_t ts2_len; + int ts2_malloc; + + ts2_len = 0; + + if (cgd.serial_num_len > sizeof(tmpstr)) { + ts2_len = cgd.serial_num_len + 1; + ts2_malloc = 1; + tmpstr2 = malloc(ts2_len, M_SCSISA, M_WAITOK | M_ZERO); + } else { + ts2_len = sizeof(tmpstr); + ts2_malloc = 0; + tmpstr2 = tmpstr; + } + + cam_strvis(tmpstr2, cgd.serial_num, cgd.serial_num_len, + ts2_len); + + SASBADDVARSTRDESC(sb, indent, tmpstr2, %s, serial_num, + (ssize_t)cgd.serial_num_len + 1, "Serial Number"); + if (ts2_malloc != 0) + free(tmpstr2, M_SCSISA); + } else { + /* + * We return a serial_num element in any case, but it will + * be empty if the device has no serial number. + */ + tmpstr[0] = '\0'; + SASBADDVARSTRDESC(sb, indent, tmpstr, %s, serial_num, + (ssize_t)0, "Serial Number"); + } + + SASBADDUINTDESC(sb, indent, softc->maxio, %u, maxio, + "Maximum I/O size allowed by driver and controller"); + + SASBADDUINTDESC(sb, indent, softc->cpi_maxio, %u, cpi_maxio, + "Maximum I/O size reported by controller"); + + SASBADDUINTDESC(sb, indent, softc->max_blk, %u, max_blk, + "Maximum block size supported by tape drive and media"); + + SASBADDUINTDESC(sb, indent, softc->min_blk, %u, min_blk, + "Minimum block size supported by tape drive and media"); + + SASBADDUINTDESC(sb, indent, softc->blk_gran, %u, blk_gran, + "Block granularity supported by tape drive and media"); + + maxio_tmp = min(softc->max_blk, softc->maxio); + + SASBADDUINTDESC(sb, indent, maxio_tmp, %u, max_effective_iosize, + "Maximum possible I/O size"); + + SASBADDINTDESC(sb, indent, softc->flags & SA_FLAG_FIXED ? 1 : 0, %d, + fixed_mode, "Set to 1 for fixed block mode, 0 for variable block"); + + /* + * XXX KDM include SIM, bus, target, LUN? + */ + if (softc->flags & SA_FLAG_COMP_UNSUPP) + tmpint = 0; + else + tmpint = 1; + SASBADDINTDESC(sb, indent, tmpint, %d, compression_supported, + "Set to 1 if compression is supported, 0 if not"); + if (softc->flags & SA_FLAG_COMP_ENABLED) + tmpint = 1; + else + tmpint = 0; + SASBADDINTDESC(sb, indent, tmpint, %d, compression_enabled, + "Set to 1 if compression is enabled, 0 if not"); + SASBADDUINTDESC(sb, indent, softc->comp_algorithm, %u, + compression_algorithm, "Numeric compression algorithm"); + + safillprot(softc, &indent, sb); + + SASBADDUINTDESC(sb, indent, softc->media_blksize, %u, + media_blocksize, "Block size reported by drive or set by user"); + SASBADDINTDESC(sb, indent, (intmax_t)softc->fileno, %jd, + calculated_fileno, "Calculated file number, -1 if unknown"); + SASBADDINTDESC(sb, indent, (intmax_t)softc->blkno, %jd, + calculated_rel_blkno, "Calculated block number relative to file, " + "set to -1 if unknown"); + SASBADDINTDESC(sb, indent, (intmax_t)softc->rep_fileno, %jd, + reported_fileno, "File number reported by drive, -1 if unknown"); + SASBADDINTDESC(sb, indent, (intmax_t)softc->rep_blkno, %jd, + reported_blkno, "Block number relative to BOP/BOT reported by " + "drive, -1 if unknown"); + SASBADDINTDESC(sb, indent, (intmax_t)softc->partition, %jd, + partition, "Current partition number, 0 is the default"); + SASBADDINTDESC(sb, indent, softc->bop, %d, bop, + "Set to 1 if drive is at the beginning of partition/tape, 0 if " + "not, -1 if unknown"); + SASBADDINTDESC(sb, indent, softc->eop, %d, eop, + "Set to 1 if drive is past early warning, 0 if not, -1 if unknown"); + SASBADDINTDESC(sb, indent, softc->bpew, %d, bpew, + "Set to 1 if drive is past programmable early warning, 0 if not, " + "-1 if unknown"); + SASBADDINTDESC(sb, indent, (intmax_t)softc->last_io_resid, %jd, + residual, "Residual for the last I/O"); + /* + * XXX KDM should we send a string with the current driver + * status already decoded instead of a numeric value? + */ + SASBADDINTDESC(sb, indent, softc->dsreg, %d, dsreg, + "Current state of the driver"); + + safilldensitysb(softc, &indent, sb); + + SASBENDNODE(sb, indent, mtextget); + +extget_bailout: + + return (error); +} + +static int +saparamget(struct sa_softc *softc, struct sbuf *sb) +{ + int indent; + + indent = 0; + SASBADDNODE(sb, indent, mtparamget); + SASBADDINTDESC(sb, indent, softc->sili, %d, sili, + "Suppress an error on underlength variable reads"); + SASBADDINTDESC(sb, indent, softc->eot_warn, %d, eot_warn, + "Return an error to warn that end of tape is approaching"); + safillprot(softc, &indent, sb); + SASBENDNODE(sb, indent, mtparamget); + + return (0); +} + static void saprevent(struct cam_periph *periph, int action) { @@ -3207,10 +4607,14 @@ sarewind(struct cam_periph *periph) softc->dsreg = MTIO_DSREG_REST; xpt_release_ccb(ccb); - if (error == 0) - softc->fileno = softc->blkno = (daddr_t) 0; - else + if (error == 0) { + softc->partition = softc->fileno = softc->blkno = (daddr_t) 0; + softc->rep_fileno = softc->rep_blkno = (daddr_t) 0; + } else { softc->fileno = softc->blkno = (daddr_t) -1; + softc->partition = (daddr_t) -1; + softc->rep_fileno = softc->rep_blkno = (daddr_t) -1; + } return (error); } @@ -3262,6 +4666,8 @@ saspace(struct cam_periph *periph, int count, scsi_space_code code) */ if (error) { softc->fileno = softc->blkno = (daddr_t) -1; + softc->rep_blkno = softc->partition = (daddr_t) -1; + softc->rep_fileno = (daddr_t) -1; } else if (code == SS_SETMARKS || code == SS_EOD) { softc->fileno = softc->blkno = (daddr_t) -1; } else if (code == SS_FILEMARKS && softc->fileno != (daddr_t) -1) { @@ -3281,11 +4687,14 @@ saspace(struct cam_periph *periph, int count, scsi_space_code code) } } } + if (error == 0) + sagetpos(periph); + return (error); } static int -sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks) +sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks, int immed) { union ccb *ccb; struct sa_softc *softc; @@ -3304,7 +4713,7 @@ sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks) softc->dsreg = MTIO_DSREG_FMK; /* this *must* not be retried */ scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, - FALSE, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT); + immed, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT); softc->dsreg = MTIO_DSREG_REST; @@ -3322,11 +4731,120 @@ sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks) * Update relative positions (if we're doing that). */ if (error) { - softc->fileno = softc->blkno = (daddr_t) -1; + softc->fileno = softc->blkno = softc->partition = (daddr_t) -1; } else if (softc->fileno != (daddr_t) -1) { softc->fileno += nwm; softc->blkno = 0; } + + /* + * Ask the tape drive for position information. + */ + sagetpos(periph); + + /* + * If we got valid position information, since we just wrote a file + * mark, we know we're at the file mark and block 0 after that + * filemark. + */ + if (softc->rep_fileno != (daddr_t) -1) { + softc->fileno = softc->rep_fileno; + softc->blkno = 0; + } + + return (error); +} + +static int +sagetpos(struct cam_periph *periph) +{ + union ccb *ccb; + struct scsi_tape_position_long_data long_pos; + struct sa_softc *softc = (struct sa_softc *)periph->softc; + int error; + + if (softc->quirks & SA_QUIRK_NO_LONG_POS) { + softc->rep_fileno = (daddr_t) -1; + softc->rep_blkno = (daddr_t) -1; + softc->bop = softc->eop = softc->bpew = -1; + return (EOPNOTSUPP); + } + + bzero(&long_pos, sizeof(long_pos)); + + ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); + scsi_read_position_10(&ccb->csio, + /*retries*/ 1, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*service_action*/ SA_RPOS_LONG_FORM, + /*data_ptr*/ (uint8_t *)&long_pos, + /*length*/ sizeof(long_pos), + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ SCSIOP_TIMEOUT); + + softc->dsreg = MTIO_DSREG_RBSY; + error = cam_periph_runccb(ccb, saerror, 0, SF_QUIET_IR, + softc->device_stats); + softc->dsreg = MTIO_DSREG_REST; + + if (error == 0) { + if (long_pos.flags & SA_RPOS_LONG_MPU) { + /* + * If the drive doesn't know what file mark it is + * on, our calculated filemark isn't going to be + * accurate either. + */ + softc->fileno = (daddr_t) -1; + softc->rep_fileno = (daddr_t) -1; + } else { + softc->fileno = softc->rep_fileno = + scsi_8btou64(long_pos.logical_file_num); + } + + if (long_pos.flags & SA_RPOS_LONG_LONU) { + softc->partition = (daddr_t) -1; + softc->rep_blkno = (daddr_t) -1; + /* + * If the tape drive doesn't know its block + * position, we can't claim to know it either. + */ + softc->blkno = (daddr_t) -1; + } else { + softc->partition = scsi_4btoul(long_pos.partition); + softc->rep_blkno = + scsi_8btou64(long_pos.logical_object_num); + } + if (long_pos.flags & SA_RPOS_LONG_BOP) + softc->bop = 1; + else + softc->bop = 0; + + if (long_pos.flags & SA_RPOS_LONG_EOP) + softc->eop = 1; + else + softc->eop = 0; + + if (long_pos.flags & SA_RPOS_LONG_BPEW) + softc->bpew = 1; + else + softc->bpew = 0; + } else if (error == EINVAL) { + /* + * If this drive returned an invalid-request type error, + * then it likely doesn't support the long form report. + */ + softc->quirks |= SA_QUIRK_NO_LONG_POS; + } + + if (error != 0) { + softc->rep_fileno = softc->rep_blkno = (daddr_t) -1; + softc->partition = (daddr_t) -1; + softc->bop = softc->eop = softc->bpew = -1; + } + + xpt_release_ccb(ccb); + return (error); } @@ -3349,7 +4867,7 @@ sardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr) */ if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) { - error = sawritefilemarks(periph, 0, 0); + error = sawritefilemarks(periph, 0, 0, 0); if (error && error != EACCES) return (error); } @@ -3374,10 +4892,12 @@ sardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr) } static int -sasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr) +sasetpos(struct cam_periph *periph, int hard, struct mtlocate *locate_info) { union ccb *ccb; struct sa_softc *softc; + int locate16; + int immed, cp; int error; /* @@ -3391,19 +4911,100 @@ sasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr) softc = (struct sa_softc *)periph->softc; ccb = cam_periph_getccb(periph, 1); - - scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, - hard, *blkptr, SSD_FULL_SIZE, SPACE_TIMEOUT); + cp = locate_info->flags & MT_LOCATE_FLAG_CHANGE_PART ? 1 : 0; + immed = locate_info->flags & MT_LOCATE_FLAG_IMMED ? 1 : 0; + /* + * Determine whether we have to use LOCATE or LOCATE16. The hard + * bit is only possible with LOCATE, but the new ioctls do not + * allow setting that bit. So we can't get into the situation of + * having the hard bit set with a block address that is larger than + * 32-bits. + */ + if (hard != 0) + locate16 = 0; + else if ((locate_info->dest_type != MT_LOCATE_DEST_OBJECT) + || (locate_info->block_address_mode != MT_LOCATE_BAM_IMPLICIT) + || (locate_info->logical_id > SA_SPOS_MAX_BLK)) + locate16 = 1; + else + locate16 = 0; + + if (locate16 != 0) { + scsi_locate_16(&ccb->csio, + /*retries*/ 1, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*immed*/ immed, + /*cp*/ cp, + /*dest_type*/ locate_info->dest_type, + /*bam*/ locate_info->block_address_mode, + /*partition*/ locate_info->partition, + /*logical_id*/ locate_info->logical_id, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ SPACE_TIMEOUT); + } else { + uint32_t blk_pointer; + + blk_pointer = locate_info->logical_id; + + scsi_locate_10(&ccb->csio, + /*retries*/ 1, + /*cbfcnp*/ sadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*immed*/ immed, + /*cp*/ cp, + /*hard*/ hard, + /*partition*/ locate_info->partition, + /*block_address*/ locate_info->logical_id, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ SPACE_TIMEOUT); + } softc->dsreg = MTIO_DSREG_POS; error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats); softc->dsreg = MTIO_DSREG_REST; xpt_release_ccb(ccb); + /* - * Note relative file && block number position as now unknown. + * We assume the calculated file and block numbers are unknown + * unless we have enough information to populate them. */ softc->fileno = softc->blkno = (daddr_t) -1; + + /* + * If the user requested changing the partition and the request + * succeeded, note the partition. + */ + if ((error == 0) + && (cp != 0)) + softc->partition = locate_info->partition; + else + softc->partition = (daddr_t) -1; + + if (error == 0) { + switch (locate_info->dest_type) { + case MT_LOCATE_DEST_FILE: + /* + * This is the only case where we can reliably + * calculate the file and block numbers. + */ + softc->fileno = locate_info->logical_id; + softc->blkno = 0; + break; + case MT_LOCATE_DEST_OBJECT: + case MT_LOCATE_DEST_SET: + case MT_LOCATE_DEST_EOD: + default: + break; + } + } + + /* + * Ask the drive for current position information. + */ + sagetpos(periph); + return (error); } @@ -3427,10 +5028,11 @@ saretension(struct cam_periph *periph) softc->dsreg = MTIO_DSREG_REST; xpt_release_ccb(ccb); - if (error == 0) - softc->fileno = softc->blkno = (daddr_t) 0; - else - softc->fileno = softc->blkno = (daddr_t) -1; + if (error == 0) { + softc->partition = softc->fileno = softc->blkno = (daddr_t) 0; + sagetpos(periph); + } else + softc->partition = softc->fileno = softc->blkno = (daddr_t) -1; return (error); } @@ -3484,10 +5086,13 @@ saloadunload(struct cam_periph *periph, int load) softc->dsreg = MTIO_DSREG_REST; xpt_release_ccb(ccb); - if (error || load == 0) - softc->fileno = softc->blkno = (daddr_t) -1; - else if (error == 0) - softc->fileno = softc->blkno = (daddr_t) 0; + if (error || load == 0) { + softc->partition = softc->fileno = softc->blkno = (daddr_t) -1; + softc->rep_fileno = softc->rep_blkno = (daddr_t) -1; + } else if (error == 0) { + softc->partition = softc->fileno = softc->blkno = (daddr_t) 0; + sagetpos(periph); + } return (error); } @@ -3516,6 +5121,297 @@ saerase(struct cam_periph *periph, int longerase) return (error); } +/* + * Fill an sbuf with density data in XML format. This particular macro + * works for multi-byte integer fields. + * + * Note that 1 byte fields aren't supported here. The reason is that the + * compiler does not evaluate the sizeof(), and assumes that any of the + * sizes are possible for a given field. So passing in a multi-byte + * field will result in a warning that the assignment makes an integer + * from a pointer without a cast, if there is an assignment in the 1 byte + * case. + */ +#define SAFILLDENSSB(dens_data, sb, indent, field, desc_remain, \ + len_to_go, cur_offset, desc){ \ + size_t cur_field_len; \ + \ + cur_field_len = sizeof(dens_data->field); \ + if (desc_remain < cur_field_len) { \ + len_to_go -= desc_remain; \ + cur_offset += desc_remain; \ + continue; \ + } \ + len_to_go -= cur_field_len; \ + cur_offset += cur_field_len; \ + desc_remain -= cur_field_len; \ + \ + switch (sizeof(dens_data->field)) { \ + case 1: \ + KASSERT(1 == 0, ("Programmer error, invalid 1 byte " \ + "field width for SAFILLDENSFIELD")); \ + break; \ + case 2: \ + SASBADDUINTDESC(sb, indent, \ + scsi_2btoul(dens_data->field), %u, field, desc); \ + break; \ + case 3: \ + SASBADDUINTDESC(sb, indent, \ + scsi_3btoul(dens_data->field), %u, field, desc); \ + break; \ + case 4: \ + SASBADDUINTDESC(sb, indent, \ + scsi_4btoul(dens_data->field), %u, field, desc); \ + break; \ + case 8: \ + SASBADDUINTDESC(sb, indent, \ + (uintmax_t)scsi_8btou64(dens_data->field), %ju, \ + field, desc); \ + break; \ + default: \ + break; \ + } \ +}; +/* + * Fill an sbuf with density data in XML format. This particular macro + * works for strings. + */ +#define SAFILLDENSSBSTR(dens_data, sb, indent, field, desc_remain, \ + len_to_go, cur_offset, desc){ \ + size_t cur_field_len; \ + char tmpstr[32]; \ + \ + cur_field_len = sizeof(dens_data->field); \ + if (desc_remain < cur_field_len) { \ + len_to_go -= desc_remain; \ + cur_offset += desc_remain; \ + continue; \ + } \ + len_to_go -= cur_field_len; \ + cur_offset += cur_field_len; \ + desc_remain -= cur_field_len; \ + \ + cam_strvis(tmpstr, dens_data->field, \ + sizeof(dens_data->field), sizeof(tmpstr)); \ + SASBADDVARSTRDESC(sb, indent, tmpstr, %s, field, \ + strlen(tmpstr) + 1, desc); \ +}; + +/* + * Fill an sbuf with density data descriptors. + */ +static void +safilldenstypesb(struct sbuf *sb, int *indent, uint8_t *buf, int buf_len, + int is_density) +{ + struct scsi_density_hdr *hdr; + uint32_t hdr_len; + int len_to_go, cur_offset; + int length_offset; + int num_reports, need_close; + + /* + * We need at least the header length. Note that this isn't an + * error, not all tape drives will have every data type. + */ + if (buf_len < sizeof(*hdr)) + goto bailout; + + + hdr = (struct scsi_density_hdr *)buf; + hdr_len = scsi_2btoul(hdr->length); + len_to_go = min(buf_len - sizeof(*hdr), hdr_len); + if (is_density) { + length_offset = __offsetof(struct scsi_density_data, + bits_per_mm); + } else { + length_offset = __offsetof(struct scsi_medium_type_data, + num_density_codes); + } + cur_offset = sizeof(*hdr); + + num_reports = 0; + need_close = 0; + + while (len_to_go > length_offset) { + struct scsi_density_data *dens_data; + struct scsi_medium_type_data *type_data; + int desc_remain; + size_t cur_field_len; + + dens_data = NULL; + type_data = NULL; + + if (is_density) { + dens_data =(struct scsi_density_data *)&buf[cur_offset]; + if (dens_data->byte2 & SDD_DLV) + desc_remain = scsi_2btoul(dens_data->length); + else + desc_remain = SDD_DEFAULT_LENGTH - + length_offset; + } else { + type_data = (struct scsi_medium_type_data *) + &buf[cur_offset]; + desc_remain = scsi_2btoul(type_data->length); + } + + len_to_go -= length_offset; + desc_remain = min(desc_remain, len_to_go); + cur_offset += length_offset; + + if (need_close != 0) { + SASBENDNODE(sb, *indent, density_entry); + } + + SASBADDNODENUM(sb, *indent, density_entry, num_reports); + num_reports++; + need_close = 1; + + if (is_density) { + SASBADDUINTDESC(sb, *indent, + dens_data->primary_density_code, %u, + primary_density_code, "Primary Density Code"); + SASBADDUINTDESC(sb, *indent, + dens_data->secondary_density_code, %u, + secondary_density_code, "Secondary Density Code"); + SASBADDUINTDESC(sb, *indent, + dens_data->byte2 & ~SDD_DLV, %#x, density_flags, + "Density Flags"); + + SAFILLDENSSB(dens_data, sb, *indent, bits_per_mm, + desc_remain, len_to_go, cur_offset, "Bits per mm"); + SAFILLDENSSB(dens_data, sb, *indent, media_width, + desc_remain, len_to_go, cur_offset, "Media width"); + SAFILLDENSSB(dens_data, sb, *indent, tracks, + desc_remain, len_to_go, cur_offset, + "Number of Tracks"); + SAFILLDENSSB(dens_data, sb, *indent, capacity, + desc_remain, len_to_go, cur_offset, "Capacity"); + + SAFILLDENSSBSTR(dens_data, sb, *indent, assigning_org, + desc_remain, len_to_go, cur_offset, + "Assigning Organization"); + + SAFILLDENSSBSTR(dens_data, sb, *indent, density_name, + desc_remain, len_to_go, cur_offset, "Density Name"); + + SAFILLDENSSBSTR(dens_data, sb, *indent, description, + desc_remain, len_to_go, cur_offset, "Description"); + } else { + int i; + + SASBADDUINTDESC(sb, *indent, type_data->medium_type, + %u, medium_type, "Medium Type"); + + cur_field_len = + __offsetof(struct scsi_medium_type_data, + media_width) - + __offsetof(struct scsi_medium_type_data, + num_density_codes); + + if (desc_remain < cur_field_len) { + len_to_go -= desc_remain; + cur_offset += desc_remain; + continue; + } + len_to_go -= cur_field_len; + cur_offset += cur_field_len; + desc_remain -= cur_field_len; + + SASBADDINTDESC(sb, *indent, + type_data->num_density_codes, %d, + num_density_codes, "Number of Density Codes"); + SASBADDNODE(sb, *indent, density_code_list); + for (i = 0; i < type_data->num_density_codes; + i++) { + SASBADDUINTDESC(sb, *indent, + type_data->primary_density_codes[i], %u, + density_code, "Density Code"); + } + SASBENDNODE(sb, *indent, density_code_list); + + SAFILLDENSSB(type_data, sb, *indent, media_width, + desc_remain, len_to_go, cur_offset, + "Media width"); + SAFILLDENSSB(type_data, sb, *indent, medium_length, + desc_remain, len_to_go, cur_offset, + "Medium length"); + + /* + * Account for the two reserved bytes. + */ + cur_field_len = sizeof(type_data->reserved2); + if (desc_remain < cur_field_len) { + len_to_go -= desc_remain; + cur_offset += desc_remain; + continue; + } + len_to_go -= cur_field_len; + cur_offset += cur_field_len; + desc_remain -= cur_field_len; + + SAFILLDENSSBSTR(type_data, sb, *indent, assigning_org, + desc_remain, len_to_go, cur_offset, + "Assigning Organization"); + SAFILLDENSSBSTR(type_data, sb, *indent, + medium_type_name, desc_remain, len_to_go, + cur_offset, "Medium type name"); + SAFILLDENSSBSTR(type_data, sb, *indent, description, + desc_remain, len_to_go, cur_offset, "Description"); + + } + } + if (need_close != 0) { + SASBENDNODE(sb, *indent, density_entry); + } + +bailout: + return; +} + +/* + * Fill an sbuf with density data information + */ +static void +safilldensitysb(struct sa_softc *softc, int *indent, struct sbuf *sb) +{ + int i, is_density; + + SASBADDNODE(sb, *indent, mtdensity); + SASBADDUINTDESC(sb, *indent, softc->media_density, %u, media_density, + "Current Medium Density"); + is_density = 0; + for (i = 0; i < SA_DENSITY_TYPES; i++) { + int tmpint; + + if (softc->density_info_valid[i] == 0) + continue; + + SASBADDNODE(sb, *indent, density_report); + if (softc->density_type_bits[i] & SRDS_MEDIUM_TYPE) { + tmpint = 1; + is_density = 0; + } else { + tmpint = 0; + is_density = 1; + } + SASBADDINTDESC(sb, *indent, tmpint, %d, medium_type_report, + "Medium type report"); + + if (softc->density_type_bits[i] & SRDS_MEDIA) + tmpint = 1; + else + tmpint = 0; + SASBADDINTDESC(sb, *indent, tmpint, %d, media_report, + "Media report"); + + safilldenstypesb(sb, indent, softc->density_info[i], + softc->density_info_valid[i], is_density); + SASBENDNODE(sb, *indent, density_report); + } + SASBENDNODE(sb, *indent, mtdensity); +} + #endif /* _KERNEL */ /* @@ -3725,6 +5621,42 @@ scsi_read_position(struct ccb_scsiio *csio, u_int32_t retries, } /* + * Read Tape Position command. + */ +void +scsi_read_position_10(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int service_action, + u_int8_t *data_ptr, u_int32_t length, + u_int32_t sense_len, u_int32_t timeout) +{ + struct scsi_tape_read_position *scmd; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + /*data_ptr*/data_ptr, + /*dxfer_len*/length, + sense_len, + sizeof(*scmd), + timeout); + + + scmd = (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes; + bzero(scmd, sizeof(*scmd)); + scmd->opcode = READ_POSITION; + scmd->byte1 = service_action; + /* + * The length is only currently set (as of SSC4r03) if the extended + * form is specified. The other forms have fixed lengths. + */ + if (service_action == SA_RPOS_EXTENDED_FORM) + scsi_ulto2b(length, scmd->length); +} + +/* * Set Tape Position command. */ void @@ -3744,3 +5676,193 @@ scsi_set_position(struct ccb_scsiio *csio, u_int32_t retries, scmd->byte1 |= SA_SPOS_BT; scsi_ulto4b(blkno, scmd->blkaddr); } + +/* + * XXX KDM figure out how to make a compatibility function. + */ +void +scsi_locate_10(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int immed, int cp, int hard, + int64_t partition, u_int32_t block_address, + int sense_len, u_int32_t timeout) +{ + struct scsi_tape_locate *scmd; + + cam_fill_csio(csio, + retries, + cbfcnp, + CAM_DIR_NONE, + tag_action, + /*data_ptr*/ NULL, + /*dxfer_len*/ 0, + sense_len, + sizeof(*scmd), + timeout); + scmd = (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes; + bzero(scmd, sizeof(*scmd)); + scmd->opcode = LOCATE; + if (immed) + scmd->byte1 |= SA_SPOS_IMMED; + if (cp) + scmd->byte1 |= SA_SPOS_CP; + if (hard) + scmd->byte1 |= SA_SPOS_BT; + scsi_ulto4b(block_address, scmd->blkaddr); + scmd->partition = partition; +} + +void +scsi_locate_16(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int immed, int cp, u_int8_t dest_type, + int bam, int64_t partition, u_int64_t logical_id, + int sense_len, u_int32_t timeout) +{ + + struct scsi_locate_16 *scsi_cmd; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_NONE, + tag_action, + /*data_ptr*/NULL, + /*dxfer_len*/0, + sense_len, + sizeof(*scsi_cmd), + timeout); + + scsi_cmd = (struct scsi_locate_16 *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + scsi_cmd->opcode = LOCATE_16; + if (immed) + scsi_cmd->byte1 |= SA_LC_IMMEDIATE; + if (cp) + scsi_cmd->byte1 |= SA_LC_CP; + scsi_cmd->byte1 |= (dest_type << SA_LC_DEST_TYPE_SHIFT); + + scsi_cmd->byte2 |= bam; + scsi_cmd->partition = partition; + scsi_u64to8b(logical_id, scsi_cmd->logical_id); +} + +void +scsi_report_density_support(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int media, int medium_type, + u_int8_t *data_ptr, u_int32_t length, + u_int32_t sense_len, u_int32_t timeout) +{ + struct scsi_report_density_support *scsi_cmd; + + scsi_cmd =(struct scsi_report_density_support *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = REPORT_DENSITY_SUPPORT; + if (media != 0) + scsi_cmd->byte1 |= SRDS_MEDIA; + if (medium_type != 0) + scsi_cmd->byte1 |= SRDS_MEDIUM_TYPE; + + scsi_ulto2b(length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + /*data_ptr*/data_ptr, + /*dxfer_len*/length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_set_capacity(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int byte1, u_int32_t proportion, + u_int32_t sense_len, u_int32_t timeout) +{ + struct scsi_set_capacity *scsi_cmd; + + scsi_cmd = (struct scsi_set_capacity *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = SET_CAPACITY; + + scsi_cmd->byte1 = byte1; + scsi_ulto2b(proportion, scsi_cmd->cap_proportion); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_NONE, + tag_action, + /*data_ptr*/NULL, + /*dxfer_len*/0, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_format_medium(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int byte1, int byte2, + u_int8_t *data_ptr, u_int32_t dxfer_len, + u_int32_t sense_len, u_int32_t timeout) +{ + struct scsi_format_medium *scsi_cmd; + + scsi_cmd = (struct scsi_format_medium*)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = FORMAT_MEDIUM; + + scsi_cmd->byte1 = byte1; + scsi_cmd->byte2 = byte2; + + scsi_ulto2b(dxfer_len, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/(dxfer_len > 0) ? CAM_DIR_OUT : CAM_DIR_NONE, + tag_action, + /*data_ptr*/ data_ptr, + /*dxfer_len*/ dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_allow_overwrite(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int allow_overwrite, int partition, + u_int64_t logical_id, u_int32_t sense_len, u_int32_t timeout) +{ + struct scsi_allow_overwrite *scsi_cmd; + + scsi_cmd = (struct scsi_allow_overwrite *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = ALLOW_OVERWRITE; + + scsi_cmd->allow_overwrite = allow_overwrite; + scsi_cmd->partition = partition; + scsi_u64to8b(logical_id, scsi_cmd->logical_id); + + cam_fill_csio(csio, + retries, + cbfcnp, + CAM_DIR_NONE, + tag_action, + /*data_ptr*/ NULL, + /*dxfer_len*/ 0, + sense_len, + sizeof(*scsi_cmd), + timeout); +} diff --git a/sys/cam/scsi/scsi_sa.h b/sys/cam/scsi/scsi_sa.h index b1cee79..6ecef65 100644 --- a/sys/cam/scsi/scsi_sa.h +++ b/sys/cam/scsi/scsi_sa.h @@ -3,6 +3,7 @@ * SCSI Sequential Access Peripheral driver for CAM. * * Copyright (c) 1999, 2000 Matthew Jacob + * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -142,6 +143,53 @@ struct scsi_erase }; /* + * Set tape capacity. + */ +struct scsi_set_capacity +{ + u_int8_t opcode; + u_int8_t byte1; +#define SA_SSC_IMMED 0x01 + u_int8_t reserved; + u_int8_t cap_proportion[2]; + u_int8_t control; +}; + +/* + * Format tape media. The CDB opcode is the same as the disk-specific + * FORMAT UNIT command, but the fields are different inside the CDB. Thus + * the reason for a separate definition here. + */ +struct scsi_format_medium +{ + u_int8_t opcode; + u_int8_t byte1; +#define SFM_IMMED 0x01 +#define SFM_VERIFY 0x02 + u_int8_t byte2; +#define SFM_FORMAT_DEFAULT 0x00 +#define SFM_FORMAT_PARTITION 0x01 +#define SFM_FORMAT_DEF_PART 0x02 +#define SFM_FORMAT_MASK 0x0f + u_int8_t length[2]; + u_int8_t control; +}; + +struct scsi_allow_overwrite +{ + u_int8_t opcode; + u_int8_t reserved1; + u_int8_t allow_overwrite; +#define SAO_ALLOW_OVERWRITE_DISABLED 0x00 +#define SAO_ALLOW_OVERWRITE_CUR_POS 0x01 +#define SAO_ALLOW_OVERWRITE_FORMAT 0x02 + u_int8_t partition; + u_int8_t logical_id[8]; + u_int8_t reserved2[3]; + u_int8_t control; +}; + +/* * Dev specific mode page masks. */ #define SMH_SA_WP 0x80 @@ -180,11 +228,18 @@ struct scsi_dev_conf_page { #define SA_BIS 0x40 /* block identifiers supported */ #define SA_RSMK 0x20 /* report setmarks */ #define SA_AVC 0x10 /* automatic velocity control */ -#define SA_SOCF_MASK 0xc0 /* stop on consecutive formats */ -#define SA_RBO 0x20 /* recover buffer order */ -#define SA_REW 0x10 /* report early warning */ +#define SA_SOCF_MASK 0x0c /* stop on consecutive formats */ +#define SA_RBO 0x02 /* recover buffer order */ +#define SA_REW 0x01 /* report early warning */ u_int8_t gap_size; u_int8_t byte10; +/* from SCSI-3: SSC-4 Working draft (2/14) 8.3.3 */ +#define SA_EOD_DEF_MASK 0xe0 /* EOD defined */ +#define SA_EEG 0x10 /* Enable EOD Generation */ +#define SA_SEW 0x08 /* Synchronize at Early Warning */ +#define SA_SOFT_WP 0x04 /* Software Write Protect */ +#define SA_BAML 0x02 /* Block Address Mode Lock */ +#define SA_BAM 0x01 /* Block Address Mode */ u_int8_t ew_bufsize[3]; u_int8_t sel_comp_alg; #define SA_COMP_NONE 0x00 @@ -221,10 +276,78 @@ typedef union { struct scsi_data_compression_page dcomp; } sa_comp_t; +/* + * Control Data Protection subpage. This is as defined in SSC3r03. + */ +struct scsi_control_data_prot_subpage { + uint8_t page_code; +#define SA_CTRL_DP_PAGE_CODE 0x0a + uint8_t subpage_code; +#define SA_CTRL_DP_SUBPAGE_CODE 0xf0 + uint8_t length[2]; + uint8_t prot_method; +#define SA_CTRL_DP_NO_LBP 0x00 +#define SA_CTRL_DP_REED_SOLOMON 0x01 +#define SA_CTRL_DP_METHOD_MAX 0xff + uint8_t pi_length; +#define SA_CTRL_DP_PI_LENGTH_MASK 0x3f +#define SA_CTRL_DP_RS_LENGTH 4 + uint8_t prot_bits; +#define SA_CTRL_DP_LBP_W 0x80 +#define SA_CTRL_DP_LBP_R 0x40 +#define SA_CTRL_DP_RBDP 0x20 + uint8_t reserved[]; +}; + +/* + * This is the Read/Write Control mode page used on IBM Enterprise Tape + * Drives. They are known as 3592, TS, or Jaguar drives. The SCSI inquiry + * data will show a Product ID "03592XXX", where XXX is 'J1A', 'E05' (TS1120), + * 'E06' (TS1130), 'E07' (TS1140) or 'E08' (TS1150). + * + * This page definition is current as of the 3592 SCSI Reference v6, + * released on December 16th, 2014. + */ +struct scsi_tape_ibm_rw_control { + uint8_t page_code; +#define SA_IBM_RW_CTRL_PAGE_CODE 0x25 + uint8_t page_length; + uint8_t ignore_seq_checks; +#define SA_IBM_RW_CTRL_LOC_IGNORE_SEQ 0x04 +#define SA_IBM_RW_CTRL_SPC_BLK_IGNORE_SEQ 0x02 +#define SA_IBM_RW_CTRL_SPC_FM_IGNORE_SEQ 0x01 + uint8_t ignore_data_checks; +#define SA_IBM_RW_CTRL_LOC_IGNORE_DATA 0x04 +#define SA_IBM_RW_CTRL_SPC_BLK_IGNORE_DATA 0x02 +#define SA_IBM_RW_CTRL_SPC_FM_IGNORE_DATA 0x01 + uint8_t reserved1; + uint8_t leop_method; +#define SA_IBM_RW_CTRL_LEOP_DEFAULT 0x00 +#define SA_IBM_RW_CTRL_LEOP_MAX_CAP 0x01 +#define SA_IBM_RW_CTRL_LEOP_CONST_CAP 0x02 + uint8_t leop_ew[2]; + uint8_t byte8; +#define SA_IBM_RW_CTRL_DISABLE_FASTSYNC 0x80 +#define SA_IBM_RW_CTRL_DISABLE_SKIPSYNC 0x40 +#define SA_IBM_RW_CTRL_DISABLE_CROSS_EOD 0x08 +#define SA_IBM_RW_CTRL_DISABLE_CROSS_PERM_ERR 0x04 +#define SA_IBM_RW_CTRL_REPORT_SEG_EW 0x02 +#define SA_IBM_RW_CTRL_REPORT_HOUSEKEEPING_ERR 0x01 + uint8_t default_write_dens_bop_0; + uint8_t pending_write_dens_bop_0; + uint8_t reserved2[21]; +}; + struct scsi_tape_read_position { u_int8_t opcode; /* READ_POSITION */ u_int8_t byte1; /* set LSB to read hardware block pos */ - u_int8_t reserved[8]; +#define SA_RPOS_SHORT_FORM 0x00 +#define SA_RPOS_SHORT_VENDOR 0x01 +#define SA_RPOS_LONG_FORM 0x06 +#define SA_RPOS_EXTENDED_FORM 0x08 + u_int8_t reserved[5]; + u_int8_t length[2]; + u_int8_t control; }; struct scsi_tape_position_data { /* Short Form */ @@ -235,6 +358,7 @@ struct scsi_tape_position_data { /* Short Form */ #define SA_RPOS_BYCU 0x10 /* Byte Count Unknown (SCSI3) */ #define SA_RPOS_BPU 0x04 /* Block Position Unknown */ #define SA_RPOS_PERR 0x02 /* Position Error (SCSI3) */ +#define SA_RPOS_BPEW 0x01 /* Beyond Programmable Early Warning */ #define SA_RPOS_UNCERTAIN SA_RPOS_BPU u_int8_t partition; u_int8_t reserved[2]; @@ -245,6 +369,38 @@ struct scsi_tape_position_data { /* Short Form */ u_int8_t nbufbyte[4]; }; +struct scsi_tape_position_long_data { + u_int8_t flags; +#define SA_RPOS_LONG_BOP 0x80 /* Beginning of Partition */ +#define SA_RPOS_LONG_EOP 0x40 /* End of Partition */ +#define SA_RPOS_LONG_MPU 0x08 /* Mark Position Unknown */ +#define SA_RPOS_LONG_LONU 0x04 /* Logical Object Number Unknown */ +#define SA_RPOS_LONG_BPEW 0x01 /* Beyond Programmable Early Warning */ + u_int8_t reserved[3]; + u_int8_t partition[4]; + u_int8_t logical_object_num[8]; + u_int8_t logical_file_num[8]; + u_int8_t set_id[8]; +}; + +struct scsi_tape_position_ext_data { + u_int8_t flags; +#define SA_RPOS_EXT_BOP 0x80 /* Beginning of Partition */ +#define SA_RPOS_EXT_EOP 0x40 /* End of Partition */ +#define SA_RPOS_EXT_LOCU 0x20 /* Logical Object Count Unknown */ +#define SA_RPOS_EXT_BYCU 0x10 /* Byte Count Unknown */ +#define SA_RPOS_EXT_LOLU 0x04 /* Logical Object Location Unknown */ +#define SA_RPOS_EXT_PERR 0x02 /* Position Error */ +#define SA_RPOS_EXT_BPEW 0x01 /* Beyond Programmable Early Warning */ + u_int8_t partition; + u_int8_t length[2]; + u_int8_t reserved; + u_int8_t num_objects[3]; + u_int8_t first_object[8]; + u_int8_t last_object[8]; + u_int8_t bytes_in_buffer[8]; +}; + struct scsi_tape_locate { u_int8_t opcode; u_int8_t byte1; @@ -253,18 +409,521 @@ struct scsi_tape_locate { #define SA_SPOS_BT 0x04 u_int8_t reserved1; u_int8_t blkaddr[4]; +#define SA_SPOS_MAX_BLK 0xffffffff u_int8_t reserved2; u_int8_t partition; u_int8_t control; }; +struct scsi_locate_16 { + u_int8_t opcode; + u_int8_t byte1; +#define SA_LC_IMMEDIATE 0x01 +#define SA_LC_CP 0x02 +#define SA_LC_DEST_TYPE_MASK 0x38 +#define SA_LC_DEST_TYPE_SHIFT 3 +#define SA_LC_DEST_OBJECT 0x00 +#define SA_LC_DEST_FILE 0x01 +#define SA_LC_DEST_SET 0x02 +#define SA_LC_DEST_EOD 0x03 + u_int8_t byte2; +#define SA_LC_BAM_IMPLICIT 0x00 +#define SA_LC_BAM_EXPLICIT 0x01 + u_int8_t partition; + u_int8_t logical_id[8]; + u_int8_t reserved[3]; + u_int8_t control; +}; + +struct scsi_report_density_support { + u_int8_t opcode; + u_int8_t byte1; +#define SRDS_MEDIA 0x01 +#define SRDS_MEDIUM_TYPE 0x02 + u_int8_t reserved[5]; + u_int8_t length[2]; +#define SRDS_MAX_LENGTH 0xffff + u_int8_t control; +}; + +struct scsi_density_hdr { + u_int8_t length[2]; + u_int8_t reserved[2]; + u_int8_t descriptor[]; +}; + +struct scsi_density_data { + u_int8_t primary_density_code; + u_int8_t secondary_density_code; + u_int8_t byte2; +#define SDD_DLV 0x01 +#define SDD_DEFLT 0x20 +#define SDD_DUP 0x40 +#define SDD_WRTOK 0x80 + u_int8_t length[2]; +#define SDD_DEFAULT_LENGTH 52 + u_int8_t bits_per_mm[3]; + u_int8_t media_width[2]; + u_int8_t tracks[2]; + u_int8_t capacity[4]; + u_int8_t assigning_org[8]; + u_int8_t density_name[8]; + u_int8_t description[20]; +}; + +struct scsi_medium_type_data { + u_int8_t medium_type; + u_int8_t reserved1; + u_int8_t length[2]; +#define SMTD_DEFAULT_LENGTH 52 + u_int8_t num_density_codes; + u_int8_t primary_density_codes[9]; + u_int8_t media_width[2]; + u_int8_t medium_length[2]; + u_int8_t reserved2[2]; + u_int8_t assigning_org[8]; + u_int8_t medium_type_name[8]; + u_int8_t description[20]; +}; + +/* + * Security Protocol Specific values for the Tape Data Encryption protocol + * (0x20) used with SECURITY PROTOCOL IN. See below for values used with + * SECURITY PROTOCOL OUT. Current as of SSC4r03. + */ +#define TDE_IN_SUPPORT_PAGE 0x0000 +#define TDE_OUT_SUPPORT_PAGE 0x0001 +#define TDE_DATA_ENC_CAP_PAGE 0x0010 +#define TDE_SUPPORTED_KEY_FORMATS_PAGE 0x0011 +#define TDE_DATA_ENC_MAN_CAP_PAGE 0x0012 +#define TDE_DATA_ENC_STATUS_PAGE 0x0020 +#define TDE_NEXT_BLOCK_ENC_STATUS_PAGE 0x0021 +#define TDE_GET_ENC_MAN_ATTR_PAGE 0x0022 +#define TDE_RANDOM_NUM_PAGE 0x0030 +#define TDE_KEY_WRAP_PK_PAGE 0x0031 + +/* + * Tape Data Encryption protocol pages used with SECURITY PROTOCOL IN and + * SECURITY PROTOCOL OUT. + */ +/* + * Tape Data Encryption In Support page (0x0000). + */ +struct tde_in_support_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t page_codes[]; +}; + +/* + * Tape Data Encryption Out Support page (0x0001). + */ +struct tde_out_support_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t page_codes[]; +}; + +/* + * Logical block encryption algorithm descriptor. This is reported in the + * Data Encryption Capabilities page. + */ +struct tde_block_enc_alg_desc { + uint8_t alg_index; + uint8_t reserved1; + uint8_t desc_length[2]; + uint8_t byte4; +#define TDE_BEA_AVFMV 0x80 +#define TDE_BEA_SDK_C 0x40 +#define TDE_BEA_MAC_C 0x20 +#define TDE_BEA_DELB_C 0x10 +#define TDE_BEA_DECRYPT_C_MASK 0x0c +#define TDE_BEA_DECRYPT_C_EXT 0x0c +#define TDE_BEA_DECRYPT_C_HARD 0x08 +#define TDE_BEA_DECRYPT_C_SOFT 0x04 +#define TDE_BEA_DECRYPT_C_NO_CAP 0x00 +#define TDE_BEA_ENCRYPT_C_MASK 0x03 +#define TDE_BEA_ENCRYPT_C_EXT 0x03 +#define TDE_BEA_ENCRYPT_C_HARD 0x02 +#define TDE_BEA_ENCRYPT_C_SOFT 0x01 +#define TDE_BEA_ENCRYPT_C_NO_CAP 0x00 + uint8_t byte5; +#define TDE_BEA_AVFCLP_MASK 0xc0 +#define TDE_BEA_AVFCLP_VALID 0x80 +#define TDE_BEA_AVFCLP_NOT_VALID 0x40 +#define TDE_BEA_AVFCLP_NOT_APP 0x00 +#define TDE_BEA_NONCE_C_MASK 0x30 +#define TDE_BEA_NONCE_C_SUPPORTED 0x30 +#define TDE_BEA_NONCE_C_PROVIDED 0x20 +#define TDE_BEA_NONCE_C_GENERATED 0x10 +#define TDE_BEA_NONCE_C_NOT_REQUIRED 0x00 +#define TDE_BEA_KADF_C 0x08 +#define TDE_BEA_VCELB_C 0x04 +#define TDE_BEA_UKADF 0x02 +#define TDE_BEA_AKADF 0x01 + uint8_t max_unauth_key_bytes[2]; + uint8_t max_auth_key_bytes[2]; + uint8_t lbe_key_size[2]; + uint8_t byte12; +#define TDE_BEA_DKAD_C_MASK 0xc0 +#define TDE_BEA_DKAD_C_CAPABLE 0xc0 +#define TDE_BEA_DKAD_C_NOT_ALLOWED 0x80 +#define TDE_BEA_DKAD_C_REQUIRED 0x40 +#define TDE_BEA_EEMC_C_MASK 0x30 +#define TDE_BEA_EEMC_C_ALLOWED 0x20 +#define TDE_BEA_EEMC_C_NOT_ALLOWED 0x10 +#define TDE_BEA_EEMC_C_NOT_SPECIFIED 0x00 + /* + * Raw Decryption Mode Control Capabilities (RDMC_C) field. The + * descriptions are too complex to represent as a simple name. + */ +#define TDE_BEA_RDMC_C_MASK 0x0e +#define TDE_BEA_RDMC_C_MODE_7 0x0e +#define TDE_BEA_RDMC_C_MODE_6 0x0c +#define TDE_BEA_RDMC_C_MODE_5 0x0a +#define TDE_BEA_RDMC_C_MODE_4 0x08 +#define TDE_BEA_RDMC_C_MODE_1 0x02 +#define TDE_BEA_EAREM 0x01 + uint8_t byte13; +#define TDE_BEA_MAX_EEDKS_MASK 0x0f + uint8_t msdk_count[2]; + uint8_t max_eedk_size[2]; + uint8_t reserved2[2]; + uint8_t security_algo_code[4]; +}; + +/* + * Data Encryption Capabilities page (0x0010). + */ +struct tde_data_enc_cap_page { + uint8_t page_code[2]; + uint8_t page_length; + uint8_t byte4; +#define DATA_ENC_CAP_EXTDECC_MASK 0x0c +#define DATA_ENC_CAP_EXTDECC_NOT_REPORTED 0x00 +#define DATA_ENC_CAP_EXTDECC_NOT_CAPABLE 0x04 +#define DATA_ENC_CAP_EXTDECC_CAPABLE 0x08 +#define DATA_ENC_CAP_CFG_P_MASK 0x03 +#define DATA_ENC_CAP_CFG_P_NOT_REPORTED 0x00 +#define DATA_ENC_CAP_CFG_P_ALLOWED 0x01 +#define DATA_ENC_CAP_CFG_P_NOT_ALLOWED 0x02 + uint8_t reserved[15]; + struct tde_block_enc_alg_desc alg_descs[]; +}; + +/* + * Tape Data Encryption Supported Key Formats page (0x0011). + */ +struct tde_supported_key_formats_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t key_formats_list[]; +}; + +/* + * Tape Data Encryption Management Capabilities page (0x0012). + */ +struct tde_data_enc_man_cap_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t byte4; +#define TDE_DEMC_LOCK_C 0x01 + uint8_t byte5; +#define TDE_DEMC_CKOD_C 0x04 +#define TDE_DEMC_CKORP_C 0x02 +#define TDE_DEMC_CKORL_C 0x01 + uint8_t reserved1; + uint8_t byte7; +#define TDE_DEMC_AITN_C 0x04 +#define TDE_DEMC_LOCAL_C 0x02 +#define TDE_DEMC_PUBLIC_C 0x01 + uint8_t reserved2[8]; +}; + +/* + * Tape Data Encryption Status Page (0x0020). + */ +struct tde_data_enc_status_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t scope; +#define TDE_DES_IT_NEXUS_SCOPE_MASK 0xe0 +#define TDE_DES_LBE_SCOPE_MASK 0x07 + uint8_t encryption_mode; + uint8_t decryption_mode; + uint8_t algo_index; + uint8_t key_instance_counter[4]; + uint8_t byte12; +#define TDE_DES_PARAM_CTRL_MASK 0x70 +#define TDE_DES_PARAM_CTRL_MGMT 0x40 +#define TDE_DES_PARAM_CTRL_CHANGER 0x30 +#define TDE_DES_PARAM_CTRL_DRIVE 0x20 +#define TDE_DES_PARAM_CTRL_EXT 0x10 +#define TDE_DES_PARAM_CTRL_NOT_REPORTED 0x00 +#define TDE_DES_VCELB 0x08 +#define TDE_DES_CEEMS_MASK 0x06 +#define TDE_DES_RDMD 0x01 + uint8_t enc_params_kad_format; + uint8_t asdk_count[2]; + uint8_t reserved[8]; + uint8_t key_assoc_data_desc[]; +}; + +/* + * Tape Data Encryption Next Block Encryption Status page (0x0021). + */ +struct tde_next_block_enc_status_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t logical_obj_number[8]; + uint8_t status; +#define TDE_NBES_COMP_STATUS_MASK 0xf0 +#define TDE_NBES_COMP_INCAPABLE 0x00 +#define TDE_NBES_COMP_NOT_YET 0x10 +#define TDE_NBES_COMP_NOT_A_BLOCK 0x20 +#define TDE_NBES_COMP_NOT_COMPRESSED 0x30 +#define TDE_NBES_COMP_COMPRESSED 0x40 +#define TDE_NBES_ENC_STATUS_MASK 0x0f +#define TDE_NBES_ENC_INCAPABLE 0x00 +#define TDE_NBES_ENC_NOT_YET 0x01 +#define TDE_NBES_ENC_NOT_A_BLOCK 0x02 +#define TDE_NBES_ENC_NOT_ENCRYPTED 0x03 +#define TDE_NBES_ENC_ALG_NOT_SUPPORTED 0x04 +#define TDE_NBES_ENC_SUPPORTED_ALG 0x05 +#define TDE_NBES_ENC_NO_KEY 0x06 + uint8_t algo_index; + uint8_t byte14; +#define TDE_NBES_EMES 0x02 +#define TDE_NBES_RDMDS 0x01 + uint8_t next_block_kad_format; + uint8_t key_assoc_data_desc[]; +}; + +/* + * Tape Data Encryption Get Encryption Management Attributes page (0x0022). + */ +struct tde_get_enc_man_attr_page { + uint8_t page_code[2]; + uint8_t reserved[3]; + uint8_t byte5; +#define TDE_GEMA_CAOD 0x01 + uint8_t page_length[2]; + uint8_t enc_mgmt_attr_desc[]; +}; + +/* + * Tape Data Encryption Random Number page (0x0030). + */ +struct tde_random_num_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t random_number[32]; +}; + +/* + * Tape Data Encryption Device Server Key Wrapping Public Key page (0x0031). + */ +struct tde_key_wrap_pk_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t public_key_type[4]; + uint8_t public_key_format[4]; + uint8_t public_key_length[2]; + uint8_t public_key[]; +}; + +/* + * Security Protocol Specific values for the Tape Data Encryption protocol + * (0x20) used with SECURITY PROTOCOL OUT. See above for values used with + * SECURITY PROTOCOL IN. Current as of SSCr03. + */ +#define TDE_SET_DATA_ENC_PAGE 0x0010 +#define TDE_SA_ENCAP_PAGE 0x0011 +#define TDE_SET_ENC_MGMT_ATTR_PAGE 0x0022 + +/* + * Tape Data Encryption Set Data Encryption page (0x0010). + */ +struct tde_set_data_enc_page { + uint8_t page_code[2]; + uint8_t page_length[2]; + uint8_t byte4; +#define TDE_SDE_SCOPE_MASK 0xe0 +#define TDE_SDE_SCOPE_ALL_IT_NEXUS 0x80 +#define TDE_SDE_SCOPE_LOCAL 0x40 +#define TDE_SDE_SCOPE_PUBLIC 0x00 +#define TDE_SDE_LOCK 0x01 + uint8_t byte5; +#define TDE_SDE_CEEM_MASK 0xc0 +#define TDE_SDE_CEEM_ENCRYPT 0xc0 +#define TDE_SDE_CEEM_EXTERNAL 0x80 +#define TDE_SDE_CEEM_NO_CHECK 0x40 +#define TDE_SDE_RDMC_MASK 0x30 +#define TDE_SDE_RDMC_DISABLED 0x30 +#define TDE_SDE_RDMC_ENABLED 0x20 +#define TDE_SDE_RDMC_DEFAULT 0x00 +#define TDE_SDE_SDK 0x08 +#define TDE_SDE_CKOD 0x04 +#define TDE_SDE_CKORP 0x02 +#define TDE_SDE_CKORL 0x01 + uint8_t encryption_mode; +#define TDE_SDE_ENC_MODE_DISABLE 0x00 +#define TDE_SDE_ENC_MODE_EXTERNAL 0x01 +#define TDE_SDE_ENC_MODE_ENCRYPT 0x02 + uint8_t decryption_mode; +#define TDE_SDE_DEC_MODE_DISABLE 0x00 +#define TDE_SDE_DEC_MODE_RAW 0x01 +#define TDE_SDE_DEC_MODE_DECRYPT 0x02 +#define TDE_SDE_DEC_MODE_MIXED 0x03 + uint8_t algo_index; + uint8_t lbe_key_format; +#define TDE_SDE_KEY_PLAINTEXT 0x00 +#define TDE_SDE_KEY_VENDOR_SPEC 0x01 +#define TDE_SDE_KEY_PUBLIC_WRAP 0x02 +#define TDE_SDE_KEY_ESP_SCSI 0x03 + uint8_t kad_format; +#define TDE_SDE_KAD_ASCII 0x02 +#define TDE_SDE_KAD_BINARY 0x01 +#define TDE_SDE_KAD_UNSPECIFIED 0x00 + uint8_t reserved[7]; + uint8_t lbe_key_length[2]; + uint8_t lbe_key[]; +}; + +/* + * Used for the Vendor Specific key format (0x01). + */ +struct tde_key_format_vendor { + uint8_t t10_vendor_id[8]; + uint8_t vendor_key[]; +}; + +/* + * Used for the public key wrapped format (0x02). + */ +struct tde_key_format_public_wrap { + uint8_t parameter_set[2]; +#define TDE_PARAM_SET_RSA2048 0x0000 +#define TDE_PARAM_SET_ECC521 0x0010 + uint8_t label_length[2]; + uint8_t label[]; +}; + +/* + * Tape Data Encryption SA Encapsulation page (0x0011). + */ +struct tde_sa_encap_page { + uint8_t page_code[2]; + uint8_t data_desc[]; +}; + +/* + * Tape Data Encryption Set Encryption Management Attributes page (0x0022). + */ +struct tde_set_enc_mgmt_attr_page { + uint8_t page_code[2]; + uint8_t reserved[3]; + uint8_t byte5; +#define TDE_SEMA_CAOD 0x01 + uint8_t page_length[2]; + uint8_t attr_desc[]; +}; + +/* + * Tape Data Encryption descriptor format. + * SSC4r03 Section 8.5.4.2.1 Table 197 + */ +struct tde_data_enc_desc { + uint8_t key_desc_type; +#define TDE_KEY_DESC_WK_KAD 0x04 +#define TDE_KEY_DESC_M_KAD 0x03 +#define TDE_KEY_DESC_NONCE_VALUE 0x02 +#define TDE_KEY_DESC_A_KAD 0x01 +#define TDE_KEY_DESC_U_KAD 0x00 + uint8_t byte2; +#define TDE_KEY_DESC_AUTH_MASK 0x07 +#define TDE_KEY_DESC_AUTH_FAILED 0x04 +#define TDE_KEY_DESC_AUTH_SUCCESS 0x03 +#define TDE_KEY_DESC_AUTH_NO_ATTEMPT 0x02 +#define TDE_KEY_DESC_AUTH_U_KAD 0x01 + uint8_t key_desc_length[2]; + uint8_t key_desc[]; +}; + +/* + * Wrapped Key descriptor format. + * SSC4r03 Section 8.5.4.3.1 Table 200 + */ +struct tde_wrapped_key_desc { + uint8_t wrapped_key_type; +#define TDE_WRAP_KEY_DESC_LENGTH 0x04 +#define TDE_WRAP_KEY_DESC_IDENT 0x03 +#define TDE_WRAP_KEY_DESC_INFO 0x02 +#define TDE_WRAP_KEY_DESC_ENTITY_ID 0x01 +#define TDE_WRAP_KEY_DESC_DEVICE_ID 0x00 + uint8_t reserved; + uint8_t wrapped_desc_length[2]; + uint8_t wrapped_desc[]; +}; + +/* + * Encryption management attributes descriptor format. + * SSC4r03 Section 8.5.4.4.1 Table 202 + */ +struct tde_enc_mgmt_attr_desc { + uint8_t enc_mgmt_attr_type[2]; +#define TDE_EMAD_DESIRED_KEY_MGR_OP 0x0000 +#define TDE_EMAD_LOG_BLOCK_ENC_KEY_CRIT 0x0001 +#define TDE_EMAD_LOG_BLOCK_ENC_KEY_WRAP 0x0002 + uint8_t reserved; + uint8_t byte2; +#define TDE_EMAD_CRIT 0x80 + uint8_t attr_length[2]; + uint8_t attributes[]; +#define TDE_EMAD_DESIRED_KEY_CREATE 0x0001 +#define TDE_EMAD_DESIRED_KEY_RESOLVE 0x0002 +}; + +/* + * Logical block encryption key selection criteria descriptor format. + * SSC4r03 Section 8.5.4.4.3.1 Table 206 + */ +struct tde_lb_enc_key_sel_desc { + uint8_t lbe_key_sel_crit_type[2]; + /* + * The CRIT bit is the top bit of the first byte of the type. + */ +#define TDE_LBE_KEY_SEL_CRIT 0x80 +#define TDE_LBE_KEY_SEL_ALGO 0x0001 +#define TDE_LBE_KEY_SEL_ID 0x0002 + uint8_t lbe_key_sel_crit_length[2]; + uint8_t lbe_key_sel_crit[]; +}; + +/* + * Logical block encryption key wrapping attribute descriptor format. + * SSC4r03 Section 8.5.4.4.4.1 Table 209 + */ +struct tde_lb_enc_key_wrap_desc { + uint8_t lbe_key_wrap_type[2]; + /* + * The CRIT bit is the top bit of the first byte of the type. + */ +#define TDE_LBE_KEY_WRAP_CRIT 0x80 +#define TDE_LBE_KEY_WRAP_KEKS 0x0001 + uint8_t lbe_key_wrap_length[2]; + uint8_t lbe_key_wrap_attr[]; +}; + /* * Opcodes */ #define REWIND 0x01 +#define FORMAT_MEDIUM 0x04 #define READ_BLOCK_LIMITS 0x05 #define SA_READ 0x08 #define SA_WRITE 0x0A +#define SET_CAPACITY 0x0B #define WRITE_FILEMARKS 0x10 #define SPACE 0x11 #define RESERVE_UNIT 0x16 @@ -273,6 +932,9 @@ struct scsi_tape_locate { #define LOAD_UNLOAD 0x1B #define LOCATE 0x2B #define READ_POSITION 0x34 +#define REPORT_DENSITY_SUPPORT 0x44 +#define ALLOW_OVERWRITE 0x82 +#define LOCATE_16 0x92 /* * Tape specific density codes- only enough of them here to recognize @@ -352,11 +1014,55 @@ void scsi_read_position(struct ccb_scsiio *csio, u_int32_t retries, u_int8_t tag_action, int hardsoft, struct scsi_tape_position_data *sbp, u_int8_t sense_len, u_int32_t timeout); +void scsi_read_position_10(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int service_action, + u_int8_t *data_ptr, u_int32_t length, + u_int32_t sense_len, u_int32_t timeout); void scsi_set_position(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int hardsoft, u_int32_t blkno, u_int8_t sense_len, u_int32_t timeout); + +void scsi_locate_10(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int immed, int cp, int hard, + int64_t partition, u_int32_t block_address, + int sense_len, u_int32_t timeout); + +void scsi_locate_16(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int immed, int cp, + u_int8_t dest_type, int bam, int64_t partition, + u_int64_t logical_id, int sense_len, + u_int32_t timeout); + +void scsi_report_density_support(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + u_int8_t tag_action, int media, + int medium_type, u_int8_t *data_ptr, + u_int32_t length, u_int32_t sense_len, + u_int32_t timeout); + +void scsi_set_capacity(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int byte1, u_int32_t proportion, + u_int32_t sense_len, u_int32_t timeout); + +void scsi_format_medium(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int byte1, int byte2, + u_int8_t *data_ptr, u_int32_t length, + u_int32_t sense_len, u_int32_t timeout); + +void scsi_allow_overwrite(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int allow_overwrite, + int partition, u_int64_t logical_id, + u_int32_t sense_len, u_int32_t timeout); + __END_DECLS #endif /* _SCSI_SCSI_SA_H */ |