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