Home | History | Annotate | Line # | Download | only in mmcformat
      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 (at) 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
     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
     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
    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
    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
    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
    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
    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
    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
    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 
    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
    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
    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
    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
    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
    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
    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
    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
    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