scsi_cd.c revision 47625
1/*
2 * Copyright (c) 1997 Justin T. Gibbs.
3 * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer,
11 *    without modification, immediately at the beginning of the file.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *      $Id: scsi_cd.c,v 1.21 1999/05/22 22:00:18 gibbs Exp $
28 */
29/*
30 * Portions of this driver taken from the original FreeBSD cd driver.
31 * Written by Julian Elischer (julian@tfs.com)
32 * for TRW Financial Systems for use under the MACH(2.5) operating system.
33 *
34 * TRW Financial Systems, in accordance with their agreement with Carnegie
35 * Mellon University, makes this software available to CMU to distribute
36 * or use in any manner that they see fit as long as this message is kept with
37 * the software. For this reason TFS also grants any other persons or
38 * organisations permission to use or modify this software.
39 *
40 * TFS supplies this software to be publicly redistributed
41 * on the understanding that TFS is not responsible for the correct
42 * functioning of this software in any circumstances.
43 *
44 * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
45 *
46 *      from: cd.c,v 1.83 1997/05/04 15:24:22 joerg Exp $
47 */
48
49#include "opt_cd.h"
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/kernel.h>
54#include <sys/buf.h>
55#include <sys/dkbad.h>
56#include <sys/disklabel.h>
57#include <sys/diskslice.h>
58#include <sys/malloc.h>
59#include <sys/conf.h>
60#include <sys/cdio.h>
61#include <sys/devicestat.h>
62#include <sys/sysctl.h>
63
64#include <cam/cam.h>
65#include <cam/cam_ccb.h>
66#include <cam/cam_extend.h>
67#include <cam/cam_periph.h>
68#include <cam/cam_xpt_periph.h>
69#include <cam/cam_queue.h>
70
71#include <cam/scsi/scsi_message.h>
72#include <cam/scsi/scsi_da.h>
73#include <cam/scsi/scsi_cd.h>
74
75#define LEADOUT         0xaa            /* leadout toc entry */
76
77struct cd_params {
78	u_int32_t blksize;
79	u_long    disksize;
80};
81
82typedef enum {
83	CD_Q_NONE	= 0x00,
84	CD_Q_NO_TOUCH	= 0x01,
85	CD_Q_BCD_TRACKS	= 0x02,
86	CD_Q_NO_CHANGER	= 0x04,
87	CD_Q_CHANGER	= 0x08
88} cd_quirks;
89
90typedef enum {
91	CD_FLAG_INVALID		= 0x001,
92	CD_FLAG_NEW_DISC	= 0x002,
93	CD_FLAG_DISC_LOCKED	= 0x004,
94	CD_FLAG_DISC_REMOVABLE	= 0x008,
95	CD_FLAG_TAGGED_QUEUING	= 0x010,
96	CD_FLAG_OPEN		= 0x020,
97	CD_FLAG_CHANGER		= 0x040,
98	CD_FLAG_ACTIVE		= 0x080,
99	CD_FLAG_SCHED_ON_COMP	= 0x100,
100	CD_FLAG_RETRY_UA	= 0x200
101} cd_flags;
102
103typedef enum {
104	CD_CCB_PROBE		= 0x01,
105	CD_CCB_BUFFER_IO	= 0x02,
106	CD_CCB_WAITING		= 0x03,
107	CD_CCB_TYPE_MASK	= 0x0F,
108	CD_CCB_RETRY_UA		= 0x10
109} cd_ccb_state;
110
111typedef enum {
112	CHANGER_TIMEOUT_SCHED		= 0x01,
113	CHANGER_SHORT_TMOUT_SCHED	= 0x02,
114	CHANGER_MANUAL_CALL		= 0x04,
115	CHANGER_NEED_TIMEOUT		= 0x08
116} cd_changer_flags;
117
118#define ccb_state ppriv_field0
119#define ccb_bp ppriv_ptr1
120
121typedef enum {
122	CD_STATE_PROBE,
123	CD_STATE_NORMAL
124} cd_state;
125
126struct cd_softc {
127	cam_pinfo		pinfo;
128	cd_state		state;
129	volatile cd_flags	flags;
130	struct buf_queue_head	buf_queue;
131	LIST_HEAD(, ccb_hdr)	pending_ccbs;
132	struct cd_params	params;
133	struct diskslices 	*cd_slices;
134	union ccb		saved_ccb;
135	cd_quirks		quirks;
136	struct devstat		device_stats;
137	STAILQ_ENTRY(cd_softc)	changer_links;
138	struct cdchanger	*changer;
139	int			bufs_left;
140	struct cam_periph	*periph;
141};
142
143struct cd_quirk_entry {
144	struct scsi_inquiry_pattern inq_pat;
145	cd_quirks quirks;
146};
147
148/*
149 * These quirk entries aren't strictly necessary.  Basically, what they do
150 * is tell cdregister() up front that a device is a changer.  Otherwise, it
151 * will figure that fact out once it sees a LUN on the device that is
152 * greater than 0.  If it is known up front that a device is a changer, all
153 * I/O to the device will go through the changer scheduling routines, as
154 * opposed to the "normal" CD code.
155 */
156static struct cd_quirk_entry cd_quirk_table[] =
157{
158	{
159		{ T_CDROM, SIP_MEDIA_REMOVABLE, "NRC", "MBR-7", "*"},
160		 /*quirks*/ CD_Q_CHANGER
161	},
162	{
163		{ T_CDROM, SIP_MEDIA_REMOVABLE, "PIONEER", "CD-ROM DRM-604X",
164		  "*"}, /* quirks */ CD_Q_CHANGER
165	},
166	{
167		{ T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"},
168		/* quirks */ CD_Q_BCD_TRACKS
169	}
170};
171
172#ifndef MIN
173#define MIN(x,y) ((x<y) ? x : y)
174#endif
175
176#define CD_CDEV_MAJOR 15
177#define CD_BDEV_MAJOR 6
178
179static	d_open_t	cdopen;
180static	d_close_t	cdclose;
181static	d_ioctl_t	cdioctl;
182static	d_strategy_t	cdstrategy;
183static	d_strategy_t	cdstrategy1;
184
185static	periph_init_t	cdinit;
186static	periph_ctor_t	cdregister;
187static	periph_dtor_t	cdcleanup;
188static	periph_start_t	cdstart;
189static	periph_oninv_t	cdoninvalidate;
190static	void		cdasync(void *callback_arg, u_int32_t code,
191				struct cam_path *path, void *arg);
192static	void		cdshorttimeout(void *arg);
193static	void		cdschedule(struct cam_periph *periph, int priority);
194static	void		cdrunchangerqueue(void *arg);
195static	void		cdchangerschedule(struct cd_softc *softc);
196static	int		cdrunccb(union ccb *ccb,
197				 int (*error_routine)(union ccb *ccb,
198						      u_int32_t cam_flags,
199						      u_int32_t sense_flags),
200				 u_int32_t cam_flags, u_int32_t sense_flags);
201static union	ccb 	*cdgetccb(struct cam_periph *periph,
202				  u_int32_t priority);
203static	void		cddone(struct cam_periph *periph,
204			       union ccb *start_ccb);
205static	int		cderror(union ccb *ccb, u_int32_t cam_flags,
206				u_int32_t sense_flags);
207static	void		cdprevent(struct cam_periph *periph, int action);
208static	int		cdsize(dev_t dev, u_int32_t *size);
209static	int		cdreadtoc(struct cam_periph *periph, u_int32_t mode,
210				  u_int32_t start, struct cd_toc_entry *data,
211				  u_int32_t len);
212static	int		cdgetmode(struct cam_periph *periph,
213				  struct cd_mode_data *data, u_int32_t page);
214static	int		cdsetmode(struct cam_periph *periph,
215				  struct cd_mode_data *data);
216static	int		cdplay(struct cam_periph *periph, u_int32_t blk,
217			       u_int32_t len);
218static	int		cdreadsubchannel(struct cam_periph *periph,
219					 u_int32_t mode, u_int32_t format,
220					 int track,
221					 struct cd_sub_channel_info *data,
222					 u_int32_t len);
223static	int		cdplaymsf(struct cam_periph *periph, u_int32_t startm,
224				  u_int32_t starts, u_int32_t startf,
225				  u_int32_t endm, u_int32_t ends,
226				  u_int32_t endf);
227static	int		cdplaytracks(struct cam_periph *periph,
228				     u_int32_t strack, u_int32_t sindex,
229				     u_int32_t etrack, u_int32_t eindex);
230static	int		cdpause(struct cam_periph *periph, u_int32_t go);
231static	int		cdstopunit(struct cam_periph *periph, u_int32_t eject);
232static	int		cdstartunit(struct cam_periph *periph);
233
234static struct periph_driver cddriver =
235{
236	cdinit, "cd",
237	TAILQ_HEAD_INITIALIZER(cddriver.units), /* generation */ 0
238};
239
240DATA_SET(periphdriver_set, cddriver);
241
242/* For 2.2-stable support */
243#ifndef D_DISK
244#define D_DISK 0
245#endif
246static struct cdevsw cd_cdevsw = {
247	/* open */	cdopen,
248	/* close */	cdclose,
249	/* read */	physread,
250	/* write */	nowrite,
251	/* ioctl */	cdioctl,
252	/* stop */	nostop,
253	/* reset */	noreset,
254	/* devtotty */	nodevtotty,
255	/* poll */	nopoll,
256	/* mmap */	nommap,
257	/* strategy */	cdstrategy,
258	/* name */	"cd",
259	/* parms */	noparms,
260	/* maj */	CD_CDEV_MAJOR,
261	/* dump */	nodump,
262	/* psize */	nopsize,
263	/* flags */	D_DISK,
264	/* maxio */	0,
265	/* bmaj */	CD_BDEV_MAJOR
266};
267
268static struct extend_array *cdperiphs;
269static int num_changers;
270
271#ifndef CHANGER_MIN_BUSY_SECONDS
272#define CHANGER_MIN_BUSY_SECONDS	5
273#endif
274#ifndef CHANGER_MAX_BUSY_SECONDS
275#define CHANGER_MAX_BUSY_SECONDS	15
276#endif
277
278static int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS;
279static int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS;
280
281/*
282 * XXX KDM this CAM node should be moved if we ever get more CAM sysctl
283 * variables.
284 */
285SYSCTL_NODE(_kern, OID_AUTO, cam, CTLFLAG_RD, 0, "CAM Subsystem");
286SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver");
287SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer");
288SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW,
289	   &changer_min_busy_seconds, 0, "Minimum changer scheduling quantum");
290SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW,
291	   &changer_max_busy_seconds, 0, "Maximum changer scheduling quantum");
292
293struct cdchanger {
294	path_id_t			 path_id;
295	target_id_t			 target_id;
296	int				 num_devices;
297	struct camq			 devq;
298	struct timeval			 start_time;
299	struct cd_softc			 *cur_device;
300	struct callout_handle		 short_handle;
301	struct callout_handle		 long_handle;
302	volatile cd_changer_flags	 flags;
303	STAILQ_ENTRY(cdchanger)		 changer_links;
304	STAILQ_HEAD(chdevlist, cd_softc) chluns;
305};
306
307static STAILQ_HEAD(changerlist, cdchanger) changerq;
308
309void
310cdinit(void)
311{
312	cam_status status;
313	struct cam_path *path;
314
315	/*
316	 * Create our extend array for storing the devices we attach to.
317	 */
318	cdperiphs = cam_extend_new();
319	if (cdperiphs == NULL) {
320		printf("cd: Failed to alloc extend array!\n");
321		return;
322	}
323
324	/*
325	 * Install a global async callback.  This callback will
326	 * receive async callbacks like "new device found".
327	 */
328	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
329				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
330
331	if (status == CAM_REQ_CMP) {
332		struct ccb_setasync csa;
333
334                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
335                csa.ccb_h.func_code = XPT_SASYNC_CB;
336                csa.event_enable = AC_FOUND_DEVICE;
337                csa.callback = cdasync;
338                csa.callback_arg = NULL;
339                xpt_action((union ccb *)&csa);
340		status = csa.ccb_h.status;
341                xpt_free_path(path);
342        }
343
344	if (status != CAM_REQ_CMP) {
345		printf("cd: Failed to attach master async callback "
346		       "due to status 0x%x!\n", status);
347	} else {
348		/* If we were successfull, register our devsw */
349		cdevsw_add_generic(CD_BDEV_MAJOR, CD_CDEV_MAJOR, &cd_cdevsw);
350	}
351}
352
353static void
354cdoninvalidate(struct cam_periph *periph)
355{
356	int s;
357	struct cd_softc *softc;
358	struct buf *q_bp;
359	struct ccb_setasync csa;
360
361	softc = (struct cd_softc *)periph->softc;
362
363	/*
364	 * De-register any async callbacks.
365	 */
366	xpt_setup_ccb(&csa.ccb_h, periph->path,
367		      /* priority */ 5);
368	csa.ccb_h.func_code = XPT_SASYNC_CB;
369	csa.event_enable = 0;
370	csa.callback = cdasync;
371	csa.callback_arg = periph;
372	xpt_action((union ccb *)&csa);
373
374	softc->flags |= CD_FLAG_INVALID;
375
376	/*
377	 * Although the oninvalidate() routines are always called at
378	 * splsoftcam, we need to be at splbio() here to keep the buffer
379	 * queue from being modified while we traverse it.
380	 */
381	s = splbio();
382
383	/*
384	 * Return all queued I/O with ENXIO.
385	 * XXX Handle any transactions queued to the card
386	 *     with XPT_ABORT_CCB.
387	 */
388	while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
389		bufq_remove(&softc->buf_queue, q_bp);
390		q_bp->b_resid = q_bp->b_bcount;
391		q_bp->b_error = ENXIO;
392		q_bp->b_flags |= B_ERROR;
393		biodone(q_bp);
394	}
395	splx(s);
396
397	/*
398	 * If this device is part of a changer, and it was scheduled
399	 * to run, remove it from the run queue since we just nuked
400	 * all of its scheduled I/O.
401	 */
402	if ((softc->flags & CD_FLAG_CHANGER)
403	 && (softc->pinfo.index != CAM_UNQUEUED_INDEX))
404		camq_remove(&softc->changer->devq, softc->pinfo.index);
405
406	xpt_print_path(periph->path);
407	printf("lost device\n");
408}
409
410static void
411cdcleanup(struct cam_periph *periph)
412{
413	struct cd_softc *softc;
414	int s;
415
416	softc = (struct cd_softc *)periph->softc;
417
418	xpt_print_path(periph->path);
419	printf("removing device entry\n");
420
421	s = splsoftcam();
422	/*
423	 * In the queued, non-active case, the device in question
424	 * has already been removed from the changer run queue.  Since this
425	 * device is active, we need to de-activate it, and schedule
426	 * another device to run.  (if there is another one to run)
427	 */
428	if ((softc->flags & CD_FLAG_CHANGER)
429	 && (softc->flags & CD_FLAG_ACTIVE)) {
430
431		/*
432		 * The purpose of the short timeout is soley to determine
433		 * whether the current device has finished or not.  Well,
434		 * since we're removing the active device, we know that it
435		 * is finished.  So, get rid of the short timeout.
436		 * Otherwise, if we're in the time period before the short
437		 * timeout fires, and there are no other devices in the
438		 * queue to run, there won't be any other device put in the
439		 * active slot.  i.e., when we call cdrunchangerqueue()
440		 * below, it won't do anything.  Then, when the short
441		 * timeout fires, it'll look at the "current device", which
442		 * we are free below, and possibly panic the kernel on a
443		 * bogus pointer reference.
444		 *
445		 * The long timeout doesn't really matter, since we
446		 * decrement the qfrozen_cnt to indicate that there is
447		 * nothing in the active slot now.  Therefore, there won't
448		 * be any bogus pointer references there.
449		 */
450		if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
451			untimeout(cdshorttimeout, softc->changer,
452				  softc->changer->short_handle);
453			softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
454		}
455		softc->changer->devq.qfrozen_cnt--;
456		softc->changer->flags |= CHANGER_MANUAL_CALL;
457		cdrunchangerqueue(softc->changer);
458	}
459
460	/*
461	 * If we're removing the last device on the changer, go ahead and
462	 * remove the changer device structure.
463	 */
464	if ((softc->flags & CD_FLAG_CHANGER)
465	 && (--softc->changer->num_devices == 0)) {
466
467		/*
468		 * Theoretically, there shouldn't be any timeouts left, but
469		 * I'm not completely sure that that will be the case.  So,
470		 * it won't hurt to check and see if there are any left.
471		 */
472		if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) {
473			untimeout(cdrunchangerqueue, softc->changer,
474				  softc->changer->long_handle);
475			softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED;
476		}
477
478		if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
479			untimeout(cdshorttimeout, softc->changer,
480				  softc->changer->short_handle);
481			softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
482		}
483
484		STAILQ_REMOVE(&changerq, softc->changer, cdchanger,
485			      changer_links);
486		xpt_print_path(periph->path);
487		printf("removing changer entry\n");
488		free(softc->changer, M_DEVBUF);
489		num_changers--;
490	}
491	devstat_remove_entry(&softc->device_stats);
492	cam_extend_release(cdperiphs, periph->unit_number);
493	free(softc, M_DEVBUF);
494	splx(s);
495}
496
497static void
498cdasync(void *callback_arg, u_int32_t code,
499	struct cam_path *path, void *arg)
500{
501	struct cam_periph *periph;
502
503	periph = (struct cam_periph *)callback_arg;
504	switch (code) {
505	case AC_FOUND_DEVICE:
506	{
507		struct ccb_getdev *cgd;
508		cam_status status;
509
510		cgd = (struct ccb_getdev *)arg;
511
512		if ((cgd->pd_type != T_CDROM) && (cgd->pd_type != T_WORM))
513			break;
514
515		/*
516		 * Allocate a peripheral instance for
517		 * this device and start the probe
518		 * process.
519		 */
520		status = cam_periph_alloc(cdregister, cdoninvalidate,
521					  cdcleanup, cdstart,
522					  "cd", CAM_PERIPH_BIO,
523					  cgd->ccb_h.path, cdasync,
524					  AC_FOUND_DEVICE, cgd);
525
526		if (status != CAM_REQ_CMP
527		 && status != CAM_REQ_INPROG)
528			printf("cdasync: Unable to attach new device "
529			       "due to status 0x%x\n", status);
530
531		break;
532	}
533	case AC_SENT_BDR:
534	case AC_BUS_RESET:
535	{
536		struct cd_softc *softc;
537		struct ccb_hdr *ccbh;
538		int s;
539
540		softc = (struct cd_softc *)periph->softc;
541		s = splsoftcam();
542		/*
543		 * Don't fail on the expected unit attention
544		 * that will occur.
545		 */
546		softc->flags |= CD_FLAG_RETRY_UA;
547		for (ccbh = LIST_FIRST(&softc->pending_ccbs);
548		     ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
549			ccbh->ccb_state |= CD_CCB_RETRY_UA;
550		splx(s);
551		/* FALLTHROUGH */
552	}
553	default:
554		cam_periph_async(periph, code, path, arg);
555		break;
556	}
557}
558
559static cam_status
560cdregister(struct cam_periph *periph, void *arg)
561{
562	struct cd_softc *softc;
563	struct ccb_setasync csa;
564	struct ccb_getdev *cgd;
565	caddr_t match;
566
567	cgd = (struct ccb_getdev *)arg;
568	if (periph == NULL) {
569		printf("cdregister: periph was NULL!!\n");
570		return(CAM_REQ_CMP_ERR);
571	}
572	if (cgd == NULL) {
573		printf("cdregister: no getdev CCB, can't register device\n");
574		return(CAM_REQ_CMP_ERR);
575	}
576
577	softc = (struct cd_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
578
579	if (softc == NULL) {
580		printf("cdregister: Unable to probe new device. "
581		       "Unable to allocate softc\n");
582		return(CAM_REQ_CMP_ERR);
583	}
584
585	bzero(softc, sizeof(*softc));
586	LIST_INIT(&softc->pending_ccbs);
587	softc->state = CD_STATE_PROBE;
588	bufq_init(&softc->buf_queue);
589	if (SID_IS_REMOVABLE(&cgd->inq_data))
590		softc->flags |= CD_FLAG_DISC_REMOVABLE;
591	if ((cgd->inq_data.flags & SID_CmdQue) != 0)
592		softc->flags |= CD_FLAG_TAGGED_QUEUING;
593
594	periph->softc = softc;
595	softc->periph = periph;
596
597	cam_extend_set(cdperiphs, periph->unit_number, periph);
598
599	/*
600	 * See if this device has any quirks.
601	 */
602	match = cam_quirkmatch((caddr_t)&cgd->inq_data,
603			       (caddr_t)cd_quirk_table,
604			       sizeof(cd_quirk_table)/sizeof(*cd_quirk_table),
605			       sizeof(*cd_quirk_table), scsi_inquiry_match);
606
607	if (match != NULL)
608		softc->quirks = ((struct cd_quirk_entry *)match)->quirks;
609	else
610		softc->quirks = CD_Q_NONE;
611
612	/*
613	 * We need to register the statistics structure for this device,
614	 * but we don't have the blocksize yet for it.  So, we register
615	 * the structure and indicate that we don't have the blocksize
616	 * yet.  Unlike other SCSI peripheral drivers, we explicitly set
617	 * the device type here to be CDROM, rather than just ORing in
618	 * cgd->pd_type.  This is because this driver can attach to either
619	 * CDROM or WORM devices, and we want this peripheral driver to
620	 * show up in the devstat list as a CD peripheral driver, not a
621	 * WORM peripheral driver.  WORM drives will also have the WORM
622	 * driver attached to them.
623	 */
624	devstat_add_entry(&softc->device_stats, "cd",
625			  periph->unit_number, 0,
626	  		  DEVSTAT_BS_UNAVAILABLE,
627			  DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_SCSI,
628			  DEVSTAT_PRIORITY_CD);
629
630	/*
631	 * Add an async callback so that we get
632	 * notified if this device goes away.
633	 */
634	xpt_setup_ccb(&csa.ccb_h, periph->path,
635		      /* priority */ 5);
636	csa.ccb_h.func_code = XPT_SASYNC_CB;
637	csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
638	csa.callback = cdasync;
639	csa.callback_arg = periph;
640	xpt_action((union ccb *)&csa);
641
642	/*
643	 * If the target lun is greater than 0, we most likely have a CD
644	 * changer device.  Check the quirk entries as well, though, just
645	 * in case someone has a CD tower with one lun per drive or
646	 * something like that.  Also, if we know up front that a
647	 * particular device is a changer, we can mark it as such starting
648	 * with lun 0, instead of lun 1.  It shouldn't be necessary to have
649	 * a quirk entry to define something as a changer, however.
650	 */
651	if (((cgd->ccb_h.target_lun > 0)
652	  && ((softc->quirks & CD_Q_NO_CHANGER) == 0))
653	 || ((softc->quirks & CD_Q_CHANGER) != 0)) {
654		struct cdchanger *nchanger;
655		struct cam_periph *nperiph;
656		struct cam_path *path;
657		cam_status status;
658		int found;
659
660		/* Set the changer flag in the current device's softc */
661		softc->flags |= CD_FLAG_CHANGER;
662
663		if (num_changers == 0)
664			STAILQ_INIT(&changerq);
665
666		/*
667		 * Now, look around for an existing changer device with the
668		 * same path and target ID as the current device.
669		 */
670		for (found = 0,
671		     nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq);
672		     nchanger != NULL;
673		     nchanger = STAILQ_NEXT(nchanger, changer_links)){
674			if ((nchanger->path_id == cgd->ccb_h.path_id)
675			 && (nchanger->target_id == cgd->ccb_h.target_id)) {
676				found = 1;
677				break;
678			}
679		}
680
681		/*
682		 * If we found a matching entry, just add this device to
683		 * the list of devices on this changer.
684		 */
685		if (found == 1) {
686			struct chdevlist *chlunhead;
687
688			chlunhead = &nchanger->chluns;
689
690			/*
691			 * XXX KDM look at consolidating this code with the
692			 * code below in a separate function.
693			 */
694
695			/*
696			 * Create a path with lun id 0, and see if we can
697			 * find a matching device
698			 */
699			status = xpt_create_path(&path, /*periph*/ periph,
700						 cgd->ccb_h.path_id,
701						 cgd->ccb_h.target_id, 0);
702
703			if ((status == CAM_REQ_CMP)
704			 && ((nperiph = cam_periph_find(path, "cd")) != NULL)){
705				struct cd_softc *nsoftc;
706
707				nsoftc = (struct cd_softc *)nperiph->softc;
708
709				if ((nsoftc->flags & CD_FLAG_CHANGER) == 0){
710					nsoftc->flags |= CD_FLAG_CHANGER;
711					nchanger->num_devices++;
712					if (camq_resize(&nchanger->devq,
713					   nchanger->num_devices)!=CAM_REQ_CMP){
714						printf("cdregister: "
715						       "camq_resize "
716						       "failed, changer "
717						       "support may "
718						       "be messed up\n");
719					}
720					nsoftc->changer = nchanger;
721					nsoftc->pinfo.index =CAM_UNQUEUED_INDEX;
722
723					STAILQ_INSERT_TAIL(&nchanger->chluns,
724							  nsoftc,changer_links);
725				}
726			} else if (status == CAM_REQ_CMP)
727				xpt_free_path(path);
728			else {
729				printf("cdregister: unable to allocate path\n"
730				       "cdregister: changer support may be "
731				       "broken\n");
732			}
733
734			nchanger->num_devices++;
735
736			softc->changer = nchanger;
737			softc->pinfo.index = CAM_UNQUEUED_INDEX;
738
739			if (camq_resize(&nchanger->devq,
740			    nchanger->num_devices) != CAM_REQ_CMP) {
741				printf("cdregister: camq_resize "
742				       "failed, changer support may "
743				       "be messed up\n");
744			}
745
746			STAILQ_INSERT_TAIL(chlunhead, softc, changer_links);
747		}
748		/*
749		 * In this case, we don't already have an entry for this
750		 * particular changer, so we need to create one, add it to
751		 * the queue, and queue this device on the list for this
752		 * changer.  Before we queue this device, however, we need
753		 * to search for lun id 0 on this target, and add it to the
754		 * queue first, if it exists.  (and if it hasn't already
755		 * been marked as part of the changer.)
756		 */
757		else {
758			nchanger = malloc(sizeof(struct cdchanger),
759				M_DEVBUF, M_NOWAIT);
760
761			if (nchanger == NULL) {
762				softc->flags &= ~CD_FLAG_CHANGER;
763				printf("cdregister: unable to malloc "
764				       "changer structure\ncdregister: "
765				       "changer support disabled\n");
766
767				/*
768				 * Yes, gotos can be gross but in this case
769				 * I think it's justified..
770				 */
771				goto cdregisterexit;
772			}
773
774			/* zero the structure */
775			bzero(nchanger, sizeof(struct cdchanger));
776
777			if (camq_init(&nchanger->devq, 1) != 0) {
778				softc->flags &= ~CD_FLAG_CHANGER;
779				printf("cdregister: changer support "
780				       "disabled\n");
781				goto cdregisterexit;
782			}
783
784			num_changers++;
785
786			nchanger->path_id = cgd->ccb_h.path_id;
787			nchanger->target_id = cgd->ccb_h.target_id;
788
789			/* this is superfluous, but it makes things clearer */
790			nchanger->num_devices = 0;
791
792			STAILQ_INIT(&nchanger->chluns);
793
794			STAILQ_INSERT_TAIL(&changerq, nchanger,
795					   changer_links);
796
797			/*
798			 * Create a path with lun id 0, and see if we can
799			 * find a matching device
800			 */
801			status = xpt_create_path(&path, /*periph*/ periph,
802						 cgd->ccb_h.path_id,
803						 cgd->ccb_h.target_id, 0);
804
805			/*
806			 * If we were able to allocate the path, and if we
807			 * find a matching device and it isn't already
808			 * marked as part of a changer, then we add it to
809			 * the current changer.
810			 */
811			if ((status == CAM_REQ_CMP)
812			 && ((nperiph = cam_periph_find(path, "cd")) != NULL)
813			 && ((((struct cd_softc *)periph->softc)->flags &
814			       CD_FLAG_CHANGER) == 0)) {
815				struct cd_softc *nsoftc;
816
817				nsoftc = (struct cd_softc *)nperiph->softc;
818
819				nsoftc->flags |= CD_FLAG_CHANGER;
820				nchanger->num_devices++;
821				if (camq_resize(&nchanger->devq,
822				    nchanger->num_devices) != CAM_REQ_CMP) {
823					printf("cdregister: camq_resize "
824					       "failed, changer support may "
825					       "be messed up\n");
826				}
827				nsoftc->changer = nchanger;
828				nsoftc->pinfo.index = CAM_UNQUEUED_INDEX;
829
830				STAILQ_INSERT_TAIL(&nchanger->chluns,
831						   nsoftc, changer_links);
832			} else if (status == CAM_REQ_CMP)
833				xpt_free_path(path);
834			else {
835				printf("cdregister: unable to allocate path\n"
836				       "cdregister: changer support may be "
837				       "broken\n");
838			}
839
840			softc->changer = nchanger;
841			softc->pinfo.index = CAM_UNQUEUED_INDEX;
842			nchanger->num_devices++;
843			if (camq_resize(&nchanger->devq,
844			    nchanger->num_devices) != CAM_REQ_CMP) {
845				printf("cdregister: camq_resize "
846				       "failed, changer support may "
847				       "be messed up\n");
848			}
849			STAILQ_INSERT_TAIL(&nchanger->chluns, softc,
850					   changer_links);
851		}
852	}
853
854cdregisterexit:
855
856	/* Lock this peripheral until we are setup */
857	/* Can't block */
858	cam_periph_lock(periph, PRIBIO);
859
860	if ((softc->flags & CD_FLAG_CHANGER) == 0)
861		xpt_schedule(periph, /*priority*/5);
862	else
863		cdschedule(periph, /*priority*/ 5);
864
865	return(CAM_REQ_CMP);
866}
867
868static int
869cdopen(dev_t dev, int flags, int fmt, struct proc *p)
870{
871	struct disklabel label;
872	struct cam_periph *periph;
873	struct cd_softc *softc;
874	struct ccb_getdev cgd;
875	u_int32_t size;
876	int unit, error;
877	int s;
878
879	unit = dkunit(dev);
880	periph = cam_extend_get(cdperiphs, unit);
881
882	if (periph == NULL)
883		return (ENXIO);
884
885	softc = (struct cd_softc *)periph->softc;
886
887	/*
888	 * Grab splsoftcam and hold it until we lock the peripheral.
889	 */
890	s = splsoftcam();
891	if (softc->flags & CD_FLAG_INVALID) {
892		splx(s);
893		return(ENXIO);
894	}
895
896	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) {
897		splx(s);
898		return (error);
899	}
900
901	splx(s);
902
903	if ((softc->flags & CD_FLAG_OPEN) == 0) {
904		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
905			return(ENXIO);
906		softc->flags |= CD_FLAG_OPEN;
907
908		cdprevent(periph, PR_PREVENT);
909	}
910
911	/* find out the size */
912	if ((error = cdsize(dev, &size)) != 0) {
913		if (dsisopen(softc->cd_slices) == 0) {
914			cdprevent(periph, PR_ALLOW);
915			softc->flags &= ~CD_FLAG_OPEN;
916		}
917		cam_periph_unlock(periph);
918
919		if ((softc->flags & CD_FLAG_OPEN) == 0)
920			cam_periph_release(periph);
921
922		return(error);
923	}
924
925	/*
926	 * Build prototype label for whole disk.
927	 * Should take information about different data tracks from the
928	 * TOC and put it in the partition table.
929	 */
930	bzero(&label, sizeof(label));
931	label.d_type = DTYPE_SCSI;
932
933	/*
934	 * Grab the inquiry data to get the vendor and product names.
935	 * Put them in the typename and packname for the label.
936	 */
937	xpt_setup_ccb(&cgd.ccb_h, periph->path, /*priority*/ 1);
938	cgd.ccb_h.func_code = XPT_GDEV_TYPE;
939	xpt_action((union ccb *)&cgd);
940
941	strncpy(label.d_typename, cgd.inq_data.vendor,
942		min(SID_VENDOR_SIZE, sizeof(label.d_typename)));
943	strncpy(label.d_packname, cgd.inq_data.product,
944		min(SID_PRODUCT_SIZE, sizeof(label.d_packname)));
945
946	label.d_secsize = softc->params.blksize;
947	label.d_secperunit = softc->params.disksize;
948	label.d_flags = D_REMOVABLE;
949	/*
950	 * Make partition 'a' cover the whole disk.  This is a temporary
951	 * compatibility hack.  The 'a' partition should not exist, so
952	 * the slice code won't create it.  The slice code will make
953	 * partition (RAW_PART + 'a') cover the whole disk and fill in
954	 * some more defaults.
955	 */
956	label.d_partitions[0].p_size = label.d_secperunit;
957	label.d_partitions[0].p_fstype = FS_OTHER;
958
959	/* Initialize slice tables. */
960	error = dsopen("cd", dev, fmt, DSO_NOLABELS | DSO_ONESLICE,
961		       &softc->cd_slices, &label, cdstrategy1,
962		       (ds_setgeom_t *)NULL, &cd_cdevsw);
963
964	if (error == 0) {
965		/*
966		 * We unconditionally (re)set the blocksize each time the
967		 * CD device is opened.  This is because the CD can change,
968		 * and therefore the blocksize might change.
969		 * XXX problems here if some slice or partition is still
970		 * open with the old size?
971		 */
972		if ((softc->device_stats.flags & DEVSTAT_BS_UNAVAILABLE) != 0)
973			softc->device_stats.flags &= ~DEVSTAT_BS_UNAVAILABLE;
974		softc->device_stats.block_size = softc->params.blksize;
975	} else {
976		if ((dsisopen(softc->cd_slices) == 0)
977		 && ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0))
978			cdprevent(periph, PR_ALLOW);
979	}
980
981	cam_periph_unlock(periph);
982
983	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
984
985	return (error);
986}
987
988static int
989cdclose(dev_t dev, int flag, int fmt, struct proc *p)
990{
991	struct 	cam_periph *periph;
992	struct	cd_softc *softc;
993	int	unit, error;
994
995	unit = dkunit(dev);
996	periph = cam_extend_get(cdperiphs, unit);
997	if (periph == NULL)
998		return (ENXIO);
999
1000	softc = (struct cd_softc *)periph->softc;
1001
1002	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
1003		return (error);
1004
1005	dsclose(dev, fmt, softc->cd_slices);
1006	if (dsisopen(softc->cd_slices)) {
1007		cam_periph_unlock(periph);
1008		return (0);
1009	}
1010
1011	if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0)
1012		cdprevent(periph, PR_ALLOW);
1013
1014	/*
1015	 * Since we're closing this CD, mark the blocksize as unavailable.
1016	 * It will be marked as available whence the CD is opened again.
1017	 */
1018	softc->device_stats.flags |= DEVSTAT_BS_UNAVAILABLE;
1019
1020	softc->flags &= ~CD_FLAG_OPEN;
1021	cam_periph_unlock(periph);
1022	cam_periph_release(periph);
1023
1024	return (0);
1025}
1026
1027static void
1028cdshorttimeout(void *arg)
1029{
1030	struct cdchanger *changer;
1031	int s;
1032
1033	s = splsoftcam();
1034
1035	changer = (struct cdchanger *)arg;
1036
1037	/* Always clear the short timeout flag, since that's what we're in */
1038	changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
1039
1040	/*
1041	 * Check to see if there is any more pending or outstanding I/O for
1042	 * this device.  If not, move it out of the active slot.
1043	 */
1044	if ((bufq_first(&changer->cur_device->buf_queue) == NULL)
1045	 && (changer->cur_device->device_stats.busy_count == 0)) {
1046		changer->flags |= CHANGER_MANUAL_CALL;
1047		cdrunchangerqueue(changer);
1048	}
1049
1050	splx(s);
1051}
1052
1053/*
1054 * This is a wrapper for xpt_schedule.  It only applies to changers.
1055 */
1056static void
1057cdschedule(struct cam_periph *periph, int priority)
1058{
1059	struct cd_softc *softc;
1060	int s;
1061
1062	s = splsoftcam();
1063
1064	softc = (struct cd_softc *)periph->softc;
1065
1066	/*
1067	 * If this device isn't currently queued, and if it isn't
1068	 * the active device, then we queue this device and run the
1069	 * changer queue if there is no timeout scheduled to do it.
1070	 * If this device is the active device, just schedule it
1071	 * to run again.  If this device is queued, there should be
1072	 * a timeout in place already that will make sure it runs.
1073	 */
1074	if ((softc->pinfo.index == CAM_UNQUEUED_INDEX)
1075	 && ((softc->flags & CD_FLAG_ACTIVE) == 0)) {
1076		/*
1077		 * We don't do anything with the priority here.
1078		 * This is strictly a fifo queue.
1079		 */
1080		softc->pinfo.priority = 1;
1081		softc->pinfo.generation = ++softc->changer->devq.generation;
1082		camq_insert(&softc->changer->devq, (cam_pinfo *)softc);
1083
1084		/*
1085		 * Since we just put a device in the changer queue,
1086		 * check and see if there is a timeout scheduled for
1087		 * this changer.  If so, let the timeout handle
1088		 * switching this device into the active slot.  If
1089		 * not, manually call the timeout routine to
1090		 * bootstrap things.
1091		 */
1092		if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0)
1093		 && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0)
1094		 && ((softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED)==0)){
1095			softc->changer->flags |= CHANGER_MANUAL_CALL;
1096			cdrunchangerqueue(softc->changer);
1097		}
1098	} else if ((softc->flags & CD_FLAG_ACTIVE)
1099		&& ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0))
1100		xpt_schedule(periph, priority);
1101
1102	splx(s);
1103
1104}
1105
1106static void
1107cdrunchangerqueue(void *arg)
1108{
1109	struct cd_softc *softc;
1110	struct cdchanger *changer;
1111	int called_from_timeout;
1112	int s;
1113
1114	s = splsoftcam();
1115
1116	changer = (struct cdchanger *)arg;
1117
1118	/*
1119	 * If we have NOT been called from cdstrategy() or cddone(), and
1120	 * instead from a timeout routine, go ahead and clear the
1121	 * timeout flag.
1122	 */
1123	if ((changer->flags & CHANGER_MANUAL_CALL) == 0) {
1124		changer->flags &= ~CHANGER_TIMEOUT_SCHED;
1125		called_from_timeout = 1;
1126	} else
1127		called_from_timeout = 0;
1128
1129	/* Always clear the manual call flag */
1130	changer->flags &= ~CHANGER_MANUAL_CALL;
1131
1132	/* nothing to do if the queue is empty */
1133	if (changer->devq.entries <= 0) {
1134		splx(s);
1135		return;
1136	}
1137
1138	/*
1139	 * If the changer queue is frozen, that means we have an active
1140	 * device.
1141	 */
1142	if (changer->devq.qfrozen_cnt > 0) {
1143
1144		if (changer->cur_device->device_stats.busy_count > 0) {
1145			changer->cur_device->flags |= CD_FLAG_SCHED_ON_COMP;
1146			changer->cur_device->bufs_left =
1147				changer->cur_device->device_stats.busy_count;
1148			if (called_from_timeout) {
1149				changer->long_handle =
1150					timeout(cdrunchangerqueue, changer,
1151				        changer_max_busy_seconds * hz);
1152				changer->flags |= CHANGER_TIMEOUT_SCHED;
1153			}
1154			splx(s);
1155			return;
1156		}
1157
1158		/*
1159		 * We always need to reset the frozen count and clear the
1160		 * active flag.
1161		 */
1162		changer->devq.qfrozen_cnt--;
1163		changer->cur_device->flags &= ~CD_FLAG_ACTIVE;
1164		changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP;
1165
1166		/*
1167		 * Check to see whether the current device has any I/O left
1168		 * to do.  If so, requeue it at the end of the queue.  If
1169		 * not, there is no need to requeue it.
1170		 */
1171		if (bufq_first(&changer->cur_device->buf_queue) != NULL) {
1172
1173			changer->cur_device->pinfo.generation =
1174				++changer->devq.generation;
1175			camq_insert(&changer->devq,
1176				(cam_pinfo *)changer->cur_device);
1177		}
1178	}
1179
1180	softc = (struct cd_softc *)camq_remove(&changer->devq, CAMQ_HEAD);
1181
1182	changer->cur_device = softc;
1183
1184	changer->devq.qfrozen_cnt++;
1185	softc->flags |= CD_FLAG_ACTIVE;
1186
1187	/* Just in case this device is waiting */
1188	wakeup(&softc->changer);
1189	xpt_schedule(softc->periph, /*priority*/ 1);
1190
1191	/*
1192	 * Get rid of any pending timeouts, and set a flag to schedule new
1193	 * ones so this device gets its full time quantum.
1194	 */
1195	if (changer->flags & CHANGER_TIMEOUT_SCHED) {
1196		untimeout(cdrunchangerqueue, changer, changer->long_handle);
1197		changer->flags &= ~CHANGER_TIMEOUT_SCHED;
1198	}
1199
1200	if (changer->flags & CHANGER_SHORT_TMOUT_SCHED) {
1201		untimeout(cdshorttimeout, changer, changer->short_handle);
1202		changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
1203	}
1204
1205	/*
1206	 * We need to schedule timeouts, but we only do this after the
1207	 * first transaction has completed.  This eliminates the changer
1208	 * switch time.
1209	 */
1210	changer->flags |= CHANGER_NEED_TIMEOUT;
1211
1212	splx(s);
1213}
1214
1215static void
1216cdchangerschedule(struct cd_softc *softc)
1217{
1218	struct cdchanger *changer;
1219	int s;
1220
1221	s = splsoftcam();
1222
1223	changer = softc->changer;
1224
1225	/*
1226	 * If this is a changer, and this is the current device,
1227	 * and this device has at least the minimum time quantum to
1228	 * run, see if we can switch it out.
1229	 */
1230	if ((softc->flags & CD_FLAG_ACTIVE)
1231	 && ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0)
1232	 && ((changer->flags & CHANGER_NEED_TIMEOUT) == 0)) {
1233		/*
1234		 * We try three things here.  The first is that we
1235		 * check to see whether the schedule on completion
1236		 * flag is set.  If it is, we decrement the number
1237		 * of buffers left, and if it's zero, we reschedule.
1238		 * Next, we check to see whether the pending buffer
1239		 * queue is empty and whether there are no
1240		 * outstanding transactions.  If so, we reschedule.
1241		 * Next, we see if the pending buffer queue is empty.
1242		 * If it is, we set the number of buffers left to
1243		 * the current active buffer count and set the
1244		 * schedule on complete flag.
1245		 */
1246		if (softc->flags & CD_FLAG_SCHED_ON_COMP) {
1247		 	if (--softc->bufs_left == 0) {
1248				softc->changer->flags |=
1249					CHANGER_MANUAL_CALL;
1250				softc->flags &= ~CD_FLAG_SCHED_ON_COMP;
1251				cdrunchangerqueue(softc->changer);
1252			}
1253		} else if ((bufq_first(&softc->buf_queue) == NULL)
1254		        && (softc->device_stats.busy_count == 0)) {
1255			softc->changer->flags |= CHANGER_MANUAL_CALL;
1256			cdrunchangerqueue(softc->changer);
1257		}
1258	} else if ((softc->changer->flags & CHANGER_NEED_TIMEOUT)
1259		&& (softc->flags & CD_FLAG_ACTIVE)) {
1260
1261		/*
1262		 * Now that the first transaction to this
1263		 * particular device has completed, we can go ahead
1264		 * and schedule our timeouts.
1265		 */
1266		if ((changer->flags & CHANGER_TIMEOUT_SCHED) == 0) {
1267			changer->long_handle =
1268			    timeout(cdrunchangerqueue, changer,
1269				    changer_max_busy_seconds * hz);
1270			changer->flags |= CHANGER_TIMEOUT_SCHED;
1271		} else
1272			printf("cdchangerschedule: already have a long"
1273			       " timeout!\n");
1274
1275		if ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) {
1276			changer->short_handle =
1277			    timeout(cdshorttimeout, changer,
1278				    changer_min_busy_seconds * hz);
1279			changer->flags |= CHANGER_SHORT_TMOUT_SCHED;
1280		} else
1281			printf("cdchangerschedule: already have a short "
1282			       "timeout!\n");
1283
1284		/*
1285		 * We just scheduled timeouts, no need to schedule
1286		 * more.
1287		 */
1288		changer->flags &= ~CHANGER_NEED_TIMEOUT;
1289
1290	}
1291	splx(s);
1292}
1293
1294static int
1295cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb,
1296					      u_int32_t cam_flags,
1297					      u_int32_t sense_flags),
1298	 u_int32_t cam_flags, u_int32_t sense_flags)
1299{
1300	struct cd_softc *softc;
1301	struct cam_periph *periph;
1302	int error;
1303
1304	periph = xpt_path_periph(ccb->ccb_h.path);
1305	softc = (struct cd_softc *)periph->softc;
1306
1307	error = cam_periph_runccb(ccb, error_routine, cam_flags, sense_flags,
1308				  &softc->device_stats);
1309
1310	if (softc->flags & CD_FLAG_CHANGER)
1311		cdchangerschedule(softc);
1312
1313	return(error);
1314}
1315
1316static union ccb *
1317cdgetccb(struct cam_periph *periph, u_int32_t priority)
1318{
1319	struct cd_softc *softc;
1320	int s;
1321
1322	softc = (struct cd_softc *)periph->softc;
1323
1324	if (softc->flags & CD_FLAG_CHANGER) {
1325
1326		s = splsoftcam();
1327
1328		/*
1329		 * This should work the first time this device is woken up,
1330		 * but just in case it doesn't, we use a while loop.
1331		 */
1332		while ((softc->flags & CD_FLAG_ACTIVE) == 0) {
1333			/*
1334			 * If this changer isn't already queued, queue it up.
1335			 */
1336			if (softc->pinfo.index == CAM_UNQUEUED_INDEX) {
1337				softc->pinfo.priority = 1;
1338				softc->pinfo.generation =
1339					++softc->changer->devq.generation;
1340				camq_insert(&softc->changer->devq,
1341					    (cam_pinfo *)softc);
1342			}
1343			if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0)
1344			 && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0)
1345			 && ((softc->changer->flags
1346			      & CHANGER_SHORT_TMOUT_SCHED)==0)) {
1347				softc->changer->flags |= CHANGER_MANUAL_CALL;
1348				cdrunchangerqueue(softc->changer);
1349			} else
1350				tsleep(&softc->changer, PRIBIO, "cgticb", 0);
1351		}
1352		splx(s);
1353	}
1354	return(cam_periph_getccb(periph, priority));
1355}
1356
1357
1358/*
1359 * Actually translate the requested transfer into one the physical driver
1360 * can understand.  The transfer is described by a buf and will include
1361 * only one physical transfer.
1362 */
1363static void
1364cdstrategy(struct buf *bp)
1365{
1366	struct cam_periph *periph;
1367	struct cd_softc *softc;
1368	u_int  unit, part;
1369	int    s;
1370
1371	unit = dkunit(bp->b_dev);
1372	part = dkpart(bp->b_dev);
1373	periph = cam_extend_get(cdperiphs, unit);
1374	if (periph == NULL) {
1375		bp->b_error = ENXIO;
1376		goto bad;
1377	}
1378
1379	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstrategy\n"));
1380
1381	softc = (struct cd_softc *)periph->softc;
1382
1383	/*
1384	 * Do bounds checking, adjust transfer, and set b_pbklno.
1385	 */
1386	if (dscheck(bp, softc->cd_slices) <= 0)
1387		goto done;
1388
1389	/*
1390	 * Mask interrupts so that the pack cannot be invalidated until
1391	 * after we are in the queue.  Otherwise, we might not properly
1392	 * clean up one of the buffers.
1393	 */
1394	s = splbio();
1395
1396	/*
1397	 * If the device has been made invalid, error out
1398	 */
1399	if ((softc->flags & CD_FLAG_INVALID)) {
1400		splx(s);
1401		bp->b_error = ENXIO;
1402		goto bad;
1403	}
1404
1405	/*
1406	 * Place it in the queue of disk activities for this disk
1407	 */
1408	bufqdisksort(&softc->buf_queue, bp);
1409
1410	splx(s);
1411
1412	/*
1413	 * Schedule ourselves for performing the work.  We do things
1414	 * differently for changers.
1415	 */
1416	if ((softc->flags & CD_FLAG_CHANGER) == 0)
1417		xpt_schedule(periph, /* XXX priority */1);
1418	else
1419		cdschedule(periph, /* priority */ 1);
1420
1421	return;
1422bad:
1423	bp->b_flags |= B_ERROR;
1424done:
1425	/*
1426	 * Correctly set the buf to indicate a completed xfer
1427	 */
1428	bp->b_resid = bp->b_bcount;
1429	biodone(bp);
1430	return;
1431}
1432
1433static void
1434cdstrategy1(struct buf *bp)
1435{
1436	/*
1437	 * XXX - do something to make cdstrategy() but not this block while
1438	 * we're doing dsopen() and dsioctl().
1439	 */
1440	cdstrategy(bp);
1441}
1442
1443static void
1444cdstart(struct cam_periph *periph, union ccb *start_ccb)
1445{
1446	struct cd_softc *softc;
1447	struct buf *bp;
1448	struct ccb_scsiio *csio;
1449	struct scsi_read_capacity_data *rcap;
1450	int s;
1451
1452	softc = (struct cd_softc *)periph->softc;
1453
1454	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstart\n"));
1455
1456	switch (softc->state) {
1457	case CD_STATE_NORMAL:
1458	{
1459		int oldspl;
1460
1461		s = splbio();
1462		bp = bufq_first(&softc->buf_queue);
1463		if (periph->immediate_priority <= periph->pinfo.priority) {
1464			start_ccb->ccb_h.ccb_state = CD_CCB_WAITING;
1465
1466			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
1467					  periph_links.sle);
1468			periph->immediate_priority = CAM_PRIORITY_NONE;
1469			splx(s);
1470			wakeup(&periph->ccb_list);
1471		} else if (bp == NULL) {
1472			splx(s);
1473			xpt_release_ccb(start_ccb);
1474		} else {
1475			bufq_remove(&softc->buf_queue, bp);
1476
1477			devstat_start_transaction(&softc->device_stats);
1478
1479			scsi_read_write(&start_ccb->csio,
1480					/*retries*/4,
1481					/* cbfcnp */ cddone,
1482					(bp->b_flags & B_ORDERED) != 0 ?
1483					    MSG_ORDERED_Q_TAG :
1484					    MSG_SIMPLE_Q_TAG,
1485					/* read */bp->b_flags & B_READ,
1486					/* byte2 */ 0,
1487					/* minimum_cmd_size */ 10,
1488					/* lba */ bp->b_pblkno,
1489					bp->b_bcount / softc->params.blksize,
1490					/* data_ptr */ bp->b_data,
1491					/* dxfer_len */ bp->b_bcount,
1492					/* sense_len */ SSD_FULL_SIZE,
1493					/* timeout */ 30000);
1494			start_ccb->ccb_h.ccb_state = CD_CCB_BUFFER_IO;
1495
1496
1497			/*
1498			 * Block out any asyncronous callbacks
1499			 * while we touch the pending ccb list.
1500			 */
1501			oldspl = splcam();
1502			LIST_INSERT_HEAD(&softc->pending_ccbs,
1503					 &start_ccb->ccb_h, periph_links.le);
1504			splx(oldspl);
1505
1506			/* We expect a unit attention from this device */
1507			if ((softc->flags & CD_FLAG_RETRY_UA) != 0) {
1508				start_ccb->ccb_h.ccb_state |= CD_CCB_RETRY_UA;
1509				softc->flags &= ~CD_FLAG_RETRY_UA;
1510			}
1511
1512			start_ccb->ccb_h.ccb_bp = bp;
1513			bp = bufq_first(&softc->buf_queue);
1514			splx(s);
1515
1516			xpt_action(start_ccb);
1517		}
1518		if (bp != NULL) {
1519			/* Have more work to do, so ensure we stay scheduled */
1520			xpt_schedule(periph, /* XXX priority */1);
1521		}
1522		break;
1523	}
1524	case CD_STATE_PROBE:
1525	{
1526
1527		rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
1528								M_TEMP,
1529								M_NOWAIT);
1530		if (rcap == NULL) {
1531			xpt_print_path(periph->path);
1532			printf("cdstart: Couldn't malloc read_capacity data\n");
1533			/* cd_free_periph??? */
1534			break;
1535		}
1536		csio = &start_ccb->csio;
1537		scsi_read_capacity(csio,
1538				   /*retries*/1,
1539				   cddone,
1540				   MSG_SIMPLE_Q_TAG,
1541				   rcap,
1542				   SSD_FULL_SIZE,
1543				   /*timeout*/20000);
1544		start_ccb->ccb_h.ccb_bp = NULL;
1545		start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
1546		xpt_action(start_ccb);
1547		break;
1548	}
1549	}
1550}
1551
1552static void
1553cddone(struct cam_periph *periph, union ccb *done_ccb)
1554{
1555	struct cd_softc *softc;
1556	struct ccb_scsiio *csio;
1557
1558	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cddone\n"));
1559
1560	softc = (struct cd_softc *)periph->softc;
1561	csio = &done_ccb->csio;
1562
1563	switch (csio->ccb_h.ccb_state & CD_CCB_TYPE_MASK) {
1564	case CD_CCB_BUFFER_IO:
1565	{
1566		struct buf	*bp;
1567		int		error;
1568		int		oldspl;
1569
1570		bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
1571		error = 0;
1572
1573		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
1574			int sf;
1575
1576			if ((done_ccb->ccb_h.ccb_state & CD_CCB_RETRY_UA) != 0)
1577				sf = SF_RETRY_UA;
1578			else
1579				sf = 0;
1580
1581			/* Retry selection timeouts */
1582			sf |= SF_RETRY_SELTO;
1583
1584			if ((error = cderror(done_ccb, 0, sf)) == ERESTART) {
1585				/*
1586				 * A retry was scheuled, so
1587				 * just return.
1588				 */
1589				return;
1590			}
1591		}
1592
1593		if (error != 0) {
1594			int s;
1595			struct buf *q_bp;
1596
1597			xpt_print_path(periph->path);
1598			printf("cddone: got error %#x back\n", error);
1599			s = splbio();
1600			while ((q_bp = bufq_first(&softc->buf_queue)) != NULL) {
1601				bufq_remove(&softc->buf_queue, q_bp);
1602				q_bp->b_resid = q_bp->b_bcount;
1603				q_bp->b_error = EIO;
1604				q_bp->b_flags |= B_ERROR;
1605				biodone(q_bp);
1606			}
1607			splx(s);
1608			bp->b_resid = bp->b_bcount;
1609			bp->b_error = error;
1610			bp->b_flags |= B_ERROR;
1611			cam_release_devq(done_ccb->ccb_h.path,
1612					 /*relsim_flags*/0,
1613					 /*reduction*/0,
1614					 /*timeout*/0,
1615					 /*getcount_only*/0);
1616
1617		} else {
1618			bp->b_resid = csio->resid;
1619			bp->b_error = 0;
1620			if (bp->b_resid != 0) {
1621				/* Short transfer ??? */
1622				bp->b_flags |= B_ERROR;
1623			}
1624		}
1625
1626		/*
1627		 * Block out any asyncronous callbacks
1628		 * while we touch the pending ccb list.
1629		 */
1630		oldspl = splcam();
1631		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
1632		splx(oldspl);
1633
1634		devstat_end_transaction(&softc->device_stats,
1635					bp->b_bcount - bp->b_resid,
1636					done_ccb->csio.tag_action & 0xf,
1637					(bp->b_flags & B_READ) ? DEVSTAT_READ
1638							       : DEVSTAT_WRITE);
1639
1640		if (softc->flags & CD_FLAG_CHANGER)
1641			cdchangerschedule(softc);
1642
1643		biodone(bp);
1644		break;
1645	}
1646	case CD_CCB_PROBE:
1647	{
1648		struct	   scsi_read_capacity_data *rdcap;
1649		char	   announce_buf[120]; /*
1650					       * Currently (9/30/97) the
1651					       * longest possible announce
1652					       * buffer is 108 bytes, for the
1653					       * first error case below.
1654					       * That is 39 bytes for the
1655					       * basic string, 16 bytes for the
1656					       * biggest sense key (hardware
1657					       * error), 52 bytes for the
1658					       * text of the largest sense
1659					       * qualifier valid for a CDROM,
1660					       * (0x72, 0x03 or 0x04,
1661					       * 0x03), and one byte for the
1662					       * null terminating character.
1663					       * To allow for longer strings,
1664					       * the announce buffer is 120
1665					       * bytes.
1666					       */
1667		struct	   cd_params *cdp;
1668
1669		cdp = &softc->params;
1670
1671		rdcap = (struct scsi_read_capacity_data *)csio->data_ptr;
1672
1673		cdp->disksize = scsi_4btoul (rdcap->addr) + 1;
1674		cdp->blksize = scsi_4btoul (rdcap->length);
1675
1676		if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
1677
1678			snprintf(announce_buf, sizeof(announce_buf),
1679				"cd present [%lu x %lu byte records]",
1680				cdp->disksize, (u_long)cdp->blksize);
1681
1682		} else {
1683			int	error;
1684			/*
1685			 * Retry any UNIT ATTENTION type errors.  They
1686			 * are expected at boot.
1687			 */
1688			error = cderror(done_ccb, 0, SF_RETRY_UA |
1689					SF_NO_PRINT | SF_RETRY_SELTO);
1690			if (error == ERESTART) {
1691				/*
1692				 * A retry was scheuled, so
1693				 * just return.
1694				 */
1695				return;
1696			} else if (error != 0) {
1697
1698				struct scsi_sense_data *sense;
1699				int asc, ascq;
1700				int sense_key, error_code;
1701				int have_sense;
1702				cam_status status;
1703				struct ccb_getdev cgd;
1704
1705				/* Don't wedge this device's queue */
1706				cam_release_devq(done_ccb->ccb_h.path,
1707						 /*relsim_flags*/0,
1708						 /*reduction*/0,
1709						 /*timeout*/0,
1710						 /*getcount_only*/0);
1711
1712				status = done_ccb->ccb_h.status;
1713
1714				xpt_setup_ccb(&cgd.ccb_h,
1715					      done_ccb->ccb_h.path,
1716					      /* priority */ 1);
1717				cgd.ccb_h.func_code = XPT_GDEV_TYPE;
1718				xpt_action((union ccb *)&cgd);
1719
1720				if (((csio->ccb_h.flags & CAM_SENSE_PHYS) != 0)
1721				 || ((csio->ccb_h.flags & CAM_SENSE_PTR) != 0)
1722				 || ((status & CAM_AUTOSNS_VALID) == 0))
1723					have_sense = FALSE;
1724				else
1725					have_sense = TRUE;
1726
1727				if (have_sense) {
1728					sense = &csio->sense_data;
1729					scsi_extract_sense(sense, &error_code,
1730							   &sense_key,
1731							   &asc, &ascq);
1732				}
1733				/*
1734				 * Attach to anything that claims to be a
1735				 * CDROM or WORM device, as long as it
1736				 * doesn't return a "Logical unit not
1737				 * supported" (0x25) error.
1738				 */
1739				if ((have_sense) && (asc != 0x25)
1740				 && (error_code == SSD_CURRENT_ERROR))
1741					snprintf(announce_buf,
1742					    sizeof(announce_buf),
1743						"Attempt to query device "
1744						"size failed: %s, %s",
1745						scsi_sense_key_text[sense_key],
1746						scsi_sense_desc(asc,ascq,
1747								&cgd.inq_data));
1748				else if (cgd.pd_type == T_CDROM) {
1749					/*
1750					 * We only print out an error for
1751					 * CDROM type devices.  For WORM
1752					 * devices, we don't print out an
1753					 * error since a few WORM devices
1754					 * don't support CDROM commands.
1755					 * If we have sense information, go
1756					 * ahead and print it out.
1757					 * Otherwise, just say that we
1758					 * couldn't attach.
1759					 */
1760
1761					/*
1762					 * Just print out the error, not
1763					 * the full probe message, when we
1764					 * don't attach.
1765					 */
1766					if (have_sense)
1767						scsi_sense_print(
1768							&done_ccb->csio);
1769					else {
1770						xpt_print_path(periph->path);
1771						printf("got CAM status %#x\n",
1772						       done_ccb->ccb_h.status);
1773					}
1774					xpt_print_path(periph->path);
1775					printf("fatal error, failed"
1776					       " to attach to device\n");
1777
1778					/*
1779					 * Invalidate this peripheral.
1780					 */
1781					cam_periph_invalidate(periph);
1782
1783					announce_buf[0] = '\0';
1784				} else {
1785
1786					/*
1787					 * Invalidate this peripheral.
1788					 */
1789					cam_periph_invalidate(periph);
1790					announce_buf[0] = '\0';
1791				}
1792			}
1793		}
1794		free(rdcap, M_TEMP);
1795		if (announce_buf[0] != '\0') {
1796			xpt_announce_periph(periph, announce_buf);
1797			if (softc->flags & CD_FLAG_CHANGER)
1798				cdchangerschedule(softc);
1799		}
1800		softc->state = CD_STATE_NORMAL;
1801		/*
1802		 * Since our peripheral may be invalidated by an error
1803		 * above or an external event, we must release our CCB
1804		 * before releasing the probe lock on the peripheral.
1805		 * The peripheral will only go away once the last lock
1806		 * is removed, and we need it around for the CCB release
1807		 * operation.
1808		 */
1809		xpt_release_ccb(done_ccb);
1810		cam_periph_unlock(periph);
1811		return;
1812	}
1813	case CD_CCB_WAITING:
1814	{
1815		/* Caller will release the CCB */
1816		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
1817			  ("trying to wakeup ccbwait\n"));
1818
1819		wakeup(&done_ccb->ccb_h.cbfcnp);
1820		return;
1821	}
1822	default:
1823		break;
1824	}
1825	xpt_release_ccb(done_ccb);
1826}
1827
1828static int
1829cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
1830{
1831
1832	struct 	cam_periph *periph;
1833	struct	cd_softc *softc;
1834	int	error, unit;
1835
1836	unit = dkunit(dev);
1837
1838	periph = cam_extend_get(cdperiphs, unit);
1839	if (periph == NULL)
1840		return(ENXIO);
1841
1842	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdioctl\n"));
1843
1844	softc = (struct cd_softc *)periph->softc;
1845
1846	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
1847		  ("trying to do ioctl %#lx\n", cmd));
1848
1849	error = cam_periph_lock(periph, PRIBIO | PCATCH);
1850
1851	if (error != 0)
1852		return(error);
1853
1854	switch (cmd) {
1855
1856	case CDIOCPLAYTRACKS:
1857		{
1858			struct ioc_play_track *args
1859			    = (struct ioc_play_track *) addr;
1860			struct cd_mode_data *data;
1861
1862			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
1863				      M_WAITOK);
1864
1865			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
1866				  ("trying to do CDIOCPLAYTRACKS\n"));
1867
1868			error = cdgetmode(periph, data, AUDIO_PAGE);
1869			if (error) {
1870				free(data, M_TEMP);
1871				break;
1872			}
1873			data->page.audio.flags &= ~CD_PA_SOTC;
1874			data->page.audio.flags |= CD_PA_IMMED;
1875			error = cdsetmode(periph, data);
1876			free(data, M_TEMP);
1877			if (error)
1878				break;
1879			if (softc->quirks & CD_Q_BCD_TRACKS) {
1880				args->start_track = bin2bcd(args->start_track);
1881				args->end_track = bin2bcd(args->end_track);
1882			}
1883			error = cdplaytracks(periph,
1884					     args->start_track,
1885					     args->start_index,
1886					     args->end_track,
1887					     args->end_index);
1888		}
1889		break;
1890	case CDIOCPLAYMSF:
1891		{
1892			struct ioc_play_msf *args
1893				= (struct ioc_play_msf *) addr;
1894			struct cd_mode_data *data;
1895
1896			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
1897				      M_WAITOK);
1898
1899			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
1900				  ("trying to do CDIOCPLAYMSF\n"));
1901
1902			error = cdgetmode(periph, data, AUDIO_PAGE);
1903			if (error) {
1904				free(data, M_TEMP);
1905				break;
1906			}
1907			data->page.audio.flags &= ~CD_PA_SOTC;
1908			data->page.audio.flags |= CD_PA_IMMED;
1909			error = cdsetmode(periph, data);
1910			free(data, M_TEMP);
1911			if (error)
1912				break;
1913			error = cdplaymsf(periph,
1914					  args->start_m,
1915					  args->start_s,
1916					  args->start_f,
1917					  args->end_m,
1918					  args->end_s,
1919					  args->end_f);
1920		}
1921		break;
1922	case CDIOCPLAYBLOCKS:
1923		{
1924			struct ioc_play_blocks *args
1925				= (struct ioc_play_blocks *) addr;
1926			struct cd_mode_data *data;
1927
1928			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
1929				  ("trying to do CDIOCPLAYBLOCKS\n"));
1930
1931			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
1932				      M_WAITOK);
1933
1934			error = cdgetmode(periph, data, AUDIO_PAGE);
1935			if (error) {
1936				free(data, M_TEMP);
1937				break;
1938			}
1939			data->page.audio.flags &= ~CD_PA_SOTC;
1940			data->page.audio.flags |= CD_PA_IMMED;
1941			error = cdsetmode(periph, data);
1942			free(data, M_TEMP);
1943			if (error)
1944				break;
1945			error = cdplay(periph, args->blk, args->len);
1946		}
1947		break;
1948	case CDIOCREADSUBCHANNEL:
1949		{
1950			struct ioc_read_subchannel *args
1951				= (struct ioc_read_subchannel *) addr;
1952			struct cd_sub_channel_info *data;
1953			u_int32_t len = args->data_len;
1954
1955			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
1956				  ("trying to do CDIOCREADSUBCHANNEL\n"));
1957
1958			data = malloc(sizeof(struct cd_sub_channel_info),
1959				      M_TEMP, M_WAITOK);
1960
1961			if ((len > sizeof(struct cd_sub_channel_info)) ||
1962			    (len < sizeof(struct cd_sub_channel_header))) {
1963				printf(
1964					"scsi_cd: cdioctl: "
1965					"cdioreadsubchannel: error, len=%d\n",
1966					len);
1967				error = EINVAL;
1968				free(data, M_TEMP);
1969				break;
1970			}
1971
1972			if (softc->quirks & CD_Q_BCD_TRACKS)
1973				args->track = bin2bcd(args->track);
1974
1975			error = cdreadsubchannel(periph, args->address_format,
1976				args->data_format, args->track, data, len);
1977
1978			if (error) {
1979				free(data, M_TEMP);
1980	 			break;
1981			}
1982			if (softc->quirks & CD_Q_BCD_TRACKS)
1983				data->what.track_info.track_number =
1984				    bcd2bin(data->what.track_info.track_number);
1985			len = min(len, ((data->header.data_len[0] << 8) +
1986				data->header.data_len[1] +
1987				sizeof(struct cd_sub_channel_header)));
1988			if (copyout(data, args->data, len) != 0) {
1989				error = EFAULT;
1990			}
1991			free(data, M_TEMP);
1992		}
1993		break;
1994
1995	case CDIOREADTOCHEADER:
1996		{
1997			struct ioc_toc_header *th;
1998
1999			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2000				  ("trying to do CDIOREADTOCHEADER\n"));
2001
2002			th = malloc(sizeof(struct ioc_toc_header), M_TEMP,
2003				    M_WAITOK);
2004			error = cdreadtoc(periph, 0, 0,
2005					  (struct cd_toc_entry *)th,
2006				          sizeof (*th));
2007			if (error) {
2008				free(th, M_TEMP);
2009				break;
2010			}
2011			if (softc->quirks & CD_Q_BCD_TRACKS) {
2012				/* we are going to have to convert the BCD
2013				 * encoding on the cd to what is expected
2014				 */
2015				th->starting_track =
2016					bcd2bin(th->starting_track);
2017				th->ending_track = bcd2bin(th->ending_track);
2018			}
2019			NTOHS(th->len);
2020			bcopy(th, addr, sizeof(*th));
2021			free(th, M_TEMP);
2022		}
2023		break;
2024	case CDIOREADTOCENTRYS:
2025		{
2026			typedef struct {
2027				struct ioc_toc_header header;
2028				struct cd_toc_entry entries[100];
2029			} data_t;
2030			typedef struct {
2031				struct ioc_toc_header header;
2032				struct cd_toc_entry entry;
2033			} lead_t;
2034
2035			data_t *data;
2036			lead_t *lead;
2037			struct ioc_read_toc_entry *te =
2038				(struct ioc_read_toc_entry *) addr;
2039			struct ioc_toc_header *th;
2040			u_int32_t len, readlen, idx, num;
2041			u_int32_t starting_track = te->starting_track;
2042
2043			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2044				  ("trying to do CDIOREADTOCENTRYS\n"));
2045
2046			data = malloc(sizeof(data_t), M_TEMP, M_WAITOK);
2047			lead = malloc(sizeof(lead_t), M_TEMP, M_WAITOK);
2048
2049			if (te->data_len < sizeof(struct cd_toc_entry)
2050			 || (te->data_len % sizeof(struct cd_toc_entry)) != 0
2051			 || (te->address_format != CD_MSF_FORMAT
2052			  && te->address_format != CD_LBA_FORMAT)) {
2053				error = EINVAL;
2054				printf("scsi_cd: error in readtocentries, "
2055				       "returning EINVAL\n");
2056				free(data, M_TEMP);
2057				free(lead, M_TEMP);
2058				break;
2059			}
2060
2061			th = &data->header;
2062			error = cdreadtoc(periph, 0, 0,
2063					  (struct cd_toc_entry *)th,
2064					  sizeof (*th));
2065			if (error) {
2066				free(data, M_TEMP);
2067				free(lead, M_TEMP);
2068				break;
2069			}
2070
2071			if (softc->quirks & CD_Q_BCD_TRACKS) {
2072				/* we are going to have to convert the BCD
2073				 * encoding on the cd to what is expected
2074				 */
2075				th->starting_track =
2076				    bcd2bin(th->starting_track);
2077				th->ending_track = bcd2bin(th->ending_track);
2078			}
2079
2080			if (starting_track == 0)
2081				starting_track = th->starting_track;
2082			else if (starting_track == LEADOUT)
2083				starting_track = th->ending_track + 1;
2084			else if (starting_track < th->starting_track ||
2085				 starting_track > th->ending_track + 1) {
2086				printf("scsi_cd: error in readtocentries, "
2087				       "returning EINVAL\n");
2088				free(data, M_TEMP);
2089				free(lead, M_TEMP);
2090				error = EINVAL;
2091				break;
2092			}
2093
2094			/* calculate reading length without leadout entry */
2095			readlen = (th->ending_track - starting_track + 1) *
2096				  sizeof(struct cd_toc_entry);
2097
2098			/* and with leadout entry */
2099			len = readlen + sizeof(struct cd_toc_entry);
2100			if (te->data_len < len) {
2101				len = te->data_len;
2102				if (readlen > len)
2103					readlen = len;
2104			}
2105			if (len > sizeof(data->entries)) {
2106				printf("scsi_cd: error in readtocentries, "
2107				       "returning EINVAL\n");
2108				error = EINVAL;
2109				free(data, M_TEMP);
2110				free(lead, M_TEMP);
2111				break;
2112			}
2113			num = len / sizeof(struct cd_toc_entry);
2114
2115			if (readlen > 0) {
2116				error = cdreadtoc(periph, te->address_format,
2117						  starting_track,
2118						  (struct cd_toc_entry *)data,
2119						  readlen + sizeof (*th));
2120				if (error) {
2121					free(data, M_TEMP);
2122					free(lead, M_TEMP);
2123					break;
2124				}
2125			}
2126
2127			/* make leadout entry if needed */
2128			idx = starting_track + num - 1;
2129			if (softc->quirks & CD_Q_BCD_TRACKS)
2130				th->ending_track = bcd2bin(th->ending_track);
2131			if (idx == th->ending_track + 1) {
2132				error = cdreadtoc(periph, te->address_format,
2133						  LEADOUT,
2134						  (struct cd_toc_entry *)lead,
2135						  sizeof(*lead));
2136				if (error) {
2137					free(data, M_TEMP);
2138					free(lead, M_TEMP);
2139					break;
2140				}
2141				data->entries[idx - starting_track] =
2142					lead->entry;
2143			}
2144			if (softc->quirks & CD_Q_BCD_TRACKS) {
2145				for (idx = 0; idx < num - 1; idx++) {
2146					data->entries[idx].track =
2147					    bcd2bin(data->entries[idx].track);
2148				}
2149			}
2150
2151			error = copyout(data->entries, te->data, len);
2152			free(data, M_TEMP);
2153			free(lead, M_TEMP);
2154		}
2155		break;
2156	case CDIOREADTOCENTRY:
2157		{
2158			/* yeah yeah, this is ugly */
2159			typedef struct {
2160				struct ioc_toc_header header;
2161				struct cd_toc_entry entry;
2162			} data_t;
2163
2164			data_t *data;
2165			struct ioc_read_toc_single_entry *te =
2166				(struct ioc_read_toc_single_entry *) addr;
2167			struct ioc_toc_header *th;
2168			u_int32_t track;
2169
2170			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2171				  ("trying to do CDIOREADTOCENTRY\n"));
2172
2173			data = malloc(sizeof(data_t), M_TEMP, M_WAITOK);
2174
2175			if (te->address_format != CD_MSF_FORMAT
2176			    && te->address_format != CD_LBA_FORMAT) {
2177				printf("error in readtocentry, "
2178				       " returning EINVAL\n");
2179				free(data, M_TEMP);
2180				error = EINVAL;
2181				break;
2182			}
2183
2184			th = &data->header;
2185			error = cdreadtoc(periph, 0, 0,
2186					  (struct cd_toc_entry *)th,
2187					  sizeof (*th));
2188			if (error) {
2189				free(data, M_TEMP);
2190				break;
2191			}
2192
2193			if (softc->quirks & CD_Q_BCD_TRACKS) {
2194				/* we are going to have to convert the BCD
2195				 * encoding on the cd to what is expected
2196				 */
2197				th->starting_track =
2198				    bcd2bin(th->starting_track);
2199				th->ending_track = bcd2bin(th->ending_track);
2200			}
2201			track = te->track;
2202			if (track == 0)
2203				track = th->starting_track;
2204			else if (track == LEADOUT)
2205				/* OK */;
2206			else if (track < th->starting_track ||
2207				 track > th->ending_track + 1) {
2208				printf("error in readtocentry, "
2209				       " returning EINVAL\n");
2210				free(data, M_TEMP);
2211				error = EINVAL;
2212				break;
2213			}
2214
2215			error = cdreadtoc(periph, te->address_format, track,
2216					  (struct cd_toc_entry *)data,
2217					  sizeof(data_t));
2218			if (error) {
2219				free(data, M_TEMP);
2220				break;
2221			}
2222
2223			if (softc->quirks & CD_Q_BCD_TRACKS)
2224				data->entry.track = bcd2bin(data->entry.track);
2225			bcopy(&data->entry, &te->entry,
2226			      sizeof(struct cd_toc_entry));
2227			free(data, M_TEMP);
2228		}
2229		break;
2230	case CDIOCSETPATCH:
2231		{
2232			struct ioc_patch *arg = (struct ioc_patch *) addr;
2233			struct cd_mode_data *data;
2234
2235			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2236				  ("trying to do CDIOCSETPATCH\n"));
2237
2238			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2239				      M_WAITOK);
2240			error = cdgetmode(periph, data, AUDIO_PAGE);
2241			if (error) {
2242				free(data, M_TEMP);
2243				break;
2244			}
2245			data->page.audio.port[LEFT_PORT].channels =
2246				arg->patch[0];
2247			data->page.audio.port[RIGHT_PORT].channels =
2248				arg->patch[1];
2249			data->page.audio.port[2].channels = arg->patch[2];
2250			data->page.audio.port[3].channels = arg->patch[3];
2251			error = cdsetmode(periph, data);
2252			free(data, M_TEMP);
2253		}
2254		break;
2255	case CDIOCGETVOL:
2256		{
2257			struct ioc_vol *arg = (struct ioc_vol *) addr;
2258			struct cd_mode_data *data;
2259
2260			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2261				  ("trying to do CDIOCGETVOL\n"));
2262
2263			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2264				      M_WAITOK);
2265			error = cdgetmode(periph, data, AUDIO_PAGE);
2266			if (error) {
2267				free(data, M_TEMP);
2268				break;
2269			}
2270			arg->vol[LEFT_PORT] =
2271				data->page.audio.port[LEFT_PORT].volume;
2272			arg->vol[RIGHT_PORT] =
2273				data->page.audio.port[RIGHT_PORT].volume;
2274			arg->vol[2] = data->page.audio.port[2].volume;
2275			arg->vol[3] = data->page.audio.port[3].volume;
2276			free(data, M_TEMP);
2277		}
2278		break;
2279	case CDIOCSETVOL:
2280		{
2281			struct ioc_vol *arg = (struct ioc_vol *) addr;
2282			struct cd_mode_data *data;
2283
2284			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2285				  ("trying to do CDIOCSETVOL\n"));
2286
2287			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2288				      M_WAITOK);
2289			error = cdgetmode(periph, data, AUDIO_PAGE);
2290			if (error) {
2291				free(data, M_TEMP);
2292				break;
2293			}
2294			data->page.audio.port[LEFT_PORT].channels = CHANNEL_0;
2295			data->page.audio.port[LEFT_PORT].volume =
2296				arg->vol[LEFT_PORT];
2297			data->page.audio.port[RIGHT_PORT].channels = CHANNEL_1;
2298			data->page.audio.port[RIGHT_PORT].volume =
2299				arg->vol[RIGHT_PORT];
2300			data->page.audio.port[2].volume = arg->vol[2];
2301			data->page.audio.port[3].volume = arg->vol[3];
2302			error = cdsetmode(periph, data);
2303			free(data, M_TEMP);
2304		}
2305		break;
2306	case CDIOCSETMONO:
2307		{
2308			struct cd_mode_data *data;
2309
2310			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2311				  ("trying to do CDIOCSETMONO\n"));
2312
2313			data = malloc(sizeof(struct cd_mode_data),
2314				      M_TEMP, M_WAITOK);
2315			error = cdgetmode(periph, data, AUDIO_PAGE);
2316			if (error) {
2317				free(data, M_TEMP);
2318				break;
2319			}
2320			data->page.audio.port[LEFT_PORT].channels =
2321				LEFT_CHANNEL | RIGHT_CHANNEL;
2322			data->page.audio.port[RIGHT_PORT].channels =
2323				LEFT_CHANNEL | RIGHT_CHANNEL;
2324			data->page.audio.port[2].channels = 0;
2325			data->page.audio.port[3].channels = 0;
2326			error = cdsetmode(periph, data);
2327			free(data, M_TEMP);
2328		}
2329		break;
2330	case CDIOCSETSTEREO:
2331		{
2332			struct cd_mode_data *data;
2333
2334			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2335				  ("trying to do CDIOCSETSTEREO\n"));
2336
2337			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2338				      M_WAITOK);
2339			error = cdgetmode(periph, data, AUDIO_PAGE);
2340			if (error) {
2341				free(data, M_TEMP);
2342				break;
2343			}
2344			data->page.audio.port[LEFT_PORT].channels =
2345				LEFT_CHANNEL;
2346			data->page.audio.port[RIGHT_PORT].channels =
2347				RIGHT_CHANNEL;
2348			data->page.audio.port[2].channels = 0;
2349			data->page.audio.port[3].channels = 0;
2350			error = cdsetmode(periph, data);
2351			free(data, M_TEMP);
2352		}
2353		break;
2354	case CDIOCSETMUTE:
2355		{
2356			struct cd_mode_data *data;
2357
2358			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2359				  ("trying to do CDIOCSETMUTE\n"));
2360
2361			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2362				      M_WAITOK);
2363			error = cdgetmode(periph, data, AUDIO_PAGE);
2364			if (error) {
2365				free(data, M_TEMP);
2366				break;
2367			}
2368			data->page.audio.port[LEFT_PORT].channels = 0;
2369			data->page.audio.port[RIGHT_PORT].channels = 0;
2370			data->page.audio.port[2].channels = 0;
2371			data->page.audio.port[3].channels = 0;
2372			error = cdsetmode(periph, data);
2373			free(data, M_TEMP);
2374		}
2375		break;
2376	case CDIOCSETLEFT:
2377		{
2378			struct cd_mode_data *data;
2379
2380			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2381				  ("trying to do CDIOCSETLEFT\n"));
2382
2383			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2384				      M_WAITOK);
2385			error = cdgetmode(periph, data, AUDIO_PAGE);
2386			if (error) {
2387				free(data, M_TEMP);
2388				break;
2389			}
2390			data->page.audio.port[LEFT_PORT].channels =
2391				LEFT_CHANNEL;
2392			data->page.audio.port[RIGHT_PORT].channels =
2393				LEFT_CHANNEL;
2394			data->page.audio.port[2].channels = 0;
2395			data->page.audio.port[3].channels = 0;
2396			error = cdsetmode(periph, data);
2397			free(data, M_TEMP);
2398		}
2399		break;
2400	case CDIOCSETRIGHT:
2401		{
2402			struct cd_mode_data *data;
2403
2404			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
2405				  ("trying to do CDIOCSETRIGHT\n"));
2406
2407			data = malloc(sizeof(struct cd_mode_data), M_TEMP,
2408				      M_WAITOK);
2409			error = cdgetmode(periph, data, AUDIO_PAGE);
2410			if (error) {
2411				free(data, M_TEMP);
2412				break;
2413			}
2414			data->page.audio.port[LEFT_PORT].channels =
2415				RIGHT_CHANNEL;
2416			data->page.audio.port[RIGHT_PORT].channels =
2417				RIGHT_CHANNEL;
2418			data->page.audio.port[2].channels = 0;
2419			data->page.audio.port[3].channels = 0;
2420			error = cdsetmode(periph, data);
2421			free(data, M_TEMP);
2422		}
2423		break;
2424	case CDIOCRESUME:
2425		error = cdpause(periph, 1);
2426		break;
2427	case CDIOCPAUSE:
2428		error = cdpause(periph, 0);
2429		break;
2430	case CDIOCSTART:
2431		error = cdstartunit(periph);
2432		break;
2433	case CDIOCSTOP:
2434		error = cdstopunit(periph, 0);
2435		break;
2436	case CDIOCEJECT:
2437		error = cdstopunit(periph, 1);
2438		break;
2439	case CDIOCALLOW:
2440		cdprevent(periph, PR_ALLOW);
2441		break;
2442	case CDIOCPREVENT:
2443		cdprevent(periph, PR_PREVENT);
2444		break;
2445	case CDIOCSETDEBUG:
2446		/* sc_link->flags |= (SDEV_DB1 | SDEV_DB2); */
2447		error = ENOTTY;
2448		break;
2449	case CDIOCCLRDEBUG:
2450		/* sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); */
2451		error = ENOTTY;
2452		break;
2453	case CDIOCRESET:
2454		/* return (cd_reset(periph)); */
2455		error = ENOTTY;
2456		break;
2457	default:
2458		if (cmd == DIOCSBAD) {
2459			error = EINVAL;	/* XXX */
2460			break;
2461		}
2462
2463		/*
2464		 * Check to see whether we've got a disk-type ioctl.  If we
2465		 * don't, dsioctl will pass back an error code of ENOIOCTL.
2466		 */
2467		error = dsioctl("cd", dev, cmd, addr, flag, &softc->cd_slices,
2468				cdstrategy1, (ds_setgeom_t *)NULL);
2469
2470		if (error != ENOIOCTL)
2471			break;
2472
2473		error = cam_periph_ioctl(periph, cmd, addr, cderror);
2474		break;
2475	}
2476
2477	cam_periph_unlock(periph);
2478
2479	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdioctl\n"));
2480
2481	return (error);
2482}
2483
2484static void
2485cdprevent(struct cam_periph *periph, int action)
2486{
2487	union	ccb *ccb;
2488	struct	cd_softc *softc;
2489	int	error;
2490
2491	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdprevent\n"));
2492
2493	softc = (struct cd_softc *)periph->softc;
2494
2495	if (((action == PR_ALLOW)
2496	  && (softc->flags & CD_FLAG_DISC_LOCKED) == 0)
2497	 || ((action == PR_PREVENT)
2498	  && (softc->flags & CD_FLAG_DISC_LOCKED) != 0)) {
2499		return;
2500	}
2501
2502	ccb = cdgetccb(periph, /* priority */ 1);
2503
2504	scsi_prevent(&ccb->csio,
2505		     /*retries*/ 1,
2506		     cddone,
2507		     MSG_SIMPLE_Q_TAG,
2508		     action,
2509		     SSD_FULL_SIZE,
2510		     /* timeout */60000);
2511
2512	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2513			/*sense_flags*/SF_RETRY_UA|SF_NO_PRINT|SF_RETRY_SELTO);
2514
2515	xpt_release_ccb(ccb);
2516
2517	if (error == 0) {
2518		if (action == PR_ALLOW)
2519			softc->flags &= ~CD_FLAG_DISC_LOCKED;
2520		else
2521			softc->flags |= CD_FLAG_DISC_LOCKED;
2522	}
2523}
2524
2525static int
2526cdsize(dev_t dev, u_int32_t *size)
2527{
2528	struct cam_periph *periph;
2529	struct cd_softc *softc;
2530	union ccb *ccb;
2531	struct scsi_read_capacity_data *rcap_buf;
2532	int error;
2533
2534	periph = cam_extend_get(cdperiphs, dkunit(dev));
2535
2536	if (periph == NULL)
2537		return (ENXIO);
2538
2539	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n"));
2540
2541	softc = (struct cd_softc *)periph->softc;
2542
2543	ccb = cdgetccb(periph, /* priority */ 1);
2544
2545	rcap_buf = malloc(sizeof(struct scsi_read_capacity_data),
2546			  M_TEMP, M_WAITOK);
2547
2548	scsi_read_capacity(&ccb->csio,
2549			   /*retries*/ 1,
2550			   cddone,
2551			   MSG_SIMPLE_Q_TAG,
2552			   rcap_buf,
2553			   SSD_FULL_SIZE,
2554			   /* timeout */20000);
2555
2556	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2557			 /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT|SF_RETRY_SELTO);
2558
2559	xpt_release_ccb(ccb);
2560
2561	softc->params.disksize = scsi_4btoul(rcap_buf->addr) + 1;
2562	softc->params.blksize  = scsi_4btoul(rcap_buf->length);
2563
2564	free(rcap_buf, M_TEMP);
2565	*size = softc->params.disksize;
2566
2567	return (error);
2568
2569}
2570
2571static int
2572cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
2573{
2574	struct cd_softc *softc;
2575	struct cam_periph *periph;
2576
2577	periph = xpt_path_periph(ccb->ccb_h.path);
2578	softc = (struct cd_softc *)periph->softc;
2579
2580	/*
2581	 * XXX
2582	 * Until we have a better way of doing pack validation,
2583	 * don't treat UAs as errors.
2584	 */
2585	sense_flags |= SF_RETRY_UA;
2586	return (cam_periph_error(ccb, cam_flags, sense_flags,
2587				 &softc->saved_ccb));
2588}
2589
2590/*
2591 * Read table of contents
2592 */
2593static int
2594cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
2595	    struct cd_toc_entry *data, u_int32_t len)
2596{
2597	struct scsi_read_toc *scsi_cmd;
2598	u_int32_t ntoc;
2599        struct ccb_scsiio *csio;
2600	union ccb *ccb;
2601	int error;
2602
2603	ntoc = len;
2604	error = 0;
2605
2606	ccb = cdgetccb(periph, /* priority */ 1);
2607
2608	csio = &ccb->csio;
2609
2610	cam_fill_csio(csio,
2611		      /* retries */ 1,
2612		      /* cbfcnp */ cddone,
2613		      /* flags */ CAM_DIR_IN,
2614		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2615		      /* data_ptr */ (u_int8_t *)data,
2616		      /* dxfer_len */ len,
2617		      /* sense_len */ SSD_FULL_SIZE,
2618		      sizeof(struct scsi_read_toc),
2619 		      /* timeout */ 50000);
2620
2621	scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes;
2622	bzero (scsi_cmd, sizeof(*scsi_cmd));
2623
2624	if (mode == CD_MSF_FORMAT)
2625		scsi_cmd->byte2 |= CD_MSF;
2626	scsi_cmd->from_track = start;
2627	/* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */
2628	scsi_cmd->data_len[0] = (ntoc) >> 8;
2629	scsi_cmd->data_len[1] = (ntoc) & 0xff;
2630
2631	scsi_cmd->op_code = READ_TOC;
2632
2633	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2634			 /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO);
2635
2636	xpt_release_ccb(ccb);
2637
2638	return(error);
2639}
2640
2641static int
2642cdreadsubchannel(struct cam_periph *periph, u_int32_t mode,
2643		 u_int32_t format, int track,
2644		 struct cd_sub_channel_info *data, u_int32_t len)
2645{
2646	struct scsi_read_subchannel *scsi_cmd;
2647        struct ccb_scsiio *csio;
2648	union ccb *ccb;
2649	int error;
2650
2651	error = 0;
2652
2653	ccb = cdgetccb(periph, /* priority */ 1);
2654
2655	csio = &ccb->csio;
2656
2657	cam_fill_csio(csio,
2658		      /* retries */ 1,
2659		      /* cbfcnp */ cddone,
2660		      /* flags */ CAM_DIR_IN,
2661		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2662		      /* data_ptr */ (u_int8_t *)data,
2663		      /* dxfer_len */ len,
2664		      /* sense_len */ SSD_FULL_SIZE,
2665		      sizeof(struct scsi_read_subchannel),
2666 		      /* timeout */ 50000);
2667
2668	scsi_cmd = (struct scsi_read_subchannel *)&csio->cdb_io.cdb_bytes;
2669	bzero (scsi_cmd, sizeof(*scsi_cmd));
2670
2671	scsi_cmd->op_code = READ_SUBCHANNEL;
2672	if (mode == CD_MSF_FORMAT)
2673		scsi_cmd->byte1 |= CD_MSF;
2674	scsi_cmd->byte2 = SRS_SUBQ;
2675	scsi_cmd->subchan_format = format;
2676	scsi_cmd->track = track;
2677	scsi_ulto2b(len, (u_int8_t *)scsi_cmd->data_len);
2678	scsi_cmd->control = 0;
2679
2680	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2681			 /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO);
2682
2683	xpt_release_ccb(ccb);
2684
2685	return(error);
2686}
2687
2688
2689static int
2690cdgetmode(struct cam_periph *periph, struct cd_mode_data *data, u_int32_t page)
2691{
2692	struct scsi_mode_sense_6 *scsi_cmd;
2693        struct ccb_scsiio *csio;
2694	union ccb *ccb;
2695	int error;
2696
2697	ccb = cdgetccb(periph, /* priority */ 1);
2698
2699	csio = &ccb->csio;
2700
2701	bzero(data, sizeof(*data));
2702	cam_fill_csio(csio,
2703		      /* retries */ 1,
2704		      /* cbfcnp */ cddone,
2705		      /* flags */ CAM_DIR_IN,
2706		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2707		      /* data_ptr */ (u_int8_t *)data,
2708		      /* dxfer_len */ sizeof(*data),
2709		      /* sense_len */ SSD_FULL_SIZE,
2710		      sizeof(struct scsi_mode_sense_6),
2711 		      /* timeout */ 50000);
2712
2713	scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes;
2714	bzero (scsi_cmd, sizeof(*scsi_cmd));
2715
2716	scsi_cmd->page = page;
2717	scsi_cmd->length = sizeof(*data) & 0xff;
2718	scsi_cmd->opcode = MODE_SENSE;
2719
2720	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2721			 /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO);
2722
2723	xpt_release_ccb(ccb);
2724
2725	return(error);
2726}
2727
2728static int
2729cdsetmode(struct cam_periph *periph, struct cd_mode_data *data)
2730{
2731	struct scsi_mode_select_6 *scsi_cmd;
2732        struct ccb_scsiio *csio;
2733	union ccb *ccb;
2734	int error;
2735
2736	ccb = cdgetccb(periph, /* priority */ 1);
2737
2738	csio = &ccb->csio;
2739
2740	error = 0;
2741
2742	cam_fill_csio(csio,
2743		      /* retries */ 1,
2744		      /* cbfcnp */ cddone,
2745		      /* flags */ CAM_DIR_OUT,
2746		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2747		      /* data_ptr */ (u_int8_t *)data,
2748		      /* dxfer_len */ sizeof(*data),
2749		      /* sense_len */ SSD_FULL_SIZE,
2750		      sizeof(struct scsi_mode_select_6),
2751 		      /* timeout */ 50000);
2752
2753	scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes;
2754
2755	bzero(scsi_cmd, sizeof(*scsi_cmd));
2756	scsi_cmd->opcode = MODE_SELECT;
2757	scsi_cmd->byte2 |= SMS_PF;
2758	scsi_cmd->length = sizeof(*data) & 0xff;
2759	data->header.data_length = 0;
2760	/*
2761	 * SONY drives do not allow a mode select with a medium_type
2762	 * value that has just been returned by a mode sense; use a
2763	 * medium_type of 0 (Default) instead.
2764	 */
2765	data->header.medium_type = 0;
2766
2767	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2768			 /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO);
2769
2770	xpt_release_ccb(ccb);
2771
2772	return(error);
2773}
2774
2775
2776static int
2777cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len)
2778{
2779	struct ccb_scsiio *csio;
2780	union ccb *ccb;
2781	int error;
2782	u_int8_t cdb_len;
2783
2784	error = 0;
2785	ccb = cdgetccb(periph, /* priority */ 1);
2786	csio = &ccb->csio;
2787	/*
2788	 * Use the smallest possible command to perform the operation.
2789	 */
2790	if ((len & 0xffff0000) == 0) {
2791		/*
2792		 * We can fit in a 10 byte cdb.
2793		 */
2794		struct scsi_play_10 *scsi_cmd;
2795
2796		scsi_cmd = (struct scsi_play_10 *)&csio->cdb_io.cdb_bytes;
2797		bzero (scsi_cmd, sizeof(*scsi_cmd));
2798		scsi_cmd->op_code = PLAY_10;
2799		scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr);
2800		scsi_ulto2b(len, (u_int8_t *)scsi_cmd->xfer_len);
2801		cdb_len = sizeof(*scsi_cmd);
2802	} else  {
2803		struct scsi_play_12 *scsi_cmd;
2804
2805		scsi_cmd = (struct scsi_play_12 *)&csio->cdb_io.cdb_bytes;
2806		bzero (scsi_cmd, sizeof(*scsi_cmd));
2807		scsi_cmd->op_code = PLAY_12;
2808		scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr);
2809		scsi_ulto4b(len, (u_int8_t *)scsi_cmd->xfer_len);
2810		cdb_len = sizeof(*scsi_cmd);
2811	}
2812	cam_fill_csio(csio,
2813		      /*retries*/2,
2814		      cddone,
2815		      /*flags*/CAM_DIR_NONE,
2816		      MSG_SIMPLE_Q_TAG,
2817		      /*dataptr*/NULL,
2818		      /*datalen*/0,
2819		      /*sense_len*/SSD_FULL_SIZE,
2820		      cdb_len,
2821		      /*timeout*/50 * 1000);
2822
2823	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2824			 /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO);
2825
2826	xpt_release_ccb(ccb);
2827
2828	return(error);
2829}
2830
2831static int
2832cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts,
2833	  u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf)
2834{
2835	struct scsi_play_msf *scsi_cmd;
2836        struct ccb_scsiio *csio;
2837	union ccb *ccb;
2838	int error;
2839
2840	error = 0;
2841
2842	ccb = cdgetccb(periph, /* priority */ 1);
2843
2844	csio = &ccb->csio;
2845
2846	cam_fill_csio(csio,
2847		      /* retries */ 1,
2848		      /* cbfcnp */ cddone,
2849		      /* flags */ CAM_DIR_NONE,
2850		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2851		      /* data_ptr */ NULL,
2852		      /* dxfer_len */ 0,
2853		      /* sense_len */ SSD_FULL_SIZE,
2854		      sizeof(struct scsi_play_msf),
2855 		      /* timeout */ 50000);
2856
2857	scsi_cmd = (struct scsi_play_msf *)&csio->cdb_io.cdb_bytes;
2858	bzero (scsi_cmd, sizeof(*scsi_cmd));
2859
2860        scsi_cmd->op_code = PLAY_MSF;
2861        scsi_cmd->start_m = startm;
2862        scsi_cmd->start_s = starts;
2863        scsi_cmd->start_f = startf;
2864        scsi_cmd->end_m = endm;
2865        scsi_cmd->end_s = ends;
2866        scsi_cmd->end_f = endf;
2867
2868	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2869			 /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO);
2870
2871	xpt_release_ccb(ccb);
2872
2873	return(error);
2874}
2875
2876
2877static int
2878cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex,
2879	     u_int32_t etrack, u_int32_t eindex)
2880{
2881	struct scsi_play_track *scsi_cmd;
2882        struct ccb_scsiio *csio;
2883	union ccb *ccb;
2884	int error;
2885
2886	error = 0;
2887
2888	ccb = cdgetccb(periph, /* priority */ 1);
2889
2890	csio = &ccb->csio;
2891
2892	cam_fill_csio(csio,
2893		      /* retries */ 1,
2894		      /* cbfcnp */ cddone,
2895		      /* flags */ CAM_DIR_NONE,
2896		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2897		      /* data_ptr */ NULL,
2898		      /* dxfer_len */ 0,
2899		      /* sense_len */ SSD_FULL_SIZE,
2900		      sizeof(struct scsi_play_track),
2901 		      /* timeout */ 50000);
2902
2903	scsi_cmd = (struct scsi_play_track *)&csio->cdb_io.cdb_bytes;
2904	bzero (scsi_cmd, sizeof(*scsi_cmd));
2905
2906        scsi_cmd->op_code = PLAY_TRACK;
2907        scsi_cmd->start_track = strack;
2908        scsi_cmd->start_index = sindex;
2909        scsi_cmd->end_track = etrack;
2910        scsi_cmd->end_index = eindex;
2911
2912	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2913			 /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO);
2914
2915	xpt_release_ccb(ccb);
2916
2917	return(error);
2918}
2919
2920static int
2921cdpause(struct cam_periph *periph, u_int32_t go)
2922{
2923	struct scsi_pause *scsi_cmd;
2924        struct ccb_scsiio *csio;
2925	union ccb *ccb;
2926	int error;
2927
2928	error = 0;
2929
2930	ccb = cdgetccb(periph, /* priority */ 1);
2931
2932	csio = &ccb->csio;
2933
2934	cam_fill_csio(csio,
2935		      /* retries */ 1,
2936		      /* cbfcnp */ cddone,
2937		      /* flags */ CAM_DIR_NONE,
2938		      /* tag_action */ MSG_SIMPLE_Q_TAG,
2939		      /* data_ptr */ NULL,
2940		      /* dxfer_len */ 0,
2941		      /* sense_len */ SSD_FULL_SIZE,
2942		      sizeof(struct scsi_pause),
2943 		      /* timeout */ 50000);
2944
2945	scsi_cmd = (struct scsi_pause *)&csio->cdb_io.cdb_bytes;
2946	bzero (scsi_cmd, sizeof(*scsi_cmd));
2947
2948        scsi_cmd->op_code = PAUSE;
2949	scsi_cmd->resume = go;
2950
2951	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2952			 /*sense_flags*/SF_RETRY_UA |SF_RETRY_SELTO);
2953
2954	xpt_release_ccb(ccb);
2955
2956	return(error);
2957}
2958
2959static int
2960cdstartunit(struct cam_periph *periph)
2961{
2962	union ccb *ccb;
2963	int error;
2964
2965	error = 0;
2966
2967	ccb = cdgetccb(periph, /* priority */ 1);
2968
2969	scsi_start_stop(&ccb->csio,
2970			/* retries */ 1,
2971			/* cbfcnp */ cddone,
2972			/* tag_action */ MSG_SIMPLE_Q_TAG,
2973			/* start */ TRUE,
2974			/* load_eject */ TRUE,
2975			/* immediate */ FALSE,
2976			/* sense_len */ SSD_FULL_SIZE,
2977			/* timeout */ 50000);
2978
2979	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
2980			 /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO);
2981
2982	xpt_release_ccb(ccb);
2983
2984	return(error);
2985}
2986
2987static int
2988cdstopunit(struct cam_periph *periph, u_int32_t eject)
2989{
2990	union ccb *ccb;
2991	int error;
2992
2993	error = 0;
2994
2995	ccb = cdgetccb(periph, /* priority */ 1);
2996
2997	scsi_start_stop(&ccb->csio,
2998			/* retries */ 1,
2999			/* cbfcnp */ cddone,
3000			/* tag_action */ MSG_SIMPLE_Q_TAG,
3001			/* start */ FALSE,
3002			/* load_eject */ eject,
3003			/* immediate */ FALSE,
3004			/* sense_len */ SSD_FULL_SIZE,
3005			/* timeout */ 50000);
3006
3007	error = cdrunccb(ccb, cderror, /*cam_flags*/0,
3008			 /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO);
3009
3010	xpt_release_ccb(ccb);
3011
3012	return(error);
3013}
3014