xref: /netbsd/usr.sbin/mmcformat/uscsi_subr.c (revision bce879b8)
1*bce879b8Sandvar /* $NetBSD: uscsi_subr.c,v 1.5 2022/05/28 21:14:57 andvar Exp $	*/
2e979c658Sreinoud 
3e979c658Sreinoud /*-
4e979c658Sreinoud  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5e979c658Sreinoud  * All rights reserved.
6e979c658Sreinoud  *
7e979c658Sreinoud  * This code is derived from software contributed to The NetBSD Foundation
8e979c658Sreinoud  * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace
9e979c658Sreinoud  * Simulation Facility, NASA Ames Research Center.
10e979c658Sreinoud  *
11e979c658Sreinoud  * Redistribution and use in source and binary forms, with or without
12e979c658Sreinoud  * modification, are permitted provided that the following conditions
13e979c658Sreinoud  * are met:
14e979c658Sreinoud  * 1. Redistributions of source code must retain the above copyright
15e979c658Sreinoud  *    notice, this list of conditions and the following disclaimer.
16e979c658Sreinoud  * 2. Redistributions in binary form must reproduce the above copyright
17e979c658Sreinoud  *    notice, this list of conditions and the following disclaimer in the
18e979c658Sreinoud  *    documentation and/or other materials provided with the distribution.
19e979c658Sreinoud  *
20e979c658Sreinoud  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21e979c658Sreinoud  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22e979c658Sreinoud  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23e979c658Sreinoud  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24e979c658Sreinoud  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25e979c658Sreinoud  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26e979c658Sreinoud  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27e979c658Sreinoud  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28e979c658Sreinoud  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29e979c658Sreinoud  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30e979c658Sreinoud  * POSSIBILITY OF SUCH DAMAGE.
31e979c658Sreinoud  *
32e979c658Sreinoud  * Small changes, generalisations and Linux support by Reinoud Zandijk
33e979c658Sreinoud  * <reinoud@netbsd.org>.
34e979c658Sreinoud  *
35e979c658Sreinoud  */
36e979c658Sreinoud 
37e979c658Sreinoud 
38e979c658Sreinoud /*
39e979c658Sreinoud  * SCSI support subroutines.
40e979c658Sreinoud  */
41e979c658Sreinoud 
42e979c658Sreinoud #include <sys/param.h>
43e979c658Sreinoud #include <sys/ioctl.h>
44e979c658Sreinoud #include <err.h>
45e979c658Sreinoud #include <errno.h>
46e979c658Sreinoud #include <stdio.h>
47e979c658Sreinoud #include <stdlib.h>
48e979c658Sreinoud #include <string.h>
49e979c658Sreinoud #include <unistd.h>
50e979c658Sreinoud #include <fcntl.h>
51e979c658Sreinoud #include <sys/types.h>
52e979c658Sreinoud #include <inttypes.h>
53e979c658Sreinoud #include <assert.h>
54e979c658Sreinoud 
55e979c658Sreinoud #include "uscsilib.h"
56e979c658Sreinoud 
57e979c658Sreinoud 
58e979c658Sreinoud int uscsilib_verbose = 0;
59e979c658Sreinoud 
60e979c658Sreinoud 
61e979c658Sreinoud #ifdef USCSI_SCSIPI
62e979c658Sreinoud 	/*
63e979c658Sreinoud 	 * scsipi is a integrated SCSI and ATAPI layer under NetBSD and exists
64e979c658Sreinoud 	 * in a modified form under OpenBSD and possibly also under other
65e979c658Sreinoud 	 * operating systems.
66e979c658Sreinoud 	 */
67e979c658Sreinoud 
68e979c658Sreinoud 
69e979c658Sreinoud #include <sys/scsiio.h>
70e979c658Sreinoud #ifdef __OpenBSD__
71e979c658Sreinoud #include <scsi/uscsi_all.h>
72e979c658Sreinoud #else
73e979c658Sreinoud #include <dev/scsipi/scsipi_all.h>
74e979c658Sreinoud #endif
75e979c658Sreinoud 
76e979c658Sreinoud 
77e979c658Sreinoud int
uscsi_open(struct uscsi_dev * disc)78e979c658Sreinoud uscsi_open(struct uscsi_dev *disc)
79e979c658Sreinoud {
80e979c658Sreinoud 	struct stat dstat;
81e979c658Sreinoud 
82e979c658Sreinoud 	disc->fhandle = open(disc->dev_name, O_RDWR, 0); /* no create */
83e979c658Sreinoud 	if (disc->fhandle<0) {
84e979c658Sreinoud 		perror("Failure to open device or file");
85e979c658Sreinoud 		return ENODEV;
86e979c658Sreinoud 	}
87e979c658Sreinoud 
88e979c658Sreinoud 	if (fstat(disc->fhandle, &dstat) < 0) {
89e979c658Sreinoud 		perror("Can't stat device or file");
90e979c658Sreinoud 		uscsi_close(disc);
91e979c658Sreinoud 		return ENODEV;
92e979c658Sreinoud 	}
93e979c658Sreinoud 
94e979c658Sreinoud 	return 0;
95e979c658Sreinoud }
96e979c658Sreinoud 
97e979c658Sreinoud 
98e979c658Sreinoud int
uscsi_close(struct uscsi_dev * disc)99e979c658Sreinoud uscsi_close(struct uscsi_dev * disc)
100e979c658Sreinoud {
101e979c658Sreinoud 	close(disc->fhandle);
102e979c658Sreinoud 	disc->fhandle = -1;
103e979c658Sreinoud 
104e979c658Sreinoud 	return 0;
105e979c658Sreinoud }
106e979c658Sreinoud 
107e979c658Sreinoud 
108e979c658Sreinoud int
uscsi_command(int flags,struct uscsi_dev * disc,void * cmd,size_t cmdlen,void * data,size_t datalen,uint32_t timeout,struct uscsi_sense * uscsi_sense)109e979c658Sreinoud uscsi_command(int flags, struct uscsi_dev *disc,
110e979c658Sreinoud 	void *cmd, size_t cmdlen, void *data, size_t datalen,
111e979c658Sreinoud 	uint32_t timeout, struct uscsi_sense *uscsi_sense)
112e979c658Sreinoud {
113e979c658Sreinoud 	scsireq_t req;
114e979c658Sreinoud 
115e979c658Sreinoud 	memset(&req, 0, sizeof(req));
116e979c658Sreinoud 	if (uscsi_sense)
117e979c658Sreinoud 		bzero(uscsi_sense, sizeof(struct uscsi_sense));
118e979c658Sreinoud 
119e979c658Sreinoud 	memcpy(req.cmd, cmd, cmdlen);
120e979c658Sreinoud 	req.cmdlen = cmdlen;
121e979c658Sreinoud 	req.databuf = data;
122e979c658Sreinoud 	req.datalen = datalen;
123e979c658Sreinoud 	req.timeout = timeout;
124e979c658Sreinoud 	req.flags = flags;
125e979c658Sreinoud 	req.senselen = SENSEBUFLEN;
126e979c658Sreinoud 
127e979c658Sreinoud 	if (ioctl(disc->fhandle, SCIOCCOMMAND, &req) == -1)
128e979c658Sreinoud 		err(1, "SCIOCCOMMAND");
129e979c658Sreinoud 
130e979c658Sreinoud 	if (req.retsts == SCCMD_OK)
131e979c658Sreinoud 		return 0;
132e979c658Sreinoud 
133e979c658Sreinoud 	/* Some problem; report it and exit. */
134e979c658Sreinoud 	if (req.retsts == SCCMD_TIMEOUT) {
135e979c658Sreinoud 		if (uscsilib_verbose)
136e979c658Sreinoud 			fprintf(stderr, "%s: SCSI command timed out\n",
137e979c658Sreinoud 				disc->dev_name);
138e979c658Sreinoud 		return EAGAIN;
139e979c658Sreinoud 	} else if (req.retsts == SCCMD_BUSY) {
140e979c658Sreinoud 		if (uscsilib_verbose)
141e979c658Sreinoud 			fprintf(stderr, "%s: device is busy\n",
142e979c658Sreinoud 				disc->dev_name);
143e979c658Sreinoud 		return EBUSY;
144e979c658Sreinoud 	} else if (req.retsts == SCCMD_SENSE) {
145e979c658Sreinoud 		if (uscsi_sense) {
146e979c658Sreinoud 			uscsi_sense->asc        =  req.sense[12];
147e979c658Sreinoud 			uscsi_sense->ascq       =  req.sense[13];
148e979c658Sreinoud 			uscsi_sense->skey_valid =  req.sense[15] & 128;
149e979c658Sreinoud 			uscsi_sense->sense_key  = (req.sense[16] << 8) |
150e979c658Sreinoud 						  (req.sense[17]);
151e979c658Sreinoud 		}
152e979c658Sreinoud 		if (uscsilib_verbose)
153e979c658Sreinoud 			uscsi_print_sense((char *) disc->dev_name,
154e979c658Sreinoud 				req.cmd, req.cmdlen,
155e979c658Sreinoud 				req.sense, req.senselen_used, 1);
156e979c658Sreinoud 		return EIO;
157e979c658Sreinoud 	} else
158e979c658Sreinoud 		if (uscsilib_verbose)
159e979c658Sreinoud 			fprintf(stderr, "%s: device had unknown status %x\n",
160e979c658Sreinoud 				disc->dev_name,
161e979c658Sreinoud 		  	  req.retsts);
162e979c658Sreinoud 
163e979c658Sreinoud 	return EFAULT;
164e979c658Sreinoud }
165e979c658Sreinoud 
166e979c658Sreinoud 
167e979c658Sreinoud /*
168e979c658Sreinoud  * The reasoning behind this explicit copy is for compatibility with changes
169e979c658Sreinoud  * in our uscsi_addr structure.
170e979c658Sreinoud  */
171e979c658Sreinoud int
uscsi_identify(struct uscsi_dev * disc,struct uscsi_addr * saddr)172e979c658Sreinoud uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
173e979c658Sreinoud {
174e979c658Sreinoud 	struct scsi_addr raddr;
175e979c658Sreinoud 	int error;
176e979c658Sreinoud 
177e979c658Sreinoud 	bzero(saddr, sizeof(struct scsi_addr));
178e979c658Sreinoud 	error = ioctl(disc->fhandle, SCIOCIDENTIFY, &raddr);
179e979c658Sreinoud 	if (error) return error;
180e979c658Sreinoud 
181e979c658Sreinoud #ifdef __NetBSD__
182e979c658Sreinoud 	/* scsi and atapi are split up like in uscsi_addr */
183e979c658Sreinoud 	if (raddr.type == 0) {
184e979c658Sreinoud 		saddr->type = USCSI_TYPE_SCSI;
185e979c658Sreinoud 		saddr->addr.scsi.scbus  = raddr.addr.scsi.scbus;
186e979c658Sreinoud 		saddr->addr.scsi.target = raddr.addr.scsi.target;
187e979c658Sreinoud 		saddr->addr.scsi.lun    = raddr.addr.scsi.lun;
188e979c658Sreinoud 	} else {
189e979c658Sreinoud 		saddr->type = USCSI_TYPE_ATAPI;
190e979c658Sreinoud 		saddr->addr.atapi.atbus = raddr.addr.atapi.atbus;
191e979c658Sreinoud 		saddr->addr.atapi.drive = raddr.addr.atapi.drive;
192e979c658Sreinoud 	}
193e979c658Sreinoud #endif
194e979c658Sreinoud #ifdef __OpenBSD__
195e979c658Sreinoud 	/* atapi's are shown as SCSI devices */
196e979c658Sreinoud 	if (raddr.type == 0) {
197e979c658Sreinoud 		saddr->type = USCSI_TYPE_SCSI;
198e979c658Sreinoud 		saddr->addr.scsi.scbus  = raddr.scbus;
199e979c658Sreinoud 		saddr->addr.scsi.target = raddr.target;
200e979c658Sreinoud 		saddr->addr.scsi.lun    = raddr.lun;
201e979c658Sreinoud 	} else {
202e979c658Sreinoud 		saddr->type = USCSI_TYPE_ATAPI;
203e979c658Sreinoud 		saddr->addr.atapi.atbus = raddr.scbus;	/* overload */
204e979c658Sreinoud 		saddr->addr.atapi.drive = raddr.target;	/* overload */
205e979c658Sreinoud 	}
206e979c658Sreinoud #endif
207e979c658Sreinoud 
208e979c658Sreinoud 	return 0;
209e979c658Sreinoud }
210e979c658Sreinoud 
211e979c658Sreinoud 
212e979c658Sreinoud int
uscsi_check_for_scsi(struct uscsi_dev * disc)213e979c658Sreinoud uscsi_check_for_scsi(struct uscsi_dev *disc)
214e979c658Sreinoud {
215e979c658Sreinoud 	struct uscsi_addr	saddr;
216e979c658Sreinoud 
217e979c658Sreinoud 	return uscsi_identify(disc, &saddr);
218e979c658Sreinoud }
219e979c658Sreinoud #endif	/* SCSILIB_SCSIPI */
220e979c658Sreinoud 
221e979c658Sreinoud 
222e979c658Sreinoud 
223e979c658Sreinoud 
224e979c658Sreinoud #ifdef USCSI_LINUX_SCSI
225e979c658Sreinoud 	/*
226e979c658Sreinoud 	 * Support code for Linux SCSI code. It uses the ioctl() way of
227*bce879b8Sandvar 	 * communicating since this is more close to the original NetBSD
228e979c658Sreinoud 	 * scsipi implementation.
229e979c658Sreinoud 	 */
230e979c658Sreinoud #include <scsi/sg.h>
231e979c658Sreinoud #include <scsi/scsi.h>
232e979c658Sreinoud 
233e979c658Sreinoud #define SENSEBUFLEN 48
234e979c658Sreinoud 
235e979c658Sreinoud 
236e979c658Sreinoud int
uscsi_open(struct uscsi_dev * disc)237e979c658Sreinoud uscsi_open(struct uscsi_dev * disc)
238e979c658Sreinoud {
239e979c658Sreinoud 	int flags;
240e979c658Sreinoud 	struct stat stat;
241e979c658Sreinoud 
242e979c658Sreinoud 	/* in Linux we are NOT allowed to open it blocking */
243e979c658Sreinoud 	/* no create! */
244e979c658Sreinoud 	disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0);
245e979c658Sreinoud 	if (disc->fhandle<0) {
246e979c658Sreinoud 		perror("Failure to open device or file");
247e979c658Sreinoud 		return ENODEV;
248e979c658Sreinoud 	}
249e979c658Sreinoud 
250e979c658Sreinoud 	/* explicitly mark it non blocking (again) (silly Linux) */
251e979c658Sreinoud 	flags = fcntl(disc->fhandle, F_GETFL);
252e979c658Sreinoud 	flags &= ~O_NONBLOCK;
253e979c658Sreinoud 	fcntl(disc->fhandle, F_SETFL, flags);
254e979c658Sreinoud 
255e979c658Sreinoud 	if (fstat(disc->fhandle, &stat) < 0) {
256e979c658Sreinoud 		perror("Can't stat device or file");
257e979c658Sreinoud 		uscsi_close(disc);
258e979c658Sreinoud 		return ENODEV;
259e979c658Sreinoud 	}
260e979c658Sreinoud 
261e979c658Sreinoud 	return 0;
262e979c658Sreinoud }
263e979c658Sreinoud 
264e979c658Sreinoud 
265e979c658Sreinoud int
uscsi_close(struct uscsi_dev * disc)266e979c658Sreinoud uscsi_close(struct uscsi_dev * disc)
267e979c658Sreinoud {
268e979c658Sreinoud 	close(disc->fhandle);
269e979c658Sreinoud 	disc->fhandle = -1;
270e979c658Sreinoud 
271e979c658Sreinoud 	return 0;
272e979c658Sreinoud }
273e979c658Sreinoud 
274e979c658Sreinoud 
275e979c658Sreinoud int
uscsi_command(int flags,struct uscsi_dev * disc,void * cmd,size_t cmdlen,void * data,size_t datalen,uint32_t timeout,struct uscsi_sense * uscsi_sense)276e979c658Sreinoud uscsi_command(int flags, struct uscsi_dev *disc,
277e979c658Sreinoud 	void *cmd, size_t cmdlen,
278e979c658Sreinoud 	void *data, size_t datalen,
279e979c658Sreinoud 	uint32_t timeout, struct uscsi_sense *uscsi_sense)
280e979c658Sreinoud {
281e979c658Sreinoud 	struct sg_io_hdr req;
282e979c658Sreinoud 	uint8_t sense_buffer[SENSEBUFLEN];
283e979c658Sreinoud 	int error;
284e979c658Sreinoud 
285e979c658Sreinoud 	bzero(&req, sizeof(req));
286e979c658Sreinoud 	if (flags == SG_DXFER_FROM_DEV) bzero(data, datalen);
287e979c658Sreinoud 
288e979c658Sreinoud 	req.interface_id    = 'S';
289e979c658Sreinoud 	req.dxfer_direction = flags;
290e979c658Sreinoud 	req.cmd_len	    = cmdlen;
291e979c658Sreinoud 	req.mx_sb_len	    = SENSEBUFLEN;
292e979c658Sreinoud 	req.iovec_count	    = 0;
293e979c658Sreinoud 	req.dxfer_len	    = datalen;
294e979c658Sreinoud 	req.dxferp	    = data;
295e979c658Sreinoud 	req.cmdp	    = cmd;
296e979c658Sreinoud 	req.sbp		    = sense_buffer;
297e979c658Sreinoud 	req.flags	    = 0;
298e979c658Sreinoud 	req.timeout	    = timeout;
299e979c658Sreinoud 
300e979c658Sreinoud 	error = ioctl(disc->fhandle, SG_IO, &req);
301e979c658Sreinoud 
302e979c658Sreinoud 	if (req.status) {
303e979c658Sreinoud 		/* Is this OK? */
304e979c658Sreinoud 		if (uscsi_sense) {
305e979c658Sreinoud 			uscsi_sense->asc        =  sense_buffer[12];
306e979c658Sreinoud 			uscsi_sense->ascq       =  sense_buffer[13];
307e979c658Sreinoud 			uscsi_sense->skey_valid =  sense_buffer[15] & 128;
308e979c658Sreinoud 			uscsi_sense->sense_key  = (sense_buffer[16] << 8) |
309e979c658Sreinoud 						  (sense_buffer[17]);
310e979c658Sreinoud 		}
311e979c658Sreinoud 		if (uscsilib_verbose) {
312e979c658Sreinoud 			uscsi_print_sense((char *) disc->dev_name,
313e979c658Sreinoud 				cmd, cmdlen, sense_buffer, req.sb_len_wr, 1);
314e979c658Sreinoud 		}
315e979c658Sreinoud 	}
316e979c658Sreinoud 
317e979c658Sreinoud 	return error;
318e979c658Sreinoud }
319e979c658Sreinoud 
320e979c658Sreinoud 
321e979c658Sreinoud int
uscsi_identify(struct uscsi_dev * disc,struct uscsi_addr * saddr)322e979c658Sreinoud uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
323e979c658Sreinoud {
324e979c658Sreinoud 	struct sg_scsi_id sg_scsi_id;
325e979c658Sreinoud 	struct sg_id {
326e979c658Sreinoud 		/* target | lun << 8 | channel << 16 | low_ino << 24 */
327e979c658Sreinoud 		uint32_t tlci;
328e979c658Sreinoud 		uint32_t uniq_id;
329e979c658Sreinoud 	} sg_id;
330e979c658Sreinoud 	int emulated;
331e979c658Sreinoud 	int error;
332e979c658Sreinoud 
333e979c658Sreinoud 	/* clean result */
334e979c658Sreinoud 	bzero(saddr, sizeof(struct uscsi_addr));
335e979c658Sreinoud 
336e979c658Sreinoud 	/* check if its really SCSI or emulated SCSI (ATAPI f.e.) */
337e979c658Sreinoud 	saddr->type = USCSI_TYPE_SCSI;
338e979c658Sreinoud 	ioctl(disc->fhandle, SG_EMULATED_HOST, &emulated);
339e979c658Sreinoud 	if (emulated) saddr->type = USCSI_TYPE_ATAPI;
340e979c658Sreinoud 
341e979c658Sreinoud 	/* try 2.4 kernel or older */
342e979c658Sreinoud 	error = ioctl(disc->fhandle, SG_GET_SCSI_ID, &sg_scsi_id);
343e979c658Sreinoud 	if (!error) {
344e979c658Sreinoud 		saddr->addr.scsi.target = sg_scsi_id.scsi_id;
345e979c658Sreinoud 		saddr->addr.scsi.lun    = sg_scsi_id.lun;
346e979c658Sreinoud 		saddr->addr.scsi.scbus  = sg_scsi_id.channel;
347e979c658Sreinoud 
348e979c658Sreinoud 		return 0;
349e979c658Sreinoud 	}
350e979c658Sreinoud 
351e979c658Sreinoud 	/* 2.6 kernel or newer */
352e979c658Sreinoud  	error = ioctl(disc->fhandle, SCSI_IOCTL_GET_IDLUN, &sg_id);
353e979c658Sreinoud 	if (error) return error;
354e979c658Sreinoud 
355e979c658Sreinoud 	saddr->addr.scsi.target = (sg_id.tlci      ) & 0xff;
356e979c658Sreinoud 	saddr->addr.scsi.lun    = (sg_id.tlci >>  8) & 0xff;
357e979c658Sreinoud 	saddr->addr.scsi.scbus  = (sg_id.tlci >> 16) & 0xff;
358e979c658Sreinoud 
359e979c658Sreinoud 	return 0;
360e979c658Sreinoud }
361e979c658Sreinoud 
362e979c658Sreinoud 
uscsi_check_for_scsi(struct uscsi_dev * disc)363e979c658Sreinoud int uscsi_check_for_scsi(struct uscsi_dev *disc) {
364e979c658Sreinoud 	struct uscsi_addr saddr;
365e979c658Sreinoud 
366e979c658Sreinoud 	return uscsi_identify(disc, &saddr);
367e979c658Sreinoud }
368e979c658Sreinoud #endif	/* USCSI_LINUX_SCSI */
369e979c658Sreinoud 
370e979c658Sreinoud 
371e979c658Sreinoud 
372e979c658Sreinoud 
373e979c658Sreinoud #ifdef USCSI_FREEBSD_CAM
374e979c658Sreinoud 
375e979c658Sreinoud int
uscsi_open(struct uscsi_dev * disc)376e979c658Sreinoud uscsi_open(struct uscsi_dev *disc)
377e979c658Sreinoud {
378e979c658Sreinoud 	disc->devhandle = cam_open_device(disc->dev_name, O_RDWR);
379e979c658Sreinoud 
380e979c658Sreinoud 	if (disc->devhandle == NULL) {
381e979c658Sreinoud 		disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0);
382e979c658Sreinoud 		if (disc->fhandle < 0) {
383e979c658Sreinoud 			perror("Failure to open device or file");
384e979c658Sreinoud 			return ENODEV;
385e979c658Sreinoud 		}
386e979c658Sreinoud 	}
387e979c658Sreinoud 
388e979c658Sreinoud 	return 0;
389e979c658Sreinoud }
390e979c658Sreinoud 
391e979c658Sreinoud 
392e979c658Sreinoud int
uscsi_close(struct uscsi_dev * disc)393e979c658Sreinoud uscsi_close(struct uscsi_dev *disc)
394e979c658Sreinoud {
395e979c658Sreinoud 	if (disc->devhandle != NULL) {
396e979c658Sreinoud 		cam_close_device(disc->devhandle);
397e979c658Sreinoud 		disc->devhandle = NULL;
398e979c658Sreinoud 	} else {
399e979c658Sreinoud 		close(disc->fhandle);
400e979c658Sreinoud 		disc->fhandle = -1;
401e979c658Sreinoud 	}
402e979c658Sreinoud 
403e979c658Sreinoud 	return 0;
404e979c658Sreinoud }
405e979c658Sreinoud 
406e979c658Sreinoud 
407e979c658Sreinoud int
uscsi_command(int flags,struct uscsi_dev * disc,void * cmd,size_t cmdlen,void * data,size_t datalen,uint32_t timeout,struct uscsi_sense * uscsi_sense)408e979c658Sreinoud uscsi_command(int flags, struct uscsi_dev *disc,
409e979c658Sreinoud 	void *cmd, size_t cmdlen,
410e979c658Sreinoud 	void *data, size_t datalen,
411e979c658Sreinoud 	uint32_t timeout, struct uscsi_sense *uscsi_sense)
412e979c658Sreinoud {
413e979c658Sreinoud 	struct cam_device *cam_dev;
414e979c658Sreinoud 	struct scsi_sense_data *cam_sense_data;
415e979c658Sreinoud 	union ccb ccb;
416e979c658Sreinoud 	uint32_t cam_sense;
417e979c658Sreinoud 	uint8_t *keypos;
418e979c658Sreinoud 	int camflags;
419e979c658Sreinoud 
420e979c658Sreinoud 	memset(&ccb, 0, sizeof(ccb));
421e979c658Sreinoud 	cam_dev = (struct cam_device *) disc->devhandle;
422e979c658Sreinoud 
423e979c658Sreinoud 	if (datalen == 0) flags = SCSI_NODATACMD;
424e979c658Sreinoud 	/* optional : */
425e979c658Sreinoud 	/* if (data) assert(flags == SCSI_NODATACMD); */
426e979c658Sreinoud 
427e979c658Sreinoud 	camflags = CAM_DIR_NONE;
428e979c658Sreinoud 	if (flags & SCSI_READCMD)
429e979c658Sreinoud 		camflags = CAM_DIR_IN;
430e979c658Sreinoud 	if (flags & SCSI_WRITECMD)
431e979c658Sreinoud 		camflags = CAM_DIR_OUT;
432e979c658Sreinoud 
433e979c658Sreinoud 	cam_fill_csio(
434e979c658Sreinoud 		&ccb.csio,
435e979c658Sreinoud 		0,			/* retries */
436e979c658Sreinoud 		NULL,			/* cbfcnp */
437e979c658Sreinoud 		camflags,		/* flags */
438e979c658Sreinoud 		MSG_SIMPLE_Q_TAG,	/* tag_action */
439e979c658Sreinoud 		(u_int8_t *) data,	/* data_ptr */
440e979c658Sreinoud 		datalen,		/* dxfer_len */
441e979c658Sreinoud 		SSD_FULL_SIZE,		/* sense_len */
442e979c658Sreinoud 		cmdlen,			/* cdb_len */
443e979c658Sreinoud 		timeout			/* timeout */
444e979c658Sreinoud 	);
445e979c658Sreinoud 
446e979c658Sreinoud 	/* Disable freezing the device queue */
447e979c658Sreinoud 	ccb.ccb_h.flags |= CAM_DEV_QFRZDIS;
448e979c658Sreinoud 
449e979c658Sreinoud 	memcpy(ccb.csio.cdb_io.cdb_bytes, cmd, cmdlen);
450e979c658Sreinoud 
451e979c658Sreinoud 	/* Send the command down via the CAM interface */
452e979c658Sreinoud 	if (cam_send_ccb(cam_dev, &ccb) < 0) {
453e979c658Sreinoud 		err(1, "cam_send_ccb");
454e979c658Sreinoud 	}
455e979c658Sreinoud 
456e979c658Sreinoud 	if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
457e979c658Sreinoud 		return 0;
458e979c658Sreinoud 
459e979c658Sreinoud 	/* print error using the uscsi_sense routines? */
460e979c658Sreinoud 
461e979c658Sreinoud 	cam_sense = (ccb.ccb_h.status & (CAM_STATUS_MASK | CAM_AUTOSNS_VALID));
462e979c658Sreinoud 	if (cam_sense != (CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID))
463e979c658Sreinoud 		return EFAULT;
464e979c658Sreinoud 
465e979c658Sreinoud 	/* drive responds with sense information */
466e979c658Sreinoud 	if (!uscsilib_verbose)
467e979c658Sreinoud 		return EFAULT;
468e979c658Sreinoud 
469e979c658Sreinoud 	/* print sense info */
470e979c658Sreinoud 	cam_sense_data = &ccb.csio.sense_data;
471e979c658Sreinoud 	if (uscsi_sense) {
472e979c658Sreinoud 		uscsi_sense->asc  = cam_sense_data->add_sense_code;
473e979c658Sreinoud 		uscsi_sense->ascq = cam_sense_data->add_sense_code_qual;
474e979c658Sreinoud 		keypos  = cam_sense_data->sense_key_spec;
475e979c658Sreinoud 		uscsi_sense->skey_valid =  keypos[0] & 128;
476e979c658Sreinoud 		uscsi_sense->sense_key  = (keypos[1] << 8) | (keypos[2]);
477e979c658Sreinoud 	}
478e979c658Sreinoud 
479e979c658Sreinoud 	uscsi_print_sense((char *) disc->dev_name,
480e979c658Sreinoud 		cmd, cmdlen,
481e979c658Sreinoud 		(uint8_t *) cam_sense_data, 8 + cam_sense_data->extra_len, 1);
482e979c658Sreinoud 
483e979c658Sreinoud 	return EFAULT;
484e979c658Sreinoud }
485e979c658Sreinoud 
486e979c658Sreinoud 
487e979c658Sreinoud int
uscsi_identify(struct uscsi_dev * disc,struct uscsi_addr * saddr)488e979c658Sreinoud uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
489e979c658Sreinoud {
490e979c658Sreinoud 	struct cam_device *cam_dev;
491e979c658Sreinoud 
492e979c658Sreinoud 	/* clean result */
493e979c658Sreinoud 	bzero(saddr, sizeof(struct uscsi_addr));
494e979c658Sreinoud 
495e979c658Sreinoud 	cam_dev = (struct cam_device *) disc->devhandle;
496e979c658Sreinoud 	if (!cam_dev) return ENODEV;
497e979c658Sreinoud 
498e979c658Sreinoud 	/* check if its really SCSI or emulated SCSI (ATAPI f.e.) ? */
499e979c658Sreinoud 	saddr->type = USCSI_TYPE_SCSI;
500e979c658Sreinoud 	saddr->addr.scsi.target = cam_dev->target_id;
501e979c658Sreinoud 	saddr->addr.scsi.lun    = cam_dev->target_lun;
502e979c658Sreinoud 	saddr->addr.scsi.scbus  = cam_dev->bus_id;
503e979c658Sreinoud 
504e979c658Sreinoud 	return 0;
505e979c658Sreinoud }
506e979c658Sreinoud 
507e979c658Sreinoud 
508e979c658Sreinoud int
uscsi_check_for_scsi(struct uscsi_dev * disc)509e979c658Sreinoud uscsi_check_for_scsi(struct uscsi_dev *disc)
510e979c658Sreinoud {
511e979c658Sreinoud 	struct uscsi_addr saddr;
512e979c658Sreinoud 
513e979c658Sreinoud 	return uscsi_identify(disc, &saddr);
514e979c658Sreinoud }
515e979c658Sreinoud 
516e979c658Sreinoud #endif	/* USCSI_FREEBSD_CAM */
517e979c658Sreinoud 
518e979c658Sreinoud 
519e979c658Sreinoud 
520e979c658Sreinoud /*
5219e3d1407Smsaitoh  * Generic SCSI functions also used by the sense printing functionality.
5222d52435aSandvar  * FreeBSD support has it already asked for by the CAM.
523e979c658Sreinoud  */
524e979c658Sreinoud 
525e979c658Sreinoud int
uscsi_mode_sense(struct uscsi_dev * dev,uint8_t pgcode,uint8_t pctl,void * buf,size_t len)526e979c658Sreinoud uscsi_mode_sense(struct uscsi_dev *dev,
527e979c658Sreinoud 	uint8_t pgcode, uint8_t pctl, void *buf, size_t len)
528e979c658Sreinoud {
529e979c658Sreinoud 	scsicmd cmd;
530e979c658Sreinoud 
531a46a1fcdSmsaitoh 	bzero(buf, len);		/* initialise receiving buffer	*/
532e979c658Sreinoud 
533e979c658Sreinoud 	bzero(cmd, SCSI_CMD_LEN);
534e979c658Sreinoud 	cmd[ 0] = 0x1a;			/* MODE SENSE			*/
535e979c658Sreinoud 	cmd[ 1] = 0;			/* -				*/
536e979c658Sreinoud 	cmd[ 2] = pgcode | pctl;	/* page code and control flags	*/
537e979c658Sreinoud 	cmd[ 3] = 0;			/* -				*/
538a46a1fcdSmsaitoh 	cmd[ 4] = len;			/* length of receive buffer	*/
539e979c658Sreinoud 	cmd[ 5] = 0;			/* control			*/
540e979c658Sreinoud 
541e979c658Sreinoud 	return uscsi_command(SCSI_READCMD, dev, &cmd, 6, buf, len, 10000, NULL);
542e979c658Sreinoud }
543e979c658Sreinoud 
544e979c658Sreinoud 
545e979c658Sreinoud int
uscsi_mode_select(struct uscsi_dev * dev,uint8_t byte2,void * buf,size_t len)546e979c658Sreinoud uscsi_mode_select(struct uscsi_dev *dev,
547e979c658Sreinoud 	uint8_t byte2, void *buf, size_t len)
548e979c658Sreinoud {
549e979c658Sreinoud 	scsicmd cmd;
550e979c658Sreinoud 
551e979c658Sreinoud 	bzero(cmd, SCSI_CMD_LEN);
552e979c658Sreinoud 	cmd[ 0] = 0x15;			/* MODE SELECT			*/
553e979c658Sreinoud 	cmd[ 1] = 0x10 | byte2;		/* SCSI-2 page format select	*/
554e979c658Sreinoud 	cmd[ 4] = len;			/* length of page settings	*/
555e979c658Sreinoud 	cmd[ 5] = 0;			/* control			*/
556e979c658Sreinoud 
557e979c658Sreinoud 	return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len,
558e979c658Sreinoud 			10000, NULL);
559e979c658Sreinoud }
560e979c658Sreinoud 
561e979c658Sreinoud 
562e979c658Sreinoud int
uscsi_request_sense(struct uscsi_dev * dev,void * buf,size_t len)563e979c658Sreinoud uscsi_request_sense(struct uscsi_dev *dev, void *buf, size_t len)
564e979c658Sreinoud {
565e979c658Sreinoud 	scsicmd cmd;
566e979c658Sreinoud 
567a46a1fcdSmsaitoh 	bzero(buf, len);		/* initialise receiving buffer	*/
568e979c658Sreinoud 
569e979c658Sreinoud 	bzero(cmd, SCSI_CMD_LEN);
570e979c658Sreinoud 	cmd[ 0] = 0x03;			/* REQUEST SENSE		*/
571e979c658Sreinoud 	cmd[ 4] = len;			/* length of data to be read	*/
572e979c658Sreinoud 	cmd[ 5] = 0;			/* control			*/
573e979c658Sreinoud 
574e979c658Sreinoud 	return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len,
575e979c658Sreinoud 			10000, NULL);
576e979c658Sreinoud }
577e979c658Sreinoud 
578e979c658Sreinoud 
579e979c658Sreinoud /* end of uscsi_subr.c */
580e979c658Sreinoud 
581