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