1 /* $NetBSD: uscsi_subr.c,v 1.7 2002/10/08 20:17:06 soren Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
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.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
32 * Small changes, generalisations and Linux support by Reinoud Zandijk
33 * <reinoud@netbsd.org>.
39 * SCSI support subroutines.
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
51 #include <sys/types.h>
58 int uscsilib_verbose
= 0;
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
69 #include <sys/scsiio.h>
71 #include <scsi/uscsi_all.h>
73 #include <dev/scsipi/scsipi_all.h>
78 uscsi_open(struct uscsi_dev
*disc
)
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");
88 if (fstat(disc
->fhandle
, &dstat
) < 0) {
89 perror("Can't stat device or file");
99 uscsi_close(struct uscsi_dev
* disc
)
101 close(disc
->fhandle
);
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
)
115 memset(&req
, 0, sizeof(req
));
117 bzero(uscsi_sense
, sizeof(struct uscsi_sense
));
119 memcpy(req
.cmd
, cmd
, cmdlen
);
122 req
.datalen
= datalen
;
123 req
.timeout
= timeout
;
125 req
.senselen
= SENSEBUFLEN
;
127 if (ioctl(disc
->fhandle
, SCIOCCOMMAND
, &req
) == -1)
128 err(1, "SCIOCCOMMAND");
130 if (req
.retsts
== SCCMD_OK
)
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",
139 } else if (req
.retsts
== SCCMD_BUSY
) {
140 if (uscsilib_verbose
)
141 fprintf(stderr
, "%s: device is busy\n",
144 } else if (req
.retsts
== SCCMD_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) |
152 if (uscsilib_verbose
)
153 uscsi_print_sense((char *) disc
->dev_name
,
155 req
.sense
, req
.senselen_used
, 1);
158 if (uscsilib_verbose
)
159 fprintf(stderr
, "%s: device had unknown status %x\n",
168 * The reasoning behind this explicit copy is for compatibility with changes
169 * in our uscsi_addr structure.
172 uscsi_identify(struct uscsi_dev
*disc
, struct uscsi_addr
*saddr
)
174 struct scsi_addr raddr
;
177 bzero(saddr
, sizeof(struct scsi_addr
));
178 error
= ioctl(disc
->fhandle
, SCIOCIDENTIFY
, &raddr
);
179 if (error
) return error
;
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
;
189 saddr
->type
= USCSI_TYPE_ATAPI
;
190 saddr
->addr
.atapi
.atbus
= raddr
.addr
.atapi
.atbus
;
191 saddr
->addr
.atapi
.drive
= raddr
.addr
.atapi
.drive
;
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
;
202 saddr
->type
= USCSI_TYPE_ATAPI
;
203 saddr
->addr
.atapi
.atbus
= raddr
.scbus
; /* overload */
204 saddr
->addr
.atapi
.drive
= raddr
.target
; /* overload */
213 uscsi_check_for_scsi(struct uscsi_dev
*disc
)
215 struct uscsi_addr saddr
;
217 return uscsi_identify(disc
, &saddr
);
219 #endif /* SCSILIB_SCSIPI */
224 #ifdef USCSI_LINUX_SCSI
226 * Support code for Linux SCSI code. It uses the ioctl() way of
227 * communicating since this is more close to the origional NetBSD
228 * scsipi implementation.
231 #include <scsi/scsi.h>
233 #define SENSEBUFLEN 48
237 uscsi_open(struct uscsi_dev
* disc
)
242 /* in Linux we are NOT allowed to open it blocking */
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");
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
);
255 if (fstat(disc
->fhandle
, &stat
) < 0) {
256 perror("Can't stat device or file");
266 uscsi_close(struct uscsi_dev
* disc
)
268 close(disc
->fhandle
);
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
)
281 struct sg_io_hdr req
;
282 uint8_t sense_buffer
[SENSEBUFLEN
];
285 bzero(&req
, sizeof(req
));
286 if (flags
== SG_DXFER_FROM_DEV
) bzero(data
, datalen
);
288 req
.interface_id
= 'S';
289 req
.dxfer_direction
= flags
;
290 req
.cmd_len
= cmdlen
;
291 req
.mx_sb_len
= SENSEBUFLEN
;
293 req
.dxfer_len
= datalen
;
296 req
.sbp
= sense_buffer
;
298 req
.timeout
= timeout
;
300 error
= ioctl(disc
->fhandle
, SG_IO
, &req
);
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) |
311 if (uscsilib_verbose
) {
312 uscsi_print_sense((char *) disc
->dev_name
,
313 cmd
, cmdlen
, sense_buffer
, req
.sb_len_wr
, 1);
322 uscsi_identify(struct uscsi_dev
*disc
, struct uscsi_addr
*saddr
)
324 struct sg_scsi_id sg_scsi_id
;
326 /* target | lun << 8 | channel << 16 | low_ino << 24 */
334 bzero(saddr
, sizeof(struct uscsi_addr
));
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
;
341 /* try 2.4 kernel or older */
342 error
= ioctl(disc
->fhandle
, SG_GET_SCSI_ID
, &sg_scsi_id
);
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
;
351 /* 2.6 kernel or newer */
352 error
= ioctl(disc
->fhandle
, SCSI_IOCTL_GET_IDLUN
, &sg_id
);
353 if (error
) return error
;
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;
363 int uscsi_check_for_scsi(struct uscsi_dev
*disc
) {
364 struct uscsi_addr saddr
;
366 return uscsi_identify(disc
, &saddr
);
368 #endif /* USCSI_LINUX_SCSI */
373 #ifdef USCSI_FREEBSD_CAM
376 uscsi_open(struct uscsi_dev
*disc
)
378 disc
->devhandle
= cam_open_device(disc
->dev_name
, O_RDWR
);
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");
393 uscsi_close(struct uscsi_dev
*disc
)
395 if (disc
->devhandle
!= NULL
) {
396 cam_close_device(disc
->devhandle
);
397 disc
->devhandle
= NULL
;
399 close(disc
->fhandle
);
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
)
413 struct cam_device
*cam_dev
;
414 struct scsi_sense_data
*cam_sense_data
;
420 memset(&ccb
, 0, sizeof(ccb
));
421 cam_dev
= (struct cam_device
*) disc
->devhandle
;
423 if (datalen
== 0) flags
= SCSI_NODATACMD
;
425 /* if (data) assert(flags == SCSI_NODATACMD); */
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
;
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 */
446 /* Disable freezing the device queue */
447 ccb
.ccb_h
.flags
|= CAM_DEV_QFRZDIS
;
449 memcpy(ccb
.csio
.cdb_io
.cdb_bytes
, cmd
, cmdlen
);
451 /* Send the command down via the CAM interface */
452 if (cam_send_ccb(cam_dev
, &ccb
) < 0) {
453 err(1, "cam_send_ccb");
456 if ((ccb
.ccb_h
.status
& CAM_STATUS_MASK
) == CAM_REQ_CMP
)
459 /* print error using the uscsi_sense routines? */
461 cam_sense
= (ccb
.ccb_h
.status
& (CAM_STATUS_MASK
| CAM_AUTOSNS_VALID
));
462 if (cam_sense
!= (CAM_SCSI_STATUS_ERROR
| CAM_AUTOSNS_VALID
))
465 /* drive responds with sense information */
466 if (!uscsilib_verbose
)
469 /* print sense info */
470 cam_sense_data
= &ccb
.csio
.sense_data
;
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]);
479 uscsi_print_sense((char *) disc
->dev_name
,
481 (uint8_t *) cam_sense_data
, 8 + cam_sense_data
->extra_len
, 1);
488 uscsi_identify(struct uscsi_dev
*disc
, struct uscsi_addr
*saddr
)
490 struct cam_device
*cam_dev
;
493 bzero(saddr
, sizeof(struct uscsi_addr
));
495 cam_dev
= (struct cam_device
*) disc
->devhandle
;
496 if (!cam_dev
) return ENODEV
;
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
;
509 uscsi_check_for_scsi(struct uscsi_dev
*disc
)
511 struct uscsi_addr saddr
;
513 return uscsi_identify(disc
, &saddr
);
516 #endif /* USCSI_FREEBSD_CAM */
521 * Generic SCSI funtions also used by the sense printing functionality.
522 * FreeBSD support has it allready asked for by the CAM.
526 uscsi_mode_sense(struct uscsi_dev
*dev
,
527 uint8_t pgcode
, uint8_t pctl
, void *buf
, size_t len
)
531 bzero(buf
, len
); /* initialise recieving buffer */
533 bzero(cmd
, SCSI_CMD_LEN
);
534 cmd
[ 0] = 0x1a; /* MODE SENSE */
536 cmd
[ 2] = pgcode
| pctl
; /* page code and control flags */
538 cmd
[ 4] = len
; /* length of recieve buffer */
539 cmd
[ 5] = 0; /* control */
541 return uscsi_command(SCSI_READCMD
, dev
, &cmd
, 6, buf
, len
, 10000, NULL
);
546 uscsi_mode_select(struct uscsi_dev
*dev
,
547 uint8_t byte2
, void *buf
, size_t len
)
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 */
557 return uscsi_command(SCSI_WRITECMD
, dev
, &cmd
, 6, buf
, len
,
563 uscsi_request_sense(struct uscsi_dev
*dev
, void *buf
, size_t len
)
567 bzero(buf
, len
); /* initialise recieving buffer */
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 */
574 return uscsi_command(SCSI_WRITECMD
, dev
, &cmd
, 6, buf
, len
,
579 /* end of uscsi_subr.c */