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