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