1 /* @(#)scsi-qnx.c	1.5 09/06/30 Copyright 1998-2009 J. Schilling */
2 #ifndef lint
3 static	char __sccsid[] =
4 	"@(#)scsi-qnx.c	1.5 09/06/30 Copyright 1998-2009 J. Schilling";
5 #endif
6 /*
7  *	Interface for QNX (Neutrino generic SCSI implementation).
8  *	First version adopted from the OSF-1 version by
9  *	Kevin Chiles <kchiles@qnx.com>
10  *
11  *	Warning: you may change this source, but if you do that
12  *	you need to change the _scg_version and _scg_auth* string below.
13  *	You may not return "schily" for an SCG_AUTHOR request anymore.
14  *	Choose your name instead of "schily" and make clear that the version
15  *	string is related to a modified source.
16  *
17  *	Copyright (c) 1998-2009 J. Schilling
18  */
19 /*
20  * The contents of this file are subject to the terms of the
21  * Common Development and Distribution License, Version 1.0 only
22  * (the "License").  You may not use this file except in compliance
23  * with the License.
24  *
25  * See the file CDDL.Schily.txt in this distribution for details.
26  * A copy of the CDDL is also available via the Internet at
27  * http://www.opensource.org/licenses/cddl1.txt
28  *
29  * The following exceptions apply:
30  * CDDL �3.6 needs to be replaced by: "You may create a Larger Work by
31  * combining Covered Software with other code if all other code is governed by
32  * the terms of a license that is OSI approved (see www.opensource.org) and
33  * you may distribute the Larger Work as a single product. In such a case,
34  * You must make sure the requirements of this License are fulfilled for
35  * the Covered Software."
36  *
37  * When distributing Covered Code, include this CDDL HEADER in each
38  * file and include the License file CDDL.Schily.txt from this distribution.
39  */
40 
41 #include <schily/mman.h>
42 #include <schily/types.h>
43 #include <sys/dcmd_cam.h>
44 #include <sys/cam_device.h>
45 
46 /*
47  *	Warning: you may change this source, but if you do that
48  *	you need to change the _scg_version and _scg_auth* string below.
49  *	You may not return "schily" for an SCG_AUTHOR request anymore.
50  *	Choose your name instead of "schily" and make clear that the version
51  *	string is related to a modified source.
52  */
53 LOCAL	char	_scg_trans_version[] = "scsi-qnx.c-1.5";	/* The version for this transport*/
54 
55 #define	MAX_SCG		16	/* Max # of SCSI controllers */
56 #define	MAX_TGT		16
57 #define	MAX_LUN		8
58 
59 struct scg_local {
60 	int		fd;
61 };
62 
63 #define	scglocal(p)	((struct scg_local *)((p)->local))
64 #define	QNX_CAM_MAX_DMA	(32*1024)
65 
66 #ifndef	AUTO_SENSE_LEN
67 #	define	AUTO_SENSE_LEN	32	/* SCG_MAX_SENSE */
68 #endif
69 
70 /*
71  * Return version information for the low level SCSI transport code.
72  * This has been introduced to make it easier to trace down problems
73  * in applications.
74  */
75 LOCAL char *
scgo_version(scgp,what)76 scgo_version(scgp, what)
77 	SCSI	*scgp;
78 	int	what;
79 {
80 	if (scgp != (SCSI *)0) {
81 		switch (what) {
82 
83 		case SCG_VERSION:
84 			return (_scg_trans_version);
85 		/*
86 		 * If you changed this source, you are not allowed to
87 		 * return "schily" for the SCG_AUTHOR request.
88 		 */
89 		case SCG_AUTHOR:
90 			return ("Initial Version adopted from OSF-1 by QNX-people");
91 			return (_scg_auth_schily);
92 		case SCG_SCCS_ID:
93 			return (__sccsid);
94 		}
95 	}
96 	return ((char *)0);
97 }
98 
99 LOCAL int
scgo_help(scgp,f)100 scgo_help(scgp, f)
101 	SCSI	*scgp;
102 	FILE	*f;
103 {
104 	__scg_help(f, "CAM", "Generic transport independent SCSI (Common Access Method)",
105 		"", "bus,target,lun", "1,2,0", TRUE, FALSE);
106 	return (0);
107 }
108 
109 LOCAL int
scgo_open(scgp,device)110 scgo_open(scgp, device)
111 	SCSI	*scgp;
112 	char	*device;
113 {
114 	int fd;
115 
116 	if (device == NULL || *device == '\0') {
117 		errno = EINVAL;
118 		if (scgp->errstr)
119 			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
120 				"'devname' must be specified on this OS");
121 		return (-1);
122 	}
123 
124 	if (scgp->local == NULL) {
125 		scgp->local = malloc(sizeof (struct scg_local));
126 		if (scgp->local == NULL)
127 			return (0);
128 		scglocal(scgp)->fd = -1;
129 	}
130 
131 	if (scglocal(scgp)->fd != -1)	/* multiple open? */
132 		return (1);
133 
134 	if ((scglocal(scgp)->fd = open(device, O_RDONLY, 0)) < 0) {
135 		if (scgp->errstr)
136 			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
137 				"Cannot open '%s'", device);
138 		return (-1);
139 	}
140 
141 	scg_settarget(scgp, 0, 0, 0);
142 
143 	return (1);
144 }
145 
146 LOCAL int
scgo_close(scgp)147 scgo_close(scgp)
148 	SCSI	*scgp;
149 {
150 	if (scgp->local == NULL)
151 		return (-1);
152 
153 	if (scglocal(scgp)->fd >= 0)
154 		close(scglocal(scgp)->fd);
155 	scglocal(scgp)->fd = -1;
156 	return (0);
157 }
158 
159 LOCAL long
scgo_maxdma(scgp,amt)160 scgo_maxdma(scgp, amt)
161 	SCSI	*scgp;
162 	long	amt;
163 {
164 	long maxdma = QNX_CAM_MAX_DMA;
165 
166 	return (maxdma);
167 }
168 
169 LOCAL void *
scgo_getbuf(scgp,amt)170 scgo_getbuf(scgp, amt)
171 	SCSI	*scgp;
172 	long	amt;
173 {
174 	void	*addr;
175 
176 	if (scgp->debug > 0) {
177 		js_fprintf((FILE *)scgp->errfile, "scgo_getbuf: %ld bytes\n", amt);
178 	}
179 
180 	if ((addr = mmap(NULL, amt, PROT_READ | PROT_WRITE | PROT_NOCACHE,
181 						MAP_ANON | MAP_PHYS | MAP_NOX64K, NOFD, 0)) == MAP_FAILED) {
182 		return (NULL);
183 	}
184 
185 	scgp->bufbase = addr;
186 	return (addr);
187 }
188 
189 LOCAL void
scgo_freebuf(scgp)190 scgo_freebuf(scgp)
191 	SCSI	*scgp;
192 {
193 	if (scgp->bufbase)
194 		munmap(scgp->bufbase, QNX_CAM_MAX_DMA);
195 	scgp->bufbase = NULL;
196 }
197 
198 LOCAL int
scgo_numbus(scgp)199 scgo_numbus(scgp)
200 	SCSI	*scgp;
201 {
202 	return (MAX_SCG);
203 }
204 
205 LOCAL BOOL
scgo_havebus(scgp,busno)206 scgo_havebus(scgp, busno)
207 	SCSI	*scgp;
208 	int	busno;
209 {
210 	return (FALSE);
211 }
212 
213 
214 LOCAL int
scgo_fileno(scgp,busno,tgt,tlun)215 scgo_fileno(scgp, busno, tgt, tlun)
216 	SCSI	*scgp;
217 	int	busno;
218 	int	tgt;
219 	int	tlun;
220 {
221 	if (scgp->local == NULL)
222 		return (-1);
223 
224 	return ((busno < 0 || busno >= MAX_SCG) ? -1 : scglocal(scgp)->fd);
225 }
226 
227 LOCAL int
scgo_initiator_id(scgp)228 scgo_initiator_id(scgp)
229 	SCSI	*scgp;
230 {
231 	return (-1);
232 }
233 
234 LOCAL int
scgo_isatapi(scgp)235 scgo_isatapi(scgp)
236 	SCSI	*scgp;
237 {
238 	cam_devinfo_t	cinfo;
239 
240 	if (devctl(scgp->fd, DCMD_CAM_DEVINFO, &cinfo, sizeof (cinfo), NULL) != EOK) {
241 		return (TRUE);		/* default to ATAPI */
242 	}
243 	return ((cinfo.flags & DEV_ATAPI) ? TRUE : FALSE);
244 }
245 
246 LOCAL int
scgo_reset(scgp,what)247 scgo_reset(scgp, what)
248 	SCSI	*scgp;
249 	int	what;
250 {
251 	errno = EINVAL;
252 	return (-1);
253 }
254 
255 LOCAL int
scgo_send(scgp)256 scgo_send(scgp)
257 	SCSI		*scgp;
258 {
259 	int		i;
260 	struct scg_cmd	*sp;
261 	int		icnt;
262 	iov_t   	iov[3];
263 	CAM_PASS_THRU	cpt;
264 
265 	icnt	= 1;
266 	sp	= scgp->scmd;
267 	if (scgp->fd < 0) {
268 		sp->error = SCG_FATAL;
269 		return (0);
270 	}
271 
272 	memset(&cpt, 0, sizeof (cpt));
273 
274 	sp->sense_count	= 0;
275 	sp->ux_errno	= 0;
276 	sp->error	= SCG_NO_ERROR;
277 	cpt.cam_timeout	= sp->timeout;
278 	cpt.cam_cdb_len = sp->cdb_len;
279 	memcpy(cpt.cam_cdb, sp->cdb.cmd_cdb, sp->cdb_len);
280 
281 	if (sp->sense_len != -1) {
282 		cpt.cam_sense_len	= sp->sense_len;
283 		cpt.cam_sense_ptr	= sizeof (cpt);	/* XXX Offset from start of struct to data ??? */
284 		icnt++;
285 	} else {
286 		cpt.cam_flags |= CAM_DIS_AUTOSENSE;
287 	}
288 
289 	if (cpt.cam_dxfer_len = sp->size) {
290 		icnt++;
291 		cpt.cam_data_ptr	= (paddr_t)sizeof (cpt) + cpt.cam_sense_len;
292 		if (sp->flags & SCG_RECV_DATA) {
293 			cpt.cam_flags |= CAM_DIR_IN;
294 		} else {
295 			cpt.cam_flags |= CAM_DIR_OUT;
296 		}
297 	} else {
298 		cpt.cam_flags |= CAM_DIR_NONE;
299 	}
300 
301 	SETIOV(&iov[0], &cpt, sizeof (cpt));
302 	SETIOV(&iov[1], sp->u_sense.cmd_sense, cpt.cam_sense_len);
303 	SETIOV(&iov[2], sp->addr, sp->size);
304 	if (devctlv(scglocal(scgp)->fd, DCMD_CAM_PASS_THRU, icnt, icnt, iov, iov, NULL)) {
305 		sp->ux_errno = geterrno();
306 		sp->error = SCG_FATAL;
307 		if (scgp->debug > 0) {
308 			errmsg("cam_io failed\n");
309 		}
310 		return (0);
311 	}
312 
313 	sp->resid		= cpt.cam_resid;
314 	sp->u_scb.cmd_scb[0]	= cpt.cam_scsi_status;
315 
316 	switch (cpt.cam_status & CAM_STATUS_MASK) {
317 		case CAM_REQ_CMP:
318 			break;
319 
320 		case CAM_SEL_TIMEOUT:
321 			sp->error	= SCG_FATAL;
322 			sp->ux_errno	= EIO;
323 			break;
324 
325 		case CAM_CMD_TIMEOUT:
326 			sp->error	= SCG_TIMEOUT;
327 			sp->ux_errno	= EIO;
328 			break;
329 
330 		default:
331 			sp->error	= SCG_RETRYABLE;
332 			sp->ux_errno	= EIO;
333 			break;
334 	}
335 
336 	if (cpt.cam_status & CAM_AUTOSNS_VALID) {
337 		sp->sense_count = min(cpt.cam_sense_len - cpt.cam_sense_resid,
338 							SCG_MAX_SENSE);
339 		sp->sense_count = min(sp->sense_count, sp->sense_len);
340 		if (sp->sense_len < 0)
341 			sp->sense_count = 0;
342 	}
343 
344 	return (0);
345 }
346