1275c9da8Seschrock /*
2275c9da8Seschrock  * CDDL HEADER START
3275c9da8Seschrock  *
4275c9da8Seschrock  * The contents of this file are subject to the terms of the
5275c9da8Seschrock  * Common Development and Distribution License (the "License").
6275c9da8Seschrock  * You may not use this file except in compliance with the License.
7275c9da8Seschrock  *
8275c9da8Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275c9da8Seschrock  * or http://www.opensolaris.org/os/licensing.
10275c9da8Seschrock  * See the License for the specific language governing permissions
11275c9da8Seschrock  * and limitations under the License.
12275c9da8Seschrock  *
13275c9da8Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14275c9da8Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275c9da8Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16275c9da8Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17275c9da8Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18275c9da8Seschrock  *
19275c9da8Seschrock  * CDDL HEADER END
20275c9da8Seschrock  */
21275c9da8Seschrock 
22275c9da8Seschrock /*
23275c9da8Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24275c9da8Seschrock  * Use is subject to license terms.
25b75e7d76SRobert Mustacchi  *
26b75e7d76SRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
27275c9da8Seschrock  */
28275c9da8Seschrock 
29275c9da8Seschrock #include <sys/types.h>
30275c9da8Seschrock #include <sys/scsi/impl/uscsi.h>
31275c9da8Seschrock #include <sys/scsi/generic/commands.h>
32275c9da8Seschrock 
33275c9da8Seschrock #include <unistd.h>
34275c9da8Seschrock #include <fcntl.h>
35275c9da8Seschrock #include <errno.h>
36275c9da8Seschrock #include <string.h>
37275c9da8Seschrock #include <strings.h>
38275c9da8Seschrock #include <stdio.h>
39275c9da8Seschrock #include <limits.h>
40275c9da8Seschrock 
41275c9da8Seschrock #include <scsi/libscsi.h>
42275c9da8Seschrock #include "libscsi_impl.h"
43275c9da8Seschrock 
44275c9da8Seschrock struct uscsi_dev {
45275c9da8Seschrock 	int fd;
46275c9da8Seschrock 	char *dev;
47275c9da8Seschrock };
48275c9da8Seschrock 
49275c9da8Seschrock static void *
uscsi_open(libscsi_hdl_t * hp,const void * target)50275c9da8Seschrock uscsi_open(libscsi_hdl_t *hp, const void *target)
51275c9da8Seschrock {
52275c9da8Seschrock 	struct uscsi_dev *dp;
53275c9da8Seschrock 	const char *target_name = (const char *)target;
54275c9da8Seschrock 
55275c9da8Seschrock 	if ((dp = libscsi_zalloc(hp, sizeof (struct uscsi_dev))) == NULL)
56275c9da8Seschrock 		return (NULL);
57275c9da8Seschrock 
58275c9da8Seschrock 	if ((dp->dev = libscsi_strdup(hp, target_name)) == NULL) {
59275c9da8Seschrock 		libscsi_free(hp, dp);
60275c9da8Seschrock 		return (NULL);
61275c9da8Seschrock 	}
62275c9da8Seschrock 
63275c9da8Seschrock 	if ((dp->fd = open(target_name, O_RDONLY)) < 0) {
64275c9da8Seschrock 		(void) libscsi_error(hp, ESCSI_BADTARGET, "failed to open %s "
65275c9da8Seschrock 		    "for reading: %s", target_name, strerror(errno));
66275c9da8Seschrock 		libscsi_free(hp, dp->dev);
67275c9da8Seschrock 		libscsi_free(hp, dp);
68275c9da8Seschrock 		return (NULL);
69275c9da8Seschrock 	}
70275c9da8Seschrock 
71275c9da8Seschrock 	return (dp);
72275c9da8Seschrock }
73275c9da8Seschrock 
74275c9da8Seschrock static void
uscsi_close(libscsi_hdl_t * hp,void * private)75275c9da8Seschrock uscsi_close(libscsi_hdl_t *hp, void *private)
76275c9da8Seschrock {
77275c9da8Seschrock 	struct uscsi_dev *dp = (struct uscsi_dev *)private;
78275c9da8Seschrock 
79275c9da8Seschrock 	if (dp == NULL)
80275c9da8Seschrock 		return;
81275c9da8Seschrock 
82275c9da8Seschrock 	if (dp->fd > 0)
83275c9da8Seschrock 		(void) close(dp->fd);
84275c9da8Seschrock 
85275c9da8Seschrock 	libscsi_free(hp, dp->dev);
86275c9da8Seschrock 	libscsi_free(hp, dp);
87275c9da8Seschrock }
88275c9da8Seschrock 
89275c9da8Seschrock static int
xlate_flags(libscsi_hdl_t * hp,uint_t flags,int * uf)90275c9da8Seschrock xlate_flags(libscsi_hdl_t *hp, uint_t flags, int *uf)
91275c9da8Seschrock {
92275c9da8Seschrock 	uint_t f;
93275c9da8Seschrock 	int i;
94275c9da8Seschrock 
95275c9da8Seschrock 	f = 0;
96275c9da8Seschrock 
97275c9da8Seschrock 	for (i = 0; i < sizeof (flags) * 8; i++) {
98275c9da8Seschrock 		switch (flags & (1 << i)) {
99275c9da8Seschrock 		case 0:
100275c9da8Seschrock 			continue;
101275c9da8Seschrock 		case LIBSCSI_AF_READ:
102275c9da8Seschrock 			f |= USCSI_READ;
103275c9da8Seschrock 			break;
104275c9da8Seschrock 		case LIBSCSI_AF_WRITE:
105275c9da8Seschrock 			f |= USCSI_WRITE;
106275c9da8Seschrock 			break;
107275c9da8Seschrock 		case LIBSCSI_AF_SILENT:
108275c9da8Seschrock 			f |= USCSI_SILENT;
109275c9da8Seschrock 			break;
110275c9da8Seschrock 		case LIBSCSI_AF_DIAGNOSE:
111275c9da8Seschrock 			f |= USCSI_DIAGNOSE;
112275c9da8Seschrock 			break;
113275c9da8Seschrock 		case LIBSCSI_AF_ISOLATE:
114*0b4d6575SRobert Mustacchi 			f |= USCSI_ISOLATE;
115275c9da8Seschrock 			break;
116275c9da8Seschrock 		case LIBSCSI_AF_RQSENSE:
117*0b4d6575SRobert Mustacchi 			f |= USCSI_RQENABLE;
118275c9da8Seschrock 			break;
119275c9da8Seschrock 		default:
120275c9da8Seschrock 			return (libscsi_error(hp, ESCSI_BOGUSFLAGS,
121275c9da8Seschrock 			    "flag 0x%x is unknown", 1 << i));
122275c9da8Seschrock 		}
123275c9da8Seschrock 	}
124275c9da8Seschrock 
125275c9da8Seschrock 	*uf = f;
126275c9da8Seschrock 
127275c9da8Seschrock 	return (0);
128275c9da8Seschrock }
129275c9da8Seschrock 
130275c9da8Seschrock static int
uscsi_exec(libscsi_hdl_t * hp,void * private,libscsi_action_t * ap)131275c9da8Seschrock uscsi_exec(libscsi_hdl_t *hp, void *private, libscsi_action_t *ap)
132275c9da8Seschrock {
133275c9da8Seschrock 	struct uscsi_dev *dp = (struct uscsi_dev *)private;
134275c9da8Seschrock 	struct uscsi_cmd cmd;
135275c9da8Seschrock 	size_t data_a, data_v;
136275c9da8Seschrock 	uint8_t *cp;
137275c9da8Seschrock 	uint_t flags;
138275c9da8Seschrock 
139275c9da8Seschrock 	bzero(&cmd, sizeof (cmd));
140275c9da8Seschrock 
141275c9da8Seschrock 	cp = libscsi_action_get_cdb(ap);
142275c9da8Seschrock 	if (cp == NULL)
143275c9da8Seschrock 		return (-1);
144275c9da8Seschrock 
145275c9da8Seschrock 	flags = libscsi_action_get_flags(ap);
146275c9da8Seschrock 	if (xlate_flags(hp, flags, &cmd.uscsi_flags) != 0)
147275c9da8Seschrock 		return (-1);
148275c9da8Seschrock 
149275c9da8Seschrock 	cmd.uscsi_status = (short)-1;
150275c9da8Seschrock 	cmd.uscsi_timeout = (short)libscsi_action_get_timeout(ap);
151275c9da8Seschrock 
152275c9da8Seschrock 	cmd.uscsi_cdb = (caddr_t)cp;
153b75e7d76SRobert Mustacchi 	cmd.uscsi_cdblen = libscsi_action_get_cdblen(ap);
154275c9da8Seschrock 	if (cmd.uscsi_cdblen == 0)
155275c9da8Seschrock 		return (-1);
156275c9da8Seschrock 
157275c9da8Seschrock 	if (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) {
158275c9da8Seschrock 		if (libscsi_action_get_buffer(ap,
159275c9da8Seschrock 		    (uint8_t **)&cmd.uscsi_bufaddr, &data_a, &data_v) != 0)
160275c9da8Seschrock 			return (-1);
161275c9da8Seschrock 		if (flags & LIBSCSI_AF_READ)
162275c9da8Seschrock 			cmd.uscsi_buflen = data_a;
163275c9da8Seschrock 		else
164275c9da8Seschrock 			cmd.uscsi_buflen = data_v;
165275c9da8Seschrock 	}
166275c9da8Seschrock 	if (flags & LIBSCSI_AF_RQSENSE) {
167275c9da8Seschrock 		if (libscsi_action_get_sense(ap, (uint8_t **)&cmd.uscsi_rqbuf,
168275c9da8Seschrock 		    &data_a, NULL) != 0)
169275c9da8Seschrock 			return (-1);
170275c9da8Seschrock 		if (data_a > UCHAR_MAX)
171275c9da8Seschrock 			data_a = UCHAR_MAX;
172275c9da8Seschrock 		cmd.uscsi_rqlen = (uchar_t)data_a;
173275c9da8Seschrock 		cmd.uscsi_rqstatus = (uchar_t)-1;
174275c9da8Seschrock 	}
175275c9da8Seschrock 
176275c9da8Seschrock 	if (ioctl(dp->fd, USCSICMD, &cmd) < 0) {
177275c9da8Seschrock 		ASSERT(errno != EFAULT);
178275c9da8Seschrock 		switch (errno) {
179275c9da8Seschrock 		case EINVAL:
180275c9da8Seschrock 			return (libscsi_error(hp, ESCSI_BADCMD, "internal "
181275c9da8Seschrock 			    "uscsi error"));
182275c9da8Seschrock 		case EPERM:
183275c9da8Seschrock 			return (libscsi_error(hp, ESCSI_PERM, "insufficient "
184275c9da8Seschrock 			    "privileges "));
185275c9da8Seschrock 		case EIO:
186275c9da8Seschrock 			/* Command never executed at all */
187275c9da8Seschrock 			if (cmd.uscsi_status == (short)-1)
188275c9da8Seschrock 				return (libscsi_error(hp, ESCSI_IO, "I/O "
189275c9da8Seschrock 				    "error", strerror(errno)));
190275c9da8Seschrock 			break;
191275c9da8Seschrock 		default:
192275c9da8Seschrock 			return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl "
193275c9da8Seschrock 			    "failed: %s", strerror(errno)));
194275c9da8Seschrock 		}
195275c9da8Seschrock 	}
196275c9da8Seschrock 
197275c9da8Seschrock 	libscsi_action_set_status(ap, cmd.uscsi_status);
198275c9da8Seschrock 	if ((flags & LIBSCSI_AF_READ) && libscsi_action_set_datalen(ap,
199275c9da8Seschrock 	    cmd.uscsi_buflen - cmd.uscsi_resid) != 0)
200275c9da8Seschrock 		return (-1);
201275c9da8Seschrock 	if ((flags & LIBSCSI_AF_RQSENSE) && libscsi_action_set_senselen(ap,
202275c9da8Seschrock 	    cmd.uscsi_rqlen - cmd.uscsi_rqresid) != 0)
203275c9da8Seschrock 		return (-1);
204275c9da8Seschrock 
205275c9da8Seschrock 	return (0);
206275c9da8Seschrock }
207275c9da8Seschrock 
208275c9da8Seschrock /*ARGSUSED*/
209275c9da8Seschrock static void
uscsi_target_name(libscsi_hdl_t * hp,void * private,char * buf,size_t len)210275c9da8Seschrock uscsi_target_name(libscsi_hdl_t *hp, void *private, char *buf, size_t len)
211275c9da8Seschrock {
212275c9da8Seschrock 	struct uscsi_dev *dp = (struct uscsi_dev *)private;
213275c9da8Seschrock 
214275c9da8Seschrock 	(void) snprintf(buf, len, "%s", dp->dev);
215275c9da8Seschrock }
216275c9da8Seschrock 
217*0b4d6575SRobert Mustacchi static int
uscsi_max_transfer(libscsi_hdl_t * hp,void * private,size_t * sizep)218*0b4d6575SRobert Mustacchi uscsi_max_transfer(libscsi_hdl_t *hp, void *private, size_t *sizep)
219*0b4d6575SRobert Mustacchi {
220*0b4d6575SRobert Mustacchi 	uscsi_xfer_t xfer;
221*0b4d6575SRobert Mustacchi 	struct uscsi_dev *dp = (struct uscsi_dev *)private;
222*0b4d6575SRobert Mustacchi 
223*0b4d6575SRobert Mustacchi 	if (ioctl(dp->fd, USCSIMAXXFER, &xfer) < 0) {
224*0b4d6575SRobert Mustacchi 		ASSERT(errno != EFAULT);
225*0b4d6575SRobert Mustacchi 		switch (errno) {
226*0b4d6575SRobert Mustacchi 		case EINVAL:
227*0b4d6575SRobert Mustacchi 			return (libscsi_error(hp, ESCSI_BADCMD, "internal "
228*0b4d6575SRobert Mustacchi 			    "uscsi error"));
229*0b4d6575SRobert Mustacchi 		case EPERM:
230*0b4d6575SRobert Mustacchi 			return (libscsi_error(hp, ESCSI_PERM, "insufficient "
231*0b4d6575SRobert Mustacchi 			    "privileges "));
232*0b4d6575SRobert Mustacchi 		case ENOTTY:
233*0b4d6575SRobert Mustacchi 			return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer "
234*0b4d6575SRobert Mustacchi 			    "request not supported on device"));
235*0b4d6575SRobert Mustacchi 		default:
236*0b4d6575SRobert Mustacchi 			return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl "
237*0b4d6575SRobert Mustacchi 			    "failed: %s", strerror(errno)));
238*0b4d6575SRobert Mustacchi 		}
239*0b4d6575SRobert Mustacchi 	}
240*0b4d6575SRobert Mustacchi 
241*0b4d6575SRobert Mustacchi 	if (xfer > SIZE_MAX)
242*0b4d6575SRobert Mustacchi 		xfer = SIZE_MAX;
243*0b4d6575SRobert Mustacchi 
244*0b4d6575SRobert Mustacchi 	*sizep = (size_t)xfer;
245*0b4d6575SRobert Mustacchi 	return (0);
246*0b4d6575SRobert Mustacchi }
247*0b4d6575SRobert Mustacchi 
248275c9da8Seschrock static const libscsi_engine_ops_t uscsi_ops = {
249275c9da8Seschrock 	.lseo_open = uscsi_open,
250275c9da8Seschrock 	.lseo_close = uscsi_close,
251275c9da8Seschrock 	.lseo_exec = uscsi_exec,
252*0b4d6575SRobert Mustacchi 	.lseo_target_name = uscsi_target_name,
253*0b4d6575SRobert Mustacchi 	.lseo_max_transfer = uscsi_max_transfer
254275c9da8Seschrock };
255275c9da8Seschrock 
256275c9da8Seschrock static const libscsi_engine_t uscsi_engine = {
257275c9da8Seschrock 	.lse_name = "uscsi",
258275c9da8Seschrock 	.lse_libversion = LIBSCSI_VERSION,
259275c9da8Seschrock 	.lse_ops = &uscsi_ops
260275c9da8Seschrock };
261275c9da8Seschrock 
262275c9da8Seschrock /*ARGSUSED*/
263275c9da8Seschrock const libscsi_engine_t *
libscsi_uscsi_init(libscsi_hdl_t * hp)264275c9da8Seschrock libscsi_uscsi_init(libscsi_hdl_t *hp)
265275c9da8Seschrock {
266275c9da8Seschrock 	return (&uscsi_engine);
267275c9da8Seschrock }
268