1/*  Copyright 1996   Grant R. Guenther,  based on work of Itai Nahshon
2 *   http://www.torque.net/ziptool.html
3 *  Copyright 1997-1999,2001,2002,2005,2007,2009 Alain Knaff.
4 *  This file is part of mtools.
5 *
6 *  Mtools is free software: you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation, either version 3 of the License, or
9 *  (at your option) any later version.
10 *
11 *  Mtools is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
18 *
19 * scsi.c
20 * Iomega Zip/Jaz drive tool
21 * change protection mode and eject disk
22 */
23
24/* scis.c by Markus Gyger <mgyger@itr.ch> */
25/* This code is based on ftp://gear.torque.net/pub/ziptool.c */
26/* by Grant R. Guenther with the following copyright notice: */
27
28/*  (c) 1996   Grant R. Guenther,  based on work of Itai Nahshon  */
29/*  http://www.torque.net/ziptool.html  */
30
31
32/* A.K. Moved this from mzip.c to a separate file in order to share with
33 * plain_io.c */
34
35#include "sysincludes.h"
36#include "mtools.h"
37#include "scsi.h"
38
39#if defined OS_hpux
40#include <sys/scsi.h>
41#endif
42
43#ifdef OS_solaris
44#include <sys/scsi/scsi.h>
45#endif /* solaris */
46
47#ifdef OS_sunos
48#include <scsi/generic/commands.h>
49#include <scsi/impl/uscsi.h>
50#endif /* sunos */
51
52#ifdef sgi
53#include <sys/dsreq.h>
54#endif
55
56#ifdef OS_linux
57#define SCSI_IOCTL_SEND_COMMAND 1
58struct scsi_ioctl_command {
59    int  inlen;
60    int  outlen;
61    char cmd[5008];
62};
63#endif
64
65#ifdef _SCO_DS
66#include <sys/scsicmd.h>
67#endif
68
69#if (defined(OS_freebsd)) && (__FreeBSD__ >= 2)
70#include <camlib.h>
71#endif
72
73#if defined(OS_netbsd) || defined(OS_netbsdelf)
74#include <sys/scsiio.h>
75#endif
76
77int scsi_max_length(void)
78{
79#ifdef OS_linux
80	return 8;
81#else
82	return 255;
83#endif
84}
85
86int scsi_open(const char *name, int flag, int mode, void **extra_data)
87{
88#if (defined(OS_freebsd)) && (__FreeBSD__ >= 2)
89    struct cam_device *cam_dev;
90    cam_dev = cam_open_device(name, O_RDWR);
91    *extra_data = (void *) cam_dev;
92    if (cam_dev)
93        return cam_dev->fd;
94    else
95        return -1;
96#else
97    return open(name, O_RDONLY | O_LARGEFILE | O_BINARY
98#ifdef O_NDELAY
99		| O_NDELAY
100#endif
101	/* O_RDONLY  | dev->mode*/);
102#endif
103}
104
105int scsi_cmd(int fd, unsigned char *cdb, int cmdlen, scsi_io_mode_t mode,
106	     void *data, size_t len, void *extra_data)
107{
108#if defined OS_hpux
109	struct sctl_io sctl_io;
110
111	memset(&sctl_io, 0, sizeof sctl_io);   /* clear reserved fields */
112	memcpy(sctl_io.cdb, cdb, cmdlen);      /* copy command */
113	sctl_io.cdb_length = cmdlen;           /* command length */
114	sctl_io.max_msecs = 2000;              /* allow 2 seconds for cmd */
115
116	switch (mode) {
117		case SCSI_IO_READ:
118			sctl_io.flags = SCTL_READ;
119			sctl_io.data_length = len;
120			sctl_io.data = data;
121			break;
122		case SCSI_IO_WRITE:
123			sctl_io.flags = 0;
124			sctl_io.data_length = data ? len : 0;
125			sctl_io.data = len ? data : 0;
126			break;
127	}
128
129	if (ioctl(fd, SIOC_IO, &sctl_io) == -1) {
130		perror("scsi_io");
131		return -1;
132	}
133
134	return sctl_io.cdb_status;
135
136#elif defined OS_sunos || defined OS_solaris
137	struct uscsi_cmd uscsi_cmd;
138	memset(&uscsi_cmd, 0, sizeof uscsi_cmd);
139	uscsi_cmd.uscsi_cdb = (char *)cdb;
140	uscsi_cmd.uscsi_cdblen = cmdlen;
141#ifdef OS_solaris
142	uscsi_cmd.uscsi_timeout = 20;  /* msec? */
143#endif /* solaris */
144
145	uscsi_cmd.uscsi_buflen = (u_int)len;
146	uscsi_cmd.uscsi_bufaddr = data;
147
148	switch (mode) {
149		case SCSI_IO_READ:
150			uscsi_cmd.uscsi_flags = USCSI_READ;
151			break;
152		case SCSI_IO_WRITE:
153			uscsi_cmd.uscsi_flags = USCSI_WRITE;
154			break;
155	}
156
157	if (ioctl(fd, USCSICMD, &uscsi_cmd) == -1) {
158		perror("scsi_io");
159		return -1;
160	}
161
162	if(uscsi_cmd.uscsi_status) {
163		errno = 0;
164		fprintf(stderr,"scsi status=%x\n",
165			(unsigned short)uscsi_cmd.uscsi_status);
166		return -1;
167	}
168
169	return 0;
170
171#elif defined OS_linux
172	struct scsi_ioctl_command my_scsi_cmd;
173
174
175	memcpy(my_scsi_cmd.cmd, cdb, cmdlen);        /* copy command */
176
177	switch (mode) {
178		case SCSI_IO_READ:
179			my_scsi_cmd.inlen = 0;
180			my_scsi_cmd.outlen = len;
181			break;
182		case SCSI_IO_WRITE:
183			my_scsi_cmd.inlen = len;
184			my_scsi_cmd.outlen = 0;
185			memcpy(my_scsi_cmd.cmd + cmdlen,data,len);
186			break;
187	}
188
189	if (ioctl(fd, SCSI_IOCTL_SEND_COMMAND, &my_scsi_cmd) < 0) {
190		perror("scsi_io");
191		return -1;
192	}
193
194	switch (mode) {
195		case SCSI_IO_READ:
196			memcpy(data, &my_scsi_cmd.cmd[0], len);
197			break;
198		case SCSI_IO_WRITE:
199			break;
200    }
201
202	return 0;  /* where to get scsi status? */
203
204#elif (defined _SCO_DS) && (defined SCSIUSERCMD)
205	struct scsicmd my_scsi_cmd;
206
207	memset(my_scsi_cmd.cdb, 0, SCSICMDLEN);	/* ensure zero pad */
208	memcpy(my_scsi_cmd.cdb, cdb, cmdlen);
209	my_scsi_cmd.cdb_len = cmdlen;
210	my_scsi_cmd.data_len = len;
211	my_scsi_cmd.data_ptr = data;
212	my_scsi_cmd.is_write = mode == SCSI_IO_WRITE;
213	if (ioctl(fd,SCSIUSERCMD,&my_scsi_cmd) == -1) {
214		perror("scsi_io: SCSIUSERCMD");
215		return -1;
216	}
217	if (my_scsi_cmd.host_sts != 0 || my_scsi_cmd.target_sts != 0) {
218		fprintf(stderr, "scsi_io: scsi status: host=%x; target=%x\n",
219		(unsigned)my_scsi_cmd.host_sts,(unsigned)my_scsi_cmd.target_sts);
220		return -1;
221	}
222	return 0;
223#elif defined sgi
224 	struct dsreq my_scsi_cmd;
225
226	my_scsi_cmd.ds_cmdbuf = (char *)cdb;
227	my_scsi_cmd.ds_cmdlen = cmdlen;
228	my_scsi_cmd.ds_databuf = data;
229	my_scsi_cmd.ds_datalen = len;
230       	switch (mode) {
231	case SCSI_IO_READ:
232	  my_scsi_cmd.ds_flags = DSRQ_READ|DSRQ_SENSE;
233	  break;
234	case SCSI_IO_WRITE:
235	  my_scsi_cmd.ds_flags = DSRQ_WRITE|DSRQ_SENSE;
236	  break;
237        }
238	my_scsi_cmd.ds_time = 10000;
239	my_scsi_cmd.ds_link = 0;
240	my_scsi_cmd.ds_synch =0;
241	my_scsi_cmd.ds_ret =0;
242	if (ioctl(fd, DS_ENTER, &my_scsi_cmd) == -1) {
243                perror("scsi_io");
244                return -1;
245        }
246
247        if(my_scsi_cmd.ds_status) {
248                errno = 0;
249                fprintf(stderr,"scsi status=%x\n",
250                        (unsigned short)my_scsi_cmd.ds_status);
251                return -1;
252        }
253
254        return 0;
255#elif (defined OS_freebsd) && (__FreeBSD__ >= 2)
256#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
257      union ccb *ccb;
258      int flags;
259      int r;
260      struct cam_device *cam_dev = (struct cam_device *) extra_data;
261
262
263      if (cam_dev==NULL || cam_dev->fd!=fd)
264      {
265                fprintf(stderr,"invalid file descriptor\n");
266              return -1;
267      }
268      ccb = cam_getccb(cam_dev);
269
270      bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cmdlen);
271
272      if (mode == SCSI_IO_READ)
273              flags = CAM_DIR_IN;
274      else if (data && len)
275              flags = CAM_DIR_OUT;
276      else
277              flags = CAM_DIR_NONE;
278      cam_fill_csio(&ccb->csio,
279                    /* retry */ 1,
280                    /* cbfcnp */ NULL,
281                    flags,
282                    /* tag_action */ MSG_SIMPLE_Q_TAG,
283                    /*data_ptr*/ len ? data : 0,
284                    /*data_len */ data ? len : 0,
285                    96,
286                    cmdlen,
287                    5000);
288
289      if (cam_send_ccb(cam_dev, ccb) < 0 ||
290	  (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
291	  return -1;
292      }
293      return 0;
294#elif defined(OS_netbsd) || defined(OS_netbsdelf)
295 	struct scsireq sc;
296
297	memset(&sc, 0, sizeof(sc));
298	memcpy(sc.cmd, cdb, cmdlen);
299	sc.cmdlen = cmdlen;
300	sc.databuf = data;
301	sc.datalen = len;
302	sc.senselen = 0;
303	sc.timeout = 10000;
304	switch (mode) {
305	case SCSI_IO_READ:
306	  sc.flags = SCCMD_READ;
307	  break;
308	case SCSI_IO_WRITE:
309	  sc.flags = SCCMD_WRITE;
310	  break;
311	}
312
313	if (ioctl(fd, SCIOCCOMMAND, &sc) == -1) {
314                perror("SCIOCCOMMAND ioctl");
315                return -1;
316	}
317
318	if (sc.retsts) {
319                errno = EIO;
320                fprintf(stderr, "SCSI command failed, retsts %d\n",
321sc.retsts);
322                return -1;
323	}
324
325        return 0;
326#else
327      fprintf(stderr, "scsi_io not implemented\n");
328      return -1;
329#endif
330}
331