scsi_cd.c revision 125975
139213Sgibbs/*
239213Sgibbs * Copyright (c) 1997 Justin T. Gibbs.
3111206Sken * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Kenneth D. Merry.
439213Sgibbs * All rights reserved.
539213Sgibbs *
639213Sgibbs * Redistribution and use in source and binary forms, with or without
739213Sgibbs * modification, are permitted provided that the following conditions
839213Sgibbs * are met:
939213Sgibbs * 1. Redistributions of source code must retain the above copyright
1039213Sgibbs *    notice, this list of conditions, and the following disclaimer,
1139213Sgibbs *    without modification, immediately at the beginning of the file.
1239213Sgibbs * 2. The name of the author may not be used to endorse or promote products
1339213Sgibbs *    derived from this software without specific prior written permission.
1439213Sgibbs *
1539213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1639213Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1739213Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1839213Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1939213Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2039213Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2139213Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2239213Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2339213Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2439213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2539213Sgibbs * SUCH DAMAGE.
2639213Sgibbs */
27116162Sobrien
2839213Sgibbs/*
2939213Sgibbs * Portions of this driver taken from the original FreeBSD cd driver.
3039213Sgibbs * Written by Julian Elischer (julian@tfs.com)
3139213Sgibbs * for TRW Financial Systems for use under the MACH(2.5) operating system.
3239213Sgibbs *
3339213Sgibbs * TRW Financial Systems, in accordance with their agreement with Carnegie
3439213Sgibbs * Mellon University, makes this software available to CMU to distribute
3539213Sgibbs * or use in any manner that they see fit as long as this message is kept with
3639213Sgibbs * the software. For this reason TFS also grants any other persons or
3739213Sgibbs * organisations permission to use or modify this software.
3839213Sgibbs *
3939213Sgibbs * TFS supplies this software to be publicly redistributed
4039213Sgibbs * on the understanding that TFS is not responsible for the correct
4139213Sgibbs * functioning of this software in any circumstances.
4239213Sgibbs *
4339213Sgibbs * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
4439213Sgibbs *
4539213Sgibbs *      from: cd.c,v 1.83 1997/05/04 15:24:22 joerg Exp $
4639213Sgibbs */
4739213Sgibbs
48116162Sobrien#include <sys/cdefs.h>
49116162Sobrien__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_cd.c 125975 2004-02-18 21:36:53Z phk $");
50116162Sobrien
5140020Sken#include "opt_cd.h"
5239213Sgibbs
5339213Sgibbs#include <sys/param.h>
5439213Sgibbs#include <sys/systm.h>
5539213Sgibbs#include <sys/kernel.h>
5660041Sphk#include <sys/bio.h>
5751836Sphk#include <sys/conf.h>
5851836Sphk#include <sys/disk.h>
5939213Sgibbs#include <sys/malloc.h>
6039213Sgibbs#include <sys/cdio.h>
61105421Snjl#include <sys/cdrio.h>
6260422Sken#include <sys/dvdio.h>
6339213Sgibbs#include <sys/devicestat.h>
6439213Sgibbs#include <sys/sysctl.h>
65119708Sken#include <sys/taskqueue.h>
66120599Sphk#include <geom/geom_disk.h>
6739213Sgibbs
6839213Sgibbs#include <cam/cam.h>
6939213Sgibbs#include <cam/cam_ccb.h>
7039213Sgibbs#include <cam/cam_periph.h>
7139213Sgibbs#include <cam/cam_xpt_periph.h>
7239213Sgibbs#include <cam/cam_queue.h>
7339213Sgibbs
7439213Sgibbs#include <cam/scsi/scsi_message.h>
7539213Sgibbs#include <cam/scsi/scsi_da.h>
7639213Sgibbs#include <cam/scsi/scsi_cd.h>
7739213Sgibbs
7839213Sgibbs#define LEADOUT         0xaa            /* leadout toc entry */
7939213Sgibbs
8039213Sgibbsstruct cd_params {
8139213Sgibbs	u_int32_t blksize;
8239213Sgibbs	u_long    disksize;
8339213Sgibbs};
8439213Sgibbs
8539213Sgibbstypedef enum {
86111206Sken	CD_Q_NONE		= 0x00,
87111206Sken	CD_Q_NO_TOUCH		= 0x01,
88111206Sken	CD_Q_BCD_TRACKS		= 0x02,
89111206Sken	CD_Q_NO_CHANGER		= 0x04,
90111206Sken	CD_Q_CHANGER		= 0x08,
91111206Sken	CD_Q_10_BYTE_ONLY	= 0x10
9239213Sgibbs} cd_quirks;
9339213Sgibbs
9439213Sgibbstypedef enum {
95120884Sthomas	CD_FLAG_INVALID		= 0x0001,
96120884Sthomas	CD_FLAG_NEW_DISC	= 0x0002,
97120884Sthomas	CD_FLAG_DISC_LOCKED	= 0x0004,
98120884Sthomas	CD_FLAG_DISC_REMOVABLE	= 0x0008,
99120884Sthomas	CD_FLAG_TAGGED_QUEUING	= 0x0010,
100120884Sthomas	CD_FLAG_CHANGER		= 0x0040,
101120884Sthomas	CD_FLAG_ACTIVE		= 0x0080,
102120884Sthomas	CD_FLAG_SCHED_ON_COMP	= 0x0100,
103120884Sthomas	CD_FLAG_RETRY_UA	= 0x0200,
104120884Sthomas	CD_FLAG_VALID_MEDIA	= 0x0400,
105120884Sthomas	CD_FLAG_VALID_TOC	= 0x0800,
106120884Sthomas	CD_FLAG_SCTX_INIT	= 0x1000
10739213Sgibbs} cd_flags;
10839213Sgibbs
10939213Sgibbstypedef enum {
11039213Sgibbs	CD_CCB_PROBE		= 0x01,
11139213Sgibbs	CD_CCB_BUFFER_IO	= 0x02,
11239213Sgibbs	CD_CCB_WAITING		= 0x03,
11339213Sgibbs	CD_CCB_TYPE_MASK	= 0x0F,
11439213Sgibbs	CD_CCB_RETRY_UA		= 0x10
11539213Sgibbs} cd_ccb_state;
11639213Sgibbs
11739213Sgibbstypedef enum {
11839213Sgibbs	CHANGER_TIMEOUT_SCHED		= 0x01,
11939213Sgibbs	CHANGER_SHORT_TMOUT_SCHED	= 0x02,
12039213Sgibbs	CHANGER_MANUAL_CALL		= 0x04,
12139213Sgibbs	CHANGER_NEED_TIMEOUT		= 0x08
12239213Sgibbs} cd_changer_flags;
12339213Sgibbs
12439213Sgibbs#define ccb_state ppriv_field0
12539213Sgibbs#define ccb_bp ppriv_ptr1
12639213Sgibbs
127111206Skenstruct cd_tocdata {
128111206Sken	struct ioc_toc_header header;
129111206Sken	struct cd_toc_entry entries[100];
130111206Sken};
131111206Sken
132111206Skenstruct cd_toc_single {
133111206Sken	struct ioc_toc_header header;
134111206Sken	struct cd_toc_entry entry;
135111206Sken};
136111206Sken
13739213Sgibbstypedef enum {
13839213Sgibbs	CD_STATE_PROBE,
13939213Sgibbs	CD_STATE_NORMAL
14039213Sgibbs} cd_state;
14139213Sgibbs
14239213Sgibbsstruct cd_softc {
14339213Sgibbs	cam_pinfo		pinfo;
14439213Sgibbs	cd_state		state;
14546581Sken	volatile cd_flags	flags;
14659249Sphk	struct bio_queue_head	bio_queue;
14760938Sjake	LIST_HEAD(, ccb_hdr)	pending_ccbs;
14839213Sgibbs	struct cd_params	params;
14939213Sgibbs	union ccb		saved_ccb;
15039213Sgibbs	cd_quirks		quirks;
15160938Sjake	STAILQ_ENTRY(cd_softc)	changer_links;
15239213Sgibbs	struct cdchanger	*changer;
15339213Sgibbs	int			bufs_left;
15439213Sgibbs	struct cam_periph	*periph;
155111206Sken	int			minimum_command_size;
156112262Sphk	int			outstanding_cmds;
157119708Sken	struct task		sysctl_task;
158111206Sken	struct sysctl_ctx_list	sysctl_ctx;
159111206Sken	struct sysctl_oid	*sysctl_tree;
160111206Sken	STAILQ_HEAD(, cd_mode_params)	mode_queue;
161111206Sken	struct cd_tocdata	toc;
162125975Sphk	struct disk		*disk;
16339213Sgibbs};
16439213Sgibbs
165111206Skenstruct cd_page_sizes {
166111206Sken	int page;
167111206Sken	int page_size;
168111206Sken};
169111206Sken
170111206Skenstatic struct cd_page_sizes cd_page_size_table[] =
171111206Sken{
172111206Sken	{ AUDIO_PAGE, sizeof(struct cd_audio_page)}
173111206Sken};
174111206Sken
17539213Sgibbsstruct cd_quirk_entry {
17639213Sgibbs	struct scsi_inquiry_pattern inq_pat;
17739213Sgibbs	cd_quirks quirks;
17839213Sgibbs};
17939213Sgibbs
18039213Sgibbs/*
181111206Sken * The changer quirk entries aren't strictly necessary.  Basically, what
182111206Sken * they do is tell cdregister() up front that a device is a changer.
183111206Sken * Otherwise, it will figure that fact out once it sees a LUN on the device
184111206Sken * that is greater than 0.  If it is known up front that a device is a changer,
185111206Sken * all I/O to the device will go through the changer scheduling routines, as
18639213Sgibbs * opposed to the "normal" CD code.
187111206Sken *
188111206Sken * NOTE ON 10_BYTE_ONLY quirks:  Any 10_BYTE_ONLY quirks MUST be because
189111206Sken * your device hangs when it gets a 10 byte command.  Adding a quirk just
190111206Sken * to get rid of the informative diagnostic message is not acceptable.  All
191111206Sken * 10_BYTE_ONLY quirks must be documented in full in a PR (which should be
192111206Sken * referenced in a comment along with the quirk) , and must be approved by
193111206Sken * ken@FreeBSD.org.  Any quirks added that don't adhere to this policy may
194111206Sken * be removed until the submitter can explain why they are needed.
195111206Sken * 10_BYTE_ONLY quirks will be removed (as they will no longer be necessary)
196111206Sken * when the CAM_NEW_TRAN_CODE work is done.
19739213Sgibbs */
19839213Sgibbsstatic struct cd_quirk_entry cd_quirk_table[] =
19939213Sgibbs{
20039213Sgibbs	{
20139213Sgibbs		{ T_CDROM, SIP_MEDIA_REMOVABLE, "NRC", "MBR-7", "*"},
20239213Sgibbs		 /*quirks*/ CD_Q_CHANGER
20339213Sgibbs	},
20439213Sgibbs	{
20554451Sken		{ T_CDROM, SIP_MEDIA_REMOVABLE, "PIONEER", "CD-ROM DRM*",
20639213Sgibbs		  "*"}, /* quirks */ CD_Q_CHANGER
20740262Sken	},
20840262Sken	{
20967752Sken		{ T_CDROM, SIP_MEDIA_REMOVABLE, "NAKAMICH", "MJ-*", "*"},
21067752Sken		 /* quirks */ CD_Q_CHANGER
21167752Sken	},
21267752Sken	{
21340262Sken		{ T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"},
21440262Sken		/* quirks */ CD_Q_BCD_TRACKS
21539213Sgibbs	}
21639213Sgibbs};
21739213Sgibbs
218120599Sphkstatic	disk_open_t	cdopen;
219120599Sphkstatic	disk_close_t	cdclose;
220120599Sphkstatic	disk_ioctl_t	cdioctl;
221120599Sphkstatic	disk_strategy_t	cdstrategy;
22239213Sgibbs
22339213Sgibbsstatic	periph_init_t	cdinit;
22439213Sgibbsstatic	periph_ctor_t	cdregister;
22539213Sgibbsstatic	periph_dtor_t	cdcleanup;
22639213Sgibbsstatic	periph_start_t	cdstart;
22740603Skenstatic	periph_oninv_t	cdoninvalidate;
22839213Sgibbsstatic	void		cdasync(void *callback_arg, u_int32_t code,
22939213Sgibbs				struct cam_path *path, void *arg);
230111206Skenstatic	int		cdcmdsizesysctl(SYSCTL_HANDLER_ARGS);
23139213Sgibbsstatic	void		cdshorttimeout(void *arg);
23239213Sgibbsstatic	void		cdschedule(struct cam_periph *periph, int priority);
23339213Sgibbsstatic	void		cdrunchangerqueue(void *arg);
23439213Sgibbsstatic	void		cdchangerschedule(struct cd_softc *softc);
23539213Sgibbsstatic	int		cdrunccb(union ccb *ccb,
23639213Sgibbs				 int (*error_routine)(union ccb *ccb,
23739213Sgibbs						      u_int32_t cam_flags,
23839213Sgibbs						      u_int32_t sense_flags),
23939213Sgibbs				 u_int32_t cam_flags, u_int32_t sense_flags);
240111206Skenstatic	union ccb 	*cdgetccb(struct cam_periph *periph,
24139213Sgibbs				  u_int32_t priority);
24239213Sgibbsstatic	void		cddone(struct cam_periph *periph,
24339213Sgibbs			       union ccb *start_ccb);
244111206Skenstatic	union cd_pages	*cdgetpage(struct cd_mode_params *mode_params);
245111206Skenstatic	int		cdgetpagesize(int page_num);
246111206Skenstatic	void		cdprevent(struct cam_periph *periph, int action);
247111206Skenstatic	int		cdcheckmedia(struct cam_periph *periph);
248111206Skenstatic	int		cdsize(struct cam_periph *periph, u_int32_t *size);
249111206Skenstatic	int		cd6byteworkaround(union ccb *ccb);
25039213Sgibbsstatic	int		cderror(union ccb *ccb, u_int32_t cam_flags,
25139213Sgibbs				u_int32_t sense_flags);
25239213Sgibbsstatic	int		cdreadtoc(struct cam_periph *periph, u_int32_t mode,
253111206Sken				  u_int32_t start, u_int8_t *data,
254111206Sken				  u_int32_t len, u_int32_t sense_flags);
25539213Sgibbsstatic	int		cdgetmode(struct cam_periph *periph,
256111206Sken				  struct cd_mode_params *data, u_int32_t page);
25739213Sgibbsstatic	int		cdsetmode(struct cam_periph *periph,
258111206Sken				  struct cd_mode_params *data);
25939213Sgibbsstatic	int		cdplay(struct cam_periph *periph, u_int32_t blk,
26039213Sgibbs			       u_int32_t len);
26139213Sgibbsstatic	int		cdreadsubchannel(struct cam_periph *periph,
26239213Sgibbs					 u_int32_t mode, u_int32_t format,
26339213Sgibbs					 int track,
26439213Sgibbs					 struct cd_sub_channel_info *data,
26539213Sgibbs					 u_int32_t len);
26639213Sgibbsstatic	int		cdplaymsf(struct cam_periph *periph, u_int32_t startm,
26739213Sgibbs				  u_int32_t starts, u_int32_t startf,
26839213Sgibbs				  u_int32_t endm, u_int32_t ends,
26939213Sgibbs				  u_int32_t endf);
27039213Sgibbsstatic	int		cdplaytracks(struct cam_periph *periph,
27139213Sgibbs				     u_int32_t strack, u_int32_t sindex,
27239213Sgibbs				     u_int32_t etrack, u_int32_t eindex);
27339213Sgibbsstatic	int		cdpause(struct cam_periph *periph, u_int32_t go);
27439213Sgibbsstatic	int		cdstopunit(struct cam_periph *periph, u_int32_t eject);
275111206Skenstatic	int		cdstartunit(struct cam_periph *periph, int load);
276105421Snjlstatic	int		cdsetspeed(struct cam_periph *periph,
277105421Snjl				   u_int32_t rdspeed, u_int32_t wrspeed);
27860422Skenstatic	int		cdreportkey(struct cam_periph *periph,
27960422Sken				    struct dvd_authinfo *authinfo);
28060422Skenstatic	int		cdsendkey(struct cam_periph *periph,
28160422Sken				  struct dvd_authinfo *authinfo);
28260422Skenstatic	int		cdreaddvdstructure(struct cam_periph *periph,
28360422Sken					   struct dvd_struct *dvdstruct);
28439213Sgibbs
28539213Sgibbsstatic struct periph_driver cddriver =
28639213Sgibbs{
28739213Sgibbs	cdinit, "cd",
28839213Sgibbs	TAILQ_HEAD_INITIALIZER(cddriver.units), /* generation */ 0
28939213Sgibbs};
29039213Sgibbs
29172119SpeterPERIPHDRIVER_DECLARE(cd, cddriver);
29239213Sgibbs
29339213Sgibbs
29439213Sgibbsstatic int num_changers;
29539213Sgibbs
29639213Sgibbs#ifndef CHANGER_MIN_BUSY_SECONDS
29746747Sken#define CHANGER_MIN_BUSY_SECONDS	5
29839213Sgibbs#endif
29939213Sgibbs#ifndef CHANGER_MAX_BUSY_SECONDS
30046747Sken#define CHANGER_MAX_BUSY_SECONDS	15
30139213Sgibbs#endif
30239213Sgibbs
30339213Sgibbsstatic int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS;
30439213Sgibbsstatic int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS;
30539213Sgibbs
30639213SgibbsSYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver");
30739213SgibbsSYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer");
30839213SgibbsSYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW,
30939213Sgibbs	   &changer_min_busy_seconds, 0, "Minimum changer scheduling quantum");
310111206SkenTUNABLE_INT("kern.cam.cd.changer.min_busy_seconds", &changer_min_busy_seconds);
31139213SgibbsSYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW,
31239213Sgibbs	   &changer_max_busy_seconds, 0, "Maximum changer scheduling quantum");
313111206SkenTUNABLE_INT("kern.cam.cd.changer.max_busy_seconds", &changer_max_busy_seconds);
31439213Sgibbs
31539213Sgibbsstruct cdchanger {
31639213Sgibbs	path_id_t			 path_id;
31739213Sgibbs	target_id_t			 target_id;
31839213Sgibbs	int				 num_devices;
31939213Sgibbs	struct camq			 devq;
32039213Sgibbs	struct timeval			 start_time;
32139213Sgibbs	struct cd_softc			 *cur_device;
32239213Sgibbs	struct callout_handle		 short_handle;
32339213Sgibbs	struct callout_handle		 long_handle;
32446581Sken	volatile cd_changer_flags	 flags;
32560938Sjake	STAILQ_ENTRY(cdchanger)		 changer_links;
32660938Sjake	STAILQ_HEAD(chdevlist, cd_softc) chluns;
32739213Sgibbs};
32839213Sgibbs
32960938Sjakestatic STAILQ_HEAD(changerlist, cdchanger) changerq;
33039213Sgibbs
331104880Sphk
332104880Sphkstatic void
33339213Sgibbscdinit(void)
33439213Sgibbs{
33539213Sgibbs	cam_status status;
33639213Sgibbs	struct cam_path *path;
33739213Sgibbs
33839213Sgibbs	/*
33939213Sgibbs	 * Install a global async callback.  This callback will
34039213Sgibbs	 * receive async callbacks like "new device found".
34139213Sgibbs	 */
34239213Sgibbs	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
34339213Sgibbs				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
34439213Sgibbs
34539213Sgibbs	if (status == CAM_REQ_CMP) {
34639213Sgibbs		struct ccb_setasync csa;
34739213Sgibbs
34839213Sgibbs                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
34939213Sgibbs                csa.ccb_h.func_code = XPT_SASYNC_CB;
35039213Sgibbs                csa.event_enable = AC_FOUND_DEVICE;
35139213Sgibbs                csa.callback = cdasync;
35239213Sgibbs                csa.callback_arg = NULL;
35339213Sgibbs                xpt_action((union ccb *)&csa);
35439213Sgibbs		status = csa.ccb_h.status;
35539213Sgibbs                xpt_free_path(path);
35639213Sgibbs        }
35739213Sgibbs
35839213Sgibbs	if (status != CAM_REQ_CMP) {
35939213Sgibbs		printf("cd: Failed to attach master async callback "
36039213Sgibbs		       "due to status 0x%x!\n", status);
36139213Sgibbs	}
36239213Sgibbs}
36339213Sgibbs
36439213Sgibbsstatic void
36540603Skencdoninvalidate(struct cam_periph *periph)
36640603Sken{
36740603Sken	int s;
36840603Sken	struct cd_softc *softc;
36940603Sken	struct ccb_setasync csa;
37040603Sken
37140603Sken	softc = (struct cd_softc *)periph->softc;
37240603Sken
37340603Sken	/*
37440603Sken	 * De-register any async callbacks.
37540603Sken	 */
37640603Sken	xpt_setup_ccb(&csa.ccb_h, periph->path,
37740603Sken		      /* priority */ 5);
37840603Sken	csa.ccb_h.func_code = XPT_SASYNC_CB;
37940603Sken	csa.event_enable = 0;
38040603Sken	csa.callback = cdasync;
38140603Sken	csa.callback_arg = periph;
38240603Sken	xpt_action((union ccb *)&csa);
38340603Sken
38440603Sken	softc->flags |= CD_FLAG_INVALID;
38540603Sken
38640603Sken	/*
38740603Sken	 * Although the oninvalidate() routines are always called at
38840603Sken	 * splsoftcam, we need to be at splbio() here to keep the buffer
38940603Sken	 * queue from being modified while we traverse it.
39040603Sken	 */
39140603Sken	s = splbio();
39240603Sken
39340603Sken	/*
39440603Sken	 * Return all queued I/O with ENXIO.
39540603Sken	 * XXX Handle any transactions queued to the card
39640603Sken	 *     with XPT_ABORT_CCB.
39740603Sken	 */
398112946Sphk	bioq_flush(&softc->bio_queue, NULL, ENXIO);
39940603Sken	splx(s);
40040603Sken
40140603Sken	/*
40240603Sken	 * If this device is part of a changer, and it was scheduled
40340603Sken	 * to run, remove it from the run queue since we just nuked
40440603Sken	 * all of its scheduled I/O.
40540603Sken	 */
40640603Sken	if ((softc->flags & CD_FLAG_CHANGER)
40740603Sken	 && (softc->pinfo.index != CAM_UNQUEUED_INDEX))
40840603Sken		camq_remove(&softc->changer->devq, softc->pinfo.index);
40940603Sken
41040603Sken	xpt_print_path(periph->path);
41140603Sken	printf("lost device\n");
41240603Sken}
41340603Sken
41440603Skenstatic void
41539213Sgibbscdcleanup(struct cam_periph *periph)
41639213Sgibbs{
41739213Sgibbs	struct cd_softc *softc;
41840603Sken	int s;
41939213Sgibbs
42039213Sgibbs	softc = (struct cd_softc *)periph->softc;
42139213Sgibbs
42239213Sgibbs	xpt_print_path(periph->path);
42339213Sgibbs	printf("removing device entry\n");
42440603Sken
425120884Sthomas	if ((softc->flags & CD_FLAG_SCTX_INIT) != 0
426120884Sthomas	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
427112668Sken		xpt_print_path(periph->path);
428112668Sken		printf("can't remove sysctl context\n");
429112668Sken	}
430112668Sken
43140603Sken	s = splsoftcam();
43239213Sgibbs	/*
43339213Sgibbs	 * In the queued, non-active case, the device in question
43439213Sgibbs	 * has already been removed from the changer run queue.  Since this
43539213Sgibbs	 * device is active, we need to de-activate it, and schedule
43639213Sgibbs	 * another device to run.  (if there is another one to run)
43739213Sgibbs	 */
43839213Sgibbs	if ((softc->flags & CD_FLAG_CHANGER)
43939213Sgibbs	 && (softc->flags & CD_FLAG_ACTIVE)) {
44039213Sgibbs
44139213Sgibbs		/*
44239213Sgibbs		 * The purpose of the short timeout is soley to determine
44339213Sgibbs		 * whether the current device has finished or not.  Well,
44439213Sgibbs		 * since we're removing the active device, we know that it
44539213Sgibbs		 * is finished.  So, get rid of the short timeout.
44639213Sgibbs		 * Otherwise, if we're in the time period before the short
44739213Sgibbs		 * timeout fires, and there are no other devices in the
44839213Sgibbs		 * queue to run, there won't be any other device put in the
44939213Sgibbs		 * active slot.  i.e., when we call cdrunchangerqueue()
45039213Sgibbs		 * below, it won't do anything.  Then, when the short
45139213Sgibbs		 * timeout fires, it'll look at the "current device", which
45239213Sgibbs		 * we are free below, and possibly panic the kernel on a
45339213Sgibbs		 * bogus pointer reference.
45439213Sgibbs		 *
45539213Sgibbs		 * The long timeout doesn't really matter, since we
45639213Sgibbs		 * decrement the qfrozen_cnt to indicate that there is
45739213Sgibbs		 * nothing in the active slot now.  Therefore, there won't
45839213Sgibbs		 * be any bogus pointer references there.
45939213Sgibbs		 */
46039213Sgibbs		if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
46139213Sgibbs			untimeout(cdshorttimeout, softc->changer,
46239213Sgibbs				  softc->changer->short_handle);
46339213Sgibbs			softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
46439213Sgibbs		}
46539213Sgibbs		softc->changer->devq.qfrozen_cnt--;
46639213Sgibbs		softc->changer->flags |= CHANGER_MANUAL_CALL;
46739213Sgibbs		cdrunchangerqueue(softc->changer);
46839213Sgibbs	}
46939213Sgibbs
47039213Sgibbs	/*
47139213Sgibbs	 * If we're removing the last device on the changer, go ahead and
47239213Sgibbs	 * remove the changer device structure.
47339213Sgibbs	 */
47439213Sgibbs	if ((softc->flags & CD_FLAG_CHANGER)
47539213Sgibbs	 && (--softc->changer->num_devices == 0)) {
47639213Sgibbs
47739213Sgibbs		/*
47839213Sgibbs		 * Theoretically, there shouldn't be any timeouts left, but
47939213Sgibbs		 * I'm not completely sure that that will be the case.  So,
48039213Sgibbs		 * it won't hurt to check and see if there are any left.
48139213Sgibbs		 */
48239213Sgibbs		if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) {
48339213Sgibbs			untimeout(cdrunchangerqueue, softc->changer,
48439213Sgibbs				  softc->changer->long_handle);
48539213Sgibbs			softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED;
48639213Sgibbs		}
48739213Sgibbs
48839213Sgibbs		if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
48939213Sgibbs			untimeout(cdshorttimeout, softc->changer,
49039213Sgibbs				  softc->changer->short_handle);
49139213Sgibbs			softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
49239213Sgibbs		}
49339213Sgibbs
49460938Sjake		STAILQ_REMOVE(&changerq, softc->changer, cdchanger,
49539213Sgibbs			      changer_links);
49639213Sgibbs		xpt_print_path(periph->path);
49739213Sgibbs		printf("removing changer entry\n");
49839213Sgibbs		free(softc->changer, M_DEVBUF);
49939213Sgibbs		num_changers--;
50039213Sgibbs	}
501125975Sphk	disk_destroy(softc->disk);
50240603Sken	free(softc, M_DEVBUF);
50340603Sken	splx(s);
50439213Sgibbs}
50539213Sgibbs
50639213Sgibbsstatic void
50739213Sgibbscdasync(void *callback_arg, u_int32_t code,
50839213Sgibbs	struct cam_path *path, void *arg)
50939213Sgibbs{
51039213Sgibbs	struct cam_periph *periph;
51139213Sgibbs
51239213Sgibbs	periph = (struct cam_periph *)callback_arg;
51339213Sgibbs	switch (code) {
51439213Sgibbs	case AC_FOUND_DEVICE:
51539213Sgibbs	{
51639213Sgibbs		struct ccb_getdev *cgd;
51739213Sgibbs		cam_status status;
51839213Sgibbs
51939213Sgibbs		cgd = (struct ccb_getdev *)arg;
52079177Smjacob		if (cgd == NULL)
52179177Smjacob			break;
52239213Sgibbs
52356148Smjacob		if (SID_TYPE(&cgd->inq_data) != T_CDROM
52456148Smjacob		    && SID_TYPE(&cgd->inq_data) != T_WORM)
52539213Sgibbs			break;
52639213Sgibbs
52739213Sgibbs		/*
52839213Sgibbs		 * Allocate a peripheral instance for
52939213Sgibbs		 * this device and start the probe
53039213Sgibbs		 * process.
53139213Sgibbs		 */
53240603Sken		status = cam_periph_alloc(cdregister, cdoninvalidate,
53340603Sken					  cdcleanup, cdstart,
53440603Sken					  "cd", CAM_PERIPH_BIO,
53540603Sken					  cgd->ccb_h.path, cdasync,
53640603Sken					  AC_FOUND_DEVICE, cgd);
53739213Sgibbs
53839213Sgibbs		if (status != CAM_REQ_CMP
53939213Sgibbs		 && status != CAM_REQ_INPROG)
54039213Sgibbs			printf("cdasync: Unable to attach new device "
54139213Sgibbs			       "due to status 0x%x\n", status);
54239213Sgibbs
54339213Sgibbs		break;
54439213Sgibbs	}
54539213Sgibbs	case AC_SENT_BDR:
54639213Sgibbs	case AC_BUS_RESET:
54739213Sgibbs	{
54839213Sgibbs		struct cd_softc *softc;
54939213Sgibbs		struct ccb_hdr *ccbh;
55039213Sgibbs		int s;
55139213Sgibbs
55239213Sgibbs		softc = (struct cd_softc *)periph->softc;
55339213Sgibbs		s = splsoftcam();
55439213Sgibbs		/*
55539213Sgibbs		 * Don't fail on the expected unit attention
55639213Sgibbs		 * that will occur.
55739213Sgibbs		 */
55839213Sgibbs		softc->flags |= CD_FLAG_RETRY_UA;
55971999Sphk		LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
56039213Sgibbs			ccbh->ccb_state |= CD_CCB_RETRY_UA;
56139213Sgibbs		splx(s);
56247413Sgibbs		/* FALLTHROUGH */
56339213Sgibbs	}
56439213Sgibbs	default:
56547413Sgibbs		cam_periph_async(periph, code, path, arg);
56639213Sgibbs		break;
56739213Sgibbs	}
56839213Sgibbs}
56939213Sgibbs
570119708Skenstatic void
571119708Skencdsysctlinit(void *context, int pending)
572119708Sken{
573119708Sken	struct cam_periph *periph;
574119708Sken	struct cd_softc *softc;
575119708Sken	char tmpstr[80], tmpstr2[80];
576119708Sken
577119708Sken	periph = (struct cam_periph *)context;
578119708Sken	softc = (struct cd_softc *)periph->softc;
579119708Sken
580119708Sken	snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number);
581119708Sken	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
582119708Sken
583119708Sken	mtx_lock(&Giant);
584119708Sken
585119708Sken	sysctl_ctx_init(&softc->sysctl_ctx);
586120884Sthomas	softc->flags |= CD_FLAG_SCTX_INIT;
587119708Sken	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
588119708Sken		SYSCTL_STATIC_CHILDREN(_kern_cam_cd), OID_AUTO,
589119708Sken		tmpstr2, CTLFLAG_RD, 0, tmpstr);
590119708Sken
591119708Sken	if (softc->sysctl_tree == NULL) {
592119708Sken		printf("cdsysctlinit: unable to allocate sysctl tree\n");
593119708Sken		return;
594119708Sken	}
595119708Sken
596119708Sken	/*
597119708Sken	 * Now register the sysctl handler, so the user can the value on
598119708Sken	 * the fly.
599119708Sken	 */
600119708Sken	SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree),
601119708Sken		OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW,
602119708Sken		&softc->minimum_command_size, 0, cdcmdsizesysctl, "I",
603119708Sken		"Minimum CDB size");
604119708Sken
605119708Sken	mtx_unlock(&Giant);
606119708Sken}
607119708Sken
608111206Sken/*
609111206Sken * We have a handler function for this so we can check the values when the
610111206Sken * user sets them, instead of every time we look at them.
611111206Sken */
612111206Skenstatic int
613111206Skencdcmdsizesysctl(SYSCTL_HANDLER_ARGS)
614111206Sken{
615111206Sken	int error, value;
616111206Sken
617111206Sken	value = *(int *)arg1;
618111206Sken
619111206Sken	error = sysctl_handle_int(oidp, &value, 0, req);
620111206Sken
621111206Sken	if ((error != 0)
622111206Sken	 || (req->newptr == NULL))
623111206Sken		return (error);
624111206Sken
625111206Sken	/*
626111206Sken	 * The only real values we can have here are 6 or 10.  I don't
627111206Sken	 * really forsee having 12 be an option at any time in the future.
628111206Sken	 * So if the user sets something less than or equal to 6, we'll set
629111206Sken	 * it to 6.  If he sets something greater than 6, we'll set it to 10.
630111206Sken	 *
631111206Sken	 * I suppose we could just return an error here for the wrong values,
632111206Sken	 * but I don't think it's necessary to do so, as long as we can
633111206Sken	 * determine the user's intent without too much trouble.
634111206Sken	 */
635111206Sken	if (value < 6)
636111206Sken		value = 6;
637111206Sken	else if (value > 6)
638111206Sken		value = 10;
639111206Sken
640111206Sken	*(int *)arg1 = value;
641111206Sken
642111206Sken	return (0);
643111206Sken}
644111206Sken
64539213Sgibbsstatic cam_status
64639213Sgibbscdregister(struct cam_periph *periph, void *arg)
64739213Sgibbs{
64839213Sgibbs	struct cd_softc *softc;
64939213Sgibbs	struct ccb_setasync csa;
650118105Snjl	struct ccb_pathinq cpi;
65139213Sgibbs	struct ccb_getdev *cgd;
652119708Sken	char tmpstr[80];
65339213Sgibbs	caddr_t match;
65439213Sgibbs
65539213Sgibbs	cgd = (struct ccb_getdev *)arg;
65639213Sgibbs	if (periph == NULL) {
65739213Sgibbs		printf("cdregister: periph was NULL!!\n");
65839213Sgibbs		return(CAM_REQ_CMP_ERR);
65939213Sgibbs	}
66039213Sgibbs	if (cgd == NULL) {
66139213Sgibbs		printf("cdregister: no getdev CCB, can't register device\n");
66239213Sgibbs		return(CAM_REQ_CMP_ERR);
66339213Sgibbs	}
66439213Sgibbs
66539213Sgibbs	softc = (struct cd_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
66639213Sgibbs
66739213Sgibbs	if (softc == NULL) {
66839213Sgibbs		printf("cdregister: Unable to probe new device. "
66939213Sgibbs		       "Unable to allocate softc\n");
67039213Sgibbs		return(CAM_REQ_CMP_ERR);
67139213Sgibbs	}
67239213Sgibbs
67339213Sgibbs	bzero(softc, sizeof(*softc));
67439213Sgibbs	LIST_INIT(&softc->pending_ccbs);
675111206Sken	STAILQ_INIT(&softc->mode_queue);
67639213Sgibbs	softc->state = CD_STATE_PROBE;
67759249Sphk	bioq_init(&softc->bio_queue);
67839213Sgibbs	if (SID_IS_REMOVABLE(&cgd->inq_data))
67939213Sgibbs		softc->flags |= CD_FLAG_DISC_REMOVABLE;
68039213Sgibbs	if ((cgd->inq_data.flags & SID_CmdQue) != 0)
68139213Sgibbs		softc->flags |= CD_FLAG_TAGGED_QUEUING;
68239213Sgibbs
68339213Sgibbs	periph->softc = softc;
68439213Sgibbs	softc->periph = periph;
68539213Sgibbs
68639213Sgibbs	/*
68739213Sgibbs	 * See if this device has any quirks.
68839213Sgibbs	 */
68939213Sgibbs	match = cam_quirkmatch((caddr_t)&cgd->inq_data,
69039213Sgibbs			       (caddr_t)cd_quirk_table,
69139213Sgibbs			       sizeof(cd_quirk_table)/sizeof(*cd_quirk_table),
69239213Sgibbs			       sizeof(*cd_quirk_table), scsi_inquiry_match);
69339213Sgibbs
69439213Sgibbs	if (match != NULL)
69539213Sgibbs		softc->quirks = ((struct cd_quirk_entry *)match)->quirks;
69639213Sgibbs	else
69739213Sgibbs		softc->quirks = CD_Q_NONE;
69839213Sgibbs
699118105Snjl	/* Check if the SIM does not want 6 byte commands */
700118105Snjl	xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1);
701118105Snjl	cpi.ccb_h.func_code = XPT_PATH_INQ;
702118105Snjl	xpt_action((union ccb *)&cpi);
703118105Snjl	if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE))
704118105Snjl		softc->quirks |= CD_Q_10_BYTE_ONLY;
705118105Snjl
706119708Sken	TASK_INIT(&softc->sysctl_task, 0, cdsysctlinit, periph);
707111206Sken
708111206Sken	/* The default is 6 byte commands, unless quirked otherwise */
709111206Sken	if (softc->quirks & CD_Q_10_BYTE_ONLY)
710111206Sken		softc->minimum_command_size = 10;
711111206Sken	else
712111206Sken		softc->minimum_command_size = 6;
713111206Sken
71439213Sgibbs	/*
715111206Sken	 * Load the user's default, if any.
716111206Sken	 */
717111206Sken	snprintf(tmpstr, sizeof(tmpstr), "kern.cam.cd.%d.minimum_cmd_size",
718111206Sken		 periph->unit_number);
719111206Sken	TUNABLE_INT_FETCH(tmpstr, &softc->minimum_command_size);
720111206Sken
721111206Sken	/* 6 and 10 are the only permissible values here. */
722111206Sken	if (softc->minimum_command_size < 6)
723111206Sken		softc->minimum_command_size = 6;
724111206Sken	else if (softc->minimum_command_size > 6)
725111206Sken		softc->minimum_command_size = 10;
726111206Sken
727111206Sken	/*
72839213Sgibbs	 * We need to register the statistics structure for this device,
72939213Sgibbs	 * but we don't have the blocksize yet for it.  So, we register
73039213Sgibbs	 * the structure and indicate that we don't have the blocksize
73139213Sgibbs	 * yet.  Unlike other SCSI peripheral drivers, we explicitly set
73239213Sgibbs	 * the device type here to be CDROM, rather than just ORing in
73356148Smjacob	 * the device type.  This is because this driver can attach to either
73439213Sgibbs	 * CDROM or WORM devices, and we want this peripheral driver to
73539213Sgibbs	 * show up in the devstat list as a CD peripheral driver, not a
73639213Sgibbs	 * WORM peripheral driver.  WORM drives will also have the WORM
73739213Sgibbs	 * driver attached to them.
73839213Sgibbs	 */
739125975Sphk	softc->disk = disk_alloc();
740125975Sphk	softc->disk->d_devstat = devstat_new_entry("cd",
74139213Sgibbs			  periph->unit_number, 0,
74239213Sgibbs	  		  DEVSTAT_BS_UNAVAILABLE,
74343819Sken			  DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_SCSI,
74443819Sken			  DEVSTAT_PRIORITY_CD);
745125975Sphk	softc->disk->d_open = cdopen;
746125975Sphk	softc->disk->d_close = cdclose;
747125975Sphk	softc->disk->d_strategy = cdstrategy;
748125975Sphk	softc->disk->d_ioctl = cdioctl;
749125975Sphk	softc->disk->d_name = "cd";
750125975Sphk	softc->disk->d_unit = periph->unit_number;
751125975Sphk	softc->disk->d_drv1 = periph;
752125975Sphk	softc->disk->d_flags = DISKFLAG_NEEDSGIANT;
753125975Sphk	disk_create(softc->disk, DISK_VERSION);
75439213Sgibbs
75539213Sgibbs	/*
75639213Sgibbs	 * Add an async callback so that we get
75739213Sgibbs	 * notified if this device goes away.
75839213Sgibbs	 */
75939213Sgibbs	xpt_setup_ccb(&csa.ccb_h, periph->path,
76039213Sgibbs		      /* priority */ 5);
76139213Sgibbs	csa.ccb_h.func_code = XPT_SASYNC_CB;
76239213Sgibbs	csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
76339213Sgibbs	csa.callback = cdasync;
76439213Sgibbs	csa.callback_arg = periph;
76539213Sgibbs	xpt_action((union ccb *)&csa);
76639213Sgibbs
76739213Sgibbs	/*
76839213Sgibbs	 * If the target lun is greater than 0, we most likely have a CD
76939213Sgibbs	 * changer device.  Check the quirk entries as well, though, just
77039213Sgibbs	 * in case someone has a CD tower with one lun per drive or
77139213Sgibbs	 * something like that.  Also, if we know up front that a
77239213Sgibbs	 * particular device is a changer, we can mark it as such starting
77339213Sgibbs	 * with lun 0, instead of lun 1.  It shouldn't be necessary to have
77439213Sgibbs	 * a quirk entry to define something as a changer, however.
77539213Sgibbs	 */
77639213Sgibbs	if (((cgd->ccb_h.target_lun > 0)
77739213Sgibbs	  && ((softc->quirks & CD_Q_NO_CHANGER) == 0))
77839213Sgibbs	 || ((softc->quirks & CD_Q_CHANGER) != 0)) {
77939213Sgibbs		struct cdchanger *nchanger;
78039213Sgibbs		struct cam_periph *nperiph;
78139213Sgibbs		struct cam_path *path;
78239213Sgibbs		cam_status status;
78339213Sgibbs		int found;
78439213Sgibbs
78539213Sgibbs		/* Set the changer flag in the current device's softc */
78639213Sgibbs		softc->flags |= CD_FLAG_CHANGER;
78739213Sgibbs
78839213Sgibbs		if (num_changers == 0)
78939213Sgibbs			STAILQ_INIT(&changerq);
79039213Sgibbs
79139213Sgibbs		/*
79239213Sgibbs		 * Now, look around for an existing changer device with the
79339213Sgibbs		 * same path and target ID as the current device.
79439213Sgibbs		 */
79539213Sgibbs		for (found = 0,
79639213Sgibbs		     nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq);
79739213Sgibbs		     nchanger != NULL;
79839213Sgibbs		     nchanger = STAILQ_NEXT(nchanger, changer_links)){
79939213Sgibbs			if ((nchanger->path_id == cgd->ccb_h.path_id)
80039213Sgibbs			 && (nchanger->target_id == cgd->ccb_h.target_id)) {
80139213Sgibbs				found = 1;
80239213Sgibbs				break;
80339213Sgibbs			}
80439213Sgibbs		}
80539213Sgibbs
80639213Sgibbs		/*
80739213Sgibbs		 * If we found a matching entry, just add this device to
80839213Sgibbs		 * the list of devices on this changer.
80939213Sgibbs		 */
81039213Sgibbs		if (found == 1) {
81139213Sgibbs			struct chdevlist *chlunhead;
81239213Sgibbs
81339213Sgibbs			chlunhead = &nchanger->chluns;
81439213Sgibbs
81539213Sgibbs			/*
81639213Sgibbs			 * XXX KDM look at consolidating this code with the
81739213Sgibbs			 * code below in a separate function.
81839213Sgibbs			 */
81939213Sgibbs
82039213Sgibbs			/*
82139213Sgibbs			 * Create a path with lun id 0, and see if we can
82239213Sgibbs			 * find a matching device
82339213Sgibbs			 */
82439213Sgibbs			status = xpt_create_path(&path, /*periph*/ periph,
82539213Sgibbs						 cgd->ccb_h.path_id,
82639213Sgibbs						 cgd->ccb_h.target_id, 0);
82739213Sgibbs
82839213Sgibbs			if ((status == CAM_REQ_CMP)
82939213Sgibbs			 && ((nperiph = cam_periph_find(path, "cd")) != NULL)){
83039213Sgibbs				struct cd_softc *nsoftc;
83139213Sgibbs
83239213Sgibbs				nsoftc = (struct cd_softc *)nperiph->softc;
83339213Sgibbs
83439213Sgibbs				if ((nsoftc->flags & CD_FLAG_CHANGER) == 0){
83539213Sgibbs					nsoftc->flags |= CD_FLAG_CHANGER;
83639213Sgibbs					nchanger->num_devices++;
83739213Sgibbs					if (camq_resize(&nchanger->devq,
83839213Sgibbs					   nchanger->num_devices)!=CAM_REQ_CMP){
83939213Sgibbs						printf("cdregister: "
84039213Sgibbs						       "camq_resize "
84139213Sgibbs						       "failed, changer "
84239213Sgibbs						       "support may "
84339213Sgibbs						       "be messed up\n");
84439213Sgibbs					}
84539213Sgibbs					nsoftc->changer = nchanger;
84639213Sgibbs					nsoftc->pinfo.index =CAM_UNQUEUED_INDEX;
84739213Sgibbs
84839213Sgibbs					STAILQ_INSERT_TAIL(&nchanger->chluns,
84939213Sgibbs							  nsoftc,changer_links);
85039213Sgibbs				}
85167928Sken				xpt_free_path(path);
85239213Sgibbs			} else if (status == CAM_REQ_CMP)
85339213Sgibbs				xpt_free_path(path);
85439213Sgibbs			else {
85539213Sgibbs				printf("cdregister: unable to allocate path\n"
85639213Sgibbs				       "cdregister: changer support may be "
85739213Sgibbs				       "broken\n");
85839213Sgibbs			}
85939213Sgibbs
86039213Sgibbs			nchanger->num_devices++;
86139213Sgibbs
86239213Sgibbs			softc->changer = nchanger;
86339213Sgibbs			softc->pinfo.index = CAM_UNQUEUED_INDEX;
86439213Sgibbs
86539213Sgibbs			if (camq_resize(&nchanger->devq,
86639213Sgibbs			    nchanger->num_devices) != CAM_REQ_CMP) {
86739213Sgibbs				printf("cdregister: camq_resize "
86839213Sgibbs				       "failed, changer support may "
86939213Sgibbs				       "be messed up\n");
87039213Sgibbs			}
87139213Sgibbs
87239213Sgibbs			STAILQ_INSERT_TAIL(chlunhead, softc, changer_links);
87339213Sgibbs		}
87439213Sgibbs		/*
87539213Sgibbs		 * In this case, we don't already have an entry for this
87639213Sgibbs		 * particular changer, so we need to create one, add it to
87739213Sgibbs		 * the queue, and queue this device on the list for this
87839213Sgibbs		 * changer.  Before we queue this device, however, we need
87939213Sgibbs		 * to search for lun id 0 on this target, and add it to the
88039213Sgibbs		 * queue first, if it exists.  (and if it hasn't already
88139213Sgibbs		 * been marked as part of the changer.)
88239213Sgibbs		 */
88339213Sgibbs		else {
88439213Sgibbs			nchanger = malloc(sizeof(struct cdchanger),
88539213Sgibbs				M_DEVBUF, M_NOWAIT);
88639213Sgibbs
88739213Sgibbs			if (nchanger == NULL) {
88839213Sgibbs				softc->flags &= ~CD_FLAG_CHANGER;
88939213Sgibbs				printf("cdregister: unable to malloc "
89039213Sgibbs				       "changer structure\ncdregister: "
89139213Sgibbs				       "changer support disabled\n");
89239213Sgibbs
89339213Sgibbs				/*
89439213Sgibbs				 * Yes, gotos can be gross but in this case
89539213Sgibbs				 * I think it's justified..
89639213Sgibbs				 */
89739213Sgibbs				goto cdregisterexit;
89839213Sgibbs			}
89939213Sgibbs
90039213Sgibbs			/* zero the structure */
90139213Sgibbs			bzero(nchanger, sizeof(struct cdchanger));
90239213Sgibbs
90339213Sgibbs			if (camq_init(&nchanger->devq, 1) != 0) {
90439213Sgibbs				softc->flags &= ~CD_FLAG_CHANGER;
90539213Sgibbs				printf("cdregister: changer support "
90639213Sgibbs				       "disabled\n");
90739213Sgibbs				goto cdregisterexit;
90839213Sgibbs			}
90939213Sgibbs
91039213Sgibbs			num_changers++;
91139213Sgibbs
91239213Sgibbs			nchanger->path_id = cgd->ccb_h.path_id;
91339213Sgibbs			nchanger->target_id = cgd->ccb_h.target_id;
91439213Sgibbs
91539213Sgibbs			/* this is superfluous, but it makes things clearer */
91639213Sgibbs			nchanger->num_devices = 0;
91739213Sgibbs
91839213Sgibbs			STAILQ_INIT(&nchanger->chluns);
91939213Sgibbs
92039213Sgibbs			STAILQ_INSERT_TAIL(&changerq, nchanger,
92139213Sgibbs					   changer_links);
92239213Sgibbs
92339213Sgibbs			/*
92439213Sgibbs			 * Create a path with lun id 0, and see if we can
92539213Sgibbs			 * find a matching device
92639213Sgibbs			 */
92739213Sgibbs			status = xpt_create_path(&path, /*periph*/ periph,
92839213Sgibbs						 cgd->ccb_h.path_id,
92939213Sgibbs						 cgd->ccb_h.target_id, 0);
93039213Sgibbs
93139213Sgibbs			/*
93239213Sgibbs			 * If we were able to allocate the path, and if we
93339213Sgibbs			 * find a matching device and it isn't already
93439213Sgibbs			 * marked as part of a changer, then we add it to
93539213Sgibbs			 * the current changer.
93639213Sgibbs			 */
93739213Sgibbs			if ((status == CAM_REQ_CMP)
93839213Sgibbs			 && ((nperiph = cam_periph_find(path, "cd")) != NULL)
93939213Sgibbs			 && ((((struct cd_softc *)periph->softc)->flags &
94039213Sgibbs			       CD_FLAG_CHANGER) == 0)) {
94139213Sgibbs				struct cd_softc *nsoftc;
94239213Sgibbs
94339213Sgibbs				nsoftc = (struct cd_softc *)nperiph->softc;
94439213Sgibbs
94539213Sgibbs				nsoftc->flags |= CD_FLAG_CHANGER;
94639213Sgibbs				nchanger->num_devices++;
94739213Sgibbs				if (camq_resize(&nchanger->devq,
94839213Sgibbs				    nchanger->num_devices) != CAM_REQ_CMP) {
94939213Sgibbs					printf("cdregister: camq_resize "
95039213Sgibbs					       "failed, changer support may "
95139213Sgibbs					       "be messed up\n");
95239213Sgibbs				}
95339213Sgibbs				nsoftc->changer = nchanger;
95439213Sgibbs				nsoftc->pinfo.index = CAM_UNQUEUED_INDEX;
95539213Sgibbs
95639213Sgibbs				STAILQ_INSERT_TAIL(&nchanger->chluns,
95739213Sgibbs						   nsoftc, changer_links);
95867928Sken				xpt_free_path(path);
95939213Sgibbs			} else if (status == CAM_REQ_CMP)
96039213Sgibbs				xpt_free_path(path);
96139213Sgibbs			else {
96239213Sgibbs				printf("cdregister: unable to allocate path\n"
96339213Sgibbs				       "cdregister: changer support may be "
96439213Sgibbs				       "broken\n");
96539213Sgibbs			}
96639213Sgibbs
96739213Sgibbs			softc->changer = nchanger;
96839213Sgibbs			softc->pinfo.index = CAM_UNQUEUED_INDEX;
96939213Sgibbs			nchanger->num_devices++;
97039213Sgibbs			if (camq_resize(&nchanger->devq,
97139213Sgibbs			    nchanger->num_devices) != CAM_REQ_CMP) {
97239213Sgibbs				printf("cdregister: camq_resize "
97339213Sgibbs				       "failed, changer support may "
97439213Sgibbs				       "be messed up\n");
97539213Sgibbs			}
97639213Sgibbs			STAILQ_INSERT_TAIL(&nchanger->chluns, softc,
97739213Sgibbs					   changer_links);
97839213Sgibbs		}
97939213Sgibbs	}
98039213Sgibbs
98139213Sgibbscdregisterexit:
98239213Sgibbs
98339213Sgibbs	/* Lock this peripheral until we are setup */
98439213Sgibbs	/* Can't block */
98539213Sgibbs	cam_periph_lock(periph, PRIBIO);
98639213Sgibbs
98739213Sgibbs	if ((softc->flags & CD_FLAG_CHANGER) == 0)
98839213Sgibbs		xpt_schedule(periph, /*priority*/5);
98939213Sgibbs	else
99039213Sgibbs		cdschedule(periph, /*priority*/ 5);
99139213Sgibbs
99239213Sgibbs	return(CAM_REQ_CMP);
99339213Sgibbs}
99439213Sgibbs
99539213Sgibbsstatic int
996120599Sphkcdopen(struct disk *dp)
99739213Sgibbs{
99839213Sgibbs	struct cam_periph *periph;
99939213Sgibbs	struct cd_softc *softc;
1000101940Snjl	int error;
100140603Sken	int s;
100239213Sgibbs
1003120599Sphk	periph = (struct cam_periph *)dp->d_drv1;
100439213Sgibbs	if (periph == NULL)
100539213Sgibbs		return (ENXIO);
100639213Sgibbs
100739213Sgibbs	softc = (struct cd_softc *)periph->softc;
100839213Sgibbs
100941297Sken	/*
101041297Sken	 * Grab splsoftcam and hold it until we lock the peripheral.
101141297Sken	 */
101240603Sken	s = splsoftcam();
101340603Sken	if (softc->flags & CD_FLAG_INVALID) {
101440603Sken		splx(s);
101539213Sgibbs		return(ENXIO);
101640603Sken	}
101739213Sgibbs
101841297Sken	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) {
101941297Sken		splx(s);
102039213Sgibbs		return (error);
102141297Sken	}
102239213Sgibbs
102341297Sken	splx(s);
102441297Sken
102551836Sphk	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
102651836Sphk		return(ENXIO);
102740020Sken
102840020Sken	/*
1029111206Sken	 * Check for media, and set the appropriate flags.  We don't bail
1030111206Sken	 * if we don't have media, but then we don't allow anything but the
1031111206Sken	 * CDIOCEJECT/CDIOCCLOSE ioctls if there is no media.
103251836Sphk	 */
1033111206Sken	cdcheckmedia(periph);
103440020Sken
103539213Sgibbs	cam_periph_unlock(periph);
103639213Sgibbs
103739213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
103839213Sgibbs
103939213Sgibbs	return (error);
104039213Sgibbs}
104139213Sgibbs
104239213Sgibbsstatic int
1043120599Sphkcdclose(struct disk *dp)
104439213Sgibbs{
104539213Sgibbs	struct 	cam_periph *periph;
104639213Sgibbs	struct	cd_softc *softc;
1047101940Snjl	int	error;
104839213Sgibbs
1049120599Sphk	periph = (struct cam_periph *)dp->d_drv1;
105039213Sgibbs	if (periph == NULL)
105139213Sgibbs		return (ENXIO);
105239213Sgibbs
105339213Sgibbs	softc = (struct cd_softc *)periph->softc;
105439213Sgibbs
105539213Sgibbs	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
105639213Sgibbs		return (error);
105739213Sgibbs
105839213Sgibbs	if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0)
105939213Sgibbs		cdprevent(periph, PR_ALLOW);
106039213Sgibbs
106139213Sgibbs	/*
106239213Sgibbs	 * Since we're closing this CD, mark the blocksize as unavailable.
1063111206Sken	 * It will be marked as available when the CD is opened again.
106439213Sgibbs	 */
1065125975Sphk	softc->disk->d_devstat->flags |= DEVSTAT_BS_UNAVAILABLE;
106639213Sgibbs
1067111206Sken	/*
1068111206Sken	 * We'll check the media and toc again at the next open().
1069111206Sken	 */
1070111206Sken	softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC);
1071111206Sken
107239213Sgibbs	cam_periph_unlock(periph);
107339213Sgibbs	cam_periph_release(periph);
107439213Sgibbs
107539213Sgibbs	return (0);
107639213Sgibbs}
107739213Sgibbs
107839213Sgibbsstatic void
107939213Sgibbscdshorttimeout(void *arg)
108039213Sgibbs{
108139213Sgibbs	struct cdchanger *changer;
108239213Sgibbs	int s;
108339213Sgibbs
108439213Sgibbs	s = splsoftcam();
108539213Sgibbs
108639213Sgibbs	changer = (struct cdchanger *)arg;
108739213Sgibbs
108839213Sgibbs	/* Always clear the short timeout flag, since that's what we're in */
108939213Sgibbs	changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
109039213Sgibbs
109139213Sgibbs	/*
109239213Sgibbs	 * Check to see if there is any more pending or outstanding I/O for
109339213Sgibbs	 * this device.  If not, move it out of the active slot.
109439213Sgibbs	 */
109559249Sphk	if ((bioq_first(&changer->cur_device->bio_queue) == NULL)
1096112262Sphk	 && (changer->cur_device->outstanding_cmds == 0)) {
109739213Sgibbs		changer->flags |= CHANGER_MANUAL_CALL;
109839213Sgibbs		cdrunchangerqueue(changer);
109939213Sgibbs	}
110039213Sgibbs
110139213Sgibbs	splx(s);
110239213Sgibbs}
110339213Sgibbs
110439213Sgibbs/*
110539213Sgibbs * This is a wrapper for xpt_schedule.  It only applies to changers.
110639213Sgibbs */
110739213Sgibbsstatic void
110839213Sgibbscdschedule(struct cam_periph *periph, int priority)
110939213Sgibbs{
111039213Sgibbs	struct cd_softc *softc;
111139213Sgibbs	int s;
111239213Sgibbs
111339213Sgibbs	s = splsoftcam();
111439213Sgibbs
111539213Sgibbs	softc = (struct cd_softc *)periph->softc;
111639213Sgibbs
111739213Sgibbs	/*
111839213Sgibbs	 * If this device isn't currently queued, and if it isn't
111939213Sgibbs	 * the active device, then we queue this device and run the
112039213Sgibbs	 * changer queue if there is no timeout scheduled to do it.
112139213Sgibbs	 * If this device is the active device, just schedule it
112239213Sgibbs	 * to run again.  If this device is queued, there should be
112339213Sgibbs	 * a timeout in place already that will make sure it runs.
112439213Sgibbs	 */
112539213Sgibbs	if ((softc->pinfo.index == CAM_UNQUEUED_INDEX)
112639213Sgibbs	 && ((softc->flags & CD_FLAG_ACTIVE) == 0)) {
112739213Sgibbs		/*
112839213Sgibbs		 * We don't do anything with the priority here.
112939213Sgibbs		 * This is strictly a fifo queue.
113039213Sgibbs		 */
113139213Sgibbs		softc->pinfo.priority = 1;
113245442Sgibbs		softc->pinfo.generation = ++softc->changer->devq.generation;
113339213Sgibbs		camq_insert(&softc->changer->devq, (cam_pinfo *)softc);
113439213Sgibbs
113539213Sgibbs		/*
113639213Sgibbs		 * Since we just put a device in the changer queue,
113739213Sgibbs		 * check and see if there is a timeout scheduled for
113839213Sgibbs		 * this changer.  If so, let the timeout handle
113939213Sgibbs		 * switching this device into the active slot.  If
114039213Sgibbs		 * not, manually call the timeout routine to
114139213Sgibbs		 * bootstrap things.
114239213Sgibbs		 */
114339213Sgibbs		if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0)
114446581Sken		 && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0)
114546581Sken		 && ((softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED)==0)){
114639213Sgibbs			softc->changer->flags |= CHANGER_MANUAL_CALL;
114739213Sgibbs			cdrunchangerqueue(softc->changer);
114839213Sgibbs		}
114939213Sgibbs	} else if ((softc->flags & CD_FLAG_ACTIVE)
115039213Sgibbs		&& ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0))
115139213Sgibbs		xpt_schedule(periph, priority);
115239213Sgibbs
115339213Sgibbs	splx(s);
115439213Sgibbs
115539213Sgibbs}
115639213Sgibbs
115739213Sgibbsstatic void
115839213Sgibbscdrunchangerqueue(void *arg)
115939213Sgibbs{
116039213Sgibbs	struct cd_softc *softc;
116139213Sgibbs	struct cdchanger *changer;
116239213Sgibbs	int called_from_timeout;
116339213Sgibbs	int s;
116439213Sgibbs
116539213Sgibbs	s = splsoftcam();
116639213Sgibbs
116739213Sgibbs	changer = (struct cdchanger *)arg;
116839213Sgibbs
116939213Sgibbs	/*
117039213Sgibbs	 * If we have NOT been called from cdstrategy() or cddone(), and
117139213Sgibbs	 * instead from a timeout routine, go ahead and clear the
117239213Sgibbs	 * timeout flag.
117339213Sgibbs	 */
117439213Sgibbs	if ((changer->flags & CHANGER_MANUAL_CALL) == 0) {
117539213Sgibbs		changer->flags &= ~CHANGER_TIMEOUT_SCHED;
117639213Sgibbs		called_from_timeout = 1;
117739213Sgibbs	} else
117839213Sgibbs		called_from_timeout = 0;
117939213Sgibbs
118039213Sgibbs	/* Always clear the manual call flag */
118139213Sgibbs	changer->flags &= ~CHANGER_MANUAL_CALL;
118239213Sgibbs
118339213Sgibbs	/* nothing to do if the queue is empty */
118439213Sgibbs	if (changer->devq.entries <= 0) {
118539213Sgibbs		splx(s);
118639213Sgibbs		return;
118739213Sgibbs	}
118839213Sgibbs
118939213Sgibbs	/*
119039213Sgibbs	 * If the changer queue is frozen, that means we have an active
119139213Sgibbs	 * device.
119239213Sgibbs	 */
119339213Sgibbs	if (changer->devq.qfrozen_cnt > 0) {
119439213Sgibbs
1195112262Sphk		if (changer->cur_device->outstanding_cmds > 0) {
119639213Sgibbs			changer->cur_device->flags |= CD_FLAG_SCHED_ON_COMP;
119739213Sgibbs			changer->cur_device->bufs_left =
1198112262Sphk				changer->cur_device->outstanding_cmds;
119939213Sgibbs			if (called_from_timeout) {
120039213Sgibbs				changer->long_handle =
120139213Sgibbs					timeout(cdrunchangerqueue, changer,
120239213Sgibbs				        changer_max_busy_seconds * hz);
120339213Sgibbs				changer->flags |= CHANGER_TIMEOUT_SCHED;
120439213Sgibbs			}
120539213Sgibbs			splx(s);
120639213Sgibbs			return;
120739213Sgibbs		}
120839213Sgibbs
120939213Sgibbs		/*
121039213Sgibbs		 * We always need to reset the frozen count and clear the
121139213Sgibbs		 * active flag.
121239213Sgibbs		 */
121339213Sgibbs		changer->devq.qfrozen_cnt--;
121439213Sgibbs		changer->cur_device->flags &= ~CD_FLAG_ACTIVE;
121539213Sgibbs		changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP;
121639213Sgibbs
121739213Sgibbs		/*
121839213Sgibbs		 * Check to see whether the current device has any I/O left
121939213Sgibbs		 * to do.  If so, requeue it at the end of the queue.  If
122039213Sgibbs		 * not, there is no need to requeue it.
122139213Sgibbs		 */
122259249Sphk		if (bioq_first(&changer->cur_device->bio_queue) != NULL) {
122339213Sgibbs
122439213Sgibbs			changer->cur_device->pinfo.generation =
122545442Sgibbs				++changer->devq.generation;
122639213Sgibbs			camq_insert(&changer->devq,
122739213Sgibbs				(cam_pinfo *)changer->cur_device);
122839213Sgibbs		}
122939213Sgibbs	}
123039213Sgibbs
123145845Sgibbs	softc = (struct cd_softc *)camq_remove(&changer->devq, CAMQ_HEAD);
123239213Sgibbs
123339213Sgibbs	changer->cur_device = softc;
123439213Sgibbs
123539213Sgibbs	changer->devq.qfrozen_cnt++;
123639213Sgibbs	softc->flags |= CD_FLAG_ACTIVE;
123739213Sgibbs
123839213Sgibbs	/* Just in case this device is waiting */
123939213Sgibbs	wakeup(&softc->changer);
124039213Sgibbs	xpt_schedule(softc->periph, /*priority*/ 1);
124139213Sgibbs
124239213Sgibbs	/*
124339213Sgibbs	 * Get rid of any pending timeouts, and set a flag to schedule new
124439213Sgibbs	 * ones so this device gets its full time quantum.
124539213Sgibbs	 */
124639213Sgibbs	if (changer->flags & CHANGER_TIMEOUT_SCHED) {
124739213Sgibbs		untimeout(cdrunchangerqueue, changer, changer->long_handle);
124839213Sgibbs		changer->flags &= ~CHANGER_TIMEOUT_SCHED;
124939213Sgibbs	}
125039213Sgibbs
125139213Sgibbs	if (changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
125239213Sgibbs		untimeout(cdshorttimeout, changer, changer->short_handle);
125339213Sgibbs		changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
125439213Sgibbs	}
125539213Sgibbs
125639213Sgibbs	/*
125739213Sgibbs	 * We need to schedule timeouts, but we only do this after the
125839213Sgibbs	 * first transaction has completed.  This eliminates the changer
125939213Sgibbs	 * switch time.
126039213Sgibbs	 */
126139213Sgibbs	changer->flags |= CHANGER_NEED_TIMEOUT;
126239213Sgibbs
126339213Sgibbs	splx(s);
126439213Sgibbs}
126539213Sgibbs
126639213Sgibbsstatic void
126739213Sgibbscdchangerschedule(struct cd_softc *softc)
126839213Sgibbs{
126939213Sgibbs	struct cdchanger *changer;
127039213Sgibbs	int s;
127139213Sgibbs
127239213Sgibbs	s = splsoftcam();
127339213Sgibbs
127439213Sgibbs	changer = softc->changer;
127539213Sgibbs
127639213Sgibbs	/*
127739213Sgibbs	 * If this is a changer, and this is the current device,
127839213Sgibbs	 * and this device has at least the minimum time quantum to
127939213Sgibbs	 * run, see if we can switch it out.
128039213Sgibbs	 */
128139213Sgibbs	if ((softc->flags & CD_FLAG_ACTIVE)
128239213Sgibbs	 && ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0)
128339213Sgibbs	 && ((changer->flags & CHANGER_NEED_TIMEOUT) == 0)) {
128439213Sgibbs		/*
128539213Sgibbs		 * We try three things here.  The first is that we
128639213Sgibbs		 * check to see whether the schedule on completion
128739213Sgibbs		 * flag is set.  If it is, we decrement the number
128839213Sgibbs		 * of buffers left, and if it's zero, we reschedule.
128939213Sgibbs		 * Next, we check to see whether the pending buffer
129039213Sgibbs		 * queue is empty and whether there are no
129139213Sgibbs		 * outstanding transactions.  If so, we reschedule.
129239213Sgibbs		 * Next, we see if the pending buffer queue is empty.
129339213Sgibbs		 * If it is, we set the number of buffers left to
129439213Sgibbs		 * the current active buffer count and set the
129539213Sgibbs		 * schedule on complete flag.
129639213Sgibbs		 */
129739213Sgibbs		if (softc->flags & CD_FLAG_SCHED_ON_COMP) {
129839213Sgibbs		 	if (--softc->bufs_left == 0) {
129939213Sgibbs				softc->changer->flags |=
130039213Sgibbs					CHANGER_MANUAL_CALL;
130139213Sgibbs				softc->flags &= ~CD_FLAG_SCHED_ON_COMP;
130239213Sgibbs				cdrunchangerqueue(softc->changer);
130339213Sgibbs			}
130459249Sphk		} else if ((bioq_first(&softc->bio_queue) == NULL)
1305112262Sphk		        && (softc->outstanding_cmds == 0)) {
130639213Sgibbs			softc->changer->flags |= CHANGER_MANUAL_CALL;
130739213Sgibbs			cdrunchangerqueue(softc->changer);
130839213Sgibbs		}
130939213Sgibbs	} else if ((softc->changer->flags & CHANGER_NEED_TIMEOUT)
131039213Sgibbs		&& (softc->flags & CD_FLAG_ACTIVE)) {
131139213Sgibbs
131239213Sgibbs		/*
131339213Sgibbs		 * Now that the first transaction to this
131439213Sgibbs		 * particular device has completed, we can go ahead
131539213Sgibbs		 * and schedule our timeouts.
131639213Sgibbs		 */
131739213Sgibbs		if ((changer->flags & CHANGER_TIMEOUT_SCHED) == 0) {
131839213Sgibbs			changer->long_handle =
131939213Sgibbs			    timeout(cdrunchangerqueue, changer,
132039213Sgibbs				    changer_max_busy_seconds * hz);
132139213Sgibbs			changer->flags |= CHANGER_TIMEOUT_SCHED;
132239213Sgibbs		} else
132339213Sgibbs			printf("cdchangerschedule: already have a long"
132439213Sgibbs			       " timeout!\n");
132539213Sgibbs
132639213Sgibbs		if ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) {
132739213Sgibbs			changer->short_handle =
132839213Sgibbs			    timeout(cdshorttimeout, changer,
132939213Sgibbs				    changer_min_busy_seconds * hz);
133039213Sgibbs			changer->flags |= CHANGER_SHORT_TMOUT_SCHED;
133139213Sgibbs		} else
133239213Sgibbs			printf("cdchangerschedule: already have a short "
133339213Sgibbs			       "timeout!\n");
133439213Sgibbs
133539213Sgibbs		/*
133639213Sgibbs		 * We just scheduled timeouts, no need to schedule
133739213Sgibbs		 * more.
133839213Sgibbs		 */
133939213Sgibbs		changer->flags &= ~CHANGER_NEED_TIMEOUT;
134039213Sgibbs
134139213Sgibbs	}
134239213Sgibbs	splx(s);
134339213Sgibbs}
134439213Sgibbs
134539213Sgibbsstatic int
134639213Sgibbscdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb,
134739213Sgibbs					      u_int32_t cam_flags,
134839213Sgibbs					      u_int32_t sense_flags),
134939213Sgibbs	 u_int32_t cam_flags, u_int32_t sense_flags)
135039213Sgibbs{
135139213Sgibbs	struct cd_softc *softc;
135239213Sgibbs	struct cam_periph *periph;
135339213Sgibbs	int error;
135439213Sgibbs
135539213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
135639213Sgibbs	softc = (struct cd_softc *)periph->softc;
135739213Sgibbs
135839213Sgibbs	error = cam_periph_runccb(ccb, error_routine, cam_flags, sense_flags,
1359125975Sphk				  softc->disk->d_devstat);
136039213Sgibbs
136139213Sgibbs	if (softc->flags & CD_FLAG_CHANGER)
136239213Sgibbs		cdchangerschedule(softc);
136339213Sgibbs
136439213Sgibbs	return(error);
136539213Sgibbs}
136639213Sgibbs
136742017Seivindstatic union ccb *
136839213Sgibbscdgetccb(struct cam_periph *periph, u_int32_t priority)
136939213Sgibbs{
137039213Sgibbs	struct cd_softc *softc;
137139213Sgibbs	int s;
137239213Sgibbs
137339213Sgibbs	softc = (struct cd_softc *)periph->softc;
137439213Sgibbs
137539213Sgibbs	if (softc->flags & CD_FLAG_CHANGER) {
137639213Sgibbs
137739213Sgibbs		s = splsoftcam();
137839213Sgibbs
137939213Sgibbs		/*
138039213Sgibbs		 * This should work the first time this device is woken up,
138139213Sgibbs		 * but just in case it doesn't, we use a while loop.
138239213Sgibbs		 */
138346581Sken		while ((softc->flags & CD_FLAG_ACTIVE) == 0) {
138439213Sgibbs			/*
138539213Sgibbs			 * If this changer isn't already queued, queue it up.
138639213Sgibbs			 */
138739213Sgibbs			if (softc->pinfo.index == CAM_UNQUEUED_INDEX) {
138839213Sgibbs				softc->pinfo.priority = 1;
138939213Sgibbs				softc->pinfo.generation =
139045442Sgibbs					++softc->changer->devq.generation;
139139213Sgibbs				camq_insert(&softc->changer->devq,
139239213Sgibbs					    (cam_pinfo *)softc);
139339213Sgibbs			}
139446581Sken			if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0)
139546581Sken			 && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0)
139646581Sken			 && ((softc->changer->flags
139746581Sken			      & CHANGER_SHORT_TMOUT_SCHED)==0)) {
139839213Sgibbs				softc->changer->flags |= CHANGER_MANUAL_CALL;
139939213Sgibbs				cdrunchangerqueue(softc->changer);
140039213Sgibbs			} else
140139213Sgibbs				tsleep(&softc->changer, PRIBIO, "cgticb", 0);
140239213Sgibbs		}
140339213Sgibbs		splx(s);
140439213Sgibbs	}
140539213Sgibbs	return(cam_periph_getccb(periph, priority));
140639213Sgibbs}
140739213Sgibbs
140839213Sgibbs
140939213Sgibbs/*
141039213Sgibbs * Actually translate the requested transfer into one the physical driver
141139213Sgibbs * can understand.  The transfer is described by a buf and will include
141239213Sgibbs * only one physical transfer.
141339213Sgibbs */
141439213Sgibbsstatic void
141559249Sphkcdstrategy(struct bio *bp)
141639213Sgibbs{
141739213Sgibbs	struct cam_periph *periph;
141839213Sgibbs	struct cd_softc *softc;
141939213Sgibbs	int    s;
142039213Sgibbs
1421120599Sphk	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
142239213Sgibbs	if (periph == NULL) {
142376362Sphk		biofinish(bp, NULL, ENXIO);
142476362Sphk		return;
142539213Sgibbs	}
142639213Sgibbs
142739213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstrategy\n"));
142839213Sgibbs
142939213Sgibbs	softc = (struct cd_softc *)periph->softc;
143039213Sgibbs
143139213Sgibbs	/*
143239213Sgibbs	 * Mask interrupts so that the pack cannot be invalidated until
143339213Sgibbs	 * after we are in the queue.  Otherwise, we might not properly
143439213Sgibbs	 * clean up one of the buffers.
143539213Sgibbs	 */
143639213Sgibbs	s = splbio();
143739213Sgibbs
143839213Sgibbs	/*
143939213Sgibbs	 * If the device has been made invalid, error out
144039213Sgibbs	 */
144139213Sgibbs	if ((softc->flags & CD_FLAG_INVALID)) {
144239213Sgibbs		splx(s);
144376362Sphk		biofinish(bp, NULL, ENXIO);
144476362Sphk		return;
144539213Sgibbs	}
144639213Sgibbs
1447111206Sken        /*
1448111206Sken	 * If we don't have valid media, look for it before trying to
1449111206Sken	 * schedule the I/O.
1450111206Sken	 */
1451111206Sken	if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) {
1452111206Sken		int error;
1453111206Sken
1454111206Sken		error = cdcheckmedia(periph);
1455111206Sken		if (error != 0) {
1456111206Sken			splx(s);
1457111206Sken			biofinish(bp, NULL, error);
1458111206Sken			return;
1459111206Sken		}
1460111206Sken	}
1461111206Sken
146239213Sgibbs	/*
146339213Sgibbs	 * Place it in the queue of disk activities for this disk
146439213Sgibbs	 */
1465112946Sphk	bioq_disksort(&softc->bio_queue, bp);
146639213Sgibbs
146739213Sgibbs	splx(s);
146839213Sgibbs
146939213Sgibbs	/*
147039213Sgibbs	 * Schedule ourselves for performing the work.  We do things
147139213Sgibbs	 * differently for changers.
147239213Sgibbs	 */
147339213Sgibbs	if ((softc->flags & CD_FLAG_CHANGER) == 0)
147439213Sgibbs		xpt_schedule(periph, /* XXX priority */1);
147539213Sgibbs	else
147639213Sgibbs		cdschedule(periph, /* priority */ 1);
147739213Sgibbs
147839213Sgibbs	return;
147939213Sgibbs}
148039213Sgibbs
148139213Sgibbsstatic void
148239213Sgibbscdstart(struct cam_periph *periph, union ccb *start_ccb)
148339213Sgibbs{
148439213Sgibbs	struct cd_softc *softc;
148559249Sphk	struct bio *bp;
148639213Sgibbs	struct ccb_scsiio *csio;
148739213Sgibbs	struct scsi_read_capacity_data *rcap;
148839213Sgibbs	int s;
148939213Sgibbs
149039213Sgibbs	softc = (struct cd_softc *)periph->softc;
149139213Sgibbs
149239213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstart\n"));
149339213Sgibbs
149439213Sgibbs	switch (softc->state) {
149539213Sgibbs	case CD_STATE_NORMAL:
149639213Sgibbs	{
149739213Sgibbs		int oldspl;
149839213Sgibbs
149939213Sgibbs		s = splbio();
150059249Sphk		bp = bioq_first(&softc->bio_queue);
150139213Sgibbs		if (periph->immediate_priority <= periph->pinfo.priority) {
150239213Sgibbs			start_ccb->ccb_h.ccb_state = CD_CCB_WAITING;
150339213Sgibbs
150439213Sgibbs			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
150539213Sgibbs					  periph_links.sle);
150639213Sgibbs			periph->immediate_priority = CAM_PRIORITY_NONE;
150739213Sgibbs			splx(s);
150839213Sgibbs			wakeup(&periph->ccb_list);
150939213Sgibbs		} else if (bp == NULL) {
151039213Sgibbs			splx(s);
151139213Sgibbs			xpt_release_ccb(start_ccb);
151239213Sgibbs		} else {
151359249Sphk			bioq_remove(&softc->bio_queue, bp);
151439213Sgibbs
1515125975Sphk			devstat_start_transaction_bio(softc->disk->d_devstat, bp);
151639213Sgibbs
151739213Sgibbs			scsi_read_write(&start_ccb->csio,
151839213Sgibbs					/*retries*/4,
151939213Sgibbs					/* cbfcnp */ cddone,
152091062Sphk					MSG_SIMPLE_Q_TAG,
152159249Sphk					/* read */bp->bio_cmd == BIO_READ,
152239213Sgibbs					/* byte2 */ 0,
152339213Sgibbs					/* minimum_cmd_size */ 10,
1524121209Sphk					/* lba */ bp->bio_offset /
1525121209Sphk					  softc->params.blksize,
152659249Sphk					bp->bio_bcount / softc->params.blksize,
152759249Sphk					/* data_ptr */ bp->bio_data,
152859249Sphk					/* dxfer_len */ bp->bio_bcount,
152939213Sgibbs					/* sense_len */ SSD_FULL_SIZE,
153039213Sgibbs					/* timeout */ 30000);
153139213Sgibbs			start_ccb->ccb_h.ccb_state = CD_CCB_BUFFER_IO;
153239213Sgibbs
153339213Sgibbs
153439213Sgibbs			/*
153539213Sgibbs			 * Block out any asyncronous callbacks
153639213Sgibbs			 * while we touch the pending ccb list.
153739213Sgibbs			 */
153839213Sgibbs			oldspl = splcam();
153939213Sgibbs			LIST_INSERT_HEAD(&softc->pending_ccbs,
154039213Sgibbs					 &start_ccb->ccb_h, periph_links.le);
1541112262Sphk			softc->outstanding_cmds++;
154239213Sgibbs			splx(oldspl);
154339213Sgibbs
154439213Sgibbs			/* We expect a unit attention from this device */
154539213Sgibbs			if ((softc->flags & CD_FLAG_RETRY_UA) != 0) {
154639213Sgibbs				start_ccb->ccb_h.ccb_state |= CD_CCB_RETRY_UA;
154739213Sgibbs				softc->flags &= ~CD_FLAG_RETRY_UA;
154839213Sgibbs			}
154939213Sgibbs
155039213Sgibbs			start_ccb->ccb_h.ccb_bp = bp;
155159249Sphk			bp = bioq_first(&softc->bio_queue);
155239213Sgibbs			splx(s);
155339213Sgibbs
155439213Sgibbs			xpt_action(start_ccb);
155539213Sgibbs		}
155639213Sgibbs		if (bp != NULL) {
155739213Sgibbs			/* Have more work to do, so ensure we stay scheduled */
155839213Sgibbs			xpt_schedule(periph, /* XXX priority */1);
155939213Sgibbs		}
156039213Sgibbs		break;
156139213Sgibbs	}
156239213Sgibbs	case CD_STATE_PROBE:
156339213Sgibbs	{
156439213Sgibbs
156539213Sgibbs		rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
156639213Sgibbs								M_TEMP,
156739213Sgibbs								M_NOWAIT);
156839213Sgibbs		if (rcap == NULL) {
156939213Sgibbs			xpt_print_path(periph->path);
157039213Sgibbs			printf("cdstart: Couldn't malloc read_capacity data\n");
157139213Sgibbs			/* cd_free_periph??? */
157239213Sgibbs			break;
157339213Sgibbs		}
157439213Sgibbs		csio = &start_ccb->csio;
157539213Sgibbs		scsi_read_capacity(csio,
157639213Sgibbs				   /*retries*/1,
157739213Sgibbs				   cddone,
157839213Sgibbs				   MSG_SIMPLE_Q_TAG,
157939213Sgibbs				   rcap,
158039213Sgibbs				   SSD_FULL_SIZE,
158139213Sgibbs				   /*timeout*/20000);
158239213Sgibbs		start_ccb->ccb_h.ccb_bp = NULL;
158339213Sgibbs		start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
158439213Sgibbs		xpt_action(start_ccb);
158539213Sgibbs		break;
158639213Sgibbs	}
158739213Sgibbs	}
158839213Sgibbs}
158939213Sgibbs
159039213Sgibbsstatic void
159139213Sgibbscddone(struct cam_periph *periph, union ccb *done_ccb)
159239213Sgibbs{
159339213Sgibbs	struct cd_softc *softc;
159439213Sgibbs	struct ccb_scsiio *csio;
159539213Sgibbs
159639213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cddone\n"));
159739213Sgibbs
159839213Sgibbs	softc = (struct cd_softc *)periph->softc;
159939213Sgibbs	csio = &done_ccb->csio;
160039213Sgibbs
160139213Sgibbs	switch (csio->ccb_h.ccb_state & CD_CCB_TYPE_MASK) {
160239213Sgibbs	case CD_CCB_BUFFER_IO:
160339213Sgibbs	{
160459249Sphk		struct bio	*bp;
160539213Sgibbs		int		error;
160639213Sgibbs		int		oldspl;
160739213Sgibbs
160859249Sphk		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
160939213Sgibbs		error = 0;
161039213Sgibbs
161139213Sgibbs		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
161239213Sgibbs			int sf;
161339213Sgibbs
161439213Sgibbs			if ((done_ccb->ccb_h.ccb_state & CD_CCB_RETRY_UA) != 0)
161539213Sgibbs				sf = SF_RETRY_UA;
161639213Sgibbs			else
161739213Sgibbs				sf = 0;
161846747Sken
161974840Sken			error = cderror(done_ccb, CAM_RETRY_SELTO, sf);
162074840Sken			if (error == ERESTART) {
162139213Sgibbs				/*
162239213Sgibbs				 * A retry was scheuled, so
162339213Sgibbs				 * just return.
162439213Sgibbs				 */
162539213Sgibbs				return;
162639213Sgibbs			}
162739213Sgibbs		}
162839213Sgibbs
162939213Sgibbs		if (error != 0) {
163039213Sgibbs			int s;
163139213Sgibbs
163239213Sgibbs			xpt_print_path(periph->path);
163339213Sgibbs			printf("cddone: got error %#x back\n", error);
163439213Sgibbs			s = splbio();
1635112946Sphk			bioq_flush(&softc->bio_queue, NULL, EIO);
163639213Sgibbs			splx(s);
163759249Sphk			bp->bio_resid = bp->bio_bcount;
163859249Sphk			bp->bio_error = error;
163959249Sphk			bp->bio_flags |= BIO_ERROR;
164039213Sgibbs			cam_release_devq(done_ccb->ccb_h.path,
164139213Sgibbs					 /*relsim_flags*/0,
164239213Sgibbs					 /*reduction*/0,
164339213Sgibbs					 /*timeout*/0,
164439213Sgibbs					 /*getcount_only*/0);
164539213Sgibbs
164639213Sgibbs		} else {
164759249Sphk			bp->bio_resid = csio->resid;
164859249Sphk			bp->bio_error = 0;
164959249Sphk			if (bp->bio_resid != 0) {
1650104456Sphk				/*
1651104456Sphk				 * Short transfer ???
1652104456Sphk				 * XXX: not sure this is correct for partial
1653104456Sphk				 * transfers at EOM
1654104456Sphk				 */
165559249Sphk				bp->bio_flags |= BIO_ERROR;
165639213Sgibbs			}
165739213Sgibbs		}
165839213Sgibbs
165939213Sgibbs		/*
166039213Sgibbs		 * Block out any asyncronous callbacks
166139213Sgibbs		 * while we touch the pending ccb list.
166239213Sgibbs		 */
166339213Sgibbs		oldspl = splcam();
166439213Sgibbs		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
1665112262Sphk		softc->outstanding_cmds--;
166639213Sgibbs		splx(oldspl);
166739213Sgibbs
166839213Sgibbs		if (softc->flags & CD_FLAG_CHANGER)
166939213Sgibbs			cdchangerschedule(softc);
167039213Sgibbs
1671125975Sphk		biofinish(bp, softc->disk->d_devstat, 0);
167239213Sgibbs		break;
167339213Sgibbs	}
167439213Sgibbs	case CD_CCB_PROBE:
167539213Sgibbs	{
167639213Sgibbs		struct	   scsi_read_capacity_data *rdcap;
167739213Sgibbs		char	   announce_buf[120]; /*
167839213Sgibbs					       * Currently (9/30/97) the
167939213Sgibbs					       * longest possible announce
168039213Sgibbs					       * buffer is 108 bytes, for the
168139213Sgibbs					       * first error case below.
168239213Sgibbs					       * That is 39 bytes for the
168339213Sgibbs					       * basic string, 16 bytes for the
168439213Sgibbs					       * biggest sense key (hardware
168539213Sgibbs					       * error), 52 bytes for the
168639213Sgibbs					       * text of the largest sense
168739213Sgibbs					       * qualifier valid for a CDROM,
168839213Sgibbs					       * (0x72, 0x03 or 0x04,
168939213Sgibbs					       * 0x03), and one byte for the
169039213Sgibbs					       * null terminating character.
169139213Sgibbs					       * To allow for longer strings,
169239213Sgibbs					       * the announce buffer is 120
169339213Sgibbs					       * bytes.
169439213Sgibbs					       */
169539213Sgibbs		struct	   cd_params *cdp;
169639213Sgibbs
169739213Sgibbs		cdp = &softc->params;
169839213Sgibbs
169939213Sgibbs		rdcap = (struct scsi_read_capacity_data *)csio->data_ptr;
170039213Sgibbs
170139213Sgibbs		cdp->disksize = scsi_4btoul (rdcap->addr) + 1;
170239213Sgibbs		cdp->blksize = scsi_4btoul (rdcap->length);
170339213Sgibbs
170439213Sgibbs		if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
170539213Sgibbs
170641514Sarchie			snprintf(announce_buf, sizeof(announce_buf),
170740020Sken				"cd present [%lu x %lu byte records]",
170840020Sken				cdp->disksize, (u_long)cdp->blksize);
170939213Sgibbs
171039213Sgibbs		} else {
171139213Sgibbs			int	error;
171239213Sgibbs			/*
171339213Sgibbs			 * Retry any UNIT ATTENTION type errors.  They
171439213Sgibbs			 * are expected at boot.
171539213Sgibbs			 */
171674840Sken			error = cderror(done_ccb, CAM_RETRY_SELTO,
171774840Sken					SF_RETRY_UA | SF_NO_PRINT);
171839213Sgibbs			if (error == ERESTART) {
171939213Sgibbs				/*
172039213Sgibbs				 * A retry was scheuled, so
172139213Sgibbs				 * just return.
172239213Sgibbs				 */
172339213Sgibbs				return;
172439213Sgibbs			} else if (error != 0) {
172539213Sgibbs
172639213Sgibbs				struct scsi_sense_data *sense;
172739213Sgibbs				int asc, ascq;
172839213Sgibbs				int sense_key, error_code;
172939213Sgibbs				int have_sense;
173039213Sgibbs				cam_status status;
173139213Sgibbs				struct ccb_getdev cgd;
173239213Sgibbs
173339213Sgibbs				/* Don't wedge this device's queue */
173439213Sgibbs				cam_release_devq(done_ccb->ccb_h.path,
173539213Sgibbs						 /*relsim_flags*/0,
173639213Sgibbs						 /*reduction*/0,
173739213Sgibbs						 /*timeout*/0,
173839213Sgibbs						 /*getcount_only*/0);
173939213Sgibbs
174039213Sgibbs				status = done_ccb->ccb_h.status;
174139213Sgibbs
174239213Sgibbs				xpt_setup_ccb(&cgd.ccb_h,
174339213Sgibbs					      done_ccb->ccb_h.path,
174439213Sgibbs					      /* priority */ 1);
174539213Sgibbs				cgd.ccb_h.func_code = XPT_GDEV_TYPE;
174639213Sgibbs				xpt_action((union ccb *)&cgd);
174739213Sgibbs
174839213Sgibbs				if (((csio->ccb_h.flags & CAM_SENSE_PHYS) != 0)
174939213Sgibbs				 || ((csio->ccb_h.flags & CAM_SENSE_PTR) != 0)
175039213Sgibbs				 || ((status & CAM_AUTOSNS_VALID) == 0))
175139213Sgibbs					have_sense = FALSE;
175239213Sgibbs				else
175339213Sgibbs					have_sense = TRUE;
175439213Sgibbs
175539213Sgibbs				if (have_sense) {
175639213Sgibbs					sense = &csio->sense_data;
175739213Sgibbs					scsi_extract_sense(sense, &error_code,
175839213Sgibbs							   &sense_key,
175939213Sgibbs							   &asc, &ascq);
176039213Sgibbs				}
176139213Sgibbs				/*
176246581Sken				 * Attach to anything that claims to be a
176346581Sken				 * CDROM or WORM device, as long as it
176446581Sken				 * doesn't return a "Logical unit not
176546581Sken				 * supported" (0x25) error.
176639213Sgibbs				 */
176746581Sken				if ((have_sense) && (asc != 0x25)
176874840Sken				 && (error_code == SSD_CURRENT_ERROR)) {
176974840Sken					const char *sense_key_desc;
177074840Sken					const char *asc_desc;
177174840Sken
177274840Sken					scsi_sense_desc(sense_key, asc, ascq,
177374840Sken							&cgd.inq_data,
177474840Sken							&sense_key_desc,
177574840Sken							&asc_desc);
177641514Sarchie					snprintf(announce_buf,
177741514Sarchie					    sizeof(announce_buf),
177839213Sgibbs						"Attempt to query device "
177939213Sgibbs						"size failed: %s, %s",
178074840Sken						sense_key_desc,
178174840Sken						asc_desc);
178282850Sken 				} else if ((have_sense == 0)
178382850Sken 				      && ((status & CAM_STATUS_MASK) ==
178482850Sken 					   CAM_SCSI_STATUS_ERROR)
178582850Sken 				      && (csio->scsi_status ==
178682850Sken 					  SCSI_STATUS_BUSY)) {
178782850Sken 					snprintf(announce_buf,
178882850Sken 					    sizeof(announce_buf),
178982850Sken 					    "Attempt to query device "
179082850Sken 					    "size failed: SCSI Status: %s",
179182850Sken					    scsi_status_string(csio));
179274840Sken				} else if (SID_TYPE(&cgd.inq_data) == T_CDROM) {
179339213Sgibbs					/*
179439213Sgibbs					 * We only print out an error for
179539213Sgibbs					 * CDROM type devices.  For WORM
179639213Sgibbs					 * devices, we don't print out an
179739213Sgibbs					 * error since a few WORM devices
179839213Sgibbs					 * don't support CDROM commands.
179939213Sgibbs					 * If we have sense information, go
180039213Sgibbs					 * ahead and print it out.
180139213Sgibbs					 * Otherwise, just say that we
180239213Sgibbs					 * couldn't attach.
180339213Sgibbs					 */
180439213Sgibbs
180539213Sgibbs					/*
180639213Sgibbs					 * Just print out the error, not
180739213Sgibbs					 * the full probe message, when we
180839213Sgibbs					 * don't attach.
180939213Sgibbs					 */
181040020Sken					if (have_sense)
181140020Sken						scsi_sense_print(
181240020Sken							&done_ccb->csio);
181340020Sken					else {
181440020Sken						xpt_print_path(periph->path);
181540020Sken						printf("got CAM status %#x\n",
181640020Sken						       done_ccb->ccb_h.status);
181740020Sken					}
181840020Sken					xpt_print_path(periph->path);
181940020Sken					printf("fatal error, failed"
182040603Sken					       " to attach to device\n");
182139213Sgibbs
182239213Sgibbs					/*
182340603Sken					 * Invalidate this peripheral.
182439213Sgibbs					 */
182539213Sgibbs					cam_periph_invalidate(periph);
182640020Sken
182740020Sken					announce_buf[0] = '\0';
182839213Sgibbs				} else {
182940603Sken
183039213Sgibbs					/*
183140603Sken					 * Invalidate this peripheral.
183239213Sgibbs					 */
183339213Sgibbs					cam_periph_invalidate(periph);
183440020Sken					announce_buf[0] = '\0';
183539213Sgibbs				}
183639213Sgibbs			}
183739213Sgibbs		}
183839213Sgibbs		free(rdcap, M_TEMP);
183942378Smjacob		if (announce_buf[0] != '\0') {
184039213Sgibbs			xpt_announce_periph(periph, announce_buf);
184142378Smjacob			if (softc->flags & CD_FLAG_CHANGER)
184242378Smjacob				cdchangerschedule(softc);
1843119708Sken			/*
1844119708Sken			 * Create our sysctl variables, now that we know
1845119708Sken			 * we have successfully attached.
1846119708Sken			 */
1847119708Sken			taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task);
184842378Smjacob		}
184940020Sken		softc->state = CD_STATE_NORMAL;
185042378Smjacob		/*
185142378Smjacob		 * Since our peripheral may be invalidated by an error
185242378Smjacob		 * above or an external event, we must release our CCB
185342378Smjacob		 * before releasing the probe lock on the peripheral.
185442378Smjacob		 * The peripheral will only go away once the last lock
185542378Smjacob		 * is removed, and we need it around for the CCB release
185642378Smjacob		 * operation.
185742378Smjacob		 */
185842378Smjacob		xpt_release_ccb(done_ccb);
185940020Sken		cam_periph_unlock(periph);
186042378Smjacob		return;
186139213Sgibbs	}
186239213Sgibbs	case CD_CCB_WAITING:
186339213Sgibbs	{
186439213Sgibbs		/* Caller will release the CCB */
186539213Sgibbs		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
186639213Sgibbs			  ("trying to wakeup ccbwait\n"));
186739213Sgibbs
186839213Sgibbs		wakeup(&done_ccb->ccb_h.cbfcnp);
186939213Sgibbs		return;
187039213Sgibbs	}
187142378Smjacob	default:
187242378Smjacob		break;
187339213Sgibbs	}
187439213Sgibbs	xpt_release_ccb(done_ccb);
187539213Sgibbs}
187639213Sgibbs
1877111206Skenstatic union cd_pages *
1878111206Skencdgetpage(struct cd_mode_params *mode_params)
1879111206Sken{
1880111206Sken	union cd_pages *page;
1881111206Sken
1882111206Sken	if (mode_params->cdb_size == 10)
1883111206Sken		page = (union cd_pages *)find_mode_page_10(
1884111206Sken			(struct scsi_mode_header_10 *)mode_params->mode_buf);
1885111206Sken	else
1886111206Sken		page = (union cd_pages *)find_mode_page_6(
1887111206Sken			(struct scsi_mode_header_6 *)mode_params->mode_buf);
1888111206Sken
1889111206Sken	return (page);
1890111206Sken}
1891111206Sken
189239213Sgibbsstatic int
1893111206Skencdgetpagesize(int page_num)
1894111206Sken{
1895111206Sken	int i;
1896111206Sken
1897111206Sken	for (i = 0; i < (sizeof(cd_page_size_table)/
1898111206Sken	     sizeof(cd_page_size_table[0])); i++) {
1899111206Sken		if (cd_page_size_table[i].page == page_num)
1900111206Sken			return (cd_page_size_table[i].page_size);
1901111206Sken	}
1902111206Sken
1903111206Sken	return (-1);
1904111206Sken}
1905111206Sken
1906111206Skenstatic int
1907120599Sphkcdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td)
190839213Sgibbs{
190939213Sgibbs
191039213Sgibbs	struct 	cam_periph *periph;
191139213Sgibbs	struct	cd_softc *softc;
1912101940Snjl	int	error;
191339213Sgibbs
1914120599Sphk	periph = (struct cam_periph *)dp->d_drv1;
191539213Sgibbs	if (periph == NULL)
191639213Sgibbs		return(ENXIO);
191739213Sgibbs
191839213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdioctl\n"));
191939213Sgibbs
192039213Sgibbs	softc = (struct cd_softc *)periph->softc;
192139213Sgibbs
192239213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
192340020Sken		  ("trying to do ioctl %#lx\n", cmd));
192439213Sgibbs
192540020Sken	error = cam_periph_lock(periph, PRIBIO | PCATCH);
192639213Sgibbs
192740020Sken	if (error != 0)
192840020Sken		return(error);
1929111206Sken	/*
1930111206Sken	 * If we don't have media loaded, check for it.  If still don't
1931111206Sken	 * have media loaded, we can only do a load or eject.
1932111206Sken	 */
1933111206Sken	if (((softc->flags & CD_FLAG_VALID_MEDIA) == 0)
1934111206Sken	 && ((cmd != CDIOCCLOSE)
1935111206Sken	  && (cmd != CDIOCEJECT))) {
1936111206Sken		error = cdcheckmedia(periph);
1937111206Sken		if (error != 0) {
1938111206Sken			cam_periph_unlock(periph);
1939111206Sken			return (error);
1940111206Sken		}
1941111206Sken	}
194240020Sken
194339213Sgibbs	switch (cmd) {
194439213Sgibbs
194539213Sgibbs	case CDIOCPLAYTRACKS:
194639213Sgibbs		{
194739213Sgibbs			struct ioc_play_track *args
194839213Sgibbs			    = (struct ioc_play_track *) addr;
1949111206Sken			struct cd_mode_params params;
1950111206Sken			union cd_pages *page;
195139213Sgibbs
1952111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
1953111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
1954111206Sken						 M_WAITOK | M_ZERO);
195539213Sgibbs
195639213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
195739213Sgibbs				  ("trying to do CDIOCPLAYTRACKS\n"));
195839213Sgibbs
1959111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
196039213Sgibbs			if (error) {
1961111206Sken				free(params.mode_buf, M_TEMP);
196239213Sgibbs				break;
196339213Sgibbs			}
1964111206Sken			page = cdgetpage(&params);
1965111206Sken
1966111206Sken			page->audio.flags &= ~CD_PA_SOTC;
1967111206Sken			page->audio.flags |= CD_PA_IMMED;
1968111206Sken			error = cdsetmode(periph, &params);
1969111206Sken			free(params.mode_buf, M_TEMP);
197039213Sgibbs			if (error)
197139213Sgibbs				break;
1972111206Sken
1973111206Sken			/*
1974111206Sken			 * This was originally implemented with the PLAY
1975111206Sken			 * AUDIO TRACK INDEX command, but that command was
1976111206Sken			 * deprecated after SCSI-2.  Most (all?) SCSI CDROM
1977111206Sken			 * drives support it but ATAPI and ATAPI-derivative
1978111206Sken			 * drives don't seem to support it.  So we keep a
1979111206Sken			 * cache of the table of contents and translate
1980111206Sken			 * track numbers to MSF format.
1981111206Sken			 */
1982111206Sken			if (softc->flags & CD_FLAG_VALID_TOC) {
1983111206Sken				union msf_lba *sentry, *eentry;
1984111206Sken				int st, et;
1985111206Sken
1986111206Sken				if (args->end_track <
1987111206Sken				    softc->toc.header.ending_track + 1)
1988111206Sken					args->end_track++;
1989111206Sken				if (args->end_track >
1990111206Sken				    softc->toc.header.ending_track + 1)
1991111206Sken					args->end_track =
1992111206Sken					    softc->toc.header.ending_track + 1;
1993111206Sken				st = args->start_track -
1994111206Sken					softc->toc.header.starting_track;
1995111206Sken				et = args->end_track -
1996111206Sken					softc->toc.header.starting_track;
1997111206Sken				if ((st < 0)
1998111206Sken				 || (et < 0)
1999111206Sken			 	 || (st > (softc->toc.header.ending_track -
2000111206Sken				     softc->toc.header.starting_track))) {
2001111206Sken					error = EINVAL;
2002111206Sken					break;
2003111206Sken				}
2004111206Sken				sentry = &softc->toc.entries[st].addr;
2005111206Sken				eentry = &softc->toc.entries[et].addr;
2006111206Sken				error = cdplaymsf(periph,
2007111206Sken						  sentry->msf.minute,
2008111206Sken						  sentry->msf.second,
2009111206Sken						  sentry->msf.frame,
2010111206Sken						  eentry->msf.minute,
2011111206Sken						  eentry->msf.second,
2012111206Sken						  eentry->msf.frame);
2013111206Sken			} else {
2014111206Sken				/*
2015111206Sken				 * If we don't have a valid TOC, try the
2016111206Sken				 * play track index command.  It is part of
2017111206Sken				 * the SCSI-2 spec, but was removed in the
2018111206Sken				 * MMC specs.  ATAPI and ATAPI-derived
2019111206Sken				 * drives don't support it.
2020111206Sken				 */
2021111206Sken				if (softc->quirks & CD_Q_BCD_TRACKS) {
2022111206Sken					args->start_track =
2023111206Sken						bin2bcd(args->start_track);
2024111206Sken					args->end_track =
2025111206Sken						bin2bcd(args->end_track);
2026111206Sken				}
2027111206Sken				error = cdplaytracks(periph,
2028111206Sken						     args->start_track,
2029111206Sken						     args->start_index,
2030111206Sken						     args->end_track,
2031111206Sken						     args->end_index);
203239213Sgibbs			}
203339213Sgibbs		}
203439213Sgibbs		break;
203539213Sgibbs	case CDIOCPLAYMSF:
203639213Sgibbs		{
203739213Sgibbs			struct ioc_play_msf *args
203839213Sgibbs				= (struct ioc_play_msf *) addr;
2039111206Sken			struct cd_mode_params params;
2040111206Sken			union cd_pages *page;
204139213Sgibbs
2042111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2043111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2044111206Sken						 M_WAITOK | M_ZERO);
204539213Sgibbs
204639213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
204739213Sgibbs				  ("trying to do CDIOCPLAYMSF\n"));
204839213Sgibbs
2049111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
205039213Sgibbs			if (error) {
2051111206Sken				free(params.mode_buf, M_TEMP);
205239213Sgibbs				break;
205339213Sgibbs			}
2054111206Sken			page = cdgetpage(&params);
2055111206Sken
2056111206Sken			page->audio.flags &= ~CD_PA_SOTC;
2057111206Sken			page->audio.flags |= CD_PA_IMMED;
2058111206Sken			error = cdsetmode(periph, &params);
2059111206Sken			free(params.mode_buf, M_TEMP);
206039213Sgibbs			if (error)
206139213Sgibbs				break;
206239213Sgibbs			error = cdplaymsf(periph,
206339213Sgibbs					  args->start_m,
206439213Sgibbs					  args->start_s,
206539213Sgibbs					  args->start_f,
206639213Sgibbs					  args->end_m,
206739213Sgibbs					  args->end_s,
206839213Sgibbs					  args->end_f);
206939213Sgibbs		}
207039213Sgibbs		break;
207139213Sgibbs	case CDIOCPLAYBLOCKS:
207239213Sgibbs		{
207339213Sgibbs			struct ioc_play_blocks *args
207439213Sgibbs				= (struct ioc_play_blocks *) addr;
2075111206Sken			struct cd_mode_params params;
2076111206Sken			union cd_pages *page;
207739213Sgibbs
207839213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
207939213Sgibbs				  ("trying to do CDIOCPLAYBLOCKS\n"));
208039213Sgibbs
2081111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2082111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2083111206Sken						 M_WAITOK | M_ZERO);
208439213Sgibbs
2085111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
208639213Sgibbs			if (error) {
2087111206Sken				free(params.mode_buf, M_TEMP);
208839213Sgibbs				break;
208939213Sgibbs			}
2090111206Sken			page = cdgetpage(&params);
2091111206Sken
2092111206Sken			page->audio.flags &= ~CD_PA_SOTC;
2093111206Sken			page->audio.flags |= CD_PA_IMMED;
2094111206Sken			error = cdsetmode(periph, &params);
2095111206Sken			free(params.mode_buf, M_TEMP);
209639213Sgibbs			if (error)
209739213Sgibbs				break;
209839213Sgibbs			error = cdplay(periph, args->blk, args->len);
209939213Sgibbs		}
210039213Sgibbs		break;
210139213Sgibbs	case CDIOCREADSUBCHANNEL:
210239213Sgibbs		{
210339213Sgibbs			struct ioc_read_subchannel *args
210439213Sgibbs				= (struct ioc_read_subchannel *) addr;
210539213Sgibbs			struct cd_sub_channel_info *data;
210639213Sgibbs			u_int32_t len = args->data_len;
210739213Sgibbs
210839213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
210939213Sgibbs				  ("trying to do CDIOCREADSUBCHANNEL\n"));
211039213Sgibbs
211139213Sgibbs			data = malloc(sizeof(struct cd_sub_channel_info),
2112111119Simp				      M_TEMP, M_WAITOK);
211339213Sgibbs
211439213Sgibbs			if ((len > sizeof(struct cd_sub_channel_info)) ||
211539213Sgibbs			    (len < sizeof(struct cd_sub_channel_header))) {
211639213Sgibbs				printf(
211739213Sgibbs					"scsi_cd: cdioctl: "
211839213Sgibbs					"cdioreadsubchannel: error, len=%d\n",
211939213Sgibbs					len);
212039213Sgibbs				error = EINVAL;
212139213Sgibbs				free(data, M_TEMP);
212239213Sgibbs				break;
212339213Sgibbs			}
212439213Sgibbs
212539213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS)
212639213Sgibbs				args->track = bin2bcd(args->track);
212739213Sgibbs
212839213Sgibbs			error = cdreadsubchannel(periph, args->address_format,
212939213Sgibbs				args->data_format, args->track, data, len);
213039213Sgibbs
213139213Sgibbs			if (error) {
213239213Sgibbs				free(data, M_TEMP);
213339213Sgibbs	 			break;
213439213Sgibbs			}
213539213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS)
213639213Sgibbs				data->what.track_info.track_number =
213739213Sgibbs				    bcd2bin(data->what.track_info.track_number);
213839213Sgibbs			len = min(len, ((data->header.data_len[0] << 8) +
213939213Sgibbs				data->header.data_len[1] +
214039213Sgibbs				sizeof(struct cd_sub_channel_header)));
214139213Sgibbs			if (copyout(data, args->data, len) != 0) {
214239213Sgibbs				error = EFAULT;
214339213Sgibbs			}
214439213Sgibbs			free(data, M_TEMP);
214539213Sgibbs		}
214639213Sgibbs		break;
214739213Sgibbs
214839213Sgibbs	case CDIOREADTOCHEADER:
214939213Sgibbs		{
215039213Sgibbs			struct ioc_toc_header *th;
215139213Sgibbs
215239213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
215339213Sgibbs				  ("trying to do CDIOREADTOCHEADER\n"));
215439213Sgibbs
215539213Sgibbs			th = malloc(sizeof(struct ioc_toc_header), M_TEMP,
2156111119Simp				    M_WAITOK);
2157111206Sken			error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
2158111206Sken				          sizeof (*th), /*sense_flags*/0);
215939213Sgibbs			if (error) {
216039213Sgibbs				free(th, M_TEMP);
216139213Sgibbs				break;
216239213Sgibbs			}
216339213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS) {
216439213Sgibbs				/* we are going to have to convert the BCD
216539213Sgibbs				 * encoding on the cd to what is expected
216639213Sgibbs				 */
216739213Sgibbs				th->starting_track =
216839213Sgibbs					bcd2bin(th->starting_track);
216939213Sgibbs				th->ending_track = bcd2bin(th->ending_track);
217039213Sgibbs			}
217190868Smike			th->len = ntohs(th->len);
217239213Sgibbs			bcopy(th, addr, sizeof(*th));
217339213Sgibbs			free(th, M_TEMP);
217439213Sgibbs		}
217539213Sgibbs		break;
217639213Sgibbs	case CDIOREADTOCENTRYS:
217739213Sgibbs		{
2178111206Sken			struct cd_tocdata *data;
2179111206Sken			struct cd_toc_single *lead;
218039213Sgibbs			struct ioc_read_toc_entry *te =
218139213Sgibbs				(struct ioc_read_toc_entry *) addr;
218239213Sgibbs			struct ioc_toc_header *th;
218339213Sgibbs			u_int32_t len, readlen, idx, num;
218439213Sgibbs			u_int32_t starting_track = te->starting_track;
218539213Sgibbs
218639213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
218739213Sgibbs				  ("trying to do CDIOREADTOCENTRYS\n"));
218839213Sgibbs
2189111206Sken			data = malloc(sizeof(*data), M_TEMP, M_WAITOK);
2190111206Sken			lead = malloc(sizeof(*lead), M_TEMP, M_WAITOK);
219139213Sgibbs
219239213Sgibbs			if (te->data_len < sizeof(struct cd_toc_entry)
219339213Sgibbs			 || (te->data_len % sizeof(struct cd_toc_entry)) != 0
219439213Sgibbs			 || (te->address_format != CD_MSF_FORMAT
219539213Sgibbs			  && te->address_format != CD_LBA_FORMAT)) {
219639213Sgibbs				error = EINVAL;
219739213Sgibbs				printf("scsi_cd: error in readtocentries, "
219839213Sgibbs				       "returning EINVAL\n");
219939213Sgibbs				free(data, M_TEMP);
220039213Sgibbs				free(lead, M_TEMP);
220139213Sgibbs				break;
220239213Sgibbs			}
220339213Sgibbs
220439213Sgibbs			th = &data->header;
2205111206Sken			error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
2206111206Sken					  sizeof (*th), /*sense_flags*/0);
220739213Sgibbs			if (error) {
220839213Sgibbs				free(data, M_TEMP);
220939213Sgibbs				free(lead, M_TEMP);
221039213Sgibbs				break;
221139213Sgibbs			}
221239213Sgibbs
221339213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS) {
221439213Sgibbs				/* we are going to have to convert the BCD
221539213Sgibbs				 * encoding on the cd to what is expected
221639213Sgibbs				 */
221739213Sgibbs				th->starting_track =
221839213Sgibbs				    bcd2bin(th->starting_track);
221939213Sgibbs				th->ending_track = bcd2bin(th->ending_track);
222039213Sgibbs			}
222139213Sgibbs
222239213Sgibbs			if (starting_track == 0)
222339213Sgibbs				starting_track = th->starting_track;
222439213Sgibbs			else if (starting_track == LEADOUT)
222539213Sgibbs				starting_track = th->ending_track + 1;
222639213Sgibbs			else if (starting_track < th->starting_track ||
222739213Sgibbs				 starting_track > th->ending_track + 1) {
222839213Sgibbs				printf("scsi_cd: error in readtocentries, "
222939213Sgibbs				       "returning EINVAL\n");
223039213Sgibbs				free(data, M_TEMP);
223139213Sgibbs				free(lead, M_TEMP);
223239213Sgibbs				error = EINVAL;
223339213Sgibbs				break;
223439213Sgibbs			}
223539213Sgibbs
223639213Sgibbs			/* calculate reading length without leadout entry */
223739213Sgibbs			readlen = (th->ending_track - starting_track + 1) *
223839213Sgibbs				  sizeof(struct cd_toc_entry);
223939213Sgibbs
224039213Sgibbs			/* and with leadout entry */
224139213Sgibbs			len = readlen + sizeof(struct cd_toc_entry);
224239213Sgibbs			if (te->data_len < len) {
224339213Sgibbs				len = te->data_len;
224439213Sgibbs				if (readlen > len)
224539213Sgibbs					readlen = len;
224639213Sgibbs			}
224739213Sgibbs			if (len > sizeof(data->entries)) {
224839213Sgibbs				printf("scsi_cd: error in readtocentries, "
224939213Sgibbs				       "returning EINVAL\n");
225039213Sgibbs				error = EINVAL;
225139213Sgibbs				free(data, M_TEMP);
225239213Sgibbs				free(lead, M_TEMP);
225339213Sgibbs				break;
225439213Sgibbs			}
225539213Sgibbs			num = len / sizeof(struct cd_toc_entry);
225639213Sgibbs
225739213Sgibbs			if (readlen > 0) {
225839213Sgibbs				error = cdreadtoc(periph, te->address_format,
225939213Sgibbs						  starting_track,
2260111206Sken						  (u_int8_t *)data,
2261111206Sken						  readlen + sizeof (*th),
2262111206Sken						  /*sense_flags*/0);
226339213Sgibbs				if (error) {
226439213Sgibbs					free(data, M_TEMP);
226539213Sgibbs					free(lead, M_TEMP);
226639213Sgibbs					break;
226739213Sgibbs				}
226839213Sgibbs			}
226939213Sgibbs
227039213Sgibbs			/* make leadout entry if needed */
227139213Sgibbs			idx = starting_track + num - 1;
227239213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS)
227339213Sgibbs				th->ending_track = bcd2bin(th->ending_track);
227439213Sgibbs			if (idx == th->ending_track + 1) {
227539213Sgibbs				error = cdreadtoc(periph, te->address_format,
2276111206Sken						  LEADOUT, (u_int8_t *)lead,
2277111206Sken						  sizeof(*lead),
2278111206Sken						  /*sense_flags*/0);
227939213Sgibbs				if (error) {
228039213Sgibbs					free(data, M_TEMP);
228139213Sgibbs					free(lead, M_TEMP);
228239213Sgibbs					break;
228339213Sgibbs				}
228439213Sgibbs				data->entries[idx - starting_track] =
228539213Sgibbs					lead->entry;
228639213Sgibbs			}
228739213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS) {
228839213Sgibbs				for (idx = 0; idx < num - 1; idx++) {
228939213Sgibbs					data->entries[idx].track =
229039213Sgibbs					    bcd2bin(data->entries[idx].track);
229139213Sgibbs				}
229239213Sgibbs			}
229339213Sgibbs
229439213Sgibbs			error = copyout(data->entries, te->data, len);
229539213Sgibbs			free(data, M_TEMP);
229639213Sgibbs			free(lead, M_TEMP);
229739213Sgibbs		}
229839213Sgibbs		break;
229939213Sgibbs	case CDIOREADTOCENTRY:
230039213Sgibbs		{
2301111206Sken			struct cd_toc_single *data;
230239213Sgibbs			struct ioc_read_toc_single_entry *te =
230339213Sgibbs				(struct ioc_read_toc_single_entry *) addr;
230439213Sgibbs			struct ioc_toc_header *th;
230539213Sgibbs			u_int32_t track;
230639213Sgibbs
230739213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
230839213Sgibbs				  ("trying to do CDIOREADTOCENTRY\n"));
230939213Sgibbs
2310111206Sken			data = malloc(sizeof(*data), M_TEMP, M_WAITOK);
231139213Sgibbs
231239213Sgibbs			if (te->address_format != CD_MSF_FORMAT
231339213Sgibbs			    && te->address_format != CD_LBA_FORMAT) {
231439213Sgibbs				printf("error in readtocentry, "
231539213Sgibbs				       " returning EINVAL\n");
231639213Sgibbs				free(data, M_TEMP);
231739213Sgibbs				error = EINVAL;
231839213Sgibbs				break;
231939213Sgibbs			}
232039213Sgibbs
232139213Sgibbs			th = &data->header;
2322111206Sken			error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
2323111206Sken					  sizeof (*th), /*sense_flags*/0);
232439213Sgibbs			if (error) {
232539213Sgibbs				free(data, M_TEMP);
232639213Sgibbs				break;
232739213Sgibbs			}
232839213Sgibbs
232939213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS) {
233039213Sgibbs				/* we are going to have to convert the BCD
233139213Sgibbs				 * encoding on the cd to what is expected
233239213Sgibbs				 */
233339213Sgibbs				th->starting_track =
233439213Sgibbs				    bcd2bin(th->starting_track);
233539213Sgibbs				th->ending_track = bcd2bin(th->ending_track);
233639213Sgibbs			}
233739213Sgibbs			track = te->track;
233839213Sgibbs			if (track == 0)
233939213Sgibbs				track = th->starting_track;
234039213Sgibbs			else if (track == LEADOUT)
234139213Sgibbs				/* OK */;
234239213Sgibbs			else if (track < th->starting_track ||
234339213Sgibbs				 track > th->ending_track + 1) {
234439213Sgibbs				printf("error in readtocentry, "
234539213Sgibbs				       " returning EINVAL\n");
234639213Sgibbs				free(data, M_TEMP);
234739213Sgibbs				error = EINVAL;
234839213Sgibbs				break;
234939213Sgibbs			}
235039213Sgibbs
235139213Sgibbs			error = cdreadtoc(periph, te->address_format, track,
2352111206Sken					  (u_int8_t *)data, sizeof(*data),
2353111206Sken					  /*sense_flags*/0);
235439213Sgibbs			if (error) {
235539213Sgibbs				free(data, M_TEMP);
235639213Sgibbs				break;
235739213Sgibbs			}
235839213Sgibbs
235939213Sgibbs			if (softc->quirks & CD_Q_BCD_TRACKS)
236039213Sgibbs				data->entry.track = bcd2bin(data->entry.track);
236139213Sgibbs			bcopy(&data->entry, &te->entry,
236239213Sgibbs			      sizeof(struct cd_toc_entry));
236339213Sgibbs			free(data, M_TEMP);
236439213Sgibbs		}
236539213Sgibbs		break;
236639213Sgibbs	case CDIOCSETPATCH:
236739213Sgibbs		{
2368111206Sken			struct ioc_patch *arg = (struct ioc_patch *)addr;
2369111206Sken			struct cd_mode_params params;
2370111206Sken			union cd_pages *page;
237139213Sgibbs
237239213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
237339213Sgibbs				  ("trying to do CDIOCSETPATCH\n"));
237439213Sgibbs
2375111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2376111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2377111206Sken						 M_WAITOK | M_ZERO);
2378111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
237939213Sgibbs			if (error) {
2380111206Sken				free(params.mode_buf, M_TEMP);
238139213Sgibbs				break;
238239213Sgibbs			}
2383111206Sken			page = cdgetpage(&params);
2384111206Sken
2385111206Sken			page->audio.port[LEFT_PORT].channels =
238639213Sgibbs				arg->patch[0];
2387111206Sken			page->audio.port[RIGHT_PORT].channels =
238839213Sgibbs				arg->patch[1];
2389111206Sken			page->audio.port[2].channels = arg->patch[2];
2390111206Sken			page->audio.port[3].channels = arg->patch[3];
2391111206Sken			error = cdsetmode(periph, &params);
2392111206Sken			free(params.mode_buf, M_TEMP);
239339213Sgibbs		}
239439213Sgibbs		break;
239539213Sgibbs	case CDIOCGETVOL:
239639213Sgibbs		{
239739213Sgibbs			struct ioc_vol *arg = (struct ioc_vol *) addr;
2398111206Sken			struct cd_mode_params params;
2399111206Sken			union cd_pages *page;
240039213Sgibbs
240139213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
240239213Sgibbs				  ("trying to do CDIOCGETVOL\n"));
240339213Sgibbs
2404111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2405111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2406111206Sken						 M_WAITOK | M_ZERO);
2407111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
240839213Sgibbs			if (error) {
2409111206Sken				free(params.mode_buf, M_TEMP);
241039213Sgibbs				break;
241139213Sgibbs			}
2412111206Sken			page = cdgetpage(&params);
2413111206Sken
241439213Sgibbs			arg->vol[LEFT_PORT] =
2415111206Sken				page->audio.port[LEFT_PORT].volume;
241639213Sgibbs			arg->vol[RIGHT_PORT] =
2417111206Sken				page->audio.port[RIGHT_PORT].volume;
2418111206Sken			arg->vol[2] = page->audio.port[2].volume;
2419111206Sken			arg->vol[3] = page->audio.port[3].volume;
2420111206Sken			free(params.mode_buf, M_TEMP);
242139213Sgibbs		}
242239213Sgibbs		break;
242339213Sgibbs	case CDIOCSETVOL:
242439213Sgibbs		{
242539213Sgibbs			struct ioc_vol *arg = (struct ioc_vol *) addr;
2426111206Sken			struct cd_mode_params params;
2427111206Sken			union cd_pages *page;
242839213Sgibbs
242939213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
243039213Sgibbs				  ("trying to do CDIOCSETVOL\n"));
243139213Sgibbs
2432111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2433111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2434111206Sken						 M_WAITOK | M_ZERO);
2435111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
243639213Sgibbs			if (error) {
2437111206Sken				free(params.mode_buf, M_TEMP);
243839213Sgibbs				break;
243939213Sgibbs			}
2440111206Sken			page = cdgetpage(&params);
2441111206Sken
2442111206Sken			page->audio.port[LEFT_PORT].channels = CHANNEL_0;
2443111206Sken			page->audio.port[LEFT_PORT].volume =
244439213Sgibbs				arg->vol[LEFT_PORT];
2445111206Sken			page->audio.port[RIGHT_PORT].channels = CHANNEL_1;
2446111206Sken			page->audio.port[RIGHT_PORT].volume =
244739213Sgibbs				arg->vol[RIGHT_PORT];
2448111206Sken			page->audio.port[2].volume = arg->vol[2];
2449111206Sken			page->audio.port[3].volume = arg->vol[3];
2450111206Sken			error = cdsetmode(periph, &params);
2451111206Sken			free(params.mode_buf, M_TEMP);
245239213Sgibbs		}
245339213Sgibbs		break;
245439213Sgibbs	case CDIOCSETMONO:
245539213Sgibbs		{
2456111206Sken			struct cd_mode_params params;
2457111206Sken			union cd_pages *page;
245839213Sgibbs
245939213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
246039213Sgibbs				  ("trying to do CDIOCSETMONO\n"));
246139213Sgibbs
2462111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2463111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2464111206Sken						 M_WAITOK | M_ZERO);
2465111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
246639213Sgibbs			if (error) {
2467111206Sken				free(params.mode_buf, M_TEMP);
246839213Sgibbs				break;
246939213Sgibbs			}
2470111206Sken			page = cdgetpage(&params);
2471111206Sken
2472111206Sken			page->audio.port[LEFT_PORT].channels =
247339213Sgibbs				LEFT_CHANNEL | RIGHT_CHANNEL;
2474111206Sken			page->audio.port[RIGHT_PORT].channels =
247539213Sgibbs				LEFT_CHANNEL | RIGHT_CHANNEL;
2476111206Sken			page->audio.port[2].channels = 0;
2477111206Sken			page->audio.port[3].channels = 0;
2478111206Sken			error = cdsetmode(periph, &params);
2479111206Sken			free(params.mode_buf, M_TEMP);
248039213Sgibbs		}
248139213Sgibbs		break;
248239213Sgibbs	case CDIOCSETSTEREO:
248339213Sgibbs		{
2484111206Sken			struct cd_mode_params params;
2485111206Sken			union cd_pages *page;
248639213Sgibbs
248739213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
248839213Sgibbs				  ("trying to do CDIOCSETSTEREO\n"));
248939213Sgibbs
2490111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2491111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2492111206Sken						 M_WAITOK | M_ZERO);
2493111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
249439213Sgibbs			if (error) {
2495111206Sken				free(params.mode_buf, M_TEMP);
249639213Sgibbs				break;
249739213Sgibbs			}
2498111206Sken			page = cdgetpage(&params);
2499111206Sken
2500111206Sken			page->audio.port[LEFT_PORT].channels =
250139213Sgibbs				LEFT_CHANNEL;
2502111206Sken			page->audio.port[RIGHT_PORT].channels =
250339213Sgibbs				RIGHT_CHANNEL;
2504111206Sken			page->audio.port[2].channels = 0;
2505111206Sken			page->audio.port[3].channels = 0;
2506111206Sken			error = cdsetmode(periph, &params);
2507111206Sken			free(params.mode_buf, M_TEMP);
250839213Sgibbs		}
250939213Sgibbs		break;
251039213Sgibbs	case CDIOCSETMUTE:
251139213Sgibbs		{
2512111206Sken			struct cd_mode_params params;
2513111206Sken			union cd_pages *page;
251439213Sgibbs
251539213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
251639213Sgibbs				  ("trying to do CDIOCSETMUTE\n"));
251739213Sgibbs
2518111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2519111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2520111206Sken						 M_WAITOK | M_ZERO);
2521111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
252239213Sgibbs			if (error) {
2523111206Sken				free(&params, M_TEMP);
252439213Sgibbs				break;
252539213Sgibbs			}
2526111206Sken			page = cdgetpage(&params);
2527111206Sken
2528111206Sken			page->audio.port[LEFT_PORT].channels = 0;
2529111206Sken			page->audio.port[RIGHT_PORT].channels = 0;
2530111206Sken			page->audio.port[2].channels = 0;
2531111206Sken			page->audio.port[3].channels = 0;
2532111206Sken			error = cdsetmode(periph, &params);
2533111206Sken			free(params.mode_buf, M_TEMP);
253439213Sgibbs		}
253539213Sgibbs		break;
253639213Sgibbs	case CDIOCSETLEFT:
253739213Sgibbs		{
2538111206Sken			struct cd_mode_params params;
2539111206Sken			union cd_pages *page;
254039213Sgibbs
254139213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
254239213Sgibbs				  ("trying to do CDIOCSETLEFT\n"));
254339213Sgibbs
2544111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2545111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2546111206Sken						 M_WAITOK | M_ZERO);
2547111206Sken
2548111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
254939213Sgibbs			if (error) {
2550111206Sken				free(params.mode_buf, M_TEMP);
255139213Sgibbs				break;
255239213Sgibbs			}
2553111206Sken			page = cdgetpage(&params);
2554111206Sken
2555111206Sken			page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
2556111206Sken			page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL;
2557111206Sken			page->audio.port[2].channels = 0;
2558111206Sken			page->audio.port[3].channels = 0;
2559111206Sken			error = cdsetmode(periph, &params);
2560111206Sken			free(params.mode_buf, M_TEMP);
256139213Sgibbs		}
256239213Sgibbs		break;
256339213Sgibbs	case CDIOCSETRIGHT:
256439213Sgibbs		{
2565111206Sken			struct cd_mode_params params;
2566111206Sken			union cd_pages *page;
256739213Sgibbs
256839213Sgibbs			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
256939213Sgibbs				  ("trying to do CDIOCSETRIGHT\n"));
257039213Sgibbs
2571111206Sken			params.alloc_len = sizeof(union cd_mode_data_6_10);
2572111206Sken			params.mode_buf = malloc(params.alloc_len, M_TEMP,
2573111206Sken						 M_WAITOK | M_ZERO);
2574111206Sken
2575111206Sken			error = cdgetmode(periph, &params, AUDIO_PAGE);
257639213Sgibbs			if (error) {
2577111206Sken				free(params.mode_buf, M_TEMP);
257839213Sgibbs				break;
257939213Sgibbs			}
2580111206Sken			page = cdgetpage(&params);
2581111206Sken
2582111206Sken			page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL;
2583111206Sken			page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
2584111206Sken			page->audio.port[2].channels = 0;
2585111206Sken			page->audio.port[3].channels = 0;
2586111206Sken			error = cdsetmode(periph, &params);
2587111206Sken			free(params.mode_buf, M_TEMP);
258839213Sgibbs		}
258939213Sgibbs		break;
259039213Sgibbs	case CDIOCRESUME:
259139213Sgibbs		error = cdpause(periph, 1);
259239213Sgibbs		break;
259339213Sgibbs	case CDIOCPAUSE:
259439213Sgibbs		error = cdpause(periph, 0);
259539213Sgibbs		break;
259639213Sgibbs	case CDIOCSTART:
2597111206Sken		error = cdstartunit(periph, 0);
259839213Sgibbs		break;
2599111206Sken	case CDIOCCLOSE:
2600111206Sken		error = cdstartunit(periph, 1);
2601111206Sken		break;
260239213Sgibbs	case CDIOCSTOP:
260339213Sgibbs		error = cdstopunit(periph, 0);
260439213Sgibbs		break;
260539213Sgibbs	case CDIOCEJECT:
260639213Sgibbs		error = cdstopunit(periph, 1);
260739213Sgibbs		break;
260839213Sgibbs	case CDIOCALLOW:
260939213Sgibbs		cdprevent(periph, PR_ALLOW);
261039213Sgibbs		break;
261139213Sgibbs	case CDIOCPREVENT:
261239213Sgibbs		cdprevent(periph, PR_PREVENT);
261339213Sgibbs		break;
261439213Sgibbs	case CDIOCSETDEBUG:
261539213Sgibbs		/* sc_link->flags |= (SDEV_DB1 | SDEV_DB2); */
261639213Sgibbs		error = ENOTTY;
261739213Sgibbs		break;
261839213Sgibbs	case CDIOCCLRDEBUG:
261939213Sgibbs		/* sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); */
262039213Sgibbs		error = ENOTTY;
262139213Sgibbs		break;
262239213Sgibbs	case CDIOCRESET:
262339213Sgibbs		/* return (cd_reset(periph)); */
262439213Sgibbs		error = ENOTTY;
262539213Sgibbs		break;
2626105421Snjl	case CDRIOCREADSPEED:
2627105421Snjl		error = cdsetspeed(periph, *(u_int32_t *)addr, CDR_MAX_SPEED);
2628105421Snjl		break;
2629105421Snjl	case CDRIOCWRITESPEED:
2630105421Snjl		error = cdsetspeed(periph, CDR_MAX_SPEED, *(u_int32_t *)addr);
2631105421Snjl		break;
263260422Sken	case DVDIOCSENDKEY:
263360422Sken	case DVDIOCREPORTKEY: {
263460422Sken		struct dvd_authinfo *authinfo;
263560422Sken
263660422Sken		authinfo = (struct dvd_authinfo *)addr;
263760422Sken
263860422Sken		if (cmd == DVDIOCREPORTKEY)
263960422Sken			error = cdreportkey(periph, authinfo);
264060422Sken		else
264160422Sken			error = cdsendkey(periph, authinfo);
264260422Sken		break;
2643104456Sphk		}
264460422Sken	case DVDIOCREADSTRUCTURE: {
264560422Sken		struct dvd_struct *dvdstruct;
264660422Sken
264760422Sken		dvdstruct = (struct dvd_struct *)addr;
264860422Sken
264960422Sken		error = cdreaddvdstructure(periph, dvdstruct);
265060422Sken
265160422Sken		break;
265260422Sken	}
265339213Sgibbs	default:
265439213Sgibbs		error = cam_periph_ioctl(periph, cmd, addr, cderror);
265539213Sgibbs		break;
265639213Sgibbs	}
265739213Sgibbs
265840020Sken	cam_periph_unlock(periph);
265940020Sken
266039213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdioctl\n"));
2661104456Sphk	if (error && bootverbose) {
2662104456Sphk		printf("scsi_cd.c::ioctl cmd=%08lx error=%d\n", cmd, error);
2663104456Sphk	}
266439213Sgibbs
266539213Sgibbs	return (error);
266639213Sgibbs}
266739213Sgibbs
266839213Sgibbsstatic void
266939213Sgibbscdprevent(struct cam_periph *periph, int action)
267039213Sgibbs{
267139213Sgibbs	union	ccb *ccb;
267239213Sgibbs	struct	cd_softc *softc;
267339213Sgibbs	int	error;
267439213Sgibbs
267539213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdprevent\n"));
267639213Sgibbs
267739213Sgibbs	softc = (struct cd_softc *)periph->softc;
267839213Sgibbs
267939213Sgibbs	if (((action == PR_ALLOW)
268039213Sgibbs	  && (softc->flags & CD_FLAG_DISC_LOCKED) == 0)
268139213Sgibbs	 || ((action == PR_PREVENT)
268239213Sgibbs	  && (softc->flags & CD_FLAG_DISC_LOCKED) != 0)) {
268339213Sgibbs		return;
268439213Sgibbs	}
268539213Sgibbs
268639213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
268739213Sgibbs
268839213Sgibbs	scsi_prevent(&ccb->csio,
268939213Sgibbs		     /*retries*/ 1,
269039213Sgibbs		     cddone,
269139213Sgibbs		     MSG_SIMPLE_Q_TAG,
269239213Sgibbs		     action,
269339213Sgibbs		     SSD_FULL_SIZE,
269439213Sgibbs		     /* timeout */60000);
269539213Sgibbs
269674840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
269774840Sken			/*sense_flags*/SF_RETRY_UA|SF_NO_PRINT);
269839213Sgibbs
269939213Sgibbs	xpt_release_ccb(ccb);
270039213Sgibbs
270139213Sgibbs	if (error == 0) {
270239213Sgibbs		if (action == PR_ALLOW)
270339213Sgibbs			softc->flags &= ~CD_FLAG_DISC_LOCKED;
270439213Sgibbs		else
270539213Sgibbs			softc->flags |= CD_FLAG_DISC_LOCKED;
270639213Sgibbs	}
270739213Sgibbs}
270839213Sgibbs
2709120599Sphk/*
2710120599Sphk * XXX: the disk media and sector size is only really able to change
2711120599Sphk * XXX: while the device is closed.
2712120599Sphk */
271339213Sgibbsstatic int
2714111206Skencdcheckmedia(struct cam_periph *periph)
271539213Sgibbs{
271639213Sgibbs	struct cd_softc *softc;
2717111206Sken	struct ioc_toc_header *toch;
2718111206Sken	struct cd_toc_single leadout;
2719111206Sken	u_int32_t size, toclen;
2720111206Sken	int error, num_entries, cdindex;
2721111206Sken
2722111206Sken	softc = (struct cd_softc *)periph->softc;
2723111206Sken
2724111206Sken	cdprevent(periph, PR_PREVENT);
2725125975Sphk	softc->disk->d_maxsize = DFLTPHYS;
2726125975Sphk	softc->disk->d_sectorsize = 0;
2727125975Sphk	softc->disk->d_mediasize = 0;
2728111206Sken
2729111206Sken	/*
2730111206Sken	 * Get the disc size and block size.  If we can't get it, we don't
2731111206Sken	 * have media, most likely.
2732111206Sken	 */
2733111206Sken	if ((error = cdsize(periph, &size)) != 0) {
2734111206Sken		softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC);
2735111206Sken		cdprevent(periph, PR_ALLOW);
2736111206Sken		return (error);
2737111206Sken	} else
2738111206Sken		softc->flags |= CD_FLAG_VALID_MEDIA;
2739111206Sken
2740111206Sken	/*
2741111206Sken	 * Now we check the table of contents.  This (currently) is only
2742111206Sken	 * used for the CDIOCPLAYTRACKS ioctl.  It may be used later to do
2743111206Sken	 * things like present a separate entry in /dev for each track,
2744111206Sken	 * like that acd(4) driver does.
2745111206Sken	 */
2746111206Sken	bzero(&softc->toc, sizeof(softc->toc));
2747111206Sken	toch = &softc->toc.header;
2748111206Sken	/*
2749111206Sken	 * We will get errors here for media that doesn't have a table of
2750111206Sken	 * contents.  According to the MMC-3 spec: "When a Read TOC/PMA/ATIP
2751111206Sken	 * command is presented for a DDCD/CD-R/RW media, where the first TOC
2752111206Sken	 * has not been recorded (no complete session) and the Format codes
2753111206Sken	 * 0000b, 0001b, or 0010b are specified, this command shall be rejected
2754111206Sken	 * with an INVALID FIELD IN CDB.  Devices that are not capable of
2755111206Sken	 * reading an incomplete session on DDC/CD-R/RW media shall report
2756111206Sken	 * CANNOT READ MEDIUM - INCOMPATIBLE FORMAT."
2757111206Sken	 *
2758111206Sken	 * So this isn't fatal if we can't read the table of contents, it
2759111206Sken	 * just means that the user won't be able to issue the play tracks
2760111206Sken	 * ioctl, and likely lots of other stuff won't work either.  They
2761111206Sken	 * need to burn the CD before we can do a whole lot with it.  So
2762111206Sken	 * we don't print anything here if we get an error back.
2763111206Sken	 */
2764111206Sken	error = cdreadtoc(periph, 0, 0, (u_int8_t *)toch, sizeof(*toch),
2765111206Sken			  SF_NO_PRINT);
2766111206Sken	/*
2767111206Sken	 * Errors in reading the table of contents aren't fatal, we just
2768111206Sken	 * won't have a valid table of contents cached.
2769111206Sken	 */
2770111206Sken	if (error != 0) {
2771111206Sken		error = 0;
2772111206Sken		bzero(&softc->toc, sizeof(softc->toc));
2773111206Sken		goto bailout;
2774111206Sken	}
2775111206Sken
2776111206Sken	if (softc->quirks & CD_Q_BCD_TRACKS) {
2777111206Sken		toch->starting_track = bcd2bin(toch->starting_track);
2778111206Sken		toch->ending_track = bcd2bin(toch->ending_track);
2779111206Sken	}
2780111206Sken
2781111206Sken	/* Number of TOC entries, plus leadout */
2782111206Sken	num_entries = (toch->ending_track - toch->starting_track) + 2;
2783111206Sken
2784111206Sken	if (num_entries <= 0)
2785111206Sken		goto bailout;
2786111206Sken
2787111206Sken	toclen = num_entries * sizeof(struct cd_toc_entry);
2788111206Sken
2789111206Sken	error = cdreadtoc(periph, CD_MSF_FORMAT, toch->starting_track,
2790111206Sken			  (u_int8_t *)&softc->toc, toclen + sizeof(*toch),
2791111206Sken			  SF_NO_PRINT);
2792111206Sken	if (error != 0) {
2793111206Sken		error = 0;
2794111206Sken		bzero(&softc->toc, sizeof(softc->toc));
2795111206Sken		goto bailout;
2796111206Sken	}
2797111206Sken
2798111206Sken	if (softc->quirks & CD_Q_BCD_TRACKS) {
2799111206Sken		toch->starting_track = bcd2bin(toch->starting_track);
2800111206Sken		toch->ending_track = bcd2bin(toch->ending_track);
2801111206Sken	}
2802111206Sken	/*
2803111206Sken	 * XXX KDM is this necessary?  Probably only if the drive doesn't
2804111206Sken	 * return leadout information with the table of contents.
2805111206Sken	 */
2806111206Sken	cdindex = toch->starting_track + num_entries -1;
2807111206Sken	if (cdindex == toch->ending_track + 1) {
2808111206Sken
2809111206Sken		error = cdreadtoc(periph, CD_MSF_FORMAT, LEADOUT,
2810111206Sken				  (u_int8_t *)&leadout, sizeof(leadout),
2811111206Sken				  SF_NO_PRINT);
2812111206Sken		if (error != 0) {
2813111206Sken			error = 0;
2814111206Sken			goto bailout;
2815111206Sken		}
2816111206Sken		softc->toc.entries[cdindex - toch->starting_track] =
2817111206Sken			leadout.entry;
2818111206Sken	}
2819111206Sken	if (softc->quirks & CD_Q_BCD_TRACKS) {
2820111206Sken		for (cdindex = 0; cdindex < num_entries - 1; cdindex++) {
2821111206Sken			softc->toc.entries[cdindex].track =
2822111206Sken				bcd2bin(softc->toc.entries[cdindex].track);
2823111206Sken		}
2824111206Sken	}
2825111206Sken
2826111206Sken	softc->flags |= CD_FLAG_VALID_TOC;
2827125975Sphk	softc->disk->d_maxsize = DFLTPHYS;
2828125975Sphk	softc->disk->d_sectorsize = softc->params.blksize;
2829125975Sphk	softc->disk->d_mediasize =
2830120599Sphk	    (off_t)softc->params.blksize * softc->params.disksize;
2831111206Sken
2832111206Skenbailout:
2833111206Sken
2834111206Sken	/*
2835111206Sken	 * We unconditionally (re)set the blocksize each time the
2836111206Sken	 * CD device is opened.  This is because the CD can change,
2837111206Sken	 * and therefore the blocksize might change.
2838111206Sken	 * XXX problems here if some slice or partition is still
2839111206Sken	 * open with the old size?
2840111206Sken	 */
2841125975Sphk	if ((softc->disk->d_devstat->flags & DEVSTAT_BS_UNAVAILABLE) != 0)
2842125975Sphk		softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE;
2843125975Sphk	softc->disk->d_devstat->block_size = softc->params.blksize;
2844111206Sken
2845111206Sken	return (error);
2846111206Sken}
2847111206Sken
2848111206Skenstatic int
2849111206Skencdsize(struct cam_periph *periph, u_int32_t *size)
2850111206Sken{
2851111206Sken	struct cd_softc *softc;
285239213Sgibbs	union ccb *ccb;
285339213Sgibbs	struct scsi_read_capacity_data *rcap_buf;
285439213Sgibbs	int error;
285539213Sgibbs
285639213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n"));
285739213Sgibbs
285839213Sgibbs	softc = (struct cd_softc *)periph->softc;
285939213Sgibbs
286039213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
286139213Sgibbs
286239213Sgibbs	rcap_buf = malloc(sizeof(struct scsi_read_capacity_data),
2863111119Simp			  M_TEMP, M_WAITOK);
286439213Sgibbs
286539213Sgibbs	scsi_read_capacity(&ccb->csio,
286639213Sgibbs			   /*retries*/ 1,
286739213Sgibbs			   cddone,
286839213Sgibbs			   MSG_SIMPLE_Q_TAG,
286939213Sgibbs			   rcap_buf,
287039213Sgibbs			   SSD_FULL_SIZE,
287139213Sgibbs			   /* timeout */20000);
287239213Sgibbs
287374840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
287474840Sken			 /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT);
287539213Sgibbs
287639213Sgibbs	xpt_release_ccb(ccb);
287739213Sgibbs
287839213Sgibbs	softc->params.disksize = scsi_4btoul(rcap_buf->addr) + 1;
287939213Sgibbs	softc->params.blksize  = scsi_4btoul(rcap_buf->length);
288060806Sjoerg	/*
288160806Sjoerg	 * SCSI-3 mandates that the reported blocksize shall be 2048.
288260806Sjoerg	 * Older drives sometimes report funny values, trim it down to
288360806Sjoerg	 * 2048, or other parts of the kernel will get confused.
288460806Sjoerg	 *
288560806Sjoerg	 * XXX we leave drives alone that might report 512 bytes, as
288660806Sjoerg	 * well as drives reporting more weird sizes like perhaps 4K.
288760806Sjoerg	 */
288860806Sjoerg	if (softc->params.blksize > 2048 && softc->params.blksize <= 2352)
288960806Sjoerg		softc->params.blksize = 2048;
289039213Sgibbs
289139213Sgibbs	free(rcap_buf, M_TEMP);
289239213Sgibbs	*size = softc->params.disksize;
289339213Sgibbs
289439213Sgibbs	return (error);
289539213Sgibbs
289639213Sgibbs}
289739213Sgibbs
289839213Sgibbsstatic int
2899111206Skencd6byteworkaround(union ccb *ccb)
2900111206Sken{
2901111206Sken	u_int8_t *cdb;
2902111206Sken	struct cam_periph *periph;
2903111206Sken	struct cd_softc *softc;
2904111206Sken	struct cd_mode_params *params;
2905111206Sken	int frozen, found;
2906111206Sken
2907111206Sken	periph = xpt_path_periph(ccb->ccb_h.path);
2908111206Sken	softc = (struct cd_softc *)periph->softc;
2909111206Sken
2910111206Sken	cdb = ccb->csio.cdb_io.cdb_bytes;
2911111206Sken
2912111206Sken	if ((ccb->ccb_h.flags & CAM_CDB_POINTER)
2913111206Sken	 || ((cdb[0] != MODE_SENSE_6)
2914111206Sken	  && (cdb[0] != MODE_SELECT_6)))
2915111206Sken		return (0);
2916111206Sken
2917111206Sken	/*
2918111206Sken	 * Because there is no convenient place to stash the overall
2919111206Sken	 * cd_mode_params structure pointer, we have to grab it like this.
2920111206Sken	 * This means that ALL MODE_SENSE and MODE_SELECT requests in the
2921111206Sken	 * cd(4) driver MUST go through cdgetmode() and cdsetmode()!
2922111206Sken	 *
2923111206Sken	 * XXX It would be nice if, at some point, we could increase the
2924111206Sken	 * number of available peripheral private pointers.  Both pointers
2925111206Sken	 * are currently used in most every peripheral driver.
2926111206Sken	 */
2927111206Sken	found = 0;
2928111206Sken
2929111206Sken	STAILQ_FOREACH(params, &softc->mode_queue, links) {
2930111206Sken		if (params->mode_buf == ccb->csio.data_ptr) {
2931111206Sken			found = 1;
2932111206Sken			break;
2933111206Sken		}
2934111206Sken	}
2935111206Sken
2936111206Sken	/*
2937111206Sken	 * This shouldn't happen.  All mode sense and mode select
2938111206Sken	 * operations in the cd(4) driver MUST go through cdgetmode() and
2939111206Sken	 * cdsetmode()!
2940111206Sken	 */
2941111206Sken	if (found == 0) {
2942111206Sken		xpt_print_path(periph->path);
2943111206Sken		printf("mode buffer not found in mode queue!\n");
2944111206Sken		return (0);
2945111206Sken	}
2946111206Sken
2947111206Sken	params->cdb_size = 10;
2948111206Sken	softc->minimum_command_size = 10;
2949111206Sken	xpt_print_path(ccb->ccb_h.path);
2950111206Sken	printf("%s(6) failed, increasing minimum CDB size to 10 bytes\n",
2951111206Sken	       (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT");
2952111206Sken
2953111206Sken	if (cdb[0] == MODE_SENSE_6) {
2954111206Sken		struct scsi_mode_sense_10 ms10;
2955111206Sken		struct scsi_mode_sense_6 *ms6;
2956111206Sken		int len;
2957111206Sken
2958111206Sken		ms6 = (struct scsi_mode_sense_6 *)cdb;
2959111206Sken
2960111206Sken		bzero(&ms10, sizeof(ms10));
2961111206Sken 		ms10.opcode = MODE_SENSE_10;
2962111206Sken 		ms10.byte2 = ms6->byte2;
2963111206Sken 		ms10.page = ms6->page;
2964111206Sken
2965111206Sken		/*
2966111206Sken		 * 10 byte mode header, block descriptor,
2967111206Sken		 * sizeof(union cd_pages)
2968111206Sken		 */
2969111206Sken		len = sizeof(struct cd_mode_data_10);
2970111206Sken		ccb->csio.dxfer_len = len;
2971111206Sken
2972111206Sken		scsi_ulto2b(len, ms10.length);
2973111206Sken		ms10.control = ms6->control;
2974111206Sken		bcopy(&ms10, cdb, 10);
2975111206Sken		ccb->csio.cdb_len = 10;
2976111206Sken	} else {
2977111206Sken		struct scsi_mode_select_10 ms10;
2978111206Sken		struct scsi_mode_select_6 *ms6;
2979111206Sken		struct scsi_mode_header_6 *header6;
2980111206Sken		struct scsi_mode_header_10 *header10;
2981111206Sken		struct scsi_mode_page_header *page_header;
2982111206Sken		int blk_desc_len, page_num, page_size, len;
2983111206Sken
2984111206Sken		ms6 = (struct scsi_mode_select_6 *)cdb;
2985111206Sken
2986111206Sken		bzero(&ms10, sizeof(ms10));
2987111206Sken		ms10.opcode = MODE_SELECT_10;
2988111206Sken		ms10.byte2 = ms6->byte2;
2989111206Sken
2990111206Sken		header6 = (struct scsi_mode_header_6 *)params->mode_buf;
2991111206Sken		header10 = (struct scsi_mode_header_10 *)params->mode_buf;
2992111206Sken
2993111206Sken		page_header = find_mode_page_6(header6);
2994111206Sken		page_num = page_header->page_code;
2995111206Sken
2996111206Sken		blk_desc_len = header6->blk_desc_len;
2997111206Sken
2998111206Sken		page_size = cdgetpagesize(page_num);
2999111206Sken
3000111206Sken		if (page_size != (page_header->page_length +
3001111206Sken		    sizeof(*page_header)))
3002111206Sken			page_size = page_header->page_length +
3003111206Sken				sizeof(*page_header);
3004111206Sken
3005111206Sken		len = sizeof(*header10) + blk_desc_len + page_size;
3006111206Sken
3007111206Sken		len = min(params->alloc_len, len);
3008111206Sken
3009111206Sken		/*
3010111206Sken		 * Since the 6 byte parameter header is shorter than the 10
3011111206Sken		 * byte parameter header, we need to copy the actual mode
3012111206Sken		 * page data, and the block descriptor, if any, so things wind
3013111206Sken		 * up in the right place.  The regions will overlap, but
3014111206Sken		 * bcopy() does the right thing.
3015111206Sken		 */
3016111206Sken		bcopy(params->mode_buf + sizeof(*header6),
3017111206Sken		      params->mode_buf + sizeof(*header10),
3018111206Sken		      len - sizeof(*header10));
3019111206Sken
3020111206Sken		/* Make sure these fields are set correctly. */
3021111206Sken		scsi_ulto2b(0, header10->data_length);
3022111206Sken		header10->medium_type = 0;
3023111206Sken		scsi_ulto2b(blk_desc_len, header10->blk_desc_len);
3024111206Sken
3025111206Sken		ccb->csio.dxfer_len = len;
3026111206Sken
3027111206Sken		scsi_ulto2b(len, ms10.length);
3028111206Sken		ms10.control = ms6->control;
3029111206Sken		bcopy(&ms10, cdb, 10);
3030111206Sken		ccb->csio.cdb_len = 10;
3031111206Sken	}
3032111206Sken
3033111206Sken	frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0;
3034111206Sken	ccb->ccb_h.status = CAM_REQUEUE_REQ;
3035111206Sken	xpt_action(ccb);
3036111206Sken	if (frozen) {
3037111206Sken		cam_release_devq(ccb->ccb_h.path,
3038111206Sken				 /*relsim_flags*/0,
3039111206Sken				 /*openings*/0,
3040111206Sken				 /*timeout*/0,
3041111206Sken				 /*getcount_only*/0);
3042111206Sken	}
3043111206Sken
3044111206Sken	return (ERESTART);
3045111206Sken}
3046111206Sken
3047111206Skenstatic int
304839213Sgibbscderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
304939213Sgibbs{
305039213Sgibbs	struct cd_softc *softc;
305139213Sgibbs	struct cam_periph *periph;
3052111206Sken	int error;
305339213Sgibbs
305439213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
305539213Sgibbs	softc = (struct cd_softc *)periph->softc;
305639213Sgibbs
3057111206Sken	error = 0;
3058111206Sken
305939514Sgibbs	/*
3060111206Sken	 * We use a status of CAM_REQ_INVALID as shorthand -- if a 6 byte
3061111206Sken	 * CDB comes back with this particular error, try transforming it
3062111206Sken	 * into the 10 byte version.
3063111206Sken	 */
3064111206Sken	if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) {
3065111206Sken		error = cd6byteworkaround(ccb);
3066111206Sken	} else if (((ccb->ccb_h.status & CAM_STATUS_MASK) ==
3067111206Sken		     CAM_SCSI_STATUS_ERROR)
3068111206Sken	 && (ccb->ccb_h.status & CAM_AUTOSNS_VALID)
3069111206Sken	 && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)
3070111206Sken	 && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0)
3071111206Sken	 && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) {
3072111206Sken		int sense_key, error_code, asc, ascq;
3073111206Sken
3074111206Sken 		scsi_extract_sense(&ccb->csio.sense_data,
3075111206Sken				   &error_code, &sense_key, &asc, &ascq);
3076111206Sken		if (sense_key == SSD_KEY_ILLEGAL_REQUEST)
3077111206Sken 			error = cd6byteworkaround(ccb);
3078111206Sken	}
3079111206Sken
3080111206Sken	if (error == ERESTART)
3081111206Sken		return (error);
3082111206Sken
3083111206Sken	/*
308439514Sgibbs	 * XXX
308539514Sgibbs	 * Until we have a better way of doing pack validation,
308639514Sgibbs	 * don't treat UAs as errors.
308739514Sgibbs	 */
308839514Sgibbs	sense_flags |= SF_RETRY_UA;
308939213Sgibbs	return (cam_periph_error(ccb, cam_flags, sense_flags,
309039213Sgibbs				 &softc->saved_ccb));
309139213Sgibbs}
309239213Sgibbs
309339213Sgibbs/*
309439213Sgibbs * Read table of contents
309539213Sgibbs */
309639213Sgibbsstatic int
309739213Sgibbscdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
3098111206Sken	  u_int8_t *data, u_int32_t len, u_int32_t sense_flags)
309939213Sgibbs{
310039213Sgibbs	struct scsi_read_toc *scsi_cmd;
310139213Sgibbs	u_int32_t ntoc;
310239213Sgibbs        struct ccb_scsiio *csio;
310339213Sgibbs	union ccb *ccb;
310439213Sgibbs	int error;
310539213Sgibbs
310639213Sgibbs	ntoc = len;
310739213Sgibbs	error = 0;
310839213Sgibbs
310939213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
311039213Sgibbs
311139213Sgibbs	csio = &ccb->csio;
311239213Sgibbs
311339213Sgibbs	cam_fill_csio(csio,
311439213Sgibbs		      /* retries */ 1,
311539213Sgibbs		      /* cbfcnp */ cddone,
311639213Sgibbs		      /* flags */ CAM_DIR_IN,
311739213Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
3118111206Sken		      /* data_ptr */ data,
311939213Sgibbs		      /* dxfer_len */ len,
312039213Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
312139213Sgibbs		      sizeof(struct scsi_read_toc),
312239213Sgibbs 		      /* timeout */ 50000);
312339213Sgibbs
312439213Sgibbs	scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes;
312539213Sgibbs	bzero (scsi_cmd, sizeof(*scsi_cmd));
312639213Sgibbs
312739213Sgibbs	if (mode == CD_MSF_FORMAT)
312839213Sgibbs		scsi_cmd->byte2 |= CD_MSF;
312939213Sgibbs	scsi_cmd->from_track = start;
313039213Sgibbs	/* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */
313139213Sgibbs	scsi_cmd->data_len[0] = (ntoc) >> 8;
313239213Sgibbs	scsi_cmd->data_len[1] = (ntoc) & 0xff;
313339213Sgibbs
313439213Sgibbs	scsi_cmd->op_code = READ_TOC;
313539213Sgibbs
313674840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
3137111206Sken			 /*sense_flags*/SF_RETRY_UA | sense_flags);
313839213Sgibbs
313939213Sgibbs	xpt_release_ccb(ccb);
314039213Sgibbs
314139213Sgibbs	return(error);
314239213Sgibbs}
314339213Sgibbs
314439213Sgibbsstatic int
314539213Sgibbscdreadsubchannel(struct cam_periph *periph, u_int32_t mode,
314639213Sgibbs		 u_int32_t format, int track,
314739213Sgibbs		 struct cd_sub_channel_info *data, u_int32_t len)
314839213Sgibbs{
314939213Sgibbs	struct scsi_read_subchannel *scsi_cmd;
315039213Sgibbs        struct ccb_scsiio *csio;
315139213Sgibbs	union ccb *ccb;
315239213Sgibbs	int error;
315339213Sgibbs
315439213Sgibbs	error = 0;
315539213Sgibbs
315639213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
315739213Sgibbs
315839213Sgibbs	csio = &ccb->csio;
315939213Sgibbs
316039213Sgibbs	cam_fill_csio(csio,
316139213Sgibbs		      /* retries */ 1,
316239213Sgibbs		      /* cbfcnp */ cddone,
316339213Sgibbs		      /* flags */ CAM_DIR_IN,
316439213Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
316539213Sgibbs		      /* data_ptr */ (u_int8_t *)data,
316639213Sgibbs		      /* dxfer_len */ len,
316739213Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
316839213Sgibbs		      sizeof(struct scsi_read_subchannel),
316939213Sgibbs 		      /* timeout */ 50000);
317039213Sgibbs
317139213Sgibbs	scsi_cmd = (struct scsi_read_subchannel *)&csio->cdb_io.cdb_bytes;
317239213Sgibbs	bzero (scsi_cmd, sizeof(*scsi_cmd));
317339213Sgibbs
317439213Sgibbs	scsi_cmd->op_code = READ_SUBCHANNEL;
317539213Sgibbs	if (mode == CD_MSF_FORMAT)
317639213Sgibbs		scsi_cmd->byte1 |= CD_MSF;
317739213Sgibbs	scsi_cmd->byte2 = SRS_SUBQ;
317839213Sgibbs	scsi_cmd->subchan_format = format;
317939213Sgibbs	scsi_cmd->track = track;
318039213Sgibbs	scsi_ulto2b(len, (u_int8_t *)scsi_cmd->data_len);
318139213Sgibbs	scsi_cmd->control = 0;
318239213Sgibbs
318374840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
318474840Sken			 /*sense_flags*/SF_RETRY_UA);
318539213Sgibbs
318639213Sgibbs	xpt_release_ccb(ccb);
318739213Sgibbs
318839213Sgibbs	return(error);
318939213Sgibbs}
319039213Sgibbs
319139213Sgibbs
3192111206Sken/*
3193111206Sken * All MODE_SENSE requests in the cd(4) driver MUST go through this
3194111206Sken * routine.  See comments in cd6byteworkaround() for details.
3195111206Sken */
319639213Sgibbsstatic int
3197111206Skencdgetmode(struct cam_periph *periph, struct cd_mode_params *data,
3198111206Sken	  u_int32_t page)
319939213Sgibbs{
3200111206Sken	struct ccb_scsiio *csio;
3201111206Sken	struct cd_softc *softc;
320239213Sgibbs	union ccb *ccb;
3203111206Sken	int param_len;
320439213Sgibbs	int error;
320539213Sgibbs
3206111206Sken	softc = (struct cd_softc *)periph->softc;
3207111206Sken
320839213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
320939213Sgibbs
321039213Sgibbs	csio = &ccb->csio;
321139213Sgibbs
3212111206Sken	data->cdb_size = softc->minimum_command_size;
3213111206Sken	if (data->cdb_size < 10)
3214111206Sken		param_len = sizeof(struct cd_mode_data);
3215111206Sken	else
3216111206Sken		param_len = sizeof(struct cd_mode_data_10);
321739213Sgibbs
3218111206Sken	/* Don't say we've got more room than we actually allocated */
3219111206Sken	param_len = min(param_len, data->alloc_len);
322039213Sgibbs
3221111206Sken	scsi_mode_sense_len(csio,
3222111206Sken			    /* retries */ 1,
3223111206Sken			    /* cbfcnp */ cddone,
3224111206Sken			    /* tag_action */ MSG_SIMPLE_Q_TAG,
3225111206Sken			    /* dbd */ 0,
3226111206Sken			    /* page_code */ SMS_PAGE_CTRL_CURRENT,
3227111206Sken			    /* page */ page,
3228111206Sken			    /* param_buf */ data->mode_buf,
3229111206Sken			    /* param_len */ param_len,
3230111206Sken			    /* minimum_cmd_size */ softc->minimum_command_size,
3231111206Sken			    /* sense_len */ SSD_FULL_SIZE,
3232111206Sken			    /* timeout */ 50000);
323339213Sgibbs
3234111206Sken	/*
3235111206Sken	 * It would be nice not to have to do this, but there's no
3236111206Sken	 * available pointer in the CCB that would allow us to stuff the
3237111206Sken	 * mode params structure in there and retrieve it in
3238111206Sken	 * cd6byteworkaround(), so we can set the cdb size.  The cdb size
3239111206Sken	 * lets the caller know what CDB size we ended up using, so they
3240111206Sken	 * can find the actual mode page offset.
3241111206Sken	 */
3242111206Sken	STAILQ_INSERT_TAIL(&softc->mode_queue, data, links);
3243111206Sken
324474840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
324574840Sken			 /*sense_flags*/SF_RETRY_UA);
324639213Sgibbs
324739213Sgibbs	xpt_release_ccb(ccb);
324839213Sgibbs
3249111206Sken	STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links);
3250111206Sken
3251111206Sken	/*
3252111206Sken	 * This is a bit of belt-and-suspenders checking, but if we run
3253111206Sken	 * into a situation where the target sends back multiple block
3254111206Sken	 * descriptors, we might not have enough space in the buffer to
3255111206Sken	 * see the whole mode page.  Better to return an error than
3256111206Sken	 * potentially access memory beyond our malloced region.
3257111206Sken	 */
3258111206Sken	if (error == 0) {
3259111206Sken		u_int32_t data_len;
3260111206Sken
3261111206Sken		if (data->cdb_size == 10) {
3262111206Sken			struct scsi_mode_header_10 *hdr10;
3263111206Sken
3264111206Sken			hdr10 = (struct scsi_mode_header_10 *)data->mode_buf;
3265111206Sken			data_len = scsi_2btoul(hdr10->data_length);
3266111206Sken			data_len += sizeof(hdr10->data_length);
3267111206Sken		} else {
3268111206Sken			struct scsi_mode_header_6 *hdr6;
3269111206Sken
3270111206Sken			hdr6 = (struct scsi_mode_header_6 *)data->mode_buf;
3271111206Sken			data_len = hdr6->data_length;
3272111206Sken			data_len += sizeof(hdr6->data_length);
3273111206Sken		}
3274111206Sken
3275111206Sken		/*
3276111206Sken		 * Complain if there is more mode data available than we
3277111206Sken		 * allocated space for.  This could potentially happen if
3278111206Sken		 * we miscalculated the page length for some reason, if the
3279111206Sken		 * drive returns multiple block descriptors, or if it sets
3280111206Sken		 * the data length incorrectly.
3281111206Sken		 */
3282111206Sken		if (data_len > data->alloc_len) {
3283111206Sken			xpt_print_path(periph->path);
3284111206Sken			printf("allocated modepage %d length %d < returned "
3285111206Sken			       "length %d\n", page, data->alloc_len, data_len);
3286111206Sken
3287111206Sken			error = ENOSPC;
3288111206Sken		}
3289111206Sken	}
3290111206Sken	return (error);
329139213Sgibbs}
329239213Sgibbs
3293111206Sken/*
3294111206Sken * All MODE_SELECT requests in the cd(4) driver MUST go through this
3295111206Sken * routine.  See comments in cd6byteworkaround() for details.
3296111206Sken */
329739213Sgibbsstatic int
3298111206Skencdsetmode(struct cam_periph *periph, struct cd_mode_params *data)
329939213Sgibbs{
3300111206Sken	struct ccb_scsiio *csio;
3301111206Sken	struct cd_softc *softc;
330239213Sgibbs	union ccb *ccb;
3303111206Sken	int cdb_size, param_len;
330439213Sgibbs	int error;
330539213Sgibbs
3306111206Sken	softc = (struct cd_softc *)periph->softc;
3307111206Sken
330839213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
330939213Sgibbs
331039213Sgibbs	csio = &ccb->csio;
331139213Sgibbs
331239213Sgibbs	error = 0;
331339213Sgibbs
331439213Sgibbs	/*
3315111206Sken	 * If the data is formatted for the 10 byte version of the mode
3316111206Sken	 * select parameter list, we need to use the 10 byte CDB.
3317111206Sken	 * Otherwise, we use whatever the stored minimum command size.
331839213Sgibbs	 */
3319111206Sken	if (data->cdb_size == 10)
3320111206Sken		cdb_size = data->cdb_size;
3321111206Sken	else
3322111206Sken		cdb_size = softc->minimum_command_size;
332339213Sgibbs
3324111206Sken	if (cdb_size >= 10) {
3325111206Sken		struct scsi_mode_header_10 *mode_header;
3326111206Sken		u_int32_t data_len;
3327111206Sken
3328111206Sken		mode_header = (struct scsi_mode_header_10 *)data->mode_buf;
3329111206Sken
3330111206Sken		data_len = scsi_2btoul(mode_header->data_length);
3331111206Sken
3332111206Sken		scsi_ulto2b(0, mode_header->data_length);
3333111206Sken		/*
3334111206Sken		 * SONY drives do not allow a mode select with a medium_type
3335111206Sken		 * value that has just been returned by a mode sense; use a
3336111206Sken		 * medium_type of 0 (Default) instead.
3337111206Sken		 */
3338111206Sken		mode_header->medium_type = 0;
3339111206Sken
3340111206Sken		/*
3341111206Sken		 * Pass back whatever the drive passed to us, plus the size
3342111206Sken		 * of the data length field.
3343111206Sken		 */
3344111206Sken		param_len = data_len + sizeof(mode_header->data_length);
3345111206Sken
3346111206Sken	} else {
3347111206Sken		struct scsi_mode_header_6 *mode_header;
3348111206Sken
3349111206Sken		mode_header = (struct scsi_mode_header_6 *)data->mode_buf;
3350111206Sken
3351111206Sken		param_len = mode_header->data_length + 1;
3352111206Sken
3353111206Sken		mode_header->data_length = 0;
3354111206Sken		/*
3355111206Sken		 * SONY drives do not allow a mode select with a medium_type
3356111206Sken		 * value that has just been returned by a mode sense; use a
3357111206Sken		 * medium_type of 0 (Default) instead.
3358111206Sken		 */
3359111206Sken		mode_header->medium_type = 0;
3360111206Sken	}
3361111206Sken
3362111206Sken	/* Don't say we've got more room than we actually allocated */
3363111206Sken	param_len = min(param_len, data->alloc_len);
3364111206Sken
3365111206Sken	scsi_mode_select_len(csio,
3366111206Sken			     /* retries */ 1,
3367111206Sken			     /* cbfcnp */ cddone,
3368111206Sken			     /* tag_action */ MSG_SIMPLE_Q_TAG,
3369111206Sken			     /* scsi_page_fmt */ 1,
3370111206Sken			     /* save_pages */ 0,
3371111206Sken			     /* param_buf */ data->mode_buf,
3372111206Sken			     /* param_len */ param_len,
3373111206Sken			     /* minimum_cmd_size */ cdb_size,
3374111206Sken			     /* sense_len */ SSD_FULL_SIZE,
3375111206Sken			     /* timeout */ 50000);
3376111206Sken
3377111206Sken	/* See comments in cdgetmode() and cd6byteworkaround(). */
3378111206Sken	STAILQ_INSERT_TAIL(&softc->mode_queue, data, links);
3379111206Sken
338074840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
338174840Sken			 /*sense_flags*/SF_RETRY_UA);
338239213Sgibbs
338339213Sgibbs	xpt_release_ccb(ccb);
338439213Sgibbs
3385111206Sken	STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links);
3386111206Sken
3387111206Sken	return (error);
338839213Sgibbs}
338939213Sgibbs
339039213Sgibbs
339139213Sgibbsstatic int
339239213Sgibbscdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len)
339339213Sgibbs{
339439531Sken	struct ccb_scsiio *csio;
339539213Sgibbs	union ccb *ccb;
339639213Sgibbs	int error;
339739531Sken	u_int8_t cdb_len;
339839213Sgibbs
339939213Sgibbs	error = 0;
340039213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
340139213Sgibbs	csio = &ccb->csio;
340239531Sken	/*
340339531Sken	 * Use the smallest possible command to perform the operation.
340439531Sken	 */
340539531Sken	if ((len & 0xffff0000) == 0) {
340639531Sken		/*
340739531Sken		 * We can fit in a 10 byte cdb.
340839531Sken		 */
340939531Sken		struct scsi_play_10 *scsi_cmd;
341039213Sgibbs
341139531Sken		scsi_cmd = (struct scsi_play_10 *)&csio->cdb_io.cdb_bytes;
341239531Sken		bzero (scsi_cmd, sizeof(*scsi_cmd));
341339531Sken		scsi_cmd->op_code = PLAY_10;
341439531Sken		scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr);
341539531Sken		scsi_ulto2b(len, (u_int8_t *)scsi_cmd->xfer_len);
341639531Sken		cdb_len = sizeof(*scsi_cmd);
341739531Sken	} else  {
341839531Sken		struct scsi_play_12 *scsi_cmd;
341939213Sgibbs
342039531Sken		scsi_cmd = (struct scsi_play_12 *)&csio->cdb_io.cdb_bytes;
342139531Sken		bzero (scsi_cmd, sizeof(*scsi_cmd));
342239531Sken		scsi_cmd->op_code = PLAY_12;
342339531Sken		scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr);
342439531Sken		scsi_ulto4b(len, (u_int8_t *)scsi_cmd->xfer_len);
342539531Sken		cdb_len = sizeof(*scsi_cmd);
342639531Sken	}
342739531Sken	cam_fill_csio(csio,
342839531Sken		      /*retries*/2,
342939531Sken		      cddone,
343039531Sken		      /*flags*/CAM_DIR_NONE,
343139531Sken		      MSG_SIMPLE_Q_TAG,
343239531Sken		      /*dataptr*/NULL,
343339531Sken		      /*datalen*/0,
343439531Sken		      /*sense_len*/SSD_FULL_SIZE,
343539531Sken		      cdb_len,
343639531Sken		      /*timeout*/50 * 1000);
343739213Sgibbs
343874840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
343974840Sken			 /*sense_flags*/SF_RETRY_UA);
344039213Sgibbs
344139213Sgibbs	xpt_release_ccb(ccb);
344239213Sgibbs
344339213Sgibbs	return(error);
344439213Sgibbs}
344539213Sgibbs
344639213Sgibbsstatic int
344739213Sgibbscdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts,
344839213Sgibbs	  u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf)
344939213Sgibbs{
345039213Sgibbs	struct scsi_play_msf *scsi_cmd;
345139213Sgibbs        struct ccb_scsiio *csio;
345239213Sgibbs	union ccb *ccb;
345339213Sgibbs	int error;
345439213Sgibbs
345539213Sgibbs	error = 0;
345639213Sgibbs
345739213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
345839213Sgibbs
345939213Sgibbs	csio = &ccb->csio;
346039213Sgibbs
346139213Sgibbs	cam_fill_csio(csio,
346239213Sgibbs		      /* retries */ 1,
346339213Sgibbs		      /* cbfcnp */ cddone,
346439531Sken		      /* flags */ CAM_DIR_NONE,
346539213Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
346639213Sgibbs		      /* data_ptr */ NULL,
346739213Sgibbs		      /* dxfer_len */ 0,
346839213Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
346939213Sgibbs		      sizeof(struct scsi_play_msf),
347039213Sgibbs 		      /* timeout */ 50000);
347139213Sgibbs
347239213Sgibbs	scsi_cmd = (struct scsi_play_msf *)&csio->cdb_io.cdb_bytes;
347339213Sgibbs	bzero (scsi_cmd, sizeof(*scsi_cmd));
347439213Sgibbs
347539213Sgibbs        scsi_cmd->op_code = PLAY_MSF;
347639213Sgibbs        scsi_cmd->start_m = startm;
347739213Sgibbs        scsi_cmd->start_s = starts;
347839213Sgibbs        scsi_cmd->start_f = startf;
347939213Sgibbs        scsi_cmd->end_m = endm;
348039213Sgibbs        scsi_cmd->end_s = ends;
348139213Sgibbs        scsi_cmd->end_f = endf;
348239213Sgibbs
348374840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
348474840Sken			 /*sense_flags*/SF_RETRY_UA);
348539213Sgibbs
348639213Sgibbs	xpt_release_ccb(ccb);
348739213Sgibbs
348839213Sgibbs	return(error);
348939213Sgibbs}
349039213Sgibbs
349139213Sgibbs
349239213Sgibbsstatic int
349339213Sgibbscdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex,
349439213Sgibbs	     u_int32_t etrack, u_int32_t eindex)
349539213Sgibbs{
349639213Sgibbs	struct scsi_play_track *scsi_cmd;
349739213Sgibbs        struct ccb_scsiio *csio;
349839213Sgibbs	union ccb *ccb;
349939213Sgibbs	int error;
350039213Sgibbs
350139213Sgibbs	error = 0;
350239213Sgibbs
350339213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
350439213Sgibbs
350539213Sgibbs	csio = &ccb->csio;
350639213Sgibbs
350739213Sgibbs	cam_fill_csio(csio,
350839213Sgibbs		      /* retries */ 1,
350939213Sgibbs		      /* cbfcnp */ cddone,
351039531Sken		      /* flags */ CAM_DIR_NONE,
351139213Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
351239213Sgibbs		      /* data_ptr */ NULL,
351339213Sgibbs		      /* dxfer_len */ 0,
351439213Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
351539213Sgibbs		      sizeof(struct scsi_play_track),
351639213Sgibbs 		      /* timeout */ 50000);
351739213Sgibbs
351839213Sgibbs	scsi_cmd = (struct scsi_play_track *)&csio->cdb_io.cdb_bytes;
351939213Sgibbs	bzero (scsi_cmd, sizeof(*scsi_cmd));
352039213Sgibbs
352139213Sgibbs        scsi_cmd->op_code = PLAY_TRACK;
352239213Sgibbs        scsi_cmd->start_track = strack;
352339213Sgibbs        scsi_cmd->start_index = sindex;
352439213Sgibbs        scsi_cmd->end_track = etrack;
352539213Sgibbs        scsi_cmd->end_index = eindex;
352639213Sgibbs
352774840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
352874840Sken			 /*sense_flags*/SF_RETRY_UA);
352939213Sgibbs
353039213Sgibbs	xpt_release_ccb(ccb);
353139213Sgibbs
353239213Sgibbs	return(error);
353339213Sgibbs}
353439213Sgibbs
353539213Sgibbsstatic int
353639213Sgibbscdpause(struct cam_periph *periph, u_int32_t go)
353739213Sgibbs{
353839213Sgibbs	struct scsi_pause *scsi_cmd;
353939213Sgibbs        struct ccb_scsiio *csio;
354039213Sgibbs	union ccb *ccb;
354139213Sgibbs	int error;
354239213Sgibbs
354339213Sgibbs	error = 0;
354439213Sgibbs
354539213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
354639213Sgibbs
354739213Sgibbs	csio = &ccb->csio;
354839213Sgibbs
354939213Sgibbs	cam_fill_csio(csio,
355039213Sgibbs		      /* retries */ 1,
355139213Sgibbs		      /* cbfcnp */ cddone,
355239531Sken		      /* flags */ CAM_DIR_NONE,
355339213Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
355439213Sgibbs		      /* data_ptr */ NULL,
355539213Sgibbs		      /* dxfer_len */ 0,
355639213Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
355739213Sgibbs		      sizeof(struct scsi_pause),
355839213Sgibbs 		      /* timeout */ 50000);
355939213Sgibbs
356039213Sgibbs	scsi_cmd = (struct scsi_pause *)&csio->cdb_io.cdb_bytes;
356139213Sgibbs	bzero (scsi_cmd, sizeof(*scsi_cmd));
356239213Sgibbs
356339213Sgibbs        scsi_cmd->op_code = PAUSE;
356439213Sgibbs	scsi_cmd->resume = go;
356539213Sgibbs
356674840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
356774840Sken			 /*sense_flags*/SF_RETRY_UA);
356839213Sgibbs
356939213Sgibbs	xpt_release_ccb(ccb);
357039213Sgibbs
357139213Sgibbs	return(error);
357239213Sgibbs}
357339213Sgibbs
357439213Sgibbsstatic int
3575111206Skencdstartunit(struct cam_periph *periph, int load)
357639213Sgibbs{
357739213Sgibbs	union ccb *ccb;
357839213Sgibbs	int error;
357939213Sgibbs
358039213Sgibbs	error = 0;
358139213Sgibbs
358239213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
358339213Sgibbs
358439213Sgibbs	scsi_start_stop(&ccb->csio,
358539213Sgibbs			/* retries */ 1,
358639213Sgibbs			/* cbfcnp */ cddone,
358739213Sgibbs			/* tag_action */ MSG_SIMPLE_Q_TAG,
358839213Sgibbs			/* start */ TRUE,
3589111206Sken			/* load_eject */ load,
359039213Sgibbs			/* immediate */ FALSE,
359139213Sgibbs			/* sense_len */ SSD_FULL_SIZE,
359239213Sgibbs			/* timeout */ 50000);
359339213Sgibbs
359474840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
359574840Sken			 /*sense_flags*/SF_RETRY_UA);
359639213Sgibbs
359739213Sgibbs	xpt_release_ccb(ccb);
359839213Sgibbs
359939213Sgibbs	return(error);
360039213Sgibbs}
360139213Sgibbs
360239213Sgibbsstatic int
360339213Sgibbscdstopunit(struct cam_periph *periph, u_int32_t eject)
360439213Sgibbs{
360539213Sgibbs	union ccb *ccb;
360639213Sgibbs	int error;
360739213Sgibbs
360839213Sgibbs	error = 0;
360939213Sgibbs
361039213Sgibbs	ccb = cdgetccb(periph, /* priority */ 1);
361139213Sgibbs
361239213Sgibbs	scsi_start_stop(&ccb->csio,
361339213Sgibbs			/* retries */ 1,
361439213Sgibbs			/* cbfcnp */ cddone,
361539213Sgibbs			/* tag_action */ MSG_SIMPLE_Q_TAG,
361639213Sgibbs			/* start */ FALSE,
361739213Sgibbs			/* load_eject */ eject,
361839213Sgibbs			/* immediate */ FALSE,
361939213Sgibbs			/* sense_len */ SSD_FULL_SIZE,
362039213Sgibbs			/* timeout */ 50000);
362139213Sgibbs
362274840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
362374840Sken			 /*sense_flags*/SF_RETRY_UA);
362439213Sgibbs
362539213Sgibbs	xpt_release_ccb(ccb);
362639213Sgibbs
362739213Sgibbs	return(error);
362839213Sgibbs}
362960422Sken
363060422Skenstatic int
3631105421Snjlcdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed)
3632105421Snjl{
3633105421Snjl	struct scsi_set_speed *scsi_cmd;
3634105421Snjl	struct ccb_scsiio *csio;
3635105421Snjl	union ccb *ccb;
3636105421Snjl	int error;
3637105421Snjl
3638105421Snjl	error = 0;
3639105421Snjl	ccb = cdgetccb(periph, /* priority */ 1);
3640105421Snjl	csio = &ccb->csio;
3641105421Snjl
3642107193Snjl	/* Preserve old behavior: units in multiples of CDROM speed */
3643107193Snjl	if (rdspeed < 177)
3644107193Snjl		rdspeed *= 177;
3645107193Snjl	if (wrspeed < 177)
3646107193Snjl		wrspeed *= 177;
3647107193Snjl
3648105421Snjl	cam_fill_csio(csio,
3649105421Snjl		      /* retries */ 1,
3650105421Snjl		      /* cbfcnp */ cddone,
3651105421Snjl		      /* flags */ CAM_DIR_NONE,
3652105421Snjl		      /* tag_action */ MSG_SIMPLE_Q_TAG,
3653105421Snjl		      /* data_ptr */ NULL,
3654105421Snjl		      /* dxfer_len */ 0,
3655105421Snjl		      /* sense_len */ SSD_FULL_SIZE,
3656105421Snjl		      sizeof(struct scsi_set_speed),
3657105421Snjl 		      /* timeout */ 50000);
3658105421Snjl
3659105421Snjl	scsi_cmd = (struct scsi_set_speed *)&csio->cdb_io.cdb_bytes;
3660105421Snjl	bzero(scsi_cmd, sizeof(*scsi_cmd));
3661105421Snjl
3662105421Snjl	scsi_cmd->opcode = SET_CD_SPEED;
3663105421Snjl	scsi_ulto2b(rdspeed, scsi_cmd->readspeed);
3664105421Snjl	scsi_ulto2b(wrspeed, scsi_cmd->writespeed);
3665105421Snjl
3666105421Snjl	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
3667105421Snjl			 /*sense_flags*/SF_RETRY_UA);
3668105421Snjl
3669105421Snjl	xpt_release_ccb(ccb);
3670105421Snjl
3671105421Snjl	return(error);
3672105421Snjl}
3673105421Snjl
3674105421Snjlstatic int
367560422Skencdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo)
367660422Sken{
367760422Sken	union ccb *ccb;
367860422Sken	u_int8_t *databuf;
367960422Sken	u_int32_t lba;
368060422Sken	int error;
368160422Sken	int length;
368260422Sken
368360422Sken	error = 0;
368460422Sken	databuf = NULL;
368560422Sken	lba = 0;
368660422Sken
368760422Sken	ccb = cdgetccb(periph, /* priority */ 1);
368860422Sken
368960422Sken	switch (authinfo->format) {
369060422Sken	case DVD_REPORT_AGID:
369160422Sken		length = sizeof(struct scsi_report_key_data_agid);
369260422Sken		break;
369360422Sken	case DVD_REPORT_CHALLENGE:
369460422Sken		length = sizeof(struct scsi_report_key_data_challenge);
369560422Sken		break;
369660422Sken	case DVD_REPORT_KEY1:
369760422Sken		length = sizeof(struct scsi_report_key_data_key1_key2);
369860422Sken		break;
369960422Sken	case DVD_REPORT_TITLE_KEY:
370060422Sken		length = sizeof(struct scsi_report_key_data_title);
370160422Sken		/* The lba field is only set for the title key */
370260422Sken		lba = authinfo->lba;
370360422Sken		break;
370460422Sken	case DVD_REPORT_ASF:
370560422Sken		length = sizeof(struct scsi_report_key_data_asf);
370660422Sken		break;
370760422Sken	case DVD_REPORT_RPC:
370860422Sken		length = sizeof(struct scsi_report_key_data_rpc);
370960422Sken		break;
371060422Sken	case DVD_INVALIDATE_AGID:
371160422Sken		length = 0;
371260422Sken		break;
371360422Sken	default:
371460422Sken		error = EINVAL;
371560422Sken		goto bailout;
371660422Sken		break; /* NOTREACHED */
371760422Sken	}
371860422Sken
371960422Sken	if (length != 0) {
3720111119Simp		databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO);
372160422Sken	} else
372260422Sken		databuf = NULL;
372360422Sken
372460422Sken
372560422Sken	scsi_report_key(&ccb->csio,
372660422Sken			/* retries */ 1,
372760422Sken			/* cbfcnp */ cddone,
372860422Sken			/* tag_action */ MSG_SIMPLE_Q_TAG,
372960422Sken			/* lba */ lba,
373060422Sken			/* agid */ authinfo->agid,
373160422Sken			/* key_format */ authinfo->format,
373260422Sken			/* data_ptr */ databuf,
373360422Sken			/* dxfer_len */ length,
373460422Sken			/* sense_len */ SSD_FULL_SIZE,
373560422Sken			/* timeout */ 50000);
373660422Sken
373774840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
373874840Sken			 /*sense_flags*/SF_RETRY_UA);
373960422Sken
374060422Sken	if (error != 0)
374160422Sken		goto bailout;
374260422Sken
374360422Sken	if (ccb->csio.resid != 0) {
374460422Sken		xpt_print_path(periph->path);
374560422Sken		printf("warning, residual for report key command is %d\n",
374660422Sken		       ccb->csio.resid);
374760422Sken	}
374860422Sken
374960422Sken	switch(authinfo->format) {
375060422Sken	case DVD_REPORT_AGID: {
375160422Sken		struct scsi_report_key_data_agid *agid_data;
375260422Sken
375360422Sken		agid_data = (struct scsi_report_key_data_agid *)databuf;
375460422Sken
375560422Sken		authinfo->agid = (agid_data->agid & RKD_AGID_MASK) >>
375660422Sken			RKD_AGID_SHIFT;
375760422Sken		break;
375860422Sken	}
375960422Sken	case DVD_REPORT_CHALLENGE: {
376060422Sken		struct scsi_report_key_data_challenge *chal_data;
376160422Sken
376260422Sken		chal_data = (struct scsi_report_key_data_challenge *)databuf;
376360422Sken
376460422Sken		bcopy(chal_data->challenge_key, authinfo->keychal,
376560422Sken		      min(sizeof(chal_data->challenge_key),
376660422Sken		          sizeof(authinfo->keychal)));
376760422Sken		break;
376860422Sken	}
376960422Sken	case DVD_REPORT_KEY1: {
377060422Sken		struct scsi_report_key_data_key1_key2 *key1_data;
377160422Sken
377260422Sken		key1_data = (struct scsi_report_key_data_key1_key2 *)databuf;
377360422Sken
377460422Sken		bcopy(key1_data->key1, authinfo->keychal,
377560422Sken		      min(sizeof(key1_data->key1), sizeof(authinfo->keychal)));
377660422Sken		break;
377760422Sken	}
377860422Sken	case DVD_REPORT_TITLE_KEY: {
377960422Sken		struct scsi_report_key_data_title *title_data;
378060422Sken
378160422Sken		title_data = (struct scsi_report_key_data_title *)databuf;
378260422Sken
378360422Sken		authinfo->cpm = (title_data->byte0 & RKD_TITLE_CPM) >>
378460422Sken			RKD_TITLE_CPM_SHIFT;
378560422Sken		authinfo->cp_sec = (title_data->byte0 & RKD_TITLE_CP_SEC) >>
378660422Sken			RKD_TITLE_CP_SEC_SHIFT;
378760422Sken		authinfo->cgms = (title_data->byte0 & RKD_TITLE_CMGS_MASK) >>
378860422Sken			RKD_TITLE_CMGS_SHIFT;
378960422Sken		bcopy(title_data->title_key, authinfo->keychal,
379060422Sken		      min(sizeof(title_data->title_key),
379160422Sken			  sizeof(authinfo->keychal)));
379260422Sken		break;
379360422Sken	}
379460422Sken	case DVD_REPORT_ASF: {
379560422Sken		struct scsi_report_key_data_asf *asf_data;
379660422Sken
379760422Sken		asf_data = (struct scsi_report_key_data_asf *)databuf;
379860422Sken
379960422Sken		authinfo->asf = asf_data->success & RKD_ASF_SUCCESS;
380060422Sken		break;
380160422Sken	}
380260422Sken	case DVD_REPORT_RPC: {
380360422Sken		struct scsi_report_key_data_rpc *rpc_data;
380460422Sken
380560422Sken		rpc_data = (struct scsi_report_key_data_rpc *)databuf;
380660422Sken
380760422Sken		authinfo->reg_type = (rpc_data->byte4 & RKD_RPC_TYPE_MASK) >>
380860422Sken			RKD_RPC_TYPE_SHIFT;
380960422Sken		authinfo->vend_rsts =
381060422Sken			(rpc_data->byte4 & RKD_RPC_VENDOR_RESET_MASK) >>
381160422Sken			RKD_RPC_VENDOR_RESET_SHIFT;
381260422Sken		authinfo->user_rsts = rpc_data->byte4 & RKD_RPC_USER_RESET_MASK;
381371752Sken		authinfo->region = rpc_data->region_mask;
381471752Sken		authinfo->rpc_scheme = rpc_data->rpc_scheme1;
381560422Sken		break;
381660422Sken	}
381760422Sken	case DVD_INVALIDATE_AGID:
381860422Sken		break;
381960422Sken	default:
382060422Sken		/* This should be impossible, since we checked above */
382160422Sken		error = EINVAL;
382260422Sken		goto bailout;
382360422Sken		break; /* NOTREACHED */
382460422Sken	}
382560422Skenbailout:
382660422Sken	if (databuf != NULL)
382760422Sken		free(databuf, M_DEVBUF);
382860422Sken
382960422Sken	xpt_release_ccb(ccb);
383060422Sken
383160422Sken	return(error);
383260422Sken}
383360422Sken
383460422Skenstatic int
383560422Skencdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo)
383660422Sken{
383760422Sken	union ccb *ccb;
383860422Sken	u_int8_t *databuf;
383960422Sken	int length;
384060422Sken	int error;
384160422Sken
384260422Sken	error = 0;
384360422Sken	databuf = NULL;
384460422Sken
384560422Sken	ccb = cdgetccb(periph, /* priority */ 1);
384660422Sken
384760422Sken	switch(authinfo->format) {
384860422Sken	case DVD_SEND_CHALLENGE: {
384960422Sken		struct scsi_report_key_data_challenge *challenge_data;
385060422Sken
385160422Sken		length = sizeof(*challenge_data);
385260422Sken
3853111119Simp		challenge_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO);
385460422Sken
385560422Sken		databuf = (u_int8_t *)challenge_data;
385660422Sken
385760422Sken		scsi_ulto2b(length - sizeof(challenge_data->data_len),
385860422Sken			    challenge_data->data_len);
385960422Sken
386060422Sken		bcopy(authinfo->keychal, challenge_data->challenge_key,
386160422Sken		      min(sizeof(authinfo->keychal),
386260422Sken			  sizeof(challenge_data->challenge_key)));
386360422Sken		break;
386460422Sken	}
386560422Sken	case DVD_SEND_KEY2: {
386660422Sken		struct scsi_report_key_data_key1_key2 *key2_data;
386760422Sken
386860422Sken		length = sizeof(*key2_data);
386960422Sken
3870111119Simp		key2_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO);
387160422Sken
387260422Sken		databuf = (u_int8_t *)key2_data;
387360422Sken
387460422Sken		scsi_ulto2b(length - sizeof(key2_data->data_len),
387560422Sken			    key2_data->data_len);
387660422Sken
387760422Sken		bcopy(authinfo->keychal, key2_data->key1,
387860422Sken		      min(sizeof(authinfo->keychal), sizeof(key2_data->key1)));
387960422Sken
388060422Sken		break;
388160422Sken	}
388260422Sken	case DVD_SEND_RPC: {
388360422Sken		struct scsi_send_key_data_rpc *rpc_data;
388460422Sken
388560422Sken		length = sizeof(*rpc_data);
388660422Sken
3887111119Simp		rpc_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO);
388860422Sken
388960422Sken		databuf = (u_int8_t *)rpc_data;
389060422Sken
389160422Sken		scsi_ulto2b(length - sizeof(rpc_data->data_len),
389260422Sken			    rpc_data->data_len);
389360422Sken
389460422Sken		rpc_data->region_code = authinfo->region;
389560422Sken		break;
389660422Sken	}
389760422Sken	default:
389860422Sken		error = EINVAL;
389960422Sken		goto bailout;
390060422Sken		break; /* NOTREACHED */
390160422Sken	}
390260422Sken
390360422Sken	scsi_send_key(&ccb->csio,
390460422Sken		      /* retries */ 1,
390560422Sken		      /* cbfcnp */ cddone,
390660422Sken		      /* tag_action */ MSG_SIMPLE_Q_TAG,
390760422Sken		      /* agid */ authinfo->agid,
390860422Sken		      /* key_format */ authinfo->format,
390960422Sken		      /* data_ptr */ databuf,
391060422Sken		      /* dxfer_len */ length,
391160422Sken		      /* sense_len */ SSD_FULL_SIZE,
391260422Sken		      /* timeout */ 50000);
391360422Sken
391474840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
391574840Sken			 /*sense_flags*/SF_RETRY_UA);
391660422Sken
391760422Skenbailout:
391860422Sken
391960422Sken	if (databuf != NULL)
392060422Sken		free(databuf, M_DEVBUF);
392160422Sken
392260422Sken	xpt_release_ccb(ccb);
392360422Sken
392460422Sken	return(error);
392560422Sken}
392660422Sken
392760422Skenstatic int
392860422Skencdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct)
392960422Sken{
393060422Sken	union ccb *ccb;
393160422Sken	u_int8_t *databuf;
393260422Sken	u_int32_t address;
393360422Sken	int error;
393460422Sken	int length;
393560422Sken
393660422Sken	error = 0;
393760422Sken	databuf = NULL;
393860422Sken	/* The address is reserved for many of the formats */
393960422Sken	address = 0;
394060422Sken
394160422Sken	ccb = cdgetccb(periph, /* priority */ 1);
394260422Sken
394360422Sken	switch(dvdstruct->format) {
394460422Sken	case DVD_STRUCT_PHYSICAL:
394560422Sken		length = sizeof(struct scsi_read_dvd_struct_data_physical);
394660422Sken		break;
394760422Sken	case DVD_STRUCT_COPYRIGHT:
394860422Sken		length = sizeof(struct scsi_read_dvd_struct_data_copyright);
394960422Sken		break;
395060422Sken	case DVD_STRUCT_DISCKEY:
395160422Sken		length = sizeof(struct scsi_read_dvd_struct_data_disc_key);
395260422Sken		break;
395360422Sken	case DVD_STRUCT_BCA:
395460422Sken		length = sizeof(struct scsi_read_dvd_struct_data_bca);
395560422Sken		break;
395660422Sken	case DVD_STRUCT_MANUFACT:
395760422Sken		length = sizeof(struct scsi_read_dvd_struct_data_manufacturer);
395860422Sken		break;
395960422Sken	case DVD_STRUCT_CMI:
396060422Sken		error = ENODEV;
396160422Sken		goto bailout;
396260422Sken#ifdef notyet
396360422Sken		length = sizeof(struct scsi_read_dvd_struct_data_copy_manage);
396460422Sken		address = dvdstruct->address;
396560422Sken#endif
396660422Sken		break; /* NOTREACHED */
396760422Sken	case DVD_STRUCT_PROTDISCID:
396860422Sken		length = sizeof(struct scsi_read_dvd_struct_data_prot_discid);
396960422Sken		break;
397060422Sken	case DVD_STRUCT_DISCKEYBLOCK:
397160422Sken		length = sizeof(struct scsi_read_dvd_struct_data_disc_key_blk);
397260422Sken		break;
397360422Sken	case DVD_STRUCT_DDS:
397460422Sken		length = sizeof(struct scsi_read_dvd_struct_data_dds);
397560422Sken		break;
397660422Sken	case DVD_STRUCT_MEDIUM_STAT:
397760422Sken		length = sizeof(struct scsi_read_dvd_struct_data_medium_status);
397860422Sken		break;
397960422Sken	case DVD_STRUCT_SPARE_AREA:
398060422Sken		length = sizeof(struct scsi_read_dvd_struct_data_spare_area);
398160422Sken		break;
398260422Sken	case DVD_STRUCT_RMD_LAST:
398360422Sken		error = ENODEV;
398460422Sken		goto bailout;
398560422Sken#ifdef notyet
398660422Sken		length = sizeof(struct scsi_read_dvd_struct_data_rmd_borderout);
398760422Sken		address = dvdstruct->address;
398860422Sken#endif
398960422Sken		break; /* NOTREACHED */
399060422Sken	case DVD_STRUCT_RMD_RMA:
399160422Sken		error = ENODEV;
399260422Sken		goto bailout;
399360422Sken#ifdef notyet
399460422Sken		length = sizeof(struct scsi_read_dvd_struct_data_rmd);
399560422Sken		address = dvdstruct->address;
399660422Sken#endif
399760422Sken		break; /* NOTREACHED */
399860422Sken	case DVD_STRUCT_PRERECORDED:
399960422Sken		length = sizeof(struct scsi_read_dvd_struct_data_leadin);
400060422Sken		break;
400160422Sken	case DVD_STRUCT_UNIQUEID:
400260422Sken		length = sizeof(struct scsi_read_dvd_struct_data_disc_id);
400360422Sken		break;
400460422Sken	case DVD_STRUCT_DCB:
400560422Sken		error = ENODEV;
400660422Sken		goto bailout;
400760422Sken#ifdef notyet
400860422Sken		length = sizeof(struct scsi_read_dvd_struct_data_dcb);
400960422Sken		address = dvdstruct->address;
401060422Sken#endif
401160422Sken		break; /* NOTREACHED */
401260422Sken	case DVD_STRUCT_LIST:
401360422Sken		/*
401460422Sken		 * This is the maximum allocation length for the READ DVD
401560422Sken		 * STRUCTURE command.  There's nothing in the MMC3 spec
401660422Sken		 * that indicates a limit in the amount of data that can
401760422Sken		 * be returned from this call, other than the limits
401860422Sken		 * imposed by the 2-byte length variables.
401960422Sken		 */
402060422Sken		length = 65535;
402160422Sken		break;
402260422Sken	default:
402360422Sken		error = EINVAL;
402460422Sken		goto bailout;
402560422Sken		break; /* NOTREACHED */
402660422Sken	}
402760422Sken
402860422Sken	if (length != 0) {
4029111119Simp		databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO);
403060422Sken	} else
403160422Sken		databuf = NULL;
403260422Sken
403360422Sken	scsi_read_dvd_structure(&ccb->csio,
403460422Sken				/* retries */ 1,
403560422Sken				/* cbfcnp */ cddone,
403660422Sken				/* tag_action */ MSG_SIMPLE_Q_TAG,
403760422Sken				/* lba */ address,
403860422Sken				/* layer_number */ dvdstruct->layer_num,
403960422Sken				/* key_format */ dvdstruct->format,
404060422Sken				/* agid */ dvdstruct->agid,
404160422Sken				/* data_ptr */ databuf,
404260422Sken				/* dxfer_len */ length,
404360422Sken				/* sense_len */ SSD_FULL_SIZE,
404460422Sken				/* timeout */ 50000);
404560422Sken
404674840Sken	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
404774840Sken			 /*sense_flags*/SF_RETRY_UA);
404860422Sken
404960422Sken	if (error != 0)
405060422Sken		goto bailout;
405160422Sken
405260422Sken	switch(dvdstruct->format) {
405360422Sken	case DVD_STRUCT_PHYSICAL: {
405460422Sken		struct scsi_read_dvd_struct_data_layer_desc *inlayer;
405560422Sken		struct dvd_layer *outlayer;
405660422Sken		struct scsi_read_dvd_struct_data_physical *phys_data;
405760422Sken
405860422Sken		phys_data =
405960422Sken			(struct scsi_read_dvd_struct_data_physical *)databuf;
406060422Sken		inlayer = &phys_data->layer_desc;
406160422Sken		outlayer = (struct dvd_layer *)&dvdstruct->data;
406260422Sken
406360422Sken		dvdstruct->length = sizeof(*inlayer);
406460422Sken
406560422Sken		outlayer->book_type = (inlayer->book_type_version &
406660422Sken			RDSD_BOOK_TYPE_MASK) >> RDSD_BOOK_TYPE_SHIFT;
406760422Sken		outlayer->book_version = (inlayer->book_type_version &
406860422Sken			RDSD_BOOK_VERSION_MASK);
406960422Sken		outlayer->disc_size = (inlayer->disc_size_max_rate &
407060422Sken			RDSD_DISC_SIZE_MASK) >> RDSD_DISC_SIZE_SHIFT;
407160422Sken		outlayer->max_rate = (inlayer->disc_size_max_rate &
407260422Sken			RDSD_MAX_RATE_MASK);
407360422Sken		outlayer->nlayers = (inlayer->layer_info &
407460422Sken			RDSD_NUM_LAYERS_MASK) >> RDSD_NUM_LAYERS_SHIFT;
407560422Sken		outlayer->track_path = (inlayer->layer_info &
407660422Sken			RDSD_TRACK_PATH_MASK) >> RDSD_TRACK_PATH_SHIFT;
407760422Sken		outlayer->layer_type = (inlayer->layer_info &
407860422Sken			RDSD_LAYER_TYPE_MASK);
407960422Sken		outlayer->linear_density = (inlayer->density &
408060422Sken			RDSD_LIN_DENSITY_MASK) >> RDSD_LIN_DENSITY_SHIFT;
408160422Sken		outlayer->track_density = (inlayer->density &
408260422Sken			RDSD_TRACK_DENSITY_MASK);
408360422Sken		outlayer->bca = (inlayer->bca & RDSD_BCA_MASK) >>
408460422Sken			RDSD_BCA_SHIFT;
408560422Sken		outlayer->start_sector = scsi_3btoul(inlayer->main_data_start);
408660422Sken		outlayer->end_sector = scsi_3btoul(inlayer->main_data_end);
408760422Sken		outlayer->end_sector_l0 =
408860422Sken			scsi_3btoul(inlayer->end_sector_layer0);
408960422Sken		break;
409060422Sken	}
409160422Sken	case DVD_STRUCT_COPYRIGHT: {
409260422Sken		struct scsi_read_dvd_struct_data_copyright *copy_data;
409360422Sken
409460422Sken		copy_data = (struct scsi_read_dvd_struct_data_copyright *)
409560422Sken			databuf;
409660422Sken
409760422Sken		dvdstruct->cpst = copy_data->cps_type;
409860422Sken		dvdstruct->rmi = copy_data->region_info;
409960422Sken		dvdstruct->length = 0;
410060422Sken
410160422Sken		break;
410260422Sken	}
410360422Sken	default:
410460422Sken		/*
410560422Sken		 * Tell the user what the overall length is, no matter
410660422Sken		 * what we can actually fit in the data buffer.
410760422Sken		 */
410860422Sken		dvdstruct->length = length - ccb->csio.resid -
410960422Sken			sizeof(struct scsi_read_dvd_struct_data_header);
411060422Sken
411160422Sken		/*
411260422Sken		 * But only actually copy out the smaller of what we read
411360422Sken		 * in or what the structure can take.
411460422Sken		 */
411560422Sken		bcopy(databuf + sizeof(struct scsi_read_dvd_struct_data_header),
411660422Sken		      dvdstruct->data,
411760422Sken		      min(sizeof(dvdstruct->data), dvdstruct->length));
411860422Sken		break;
411960422Sken	}
412060422Skenbailout:
412160422Sken
412260422Sken	if (databuf != NULL)
412360422Sken		free(databuf, M_DEVBUF);
412460422Sken
412560422Sken	xpt_release_ccb(ccb);
412660422Sken
412760422Sken	return(error);
412860422Sken}
412960422Sken
413060422Skenvoid
413160422Skenscsi_report_key(struct ccb_scsiio *csio, u_int32_t retries,
413260422Sken		void (*cbfcnp)(struct cam_periph *, union ccb *),
413360422Sken		u_int8_t tag_action, u_int32_t lba, u_int8_t agid,
413460422Sken		u_int8_t key_format, u_int8_t *data_ptr, u_int32_t dxfer_len,
413560422Sken		u_int8_t sense_len, u_int32_t timeout)
413660422Sken{
413760422Sken	struct scsi_report_key *scsi_cmd;
413860422Sken
413960422Sken	scsi_cmd = (struct scsi_report_key *)&csio->cdb_io.cdb_bytes;
414060422Sken	bzero(scsi_cmd, sizeof(*scsi_cmd));
414160422Sken	scsi_cmd->opcode = REPORT_KEY;
414260422Sken	scsi_ulto4b(lba, scsi_cmd->lba);
414360422Sken	scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len);
414460422Sken	scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) |
414560422Sken		(key_format & RK_KF_KEYFORMAT_MASK);
414660422Sken
414760422Sken	cam_fill_csio(csio,
414860422Sken		      retries,
414960422Sken		      cbfcnp,
415060422Sken		      /*flags*/ (dxfer_len == 0) ? CAM_DIR_NONE : CAM_DIR_IN,
415160422Sken		      tag_action,
415260422Sken		      /*data_ptr*/ data_ptr,
415360422Sken		      /*dxfer_len*/ dxfer_len,
415460422Sken		      sense_len,
415560422Sken		      sizeof(*scsi_cmd),
415660422Sken		      timeout);
415760422Sken}
415860422Sken
415960422Skenvoid
416060422Skenscsi_send_key(struct ccb_scsiio *csio, u_int32_t retries,
416160422Sken	      void (*cbfcnp)(struct cam_periph *, union ccb *),
416260422Sken	      u_int8_t tag_action, u_int8_t agid, u_int8_t key_format,
416360422Sken	      u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len,
416460422Sken	      u_int32_t timeout)
416560422Sken{
416660422Sken	struct scsi_send_key *scsi_cmd;
416760422Sken
416860422Sken	scsi_cmd = (struct scsi_send_key *)&csio->cdb_io.cdb_bytes;
416960422Sken	bzero(scsi_cmd, sizeof(*scsi_cmd));
417060422Sken	scsi_cmd->opcode = SEND_KEY;
417160422Sken
417260422Sken	scsi_ulto2b(dxfer_len, scsi_cmd->param_len);
417360422Sken	scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) |
417460422Sken		(key_format & RK_KF_KEYFORMAT_MASK);
417560422Sken
417660422Sken	cam_fill_csio(csio,
417760422Sken		      retries,
417860422Sken		      cbfcnp,
417960422Sken		      /*flags*/ CAM_DIR_OUT,
418060422Sken		      tag_action,
418160422Sken		      /*data_ptr*/ data_ptr,
418260422Sken		      /*dxfer_len*/ dxfer_len,
418360422Sken		      sense_len,
418460422Sken		      sizeof(*scsi_cmd),
418560422Sken		      timeout);
418660422Sken}
418760422Sken
418860422Sken
418960422Skenvoid
419060422Skenscsi_read_dvd_structure(struct ccb_scsiio *csio, u_int32_t retries,
419160422Sken			void (*cbfcnp)(struct cam_periph *, union ccb *),
419260422Sken			u_int8_t tag_action, u_int32_t address,
419360422Sken			u_int8_t layer_number, u_int8_t format, u_int8_t agid,
419460422Sken			u_int8_t *data_ptr, u_int32_t dxfer_len,
419560422Sken			u_int8_t sense_len, u_int32_t timeout)
419660422Sken{
419760422Sken	struct scsi_read_dvd_structure *scsi_cmd;
419860422Sken
419960422Sken	scsi_cmd = (struct scsi_read_dvd_structure *)&csio->cdb_io.cdb_bytes;
420060422Sken	bzero(scsi_cmd, sizeof(*scsi_cmd));
420160422Sken	scsi_cmd->opcode = READ_DVD_STRUCTURE;
420260422Sken
420360422Sken	scsi_ulto4b(address, scsi_cmd->address);
420460422Sken	scsi_cmd->layer_number = layer_number;
420560422Sken	scsi_cmd->format = format;
420660422Sken	scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len);
420760422Sken	/* The AGID is the top two bits of this byte */
420860422Sken	scsi_cmd->agid = agid << 6;
420960422Sken
421060422Sken	cam_fill_csio(csio,
421160422Sken		      retries,
421260422Sken		      cbfcnp,
421360422Sken		      /*flags*/ CAM_DIR_IN,
421460422Sken		      tag_action,
421560422Sken		      /*data_ptr*/ data_ptr,
421660422Sken		      /*dxfer_len*/ dxfer_len,
421760422Sken		      sense_len,
421860422Sken		      sizeof(*scsi_cmd),
421960422Sken		      timeout);
422060422Sken}
4221