1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12 
13 /* @(#)scsi-aix.c	1.36 04/01/14 Copyright 1997 J. Schilling */
14 /*
15  *	Interface for the AIX generic SCSI implementation.
16  *
17  *	This is a hack, that tries to emulate the functionality
18  *	of the usal driver.
19  *
20  *	Warning: you may change this source, but if you do that
21  *	you need to change the _usal_version and _usal_auth* string below.
22  *	You may not return "schily" for an SCG_AUTHOR request anymore.
23  *	Choose your name instead of "schily" and make clear that the version
24  *	string is related to a modified source.
25  *
26  *	Copyright (c) 1997 J. Schilling
27  */
28 /*
29  * This program is free software; you can redistribute it and/or modify
30  * it under the terms of the GNU General Public License version 2
31  * as published by the Free Software Foundation.
32  *
33  * This program is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36  * GNU General Public License for more details.
37  *
38  * You should have received a copy of the GNU General Public License along with
39  * this program; see the file COPYING.  If not, write to the Free Software
40  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
41  */
42 
43 #include <sys/scdisk.h>
44 
45 /*
46  *	Warning: you may change this source, but if you do that
47  *	you need to change the _usal_version and _usal_auth* string below.
48  *	You may not return "schily" for an SCG_AUTHOR request anymore.
49  *	Choose your name instead of "schily" and make clear that the version
50  *	string is related to a modified source.
51  */
52 static	char	_usal_trans_version[] = "scsi-aix.c-1.36";	/* The version for this transport*/
53 
54 
55 #define	MAX_SCG		16	/* Max # of SCSI controllers */
56 #define	MAX_TGT		16
57 #define	MAX_LUN		8
58 
59 struct usal_local{
60 	short	usalfiles[MAX_SCG][MAX_TGT][MAX_LUN];
61 };
62 #define	usallocal(p)	((struct usal_local*)((p)->local))
63 
64 #define	MAX_DMA_AIX (64*1024)
65 
66 static	int	do_usal_cmd(SCSI *usalp, struct usal_cmd *sp);
67 static	int	do_usal_sense(SCSI *usalp, struct usal_cmd *sp);
68 
69 /*
70  * Return version information for the low level SCSI transport code.
71  * This has been introduced to make it easier to trace down problems
72  * in applications.
73  */
74 static char *
usalo_version(SCSI * usalp,int what)75 usalo_version(SCSI *usalp, int what)
76 {
77 	if (usalp != (SCSI *)0) {
78 		switch (what) {
79 
80 		case SCG_VERSION:
81 			return (_usal_trans_version);
82 		/*
83 		 * If you changed this source, you are not allowed to
84 		 * return "schily" for the SCG_AUTHOR request.
85 		 */
86 		case SCG_AUTHOR:
87 			return (_usal_auth_cdrkit);
88 		case SCG_SCCS_ID:
89 			return (__sccsid);
90 		}
91 	}
92 	return ((char *)0);
93 }
94 
95 static int
usalo_help(SCSI * usalp,FILE * f)96 usalo_help(SCSI *usalp, FILE *f)
97 {
98 	__usal_help(f, "DKIOCMD", "SCSI transport for targets known by AIX drivers",
99 		"", "bus,target,lun or UNIX device", "1,2,0 or /dev/rcd0@", FALSE, TRUE);
100 	return (0);
101 }
102 
103 static int
usalo_open(SCSI * usalp,char * device)104 usalo_open(SCSI *usalp, char *device)
105 {
106 		int	busno	= usal_scsibus(usalp);
107 		int	tgt	= usal_target(usalp);
108 		int	tlun	= usal_lun(usalp);
109 	register int	f;
110 	register int	b;
111 	register int	t;
112 	register int	l;
113 	register int	nopen = 0;
114 	char		devname[32];
115 
116 	if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
117 		errno = EINVAL;
118 		if (usalp->errstr)
119 			snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
120 				"Illegal value for busno, target or lun '%d,%d,%d'",
121 				busno, tgt, tlun);
122 		return (-1);
123 	}
124 
125 	if (usalp->local == NULL) {
126 		usalp->local = malloc(sizeof (struct usal_local));
127 		if (usalp->local == NULL)
128 			return (0);
129 
130 		for (b = 0; b < MAX_SCG; b++) {
131 			for (t = 0; t < MAX_TGT; t++) {
132 				for (l = 0; l < MAX_LUN; l++)
133 					usallocal(usalp)->usalfiles[b][t][l] = (short)-1;
134 			}
135 		}
136 	}
137 
138 	if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2))
139 		goto openbydev;
140 
141 	if (busno >= 0 && tgt >= 0 && tlun >= 0) {
142 
143 		snprintf(devname, sizeof (devname), "/dev/rcd%d", tgt);
144 		f = openx(devname, 0, 0, SC_DIAGNOSTIC);
145 		if (f < 0) {
146 			if (usalp->errstr)
147 				snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
148 					"Cannot open '%s'. Specify device number (1 for cd1) as target (1,0)",
149 					devname);
150 			return (0);
151 		}
152 		usallocal(usalp)->usalfiles[busno][tgt][tlun] = f;
153 		return (1);
154 	} else {
155 		if (usalp->errstr)
156 			snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
157 				"Unable to scan on AIX");
158 		return (0);
159 	}
160 openbydev:
161 	if (device != NULL && *device != '\0' && busno >= 0 && tgt >= 0 && tlun >= 0) {
162 		f = openx(device, 0, 0, SC_DIAGNOSTIC);
163 		if (f < 0) {
164 			if (usalp->errstr)
165 				snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
166 					"Cannot open '%s'",
167 					devname);
168 			return (0);
169 		}
170 
171 		usallocal(usalp)->usalfiles[busno][tgt][tlun] = f;
172 		usal_settarget(usalp, busno, tgt, tlun);
173 
174 		return (++nopen);
175 	}
176 	return (nopen);
177 }
178 
179 static int
usalo_close(SCSI * usalp)180 usalo_close(SCSI *usalp)
181 {
182 	register int	f;
183 	register int	b;
184 	register int	t;
185 	register int	l;
186 
187 	if (usalp->local== NULL)
188 		return (-1);
189 
190 	for (b = 0; b < MAX_SCG; b++) {
191 		for (t = 0; t < MAX_TGT; t++) {
192 			for (l = 0; l < MAX_LUN; l++) {
193 				f = usallocal(usalp)->usalfiles[b][t][l];
194 				if (f >= 0)
195 					close(f);
196 				usallocal(usalp)->usalfiles[b][t][l] = (short)-1;
197 			}
198 		}
199 	}
200 	return (0);
201 }
202 
203 static long
usalo_maxdma(SCSI * usalp,long amt)204 usalo_maxdma(SCSI *usalp, long amt)
205 {
206 	return (MAX_DMA_AIX);
207 }
208 
209 #define	palign(x, a)	(((char *)(x)) + ((a) - 1 - (((UIntptr_t)((x)-1))%(a))))
210 
211 static void *
usalo_getbuf(SCSI * usalp,long amt)212 usalo_getbuf(SCSI *usalp, long amt)
213 {
214 /* assume having a modern AIX here */
215 #ifdef HAVE_ALLOCA_H
216     usalp->bufbase = (void *)valloc((size_t)amt);
217     return (usalp->bufbase);
218 #else
219 	void	*ret;
220 	int	pagesize = getpagesize();
221 
222 	if (usalp->debug > 0) {
223 		fprintf((FILE *)usalp->errfile,
224 				"usalo_getbuf: %ld bytes\n", amt);
225 	}
226 	/*
227 	 * Damn AIX is a paged system but has no valloc()
228 	 */
229 	usalp->bufbase = ret = malloc((size_t)(amt+pagesize));
230 	if (ret == NULL)
231 		return (ret);
232 	ret = palign(ret, pagesize);
233 	return (ret);
234 #endif
235 }
236 
237 static void
usalo_freebuf(SCSI * usalp)238 usalo_freebuf(SCSI *usalp)
239 {
240 	if (usalp->bufbase)
241 		free(usalp->bufbase);
242 	usalp->bufbase = NULL;
243 }
244 
245 static BOOL
usalo_havebus(SCSI * usalp,int busno)246 usalo_havebus(SCSI *usalp, int busno)
247 {
248 	register int	t;
249 	register int	l;
250 
251 	if (busno < 0 || busno >= MAX_SCG)
252 		return (FALSE);
253 
254 	if (usalp->local == NULL)
255 		return (FALSE);
256 
257 	for (t = 0; t < MAX_TGT; t++) {
258 		for (l = 0; l < MAX_LUN; l++)
259 			if (usallocal(usalp)->usalfiles[busno][t][l] >= 0)
260 				return (TRUE);
261 	}
262 	return (FALSE);
263 }
264 
265 static int
usalo_fileno(SCSI * usalp,int busno,int tgt,int tlun)266 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
267 {
268 	if (busno < 0 || busno >= MAX_SCG ||
269 	    tgt < 0 || tgt >= MAX_TGT ||
270 	    tlun < 0 || tlun >= MAX_LUN)
271 		return (-1);
272 
273 	if (usalp->local == NULL)
274 		return (-1);
275 
276 	return ((int)usallocal(usalp)->usalfiles[busno][tgt][tlun]);
277 }
278 
279 static int
usalo_initiator_id(SCSI * usalp)280 usalo_initiator_id(SCSI *usalp)
281 {
282 	return (-1);
283 }
284 
285 static int
usalo_isatapi(SCSI * usalp)286 usalo_isatapi(SCSI *usalp)
287 {
288 	return (FALSE);
289 }
290 
291 static int
usalo_reset(SCSI * usalp,int what)292 usalo_reset(SCSI *usalp, int what)
293 {
294 	if (what == SCG_RESET_NOP)
295 		return (0);
296 	if (what != SCG_RESET_BUS) {
297 		errno = EINVAL;
298 		return (-1);
299 	}
300 	/*
301 	 * XXX Does this reset TGT or BUS ???
302 	 */
303 	return (ioctl(usalp->fd, SCIORESET, IDLUN(usal_target(usalp), usal_lun(usalp))));
304 }
305 
306 static int
do_usal_cmd(SCSI * usalp,struct usal_cmd * sp)307 do_usal_cmd(SCSI *usalp, struct usal_cmd *sp)
308 {
309 	struct sc_iocmd req;
310 	int	ret;
311 
312 	if (sp->cdb_len > 12)
313 		comerrno(EX_BAD, "Can't do %d byte command.\n", sp->cdb_len);
314 
315 	fillbytes(&req, sizeof (req), '\0');
316 
317 	req.flags = SC_ASYNC;
318 	if (sp->flags & SCG_RECV_DATA) {
319 		req.flags |= B_READ;
320 	} else if (sp->size > 0) {
321 		req.flags |= B_WRITE;
322 	}
323 	req.data_length = sp->size;
324 	req.buffer = sp->addr;
325 	req.timeout_value = sp->timeout;
326 	req.command_length = sp->cdb_len;
327 
328 	movebytes(&sp->cdb, req.scsi_cdb, 12);
329 	errno = 0;
330 	ret = ioctl(usalp->fd, DKIOCMD, &req);
331 
332 	if (usalp->debug > 0) {
333 		fprintf((FILE *)usalp->errfile, "ret: %d errno: %d (%s)\n", ret, errno, errmsgstr(errno));
334 		fprintf((FILE *)usalp->errfile, "data_length:     %d\n", req.data_length);
335 		fprintf((FILE *)usalp->errfile, "buffer:          0x%X\n", req.buffer);
336 		fprintf((FILE *)usalp->errfile, "timeout_value:   %d\n", req.timeout_value);
337 		fprintf((FILE *)usalp->errfile, "status_validity: %d\n", req.status_validity);
338 		fprintf((FILE *)usalp->errfile, "scsi_bus_status: 0x%X\n", req.scsi_bus_status);
339 		fprintf((FILE *)usalp->errfile, "adapter_status:  0x%X\n", req.adapter_status);
340 		fprintf((FILE *)usalp->errfile, "adap_q_status:   0x%X\n", req.adap_q_status);
341 		fprintf((FILE *)usalp->errfile, "q_tag_msg:       0x%X\n", req.q_tag_msg);
342 		fprintf((FILE *)usalp->errfile, "flags:           0X%X\n", req.flags);
343 	}
344 	if (ret < 0) {
345 		sp->ux_errno = geterrno();
346 		/*
347 		 * Check if SCSI command cound not be send at all.
348 		 */
349 		if (sp->ux_errno == ENOTTY || sp->ux_errno == ENXIO ||
350 		    sp->ux_errno == EINVAL || sp->ux_errno == EACCES) {
351 			return (-1);
352 		}
353 	} else {
354 		sp->ux_errno = 0;
355 	}
356 	ret = 0;
357 	sp->sense_count = 0;
358 	sp->resid = 0;		/* AIX is the same rubbish as Linux here */
359 
360 	fillbytes(&sp->scb, sizeof (sp->scb), '\0');
361 	fillbytes(&sp->u_sense.cmd_sense, sizeof (sp->u_sense.cmd_sense), '\0');
362 
363 	if (req.status_validity == 0) {
364 		sp->error = SCG_NO_ERROR;
365 		return (0);
366 	}
367 	if (req.status_validity & 1) {
368 		sp->u_scb.cmd_scb[0] = req.scsi_bus_status;
369 		sp->error = SCG_RETRYABLE;
370 	}
371 	if (req.status_validity & 2) {
372 		if (req.adapter_status & SC_NO_DEVICE_RESPONSE) {
373 			sp->error = SCG_FATAL;
374 
375 		} else if (req.adapter_status & SC_CMD_TIMEOUT) {
376 			sp->error = SCG_TIMEOUT;
377 
378 		} else if (req.adapter_status != 0) {
379 			sp->error = SCG_RETRYABLE;
380 		}
381 	}
382 
383 	return (ret);
384 }
385 
386 static int
do_usal_sense(SCSI * usalp,struct usal_cmd * sp)387 do_usal_sense(SCSI *usalp, struct usal_cmd *sp)
388 {
389 	int		ret;
390 	struct usal_cmd	s_cmd;
391 
392 	fillbytes((caddr_t)&s_cmd, sizeof (s_cmd), '\0');
393 	s_cmd.addr = sp->u_sense.cmd_sense;
394 	s_cmd.size = sp->sense_len;
395 	s_cmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
396 	s_cmd.cdb_len = SC_G0_CDBLEN;
397 	s_cmd.sense_len = CCS_SENSE_LEN;
398 	s_cmd.cdb.g0_cdb.cmd = SC_REQUEST_SENSE;
399 	s_cmd.cdb.g0_cdb.lun = sp->cdb.g0_cdb.lun;
400 	s_cmd.cdb.g0_cdb.count = sp->sense_len;
401 	ret = do_usal_cmd(usalp, &s_cmd);
402 
403 	if (ret < 0)
404 		return (ret);
405 	if (s_cmd.u_scb.cmd_scb[0] & 02) {
406 		/* XXX ??? Check condition on request Sense ??? */
407 	}
408 	sp->sense_count = sp->sense_len - s_cmd.resid;
409 	return (ret);
410 }
411 
412 static int
usalo_send(SCSI * usalp)413 usalo_send(SCSI *usalp)
414 {
415 	struct usal_cmd	*sp = usalp->scmd;
416 	int	ret;
417 
418 	if (usalp->fd < 0) {
419 		sp->error = SCG_FATAL;
420 		return (0);
421 	}
422 	ret = do_usal_cmd(usalp, sp);
423 	if (ret < 0)
424 		return (ret);
425 	if (sp->u_scb.cmd_scb[0] & 02)
426 		ret = do_usal_sense(usalp, sp);
427 	return (ret);
428 }
429