1 /*
2  * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <inttypes.h>
33 #include <stdint.h>
34 
35 #include <errno.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <pthread.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 
42 #include <fcntl.h>
43 #include <unistd.h>
44 
45 #ifdef HAVE_UUID_H
46 #include <uuid.h>
47 #endif
48 
49 #include "istgt.h"
50 #include "istgt_ver.h"
51 #include "istgt_log.h"
52 #include "istgt_conf.h"
53 #include "istgt_sock.h"
54 #include "istgt_misc.h"
55 #include "istgt_iscsi.h"
56 #include "istgt_lu.h"
57 #include "istgt_proto.h"
58 #include "istgt_scsi.h"
59 
60 #if !defined(__GNUC__)
61 #undef __attribute__
62 #define __attribute__(x)
63 #endif
64 
65 //#define ISTGT_TRACE_DVD
66 
67 #define DEFAULT_DVD_BLOCKLEN 2048
68 #define DEFAULT_DVD_PROFILE MM_PROF_DVDROM
69 
70 enum {
71 	MM_PROF_CDROM = 0x0008,
72 	MM_PROF_DVDROM = 0x0010,
73 } ISTGT_LU_MM_PROF;
74 
75 typedef struct istgt_lu_dvd_t {
76 	ISTGT_LU_Ptr lu;
77 	int num;
78 	int lun;
79 
80 	int fd;
81 	const char *file;
82 	uint64_t size;
83 	uint64_t blocklen;
84 	uint64_t blockcnt;
85 
86 #ifdef HAVE_UUID_H
87 	uuid_t uuid;
88 #endif /* HAVE_UUID_H */
89 
90 	/* cache flags */
91 	int read_cache;
92 	int write_cache;
93 
94 	/* flags */
95 	int mflags;
96 	/* current DVD/CD profile */
97 	int profile;
98 
99 	/* media state */
100 	volatile int mload;
101 	volatile int mchanged;
102 	volatile int mwait;
103 
104 	/* mode flags */
105 	volatile int lock;
106 
107 	/* SCSI sense code */
108 	volatile int sense;
109 } ISTGT_LU_DVD;
110 
111 #define BUILD_SENSE(SK,ASC,ASCQ)					\
112 	do {								\
113 		*sense_len =						\
114 			istgt_lu_dvd_build_sense_data(spec, sense_data,	\
115 			    ISTGT_SCSI_SENSE_ ## SK,			\
116 			    (ASC), (ASCQ));				\
117 	} while (0)
118 
119 static int istgt_lu_dvd_build_sense_data(ISTGT_LU_DVD *spec, uint8_t *data, int sk, int asc, int ascq);
120 
121 static int
istgt_lu_dvd_open(ISTGT_LU_DVD * spec,int flags,int mode)122 istgt_lu_dvd_open(ISTGT_LU_DVD *spec, int flags, int mode)
123 {
124 	int rc;
125 
126 	rc = open(spec->file, flags, mode);
127 	if (rc < 0) {
128 		return -1;
129 	}
130 	spec->fd = rc;
131 	return 0;
132 }
133 
134 static int
istgt_lu_dvd_close(ISTGT_LU_DVD * spec)135 istgt_lu_dvd_close(ISTGT_LU_DVD *spec)
136 {
137 	int rc;
138 
139 	if (spec->fd == -1)
140 		return 0;
141 	rc = close(spec->fd);
142 	if (rc < 0) {
143 		return -1;
144 	}
145 	spec->fd = -1;
146 	return 0;
147 }
148 
149 static int64_t
istgt_lu_dvd_seek(ISTGT_LU_DVD * spec,uint64_t offset)150 istgt_lu_dvd_seek(ISTGT_LU_DVD *spec, uint64_t offset)
151 {
152 	off_t rc;
153 
154 	rc = lseek(spec->fd, (off_t) offset, SEEK_SET);
155 	if (rc < 0) {
156 		return -1;
157 	}
158 	return 0;
159 }
160 
161 static int64_t
istgt_lu_dvd_read(ISTGT_LU_DVD * spec,void * buf,uint64_t nbytes)162 istgt_lu_dvd_read(ISTGT_LU_DVD *spec, void *buf, uint64_t nbytes)
163 {
164 	int64_t rc;
165 
166 	rc = (int64_t) read(spec->fd, buf, (size_t) nbytes);
167 	if (rc < 0) {
168 		return -1;
169 	}
170 	return rc;
171 }
172 
173 static int64_t
istgt_lu_dvd_write(ISTGT_LU_DVD * spec,const void * buf,uint64_t nbytes)174 istgt_lu_dvd_write(ISTGT_LU_DVD *spec, const void *buf, uint64_t nbytes)
175 {
176 	int64_t rc;
177 
178 	rc = (int64_t) write(spec->fd, buf, (size_t) nbytes);
179 	if (rc < 0) {
180 		return -1;
181 	}
182 	return rc;
183 }
184 
185 static int64_t
istgt_lu_dvd_sync(ISTGT_LU_DVD * spec,uint64_t offset,uint64_t nbytes)186 istgt_lu_dvd_sync(ISTGT_LU_DVD *spec, uint64_t offset __attribute__((__unused__)), uint64_t nbytes __attribute__((__unused__)))
187 {
188 	int64_t rc;
189 
190 	rc = (int64_t) fsync(spec->fd);
191 	if (rc < 0) {
192 		return -1;
193 	}
194 	return rc;
195 }
196 
197 int
istgt_lu_dvd_media_present(ISTGT_LU_DVD * spec)198 istgt_lu_dvd_media_present(ISTGT_LU_DVD *spec)
199 {
200 	if (spec->mload) {
201 		return 1;
202 	}
203 	return 0;
204 }
205 
206 int
istgt_lu_dvd_media_lock(ISTGT_LU_DVD * spec)207 istgt_lu_dvd_media_lock(ISTGT_LU_DVD *spec)
208 {
209 	if (spec->lock) {
210 		return 1;
211 	}
212 	return 0;
213 }
214 
215 static int istgt_lu_dvd_allocate(ISTGT_LU_DVD *spec);
216 
217 int
istgt_lu_dvd_load_media(ISTGT_LU_DVD * spec)218 istgt_lu_dvd_load_media(ISTGT_LU_DVD *spec)
219 {
220 	ISTGT_LU_Ptr lu;
221 	int flags;
222 	int newfile;
223 	int rc;
224 
225 	if (istgt_lu_dvd_media_present(spec)) {
226 		/* media present */
227 		return -1;
228 	}
229 	if (spec->mchanged) {
230 		/* changed soon */
231 		return -1;
232 	}
233 
234 	lu = spec->lu;
235 	if (lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
236 		ISTGT_ERRLOG("LU%d: not removable\n", lu->num);
237 		return -1;
238 	}
239 	if (strcasecmp(lu->lun[spec->lun].u.removable.file,
240 				   "/dev/null") == 0) {
241 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: empty\n", lu->num);
242 		spec->file = NULL;
243 		spec->size = 0;
244 		spec->mflags = 0;
245 		spec->blocklen = DEFAULT_DVD_BLOCKLEN;
246 		spec->blockcnt = spec->size / spec->blocklen;
247 		spec->profile = DEFAULT_DVD_PROFILE;
248 		return 0;
249 	}
250 	spec->file = lu->lun[spec->lun].u.removable.file;
251 	spec->size = lu->lun[spec->lun].u.removable.size;
252 	spec->mflags = lu->lun[spec->lun].u.removable.flags;
253 	//spec->blocklen = lu->blocklen;
254 	spec->blocklen = DEFAULT_DVD_BLOCKLEN;
255 	spec->blockcnt = spec->size / spec->blocklen;
256 	spec->profile = DEFAULT_DVD_PROFILE;
257 
258 	spec->mload = 0;
259 	spec->mchanged = 1;
260 	spec->mwait = 3;
261 
262 	if (access(spec->file, W_OK) != 0) {
263 		if (errno != ENOENT) {
264 			spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY;
265 		}
266 	} else {
267 		struct stat st;
268 		rc = stat(spec->file, &st);
269 		if (rc != 0 || !S_ISREG(st.st_mode)) {
270 			spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY;
271 		} else {
272 			if ((st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
273 				spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY;
274 			}
275 		}
276 	}
277 	if (lu->readonly
278 		|| (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
279 		flags = O_RDONLY;
280 	} else {
281 		flags = O_RDWR;
282 	}
283 	newfile = 0;
284 	rc = istgt_lu_dvd_open(spec, flags, 0666);
285 	if (rc < 0) {
286 		newfile = 1;
287 		if (lu->readonly
288 			|| (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
289 			flags = O_RDONLY;
290 		} else {
291 			flags = (O_CREAT | O_EXCL | O_RDWR);
292 		}
293 		rc = istgt_lu_dvd_open(spec, flags, 0666);
294 		if (rc < 0) {
295 			ISTGT_ERRLOG("LU%d: LUN%d: open error(errno=%d)\n",
296 			    lu->num, spec->lun, errno);
297 			return -1;
298 		}
299 		if (lu->lun[spec->lun].u.removable.size < ISTGT_LU_MEDIA_SIZE_MIN) {
300 			lu->lun[spec->lun].u.removable.size = ISTGT_LU_MEDIA_SIZE_MIN;
301 		}
302 	}
303 	if (lu->readonly
304 		|| (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
305 		/* readonly */
306 	} else {
307 		if (newfile == 0) {
308 			/* XXX TODO: existing file check */
309 		}
310 		rc = istgt_lu_dvd_allocate(spec);
311 		if (rc < 0) {
312 			ISTGT_ERRLOG("LU%d: LUN%d: allocate error\n", lu->num, spec->lun);
313 			return -1;
314 		}
315 	}
316 	return 0;
317 }
318 
319 int
istgt_lu_dvd_unload_media(ISTGT_LU_DVD * spec)320 istgt_lu_dvd_unload_media(ISTGT_LU_DVD *spec)
321 {
322 	int64_t rc;
323 
324 	if (!istgt_lu_dvd_media_present(spec)
325 		&& !spec->mchanged) {
326 		/* media absent */
327 		return 0;
328 	}
329 	if (istgt_lu_dvd_media_lock(spec)) {
330 		return -1;
331 	}
332 
333 	if (!spec->lu->readonly
334 		&& !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
335 		rc = istgt_lu_dvd_sync(spec, 0, spec->size);
336 		if (rc < 0) {
337 			ISTGT_ERRLOG("lu_dvd_sync() failed\n");
338 			return -1;
339 		}
340 	}
341 	rc = (int64_t) istgt_lu_dvd_close(spec);
342 	if (rc < 0) {
343 		ISTGT_ERRLOG("lu_dvd_close() failed\n");
344 		return -1;
345 	}
346 
347 	spec->file = NULL;
348 	spec->size = 0;
349 	spec->mflags = 0;
350 	spec->blocklen = DEFAULT_DVD_BLOCKLEN;
351 	spec->blockcnt = spec->size / spec->blocklen;
352 	spec->profile = DEFAULT_DVD_PROFILE;
353 
354 	spec->mload = 0;
355 	spec->mchanged = 0;
356 	spec->mwait = 3;
357 	return 0;
358 }
359 
360 int
istgt_lu_dvd_change_media(ISTGT_LU_DVD * spec,char * type,char * flags,char * file,char * size)361 istgt_lu_dvd_change_media(ISTGT_LU_DVD *spec, char *type, char *flags, char *file, char *size)
362 {
363 	ISTGT_LU_Ptr lu;
364 	char *mfile;
365 	uint64_t msize;
366 	int mflags;
367 	int rc;
368 
369 	if (istgt_lu_dvd_media_lock(spec)) {
370 		return -1;
371 	}
372 
373 	lu = spec->lu;
374 	if (lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
375 		ISTGT_ERRLOG("LU%d: not removable\n", lu->num);
376 		return -1;
377 	}
378 
379 	if (strcmp(type, "-") == 0) {
380 		/* use ISO image */
381 		;
382 	} else {
383 		ISTGT_ERRLOG("unsupported media type\n");
384 		return -1;
385 	}
386 
387 	mfile = xstrdup(file);
388 	mflags = istgt_lu_parse_media_flags(flags);
389 	msize = istgt_lu_parse_media_size(file, size, &mflags);
390 
391 	rc = istgt_lu_dvd_unload_media(spec);
392 	if (rc < 0) {
393 		return -1;
394 	}
395 
396 	/* replace */
397 	xfree(lu->lun[spec->lun].u.removable.file);
398 	lu->lun[spec->lun].u.removable.file = mfile;
399 	lu->lun[spec->lun].u.removable.size = msize;
400 	lu->lun[spec->lun].u.removable.flags = mflags;
401 
402 	/* reload */
403 	rc = istgt_lu_dvd_load_media(spec);
404 	if (rc < 0) {
405 		(void) istgt_lu_dvd_unload_media(spec);
406 	}
407 	if (spec->file == NULL) {
408 		(void) istgt_lu_dvd_unload_media(spec);
409 	}
410 	spec->mwait = 5;
411 	return rc;
412 }
413 
414 static int
istgt_lu_dvd_allocate(ISTGT_LU_DVD * spec)415 istgt_lu_dvd_allocate(ISTGT_LU_DVD *spec)
416 {
417 	uint8_t *data;
418 	uint64_t fsize;
419 	uint64_t size;
420 	uint64_t blocklen;
421 	uint64_t offset;
422 	uint64_t nbytes;
423 	int64_t rc;
424 
425 	size = spec->size;
426 	blocklen = spec->blocklen;
427 	nbytes = blocklen;
428 	data = xmalloc(nbytes);
429 	memset(data, 0, nbytes);
430 
431 	fsize = istgt_lu_get_filesize(spec->file);
432 	if (fsize > size) {
433 		xfree(data);
434 		return 0;
435 	}
436 
437 	offset = size - nbytes;
438 	rc = istgt_lu_dvd_seek(spec, offset);
439 	if (rc == -1) {
440 		ISTGT_ERRLOG("lu_dvd_seek() failed\n");
441 		xfree(data);
442 		return -1;
443 	}
444 	rc = istgt_lu_dvd_read(spec, data, nbytes);
445 	/* EOF is OK */
446 	if (rc == -1) {
447 		ISTGT_ERRLOG("lu_dvd_read() failed\n");
448 		xfree(data);
449 		return -1;
450 	}
451 	rc = istgt_lu_dvd_seek(spec, offset);
452 	if (rc == -1) {
453 		ISTGT_ERRLOG("lu_dvd_seek() failed\n");
454 		xfree(data);
455 		return -1;
456 	}
457 	rc = istgt_lu_dvd_write(spec, data, nbytes);
458 	if (rc == -1 || (uint64_t) rc != nbytes) {
459 		ISTGT_ERRLOG("lu_dvd_write() failed\n");
460 		xfree(data);
461 		return -1;
462 	}
463 
464 	xfree(data);
465 	return 0;
466 }
467 
468 int
istgt_lu_dvd_init(ISTGT_Ptr istgt,ISTGT_LU_Ptr lu)469 istgt_lu_dvd_init(ISTGT_Ptr istgt __attribute__((__unused__)), ISTGT_LU_Ptr lu)
470 {
471 	ISTGT_LU_DVD *spec;
472 	uint64_t gb_size;
473 	uint64_t mb_size;
474 #ifdef HAVE_UUID_H
475 	uint32_t status;
476 #endif /* HAVE_UUID_H */
477 	int mb_digit;
478 	int ro;
479 	int rc;
480 	int i;
481 
482 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_dvd_init\n");
483 
484 	printf("LU%d DVD UNIT\n", lu->num);
485 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n",
486 				   lu->num, lu->name);
487 	for (i = 0; i < lu->maxlun; i++) {
488 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
489 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
490 			    lu->num, i);
491 			lu->lun[i].spec = NULL;
492 			continue;
493 		}
494 		if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
495 			ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num);
496 			return -1;
497 		}
498 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d removable\n",
499 		    lu->num, i);
500 
501 		spec = xmalloc(sizeof *spec);
502 		memset(spec, 0, sizeof *spec);
503 		spec->lu = lu;
504 		spec->num = lu->num;
505 		spec->lun = i;
506 		spec->fd = -1;
507 		spec->read_cache = 1;
508 		spec->write_cache = 1;
509 
510 #ifdef HAVE_UUID_H
511 		uuid_create(&spec->uuid, &status);
512 		if (status != uuid_s_ok) {
513 			ISTGT_ERRLOG("LU%d: LUN%d: uuid_create() failed\n", lu->num, i);
514 			xfree(spec);
515 			return -1;
516 		}
517 #endif /* HAVE_UUID_H */
518 
519 		spec->mload = 0;
520 		spec->mchanged = 0;
521 		spec->mwait = 0;
522 		rc = istgt_lu_dvd_load_media(spec);
523 		if (rc < 0) {
524 			ISTGT_ERRLOG("lu_dvd_load_media() failed\n");
525 			xfree(spec);
526 			return -1;
527 		}
528 
529 		if (spec->file != NULL) {
530 			/* initial state */
531 			spec->mload = 1;
532 			spec->mchanged = 0;
533 			spec->mwait = 0;
534 
535 			if (spec->lu->readonly
536 			    || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
537 				ro = 1;
538 			} else {
539 				ro = 0;
540 			}
541 
542 			printf("LU%d: LUN%d file=%s, size=%"PRIu64", flag=%s\n",
543 			    lu->num, i, spec->file, spec->size, ro ? "ro" : "rw");
544 			printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n",
545 			    lu->num, i, spec->blockcnt, spec->blocklen);
546 
547 			gb_size = spec->size / ISTGT_LU_1GB;
548 			mb_size = (spec->size % ISTGT_LU_1GB) / ISTGT_LU_1MB;
549 			if (gb_size > 0) {
550 				mb_digit = (int) (((mb_size * 100) / 1024) / 10);
551 				printf("LU%d: LUN%d %"PRIu64".%dGB %sstorage for %s\n",
552 				    lu->num, i, gb_size, mb_digit,
553 				    lu->readonly ? "readonly " : "", lu->name);
554 			} else {
555 				printf("LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n",
556 				    lu->num, i, mb_size,
557 				    lu->readonly ? "readonly " : "", lu->name);
558 			}
559 		} else {
560 			/* initial state */
561 			spec->mload = 0;
562 			spec->mchanged = 0;
563 			spec->mwait = 0;
564 
565 			printf("LU%d: LUN%d empty slot\n",
566 			    lu->num, i);
567 		}
568 
569 		lu->lun[i].spec = spec;
570 	}
571 
572 	return 0;
573 }
574 
575 int
istgt_lu_dvd_shutdown(ISTGT_Ptr istgt,ISTGT_LU_Ptr lu)576 istgt_lu_dvd_shutdown(ISTGT_Ptr istgt __attribute__((__unused__)), ISTGT_LU_Ptr lu)
577 {
578 	ISTGT_LU_DVD *spec;
579 	int rc;
580 	int i;
581 
582 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_dvd_shutdown\n");
583 
584 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n",
585 				   lu->num, lu->name);
586 	for (i = 0; i < lu->maxlun; i++) {
587 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
588 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
589 			    lu->num, i);
590 			continue;
591 		}
592 		if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
593 			ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num);
594 			return -1;
595 		}
596 		spec = (ISTGT_LU_DVD *) lu->lun[i].spec;
597 
598 		if (!spec->lu->readonly
599 			&& !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
600 			rc = istgt_lu_dvd_sync(spec, 0, spec->size);
601 			if (rc < 0) {
602 				//ISTGT_ERRLOG("LU%d: lu_dvd_sync() failed\n", lu->num);
603 				/* ignore error */
604 			}
605 		}
606 		rc = istgt_lu_dvd_close(spec);
607 		if (rc < 0) {
608 			//ISTGT_ERRLOG("LU%d: lu_dvd_close() failed\n", lu->num);
609 			/* ignore error */
610 		}
611 		xfree(spec);
612 		lu->lun[i].spec = NULL;
613 	}
614 
615 	return 0;
616 }
617 
618 static int
istgt_lu_dvd_scsi_report_luns(ISTGT_LU_Ptr lu,CONN_Ptr conn,uint8_t * cdb,int sel,uint8_t * data,int alloc_len)619 istgt_lu_dvd_scsi_report_luns(ISTGT_LU_Ptr lu, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int sel, uint8_t *data, int alloc_len)
620 {
621 	uint64_t fmt_lun, lun, method;
622 	int hlen = 0, len = 0;
623 	int i;
624 
625 	if (alloc_len < 8) {
626 		return -1;
627 	}
628 
629 	if (sel == 0x00) {
630 		/* logical unit with addressing method */
631 	} else if (sel == 0x01) {
632 		/* well known logical unit */
633 	} else if (sel == 0x02) {
634 		/* logical unit */
635 	} else {
636 		return -1;
637 	}
638 
639 	/* LUN LIST LENGTH */
640 	DSET32(&data[0], 0);
641 	/* Reserved */
642 	DSET32(&data[4], 0);
643 	hlen = 8;
644 
645 	for (i = 0; i < lu->maxlun; i++) {
646 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
647 #if 0
648 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
649 			    lu->num, i);
650 #endif
651 			continue;
652 		}
653 		if (alloc_len - (hlen + len) < 8) {
654 			return -1;
655 		}
656 		lun = (uint64_t) i;
657 		if (lu->maxlun <= 0x0100) {
658 			/* below 256 */
659 			method = 0x00U;
660 			fmt_lun = (method & 0x03U) << 62;
661 			fmt_lun |= (lun & 0x00ffU) << 48;
662 		} else if (lu->maxlun <= 0x4000) {
663 			/* below 16384 */
664 			method = 0x01U;
665 			fmt_lun = (method & 0x03U) << 62;
666 			fmt_lun |= (lun & 0x3fffU) << 48;
667 		} else {
668 			/* XXX */
669 			fmt_lun = 0;
670 		}
671 		/* LUN */
672 		DSET64(&data[hlen + len], fmt_lun);
673 		len += 8;
674 	}
675 	/* LUN LIST LENGTH */
676 	DSET32(&data[0], len);
677 	return hlen + len;
678 }
679 
680 static int
istgt_lu_dvd_scsi_inquiry(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,uint8_t * data,int alloc_len)681 istgt_lu_dvd_scsi_inquiry(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int alloc_len)
682 {
683 	uint64_t LUI;
684 	uint8_t *cp, *cp2;
685 	int hlen = 0, len = 0, plen, plen2;
686 	int pc;
687 	int pq, pd;
688 	int rmb;
689 	int evpd;
690 	int pg_tag;
691 	int i, j;
692 
693 	if (alloc_len < 0xff) {
694 		return -1;
695 	}
696 
697 	pq = 0x00;
698 	pd = SPC_PERIPHERAL_DEVICE_TYPE_DVD;
699 	rmb = 1;
700 
701 	LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU);
702 
703 	pc = cdb[2];
704 	evpd = BGET8(&cdb[1], 0);
705 	if (evpd) {
706 		/* Vital product data */
707 		switch (pc) {
708 		case SPC_VPD_SUPPORTED_VPD_PAGES:
709 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
710 			BDSET8W(&data[0], pq, 7, 3);
711 			BDADD8W(&data[0], pd, 4, 5);
712 			/* PAGE CODE */
713 			data[1] = pc;
714 			/* Reserved */
715 			data[2] = 0;
716 			/* PAGE LENGTH */
717 			data[3] = 0;
718 			hlen = 4;
719 
720 			data[4] = SPC_VPD_SUPPORTED_VPD_PAGES;      /* 0x00 */
721 			data[5] = SPC_VPD_UNIT_SERIAL_NUMBER;       /* 0x80 */
722 			data[6] = SPC_VPD_DEVICE_IDENTIFICATION;    /* 0x83 */
723 			data[7] = SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES; /* 0x85 */
724 			data[8] = SPC_VPD_EXTENDED_INQUIRY_DATA;    /* 0x86 */
725 			data[9] = SPC_VPD_MODE_PAGE_POLICY;         /* 0x87 */
726 			data[10]= SPC_VPD_SCSI_PORTS;               /* 0x88 */
727 			len = 11 - hlen;
728 
729 			/* PAGE LENGTH */
730 			data[3] = len;
731 			break;
732 
733 		case SPC_VPD_UNIT_SERIAL_NUMBER:
734 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
735 			BDSET8W(&data[0], pq, 7, 3);
736 			BDADD8W(&data[0], pd, 4, 5);
737 			/* PAGE CODE */
738 			data[1] = pc;
739 			/* Reserved */
740 			data[2] = 0;
741 			/* PAGE LENGTH */
742 			data[3] = 0;
743 			hlen = 4;
744 
745 			/* PRODUCT SERIAL NUMBER */
746 			len = strlen(spec->lu->inq_serial);
747 			if (len > MAX_LU_SERIAL_STRING) {
748 				len = MAX_LU_SERIAL_STRING;
749 			}
750 			istgt_strcpy_pad(&data[4], len, spec->lu->inq_serial, ' ');
751 
752 			/* PAGE LENGTH */
753 			data[3] = len;
754 			break;
755 
756 		case SPC_VPD_DEVICE_IDENTIFICATION:
757 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
758 			BDSET8W(&data[0], pq, 7, 3);
759 			BDADD8W(&data[0], pd, 4, 5);
760 			/* PAGE CODE */
761 			data[1] = pc;
762 			/* PAGE LENGTH */
763 			DSET16(&data[2], 0);
764 			hlen = 4;
765 
766 			/* Identification descriptor 1 */
767 			/* Logical Unit */
768 			cp = &data[hlen + len];
769 
770 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
771 			BDSET8W(&cp[0], 0, 7, 4);
772 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
773 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
774 			BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */
775 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2);
776 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4);
777 			/* Reserved */
778 			cp[2] = 0;
779 			/* IDENTIFIER LENGTH */
780 			cp[3] = 0;
781 
782 			/* IDENTIFIER */
783 #if 0
784 			/* 16bytes ID */
785 			plen = istgt_lu_set_extid(&cp[4], 0, LUI);
786 #else
787 			plen = istgt_lu_set_lid(&cp[4], LUI);
788 #endif
789 
790 			cp[3] = plen;
791 			len += 4 + plen;
792 
793 			/* Identification descriptor 2 */
794 			/* T10 VENDOR IDENTIFICATION */
795 			cp = &data[hlen + len];
796 
797 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
798 			BDSET8W(&cp[0], 0, 7, 4);
799 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
800 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
801 			BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */
802 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2);
803 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID, 3, 4);
804 			/* Reserved */
805 			cp[2] = 0;
806 			/* IDENTIFIER LENGTH */
807 			cp[3] = 0;
808 
809 			/* IDENTIFIER */
810 			/* T10 VENDOR IDENTIFICATION */
811 			istgt_strcpy_pad(&cp[4], 8, spec->lu->inq_vendor, ' ');
812 			plen = 8;
813 			/* VENDOR SPECIFIC IDENTIFIER */
814 			/* PRODUCT IDENTIFICATION */
815 			istgt_strcpy_pad(&cp[12], 16, spec->lu->inq_product, ' ');
816 			/* PRODUCT SERIAL NUMBER */
817 			istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING,
818 			    spec->lu->inq_serial, ' ');
819 			plen += 16 + MAX_LU_SERIAL_STRING;
820 
821 			cp[3] = plen;
822 			len += 4 + plen;
823 
824 			/* Identification descriptor 3 */
825 			/* Target Device */
826 			cp = &data[hlen + len];
827 
828 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
829 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
830 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
831 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
832 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
833 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_DEVICE, 5, 2);
834 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4);
835 			/* Reserved */
836 			cp[2] = 0;
837 			/* IDENTIFIER LENGTH */
838 			cp[3] = 0;
839 
840 			/* IDENTIFIER */
841 			plen = snprintf((char *) &cp[4], MAX_TARGET_NAME,
842 			    "%s", spec->lu->name);
843 			cp[3] = plen;
844 			len += 4 + plen;
845 
846 			/* Identification descriptor 4 */
847 			/* Target Port */
848 			cp = &data[hlen + len];
849 
850 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
851 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
852 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
853 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
854 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
855 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
856 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4);
857 			/* Reserved */
858 			cp[2] = 0;
859 			/* IDENTIFIER LENGTH */
860 			cp[3] = 0;
861 
862 			/* IDENTIFIER */
863 			plen = snprintf((char *) &cp[4], MAX_TARGET_NAME,
864 			    "%s"",t,0x""%4.4x", spec->lu->name, conn->portal.tag);
865 			cp[3] = plen;
866 			len += 4 + plen;
867 
868 			/* Identification descriptor 5 */
869 			/* Relative Target Port */
870 			cp = &data[hlen + len];
871 
872 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
873 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
874 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
875 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
876 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
877 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
878 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT,
879 					3, 4);
880 			/* Reserved */
881 			cp[2] = 0;
882 			/* IDENTIFIER LENGTH */
883 			cp[3] = 0;
884 
885 			/* IDENTIFIER */
886 			/* Obsolete */
887 			DSET16(&cp[4], 0);
888 			/* Relative Target Port Identifier */
889 			//DSET16(&cp[6], 1); /* port1 as port A */
890 			//DSET16(&cp[6], 2); /* port2 as port B */
891 			DSET16(&cp[6], (uint16_t) (1 + conn->portal.idx));
892 			plen = 4;
893 
894 			cp[3] = plen;
895 			len += 4 + plen;
896 
897 			/* Identification descriptor 6 */
898 			/* Target port group */
899 			cp = &data[hlen + len];
900 
901 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
902 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
903 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
904 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
905 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
906 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
907 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP,
908 					3, 4);
909 			/* Reserved */
910 			cp[2] = 0;
911 			/* IDENTIFIER LENGTH */
912 			cp[3] = 0;
913 
914 			/* IDENTIFIER */
915 			/* Reserved */
916 			DSET16(&cp[4], 0);
917 			/* TARGET PORT GROUP */
918 			DSET16(&cp[6], (uint16_t) (conn->portal.tag));
919 			plen = 4;
920 
921 			cp[3] = plen;
922 			len += 4 + plen;
923 
924 			/* Identification descriptor 7 */
925 			/* Logical unit group */
926 			cp = &data[hlen + len];
927 
928 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
929 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
930 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
931 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
932 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
933 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
934 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP,
935 			    3, 4);
936 			/* Reserved */
937 			cp[2] = 0;
938 			/* IDENTIFIER LENGTH */
939 			cp[3] = 0;
940 
941 			/* IDENTIFIER */
942 			/* Reserved */
943 			DSET16(&cp[4], 0);
944 			/* LOGICAL UNIT GROUP */
945 			DSET16(&cp[6], (uint16_t) (spec->lu->num));
946 			plen = 4;
947 
948 			cp[3] = plen;
949 			len += 4 + plen;
950 
951 			/* PAGE LENGTH */
952 			if (len > 0xffff) {
953 				len = 0xffff;
954 			}
955 			DSET16(&data[2], len);
956 			break;
957 
958 		case SPC_VPD_EXTENDED_INQUIRY_DATA:
959 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
960 			BDSET8W(&data[0], pq, 7, 3);
961 			BDADD8W(&data[0], pd, 4, 5);
962 			/* PAGE CODE */
963 			data[1] = pc;
964 			/* Reserved */
965 			data[2] = 0;
966 			/* PAGE LENGTH */
967 			data[3] = 0;
968 			hlen = 4;
969 
970 			/* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */
971 			data[4] = 0;
972 			/* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */
973 			data[5] = 0;
974 			/* NV_SUP(1) V_SUP(0) */
975 			data[6] = 0;
976 			/* Reserved[7-63] */
977 			memset(&data[7], 0, (64 - 7));
978 			len = 64 - hlen;
979 
980 			/* PAGE LENGTH */
981 			data[3] = len;
982 			break;
983 
984 		case SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES:
985 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
986 			BDSET8W(&data[0], pq, 7, 3);
987 			BDADD8W(&data[0], pd, 4, 5);
988 			/* PAGE CODE */
989 			data[1] = pc;
990 			/* PAGE LENGTH */
991 			DSET16(&data[2], 0);
992 			hlen = 4;
993 
994 #if 0
995 			/* Network services descriptor N */
996 			cp = &data[hlen + len];
997 
998 			/* ASSOCIATION(6-5) SERVICE TYPE(4-0) */
999 			BDSET8W(&cp[0], 0x00, 6, 2);
1000 			BDADD8W(&cp[0], 0x00, 4, 5);
1001 			/* Reserved */
1002 			cp[1] = 0;
1003 			/* NETWORK ADDRESS LENGTH */
1004 			DSET16(&cp[2], 0);
1005 			/* NETWORK ADDRESS */
1006 			cp[4] = 0;
1007 			/* ... */
1008 			plen = 0;
1009 			DSET16(&cp[2], plen);
1010 			len += 4 + plen;
1011 #endif
1012 
1013 			/* PAGE LENGTH */
1014 			if (len > 0xffff) {
1015 				len = 0xffff;
1016 			}
1017 			DSET16(&data[2], len);
1018 			break;
1019 
1020 		case SPC_VPD_MODE_PAGE_POLICY:
1021 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1022 			BDSET8W(&data[0], pq, 7, 3);
1023 			BDADD8W(&data[0], pd, 4, 5);
1024 			/* PAGE CODE */
1025 			data[1] = pc;
1026 			/* PAGE LENGTH */
1027 			DSET16(&data[2], 0);
1028 			hlen = 4;
1029 
1030 			/* Mode page policy descriptor 1 */
1031 			cp = &data[hlen + len];
1032 
1033 			/* POLICY PAGE CODE(5-0) */
1034 			BDSET8W(&cp[0], 0x3f, 5, 6);    /* all page code */
1035 			/* POLICY SUBPAGE CODE */
1036 			cp[1] = 0xff;                   /* all sub page */
1037 			/* MLUS(7) MODE PAGE POLICY(1-0) */
1038 			//BDSET8(&cp[2], 1, 7); /* multiple logical units share */
1039 			BDSET8(&cp[2], 0, 7); /* own copy */
1040 			BDADD8W(&cp[2], 0x00, 1, 2); /* Shared */
1041 			//BDADD8W(&cp[2], 0x01, 1, 2); /* Per target port */
1042 			//BDADD8W(&cp[2], 0x02, 1, 2); /* Per initiator port */
1043 			//BDADD8W(&cp[2], 0x03, 1, 2); /* Per I_T nexus */
1044 			/* Reserved */
1045 			cp[3] = 0;
1046 			len += 4;
1047 
1048 			/* PAGE LENGTH */
1049 			if (len > 0xffff) {
1050 				len = 0xffff;
1051 			}
1052 			DSET16(&data[2], len);
1053 			break;
1054 
1055 		case SPC_VPD_SCSI_PORTS:
1056 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1057 			BDSET8W(&data[0], pq, 7, 3);
1058 			BDADD8W(&data[0], pd, 4, 5);
1059 			/* PAGE CODE */
1060 			data[1] = pc;
1061 			/* PAGE LENGTH */
1062 			DSET16(&data[2], 0);
1063 			hlen = 4;
1064 
1065 			/* Identification descriptor list */
1066 			for (i = 0; i < spec->lu->maxmap; i++) {
1067 				pg_tag = spec->lu->map[i].pg_tag;
1068 				/* skip same pg_tag */
1069 				for (j = 0; j < i; j++) {
1070 					if (spec->lu->map[j].pg_tag == pg_tag) {
1071 						goto skip_pg_tag;
1072 					}
1073 				}
1074 
1075 				/* Identification descriptor N */
1076 				cp = &data[hlen + len];
1077 
1078 				/* Reserved */
1079 				DSET16(&cp[0], 0);
1080 				/* RELATIVE PORT IDENTIFIER */
1081 				DSET16(&cp[2], (uint16_t) (1 + pg_tag));
1082 				/* Reserved */
1083 				DSET16(&cp[4], 0);
1084 				/* INITIATOR PORT TRANSPORTID LENGTH */
1085 				DSET16(&cp[6], 0);
1086 				/* Reserved */
1087 				DSET16(&cp[8], 0);
1088 				/* TARGET PORT DESCRIPTORS LENGTH */
1089 				DSET16(&cp[10], 0);
1090 				len += 12;
1091 
1092 				plen2 = 0;
1093 				/* Target port descriptor 1 */
1094 				cp2 = &data[hlen + len + plen2];
1095 
1096 				/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1097 				BDSET8W(&cp2[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1098 				BDADD8W(&cp2[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
1099 				/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1100 				BDSET8W(&cp2[1], 1, 7, 1); /* PIV */
1101 				BDADD8W(&cp2[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
1102 				BDADD8W(&cp2[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4);
1103 				/* Reserved */
1104 				cp2[2] = 0;
1105 				/* IDENTIFIER LENGTH */
1106 				cp2[3] = 0;
1107 
1108 				/* IDENTIFIER */
1109 				plen = snprintf((char *) &cp2[4], MAX_TARGET_NAME,
1110 				    "%s"",t,0x""%4.4x", spec->lu->name, pg_tag);
1111 				cp2[3] = plen;
1112 				plen2 += 4 + plen;
1113 
1114 				/* TARGET PORT DESCRIPTORS LENGTH */
1115 				DSET16(&cp[10], plen2);
1116 				len += plen2;
1117 			skip_pg_tag:
1118 				;
1119 			}
1120 
1121 			/* PAGE LENGTH */
1122 			if (len > 0xffff) {
1123 				len = 0xffff;
1124 			}
1125 			DSET16(&data[2], len);
1126 			break;
1127 
1128 		default:
1129 			if (pc >= 0xc0 && pc <= 0xff) {
1130 				ISTGT_WARNLOG("Vendor specific INQUIRY VPD page 0x%x\n", pc);
1131 			} else {
1132 				ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc);
1133 			}
1134 			return -1;
1135 		}
1136 	} else {
1137 		/* Standard INQUIRY data */
1138 		/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1139 		BDSET8W(&data[0], pq, 7, 3);
1140 		BDADD8W(&data[0], pd, 4, 5);
1141 		/* RMB(7) */
1142 		BDSET8W(&data[1], rmb, 7, 1);
1143 		/* VERSION */
1144 		/* See SPC3/SBC2/MMC4/SAM2 for more details */
1145 		data[2] = SPC_VERSION_SPC3;
1146 		/* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */
1147 		BDSET8W(&data[3], 2, 3, 4);		/* format 2 */
1148 		BDADD8(&data[1], 1, 4);         /* hierarchical support */
1149 		/* ADDITIONAL LENGTH */
1150 		data[4] = 0;
1151 		hlen = 5;
1152 
1153 		/* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */
1154 		data[5] = 0;
1155 		/* BQUE(7) ENCSERV(6) VS(5) MULTIP(4) MCHNGR(3) ADDR16(0) */
1156 		data[6] = 0;
1157 		/* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */
1158 		data[7] = 0;
1159 		/* T10 VENDOR IDENTIFICATION */
1160 		istgt_strcpy_pad(&data[8], 8, spec->lu->inq_vendor, ' ');
1161 		/* PRODUCT IDENTIFICATION */
1162 		istgt_strcpy_pad(&data[16], 16, spec->lu->inq_product, ' ');
1163 		/* PRODUCT REVISION LEVEL */
1164 		istgt_strcpy_pad(&data[32], 4, spec->lu->inq_revision, ' ');
1165 		/* Vendor specific */
1166 		memset(&data[36], 0x20, 20);
1167 		/* CLOCKING(3-2) QAS(1) IUS(0) */
1168 		data[56] = 0;
1169 		/* Reserved */
1170 		data[57] = 0;
1171 		/* VERSION DESCRIPTOR 1-8 */
1172 		DSET16(&data[58], 0x0960); /* iSCSI (no version claimed) */
1173 		DSET16(&data[60], 0x0300); /* SPC-3 (no version claimed) */
1174 		DSET16(&data[62], 0x03a0); /* MMC-4 (no version claimed) */
1175 		DSET16(&data[64], 0x0040); /* SAM-2 (no version claimed) */
1176 		DSET16(&data[66], 0x0000);
1177 		DSET16(&data[68], 0x0000);
1178 		DSET16(&data[70], 0x0000);
1179 		DSET16(&data[72], 0x0000);
1180 		/* Reserved[74-95] */
1181 		memset(&data[74], 0, (96 - 74));
1182 		/* Vendor specific parameters[96-n] */
1183 		//data[96] = 0;
1184 		len = 96 - hlen;
1185 
1186 		/* ADDITIONAL LENGTH */
1187 		data[4] = len;
1188 	}
1189 
1190 	return hlen + len;
1191 }
1192 
1193 #define MODE_SENSE_PAGE_INIT(B,L,P,SP)					\
1194 	do {								\
1195 		memset((B), 0, (L));					\
1196 		if ((SP) != 0x00) {					\
1197 			(B)[0] = (P) | 0x40; /* PAGE + SPF=1 */		\
1198 			(B)[1] = (SP);					\
1199 			DSET16(&(B)[2], (L) - 4);			\
1200 		} else {						\
1201 			(B)[0] = (P);					\
1202 			(B)[1] = (L) - 2;				\
1203 		}							\
1204 	} while (0)
1205 
1206 static int
istgt_lu_dvd_scsi_mode_sense_page(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int pc,int page,int subpage,uint8_t * data,int alloc_len)1207 istgt_lu_dvd_scsi_mode_sense_page(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int pc, int page, int subpage, uint8_t *data, int alloc_len)
1208 {
1209 	uint8_t *cp;
1210 	int len = 0;
1211 	int plen;
1212 	int i;
1213 
1214 #if 0
1215 	printf("SENSE pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage);
1216 #endif
1217 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE: pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage);
1218 
1219 	if (pc == 0x00) {
1220 		/* Current values */
1221 	} else if (pc == 0x01) {
1222 		/* Changeable values */
1223 		if (page != 0x08) {
1224 			/* not supported */
1225 			return -1;
1226 		}
1227 	} else if (pc == 0x02) {
1228 		/* Default values */
1229 	} else {
1230 		/* Saved values */
1231 	}
1232 
1233 	cp = &data[len];
1234 	switch (page) {
1235 	case 0x00:
1236 		/* Vendor specific */
1237 		break;
1238 	case 0x01:
1239 		/* Read-Write Error Recovery */
1240 		break;
1241 	case 0x02:
1242 		/* Reserved */
1243 		break;
1244 	case 0x03:
1245 		/* MRW */
1246 		break;
1247 	case 0x04:
1248 		/* Reserved */
1249 		break;
1250 	case 0x05:
1251 		/* Write Parameter */
1252 		break;
1253 	case 0x06:
1254 		/* Reserved */
1255 		break;
1256 	case 0x07:
1257 		/* Verify Error Recovery */
1258 		break;
1259 	case 0x08:
1260 		/* Caching */
1261 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Caching\n");
1262 		if (subpage != 0x00)
1263 			break;
1264 
1265 		plen = 0x12 + 2;
1266 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1267 		BDADD8(&cp[0], 1, 7); /* PS */
1268 		if (pc == 0x01) {
1269 			// Changeable values
1270 			BDADD8(&cp[2], 1, 2); /* WCE */
1271 			BDADD8(&cp[2], 1, 0); /* RCD */
1272 			len += plen;
1273 			break;
1274 		}
1275 		BDADD8(&cp[2], 1, 2); /* WCE */
1276 		//BDADD8(&cp[2], 1, 0); /* RCD */
1277 		if (spec->write_cache == 1) {
1278 			BDADD8(&cp[2], 1, 2); /* WCE=1 */
1279 		} else {
1280 			BDADD8(&cp[2], 0, 2); /* WCE=0 */
1281 		}
1282 		if (spec->read_cache == 0) {
1283 			BDADD8(&cp[2], 1, 0); /* RCD=1 */
1284 		} else {
1285 			BDADD8(&cp[2], 0, 0); /* RCD=0 */
1286 		}
1287 		len += plen;
1288 		break;
1289 	case 0x09:
1290 	case 0x0a:
1291 		/* Reserved */
1292 		break;
1293 	case 0x0b:
1294 		/* Medium Types Supported */
1295 		break;
1296 	case 0x0c:
1297 		/* Reserved */
1298 		break;
1299 	case 0x0d:
1300 		/* CD Device Parameters */
1301 		break;
1302 	case 0x0e:
1303 		/* CD Audio Control */
1304 		break;
1305 	case 0x0f:
1306 	case 0x10:
1307 	case 0x11:
1308 	case 0x12:
1309 	case 0x13:
1310 	case 0x14:
1311 	case 0x15:
1312 	case 0x16:
1313 	case 0x17:
1314 	case 0x18:
1315 	case 0x19:
1316 		/* Reserved */
1317 		break;
1318 	case 0x1a:
1319 		/* Power Condition */
1320 		break;
1321 	case 0x1b:
1322 		/* Reserved */
1323 		break;
1324 	case 0x1c:
1325 		/* Informational Exceptions Control */
1326 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Informational Exceptions Control\n");
1327 		if (subpage != 0x00)
1328 			break;
1329 
1330 		plen = 0x0a + 2;
1331 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1332 		len += plen;
1333 		break;
1334 	case 0x1d:
1335 		/* Time-out & Protect */
1336 		break;
1337 	case 0x1e:
1338 	case 0x1f:
1339 		/* Reserved */
1340 		break;
1341 	case 0x20:
1342 	case 0x21:
1343 	case 0x22:
1344 	case 0x23:
1345 	case 0x24:
1346 	case 0x25:
1347 	case 0x26:
1348 	case 0x27:
1349 	case 0x28:
1350 	case 0x29:
1351 		/* Vendor-specific */
1352 		break;
1353 	case 0x2a:
1354 		/* MM Capabilities & Mechanical Status */
1355 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE MM Capabilities & Mechanical Status\n");
1356 		if (subpage != 0x00)
1357 			break;
1358 
1359 		plen = 28 + 2;
1360 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1361 
1362 		BDADD8(&cp[2], 1, 3); /* DVD-ROM read */
1363 
1364 		len += plen;
1365 		break;
1366 	case 0x2b:
1367 		/* Reserved */
1368 		break;
1369 	case 0x2c:
1370 	case 0x2d:
1371 	case 0x2e:
1372 	case 0x2f:
1373 	case 0x30:
1374 	case 0x31:
1375 	case 0x32:
1376 	case 0x33:
1377 	case 0x34:
1378 	case 0x35:
1379 	case 0x36:
1380 	case 0x37:
1381 	case 0x38:
1382 	case 0x39:
1383 	case 0x3a:
1384 	case 0x3b:
1385 	case 0x3c:
1386 	case 0x3d:
1387 	case 0x3e:
1388 		/* Vendor-specific */
1389 		break;
1390 	case 0x3f:
1391 		switch (subpage) {
1392 		case 0x00:
1393 			/* All mode pages */
1394 			for (i = 0x00; i < 0x3e; i ++) {
1395 				len += istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len);
1396 			}
1397 			break;
1398 		case 0xff:
1399 			/* All mode pages and subpages */
1400 			for (i = 0x00; i < 0x3e; i ++) {
1401 				len += istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len);
1402 			}
1403 			for (i = 0x00; i < 0x3e; i ++) {
1404 				len += istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0xff, &cp[len], alloc_len);
1405 			}
1406 			break;
1407 		default:
1408 			/* 0x01-0x3e: Reserved */
1409 			break;
1410 		}
1411 	}
1412 
1413 	return len;
1414 }
1415 
1416 static int
istgt_lu_dvd_scsi_mode_sense6(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int dbd,int pc,int page,int subpage,uint8_t * data,int alloc_len)1417 istgt_lu_dvd_scsi_mode_sense6(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int pc, int page, int subpage, uint8_t *data, int alloc_len)
1418 {
1419 	uint8_t *cp;
1420 	int hlen = 0, len = 0, plen;
1421 	int total;
1422 	int llbaa = 0;
1423 
1424 	data[0] = 0;                    /* Mode Data Length */
1425 	if (spec->mload) {
1426 		data[1] = 0;            /* Medium Type */
1427 		data[2] = 0;            /* Device-Specific Parameter */
1428 		if (spec->lu->readonly
1429 		    || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
1430 			BDADD8(&data[2], 1, 7);     /* WP */
1431 		}
1432 	} else {
1433 		data[1] = 0;            /* Medium Type */
1434 		data[2] = 0;            /* Device-Specific Parameter */
1435 	}
1436 	data[3] = 0;                    /* Block Descripter Length */
1437 	hlen = 4;
1438 
1439 	cp = &data[4];
1440 	if (dbd) {                      /* Disable Block Descripters */
1441 		len = 0;
1442 	} else {
1443 		if (llbaa) {
1444 			if (spec->mload) {
1445 				/* Number of Blocks */
1446 				DSET64(&cp[0], spec->blockcnt);
1447 				/* Reserved */
1448 				DSET32(&cp[8], 0);
1449 				/* Block Length */
1450 				DSET32(&cp[12], (uint32_t) spec->blocklen);
1451 			} else {
1452 				/* Number of Blocks */
1453 				DSET64(&cp[0], 0ULL);
1454 				/* Reserved */
1455 				DSET32(&cp[8], 0);
1456 				/* Block Length */
1457 				DSET32(&cp[12], 0);
1458 			}
1459 			len = 16;
1460 		} else {
1461 			if (spec->mload) {
1462 				/* Number of Blocks */
1463 				if (spec->blockcnt > 0xffffffffULL) {
1464 					DSET32(&cp[0], 0xffffffffUL);
1465 				} else {
1466 					DSET32(&cp[0], (uint32_t) spec->blockcnt);
1467 				}
1468 				/* Block Length */
1469 				DSET32(&cp[4], (uint32_t) spec->blocklen);
1470 			} else {
1471 				/* Number of Blocks */
1472 				DSET32(&cp[0], 0);
1473 				/* Block Length */
1474 				DSET32(&cp[4], 0);
1475 			}
1476 			len = 8;
1477 		}
1478 		cp += len;
1479 	}
1480 	data[3] = len;                  /* Block Descripter Length */
1481 
1482 	plen = istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len);
1483 	if (plen < 0) {
1484 		return -1;
1485 	}
1486 	cp += plen;
1487 
1488 	total = hlen + len + plen;
1489 	data[0] = total - 1;            /* Mode Data Length */
1490 
1491 	return total;
1492 }
1493 
1494 static int
istgt_lu_dvd_scsi_mode_sense10(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int dbd,int llbaa,int pc,int page,int subpage,uint8_t * data,int alloc_len)1495 istgt_lu_dvd_scsi_mode_sense10(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int llbaa, int pc, int page, int subpage, uint8_t *data, int alloc_len)
1496 {
1497 	uint8_t *cp;
1498 	int hlen = 0, len = 0, plen;
1499 	int total;
1500 
1501 	DSET16(&data[0], 0);            /* Mode Data Length */
1502 	if (spec->mload) {
1503 		data[2] = 0;            /* Medium Type */
1504 		data[3] = 0;            /* Device-Specific Parameter */
1505 		if (spec->lu->readonly
1506 		    || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
1507 			BDADD8(&data[3], 1, 7);     /* WP */
1508 		}
1509 	} else {
1510 		data[2] = 0;            /* Medium Type */
1511 		data[3] = 0;            /* Device-Specific Parameter */
1512 	}
1513 	if (llbaa) {
1514 		BDSET8(&data[4], 1, 1);      /* Long LBA */
1515 	} else {
1516 		BDSET8(&data[4], 0, 1);      /* Short LBA */
1517 	}
1518 	data[5] = 0;                    /* Reserved */
1519 	DSET16(&data[6], 0);  		/* Block Descripter Length */
1520 	hlen = 8;
1521 
1522 	cp = &data[8];
1523 	if (dbd) {                      /* Disable Block Descripters */
1524 		len = 0;
1525 	} else {
1526 		if (llbaa) {
1527 			if (spec->mload) {
1528 				/* Number of Blocks */
1529 				DSET64(&cp[0], spec->blockcnt);
1530 				/* Reserved */
1531 				DSET32(&cp[8], 0);
1532 				/* Block Length */
1533 				DSET32(&cp[12], (uint32_t) spec->blocklen);
1534 			} else {
1535 				/* Number of Blocks */
1536 				DSET64(&cp[0], 0ULL);
1537 				/* Reserved */
1538 				DSET32(&cp[8], 0);
1539 				/* Block Length */
1540 				DSET32(&cp[12], 0);
1541 			}
1542 			len = 16;
1543 		} else {
1544 			if (spec->mload) {
1545 				/* Number of Blocks */
1546 				if (spec->blockcnt > 0xffffffffULL) {
1547 					DSET32(&cp[0], 0xffffffffUL);
1548 				} else {
1549 					DSET32(&cp[0], (uint32_t) spec->blockcnt);
1550 				}
1551 				/* Block Length */
1552 				DSET32(&cp[4], (uint32_t) spec->blocklen);
1553 			} else {
1554 				/* Number of Blocks */
1555 				DSET32(&cp[0], 0);
1556 				/* Block Length */
1557 				DSET32(&cp[4], 0);
1558 			}
1559 			len = 8;
1560 		}
1561 		cp += len;
1562 	}
1563 	DSET16(&data[6], len);          /* Block Descripter Length */
1564 
1565 	plen = istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len);
1566 	if (plen < 0) {
1567 		return -1;
1568 	}
1569 	cp += plen;
1570 
1571 	total = hlen + len + plen;
1572 	DSET16(&data[0], total - 2);	/* Mode Data Length */
1573 
1574 	return total;
1575 }
1576 
1577 static int
istgt_lu_dvd_transfer_data(CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint8_t * buf,size_t bufsize,size_t len)1578 istgt_lu_dvd_transfer_data(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *buf, size_t bufsize, size_t len)
1579 {
1580 	int rc;
1581 
1582 	if (len > bufsize) {
1583 		ISTGT_ERRLOG("bufsize(%zd) too small\n", bufsize);
1584 		return -1;
1585 	}
1586 	rc = istgt_iscsi_transfer_out(conn, lu_cmd, buf, bufsize, len);
1587 	if (rc < 0) {
1588 		ISTGT_ERRLOG("iscsi_transfer_out()\n");
1589 		return -1;
1590 	}
1591 	return 0;
1592 }
1593 
1594 static int
istgt_lu_dvd_scsi_mode_select_page(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int pf,int sp,uint8_t * data,size_t len)1595 istgt_lu_dvd_scsi_mode_select_page(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int pf, int sp, uint8_t *data, size_t len)
1596 {
1597 	size_t hlen, plen;
1598 	int ps, spf, page, subpage;
1599 	int rc;
1600 
1601 	if (pf == 0) {
1602 		/* vendor specific */
1603 		return 0;
1604 	}
1605 
1606 	if (len < 1)
1607 		return 0;
1608 	ps = BGET8(&data[0], 7);
1609 	spf = BGET8(&data[0], 6);
1610 	page = data[0] & 0x3f;
1611 	if (spf) {
1612 		/* Sub_page mode page format */
1613 		hlen = 4;
1614 		if (len < hlen)
1615 			return 0;
1616 		subpage = data[1];
1617 
1618 		plen = DGET16(&data[2]);
1619 	} else {
1620 		/* Page_0 mode page format */
1621 		hlen = 2;
1622 		if (len < hlen)
1623 			return 0;
1624 		subpage = 0;
1625 		plen = data[1];
1626 	}
1627 	plen += hlen;
1628 	if (len < plen)
1629 		return 0;
1630 
1631 #if 0
1632 	printf("ps=%d, page=%2.2x, subpage=%2.2x\n", ps, page, subpage);
1633 #endif
1634 	switch (page) {
1635 	case 0x08:
1636 		/* Caching */
1637 		{
1638 			int wce, rcd;
1639 
1640 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Caching\n");
1641 			if (subpage != 0x00)
1642 				break;
1643 			if (plen != 0x12 + hlen) {
1644 				/* unknown format */
1645 				break;
1646 			}
1647 			wce = BGET8(&data[2], 2); /* WCE */
1648 			rcd = BGET8(&data[2], 0); /* RCD */
1649 
1650 			if (wce) {
1651 				ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Writeback cache enable\n");
1652 				spec->write_cache = 1;
1653 			} else {
1654 				spec->write_cache = 0;
1655 			}
1656 			if (rcd) {
1657 				ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Read cache disable\n");
1658 				spec->read_cache = 0;
1659 			} else {
1660 				spec->read_cache = 1;
1661 			}
1662 		}
1663 		break;
1664 	default:
1665 		/* not supported */
1666 		break;
1667 	}
1668 
1669 	len -= plen;
1670 	if (len != 0) {
1671 		rc = istgt_lu_dvd_scsi_mode_select_page(spec, conn, cdb,  pf, sp, &data[plen], len);
1672 		if (rc < 0) {
1673 			return rc;
1674 		}
1675 	}
1676 	return 0;
1677 }
1678 
1679 #define FEATURE_DESCRIPTOR_INIT(B,L,FC)					\
1680 	do {								\
1681 		memset((B), 0, (L));					\
1682 		DSET16(&(B)[0], (FC));					\
1683 		(B)[3] = (L) - 4;					\
1684 	} while (0)
1685 
1686 static int
istgt_lu_dvd_get_feature_descriptor(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int fc,uint8_t * data)1687 istgt_lu_dvd_get_feature_descriptor(ISTGT_LU_DVD *spec, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int fc, uint8_t *data)
1688 {
1689 	uint8_t *cp;
1690 	int hlen = 0, len = 0, plen;
1691 
1692 	switch (fc) {
1693 	case 0x0000:
1694 		/* Profile List */
1695 		plen = 2 * 4 + 4;
1696 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1697 		/* Version(5-2) Persistent(1) Current(0) */
1698 		BDSET8W(&data[2], 0, 5, 4);
1699 		BSET8(&data[2], 1);			/* Persistent=1 */
1700 		BSET8(&data[2], 0);			/* Current=1 */
1701 		hlen = 4;
1702 
1703 		/* Profile Descriptor */
1704 		cp = &data[hlen + len];
1705 		/* Profile 1 (CDROM) */
1706 		DSET16(&cp[0], 0x0008);
1707 		if (spec->profile == MM_PROF_CDROM) {
1708 			BSET8(&cp[2], 0);		/* CurrentP(0)=1 */
1709 		}
1710 		plen = 4;
1711 		len += plen;
1712 
1713 		cp = &data[hlen + len];
1714 		/* Profile 2 (DVDROM) */
1715 		DSET16(&cp[0], 0x0010);
1716 		if (spec->profile == MM_PROF_DVDROM) {
1717 			BSET8(&cp[2], 0);		/* CurrentP(0)=1 */
1718 		}
1719 		plen = 4;
1720 		len += plen;
1721 		break;
1722 
1723 	case 0x0001:
1724 		/* Core Feature */
1725 		/* GET CONFIGURATION/GET EVENT STATUS NOTIFICATION/INQUIRY */
1726 		/* MODE SELECT (10)/MODE SENSE (10)/REQUEST SENSE/TEST UNIT READY */
1727 		plen = 8 + 4;
1728 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1729 		/* Version(5-2) Persistent(1) Current(0) */
1730 		BDSET8W(&data[2], 0x01, 5, 4);          /* MMC4 */
1731 		BSET8(&data[2], 1);			/* Persistent=1 */
1732 		BSET8(&data[2], 0);			/* Current=1 */
1733 		hlen = 4;
1734 
1735 		/* Physical Interface Standard */
1736 		DSET32(&data[4], 0x00000000);           /* Unspecified */
1737 		/* DBE(0) */
1738 		BCLR8(&data[8], 0);			/* DBE=0*/
1739 		len = 8;
1740 		break;
1741 
1742 	case 0x0003:
1743 		/* Removable Medium */
1744 		/* MECHANISM STATUS/PREVENT ALLOW MEDIUM REMOVAL/START STOP UNIT */
1745 		plen = 0x04 + 4;
1746 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1747 		/* Version(5-2) Persistent(1) Current(0) */
1748 		BDSET8W(&data[2], 0x01, 5, 4);
1749 		BSET8(&data[2], 1);			/* Persistent=1 */
1750 		BSET8(&data[2], 0);			/* Current=1 */
1751 		hlen = 4;
1752 
1753 		/* Loading Mechanism Type(7-5) Eject(3) Pvnt Jmpr(2) Lock(0) */
1754 		BDSET8W(&data[4], 0x01, 7, 3); /* Tray type loading mechanism */
1755 		BSET8(&data[4], 3);			/* eject via START/STOP YES */
1756 		BSET8(&data[4], 0);			/* locking YES */
1757 		len = 8;
1758 		break;
1759 
1760 	case 0x0010:
1761 		/* Random Readable */
1762 		/* READ CAPACITY/READ (10) */
1763 		plen = 4 + 4;
1764 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1765 		/* Version(5-2) Persistent(1) Current(0) */
1766 		BDSET8W(&data[2], 0x00, 5, 4);
1767 		BSET8(&data[2], 1);			/* Persistent=1 */
1768 		BSET8(&data[2], 0);			/* Current=1 */
1769 		hlen = 4;
1770 
1771 		/* Logical Block Size */
1772 		DSET32(&data[4], (uint32_t) spec->blocklen);
1773 		/* Blocking */
1774 		DSET16(&data[8], 1);
1775 		/* PP(0) */
1776 		BCLR8(&data[10], 0);			/* PP=0 */
1777 		len = 4;
1778 		break;
1779 
1780 	case 0x001d:
1781 		/* Multi-Read Feature */
1782 		/* READ (10)/READ CD/READ DISC INFORMATION/READ TRACK INFORMATION */
1783 		plen = 4;
1784 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1785 		/* Version(5-2) Persistent(1) Current(0) */
1786 		BDSET8W(&data[2], 0x00, 5, 4);
1787 		BSET8(&data[2], 1);			/* Persistent=1 */
1788 		BSET8(&data[2], 0);			/* Current=1 */
1789 		hlen = 4;
1790 		len = 0;
1791 		break;
1792 
1793 	case 0x001e:
1794 		/* CD Read */
1795 		/* READ CD/READ CD MSF/READ TOC/PMA/ATIP */
1796 		plen = 4 + 4;
1797 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1798 		/* Version(5-2) Persistent(1) Current(0) */
1799 		BDSET8W(&data[2], 0x02, 5, 4); /* MMC4 */
1800 		BCLR8(&data[2], 1);			/* Persistent=0 */
1801 		if (spec->profile == MM_PROF_CDROM) {
1802 			BSET8(&data[2], 0);		/* Current=1 */
1803 		} else {
1804 			BCLR8(&data[2], 0);		/* Current=0 */
1805 		}
1806 		hlen = 4;
1807 
1808 		/* DAP(7) C2 Flags(1) CD-Text(0) */
1809 		BCLR8(&data[4], 7);		/* not support DAP */
1810 		BCLR8(&data[4], 1);		/* not support C2 */
1811 		BCLR8(&data[4], 0);		/* not support CD-Text */
1812 		len = 4;
1813 		break;
1814 
1815 	case 0x001f:
1816 		/* DVD Read */
1817 		/* READ (10)/READ (12)/READ DVD STRUCTURE/READ TOC/PMA/ATIP */
1818 		plen = 4;
1819 		FEATURE_DESCRIPTOR_INIT(data, plen, fc);
1820 		/* Version(5-2) Persistent(1) Current(0) */
1821 		BDSET8W(&data[2], 0x00, 5, 4);
1822 		BCLR8(&data[2], 1);			/* Persistent=0 */
1823 		if (spec->profile == MM_PROF_DVDROM) {
1824 			BSET8(&data[2], 0);		/* Current=1 */
1825 		} else {
1826 			BCLR8(&data[2], 0);		/* Current=0 */
1827 		}
1828 		hlen = 4;
1829 		len = 0;
1830 		break;
1831 
1832 	default:
1833 		/* not supported */
1834 		break;
1835 	}
1836 
1837 	return hlen + len;
1838 }
1839 
1840 static int
istgt_lu_dvd_scsi_get_configuration(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int rt,int sfn,uint8_t * data)1841 istgt_lu_dvd_scsi_get_configuration(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int rt, int sfn, uint8_t *data)
1842 {
1843 	uint8_t *cp;
1844 	int hlen = 0, len = 0, plen;
1845 	int fc;
1846 
1847 	/* Feature Header */
1848 	/* Data Length */
1849 	DSET32(&data[0], 0);
1850 	/* Reserved */
1851 	data[4] = 0;
1852 	/* Reserved */
1853 	data[5] = 0;
1854 	/* Current Profile */
1855 	DSET16(&data[6], spec->profile);
1856 	hlen = 8;
1857 
1858 	cp = &data[hlen];
1859 	switch (rt) {
1860 	case 0x00:
1861 		/* all of features */
1862 		for (fc = sfn; fc < 0xffff; fc++) {
1863 			plen = istgt_lu_dvd_get_feature_descriptor(spec, conn, cdb, fc, &cp[len]);
1864 			len += plen;
1865 		}
1866 		break;
1867 
1868 	case 0x01:
1869 		/* current of features */
1870 		for (fc = sfn; fc < 0xffff; fc++) {
1871 			plen = istgt_lu_dvd_get_feature_descriptor(spec, conn, cdb, fc, &cp[len]);
1872 			if (BGET8(&cp[2], 0) == 1) {
1873 				len += plen;
1874 			} else {
1875 				/* drop non active descriptors */
1876 			}
1877 		}
1878 		break;
1879 
1880 	case 0x02:
1881 		/* specified feature */
1882 		fc = sfn;
1883 		plen = istgt_lu_dvd_get_feature_descriptor(spec, conn, cdb, fc, &cp[len]);
1884 		len += plen;
1885 		break;
1886 
1887 	default:
1888 		/* not supported */
1889 		break;
1890 	}
1891 
1892 	/* Data Length */
1893 	DSET32(&data[0], len);
1894 
1895 	return hlen + len;
1896 }
1897 
1898 static int
istgt_lu_dvd_scsi_get_event_status_notification(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int keep,int ncr,uint8_t * data)1899 istgt_lu_dvd_scsi_get_event_status_notification(ISTGT_LU_DVD *spec, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int keep __attribute__((__unused__)), int ncr, uint8_t *data)
1900 {
1901 	uint8_t *cp;
1902 	int hlen = 0, len = 0;
1903 
1904 	/* Event Descriptor Length */
1905 	DSET16(&data[0], 0);
1906 	/* NEA(7) Notification Class(2-0) */
1907 	data[2] = 0;
1908 	/* Supported Event Classes */
1909 	data[3] = 0x7e;
1910 	hlen = 4;
1911 
1912 	cp = &data[hlen];
1913 	/* Lowest class number has highest priority */
1914 	if (ncr & (1 << 0)) {
1915 		/* Reserved */
1916 		len = 0;
1917 	}
1918 	if (ncr & (1 << 1)) {
1919 		/* Operational Change */
1920 		BDSET8W(&data[2], 0x01, 2, 3);		/* Notification Class */
1921 		/* Event Code */
1922 		BDSET8W(&cp[0], 0x00, 3, 4);		/* NoChg */
1923 		/* Persistent Prevented(7) Operational Status(3-0) */
1924 		BDSET8(&cp[1], 0, 7);			/* not prevented */
1925 		BDADD8W(&cp[1], 0, 3, 4);
1926 		/* Operational Change */
1927 		DSET16(&cp[2], 0x00);			/* NoChg */
1928 		len = 4;
1929 		goto event_available;
1930 	}
1931 	if (ncr & (1 << 2)) {
1932 		/* Power Management */
1933 		BDSET8W(&data[2], 0x02, 2, 3);		/* Notification Class */
1934 		/* Event Code */
1935 		BDSET8W(&cp[0], 0x00, 3, 4);		/* NoChg */
1936 		/* Power Status */
1937 		cp[1] = 0x01;				/* Active */
1938 		/* Reserved */
1939 		cp[2] = 0;
1940 		/* Reserved */
1941 		cp[3] = 0;
1942 		len = 4;
1943 		goto event_available;
1944 	}
1945 	if (ncr & (1 << 3)) {
1946 		/* External Request */
1947 		BDSET8W(&data[2], 0x03, 2, 3);		/* Notification Class */
1948 		/* Event Code */
1949 		BDSET8W(&cp[0], 0x00, 3, 4);		/* NoChg */
1950 		/* Persistent Prevented(7) External Request Status(3-0) */
1951 		BDSET8(&cp[1], 0, 7);			/* not prevented */
1952 		BDADD8W(&cp[1], 0, 3, 4);		/* Ready */
1953 		/* External Request */
1954 		DSET16(&cp[2], 0x00);			/* No Request */
1955 		len = 4;
1956 		goto event_available;
1957 	}
1958 	if (ncr & (1 << 4)) {
1959 		/* Media */
1960 		BDSET8W(&data[2], 0x04, 2, 3);		/* Notification Class */
1961 		if (spec->mchanged) {
1962 			if (spec->mwait > 0) {
1963 				spec->mwait--;
1964 			} else {
1965 				spec->mchanged = 0;
1966 				spec->mload = 1;
1967 			}
1968 			if (spec->mload) {
1969 				/* Event Code */
1970 				BDSET8W(&cp[0], 0x02, 3, 4);	/* NewMedia */
1971 				/* Media Status */
1972 				/* Media Present(1) Door or Tray open(0) */
1973 				BDSET8(&cp[1], 1, 1);		/* media present */
1974 				BDADD8(&cp[1], 0, 0);		/* tray close */
1975 			} else {
1976 				/* Event Code */
1977 				BDSET8W(&cp[0], 0x03, 3, 4);	/* MediaRemoval */
1978 				/* Media Status */
1979 				/* Media Present(1) Door or Tray open(0) */
1980 				BDSET8(&cp[1], 0, 1);		/* media absent */
1981 				BDADD8(&cp[1], 1, 0);		/* tray open */
1982 			}
1983 		} else {
1984 			if (spec->mwait > 0) {
1985 				spec->mwait--;
1986 				/* Event Code */
1987 				BDSET8W(&cp[0], 0x01, 3, 4);	/* EjectRequest */
1988 				/* Media Status */
1989 				/* Media Present(1) Door or Tray open(0) */
1990 				BDSET8(&cp[1], 0, 1);		/* media absent */
1991 				BDADD8(&cp[1], 1, 0);		/* tray open */
1992 			} else {
1993 				if (spec->mload) {
1994 					/* Event Code */
1995 					BDSET8W(&cp[0], 0x00, 3, 4);	/* NoChg */
1996 					/* Media Status */
1997 					/* Media Present(1) Door or Tray open(0) */
1998 					BDSET8(&cp[1], 1, 1);	/* media present */
1999 					BDADD8(&cp[1], 0, 0);	/* tray close */
2000 				} else {
2001 					/* Event Code */
2002 					BDSET8W(&cp[0], 0x00, 3, 4);	/* NoChg */
2003 					/* Media Status */
2004 					/* Media Present(1) Door or Tray open(0) */
2005 					BDSET8(&cp[1], 0, 1);	/* media absent */
2006 					BDADD8(&cp[1], 0, 0);	/* tray close */
2007 				}
2008 			}
2009 		}
2010 		/* Start Slot */
2011 		cp[2] = 0;
2012 		/* End Slot */
2013 		cp[3] = 0;
2014 		len = 4;
2015 		goto event_available;
2016 	}
2017 	if (ncr & (1 << 5)) {
2018 		/* Multi-Initiator */
2019 		BDSET8W(&data[2], 0x05, 2, 3);		/* Notification Class */
2020 		/* Event Code */
2021 		BDSET8W(&cp[0], 0x00, 3, 4);		/* NoChg */
2022 		/* Persistent Prevented(7) Multiple Initiator Status(3-0) */
2023 		BDSET8(&cp[1], 0, 7);			/* not prevented */
2024 		BDADD8W(&cp[1], 0, 3, 4);		/* Ready */
2025 		/* Multiple Initiator Priority */
2026 		DSET16(&cp[2], 0x00);			/* No Request */
2027 		len = 4;
2028 		goto event_available;
2029 	}
2030 	if (ncr & (1 << 6)) {
2031 		/* Device Busy */
2032 		BDSET8W(&data[2], 0x06, 2, 3);		/* Notification Class */
2033 		/* Event Code */
2034 		BDSET8W(&cp[0], 0x00, 3, 4);		/* NoChg */
2035 		/* Media Status */
2036 		/* Device Busy Status */
2037 		cp[1] = 0;				/* Not Busy */
2038 		/* Time */
2039 		DSET16(&cp[2], 0);
2040 		len = 4;
2041 		goto event_available;
2042 	}
2043 	if (ncr & (1 << 7)) {
2044 		/* Reserved */
2045 		len = 0;
2046 	}
2047 
2048 	if (len == 0) {
2049 		/* No Event Available */
2050 		BDSET8(&data[2], 0, 7);			/* NEA=1 */
2051 	}
2052 
2053 event_available:
2054 	/* Event Descriptor Length */
2055 	DSET16(&data[0], len + (hlen - 2));
2056 
2057 	return hlen + len;
2058 }
2059 
2060 static int
istgt_lu_dvd_scsi_mechanism_status(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,uint8_t * data)2061 istgt_lu_dvd_scsi_mechanism_status(ISTGT_LU_DVD *spec, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), uint8_t *data)
2062 {
2063 	uint8_t *cp;
2064 	int hlen = 0, len = 0, plen;
2065 	int selected_slot = 0, max_slots = 1;
2066 
2067 	/* Mechanism Status Header */
2068 	/* Fault(7) Changer State(6-5) Current Slot(4-0) */
2069 	BDSET8(&data[0], 0, 7);
2070 	BDADD8W(&data[0], 0x00, 6, 2);			/* Ready */
2071 	BDADD8W(&data[0], (selected_slot & 0x1f), 4, 5); /* slot low bits */
2072 	/* Mechanism State(7-5) Door open(4) Current Slot(2-0) */
2073 	BDSET8W(&data[1], 0x00, 7, 3);		/* Idle */
2074 	BDADD8W(&data[1], (selected_slot & 0xe0) >> 5, 2, 3); /* slot high bits */
2075 	/* Current LBA (Legacy) */
2076 	DSET24(&data[2], 0);
2077 	/* Number of Slots Available */
2078 	data[5] = max_slots;
2079 	/* Length of Slot Tables */
2080 	DSET16(&data[6], 0);
2081 	hlen = 8;
2082 
2083 	/* Slot Tables */
2084 	/* Slot 0 */
2085 	cp = &data[hlen + len];
2086 
2087 	if (spec->mchanged) {
2088 		if (spec->mload) {
2089 			/* Disc Present(7) Change(0) */
2090 			BDSET8(&cp[0], 1, 7);		/* disc in slot */
2091 		} else {
2092 			/* Disc Present(7) Change(0) */
2093 			BDSET8(&cp[0], 0, 7);		/* no disc in slot */
2094 		}
2095 		BDADD8(&cp[0], 1, 0);			/* disc changed */
2096 	} else {
2097 		if (spec->mload) {
2098 			/* Disc Present(7) Change(0) */
2099 			BDSET8(&cp[0], 1, 7);		/* disc in slot */
2100 		} else {
2101 			/* Disc Present(7) Change(0) */
2102 			BDSET8(&cp[0], 0, 7);		/* no disc in slot */
2103 		}
2104 		BDADD8(&cp[0], 0, 0);			/* disc not changed */
2105 	}
2106 	/* CWP_V(1) CWP(0) */
2107 	BDSET8(&cp[1], 0, 1);				/* non Cartridge Write Protection */
2108 	BDADD8(&cp[1], 0, 0);				/* CWP=0 */
2109 	/* Reserved */
2110 	cp[2] = 0;
2111 	/* Reserved */
2112 	cp[3] = 0;
2113 	plen = 4;
2114 	len += plen;
2115 
2116 	/* Length of Slot Tables */
2117 	DSET16(&data[6], len);
2118 
2119 	return hlen + len;
2120 }
2121 
2122 static int
istgt_lu_dvd_scsi_read_toc(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int msf,int format,int track,uint8_t * data)2123 istgt_lu_dvd_scsi_read_toc(ISTGT_LU_DVD *spec, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int msf, int format, int track __attribute__((__unused__)), uint8_t *data)
2124 {
2125 	uint8_t *cp;
2126 	int hlen = 0, len = 0, plen;
2127 
2128 	switch (format) {
2129 	case 0x00: /* Formatted TOC */
2130 		/* TOC Data Length */
2131 		DSET16(&data[0], 0);
2132 		/* First Track Number */
2133 		data[2] = 1;
2134 		/* Last Track Number */
2135 		data[3] = 1;
2136 		hlen = 4;
2137 
2138 		/* TOC Track Descriptor */
2139 		/* Track 1 Descriptor */
2140 		cp = &data[hlen + len];
2141 
2142 		/* Reserved */
2143 		cp[0] = 0;
2144 		/* ADR(7-4) CONTROL(3-0) */
2145 		cp[1] = 0x14;
2146 		/* Track Number */
2147 		cp[2] = 1;
2148 		/* Reserved */
2149 		cp[3] = 0;
2150 		/* Track Start Address */
2151 		if (msf) {
2152 			DSET32(&cp[4], istgt_lba2msf(0));
2153 		} else {
2154 			DSET32(&cp[4], 0);
2155 		}
2156 		plen = 8;
2157 		len += plen;
2158 
2159 		/* Track AAh (Lead-out) Descriptor */
2160 		cp = &data[hlen + len];
2161 
2162 		/* Reserved */
2163 		cp[0] = 0;
2164 		/* ADR(7-4) CONTROL(3-0) */
2165 		cp[1] = 0x14;
2166 		/* Track Number */
2167 		cp[2] = 0xaa;
2168 		/* Reserved */
2169 		cp[3] = 0;
2170 		/* Track Start Address */
2171 		if (msf) {
2172 			DSET32(&cp[4], istgt_lba2msf(spec->blockcnt));
2173 		} else {
2174 			DSET32(&cp[4], spec->blockcnt);
2175 		}
2176 		plen = 8;
2177 		len += plen;
2178 
2179 		/* TOC Data Length */
2180 		DSET16(&data[0], hlen + len - 2);
2181 		break;
2182 
2183 	case 0x01: /* Multi-session Information */
2184 		/* TOC Data Length */
2185 		DSET16(&data[0], 0);
2186 		/* First Complete Session Number */
2187 		data[2] = 1;
2188 		/* Last Complete Session Number */
2189 		data[3] = 1;
2190 		hlen = 4;
2191 
2192 		/* TOC Track Descriptor */
2193 		cp = &data[hlen + len];
2194 
2195 		/* Reserved */
2196 		cp[0] = 0;
2197 		/* ADR(7-4) CONTROL(3-0) */
2198 		cp[1] = 0x14;
2199 		/* First Track Number In Last Complete Session */
2200 		cp[2] = 1;
2201 		/* Reserved */
2202 		cp[3] = 0;
2203 		/* Start Address of First Track in Last Session */
2204 		if (msf) {
2205 			DSET32(&cp[4], istgt_lba2msf(0));
2206 		} else {
2207 			DSET32(&cp[4], 0);
2208 		}
2209 		len = 8;
2210 
2211 		/* TOC Data Length */
2212 		DSET16(&data[0], hlen + len - 2);
2213 		break;
2214 
2215 	case 0x02: /* Raw TOC */
2216 		/* TOC Data Length */
2217 		DSET16(&data[0], 0);
2218 		/* First Complete Session Number */
2219 		data[2] = 1;
2220 		/* Last Complete Session Number */
2221 		data[3] = 1;
2222 		hlen = 4;
2223 
2224 		/* TOC Track Descriptor */
2225 		/* First Track number in the program area */
2226 		cp = &data[hlen + len];
2227 
2228 		/* Session Number */
2229 		cp[0] = 1;
2230 		/* ADR(7-4) CONTROL(3-0) */
2231 		cp[1] = 0x14;
2232 		/* TNO */
2233 		cp[2] = 0;
2234 		/* POINT */
2235 		cp[3] = 0xa0;
2236 		/* Min */
2237 		cp[4] = 0;
2238 		/* Sec */
2239 		cp[5] = 0;
2240 		/* Frame */
2241 		cp[6] = 0;
2242 		/* Zero */
2243 		cp[7] = 0;
2244 		/* PMIN / First Track Number */
2245 		cp[8] = 1;
2246 		/* PSEC / Disc Type */
2247 		cp[9] = 0x00; /* CD-DA or CD Data with first track in Mode 1 */
2248 		/* PFRAME */
2249 		cp[10] = 0;
2250 		plen = 11;
2251 		len += plen;
2252 
2253 		/* Last Track number in the program area */
2254 		cp = &data[hlen + len];
2255 
2256 		/* Session Number */
2257 		cp[0] = 1;
2258 		/* ADR(7-4) CONTROL(3-0) */
2259 		cp[1] = 0x14;
2260 		/* TNO */
2261 		cp[2] = 0;
2262 		/* POINT */
2263 		cp[3] = 0xa1;
2264 		/* Min */
2265 		cp[4] = 0;
2266 		/* Sec */
2267 		cp[5] = 0;
2268 		/* Frame */
2269 		cp[6] = 0;
2270 		/* Zero */
2271 		cp[7] = 0;
2272 		/* PMIN / Last Track Number */
2273 		cp[8] = 1;
2274 		/* PSEC */
2275 		cp[9] = 0;
2276 		/* PFRAME */
2277 		cp[10] = 0;
2278 		plen = 11;
2279 		len += plen;
2280 
2281 		/* Start location of the Lead-out area */
2282 		cp = &data[hlen + len];
2283 
2284 		/* Session Number */
2285 		cp[0] = 1;
2286 		/* ADR(7-4) CONTROL(3-0) */
2287 		cp[1] = 0x14;
2288 		/* TNO */
2289 		cp[2] = 0;
2290 		/* POINT */
2291 		cp[3] = 0xa2;
2292 		/* Min */
2293 		cp[4] = 0;
2294 		/* Sec */
2295 		cp[5] = 0;
2296 		/* Frame */
2297 		cp[6] = 0;
2298 		/* Zero */
2299 		cp[7] = 0;
2300 		/* PMIN / Start position of Lead-out */
2301 		/* PSEC / Start position of Lead-out */
2302 		/* PFRAME / Start position of Lead-out */
2303 		if (msf) {
2304 			DSET24(&cp[8], istgt_lba2msf(spec->blockcnt));
2305 		} else {
2306 			DSET24(&cp[8], spec->blockcnt);
2307 		}
2308 		plen = 11;
2309 		len += plen;
2310 
2311 		/* Track data */
2312 		cp = &data[hlen + len];
2313 
2314 		/* Session Number */
2315 		cp[0] = 1;
2316 		/* ADR(7-4) CONTROL(3-0) */
2317 		cp[1] = 0x14;
2318 		/* TNO */
2319 		cp[2] = 0;
2320 		/* POINT */
2321 		cp[3] = 1;
2322 		/* Min */
2323 		cp[4] = 0;
2324 		/* Sec */
2325 		cp[5] = 0;
2326 		/* Frame */
2327 		cp[6] = 0;
2328 		/* Zero */
2329 		cp[7] = 0;
2330 		/* PMIN / Start position of Lead-out */
2331 		/* PSEC / Start position of Lead-out */
2332 		/* PFRAME / Start position of Lead-out */
2333 		if (msf) {
2334 			DSET24(&cp[8], istgt_lba2msf(0));
2335 		} else {
2336 			DSET24(&cp[8], 0);
2337 		}
2338 		plen = 11;
2339 		len += plen;
2340 
2341 		/* TOC Data Length */
2342 		DSET16(&data[0], hlen + len - 2);
2343 		break;
2344 
2345 	default:
2346 		ISTGT_ERRLOG("unsupported format 0x%x\n", format);
2347 		return -1;
2348 	}
2349 
2350 	return hlen + len;
2351 }
2352 
2353 static int
istgt_lu_dvd_scsi_read_disc_information(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int datatype,uint8_t * data)2354 istgt_lu_dvd_scsi_read_disc_information(ISTGT_LU_DVD *spec __attribute__((__unused__)), CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int datatype, uint8_t *data)
2355 {
2356 	int hlen = 0, len = 0;
2357 
2358 	switch (datatype) {
2359 	case 0x00: /* Disc Information Block */
2360 		/* Disc Information Length */
2361 		DSET16(&data[0], 0);
2362 		hlen = 2;
2363 
2364 		/* Disc Information Data Type(7-5) Erasable(4) */
2365 		/* State of last Session(3-2) Disc Status(1-0) */
2366 		BDSET8W(&data[2], datatype, 7, 3);
2367 		BDADD8W(&data[2], 0, 4, 1);
2368 		BDADD8W(&data[2], 0x03, 3, 2);		/* Complete Session */
2369 		BDADD8W(&data[2], 0x02, 1, 2);		/* Finalized Disc */
2370 		/* Number of First Track on Disc */
2371 		data[3] = 1;
2372 		/* Number of Sessions (Least Significant Byte) */
2373 		data[4] = (1) & 0xff;
2374 		/* First Track Number in Last Session (Least Significant Byte) */
2375 		data[5] = (1) & 0xff;
2376 		/* Last Track Number in Last Session (Least Significant Byte) */
2377 		data[6] = (1) & 0xff;
2378 		/* DID_V(7) DBC_V(6) URU(5) DAC_V(4) BG Format Status(1-0) */
2379 		BDSET8(&data[7], 0, 7);			/* Disc ID Valid */
2380 		BDADD8(&data[7], 0, 6);			/* Disc Bar Code Valid */
2381 		BDADD8(&data[7], 1, 5);			/* Unrestricted Use Disc */
2382 		BDADD8(&data[7], 0, 4);			/* Disc Application Code Valid */
2383 		BDADD8W(&data[7], 0, 1, 2);		/* BG Format Status */
2384 		/* Disc Type */
2385 		data[8] = 0x00;				/* CD-DA or CD-ROM Disc */
2386 		/* Number of Sessions (Most Significant Byte) */
2387 		data[9] = (1 >> 8) & 0xff;
2388 		/* First Track Number in Last Session (Most Significant Byte) */
2389 		data[10] = (1 >> 8) & 0xff;
2390 		/* Last Track Number in Last Session (Most Significant Byte) */
2391 		data[11] = (1 >> 8) & 0xff;
2392 		/* Disc Identification */
2393 		DSET32(&data[12], 0);
2394 		/* Last Session Lead-in Start Address */
2395 		DSET32(&data[16], 0);
2396 		/* Last Possible Lead-out Start Address */
2397 		DSET32(&data[20], 0);
2398 		/* Disc Bar Code */
2399 		memset(&data[24], 0, 8);
2400 		/* Disc Application Code */
2401 		data[32] = 0;
2402 		/* Number of OPC Tables */
2403 		data[33] = 0;
2404 		/* OPC Table Entries */
2405 		//data[34] = 0;
2406 		len = 34 - hlen;
2407 
2408 		/* Disc Information Length */
2409 		DSET16(&data[0], len);
2410 		break;
2411 
2412 	case 0x01: /* Track Resources Information Block */
2413 		/* Disc Information Length */
2414 		DSET16(&data[0], 0);
2415 		hlen = 2;
2416 
2417 		/* Disc Information Data Type(7-5) */
2418 		BDSET8W(&data[2], datatype, 7, 3);
2419 		/* Reserved */
2420 		data[3] = 0;
2421 		/* Maximum possible number of the Tracks on the disc */
2422 		DSET16(&data[4], 99);
2423 		/* Number of the assigned Tracks on the disc */
2424 		DSET16(&data[6], 1);
2425 		/* Maximum possible number of appendable Tracks on the disc */
2426 		DSET16(&data[8], 99);
2427 		/* Current number of appendable Tracks on the disc */
2428 		DSET16(&data[10], 99);
2429 		len = 12 - hlen;
2430 
2431 		/* Disc Information Length */
2432 		DSET16(&data[0], len);
2433 		break;
2434 
2435 	case 0x02: /* POW Resources Information Block */
2436 		/* Disc Information Length */
2437 		DSET16(&data[0], 0);
2438 		hlen = 2;
2439 
2440 		/* Disc Information Data Type(7-5) */
2441 		BDSET8W(&data[2], datatype, 7, 3);
2442 		/* Reserved */
2443 		data[3] = 0;
2444 		/* Remaining POW Replacements */
2445 		DSET32(&data[4], 0);
2446 		/* Remaining POW Reallocation Map Entries */
2447 		DSET32(&data[8], 0);
2448 		/* Number of Remaining POW Updates */
2449 		DSET32(&data[12], 0);
2450 		len = 16 - hlen;
2451 
2452 		/* Disc Information Length */
2453 		DSET16(&data[0], len);
2454 		break;
2455 
2456 	default:
2457 		ISTGT_ERRLOG("unsupported datatype 0x%x\n", datatype);
2458 		return -1;
2459 	}
2460 
2461 	return hlen + len;
2462 }
2463 
2464 static int
istgt_lu_dvd_scsi_read_disc_structure(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int mediatype,int layernumber,int format,int agid,uint8_t * data)2465 istgt_lu_dvd_scsi_read_disc_structure(ISTGT_LU_DVD *spec, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int mediatype, int layernumber __attribute__((__unused__)), int format, int agid __attribute__((__unused__)), uint8_t *data)
2466 {
2467 	uint8_t *cp;
2468 	int hlen = 0, len = 0;
2469 
2470 	if (mediatype == 0x00) {
2471 		/* DVD and HD DVD types */
2472 	} else if (mediatype == 0x01) {
2473 		/* BD */
2474 	} else {
2475 		/* Reserved */
2476 	}
2477 
2478 	switch (format) {
2479 	case 0x00: /* Physical Format Information */
2480 		/* Disc Structure Data Length */
2481 		DSET16(&data[0], 0);
2482 		/* Reserved */
2483 		data[2] = 0;
2484 		/* Reserved */
2485 		data[3] = 0;
2486 		hlen = 4;
2487 
2488 		/* Physical Format Information */
2489 		cp = &data[hlen + len];
2490 
2491 		/* Disk Category(7-4) Part Version(3-0) */
2492 		BDSET8W(&cp[0], 0x00, 7, 4);		/* DVD-ROM */
2493 		BDADD8W(&cp[0], 0x01, 3, 4);		/* part 1 */
2494 		/* Disc Size(7-4) Maximum Rate(0-3) */
2495 		BDSET8W(&cp[1], 0x00, 7, 4);		/* 120mm */
2496 		BDADD8W(&cp[1], 0x0f, 3, 4);		/* Not Specified */
2497 		/* Number of Layers(6-5) Track(4) Layer Type(3-0) */
2498 		BDSET8W(&cp[2], 0x00, 6, 2);		/* one layer */
2499 		BDADD8W(&cp[2], 0x00, 4, 1);		/* Parallel Track Path */
2500 		BDADD8W(&cp[2], 0x00, 3, 4);		/* embossed data */
2501 		/* Linear Density(7-4) Track Density(3-0) */
2502 		BDSET8W(&cp[3], 0x00, 7, 4);		/* 0.267 um/bit */
2503 		BDADD8W(&cp[3], 0x00, 3, 4);		/* 0.74 um/track */
2504 		/* Starting Physical Sector Number of Data Area */
2505 		DSET32(&cp[4], 0);
2506 		/* End Physical Sector Number of Data Area */
2507 		DSET32(&cp[8], spec->blockcnt - 1);
2508 		/* End Physical Sector Number in Layer 0 */
2509 		DSET32(&cp[12], spec->blockcnt - 1);
2510 		/* BCA(7) */
2511 		BDSET8(&cp[16], 0, 7);
2512 		/* Media Specific */
2513 		memset(&cp[17], 0, 2048 - 16);
2514 		len = 2048;
2515 
2516 		/* Disc Information Length */
2517 		DSET16(&data[0], hlen + len - 2);
2518 		break;
2519 
2520 	case 0x01: /* DVD Copyright Information */
2521 		/* Disc Structure Data Length */
2522 		DSET16(&data[0], 0);
2523 		/* Reserved */
2524 		data[2] = 0;
2525 		/* Reserved */
2526 		data[3] = 0;
2527 		hlen = 4;
2528 
2529 		/* DVD Copyright Information */
2530 		cp = &data[hlen + len];
2531 
2532 		/* Copyright Protection System Type */
2533 		cp[0] = 0x00;
2534 		//cp[0] = 0x01;				/* CSS/CPPM */
2535 		/* Region Management Information */
2536 		cp[1] = 0x00;
2537 		//cp[1] = 0xff & ~(1 << (2 - 1));	/* 2=Japan */
2538 		/* Reserved */
2539 		cp[2] = 0;
2540 		/* Reserved */
2541 		cp[3] = 0;
2542 		len = 4;
2543 
2544 		/* Disc Information Length */
2545 		DSET16(&data[0], hlen + len - 2);
2546 		break;
2547 
2548 	default:
2549 		ISTGT_ERRLOG("unsupported format 0x%x\n", format);
2550 		return -1;
2551 	}
2552 
2553 	return hlen + len;
2554 }
2555 
2556 static int
istgt_lu_dvd_scsi_report_key(ISTGT_LU_DVD * spec,CONN_Ptr conn,uint8_t * cdb,int keyclass,int agid,int keyformat,uint8_t * data)2557 istgt_lu_dvd_scsi_report_key(ISTGT_LU_DVD *spec __attribute__((__unused__)), CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int keyclass, int agid __attribute__((__unused__)), int keyformat, uint8_t *data)
2558 {
2559 	uint8_t *cp;
2560 	int hlen = 0, len = 0;
2561 
2562 	if (keyclass == 0x00) {
2563 		/* DVD CSS/CPPM or CPRM */
2564 	} else {
2565 		return -1;
2566 	}
2567 
2568 	switch (keyformat) {
2569 	case 0x08: /* Report Drive region settings */
2570 		/* REPORT KEY Data Length */
2571 		DSET16(&data[0], 6);
2572 		/* Reserved */
2573 		data[2] = 0;
2574 		/* Reserved */
2575 		data[3] = 0;
2576 		hlen = 4;
2577 
2578 		/* RPC State */
2579 		cp = &data[hlen + len];
2580 
2581 		/* Type Code(7-6) # of Vendor Resets Available(5-3) */
2582 		/* # of User Controlled Changes Available(2-0) */
2583 		BDSET8W(&cp[0], 0x00, 7, 2);	/* No Drive region setting */
2584 		//BDSET8W(&cp[0], 0x01, 7, 2);	/* Drive region is set */
2585 		BDADD8W(&cp[0], 4, 5, 3);		/* # of vendor */
2586 		BDADD8W(&cp[0], 5, 2, 3);		/* # of user */
2587 		/* Region Mask */
2588 		cp[1] = 0;
2589 		//cp[1] = 0xff & ~(1 << (2 - 1));	/* 2=Japan */
2590 		/* RPC Scheme */
2591 		cp[2] = 0;
2592 		//cp[2] = 0x01;	/* RPC Phase II */
2593 		/* Reserved */
2594 		cp[3] = 0;
2595 		len = 4;
2596 
2597 		/* REPORT KEY Data Length */
2598 		DSET16(&data[0], hlen + len - 2);
2599 		break;
2600 
2601 	case 0x00: /* AGID for CSS/CPPM */
2602 	case 0x01: /* Challenge Key */
2603 	case 0x02: /* KEY1 */
2604 	case 0x04: /* TITLE KEY */
2605 	case 0x05: /* ASF */
2606 	case 0x11: /* AGID for CPRM */
2607 		/* not supported */
2608 		return -1;
2609 
2610 	default:
2611 		ISTGT_ERRLOG("unsupported keyformat 0x%x\n", keyformat);
2612 		return -1;
2613 	}
2614 
2615 	return hlen + len;
2616 }
2617 
2618 static int
istgt_lu_dvd_lbread(ISTGT_LU_DVD * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint64_t lba,uint32_t len)2619 istgt_lu_dvd_lbread(ISTGT_LU_DVD *spec, CONN_Ptr conn __attribute__((__unused__)), ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
2620 {
2621 	uint8_t *data;
2622 	uint64_t maxlba;
2623 	uint64_t llen;
2624 	uint64_t blen;
2625 	uint64_t offset;
2626 	uint64_t nbytes;
2627 	int64_t rc;
2628 
2629 	if (len == 0) {
2630 		lu_cmd->data = NULL;
2631 		lu_cmd->data_len = 0;
2632 		return 0;
2633 	}
2634 
2635 	maxlba = spec->blockcnt;
2636 	llen = (uint64_t) len;
2637 	blen = spec->blocklen;
2638 	offset = lba * blen;
2639 	nbytes = llen * blen;
2640 
2641 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
2642 	    "Read: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
2643 	    maxlba, lba, len);
2644 
2645 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
2646 		ISTGT_ERRLOG("end of media\n");
2647 		return -1;
2648 	}
2649 
2650 	if (nbytes > lu_cmd->iobufsize) {
2651 		ISTGT_ERRLOG("nbytes(%zu) > iobufsize(%zu)\n",
2652 		    (size_t) nbytes, lu_cmd->iobufsize);
2653 		return -1;
2654 	}
2655 	data = lu_cmd->iobuf;
2656 
2657 	rc = istgt_lu_dvd_seek(spec, offset);
2658 	if (rc < 0) {
2659 		ISTGT_ERRLOG("lu_dvd_seek() failed\n");
2660 		return -1;
2661 	}
2662 
2663 	rc = istgt_lu_dvd_read(spec, data, nbytes);
2664 	if (rc < 0) {
2665 		ISTGT_ERRLOG("lu_dvd_read() failed\n");
2666 		return -1;
2667 	}
2668 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Read %"PRId64"/%"PRIu64" bytes\n",
2669 	    rc, nbytes);
2670 
2671 	lu_cmd->data = data;
2672 	lu_cmd->data_len = rc;
2673 
2674 	return 0;
2675 }
2676 
2677 #if 0
2678 static int
2679 istgt_lu_dvd_lbwrite(ISTGT_LU_DVD *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
2680 {
2681 	uint8_t *data;
2682 	uint64_t maxlba;
2683 	uint64_t llen;
2684 	uint64_t blen;
2685 	uint64_t offset;
2686 	uint64_t nbytes;
2687 	int64_t rc;
2688 
2689 	if (len == 0) {
2690 		lu_cmd->data_len = 0;
2691 		return 0;
2692 	}
2693 
2694 	maxlba = spec->blockcnt;
2695 	llen = (uint64_t) len;
2696 	blen = spec->blocklen;
2697 	offset = lba * blen;
2698 	nbytes = llen * blen;
2699 
2700 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
2701 	    "Write: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
2702 	    maxlba, lba, len);
2703 
2704 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
2705 		ISTGT_ERRLOG("end of media\n");
2706 		return -1;
2707 	}
2708 
2709 	if (nbytes > lu_cmd->iobufsize) {
2710 		ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n",
2711 		    nbytes, lu_cmd->iobufsize);
2712 		return -1;
2713 	}
2714 	data = lu_cmd->iobuf;
2715 
2716 	rc = istgt_lu_dvd_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
2717 	    lu_cmd->iobufsize, nbytes);
2718 	if (rc < 0) {
2719 		ISTGT_ERRLOG("lu_dvd_transfer_data() failed\n");
2720 		return -1;
2721 	}
2722 
2723 	if (spec->lu->readonly) {
2724 		ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num);
2725 		return -1;
2726 	}
2727 	if (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY) {
2728 		ISTGT_ERRLOG("LU%d: readonly media\n", spec->lu->num);
2729 		return -1;
2730 	}
2731 
2732 	rc = istgt_lu_dvd_seek(spec, offset);
2733 	if (rc < 0) {
2734 		ISTGT_ERRLOG("lu_dvd_seek() failed\n");
2735 		return -1;
2736 	}
2737 
2738 	rc = istgt_lu_dvd_write(spec, data, nbytes);
2739 	if (rc < 0 || rc != nbytes) {
2740 		ISTGT_ERRLOG("lu_dvd_write() failed\n");
2741 		return -1;
2742 	}
2743 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n",
2744 	    rc, nbytes);
2745 
2746 	lu_cmd->data_len = rc;
2747 
2748 	return 0;
2749 }
2750 
2751 static int
2752 istgt_lu_dvd_lbsync(ISTGT_LU_DVD *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
2753 {
2754 	uint64_t maxlba;
2755 	uint64_t llen;
2756 	uint64_t blen;
2757 	uint64_t offset;
2758 	uint64_t nbytes;
2759 	int64_t rc;
2760 
2761 	if (len == 0) {
2762 		return 0;
2763 	}
2764 
2765 	maxlba = spec->blockcnt;
2766 	llen = (uint64_t) len;
2767 	blen = spec->blocklen;
2768 	offset = lba * blen;
2769 	nbytes = llen * blen;
2770 
2771 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
2772 	    "Sync: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
2773 	    maxlba, lba, len);
2774 
2775 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
2776 		ISTGT_ERRLOG("end of media\n");
2777 		return -1;
2778 	}
2779 
2780 	rc = istgt_lu_dvd_sync(spec, offset, nbytes);
2781 	if (rc < 0) {
2782 		ISTGT_ERRLOG("lu_dvd_sync() failed\n");
2783 		return -1;
2784 	}
2785 
2786 	return 0;
2787 }
2788 #endif
2789 
2790 static int
istgt_lu_dvd_build_sense_data(ISTGT_LU_DVD * spec,uint8_t * data,int sk,int asc,int ascq)2791 istgt_lu_dvd_build_sense_data(ISTGT_LU_DVD *spec __attribute__((__unused__)), uint8_t *data, int sk, int asc, int ascq)
2792 {
2793 	int rc;
2794 
2795 	rc = istgt_lu_scsi_build_sense_data(data, sk, asc, ascq);
2796 	if (rc < 0) {
2797 		return -1;
2798 	}
2799 	return rc;
2800 }
2801 
2802 static int
istgt_lu_dvd_build_sense_media(ISTGT_LU_DVD * spec,uint8_t * data)2803 istgt_lu_dvd_build_sense_media(ISTGT_LU_DVD *spec, uint8_t *data)
2804 {
2805 	uint8_t *sense_data;
2806 	int *sense_len;
2807 	int data_len;
2808 
2809 	sense_data = data;
2810 	sense_len = &data_len;
2811 	*sense_len = 0;
2812 
2813 	if (!spec->mload && !spec->mchanged) {
2814 		/* MEDIUM NOT PRESENT */
2815 		BUILD_SENSE(NOT_READY, 0x3a, 0x00);
2816 		return data_len;
2817 	}
2818 	if (spec->mchanged) {
2819 		/* MEDIUM NOT PRESENT */
2820 		BUILD_SENSE(NOT_READY, 0x3a, 0x00);
2821 		return data_len;
2822 #if 0
2823 		/* LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE */
2824 		BUILD_SENSE(NOT_READY, 0x04, 0x00);
2825 		return data_len;
2826 		/* LOGICAL UNIT IS IN PROCESS OF BECOMING READY */
2827 		BUILD_SENSE(NOT_READY, 0x04, 0x01);
2828 		return data_len;
2829 #endif
2830 	}
2831 	return 0;
2832 }
2833 
2834 int
istgt_lu_dvd_reset(ISTGT_LU_Ptr lu,int lun)2835 istgt_lu_dvd_reset(ISTGT_LU_Ptr lu, int lun)
2836 {
2837 	ISTGT_LU_DVD *spec;
2838 	int flags;
2839 	int rc;
2840 
2841 	if (lu == NULL) {
2842 		return -1;
2843 	}
2844 	if (lun >= lu->maxlun) {
2845 		return -1;
2846 	}
2847 	if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) {
2848 		return -1;
2849 	}
2850 	if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
2851 		return -1;
2852 	}
2853 	spec = (ISTGT_LU_DVD *) lu->lun[lun].spec;
2854 
2855 	if (spec->lock) {
2856 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "unlock by reset\n");
2857 		spec->lock = 0;
2858 	}
2859 
2860 	/* re-open file */
2861 	if (!spec->lu->readonly
2862 	    && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) {
2863 		rc = istgt_lu_dvd_sync(spec, 0, spec->size);
2864 		if (rc < 0) {
2865 			ISTGT_ERRLOG("LU%d: LUN%d: lu_dvd_sync() failed\n",
2866 			    lu->num, lun);
2867 			/* ignore error */
2868 		}
2869 	}
2870 	rc = istgt_lu_dvd_close(spec);
2871 	if (rc < 0) {
2872 		ISTGT_ERRLOG("LU%d: LUN%d: lu_dvd_close() failed\n",
2873 		    lu->num, lun);
2874 		/* ignore error */
2875 	}
2876 	flags = (lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY))
2877 		? O_RDONLY : O_RDWR;
2878 	rc = istgt_lu_dvd_open(spec, flags, 0666);
2879 	if (rc < 0) {
2880 		ISTGT_ERRLOG("LU%d: LUN%d: lu_dvd_open() failed\n",
2881 		    lu->num, lun);
2882 		return -1;
2883 	}
2884 
2885 	return 0;
2886 }
2887 
2888 int
istgt_lu_dvd_execute(CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd)2889 istgt_lu_dvd_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd)
2890 {
2891 	ISTGT_LU_Ptr lu;
2892 	ISTGT_LU_DVD *spec;
2893 	uint8_t *data;
2894 	uint8_t *cdb;
2895 	uint64_t fmt_lun;
2896 	uint64_t lun;
2897 	uint64_t method;
2898 	uint32_t allocation_len;
2899 	int data_len;
2900 	int data_alloc_len;
2901 	uint64_t lba;
2902 	uint32_t transfer_len;
2903 	uint8_t *sense_data;
2904 	size_t *sense_len;
2905 	int rc;
2906 
2907 	if (lu_cmd == NULL)
2908 		return -1;
2909 	lu = lu_cmd->lu;
2910 	if (lu == NULL) {
2911 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
2912 		return -1;
2913 	}
2914 	spec = NULL;
2915 	cdb = lu_cmd->cdb;
2916 	data = lu_cmd->data;
2917 	data_alloc_len = lu_cmd->alloc_len;
2918 	sense_data = lu_cmd->sense_data;
2919 	sense_len = &lu_cmd->sense_data_len;
2920 	*sense_len = 0;
2921 
2922 	fmt_lun = lu_cmd->lun;
2923 	method = (fmt_lun >> 62) & 0x03U;
2924 	fmt_lun = fmt_lun >> 48;
2925 	if (method == 0x00U) {
2926 		lun = fmt_lun & 0x00ffU;
2927 	} else if (method == 0x01U) {
2928 		lun = fmt_lun & 0x3fffU;
2929 	} else {
2930 		lun = 0xffffU;
2931 	}
2932 	if (lun >= (uint64_t) lu->maxlun) {
2933 #ifdef ISTGT_TRACE_DVD
2934 		ISTGT_ERRLOG("LU%d: LUN%4.4"PRIx64" invalid\n",
2935 					 lu->num, lun);
2936 #endif /* ISTGT_TRACE_DVD */
2937 		if (cdb[0] == SPC_INQUIRY) {
2938 			allocation_len = DGET16(&cdb[3]);
2939 			if (allocation_len > (size_t) data_alloc_len) {
2940 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
2941 				    data_alloc_len);
2942 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
2943 				return -1;
2944 			}
2945 			memset(data, 0, allocation_len);
2946 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
2947 			BDSET8W(&data[0], 0x03, 7, 3);
2948 			BDADD8W(&data[0], 0x1f, 4, 5);
2949 			data_len = 96;
2950 			memset(&data[1], 0, data_len - 1);
2951 			/* ADDITIONAL LENGTH */
2952 			data[4] = data_len - 5;
2953 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
2954 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
2955 			return 0;
2956 		} else {
2957 			/* LOGICAL UNIT NOT SUPPORTED */
2958 			BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00);
2959 			lu_cmd->data_len = 0;
2960 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
2961 			return 0;
2962 		}
2963 	}
2964 	spec = (ISTGT_LU_DVD *) lu->lun[lun].spec;
2965 	if (spec == NULL) {
2966 		/* LOGICAL UNIT NOT SUPPORTED */
2967 		BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00);
2968 		lu_cmd->data_len = 0;
2969 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
2970 		return 0;
2971 	}
2972 
2973 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64"\n",
2974 	    cdb[0], lu_cmd->lun);
2975 #ifdef ISTGT_TRACE_DVD
2976 	if (cdb[0] != SPC_TEST_UNIT_READY
2977 		&& cdb[0] != MMC_GET_EVENT_STATUS_NOTIFICATION) {
2978 		istgt_scsi_dump_cdb(cdb);
2979 	} else {
2980 		istgt_scsi_dump_cdb(cdb);
2981 	}
2982 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "mload=%d, mchanged=%d, mwait=%d\n", spec->mload, spec->mchanged, spec->mwait);
2983 #endif /* ISTGT_TRACE_DVD */
2984 	switch (cdb[0]) {
2985 	case SPC_INQUIRY:
2986 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "INQUIRY\n");
2987 		if (lu_cmd->R_bit == 0) {
2988 			ISTGT_ERRLOG("R_bit == 0\n");
2989 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
2990 			return -1;
2991 		}
2992 		allocation_len = DGET16(&cdb[3]);
2993 		if (allocation_len > (size_t) data_alloc_len) {
2994 			ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
2995 			    data_alloc_len);
2996 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
2997 			return -1;
2998 		}
2999 		memset(data, 0, allocation_len);
3000 		data_len = istgt_lu_dvd_scsi_inquiry(spec, conn, cdb,
3001 		    data, data_alloc_len);
3002 		if (data_len < 0) {
3003 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3004 			break;
3005 		}
3006 		ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "INQUIRY", data, data_len);
3007 		lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3008 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3009 		break;
3010 
3011 	case SPC_REPORT_LUNS:
3012 		{
3013 			int sel;
3014 
3015 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT LUNS\n");
3016 			if (lu_cmd->R_bit == 0) {
3017 				ISTGT_ERRLOG("R_bit == 0\n");
3018 				return -1;
3019 			}
3020 
3021 			sel = cdb[2];
3022 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sel=%x\n", sel);
3023 
3024 			allocation_len = DGET32(&cdb[6]);
3025 			if (allocation_len > (size_t) data_alloc_len) {
3026 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3027 				    data_alloc_len);
3028 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3029 				return -1;
3030 			}
3031 			if (allocation_len < 16) {
3032 				/* INVALID FIELD IN CDB */
3033 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3034 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3035 				break;
3036 			}
3037 			memset(data, 0, allocation_len);
3038 			data_len = istgt_lu_dvd_scsi_report_luns(lu, conn, cdb, sel,
3039 													 data, data_alloc_len);
3040 			if (data_len < 0) {
3041 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3042 				break;
3043 			}
3044 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "REPORT LUNS", data, data_len);
3045 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3046 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3047 		}
3048 		break;
3049 
3050 	case SPC_TEST_UNIT_READY:
3051 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "TEST_UNIT_READY\n");
3052 		{
3053 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3054 
3055 			/* media state change? */
3056 			if (spec->mchanged) {
3057 				/* wait OS polling */
3058 				if (spec->mwait > 0) {
3059 					spec->mwait--;
3060 				} else {
3061 					/* load new media */
3062 					spec->mchanged = 0;
3063 					spec->mload = 1;
3064 				}
3065 			}
3066 
3067 			if (data_len != 0) {
3068 				*sense_len = data_len;
3069 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3070 				break;
3071 			}
3072 
3073 			/* OK media present */
3074 			lu_cmd->data_len = 0;
3075 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3076 			break;
3077 		}
3078 
3079 	case MMC_START_STOP_UNIT:
3080 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "START_STOP_UNIT\n");
3081 		{
3082 			int pc, fl, loej, start;
3083 
3084 			pc = BGET8W(&cdb[4], 7, 4);
3085 			fl = BGET8(&cdb[4], 2);
3086 			loej = BGET8(&cdb[4], 1);
3087 			start = BGET8(&cdb[4], 0);
3088 
3089 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3090 			if (data_len != 0) {
3091 				*sense_len = data_len;
3092 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3093 				break;
3094 			}
3095 
3096 			if (!loej) {
3097 				if (start) {
3098 					/* start */
3099 				} else {
3100 					/* stop */
3101 				}
3102 				lu_cmd->data_len = 0;
3103 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3104 				break;
3105 			}
3106 
3107 			/* loej=1 */
3108 			if (start) {
3109 				/* load disc */
3110 				if (!spec->mload) {
3111 					if (istgt_lu_dvd_load_media(spec) < 0) {
3112 						ISTGT_ERRLOG("lu_dvd_load_media() failed\n");
3113 						/* INTERNAL TARGET FAILURE */
3114 						BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3115 						lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3116 						break;
3117 					}
3118 					/* OK load */
3119 				}
3120 			} else {
3121 				/* eject */
3122 				if (!spec->lock) {
3123 					if (spec->mload) {
3124 						if (istgt_lu_dvd_unload_media(spec) < 0) {
3125 							ISTGT_ERRLOG("lu_dvd_unload_media() failed\n");
3126 							/* INTERNAL TARGET FAILURE */
3127 							BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3128 							lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3129 							break;
3130 						}
3131 						/* OK unload */
3132 					}
3133 				} else {
3134 					/* MEDIUM REMOVAL PREVENTED */
3135 					BUILD_SENSE(ILLEGAL_REQUEST, 0x53, 0x02);
3136 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3137 					break;
3138 				}
3139 			}
3140 
3141 			lu_cmd->data_len = 0;
3142 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3143 			break;
3144 		}
3145 
3146 	case SPC_PREVENT_ALLOW_MEDIUM_REMOVAL:
3147 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREVENT_ALLOW_MEDIUM_REMOVAL\n");
3148 		{
3149 			int persistent, prevent;
3150 
3151 			persistent = BGET8(&cdb[4], 1);
3152 			prevent = BGET8(&cdb[4], 0);
3153 
3154 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3155 			if (data_len != 0) {
3156 				*sense_len = data_len;
3157 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3158 				break;
3159 			}
3160 
3161 			if (persistent) {
3162 				if (prevent) {
3163 					/* Persistent Prevent */
3164 				} else {
3165 					/* Persistent Allow */
3166 				}
3167 			} else {
3168 				if (prevent) {
3169 					/* Locked */
3170 					spec->lock = 1;
3171 				} else {
3172 					/* Unlocked */
3173 					spec->lock = 0;
3174 				}
3175 			}
3176 
3177 			lu_cmd->data_len = 0;
3178 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3179 			break;
3180 		}
3181 
3182 	case MMC_READ_CAPACITY:
3183 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_CAPACITY\n");
3184 		if (lu_cmd->R_bit == 0) {
3185 			ISTGT_ERRLOG("R_bit == 0\n");
3186 			return -1;
3187 		}
3188 
3189 		data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3190 		if (data_len != 0) {
3191 			*sense_len = data_len;
3192 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3193 			break;
3194 		}
3195 
3196 		if (spec->blockcnt - 1 > 0xffffffffULL) {
3197 			DSET32(&data[0], 0xffffffffUL);
3198 		} else {
3199 			DSET32(&data[0], (uint32_t) (spec->blockcnt - 1));
3200 		}
3201 		DSET32(&data[4], (uint32_t) spec->blocklen);
3202 		data_len = 8;
3203 		lu_cmd->data_len = data_len;
3204 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3205 		break;
3206 
3207 	case SPC_MODE_SELECT_6:
3208 		{
3209 			int pf, sp, pllen;
3210 			int mdlen, mt, dsp, bdlen;
3211 
3212 			pf = BGET8(&cdb[1], 4);
3213 			sp = BGET8(&cdb[1], 0);
3214 			pllen = cdb[4];             /* Parameter List Length */
3215 
3216 			/* Data-Out */
3217 			rc = istgt_lu_dvd_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
3218 			    lu_cmd->iobufsize, pllen);
3219 			if (rc < 0) {
3220 				ISTGT_ERRLOG("lu_dvd_transfer_data() failed\n");
3221 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3222 				break;
3223 			}
3224 #if 0
3225 			istgt_dump("MODE SELECT(6)", lu_cmd->iobuf, pllen);
3226 #endif
3227 			data = lu_cmd->iobuf;
3228 			mdlen = data[0];            /* Mode Data Length */
3229 			mt = data[1];               /* Medium Type */
3230 			dsp = data[2];              /* Device-Specific Parameter */
3231 			bdlen = data[3];            /* Block Descriptor Length */
3232 
3233 			/* Short LBA mode parameter block descriptor */
3234 			/* data[4]-data[7] Number of Blocks */
3235 			/* data[8]-data[11] Block Length */
3236 
3237 			/* page data */
3238 			data_len = istgt_lu_dvd_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[4 + bdlen], pllen - (4 + bdlen));
3239 			if (data_len != 0) {
3240 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3241 				break;
3242 			}
3243 			lu_cmd->data_len = pllen;
3244 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3245 			break;
3246 		}
3247 
3248 	case SPC_MODE_SELECT_10:
3249 		{
3250 			int pf, sp, pllen;
3251 			int mdlen, mt, dsp, bdlen;
3252 			int llba;
3253 
3254 			pf = BGET8(&cdb[1], 4);
3255 			sp = BGET8(&cdb[1], 0);
3256 			pllen = DGET16(&cdb[7]);    /* Parameter List Length */
3257 
3258 			/* Data-Out */
3259 			rc = istgt_lu_dvd_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
3260 			    lu_cmd->iobufsize, pllen);
3261 			if (rc < 0) {
3262 				ISTGT_ERRLOG("lu_dvd_transfer_data() failed\n");
3263 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3264 				break;
3265 			}
3266 #if 0
3267 			istgt_dump("MODE SELECT(10)", lu_cmd->iobuf, pllen);
3268 #endif
3269 			data = lu_cmd->iobuf;
3270 			mdlen = DGET16(&data[0]);   /* Mode Data Length */
3271 			mt = data[2];               /* Medium Type */
3272 			dsp = data[3];              /* Device-Specific Parameter */
3273 			llba = BGET8(&data[4], 0);  /* Long LBA */
3274 			bdlen = DGET16(&data[6]);   /* Block Descriptor Length */
3275 
3276 			if (llba) {
3277 				/* Long LBA mode parameter block descriptor */
3278 				/* data[8]-data[15] Number of Blocks */
3279 				/* data[16]-data[19] Reserved */
3280 				/* data[20]-data[23] Block Length */
3281 			} else {
3282 				/* Short LBA mode parameter block descriptor */
3283 				/* data[8]-data[11] Number of Blocks */
3284 				/* data[12]-data[15] Block Length */
3285 			}
3286 
3287 			/* page data */
3288 			data_len = istgt_lu_dvd_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[8 + bdlen], pllen - (8 + bdlen));
3289 			if (data_len != 0) {
3290 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3291 				break;
3292 			}
3293 			lu_cmd->data_len = pllen;
3294 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3295 			break;
3296 		}
3297 
3298 	case SPC_MODE_SENSE_6:
3299 		{
3300 			int dbd, pc, page, subpage;
3301 
3302 			if (lu_cmd->R_bit == 0) {
3303 				ISTGT_ERRLOG("R_bit == 0\n");
3304 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3305 				return -1;
3306 			}
3307 
3308 			dbd = BGET8(&cdb[1], 3);
3309 			pc = BGET8W(&cdb[2], 7, 2);
3310 			page = BGET8W(&cdb[2], 5, 6);
3311 			subpage = cdb[3];
3312 
3313 			allocation_len = cdb[4];
3314 			if (allocation_len > (size_t) data_alloc_len) {
3315 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3316 				    data_alloc_len);
3317 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3318 				return -1;
3319 			}
3320 			memset(data, 0, allocation_len);
3321 
3322 			data_len = istgt_lu_dvd_scsi_mode_sense6(spec, conn, cdb, dbd, pc, page, subpage, data, data_alloc_len);
3323 			if (data_len < 0) {
3324 				/* INVALID FIELD IN CDB */
3325 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3326 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3327 				break;
3328 			}
3329 #if 0
3330 			istgt_dump("MODE SENSE(6)", data, data_len);
3331 #endif
3332 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3333 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3334 			break;
3335 		}
3336 
3337 	case SPC_MODE_SENSE_10:
3338 		{
3339 			int dbd, pc, page, subpage;
3340 			int llbaa;
3341 
3342 			if (lu_cmd->R_bit == 0) {
3343 				ISTGT_ERRLOG("R_bit == 0\n");
3344 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3345 				return -1;
3346 			}
3347 
3348 			llbaa = BGET8(&cdb[1], 4);
3349 			dbd = BGET8(&cdb[1], 3);
3350 			pc = BGET8W(&cdb[2], 7, 2);
3351 			page = BGET8W(&cdb[2], 5, 6);
3352 			subpage = cdb[3];
3353 
3354 			allocation_len = DGET16(&cdb[7]);
3355 			if (allocation_len > (size_t) data_alloc_len) {
3356 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3357 				    data_alloc_len);
3358 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3359 				return -1;
3360 			}
3361 			memset(data, 0, allocation_len);
3362 
3363 			data_len = istgt_lu_dvd_scsi_mode_sense10(spec, conn, cdb, llbaa, dbd, pc, page, subpage, data, data_alloc_len);
3364 			if (data_len < 0) {
3365 				/* INVALID FIELD IN CDB */
3366 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3367 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3368 				break;
3369 			}
3370 #if 0
3371 			istgt_dump("MODE SENSE(10)", data, data_len);
3372 #endif
3373 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3374 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3375 			break;
3376 		}
3377 
3378 	case SPC_LOG_SELECT:
3379 	case SPC_LOG_SENSE:
3380 		/* INVALID COMMAND OPERATION CODE */
3381 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
3382 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3383 		break;
3384 
3385 	case SPC_REQUEST_SENSE:
3386 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REQUEST_SENSE\n");
3387 		{
3388 			int desc;
3389 			int sk, asc, ascq;
3390 
3391 			if (lu_cmd->R_bit == 0) {
3392 				ISTGT_ERRLOG("R_bit == 0\n");
3393 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3394 				return -1;
3395 			}
3396 
3397 			desc = BGET8(&cdb[1], 0);
3398 			if (desc != 0) {
3399 				/* INVALID FIELD IN CDB */
3400 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3401 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3402 				break;
3403 			}
3404 
3405 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3406 
3407 			/* media state change? */
3408 			if (spec->mchanged) {
3409 				/* wait OS polling */
3410 				if (spec->mwait > 0) {
3411 					spec->mwait--;
3412 				} else {
3413 					/* load new media */
3414 					spec->mchanged = 0;
3415 					spec->mload = 1;
3416 				}
3417 			}
3418 
3419 			if (data_len != 0) {
3420 				*sense_len = data_len;
3421 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3422 				break;
3423 			}
3424 
3425 			allocation_len = cdb[4];
3426 			if (allocation_len > (size_t) data_alloc_len) {
3427 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3428 				    data_alloc_len);
3429 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3430 				return -1;
3431 			}
3432 			memset(data, 0, allocation_len);
3433 
3434 			if (!spec->sense) {
3435 				/* NO ADDITIONAL SENSE INFORMATION */
3436 				sk = ISTGT_SCSI_SENSE_NO_SENSE;
3437 				asc = 0x00;
3438 				ascq = 0x00;
3439 			} else {
3440 				sk = (spec->sense >> 16) & 0xffU;
3441 				asc = (spec->sense >> 8) & 0xffU;
3442 				ascq = spec->sense & 0xffU;
3443 			}
3444 			data_len = istgt_lu_dvd_build_sense_data(spec, sense_data,
3445 													 sk, asc, ascq);
3446 			if (data_len < 0 || data_len < 2) {
3447 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3448 				break;
3449 			}
3450 			/* omit SenseLength */
3451 			data_len -= 2;
3452 			memcpy(data, sense_data + 2, data_len);
3453 
3454 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3455 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3456 			break;
3457 		}
3458 
3459 	case MMC_GET_CONFIGURATION:
3460 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "GET_CONFIGURATION\n");
3461 		{
3462 			int rt, sfn;
3463 
3464 			if (lu_cmd->R_bit == 0) {
3465 				ISTGT_ERRLOG("R_bit == 0\n");
3466 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3467 				return -1;
3468 			}
3469 
3470 			rt = BGET8W(&cdb[1], 1, 2);
3471 			sfn = DGET16(&cdb[2]);
3472 
3473 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3474 			if (data_len != 0) {
3475 				*sense_len = data_len;
3476 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3477 				break;
3478 			}
3479 
3480 			allocation_len = DGET16(&cdb[7]);
3481 			if (allocation_len > (size_t) data_alloc_len) {
3482 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3483 				    data_alloc_len);
3484 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3485 				return -1;
3486 			}
3487 			memset(data, 0, allocation_len);
3488 
3489 			data_len = istgt_lu_dvd_scsi_get_configuration(spec, conn, cdb, rt, sfn, data);
3490 			if (data_len < 0) {
3491 				/* INVALID FIELD IN CDB */
3492 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3493 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3494 				break;
3495 			}
3496 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3497 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3498 			break;
3499 		}
3500 
3501 	case MMC_GET_EVENT_STATUS_NOTIFICATION:
3502 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "GET_EVENT_STATUS_NOTIFICATION\n");
3503 		{
3504 			int polled, ncr;
3505 			int keep = 0;
3506 
3507 			if (lu_cmd->R_bit == 0) {
3508 				ISTGT_ERRLOG("R_bit == 0\n");
3509 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3510 				return -1;
3511 			}
3512 
3513 			polled = BGET8(&cdb[1], 0);
3514 			ncr = cdb[4];
3515 
3516 			allocation_len = DGET16(&cdb[7]);
3517 			if (allocation_len > (size_t) data_alloc_len) {
3518 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3519 				    data_alloc_len);
3520 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3521 				return -1;
3522 			}
3523 			memset(data, 0, allocation_len);
3524 
3525 			if (!polled) {
3526 				/* asynchronous operation */
3527 				/* INVALID FIELD IN CDB */
3528 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3529 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3530 				break;
3531 			}
3532 			if (allocation_len <= 4) {
3533 				/* shall not clear any event */
3534 				keep = 1;
3535 			}
3536 			data_len = istgt_lu_dvd_scsi_get_event_status_notification(spec, conn, cdb, keep, ncr, data);
3537 			if (data_len < 0) {
3538 				/* INVALID FIELD IN CDB */
3539 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3540 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3541 				break;
3542 			}
3543 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "EVENT", data, data_len);
3544 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3545 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3546 			break;
3547 		}
3548 
3549 	case MMC_GET_PERFORMANCE:
3550 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "GET_PERFORMANCE\n");
3551 		{
3552 			int dt, mnd, type;
3553 
3554 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3555 			if (data_len != 0) {
3556 				*sense_len = data_len;
3557 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3558 				break;
3559 			}
3560 
3561 			dt = BGET8W(&cdb[1], 4, 5);
3562 			lba = DGET32(&cdb[2]);
3563 			mnd = DGET16(&cdb[8]);
3564 			type = cdb[10];
3565 
3566 			/* INVALID COMMAND OPERATION CODE */
3567 			BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
3568 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3569 			break;
3570 		}
3571 
3572 	case MMC_MECHANISM_STATUS:
3573 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MECHANISM_STATUS\n");
3574 		{
3575 			if (lu_cmd->R_bit == 0) {
3576 				ISTGT_ERRLOG("R_bit == 0\n");
3577 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3578 				return -1;
3579 			}
3580 
3581 			allocation_len = DGET16(&cdb[8]);
3582 			if (allocation_len > (size_t) data_alloc_len) {
3583 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3584 				    data_alloc_len);
3585 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3586 				return -1;
3587 			}
3588 			memset(data, 0, allocation_len);
3589 
3590 			data_len = istgt_lu_dvd_scsi_mechanism_status(spec, conn, cdb, data);
3591 			if (data_len < 0) {
3592 				/* INVALID FIELD IN CDB */
3593 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3594 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3595 				break;
3596 			}
3597 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3598 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3599 			break;
3600 		}
3601 
3602 	case MMC_READ_TOC_PMA_ATIP:
3603 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_TOC_PMA_ATIP\n");
3604 		{
3605 			int msf, format, track;
3606 
3607 			if (lu_cmd->R_bit == 0) {
3608 				ISTGT_ERRLOG("R_bit == 0\n");
3609 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3610 				return -1;
3611 			}
3612 
3613 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3614 			if (data_len != 0) {
3615 				*sense_len = data_len;
3616 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3617 				break;
3618 			}
3619 
3620 			msf = BGET8(&cdb[1], 1);
3621 			format = BGET8W(&cdb[2], 3, 4);
3622 			track = cdb[6];
3623 
3624 			allocation_len = DGET16(&cdb[7]);
3625 			if (allocation_len > (size_t) data_alloc_len) {
3626 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3627 				    data_alloc_len);
3628 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3629 				return -1;
3630 			}
3631 			memset(data, 0, allocation_len);
3632 
3633 			data_len = istgt_lu_dvd_scsi_read_toc(spec, conn, cdb, msf, format, track, data);
3634 			if (data_len < 0) {
3635 				/* INVALID FIELD IN CDB */
3636 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3637 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3638 				break;
3639 			}
3640 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3641 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3642 			break;
3643 		}
3644 
3645 	case MMC_READ_DISC_INFORMATION:
3646 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_DISC_INFORMATION\n");
3647 		{
3648 			int datatype;
3649 
3650 			if (lu_cmd->R_bit == 0) {
3651 				ISTGT_ERRLOG("R_bit == 0\n");
3652 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3653 				return -1;
3654 			}
3655 
3656 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3657 			if (data_len != 0) {
3658 				*sense_len = data_len;
3659 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3660 				break;
3661 			}
3662 
3663 			datatype = BGET8W(&cdb[1], 2, 3);
3664 
3665 			allocation_len = DGET16(&cdb[7]);
3666 			if (allocation_len > (size_t) data_alloc_len) {
3667 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3668 				    data_alloc_len);
3669 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3670 				return -1;
3671 			}
3672 			memset(data, 0, allocation_len);
3673 
3674 			data_len = istgt_lu_dvd_scsi_read_disc_information(spec, conn, cdb, datatype, data);
3675 			if (data_len < 0) {
3676 				/* INVALID FIELD IN CDB */
3677 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3678 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3679 				break;
3680 			}
3681 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3682 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3683 			break;
3684 		}
3685 
3686 	case MMC_READ_DISC_STRUCTURE:
3687 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_DISC_STRUCTURE\n");
3688 		{
3689 			int mediatype, layernumber, format, agid;
3690 
3691 			if (lu_cmd->R_bit == 0) {
3692 				ISTGT_ERRLOG("R_bit == 0\n");
3693 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3694 				return -1;
3695 			}
3696 
3697 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3698 			if (data_len != 0) {
3699 				*sense_len = data_len;
3700 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3701 				break;
3702 			}
3703 
3704 			mediatype = BGET8W(&cdb[1], 3, 4);
3705 			layernumber = cdb[6];
3706 			format = cdb[7];
3707 			agid = BGET8W(&cdb[10], 7, 2);
3708 
3709 			allocation_len = DGET16(&cdb[8]);
3710 			if (allocation_len > (size_t) data_alloc_len) {
3711 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3712 				    data_alloc_len);
3713 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3714 				return -1;
3715 			}
3716 			memset(data, 0, allocation_len);
3717 
3718 			data_len = istgt_lu_dvd_scsi_read_disc_structure(spec, conn, cdb, mediatype, layernumber, format, agid, data);
3719 			if (data_len < 0) {
3720 				/* INVALID FIELD IN CDB */
3721 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3722 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3723 				break;
3724 			}
3725 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3726 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3727 			break;
3728 		}
3729 
3730 	case MMC_READ_SUB_CHANNEL:
3731 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_SUB_CHANNEL\n");
3732 		{
3733 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3734 			if (data_len != 0) {
3735 				*sense_len = data_len;
3736 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3737 				break;
3738 			}
3739 
3740 			/* INVALID COMMAND OPERATION CODE */
3741 			BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
3742 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3743 			break;
3744 		}
3745 
3746 	case MMC_REPORT_KEY:
3747 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT_KEY\n");
3748 		{
3749 			int keyclass, agid, keyformat;
3750 
3751 			if (lu_cmd->R_bit == 0) {
3752 				ISTGT_ERRLOG("R_bit == 0\n");
3753 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3754 				return -1;
3755 			}
3756 
3757 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3758 			if (data_len != 0) {
3759 				*sense_len = data_len;
3760 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3761 				break;
3762 			}
3763 
3764 			keyclass = cdb[7];
3765 			agid = BGET8W(&cdb[10], 7, 2);
3766 			keyformat = BGET8W(&cdb[10], 5, 6);
3767 
3768 			allocation_len = DGET16(&cdb[8]);
3769 			if (allocation_len > (size_t) data_alloc_len) {
3770 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
3771 				    data_alloc_len);
3772 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3773 				return -1;
3774 			}
3775 			memset(data, 0, allocation_len);
3776 
3777 			data_len = istgt_lu_dvd_scsi_report_key(spec, conn, cdb, keyclass, agid, keyformat, data);
3778 			if (data_len < 0) {
3779 				/* INVALID FIELD IN CDB */
3780 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3781 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3782 				break;
3783 			}
3784 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
3785 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3786 			break;
3787 		}
3788 
3789 	case MMC_SEND_KEY:
3790 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SEND_KEY\n");
3791 		{
3792 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3793 			if (data_len != 0) {
3794 				*sense_len = data_len;
3795 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3796 				break;
3797 			}
3798 
3799 			/* INVALID COMMAND OPERATION CODE */
3800 			BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
3801 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3802 			break;
3803 		}
3804 
3805 	case MMC_READ_10:
3806 		{
3807 			int dpo, fua;
3808 
3809 			if (lu_cmd->R_bit == 0) {
3810 				ISTGT_ERRLOG("R_bit == 0\n");
3811 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3812 				return -1;
3813 			}
3814 
3815 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3816 			if (data_len != 0) {
3817 				*sense_len = data_len;
3818 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3819 				break;
3820 			}
3821 
3822 			dpo = BGET8(&cdb[1], 4);
3823 			fua = BGET8(&cdb[1], 3);
3824 			lba = (uint64_t) DGET32(&cdb[2]);
3825 			transfer_len = (uint32_t) DGET16(&cdb[7]);
3826 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
3827 			    "READ_10(lba %"PRIu64", len %u blocks)\n",
3828 			    lba, transfer_len);
3829 			rc = istgt_lu_dvd_lbread(spec, conn, lu_cmd, lba, transfer_len);
3830 			if (rc < 0) {
3831 				ISTGT_ERRLOG("lu_dvd_lbread() failed\n");
3832 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3833 				break;
3834 			}
3835 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3836 			break;
3837 		}
3838 
3839 	case MMC_READ_12:
3840 		{
3841 			int dpo, fua;
3842 
3843 			if (lu_cmd->R_bit == 0) {
3844 				ISTGT_ERRLOG("R_bit == 0\n");
3845 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3846 				return -1;
3847 			}
3848 
3849 			data_len = istgt_lu_dvd_build_sense_media(spec, sense_data);
3850 			if (data_len != 0) {
3851 				*sense_len = data_len;
3852 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3853 				break;
3854 			}
3855 
3856 			dpo = BGET8(&cdb[1], 4);
3857 			fua = BGET8(&cdb[1], 3);
3858 			lba = (uint64_t) DGET32(&cdb[2]);
3859 			transfer_len = (uint32_t) DGET32(&cdb[6]);
3860 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
3861 			    "READ_12(lba %"PRIu64", len %u blocks)\n",
3862 			    lba, transfer_len);
3863 			rc = istgt_lu_dvd_lbread(spec, conn, lu_cmd, lba, transfer_len);
3864 			if (rc < 0) {
3865 				ISTGT_ERRLOG("lu_dvd_lbread() failed\n");
3866 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3867 				break;
3868 			}
3869 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3870 			break;
3871 		}
3872 
3873 #if 0
3874 	case MMC_WRITE_10:
3875 	case MMC_WRITE_AND_VERIFY_10:
3876 	case MMC_WRITE_12:
3877 	case MMC_VERIFY_10:
3878 	case MMC_SYNCHRONIZE_CACHE:
3879 		/* INVALID COMMAND OPERATION CODE */
3880 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
3881 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3882 		break;
3883 #endif
3884 
3885 	/* XXX TODO: fix */
3886 	case SPC2_RELEASE_6:
3887 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_6\n");
3888 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3889 		break;
3890 	case SPC2_RELEASE_10:
3891 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_10\n");
3892 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3893 		break;
3894 	case SPC2_RESERVE_6:
3895 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_6\n");
3896 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3897 		break;
3898 	case SPC2_RESERVE_10:
3899 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_10\n");
3900 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3901 		break;
3902 
3903 	default:
3904 		ISTGT_ERRLOG("unsupported SCSI OP=0x%x\n", cdb[0]);
3905 		/* INVALID COMMAND OPERATION CODE */
3906 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
3907 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3908 		break;
3909 	}
3910 
3911 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
3912 	    "SCSI OP=0x%x, LUN=0x%16.16"PRIx64" status=0x%x,"
3913 	    " complete\n",
3914 	    cdb[0], lu_cmd->lun, lu_cmd->status);
3915 	return 0;
3916 }
3917