1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Copyright (c) 2017, Joyent, Inc. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/scsi/impl/uscsi.h> 31 #include <sys/scsi/generic/commands.h> 32 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <stdio.h> 39 #include <limits.h> 40 41 #include <scsi/libscsi.h> 42 #include "libscsi_impl.h" 43 44 struct uscsi_dev { 45 int fd; 46 char *dev; 47 }; 48 49 static void * 50 uscsi_open(libscsi_hdl_t *hp, const void *target) 51 { 52 struct uscsi_dev *dp; 53 const char *target_name = (const char *)target; 54 55 if ((dp = libscsi_zalloc(hp, sizeof (struct uscsi_dev))) == NULL) 56 return (NULL); 57 58 if ((dp->dev = libscsi_strdup(hp, target_name)) == NULL) { 59 libscsi_free(hp, dp); 60 return (NULL); 61 } 62 63 if ((dp->fd = open(target_name, O_RDONLY)) < 0) { 64 (void) libscsi_error(hp, ESCSI_BADTARGET, "failed to open %s " 65 "for reading: %s", target_name, strerror(errno)); 66 libscsi_free(hp, dp->dev); 67 libscsi_free(hp, dp); 68 return (NULL); 69 } 70 71 return (dp); 72 } 73 74 static void 75 uscsi_close(libscsi_hdl_t *hp, void *private) 76 { 77 struct uscsi_dev *dp = (struct uscsi_dev *)private; 78 79 if (dp == NULL) 80 return; 81 82 if (dp->fd > 0) 83 (void) close(dp->fd); 84 85 libscsi_free(hp, dp->dev); 86 libscsi_free(hp, dp); 87 } 88 89 static int 90 xlate_flags(libscsi_hdl_t *hp, uint_t flags, int *uf) 91 { 92 uint_t f; 93 int i; 94 95 f = 0; 96 97 for (i = 0; i < sizeof (flags) * 8; i++) { 98 switch (flags & (1 << i)) { 99 case 0: 100 continue; 101 case LIBSCSI_AF_READ: 102 f |= USCSI_READ; 103 break; 104 case LIBSCSI_AF_WRITE: 105 f |= USCSI_WRITE; 106 break; 107 case LIBSCSI_AF_SILENT: 108 f |= USCSI_SILENT; 109 break; 110 case LIBSCSI_AF_DIAGNOSE: 111 f |= USCSI_DIAGNOSE; 112 break; 113 case LIBSCSI_AF_ISOLATE: 114 f |= USCSI_ISOLATE; 115 break; 116 case LIBSCSI_AF_RQSENSE: 117 f |= USCSI_RQENABLE; 118 break; 119 default: 120 return (libscsi_error(hp, ESCSI_BOGUSFLAGS, 121 "flag 0x%x is unknown", 1 << i)); 122 } 123 } 124 125 *uf = f; 126 127 return (0); 128 } 129 130 static int 131 uscsi_exec(libscsi_hdl_t *hp, void *private, libscsi_action_t *ap) 132 { 133 struct uscsi_dev *dp = (struct uscsi_dev *)private; 134 struct uscsi_cmd cmd; 135 size_t data_a, data_v; 136 uint8_t *cp; 137 uint_t flags; 138 139 bzero(&cmd, sizeof (cmd)); 140 141 cp = libscsi_action_get_cdb(ap); 142 if (cp == NULL) 143 return (-1); 144 145 flags = libscsi_action_get_flags(ap); 146 if (xlate_flags(hp, flags, &cmd.uscsi_flags) != 0) 147 return (-1); 148 149 cmd.uscsi_status = (short)-1; 150 cmd.uscsi_timeout = (short)libscsi_action_get_timeout(ap); 151 152 cmd.uscsi_cdb = (caddr_t)cp; 153 cmd.uscsi_cdblen = libscsi_action_get_cdblen(ap); 154 if (cmd.uscsi_cdblen == 0) 155 return (-1); 156 157 if (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) { 158 if (libscsi_action_get_buffer(ap, 159 (uint8_t **)&cmd.uscsi_bufaddr, &data_a, &data_v) != 0) 160 return (-1); 161 if (flags & LIBSCSI_AF_READ) 162 cmd.uscsi_buflen = data_a; 163 else 164 cmd.uscsi_buflen = data_v; 165 } 166 if (flags & LIBSCSI_AF_RQSENSE) { 167 if (libscsi_action_get_sense(ap, (uint8_t **)&cmd.uscsi_rqbuf, 168 &data_a, NULL) != 0) 169 return (-1); 170 if (data_a > UCHAR_MAX) 171 data_a = UCHAR_MAX; 172 cmd.uscsi_rqlen = (uchar_t)data_a; 173 cmd.uscsi_rqstatus = (uchar_t)-1; 174 } 175 176 if (ioctl(dp->fd, USCSICMD, &cmd) < 0) { 177 ASSERT(errno != EFAULT); 178 switch (errno) { 179 case EINVAL: 180 return (libscsi_error(hp, ESCSI_BADCMD, "internal " 181 "uscsi error")); 182 case EPERM: 183 return (libscsi_error(hp, ESCSI_PERM, "insufficient " 184 "privileges ")); 185 case EIO: 186 /* Command never executed at all */ 187 if (cmd.uscsi_status == (short)-1) 188 return (libscsi_error(hp, ESCSI_IO, "I/O " 189 "error", strerror(errno))); 190 break; 191 default: 192 return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl " 193 "failed: %s", strerror(errno))); 194 } 195 } 196 197 libscsi_action_set_status(ap, cmd.uscsi_status); 198 if ((flags & LIBSCSI_AF_READ) && libscsi_action_set_datalen(ap, 199 cmd.uscsi_buflen - cmd.uscsi_resid) != 0) 200 return (-1); 201 if ((flags & LIBSCSI_AF_RQSENSE) && libscsi_action_set_senselen(ap, 202 cmd.uscsi_rqlen - cmd.uscsi_rqresid) != 0) 203 return (-1); 204 205 return (0); 206 } 207 208 /*ARGSUSED*/ 209 static void 210 uscsi_target_name(libscsi_hdl_t *hp, void *private, char *buf, size_t len) 211 { 212 struct uscsi_dev *dp = (struct uscsi_dev *)private; 213 214 (void) snprintf(buf, len, "%s", dp->dev); 215 } 216 217 static int 218 uscsi_max_transfer(libscsi_hdl_t *hp, void *private, size_t *sizep) 219 { 220 uscsi_xfer_t xfer; 221 struct uscsi_dev *dp = (struct uscsi_dev *)private; 222 223 if (ioctl(dp->fd, USCSIMAXXFER, &xfer) < 0) { 224 ASSERT(errno != EFAULT); 225 switch (errno) { 226 case EINVAL: 227 return (libscsi_error(hp, ESCSI_BADCMD, "internal " 228 "uscsi error")); 229 case EPERM: 230 return (libscsi_error(hp, ESCSI_PERM, "insufficient " 231 "privileges ")); 232 case ENOTTY: 233 return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer " 234 "request not supported on device")); 235 default: 236 return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl " 237 "failed: %s", strerror(errno))); 238 } 239 } 240 241 if (xfer > SIZE_MAX) 242 xfer = SIZE_MAX; 243 244 *sizep = (size_t)xfer; 245 return (0); 246 } 247 248 static const libscsi_engine_ops_t uscsi_ops = { 249 .lseo_open = uscsi_open, 250 .lseo_close = uscsi_close, 251 .lseo_exec = uscsi_exec, 252 .lseo_target_name = uscsi_target_name, 253 .lseo_max_transfer = uscsi_max_transfer 254 }; 255 256 static const libscsi_engine_t uscsi_engine = { 257 .lse_name = "uscsi", 258 .lse_libversion = LIBSCSI_VERSION, 259 .lse_ops = &uscsi_ops 260 }; 261 262 /*ARGSUSED*/ 263 const libscsi_engine_t * 264 libscsi_uscsi_init(libscsi_hdl_t *hp) 265 { 266 return (&uscsi_engine); 267 } 268