xref: /freebsd/sys/cam/ata/ata_da.c (revision a6e0c5da99c72476e9cab2d6fd0eb72268e56df8)
152c9ce25SScott Long /*-
252c9ce25SScott Long  * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
352c9ce25SScott Long  * All rights reserved.
452c9ce25SScott Long  *
552c9ce25SScott Long  * Redistribution and use in source and binary forms, with or without
652c9ce25SScott Long  * modification, are permitted provided that the following conditions
752c9ce25SScott Long  * are met:
852c9ce25SScott Long  * 1. Redistributions of source code must retain the above copyright
952c9ce25SScott Long  *    notice, this list of conditions and the following disclaimer,
1052c9ce25SScott Long  *    without modification, immediately at the beginning of the file.
1152c9ce25SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
1252c9ce25SScott Long  *    notice, this list of conditions and the following disclaimer in the
1352c9ce25SScott Long  *    documentation and/or other materials provided with the distribution.
1452c9ce25SScott Long  *
1552c9ce25SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1652c9ce25SScott Long  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1752c9ce25SScott Long  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1852c9ce25SScott Long  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1952c9ce25SScott Long  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2052c9ce25SScott Long  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2152c9ce25SScott Long  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2252c9ce25SScott Long  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2352c9ce25SScott Long  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2452c9ce25SScott Long  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2552c9ce25SScott Long  */
2652c9ce25SScott Long 
2752c9ce25SScott Long #include <sys/cdefs.h>
2852c9ce25SScott Long __FBSDID("$FreeBSD$");
2952c9ce25SScott Long 
30e3a6d3a4SAlexander Motin #include "opt_ada.h"
31e3a6d3a4SAlexander Motin 
3252c9ce25SScott Long #include <sys/param.h>
3352c9ce25SScott Long 
3452c9ce25SScott Long #ifdef _KERNEL
3552c9ce25SScott Long #include <sys/systm.h>
3652c9ce25SScott Long #include <sys/kernel.h>
3752c9ce25SScott Long #include <sys/bio.h>
3852c9ce25SScott Long #include <sys/sysctl.h>
3952c9ce25SScott Long #include <sys/taskqueue.h>
4052c9ce25SScott Long #include <sys/lock.h>
4152c9ce25SScott Long #include <sys/mutex.h>
4252c9ce25SScott Long #include <sys/conf.h>
4352c9ce25SScott Long #include <sys/devicestat.h>
4452c9ce25SScott Long #include <sys/eventhandler.h>
4552c9ce25SScott Long #include <sys/malloc.h>
4652c9ce25SScott Long #include <sys/cons.h>
472f87dfb0SAlexander Motin #include <sys/proc.h>
48fd104c15SRebecca Cran #include <sys/reboot.h>
4952c9ce25SScott Long #include <geom/geom_disk.h>
5052c9ce25SScott Long #endif /* _KERNEL */
5152c9ce25SScott Long 
5252c9ce25SScott Long #ifndef _KERNEL
5352c9ce25SScott Long #include <stdio.h>
5452c9ce25SScott Long #include <string.h>
5552c9ce25SScott Long #endif /* _KERNEL */
5652c9ce25SScott Long 
5752c9ce25SScott Long #include <cam/cam.h>
5852c9ce25SScott Long #include <cam/cam_ccb.h>
5952c9ce25SScott Long #include <cam/cam_periph.h>
6052c9ce25SScott Long #include <cam/cam_xpt_periph.h>
6152c9ce25SScott Long #include <cam/cam_sim.h>
62*a6e0c5daSWarner Losh #include <cam/cam_iosched.h>
6352c9ce25SScott Long 
6452c9ce25SScott Long #include <cam/ata/ata_all.h>
6552c9ce25SScott Long 
664461491bSMarius Strobl #include <machine/md_var.h>	/* geometry translation */
674461491bSMarius Strobl 
6852c9ce25SScott Long #ifdef _KERNEL
6952c9ce25SScott Long 
7052c9ce25SScott Long #define ATA_MAX_28BIT_LBA               268435455UL
7152c9ce25SScott Long 
72*a6e0c5daSWarner Losh extern int iosched_debug;
73*a6e0c5daSWarner Losh 
7452c9ce25SScott Long typedef enum {
751ed6aaf9SAlexander Motin 	ADA_STATE_RAHEAD,
76f513d14cSAlexander Motin 	ADA_STATE_WCACHE,
771e637ba6SAlexander Motin 	ADA_STATE_NORMAL
7852c9ce25SScott Long } ada_state;
7952c9ce25SScott Long 
8052c9ce25SScott Long typedef enum {
812e1eb332SMarius Strobl 	ADA_FLAG_CAN_48BIT	= 0x0002,
822e1eb332SMarius Strobl 	ADA_FLAG_CAN_FLUSHCACHE	= 0x0004,
832e1eb332SMarius Strobl 	ADA_FLAG_CAN_NCQ	= 0x0008,
842e1eb332SMarius Strobl 	ADA_FLAG_CAN_DMA	= 0x0010,
852e1eb332SMarius Strobl 	ADA_FLAG_NEED_OTAG	= 0x0020,
86030844d1SAlexander Motin 	ADA_FLAG_WAS_OTAG	= 0x0040,
872e1eb332SMarius Strobl 	ADA_FLAG_CAN_TRIM	= 0x0080,
882e1eb332SMarius Strobl 	ADA_FLAG_OPEN		= 0x0100,
892e1eb332SMarius Strobl 	ADA_FLAG_SCTX_INIT	= 0x0200,
902e1eb332SMarius Strobl 	ADA_FLAG_CAN_CFA        = 0x0400,
912e1eb332SMarius Strobl 	ADA_FLAG_CAN_POWERMGT   = 0x0800,
9269114bc0SAlexander Motin 	ADA_FLAG_CAN_DMA48	= 0x1000,
93*a6e0c5daSWarner Losh 	ADA_FLAG_DIRTY		= 0x2000,
94*a6e0c5daSWarner Losh 	ADA_FLAG_CAN_NCQ_TRIM	= 0x4000,	/* CAN_TRIM also set */
95*a6e0c5daSWarner Losh 	ADA_FLAG_PIM_CAN_NCQ_TRIM = 0x8000
9652c9ce25SScott Long } ada_flags;
9752c9ce25SScott Long 
9852c9ce25SScott Long typedef enum {
99d3a460d3SAlexander Motin 	ADA_Q_NONE		= 0x00,
100d3a460d3SAlexander Motin 	ADA_Q_4K		= 0x01,
101*a6e0c5daSWarner Losh 	ADA_Q_NCQ_TRIM_BROKEN	= 0x02,
10252c9ce25SScott Long } ada_quirks;
10352c9ce25SScott Long 
1046fb5c84eSSteven Hartland #define ADA_Q_BIT_STRING	\
1056fb5c84eSSteven Hartland 	"\020"			\
106*a6e0c5daSWarner Losh 	"\0014K"		\
107*a6e0c5daSWarner Losh 	"\002NCQ_TRIM_BROKEN"
1086fb5c84eSSteven Hartland 
10952c9ce25SScott Long typedef enum {
1101ed6aaf9SAlexander Motin 	ADA_CCB_RAHEAD		= 0x01,
1111ed6aaf9SAlexander Motin 	ADA_CCB_WCACHE		= 0x02,
11252c9ce25SScott Long 	ADA_CCB_BUFFER_IO	= 0x03,
11352c9ce25SScott Long 	ADA_CCB_DUMP		= 0x05,
1141c80ec0aSAlexander Motin 	ADA_CCB_TRIM		= 0x06,
11552c9ce25SScott Long 	ADA_CCB_TYPE_MASK	= 0x0F,
11652c9ce25SScott Long } ada_ccb_state;
11752c9ce25SScott Long 
11852c9ce25SScott Long /* Offsets into our private area for storing information */
11952c9ce25SScott Long #define ccb_state	ppriv_field0
12052c9ce25SScott Long #define ccb_bp		ppriv_ptr1
12152c9ce25SScott Long 
122*a6e0c5daSWarner Losh typedef enum {
123*a6e0c5daSWarner Losh 	ADA_DELETE_NONE,
124*a6e0c5daSWarner Losh 	ADA_DELETE_DISABLE,
125*a6e0c5daSWarner Losh 	ADA_DELETE_CFA_ERASE,
126*a6e0c5daSWarner Losh 	ADA_DELETE_DSM_TRIM,
127*a6e0c5daSWarner Losh 	ADA_DELETE_NCQ_DSM_TRIM,
128*a6e0c5daSWarner Losh 	ADA_DELETE_MIN = ADA_DELETE_CFA_ERASE,
129*a6e0c5daSWarner Losh 	ADA_DELETE_MAX = ADA_DELETE_NCQ_DSM_TRIM,
130*a6e0c5daSWarner Losh } ada_delete_methods;
131*a6e0c5daSWarner Losh 
132*a6e0c5daSWarner Losh static const char *ada_delete_method_names[] =
133*a6e0c5daSWarner Losh     { "NONE", "DISABLE", "CFA_ERASE", "DSM_TRIM", "NCQ_DSM_TRIM" };
134*a6e0c5daSWarner Losh #if 0
135*a6e0c5daSWarner Losh static const char *ada_delete_method_desc[] =
136*a6e0c5daSWarner Losh     { "NONE", "DISABLED", "CFA Erase", "DSM Trim", "DSM Trim via NCQ" };
137*a6e0c5daSWarner Losh #endif
138*a6e0c5daSWarner Losh 
13952c9ce25SScott Long struct disk_params {
14052c9ce25SScott Long 	u_int8_t  heads;
14152c9ce25SScott Long 	u_int8_t  secs_per_track;
142c1bd46c2SAlexander Motin 	u_int32_t cylinders;
143c1bd46c2SAlexander Motin 	u_int32_t secsize;	/* Number of bytes/logical sector */
144c1bd46c2SAlexander Motin 	u_int64_t sectors;	/* Total number sectors */
14552c9ce25SScott Long };
14652c9ce25SScott Long 
1471524677aSAlexander Motin #define TRIM_MAX_BLOCKS	8
148c213c551SSteven Hartland #define TRIM_MAX_RANGES	(TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES)
1491c80ec0aSAlexander Motin struct trim_request {
150c213c551SSteven Hartland 	uint8_t		data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE];
1512030b294SAlexander Motin 	TAILQ_HEAD(, bio) bps;
1521c80ec0aSAlexander Motin };
1531c80ec0aSAlexander Motin 
15452c9ce25SScott Long struct ada_softc {
155*a6e0c5daSWarner Losh 	struct   cam_iosched_softc *cam_iosched;
156030844d1SAlexander Motin 	int	 outstanding_cmds;	/* Number of active commands */
157030844d1SAlexander Motin 	int	 refcount;		/* Active xpt_action() calls */
15852c9ce25SScott Long 	ada_state state;
15952c9ce25SScott Long 	ada_flags flags;
16052c9ce25SScott Long 	ada_quirks quirks;
161*a6e0c5daSWarner Losh 	ada_delete_methods delete_method;
1621c80ec0aSAlexander Motin 	int	 trim_max_ranges;
1631ed6aaf9SAlexander Motin 	int	 read_ahead;
164e3a6d3a4SAlexander Motin 	int	 write_cache;
165*a6e0c5daSWarner Losh 	int	 unmappedio;
166*a6e0c5daSWarner Losh 	int	 rotating;
167e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
168e3a6d3a4SAlexander Motin 	int      force_read_error;
169e3a6d3a4SAlexander Motin 	int      force_write_error;
170e3a6d3a4SAlexander Motin 	int      periodic_read_error;
171e3a6d3a4SAlexander Motin 	int      periodic_read_count;
172e3a6d3a4SAlexander Motin #endif
17352c9ce25SScott Long 	struct	 disk_params params;
17452c9ce25SScott Long 	struct	 disk *disk;
17552c9ce25SScott Long 	struct task		sysctl_task;
17652c9ce25SScott Long 	struct sysctl_ctx_list	sysctl_ctx;
17752c9ce25SScott Long 	struct sysctl_oid	*sysctl_tree;
17852c9ce25SScott Long 	struct callout		sendordered_c;
1791c80ec0aSAlexander Motin 	struct trim_request	trim_req;
180*a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
181*a6e0c5daSWarner Losh 	struct sysctl_ctx_list	sysctl_stats_ctx;
182*a6e0c5daSWarner Losh 	struct sysctl_oid	*sysctl_stats_tree;
183*a6e0c5daSWarner Losh 	u_int	timeouts;
184*a6e0c5daSWarner Losh 	u_int	errors;
185*a6e0c5daSWarner Losh 	u_int	invalidations;
186*a6e0c5daSWarner Losh #endif
18752c9ce25SScott Long };
18852c9ce25SScott Long 
18952c9ce25SScott Long struct ada_quirk_entry {
19052c9ce25SScott Long 	struct scsi_inquiry_pattern inq_pat;
19152c9ce25SScott Long 	ada_quirks quirks;
19252c9ce25SScott Long };
19352c9ce25SScott Long 
19430a4094fSAlexander Motin static struct ada_quirk_entry ada_quirk_table[] =
19530a4094fSAlexander Motin {
19630a4094fSAlexander Motin 	{
197d3a460d3SAlexander Motin 		/* Hitachi Advanced Format (4k) drives */
198d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Hitachi H??????????E3*", "*" },
199d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
200d3a460d3SAlexander Motin 	},
201d3a460d3SAlexander Motin 	{
202d3a460d3SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
203643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD155UI*", "*" },
204643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
205643d1826SAlexander Motin 	},
206643d1826SAlexander Motin 	{
207643d1826SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
208d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD204UI*", "*" },
209d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
210d3a460d3SAlexander Motin 	},
211d3a460d3SAlexander Motin 	{
212d3a460d3SAlexander Motin 		/* Seagate Barracuda Green Advanced Format (4k) drives */
213d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DL*", "*" },
214d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
215d3a460d3SAlexander Motin 	},
216d3a460d3SAlexander Motin 	{
217643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
218643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???DM*", "*" },
219643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
220643d1826SAlexander Motin 	},
221643d1826SAlexander Motin 	{
222643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
223643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DM*", "*" },
224643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
225643d1826SAlexander Motin 	},
226643d1826SAlexander Motin 	{
227d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
228d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500423AS*", "*" },
229d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
230d3a460d3SAlexander Motin 	},
231d3a460d3SAlexander Motin 	{
232d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
233d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500424AS*", "*" },
234d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
235d3a460d3SAlexander Motin 	},
236d3a460d3SAlexander Motin 	{
237d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
238643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640423AS*", "*" },
239643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
240643d1826SAlexander Motin 	},
241643d1826SAlexander Motin 	{
242643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
243643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640424AS*", "*" },
244643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
245643d1826SAlexander Motin 	},
246643d1826SAlexander Motin 	{
247643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
248d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750420AS*", "*" },
249d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
250d3a460d3SAlexander Motin 	},
251d3a460d3SAlexander Motin 	{
252d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
253d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750422AS*", "*" },
254d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
255d3a460d3SAlexander Motin 	},
256d3a460d3SAlexander Motin 	{
257643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
258643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750423AS*", "*" },
259643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
260643d1826SAlexander Motin 	},
261643d1826SAlexander Motin 	{
262d3a460d3SAlexander Motin 		/* Seagate Momentus Thin Advanced Format (4k) drives */
263d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???LT*", "*" },
264d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
265d3a460d3SAlexander Motin 	},
266d3a460d3SAlexander Motin 	{
2679073a96aSEitan Adler 		/* WDC Caviar Red Advanced Format (4k) drives */
2689073a96aSEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????CX*", "*" },
2699073a96aSEitan Adler 		/*quirks*/ADA_Q_4K
2709073a96aSEitan Adler 	},
2719073a96aSEitan Adler 	{
272d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
273d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RS*", "*" },
274d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
275d3a460d3SAlexander Motin 	},
276d3a460d3SAlexander Motin 	{
2779073a96aSEitan Adler 		/* WDC Caviar Green/Red Advanced Format (4k) drives */
278d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RX*", "*" },
279d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
280d3a460d3SAlexander Motin 	},
281d3a460d3SAlexander Motin 	{
2829073a96aSEitan Adler 		/* WDC Caviar Red Advanced Format (4k) drives */
2839073a96aSEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????CX*", "*" },
2849073a96aSEitan Adler 		/*quirks*/ADA_Q_4K
2859073a96aSEitan Adler 	},
2869073a96aSEitan Adler 	{
2879073a96aSEitan Adler 		/* WDC Caviar Black Advanced Format (4k) drives */
2889073a96aSEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????EX*", "*" },
2899073a96aSEitan Adler 		/*quirks*/ADA_Q_4K
2909073a96aSEitan Adler 	},
2919073a96aSEitan Adler 	{
292d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
293d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RS*", "*" },
294d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
295d3a460d3SAlexander Motin 	},
296d3a460d3SAlexander Motin 	{
297d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
298d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RX*", "*" },
299d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
300d3a460d3SAlexander Motin 	},
301d3a460d3SAlexander Motin 	{
302d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
303d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PKT*", "*" },
304d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
305d3a460d3SAlexander Motin 	},
306d3a460d3SAlexander Motin 	{
307d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
308d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PKT*", "*" },
309d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
310d3a460d3SAlexander Motin 	},
311d3a460d3SAlexander Motin 	{
312d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
313d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PVT*", "*" },
314d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
315d3a460d3SAlexander Motin 	},
316d3a460d3SAlexander Motin 	{
317d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
318d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PVT*", "*" },
319d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
320d3a460d3SAlexander Motin 	},
32132fe0ef7SSteven Hartland 	/* SSDs */
322d3a460d3SAlexander Motin 	{
3239d3334e1SEitan Adler 		/*
3249d3334e1SEitan Adler 		 * Corsair Force 2 SSDs
3259d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3269d3334e1SEitan Adler 		 */
3279d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair CSSD-F*", "*" },
3289d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3299d3334e1SEitan Adler 	},
3309d3334e1SEitan Adler 	{
3319d3334e1SEitan Adler 		/*
3329d3334e1SEitan Adler 		 * Corsair Force 3 SSDs
3339d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3349d3334e1SEitan Adler 		 */
3359d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force 3*", "*" },
3369d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3379d3334e1SEitan Adler 	},
3389d3334e1SEitan Adler 	{
3399d3334e1SEitan Adler 		/*
340d85805b2SSteven Hartland 		 * Corsair Neutron GTX SSDs
341d85805b2SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
342d85805b2SSteven Hartland 		 */
343d85805b2SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Neutron GTX*", "*" },
344d85805b2SSteven Hartland 		/*quirks*/ADA_Q_4K
345d85805b2SSteven Hartland 	},
346d85805b2SSteven Hartland 	{
347d85805b2SSteven Hartland 		/*
348dc98c62fSSteven Hartland 		 * Corsair Force GT & GS SSDs
3499d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3509d3334e1SEitan Adler 		 */
351dc98c62fSSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force G*", "*" },
3529d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3539d3334e1SEitan Adler 	},
3549d3334e1SEitan Adler 	{
3559d3334e1SEitan Adler 		/*
35632fe0ef7SSteven Hartland 		 * Crucial M4 SSDs
3579d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3589d3334e1SEitan Adler 		 */
35932fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "M4-CT???M4SSD2*", "*" },
3609d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3619d3334e1SEitan Adler 	},
3629d3334e1SEitan Adler 	{
3639d3334e1SEitan Adler 		/*
364*a6e0c5daSWarner Losh 		 * Crucial M500 SSDs EU07 firmware
365*a6e0c5daSWarner Losh 		 * NCQ Trim works ?
366*a6e0c5daSWarner Losh 		 */
367*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "EU07" },
368*a6e0c5daSWarner Losh 		/*quirks*/0
369*a6e0c5daSWarner Losh 	},
370*a6e0c5daSWarner Losh 	{
371*a6e0c5daSWarner Losh 		/*
372*a6e0c5daSWarner Losh 		 * Crucial M500 SSDs all other firmware
373*a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work
374*a6e0c5daSWarner Losh 		 */
375*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "*" },
376*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
377*a6e0c5daSWarner Losh 	},
378*a6e0c5daSWarner Losh 	{
379*a6e0c5daSWarner Losh 		/*
380*a6e0c5daSWarner Losh 		 * Crucial M550 SSDs
381*a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work, but only on MU01 firmware
382*a6e0c5daSWarner Losh 		 */
383*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M550*", "MU01" },
384*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
385*a6e0c5daSWarner Losh 	},
386*a6e0c5daSWarner Losh 	{
387*a6e0c5daSWarner Losh 		/*
388*a6e0c5daSWarner Losh 		 * Crucial MX100 SSDs
389*a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work, but only on MU01 firmware
390*a6e0c5daSWarner Losh 		 */
391*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*MX100*", "MU01" },
392*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
393*a6e0c5daSWarner Losh 	},
394*a6e0c5daSWarner Losh 	{
395*a6e0c5daSWarner Losh 		/*
3969d3334e1SEitan Adler 		 * Crucial RealSSD C300 SSDs
3979d3334e1SEitan Adler 		 * 4k optimised
3989d3334e1SEitan Adler 		 */
3999d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*",
4009d3334e1SEitan Adler 		"*" }, /*quirks*/ADA_Q_4K
4019d3334e1SEitan Adler 	},
4029d3334e1SEitan Adler 	{
4039d3334e1SEitan Adler 		/*
404883db1c1SEitan Adler 		 * Intel 320 Series SSDs
405883db1c1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
406883db1c1SEitan Adler 		 */
407883db1c1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2CW*", "*" },
408883db1c1SEitan Adler 		/*quirks*/ADA_Q_4K
409883db1c1SEitan Adler 	},
410883db1c1SEitan Adler 	{
411883db1c1SEitan Adler 		/*
4129d3334e1SEitan Adler 		 * Intel 330 Series SSDs
4139d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
4149d3334e1SEitan Adler 		 */
41532fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2CT*", "*" },
4169d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4179d3334e1SEitan Adler 	},
4189d3334e1SEitan Adler 	{
4199d3334e1SEitan Adler 		/*
420883db1c1SEitan Adler 		 * Intel 510 Series SSDs
421883db1c1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
422883db1c1SEitan Adler 		 */
423883db1c1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2MH*", "*" },
424883db1c1SEitan Adler 		/*quirks*/ADA_Q_4K
425883db1c1SEitan Adler 	},
426883db1c1SEitan Adler 	{
427883db1c1SEitan Adler 		/*
42832fe0ef7SSteven Hartland 		 * Intel 520 Series SSDs
4299d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
4309d3334e1SEitan Adler 		 */
43132fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2BW*", "*" },
43232fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
43332fe0ef7SSteven Hartland 	},
43432fe0ef7SSteven Hartland 	{
43532fe0ef7SSteven Hartland 		/*
436dce643c8SSteven Hartland 		 * Intel X25-M Series SSDs
437dce643c8SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
438dce643c8SSteven Hartland 		 */
439dce643c8SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2M*", "*" },
440dce643c8SSteven Hartland 		/*quirks*/ADA_Q_4K
441dce643c8SSteven Hartland 	},
442dce643c8SSteven Hartland 	{
443dce643c8SSteven Hartland 		/*
44432fe0ef7SSteven Hartland 		 * Kingston E100 Series SSDs
44532fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
44632fe0ef7SSteven Hartland 		 */
44732fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SE100S3*", "*" },
4489d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4499d3334e1SEitan Adler 	},
4509d3334e1SEitan Adler 	{
4519d3334e1SEitan Adler 		/*
4529d3334e1SEitan Adler 		 * Kingston HyperX 3k SSDs
4539d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
4549d3334e1SEitan Adler 		 */
4559d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SH103S3*", "*" },
4569d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4579d3334e1SEitan Adler 	},
4589d3334e1SEitan Adler 	{
45932fe0ef7SSteven Hartland 		/*
460dce643c8SSteven Hartland 		 * Marvell SSDs (entry taken from OpenSolaris)
461dce643c8SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
462dce643c8SSteven Hartland 		 */
463dce643c8SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "MARVELL SD88SA02*", "*" },
464dce643c8SSteven Hartland 		/*quirks*/ADA_Q_4K
465dce643c8SSteven Hartland 	},
466dce643c8SSteven Hartland 	{
467dce643c8SSteven Hartland 		/*
468*a6e0c5daSWarner Losh 		 * Micron M500 SSDs firmware EU07
469*a6e0c5daSWarner Losh 		 * NCQ Trim works?
470*a6e0c5daSWarner Losh 		 */
471*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "EU07" },
472*a6e0c5daSWarner Losh 		/*quirks*/0
473*a6e0c5daSWarner Losh 	},
474*a6e0c5daSWarner Losh 	{
475*a6e0c5daSWarner Losh 		/*
476*a6e0c5daSWarner Losh 		 * Micron M500 SSDs all other firmware
477*a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work
478*a6e0c5daSWarner Losh 		 */
479*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "*" },
480*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
481*a6e0c5daSWarner Losh 	},
482*a6e0c5daSWarner Losh 	{
483*a6e0c5daSWarner Losh 		/*
484*a6e0c5daSWarner Losh 		 * Micron M5[15]0 SSDs
485*a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work, but only MU01 firmware
486*a6e0c5daSWarner Losh 		 */
487*a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M5[15]0*", "MU01" },
488*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
489*a6e0c5daSWarner Losh 	},
490*a6e0c5daSWarner Losh 	{
491*a6e0c5daSWarner Losh 		/*
492dce643c8SSteven Hartland 		 * OCZ Agility 2 SSDs
493dce643c8SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
494dce643c8SSteven Hartland 		 */
495dce643c8SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY2*", "*" },
496dce643c8SSteven Hartland 		/*quirks*/ADA_Q_4K
497dce643c8SSteven Hartland 	},
498dce643c8SSteven Hartland 	{
499dce643c8SSteven Hartland 		/*
50032fe0ef7SSteven Hartland 		 * OCZ Agility 3 SSDs
50132fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
50232fe0ef7SSteven Hartland 		 */
50332fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY3*", "*" },
50432fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
50532fe0ef7SSteven Hartland 	},
50632fe0ef7SSteven Hartland 	{
50732fe0ef7SSteven Hartland 		/*
50832fe0ef7SSteven Hartland 		 * OCZ Deneva R Series SSDs
50932fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
51032fe0ef7SSteven Hartland 		 */
51132fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "DENRSTE251M45*", "*" },
51232fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
51332fe0ef7SSteven Hartland 	},
51432fe0ef7SSteven Hartland 	{
51532fe0ef7SSteven Hartland 		/*
51632fe0ef7SSteven Hartland 		 * OCZ Vertex 2 SSDs (inc pro series)
51732fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
51832fe0ef7SSteven Hartland 		 */
51932fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ?VERTEX2*", "*" },
52032fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
52132fe0ef7SSteven Hartland 	},
52232fe0ef7SSteven Hartland 	{
52332fe0ef7SSteven Hartland 		/*
52432fe0ef7SSteven Hartland 		 * OCZ Vertex 3 SSDs
52532fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
52632fe0ef7SSteven Hartland 		 */
52732fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX3*", "*" },
52832fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
52932fe0ef7SSteven Hartland 	},
53032fe0ef7SSteven Hartland 	{
53132fe0ef7SSteven Hartland 		/*
5327f1c7787SSteven Hartland 		 * OCZ Vertex 4 SSDs
5337f1c7787SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
5347f1c7787SSteven Hartland 		 */
5357f1c7787SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX4*", "*" },
5367f1c7787SSteven Hartland 		/*quirks*/ADA_Q_4K
5377f1c7787SSteven Hartland 	},
5387f1c7787SSteven Hartland 	{
5397f1c7787SSteven Hartland 		/*
54032fe0ef7SSteven Hartland 		 * Samsung 830 Series SSDs
541*a6e0c5daSWarner Losh 		 * 4k optimised, NCQ TRIM Broken (normal TRIM is fine)
54232fe0ef7SSteven Hartland 		 */
54332fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD 830 Series*", "*" },
544*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
54532fe0ef7SSteven Hartland 	},
54632fe0ef7SSteven Hartland 	{
54732fe0ef7SSteven Hartland 		/*
548dc98c62fSSteven Hartland 		 * Samsung 840 SSDs
549*a6e0c5daSWarner Losh 		 * 4k optimised, NCQ TRIM Broken (normal TRIM is fine)
550dc98c62fSSteven Hartland 		 */
551dc98c62fSSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 840*", "*" },
552*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
553dc98c62fSSteven Hartland 	},
554dc98c62fSSteven Hartland 	{
555dc98c62fSSteven Hartland 		/*
556e3a21bd1SGeorge V. Neville-Neil 		 * Samsung 850 SSDs
557*a6e0c5daSWarner Losh 		 * 4k optimised, NCQ TRIM broken (normal TRIM fine)
558e3a21bd1SGeorge V. Neville-Neil 		 */
559e3a21bd1SGeorge V. Neville-Neil 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 850*", "*" },
560*a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
561e3a21bd1SGeorge V. Neville-Neil 	},
5625f91863aSSean Bruno 	{
5635f91863aSSean Bruno 		/*
564eae90da9SJean-Sébastien Pédron 		 * Samsung 843T Series SSDs (MZ7WD*)
565eae90da9SJean-Sébastien Pédron 		 * Samsung PM851 Series SSDs (MZ7TE*)
566eae90da9SJean-Sébastien Pédron 		 * Samsung PM853T Series SSDs (MZ7GE*)
567eae90da9SJean-Sébastien Pédron 		 * Samsung SM863 Series SSDs (MZ7KM*)
568*a6e0c5daSWarner Losh 		 * 4k optimised, NCQ Trim believed working
569323e0f6dSSean Bruno 		 */
570eae90da9SJean-Sébastien Pédron 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" },
571844b7984SSean Bruno 		/*quirks*/ADA_Q_4K
572844b7984SSean Bruno 	},
573844b7984SSean Bruno 	{
574844b7984SSean Bruno 		/*
57532fe0ef7SSteven Hartland 		 * SuperTalent TeraDrive CT SSDs
57632fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
57732fe0ef7SSteven Hartland 		 */
57832fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "FTM??CT25H*", "*" },
57932fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
58032fe0ef7SSteven Hartland 	},
58132fe0ef7SSteven Hartland 	{
58232fe0ef7SSteven Hartland 		/*
58332fe0ef7SSteven Hartland 		 * XceedIOPS SATA SSDs
58432fe0ef7SSteven Hartland 		 * 4k optimised
58532fe0ef7SSteven Hartland 		 */
58632fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SG9XCS2D*", "*" },
58732fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
58832fe0ef7SSteven Hartland 	},
58932fe0ef7SSteven Hartland 	{
59030a4094fSAlexander Motin 		/* Default */
59130a4094fSAlexander Motin 		{
59230a4094fSAlexander Motin 		  T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
59330a4094fSAlexander Motin 		  /*vendor*/"*", /*product*/"*", /*revision*/"*"
59430a4094fSAlexander Motin 		},
59530a4094fSAlexander Motin 		/*quirks*/0
59630a4094fSAlexander Motin 	},
59730a4094fSAlexander Motin };
59852c9ce25SScott Long 
59952c9ce25SScott Long static	disk_strategy_t	adastrategy;
60052c9ce25SScott Long static	dumper_t	adadump;
60152c9ce25SScott Long static	periph_init_t	adainit;
60252c9ce25SScott Long static	void		adaasync(void *callback_arg, u_int32_t code,
60352c9ce25SScott Long 				struct cam_path *path, void *arg);
60452c9ce25SScott Long static	void		adasysctlinit(void *context, int pending);
60552c9ce25SScott Long static	periph_ctor_t	adaregister;
60652c9ce25SScott Long static	periph_dtor_t	adacleanup;
60752c9ce25SScott Long static	periph_start_t	adastart;
60852c9ce25SScott Long static	periph_oninv_t	adaoninvalidate;
60952c9ce25SScott Long static	void		adadone(struct cam_periph *periph,
61052c9ce25SScott Long 			       union ccb *done_ccb);
61152c9ce25SScott Long static  int		adaerror(union ccb *ccb, u_int32_t cam_flags,
61252c9ce25SScott Long 				u_int32_t sense_flags);
613c1bd46c2SAlexander Motin static void		adagetparams(struct cam_periph *periph,
61452c9ce25SScott Long 				struct ccb_getdev *cgd);
61552c9ce25SScott Long static timeout_t	adasendorderedtag;
61652c9ce25SScott Long static void		adashutdown(void *arg, int howto);
617c3d0d168SAlexander Motin static void		adasuspend(void *arg);
618c3d0d168SAlexander Motin static void		adaresume(void *arg);
61952c9ce25SScott Long 
6200d307e09SAlexander Motin #ifndef	ADA_DEFAULT_LEGACY_ALIASES
6210d307e09SAlexander Motin #define	ADA_DEFAULT_LEGACY_ALIASES	1
6220d307e09SAlexander Motin #endif
6230d307e09SAlexander Motin 
62452c9ce25SScott Long #ifndef ADA_DEFAULT_TIMEOUT
62552c9ce25SScott Long #define ADA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
62652c9ce25SScott Long #endif
62752c9ce25SScott Long 
62852c9ce25SScott Long #ifndef	ADA_DEFAULT_RETRY
62952c9ce25SScott Long #define	ADA_DEFAULT_RETRY	4
63052c9ce25SScott Long #endif
63152c9ce25SScott Long 
63252c9ce25SScott Long #ifndef	ADA_DEFAULT_SEND_ORDERED
63352c9ce25SScott Long #define	ADA_DEFAULT_SEND_ORDERED	1
63452c9ce25SScott Long #endif
63552c9ce25SScott Long 
636fd104c15SRebecca Cran #ifndef	ADA_DEFAULT_SPINDOWN_SHUTDOWN
637fd104c15SRebecca Cran #define	ADA_DEFAULT_SPINDOWN_SHUTDOWN	1
638fd104c15SRebecca Cran #endif
639fd104c15SRebecca Cran 
640c3d0d168SAlexander Motin #ifndef	ADA_DEFAULT_SPINDOWN_SUSPEND
641c3d0d168SAlexander Motin #define	ADA_DEFAULT_SPINDOWN_SUSPEND	1
642c3d0d168SAlexander Motin #endif
643c3d0d168SAlexander Motin 
6441ed6aaf9SAlexander Motin #ifndef	ADA_DEFAULT_READ_AHEAD
6451ed6aaf9SAlexander Motin #define	ADA_DEFAULT_READ_AHEAD	1
6461ed6aaf9SAlexander Motin #endif
6471ed6aaf9SAlexander Motin 
648f513d14cSAlexander Motin #ifndef	ADA_DEFAULT_WRITE_CACHE
649f513d14cSAlexander Motin #define	ADA_DEFAULT_WRITE_CACHE	1
650f513d14cSAlexander Motin #endif
651f513d14cSAlexander Motin 
6521ed6aaf9SAlexander Motin #define	ADA_RA	(softc->read_ahead >= 0 ? \
6531ed6aaf9SAlexander Motin 		 softc->read_ahead : ada_read_ahead)
6541ed6aaf9SAlexander Motin #define	ADA_WC	(softc->write_cache >= 0 ? \
6551ed6aaf9SAlexander Motin 		 softc->write_cache : ada_write_cache)
6561ed6aaf9SAlexander Motin 
6574461491bSMarius Strobl /*
6584461491bSMarius Strobl  * Most platforms map firmware geometry to actual, but some don't.  If
6594461491bSMarius Strobl  * not overridden, default to nothing.
6604461491bSMarius Strobl  */
6614461491bSMarius Strobl #ifndef ata_disk_firmware_geom_adjust
6624461491bSMarius Strobl #define	ata_disk_firmware_geom_adjust(disk)
6634461491bSMarius Strobl #endif
66452c9ce25SScott Long 
66552c9ce25SScott Long static int ada_retry_count = ADA_DEFAULT_RETRY;
66652c9ce25SScott Long static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
66752c9ce25SScott Long static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
668fd104c15SRebecca Cran static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
669c3d0d168SAlexander Motin static int ada_spindown_suspend = ADA_DEFAULT_SPINDOWN_SUSPEND;
6701ed6aaf9SAlexander Motin static int ada_read_ahead = ADA_DEFAULT_READ_AHEAD;
671f513d14cSAlexander Motin static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE;
67252c9ce25SScott Long 
6736472ac3dSEd Schouten static SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
67452c9ce25SScott Long             "CAM Direct Access Disk driver");
675af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, retry_count, CTLFLAG_RWTUN,
67652c9ce25SScott Long            &ada_retry_count, 0, "Normal I/O retry count");
677af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, default_timeout, CTLFLAG_RWTUN,
67852c9ce25SScott Long            &ada_default_timeout, 0, "Normal I/O timeout (in seconds)");
679af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, send_ordered, CTLFLAG_RWTUN,
68052c9ce25SScott Long            &ada_send_ordered, 0, "Send Ordered Tags");
681af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RWTUN,
682fd104c15SRebecca Cran            &ada_spindown_shutdown, 0, "Spin down upon shutdown");
683af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_suspend, CTLFLAG_RWTUN,
684c3d0d168SAlexander Motin            &ada_spindown_suspend, 0, "Spin down upon suspend");
685af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, read_ahead, CTLFLAG_RWTUN,
6861ed6aaf9SAlexander Motin            &ada_read_ahead, 0, "Enable disk read-ahead");
687af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RWTUN,
688f513d14cSAlexander Motin            &ada_write_cache, 0, "Enable disk write cache");
68952c9ce25SScott Long 
69052c9ce25SScott Long /*
69152c9ce25SScott Long  * ADA_ORDEREDTAG_INTERVAL determines how often, relative
69252c9ce25SScott Long  * to the default timeout, we check to see whether an ordered
69352c9ce25SScott Long  * tagged transaction is appropriate to prevent simple tag
69452c9ce25SScott Long  * starvation.  Since we'd like to ensure that there is at least
69552c9ce25SScott Long  * 1/2 of the timeout length left for a starved transaction to
69652c9ce25SScott Long  * complete after we've sent an ordered tag, we must poll at least
69752c9ce25SScott Long  * four times in every timeout period.  This takes care of the worst
69852c9ce25SScott Long  * case where a starved transaction starts during an interval that
69952c9ce25SScott Long  * meets the requirement "don't send an ordered tag" test so it takes
70052c9ce25SScott Long  * us two intervals to determine that a tag must be sent.
70152c9ce25SScott Long  */
70252c9ce25SScott Long #ifndef ADA_ORDEREDTAG_INTERVAL
70352c9ce25SScott Long #define ADA_ORDEREDTAG_INTERVAL 4
70452c9ce25SScott Long #endif
70552c9ce25SScott Long 
70652c9ce25SScott Long static struct periph_driver adadriver =
70752c9ce25SScott Long {
70852c9ce25SScott Long 	adainit, "ada",
70952c9ce25SScott Long 	TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0
71052c9ce25SScott Long };
71152c9ce25SScott Long 
712*a6e0c5daSWarner Losh static int adadeletemethodsysctl(SYSCTL_HANDLER_ARGS);
713*a6e0c5daSWarner Losh 
71452c9ce25SScott Long PERIPHDRIVER_DECLARE(ada, adadriver);
71552c9ce25SScott Long 
71652c9ce25SScott Long static int
71752c9ce25SScott Long adaopen(struct disk *dp)
71852c9ce25SScott Long {
71952c9ce25SScott Long 	struct cam_periph *periph;
72052c9ce25SScott Long 	struct ada_softc *softc;
72152c9ce25SScott Long 	int error;
72252c9ce25SScott Long 
72352c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
72452c9ce25SScott Long 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
72552c9ce25SScott Long 		return(ENXIO);
72652c9ce25SScott Long 	}
72752c9ce25SScott Long 
72852c9ce25SScott Long 	cam_periph_lock(periph);
72952c9ce25SScott Long 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
73052c9ce25SScott Long 		cam_periph_unlock(periph);
73152c9ce25SScott Long 		cam_periph_release(periph);
73252c9ce25SScott Long 		return (error);
73352c9ce25SScott Long 	}
73452c9ce25SScott Long 
735fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
736fddde2b8SAlexander Motin 	    ("adaopen\n"));
73752c9ce25SScott Long 
7387338ef1aSAlexander Motin 	softc = (struct ada_softc *)periph->softc;
7397338ef1aSAlexander Motin 	softc->flags |= ADA_FLAG_OPEN;
74052c9ce25SScott Long 
74152c9ce25SScott Long 	cam_periph_unhold(periph);
74252c9ce25SScott Long 	cam_periph_unlock(periph);
74352c9ce25SScott Long 	return (0);
74452c9ce25SScott Long }
74552c9ce25SScott Long 
74652c9ce25SScott Long static int
74752c9ce25SScott Long adaclose(struct disk *dp)
74852c9ce25SScott Long {
74952c9ce25SScott Long 	struct	cam_periph *periph;
75052c9ce25SScott Long 	struct	ada_softc *softc;
75152c9ce25SScott Long 	union ccb *ccb;
75269114bc0SAlexander Motin 	int error;
75352c9ce25SScott Long 
75452c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
75552c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
756227d67aaSAlexander Motin 	cam_periph_lock(periph);
757fddde2b8SAlexander Motin 
758fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
759fddde2b8SAlexander Motin 	    ("adaclose\n"));
760fddde2b8SAlexander Motin 
76152c9ce25SScott Long 	/* We only sync the cache if the drive is capable of it. */
76269114bc0SAlexander Motin 	if ((softc->flags & ADA_FLAG_DIRTY) != 0 &&
76369114bc0SAlexander Motin 	    (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) != 0 &&
764227d67aaSAlexander Motin 	    (periph->flags & CAM_PERIPH_INVALID) == 0 &&
765227d67aaSAlexander Motin 	    cam_periph_hold(periph, PRIBIO) == 0) {
76652c9ce25SScott Long 
767bbfa4aa1SAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
76852c9ce25SScott Long 		cam_fill_ataio(&ccb->ataio,
76952c9ce25SScott Long 				    1,
77052c9ce25SScott Long 				    adadone,
77152c9ce25SScott Long 				    CAM_DIR_NONE,
77252c9ce25SScott Long 				    0,
77352c9ce25SScott Long 				    NULL,
77452c9ce25SScott Long 				    0,
77552c9ce25SScott Long 				    ada_default_timeout*1000);
77652c9ce25SScott Long 
77752c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
77852c9ce25SScott Long 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
77952c9ce25SScott Long 		else
7807606b445SAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
78169114bc0SAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
78246f118feSAlexander Motin 		    /*sense_flags*/0, softc->disk->d_devstat);
78352c9ce25SScott Long 
78469114bc0SAlexander Motin 		if (error != 0)
78552c9ce25SScott Long 			xpt_print(periph->path, "Synchronize cache failed\n");
78669114bc0SAlexander Motin 		else
78769114bc0SAlexander Motin 			softc->flags &= ~ADA_FLAG_DIRTY;
78852c9ce25SScott Long 		xpt_release_ccb(ccb);
789227d67aaSAlexander Motin 		cam_periph_unhold(periph);
79052c9ce25SScott Long 	}
79152c9ce25SScott Long 
79252c9ce25SScott Long 	softc->flags &= ~ADA_FLAG_OPEN;
793227d67aaSAlexander Motin 
794227d67aaSAlexander Motin 	while (softc->refcount != 0)
795227d67aaSAlexander Motin 		cam_periph_sleep(periph, &softc->refcount, PRIBIO, "adaclose", 1);
79652c9ce25SScott Long 	cam_periph_unlock(periph);
79752c9ce25SScott Long 	cam_periph_release(periph);
79852c9ce25SScott Long 	return (0);
79952c9ce25SScott Long }
80052c9ce25SScott Long 
8011c80ec0aSAlexander Motin static void
8021c80ec0aSAlexander Motin adaschedule(struct cam_periph *periph)
8031c80ec0aSAlexander Motin {
8041c80ec0aSAlexander Motin 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
8051c80ec0aSAlexander Motin 
8066bf435dcSAlexander Motin 	if (softc->state != ADA_STATE_NORMAL)
8076bf435dcSAlexander Motin 		return;
8086bf435dcSAlexander Motin 
809*a6e0c5daSWarner Losh 	cam_iosched_schedule(softc->cam_iosched, periph);
8101c80ec0aSAlexander Motin }
8111c80ec0aSAlexander Motin 
81252c9ce25SScott Long /*
81352c9ce25SScott Long  * Actually translate the requested transfer into one the physical driver
81452c9ce25SScott Long  * can understand.  The transfer is described by a buf and will include
81552c9ce25SScott Long  * only one physical transfer.
81652c9ce25SScott Long  */
81752c9ce25SScott Long static void
81852c9ce25SScott Long adastrategy(struct bio *bp)
81952c9ce25SScott Long {
82052c9ce25SScott Long 	struct cam_periph *periph;
82152c9ce25SScott Long 	struct ada_softc *softc;
82252c9ce25SScott Long 
82352c9ce25SScott Long 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
82452c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
82552c9ce25SScott Long 
82652c9ce25SScott Long 	cam_periph_lock(periph);
82752c9ce25SScott Long 
828fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastrategy(%p)\n", bp));
829fddde2b8SAlexander Motin 
83052c9ce25SScott Long 	/*
83152c9ce25SScott Long 	 * If the device has been made invalid, error out
83252c9ce25SScott Long 	 */
8337338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
83452c9ce25SScott Long 		cam_periph_unlock(periph);
83552c9ce25SScott Long 		biofinish(bp, NULL, ENXIO);
83652c9ce25SScott Long 		return;
83752c9ce25SScott Long 	}
83852c9ce25SScott Long 
83952c9ce25SScott Long 	/*
84052c9ce25SScott Long 	 * Place it in the queue of disk activities for this disk
84152c9ce25SScott Long 	 */
842*a6e0c5daSWarner Losh 	cam_iosched_queue_work(softc->cam_iosched, bp);
84352c9ce25SScott Long 
84452c9ce25SScott Long 	/*
84552c9ce25SScott Long 	 * Schedule ourselves for performing the work.
84652c9ce25SScott Long 	 */
8471c80ec0aSAlexander Motin 	adaschedule(periph);
84852c9ce25SScott Long 	cam_periph_unlock(periph);
84952c9ce25SScott Long 
85052c9ce25SScott Long 	return;
85152c9ce25SScott Long }
85252c9ce25SScott Long 
85352c9ce25SScott Long static int
85452c9ce25SScott Long adadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
85552c9ce25SScott Long {
85652c9ce25SScott Long 	struct	    cam_periph *periph;
85752c9ce25SScott Long 	struct	    ada_softc *softc;
85852c9ce25SScott Long 	u_int	    secsize;
85952c9ce25SScott Long 	union	    ccb ccb;
86052c9ce25SScott Long 	struct	    disk *dp;
86152c9ce25SScott Long 	uint64_t    lba;
86252c9ce25SScott Long 	uint16_t    count;
8630191d9b3SAlexander Motin 	int	    error = 0;
86452c9ce25SScott Long 
86552c9ce25SScott Long 	dp = arg;
86652c9ce25SScott Long 	periph = dp->d_drv1;
86752c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
86852c9ce25SScott Long 	cam_periph_lock(periph);
86952c9ce25SScott Long 	secsize = softc->params.secsize;
87052c9ce25SScott Long 	lba = offset / secsize;
87152c9ce25SScott Long 	count = length / secsize;
87252c9ce25SScott Long 
8737338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
87452c9ce25SScott Long 		cam_periph_unlock(periph);
87552c9ce25SScott Long 		return (ENXIO);
87652c9ce25SScott Long 	}
87752c9ce25SScott Long 
87852c9ce25SScott Long 	if (length > 0) {
879bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
88052c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
88152c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
88252c9ce25SScott Long 		    0,
88352c9ce25SScott Long 		    adadone,
88452c9ce25SScott Long 		    CAM_DIR_OUT,
88552c9ce25SScott Long 		    0,
88652c9ce25SScott Long 		    (u_int8_t *) virtual,
88752c9ce25SScott Long 		    length,
88852c9ce25SScott Long 		    ada_default_timeout*1000);
88952c9ce25SScott Long 		if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
89052c9ce25SScott Long 		    (lba + count >= ATA_MAX_28BIT_LBA ||
89152c9ce25SScott Long 		    count >= 256)) {
89252c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_WRITE_DMA48,
89352c9ce25SScott Long 			    0, lba, count);
89452c9ce25SScott Long 		} else {
8957606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_WRITE_DMA,
89652c9ce25SScott Long 			    0, lba, count);
89752c9ce25SScott Long 		}
89852c9ce25SScott Long 		xpt_polled_action(&ccb);
89952c9ce25SScott Long 
9000191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
9010191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
9020191d9b3SAlexander Motin 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
9030191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
9040191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
9050191d9b3SAlexander Motin 		if (error != 0)
90652c9ce25SScott Long 			printf("Aborting dump due to I/O error.\n");
9070191d9b3SAlexander Motin 
90852c9ce25SScott Long 		cam_periph_unlock(periph);
9090191d9b3SAlexander Motin 		return (error);
91052c9ce25SScott Long 	}
91152c9ce25SScott Long 
91252c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) {
913bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
91452c9ce25SScott Long 
91552c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
91652c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
9170191d9b3SAlexander Motin 				    0,
91852c9ce25SScott Long 				    adadone,
91952c9ce25SScott Long 				    CAM_DIR_NONE,
92052c9ce25SScott Long 				    0,
92152c9ce25SScott Long 				    NULL,
92252c9ce25SScott Long 				    0,
923*a6e0c5daSWarner Losh 				    5*1000);
92452c9ce25SScott Long 
92552c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
92652c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0);
92752c9ce25SScott Long 		else
9287606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_FLUSHCACHE, 0, 0, 0);
92952c9ce25SScott Long 		xpt_polled_action(&ccb);
93052c9ce25SScott Long 
9310191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
9320191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
93352c9ce25SScott Long 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
9340191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
9350191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
9360191d9b3SAlexander Motin 		if (error != 0)
9370191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
93852c9ce25SScott Long 	}
93952c9ce25SScott Long 	cam_periph_unlock(periph);
9400191d9b3SAlexander Motin 	return (error);
94152c9ce25SScott Long }
94252c9ce25SScott Long 
94352c9ce25SScott Long static void
94452c9ce25SScott Long adainit(void)
94552c9ce25SScott Long {
94652c9ce25SScott Long 	cam_status status;
94752c9ce25SScott Long 
94852c9ce25SScott Long 	/*
94952c9ce25SScott Long 	 * Install a global async callback.  This callback will
95052c9ce25SScott Long 	 * receive async callbacks like "new device found".
95152c9ce25SScott Long 	 */
95252c9ce25SScott Long 	status = xpt_register_async(AC_FOUND_DEVICE, adaasync, NULL, NULL);
95352c9ce25SScott Long 
95452c9ce25SScott Long 	if (status != CAM_REQ_CMP) {
95552c9ce25SScott Long 		printf("ada: Failed to attach master async callback "
95652c9ce25SScott Long 		       "due to status 0x%x!\n", status);
95752c9ce25SScott Long 	} else if (ada_send_ordered) {
95852c9ce25SScott Long 
959c3d0d168SAlexander Motin 		/* Register our event handlers */
960c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_suspend, adasuspend,
961c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
962c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
963c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_resume, adaresume,
964c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
965c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
96652c9ce25SScott Long 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, adashutdown,
96752c9ce25SScott Long 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
96852c9ce25SScott Long 		    printf("adainit: shutdown event registration failed!\n");
96952c9ce25SScott Long 	}
97052c9ce25SScott Long }
97152c9ce25SScott Long 
9720ba1e4d0SKenneth D. Merry /*
9730ba1e4d0SKenneth D. Merry  * Callback from GEOM, called when it has finished cleaning up its
9740ba1e4d0SKenneth D. Merry  * resources.
9750ba1e4d0SKenneth D. Merry  */
9760ba1e4d0SKenneth D. Merry static void
9770ba1e4d0SKenneth D. Merry adadiskgonecb(struct disk *dp)
9780ba1e4d0SKenneth D. Merry {
9790ba1e4d0SKenneth D. Merry 	struct cam_periph *periph;
9800ba1e4d0SKenneth D. Merry 
9810ba1e4d0SKenneth D. Merry 	periph = (struct cam_periph *)dp->d_drv1;
9820ba1e4d0SKenneth D. Merry 
9830ba1e4d0SKenneth D. Merry 	cam_periph_release(periph);
9840ba1e4d0SKenneth D. Merry }
9850ba1e4d0SKenneth D. Merry 
98652c9ce25SScott Long static void
98752c9ce25SScott Long adaoninvalidate(struct cam_periph *periph)
98852c9ce25SScott Long {
98952c9ce25SScott Long 	struct ada_softc *softc;
99052c9ce25SScott Long 
99152c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
99252c9ce25SScott Long 
99352c9ce25SScott Long 	/*
99452c9ce25SScott Long 	 * De-register any async callbacks.
99552c9ce25SScott Long 	 */
99652c9ce25SScott Long 	xpt_register_async(0, adaasync, periph, periph->path);
997*a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
998*a6e0c5daSWarner Losh 	softc->invalidations++;
999*a6e0c5daSWarner Losh #endif
100052c9ce25SScott Long 
100152c9ce25SScott Long 	/*
100252c9ce25SScott Long 	 * Return all queued I/O with ENXIO.
100352c9ce25SScott Long 	 * XXX Handle any transactions queued to the card
100452c9ce25SScott Long 	 *     with XPT_ABORT_CCB.
100552c9ce25SScott Long 	 */
1006*a6e0c5daSWarner Losh 	cam_iosched_flush(softc->cam_iosched, NULL, ENXIO);
100752c9ce25SScott Long 
100852c9ce25SScott Long 	disk_gone(softc->disk);
100952c9ce25SScott Long }
101052c9ce25SScott Long 
101152c9ce25SScott Long static void
101252c9ce25SScott Long adacleanup(struct cam_periph *periph)
101352c9ce25SScott Long {
101452c9ce25SScott Long 	struct ada_softc *softc;
101552c9ce25SScott Long 
101652c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
101752c9ce25SScott Long 
101852c9ce25SScott Long 	cam_periph_unlock(periph);
101952c9ce25SScott Long 
1020*a6e0c5daSWarner Losh 	cam_iosched_fini(softc->cam_iosched);
1021*a6e0c5daSWarner Losh 
102252c9ce25SScott Long 	/*
102352c9ce25SScott Long 	 * If we can't free the sysctl tree, oh well...
102452c9ce25SScott Long 	 */
1025*a6e0c5daSWarner Losh 	if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0) {
1026*a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
1027*a6e0c5daSWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0)
1028*a6e0c5daSWarner Losh 			xpt_print(periph->path,
1029*a6e0c5daSWarner Losh 			    "can't remove sysctl stats context\n");
1030*a6e0c5daSWarner Losh #endif
1031*a6e0c5daSWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_ctx) != 0)
1032*a6e0c5daSWarner Losh 			xpt_print(periph->path,
1033*a6e0c5daSWarner Losh 			    "can't remove sysctl context\n");
103452c9ce25SScott Long 	}
103552c9ce25SScott Long 
103652c9ce25SScott Long 	disk_destroy(softc->disk);
103752c9ce25SScott Long 	callout_drain(&softc->sendordered_c);
103852c9ce25SScott Long 	free(softc, M_DEVBUF);
103952c9ce25SScott Long 	cam_periph_lock(periph);
104052c9ce25SScott Long }
104152c9ce25SScott Long 
104252c9ce25SScott Long static void
1043*a6e0c5daSWarner Losh adasetdeletemethod(struct ada_softc *softc)
1044*a6e0c5daSWarner Losh {
1045*a6e0c5daSWarner Losh 
1046*a6e0c5daSWarner Losh 	if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM)
1047*a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_NCQ_DSM_TRIM;
1048*a6e0c5daSWarner Losh 	else if (softc->flags & ADA_FLAG_CAN_TRIM)
1049*a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_DSM_TRIM;
1050*a6e0c5daSWarner Losh 	else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT))
1051*a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_CFA_ERASE;
1052*a6e0c5daSWarner Losh 	else
1053*a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_NONE;
1054*a6e0c5daSWarner Losh }
1055*a6e0c5daSWarner Losh 
1056*a6e0c5daSWarner Losh static void
105752c9ce25SScott Long adaasync(void *callback_arg, u_int32_t code,
105852c9ce25SScott Long 	struct cam_path *path, void *arg)
105952c9ce25SScott Long {
1060581b2e78SAlexander Motin 	struct ccb_getdev cgd;
106152c9ce25SScott Long 	struct cam_periph *periph;
1062f513d14cSAlexander Motin 	struct ada_softc *softc;
106352c9ce25SScott Long 
106452c9ce25SScott Long 	periph = (struct cam_periph *)callback_arg;
106552c9ce25SScott Long 	switch (code) {
106652c9ce25SScott Long 	case AC_FOUND_DEVICE:
106752c9ce25SScott Long 	{
106852c9ce25SScott Long 		struct ccb_getdev *cgd;
106952c9ce25SScott Long 		cam_status status;
107052c9ce25SScott Long 
107152c9ce25SScott Long 		cgd = (struct ccb_getdev *)arg;
107252c9ce25SScott Long 		if (cgd == NULL)
107352c9ce25SScott Long 			break;
107452c9ce25SScott Long 
107552c9ce25SScott Long 		if (cgd->protocol != PROTO_ATA)
107652c9ce25SScott Long 			break;
107752c9ce25SScott Long 
107852c9ce25SScott Long 		/*
107952c9ce25SScott Long 		 * Allocate a peripheral instance for
108052c9ce25SScott Long 		 * this device and start the probe
108152c9ce25SScott Long 		 * process.
108252c9ce25SScott Long 		 */
108352c9ce25SScott Long 		status = cam_periph_alloc(adaregister, adaoninvalidate,
108452c9ce25SScott Long 					  adacleanup, adastart,
108552c9ce25SScott Long 					  "ada", CAM_PERIPH_BIO,
1086227d67aaSAlexander Motin 					  path, adaasync,
108752c9ce25SScott Long 					  AC_FOUND_DEVICE, cgd);
108852c9ce25SScott Long 
108952c9ce25SScott Long 		if (status != CAM_REQ_CMP
109052c9ce25SScott Long 		 && status != CAM_REQ_INPROG)
109152c9ce25SScott Long 			printf("adaasync: Unable to attach to new device "
109252c9ce25SScott Long 				"due to status 0x%x\n", status);
109352c9ce25SScott Long 		break;
109452c9ce25SScott Long 	}
1095581b2e78SAlexander Motin 	case AC_GETDEV_CHANGED:
1096581b2e78SAlexander Motin 	{
1097581b2e78SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
1098581b2e78SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
1099581b2e78SAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
1100581b2e78SAlexander Motin 		xpt_action((union ccb *)&cgd);
1101581b2e78SAlexander Motin 
1102581b2e78SAlexander Motin 		if ((cgd.ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
1103581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA))
1104581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_DMA;
1105581b2e78SAlexander Motin 		else
1106581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_DMA;
11072e1eb332SMarius Strobl 		if (cgd.ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
11082e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_48BIT;
11092e1eb332SMarius Strobl 			if (cgd.inq_flags & SID_DMA48)
11102e1eb332SMarius Strobl 				softc->flags |= ADA_FLAG_CAN_DMA48;
11112e1eb332SMarius Strobl 			else
11122e1eb332SMarius Strobl 				softc->flags &= ~ADA_FLAG_CAN_DMA48;
11132e1eb332SMarius Strobl 		} else
11142e1eb332SMarius Strobl 			softc->flags &= ~(ADA_FLAG_CAN_48BIT |
11152e1eb332SMarius Strobl 			    ADA_FLAG_CAN_DMA48);
1116581b2e78SAlexander Motin 		if ((cgd.ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
1117581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA) && (cgd.inq_flags & SID_CmdQue))
1118581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_NCQ;
1119581b2e78SAlexander Motin 		else
1120581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_NCQ;
1121*a6e0c5daSWarner Losh 
1122581b2e78SAlexander Motin 		if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
1123*a6e0c5daSWarner Losh 		    (cgd.inq_flags & SID_DMA)) {
1124581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_TRIM;
1125*a6e0c5daSWarner Losh 			/*
1126*a6e0c5daSWarner Losh 			 * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do
1127*a6e0c5daSWarner Losh 			 * NCQ trims, if we support trims at all. We also need support from
1128*a6e0c5daSWarner Losh 			 * the sim do do things properly. Perhaps we should look at log 13
1129*a6e0c5daSWarner Losh 			 * dword 0 bit 0 and dword 1 bit 0 are set too...
1130*a6e0c5daSWarner Losh 			 */
1131*a6e0c5daSWarner Losh 			if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 &&
1132*a6e0c5daSWarner Losh 			    (softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 &&
1133*a6e0c5daSWarner Losh 			    (cgd.ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 &&
1134*a6e0c5daSWarner Losh 			    (softc->flags & ADA_FLAG_CAN_TRIM) != 0)
1135*a6e0c5daSWarner Losh 				softc->flags |= ADA_FLAG_CAN_NCQ_TRIM;
1136581b2e78SAlexander Motin 			else
1137*a6e0c5daSWarner Losh 				softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM;
1138*a6e0c5daSWarner Losh 		} else
1139*a6e0c5daSWarner Losh 			softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM);
1140*a6e0c5daSWarner Losh 		adasetdeletemethod(softc);
1141581b2e78SAlexander Motin 
1142581b2e78SAlexander Motin 		cam_periph_async(periph, code, path, arg);
1143581b2e78SAlexander Motin 		break;
1144581b2e78SAlexander Motin 	}
11453089bb2eSAlexander Motin 	case AC_ADVINFO_CHANGED:
11463089bb2eSAlexander Motin 	{
11473089bb2eSAlexander Motin 		uintptr_t buftype;
11483089bb2eSAlexander Motin 
11493089bb2eSAlexander Motin 		buftype = (uintptr_t)arg;
11503089bb2eSAlexander Motin 		if (buftype == CDAI_TYPE_PHYS_PATH) {
11513089bb2eSAlexander Motin 			struct ada_softc *softc;
11523089bb2eSAlexander Motin 
11533089bb2eSAlexander Motin 			softc = periph->softc;
11543089bb2eSAlexander Motin 			disk_attr_changed(softc->disk, "GEOM::physpath",
11553089bb2eSAlexander Motin 					  M_NOWAIT);
11563089bb2eSAlexander Motin 		}
11573089bb2eSAlexander Motin 		break;
11583089bb2eSAlexander Motin 	}
1159f513d14cSAlexander Motin 	case AC_SENT_BDR:
1160f513d14cSAlexander Motin 	case AC_BUS_RESET:
1161f513d14cSAlexander Motin 	{
1162f513d14cSAlexander Motin 		softc = (struct ada_softc *)periph->softc;
1163f513d14cSAlexander Motin 		cam_periph_async(periph, code, path, arg);
1164f513d14cSAlexander Motin 		if (softc->state != ADA_STATE_NORMAL)
1165f513d14cSAlexander Motin 			break;
1166e3a6d3a4SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
1167f513d14cSAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
1168f513d14cSAlexander Motin 		xpt_action((union ccb *)&cgd);
11691ed6aaf9SAlexander Motin 		if (ADA_RA >= 0 &&
11701ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD)
11711ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_RAHEAD;
11721ed6aaf9SAlexander Motin 		else if (ADA_WC >= 0 &&
11731ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE)
1174f513d14cSAlexander Motin 			softc->state = ADA_STATE_WCACHE;
11751ed6aaf9SAlexander Motin 		else
11761ed6aaf9SAlexander Motin 		    break;
1177227d67aaSAlexander Motin 		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
1178227d67aaSAlexander Motin 			softc->state = ADA_STATE_NORMAL;
1179227d67aaSAlexander Motin 		else
1180f513d14cSAlexander Motin 			xpt_schedule(periph, CAM_PRIORITY_DEV);
1181f513d14cSAlexander Motin 	}
118252c9ce25SScott Long 	default:
118352c9ce25SScott Long 		cam_periph_async(periph, code, path, arg);
118452c9ce25SScott Long 		break;
118552c9ce25SScott Long 	}
118652c9ce25SScott Long }
118752c9ce25SScott Long 
118852c9ce25SScott Long static void
118952c9ce25SScott Long adasysctlinit(void *context, int pending)
119052c9ce25SScott Long {
119152c9ce25SScott Long 	struct cam_periph *periph;
119252c9ce25SScott Long 	struct ada_softc *softc;
119352c9ce25SScott Long 	char tmpstr[80], tmpstr2[80];
119452c9ce25SScott Long 
119552c9ce25SScott Long 	periph = (struct cam_periph *)context;
1196e3a6d3a4SAlexander Motin 
1197e3a6d3a4SAlexander Motin 	/* periph was held for us when this task was enqueued */
11987338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
1199e3a6d3a4SAlexander Motin 		cam_periph_release(periph);
120052c9ce25SScott Long 		return;
1201e3a6d3a4SAlexander Motin 	}
120252c9ce25SScott Long 
120352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
120452c9ce25SScott Long 	snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d", periph->unit_number);
120552c9ce25SScott Long 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
120652c9ce25SScott Long 
120752c9ce25SScott Long 	sysctl_ctx_init(&softc->sysctl_ctx);
120852c9ce25SScott Long 	softc->flags |= ADA_FLAG_SCTX_INIT;
120952c9ce25SScott Long 	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
121052c9ce25SScott Long 		SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2,
121152c9ce25SScott Long 		CTLFLAG_RD, 0, tmpstr);
121252c9ce25SScott Long 	if (softc->sysctl_tree == NULL) {
121352c9ce25SScott Long 		printf("adasysctlinit: unable to allocate sysctl tree\n");
121452c9ce25SScott Long 		cam_periph_release(periph);
121552c9ce25SScott Long 		return;
121652c9ce25SScott Long 	}
121752c9ce25SScott Long 
1218*a6e0c5daSWarner Losh 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1219*a6e0c5daSWarner Losh 		OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW,
1220*a6e0c5daSWarner Losh 		softc, 0, adadeletemethodsysctl, "A",
1221*a6e0c5daSWarner Losh 		"BIO_DELETE execution method");
1222e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
12231ed6aaf9SAlexander Motin 		OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE,
12241ed6aaf9SAlexander Motin 		&softc->read_ahead, 0, "Enable disk read ahead.");
12251ed6aaf9SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1226e3a6d3a4SAlexander Motin 		OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE,
1227e3a6d3a4SAlexander Motin 		&softc->write_cache, 0, "Enable disk write cache.");
12285f83aee5SSteven Hartland 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1229*a6e0c5daSWarner Losh 		OID_AUTO, "unmapped_io", CTLFLAG_RD | CTLFLAG_MPSAFE,
1230*a6e0c5daSWarner Losh 		&softc->unmappedio, 0, "Unmapped I/O leaf");
1231*a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1232*a6e0c5daSWarner Losh 		OID_AUTO, "rotating", CTLFLAG_RD | CTLFLAG_MPSAFE,
1233*a6e0c5daSWarner Losh 		&softc->rotating, 0, "Rotating media");
1234e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1235e3a6d3a4SAlexander Motin 	/*
1236e3a6d3a4SAlexander Motin 	 * Add a 'door bell' sysctl which allows one to set it from userland
1237e3a6d3a4SAlexander Motin 	 * and cause something bad to happen.  For the moment, we only allow
1238e3a6d3a4SAlexander Motin 	 * whacking the next read or write.
1239e3a6d3a4SAlexander Motin 	 */
1240e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1241e3a6d3a4SAlexander Motin 		OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1242e3a6d3a4SAlexander Motin 		&softc->force_read_error, 0,
1243e3a6d3a4SAlexander Motin 		"Force a read error for the next N reads.");
1244e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1245e3a6d3a4SAlexander Motin 		OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1246e3a6d3a4SAlexander Motin 		&softc->force_write_error, 0,
1247e3a6d3a4SAlexander Motin 		"Force a write error for the next N writes.");
1248e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1249e3a6d3a4SAlexander Motin 		OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1250e3a6d3a4SAlexander Motin 		&softc->periodic_read_error, 0,
1251e3a6d3a4SAlexander Motin 		"Force a read error every N reads (don't set too low).");
1252e3a6d3a4SAlexander Motin #endif
1253*a6e0c5daSWarner Losh 
1254*a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
1255*a6e0c5daSWarner Losh 	softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx,
1256*a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats",
1257*a6e0c5daSWarner Losh 		CTLFLAG_RD, 0, "Statistics");
1258*a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
1259*a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
1260*a6e0c5daSWarner Losh 		OID_AUTO, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE,
1261*a6e0c5daSWarner Losh 		&softc->timeouts, 0,
1262*a6e0c5daSWarner Losh 		"Device timeouts reported by the SIM");
1263*a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
1264*a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
1265*a6e0c5daSWarner Losh 		OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE,
1266*a6e0c5daSWarner Losh 		&softc->errors, 0,
1267*a6e0c5daSWarner Losh 		"Transport errors reported by the SIM.");
1268*a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
1269*a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
1270*a6e0c5daSWarner Losh 		OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE,
1271*a6e0c5daSWarner Losh 		&softc->invalidations, 0,
1272*a6e0c5daSWarner Losh 		"Device pack invalidations.");
1273*a6e0c5daSWarner Losh #endif
1274*a6e0c5daSWarner Losh 
1275*a6e0c5daSWarner Losh 	cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx,
1276*a6e0c5daSWarner Losh 	    softc->sysctl_tree);
1277*a6e0c5daSWarner Losh 
127852c9ce25SScott Long 	cam_periph_release(periph);
127952c9ce25SScott Long }
128052c9ce25SScott Long 
1281416494d7SJustin T. Gibbs static int
1282416494d7SJustin T. Gibbs adagetattr(struct bio *bp)
1283416494d7SJustin T. Gibbs {
12846884b662SAlexander Motin 	int ret;
1285416494d7SJustin T. Gibbs 	struct cam_periph *periph;
1286416494d7SJustin T. Gibbs 
1287416494d7SJustin T. Gibbs 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
12886884b662SAlexander Motin 	cam_periph_lock(periph);
1289416494d7SJustin T. Gibbs 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
1290416494d7SJustin T. Gibbs 	    periph->path);
12916884b662SAlexander Motin 	cam_periph_unlock(periph);
1292416494d7SJustin T. Gibbs 	if (ret == 0)
1293416494d7SJustin T. Gibbs 		bp->bio_completed = bp->bio_length;
1294416494d7SJustin T. Gibbs 	return ret;
1295416494d7SJustin T. Gibbs }
1296416494d7SJustin T. Gibbs 
1297*a6e0c5daSWarner Losh static int
1298*a6e0c5daSWarner Losh adadeletemethodsysctl(SYSCTL_HANDLER_ARGS)
1299*a6e0c5daSWarner Losh {
1300*a6e0c5daSWarner Losh 	char buf[16];
1301*a6e0c5daSWarner Losh 	const char *p;
1302*a6e0c5daSWarner Losh 	struct ada_softc *softc;
1303*a6e0c5daSWarner Losh 	int i, error, value, methods;
1304*a6e0c5daSWarner Losh 
1305*a6e0c5daSWarner Losh 	softc = (struct ada_softc *)arg1;
1306*a6e0c5daSWarner Losh 
1307*a6e0c5daSWarner Losh 	value = softc->delete_method;
1308*a6e0c5daSWarner Losh 	if (value < 0 || value > ADA_DELETE_MAX)
1309*a6e0c5daSWarner Losh 		p = "UNKNOWN";
1310*a6e0c5daSWarner Losh 	else
1311*a6e0c5daSWarner Losh 		p = ada_delete_method_names[value];
1312*a6e0c5daSWarner Losh 	strncpy(buf, p, sizeof(buf));
1313*a6e0c5daSWarner Losh 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
1314*a6e0c5daSWarner Losh 	if (error != 0 || req->newptr == NULL)
1315*a6e0c5daSWarner Losh 		return (error);
1316*a6e0c5daSWarner Losh 	methods = 1 << ADA_DELETE_DISABLE;
1317*a6e0c5daSWarner Losh 	if ((softc->flags & ADA_FLAG_CAN_CFA) &&
1318*a6e0c5daSWarner Losh 	    !(softc->flags & ADA_FLAG_CAN_48BIT))
1319*a6e0c5daSWarner Losh 		methods |= 1 << ADA_DELETE_CFA_ERASE;
1320*a6e0c5daSWarner Losh 	if (softc->flags & ADA_FLAG_CAN_TRIM)
1321*a6e0c5daSWarner Losh 		methods |= 1 << ADA_DELETE_DSM_TRIM;
1322*a6e0c5daSWarner Losh 	if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM)
1323*a6e0c5daSWarner Losh 		methods |= 1 << ADA_DELETE_NCQ_DSM_TRIM;
1324*a6e0c5daSWarner Losh 	for (i = 0; i <= ADA_DELETE_MAX; i++) {
1325*a6e0c5daSWarner Losh 		if (!(methods & (1 << i)) ||
1326*a6e0c5daSWarner Losh 		    strcmp(buf, ada_delete_method_names[i]) != 0)
1327*a6e0c5daSWarner Losh 			continue;
1328*a6e0c5daSWarner Losh 		softc->delete_method = i;
1329*a6e0c5daSWarner Losh 		return (0);
1330*a6e0c5daSWarner Losh 	}
1331*a6e0c5daSWarner Losh 	return (EINVAL);
1332*a6e0c5daSWarner Losh }
1333*a6e0c5daSWarner Losh 
133452c9ce25SScott Long static cam_status
133552c9ce25SScott Long adaregister(struct cam_periph *periph, void *arg)
133652c9ce25SScott Long {
133752c9ce25SScott Long 	struct ada_softc *softc;
133852c9ce25SScott Long 	struct ccb_pathinq cpi;
133952c9ce25SScott Long 	struct ccb_getdev *cgd;
13404a3760baSAlexander Motin 	char   announce_buf[80];
134152c9ce25SScott Long 	struct disk_params *dp;
134252c9ce25SScott Long 	caddr_t match;
134352c9ce25SScott Long 	u_int maxio;
13444a3760baSAlexander Motin 	int quirks;
134552c9ce25SScott Long 
134652c9ce25SScott Long 	cgd = (struct ccb_getdev *)arg;
134752c9ce25SScott Long 	if (cgd == NULL) {
134852c9ce25SScott Long 		printf("adaregister: no getdev CCB, can't register device\n");
134952c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
135052c9ce25SScott Long 	}
135152c9ce25SScott Long 
135252c9ce25SScott Long 	softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF,
135352c9ce25SScott Long 	    M_NOWAIT|M_ZERO);
135452c9ce25SScott Long 
135552c9ce25SScott Long 	if (softc == NULL) {
135652c9ce25SScott Long 		printf("adaregister: Unable to probe new device. "
135752c9ce25SScott Long 		    "Unable to allocate softc\n");
135852c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
135952c9ce25SScott Long 	}
136052c9ce25SScott Long 
1361*a6e0c5daSWarner Losh 	if (cam_iosched_init(&softc->cam_iosched, periph) != 0) {
1362*a6e0c5daSWarner Losh 		printf("adaregister: Unable to probe new device. "
1363*a6e0c5daSWarner Losh 		       "Unable to allocate iosched memory\n");
1364*a6e0c5daSWarner Losh 		return(CAM_REQ_CMP_ERR);
1365*a6e0c5daSWarner Losh 	}
136652c9ce25SScott Long 
1367581b2e78SAlexander Motin 	if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
1368cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA))
136946f118feSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_DMA;
13702e1eb332SMarius Strobl 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
137152c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_48BIT;
13722e1eb332SMarius Strobl 		if (cgd->inq_flags & SID_DMA48)
13732e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_DMA48;
13742e1eb332SMarius Strobl 	}
137552c9ce25SScott Long 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE)
137652c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_FLUSHCACHE;
1377fd104c15SRebecca Cran 	if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT)
1378fd104c15SRebecca Cran 		softc->flags |= ADA_FLAG_CAN_POWERMGT;
1379581b2e78SAlexander Motin 	if ((cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
1380cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA) && (cgd->inq_flags & SID_CmdQue))
138152c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_NCQ;
1382581b2e78SAlexander Motin 	if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
1383581b2e78SAlexander Motin 	    (cgd->inq_flags & SID_DMA)) {
13841c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_TRIM;
13851c80ec0aSAlexander Motin 		softc->trim_max_ranges = TRIM_MAX_RANGES;
13861c80ec0aSAlexander Motin 		if (cgd->ident_data.max_dsm_blocks != 0) {
13871c80ec0aSAlexander Motin 			softc->trim_max_ranges =
1388c213c551SSteven Hartland 			    min(cgd->ident_data.max_dsm_blocks *
1389c213c551SSteven Hartland 				ATA_DSM_BLK_RANGES, softc->trim_max_ranges);
13901c80ec0aSAlexander Motin 		}
13911c80ec0aSAlexander Motin 	}
13921c80ec0aSAlexander Motin 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
13931c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_CFA;
139452c9ce25SScott Long 
1395*a6e0c5daSWarner Losh 	adasetdeletemethod(softc);
1396*a6e0c5daSWarner Losh 
139752c9ce25SScott Long 	periph->softc = softc;
139852c9ce25SScott Long 
139952c9ce25SScott Long 	/*
140052c9ce25SScott Long 	 * See if this device has any quirks.
140152c9ce25SScott Long 	 */
140230a4094fSAlexander Motin 	match = cam_quirkmatch((caddr_t)&cgd->ident_data,
140330a4094fSAlexander Motin 			       (caddr_t)ada_quirk_table,
140430a4094fSAlexander Motin 			       sizeof(ada_quirk_table)/sizeof(*ada_quirk_table),
140530a4094fSAlexander Motin 			       sizeof(*ada_quirk_table), ata_identify_match);
140652c9ce25SScott Long 	if (match != NULL)
140752c9ce25SScott Long 		softc->quirks = ((struct ada_quirk_entry *)match)->quirks;
140852c9ce25SScott Long 	else
140952c9ce25SScott Long 		softc->quirks = ADA_Q_NONE;
141052c9ce25SScott Long 
141152c9ce25SScott Long 	bzero(&cpi, sizeof(cpi));
141283c5d981SAlexander Motin 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
141352c9ce25SScott Long 	cpi.ccb_h.func_code = XPT_PATH_INQ;
141452c9ce25SScott Long 	xpt_action((union ccb *)&cpi);
141552c9ce25SScott Long 
141652c9ce25SScott Long 	TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph);
141752c9ce25SScott Long 
141852c9ce25SScott Long 	/*
141952c9ce25SScott Long 	 * Register this media as a disk
142052c9ce25SScott Long 	 */
1421781338b6SAlexander Motin 	(void)cam_periph_hold(periph, PRIBIO);
1422edec59d9SAlexander Motin 	cam_periph_unlock(periph);
1423d3a460d3SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1424d3a460d3SAlexander Motin 	    "kern.cam.ada.%d.quirks", periph->unit_number);
1425d3a460d3SAlexander Motin 	quirks = softc->quirks;
1426d3a460d3SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &quirks);
1427d3a460d3SAlexander Motin 	softc->quirks = quirks;
14281ed6aaf9SAlexander Motin 	softc->read_ahead = -1;
14291ed6aaf9SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
14301ed6aaf9SAlexander Motin 	    "kern.cam.ada.%d.read_ahead", periph->unit_number);
14311ed6aaf9SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead);
1432781338b6SAlexander Motin 	softc->write_cache = -1;
1433781338b6SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1434781338b6SAlexander Motin 	    "kern.cam.ada.%d.write_cache", periph->unit_number);
1435781338b6SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
143662cc3a63SSteven Hartland 	/* Disable queue sorting for non-rotational media by default. */
1437*a6e0c5daSWarner Losh 	if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) {
1438*a6e0c5daSWarner Losh 		softc->rotating = 0;
1439*a6e0c5daSWarner Losh 	} else {
1440*a6e0c5daSWarner Losh 		softc->rotating = 1;
1441*a6e0c5daSWarner Losh 	}
1442*a6e0c5daSWarner Losh 	cam_iosched_set_sort_queue(softc->cam_iosched,  softc->rotating ? -1 : 0);
1443c1bd46c2SAlexander Motin 	adagetparams(periph, cgd);
144452c9ce25SScott Long 	softc->disk = disk_alloc();
1445c28078e9SSteven Hartland 	softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate;
1446b8b6b5d3SAlexander Motin 	softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
1447b8b6b5d3SAlexander Motin 			  periph->unit_number, softc->params.secsize,
1448b8b6b5d3SAlexander Motin 			  DEVSTAT_ALL_SUPPORTED,
1449b8b6b5d3SAlexander Motin 			  DEVSTAT_TYPE_DIRECT |
1450b8b6b5d3SAlexander Motin 			  XPORT_DEVSTAT_TYPE(cpi.transport),
1451b8b6b5d3SAlexander Motin 			  DEVSTAT_PRIORITY_DISK);
145252c9ce25SScott Long 	softc->disk->d_open = adaopen;
145352c9ce25SScott Long 	softc->disk->d_close = adaclose;
145452c9ce25SScott Long 	softc->disk->d_strategy = adastrategy;
1455416494d7SJustin T. Gibbs 	softc->disk->d_getattr = adagetattr;
145652c9ce25SScott Long 	softc->disk->d_dump = adadump;
14570ba1e4d0SKenneth D. Merry 	softc->disk->d_gone = adadiskgonecb;
145852c9ce25SScott Long 	softc->disk->d_name = "ada";
145952c9ce25SScott Long 	softc->disk->d_drv1 = periph;
146052c9ce25SScott Long 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
146152c9ce25SScott Long 	if (maxio == 0)
146252c9ce25SScott Long 		maxio = DFLTPHYS;	/* traditional default */
146352c9ce25SScott Long 	else if (maxio > MAXPHYS)
146452c9ce25SScott Long 		maxio = MAXPHYS;	/* for safety */
14651c80ec0aSAlexander Motin 	if (softc->flags & ADA_FLAG_CAN_48BIT)
1466c1bd46c2SAlexander Motin 		maxio = min(maxio, 65536 * softc->params.secsize);
146752c9ce25SScott Long 	else					/* 28bit ATA command limit */
1468c1bd46c2SAlexander Motin 		maxio = min(maxio, 256 * softc->params.secsize);
146952c9ce25SScott Long 	softc->disk->d_maxsize = maxio;
147052c9ce25SScott Long 	softc->disk->d_unit = periph->unit_number;
147140ea77a0SAlexander Motin 	softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION;
147252c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
147352c9ce25SScott Long 		softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
1474c213c551SSteven Hartland 	if (softc->flags & ADA_FLAG_CAN_TRIM) {
14751c80ec0aSAlexander Motin 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
14769fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = softc->params.secsize *
14779fe9ba5bSSteven Hartland 					    ATA_DSM_RANGE_MAX *
14789fe9ba5bSSteven Hartland 					    softc->trim_max_ranges;
1479c213c551SSteven Hartland 	} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
1480c213c551SSteven Hartland 	    !(softc->flags & ADA_FLAG_CAN_48BIT)) {
1481c213c551SSteven Hartland 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
14829fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = 256 * softc->params.secsize;
14839fe9ba5bSSteven Hartland 	} else
14849fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = maxio;
1485*a6e0c5daSWarner Losh 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0) {
1486abc1e60eSKonstantin Belousov 		softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
1487*a6e0c5daSWarner Losh 		softc->unmappedio = 1;
1488*a6e0c5daSWarner Losh 	}
1489*a6e0c5daSWarner Losh 	/*
1490*a6e0c5daSWarner Losh 	 * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do
1491*a6e0c5daSWarner Losh 	 * NCQ trims, if we support trims at all. We also need support from
1492*a6e0c5daSWarner Losh 	 * the sim do do things properly. Perhaps we should look at log 13
1493*a6e0c5daSWarner Losh 	 * dword 0 bit 0 and dword 1 bit 0 are set too...
1494*a6e0c5daSWarner Losh 	 */
1495*a6e0c5daSWarner Losh 	if (cpi.hba_misc & PIM_NCQ_KLUDGE)
1496*a6e0c5daSWarner Losh 		softc->flags |= ADA_FLAG_PIM_CAN_NCQ_TRIM;
1497*a6e0c5daSWarner Losh 	if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 &&
1498*a6e0c5daSWarner Losh 	    (softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 &&
1499*a6e0c5daSWarner Losh 	    (cgd->ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 &&
1500*a6e0c5daSWarner Losh 	    (softc->flags & ADA_FLAG_CAN_TRIM) != 0)
1501*a6e0c5daSWarner Losh 		softc->flags |= ADA_FLAG_CAN_NCQ_TRIM;
150265cb6238SNathan Whitehorn 	strlcpy(softc->disk->d_descr, cgd->ident_data.model,
150365cb6238SNathan Whitehorn 	    MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
1504d50aaa6dSAndriy Gapon 	strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
1505d50aaa6dSAndriy Gapon 	    MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial)));
15068edcf694SAlexander Motin 	softc->disk->d_hba_vendor = cpi.hba_vendor;
15078edcf694SAlexander Motin 	softc->disk->d_hba_device = cpi.hba_device;
15088edcf694SAlexander Motin 	softc->disk->d_hba_subvendor = cpi.hba_subvendor;
15098edcf694SAlexander Motin 	softc->disk->d_hba_subdevice = cpi.hba_subdevice;
151052c9ce25SScott Long 
151152c9ce25SScott Long 	softc->disk->d_sectorsize = softc->params.secsize;
1512c1bd46c2SAlexander Motin 	softc->disk->d_mediasize = (off_t)softc->params.sectors *
1513c1bd46c2SAlexander Motin 	    softc->params.secsize;
1514ce8332d4SAlexander Motin 	if (ata_physical_sector_size(&cgd->ident_data) !=
1515ce8332d4SAlexander Motin 	    softc->params.secsize) {
1516ce8332d4SAlexander Motin 		softc->disk->d_stripesize =
1517ce8332d4SAlexander Motin 		    ata_physical_sector_size(&cgd->ident_data);
1518ce8332d4SAlexander Motin 		softc->disk->d_stripeoffset = (softc->disk->d_stripesize -
1519ce8332d4SAlexander Motin 		    ata_logical_sector_offset(&cgd->ident_data)) %
1520ce8332d4SAlexander Motin 		    softc->disk->d_stripesize;
1521d3a460d3SAlexander Motin 	} else if (softc->quirks & ADA_Q_4K) {
1522d3a460d3SAlexander Motin 		softc->disk->d_stripesize = 4096;
1523d3a460d3SAlexander Motin 		softc->disk->d_stripeoffset = 0;
1524ce8332d4SAlexander Motin 	}
152552c9ce25SScott Long 	softc->disk->d_fwsectors = softc->params.secs_per_track;
152652c9ce25SScott Long 	softc->disk->d_fwheads = softc->params.heads;
15274461491bSMarius Strobl 	ata_disk_firmware_geom_adjust(softc->disk);
1528*a6e0c5daSWarner Losh 	adasetdeletemethod(softc);
152952c9ce25SScott Long 
15300ba1e4d0SKenneth D. Merry 	/*
15310ba1e4d0SKenneth D. Merry 	 * Acquire a reference to the periph before we register with GEOM.
15320ba1e4d0SKenneth D. Merry 	 * We'll release this reference once GEOM calls us back (via
15330ba1e4d0SKenneth D. Merry 	 * adadiskgonecb()) telling us that our provider has been freed.
15340ba1e4d0SKenneth D. Merry 	 */
15350ba1e4d0SKenneth D. Merry 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
15360ba1e4d0SKenneth D. Merry 		xpt_print(periph->path, "%s: lost periph during "
15370ba1e4d0SKenneth D. Merry 			  "registration!\n", __func__);
15380ba1e4d0SKenneth D. Merry 		cam_periph_lock(periph);
15390ba1e4d0SKenneth D. Merry 		return (CAM_REQ_CMP_ERR);
15400ba1e4d0SKenneth D. Merry 	}
154152c9ce25SScott Long 	disk_create(softc->disk, DISK_VERSION);
1542edec59d9SAlexander Motin 	cam_periph_lock(periph);
1543781338b6SAlexander Motin 	cam_periph_unhold(periph);
154452c9ce25SScott Long 
154552c9ce25SScott Long 	dp = &softc->params;
154652c9ce25SScott Long 	snprintf(announce_buf, sizeof(announce_buf),
154768546995SAlexander Motin 	    "%juMB (%ju %u byte sectors)",
154868546995SAlexander Motin 	    ((uintmax_t)dp->secsize * dp->sectors) / (1024 * 1024),
154968546995SAlexander Motin 	    (uintmax_t)dp->sectors, dp->secsize);
155052c9ce25SScott Long 	xpt_announce_periph(periph, announce_buf);
15516fb5c84eSSteven Hartland 	xpt_announce_quirks(periph, softc->quirks, ADA_Q_BIT_STRING);
1552e3a6d3a4SAlexander Motin 
1553e3a6d3a4SAlexander Motin 	/*
1554e3a6d3a4SAlexander Motin 	 * Create our sysctl variables, now that we know
1555e3a6d3a4SAlexander Motin 	 * we have successfully attached.
1556e3a6d3a4SAlexander Motin 	 */
1557227d67aaSAlexander Motin 	if (cam_periph_acquire(periph) == CAM_REQ_CMP)
1558e3a6d3a4SAlexander Motin 		taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
1559e3a6d3a4SAlexander Motin 
156052c9ce25SScott Long 	/*
156152c9ce25SScott Long 	 * Add async callbacks for bus reset and
156252c9ce25SScott Long 	 * bus device reset calls.  I don't bother
156352c9ce25SScott Long 	 * checking if this fails as, in most cases,
156452c9ce25SScott Long 	 * the system will function just fine without
156552c9ce25SScott Long 	 * them and the only alternative would be to
156652c9ce25SScott Long 	 * not attach the device on failure.
156752c9ce25SScott Long 	 */
15683089bb2eSAlexander Motin 	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
1569581b2e78SAlexander Motin 	    AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED,
1570581b2e78SAlexander Motin 	    adaasync, periph, periph->path);
157152c9ce25SScott Long 
157252c9ce25SScott Long 	/*
157352c9ce25SScott Long 	 * Schedule a periodic event to occasionally send an
157452c9ce25SScott Long 	 * ordered tag to a device.
157552c9ce25SScott Long 	 */
1576227d67aaSAlexander Motin 	callout_init_mtx(&softc->sendordered_c, cam_periph_mtx(periph), 0);
157752c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
157847bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
157952c9ce25SScott Long 	    adasendorderedtag, softc);
158052c9ce25SScott Long 
15811ed6aaf9SAlexander Motin 	if (ADA_RA >= 0 &&
15821ed6aaf9SAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) {
15831ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_RAHEAD;
15841ed6aaf9SAlexander Motin 	} else if (ADA_WC >= 0 &&
1585f513d14cSAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
1586f513d14cSAlexander Motin 		softc->state = ADA_STATE_WCACHE;
1587227d67aaSAlexander Motin 	} else {
1588f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1589227d67aaSAlexander Motin 		return(CAM_REQ_CMP);
1590227d67aaSAlexander Motin 	}
1591227d67aaSAlexander Motin 	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
1592227d67aaSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1593227d67aaSAlexander Motin 	else
1594227d67aaSAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
159552c9ce25SScott Long 	return(CAM_REQ_CMP);
159652c9ce25SScott Long }
159752c9ce25SScott Long 
1598*a6e0c5daSWarner Losh static int
1599*a6e0c5daSWarner Losh ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req)
160052c9ce25SScott Long {
160137ddbd16SAlexander Motin 	uint64_t lastlba = (uint64_t)-1;
16022030b294SAlexander Motin 	int c, lastcount = 0, off, ranges = 0;
160352c9ce25SScott Long 
16041c80ec0aSAlexander Motin 	bzero(req, sizeof(*req));
16052030b294SAlexander Motin 	TAILQ_INIT(&req->bps);
16061c80ec0aSAlexander Motin 	do {
16077ddad071SWarner Losh 		uint64_t lba = bp->bio_pblkno;
16087ddad071SWarner Losh 		int count = bp->bio_bcount / softc->params.secsize;
16091c80ec0aSAlexander Motin 
161037ddbd16SAlexander Motin 		/* Try to extend the previous range. */
161137ddbd16SAlexander Motin 		if (lba == lastlba) {
1612c213c551SSteven Hartland 			c = min(count, ATA_DSM_RANGE_MAX - lastcount);
161337ddbd16SAlexander Motin 			lastcount += c;
1614c213c551SSteven Hartland 			off = (ranges - 1) * ATA_DSM_RANGE_SIZE;
161537ddbd16SAlexander Motin 			req->data[off + 6] = lastcount & 0xff;
161637ddbd16SAlexander Motin 			req->data[off + 7] =
161737ddbd16SAlexander Motin 				(lastcount >> 8) & 0xff;
161837ddbd16SAlexander Motin 			count -= c;
161937ddbd16SAlexander Motin 			lba += c;
162037ddbd16SAlexander Motin 		}
162137ddbd16SAlexander Motin 
162237ddbd16SAlexander Motin 		while (count > 0) {
1623c213c551SSteven Hartland 			c = min(count, ATA_DSM_RANGE_MAX);
1624c213c551SSteven Hartland 			off = ranges * ATA_DSM_RANGE_SIZE;
16251c80ec0aSAlexander Motin 			req->data[off + 0] = lba & 0xff;
16261c80ec0aSAlexander Motin 			req->data[off + 1] = (lba >> 8) & 0xff;
16271c80ec0aSAlexander Motin 			req->data[off + 2] = (lba >> 16) & 0xff;
16281c80ec0aSAlexander Motin 			req->data[off + 3] = (lba >> 24) & 0xff;
16291c80ec0aSAlexander Motin 			req->data[off + 4] = (lba >> 32) & 0xff;
16301c80ec0aSAlexander Motin 			req->data[off + 5] = (lba >> 40) & 0xff;
16311c80ec0aSAlexander Motin 			req->data[off + 6] = c & 0xff;
16321c80ec0aSAlexander Motin 			req->data[off + 7] = (c >> 8) & 0xff;
16331c80ec0aSAlexander Motin 			lba += c;
16341c80ec0aSAlexander Motin 			count -= c;
163537ddbd16SAlexander Motin 			lastcount = c;
16361c80ec0aSAlexander Motin 			ranges++;
1637c213c551SSteven Hartland 			/*
1638c213c551SSteven Hartland 			 * Its the caller's responsibility to ensure the
1639c213c551SSteven Hartland 			 * request will fit so we don't need to check for
1640c213c551SSteven Hartland 			 * overrun here
1641c213c551SSteven Hartland 			 */
16421c80ec0aSAlexander Motin 		}
164337ddbd16SAlexander Motin 		lastlba = lba;
16447ddad071SWarner Losh 		TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue);
1645*a6e0c5daSWarner Losh 
1646*a6e0c5daSWarner Losh 		bp = cam_iosched_next_trim(softc->cam_iosched);
1647*a6e0c5daSWarner Losh 		if (bp == NULL)
16481c80ec0aSAlexander Motin 			break;
1649*a6e0c5daSWarner Losh 		if (bp->bio_bcount / softc->params.secsize >
1650*a6e0c5daSWarner Losh 		    (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) {
1651*a6e0c5daSWarner Losh 			cam_iosched_put_back_trim(softc->cam_iosched, bp);
1652*a6e0c5daSWarner Losh 			break;
1653*a6e0c5daSWarner Losh 		}
16541c80ec0aSAlexander Motin 	} while (1);
1655*a6e0c5daSWarner Losh 
1656*a6e0c5daSWarner Losh 	return (ranges);
1657*a6e0c5daSWarner Losh }
1658*a6e0c5daSWarner Losh 
1659*a6e0c5daSWarner Losh static void
1660*a6e0c5daSWarner Losh ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
1661*a6e0c5daSWarner Losh {
1662*a6e0c5daSWarner Losh 	struct trim_request *req = &softc->trim_req;
1663*a6e0c5daSWarner Losh 	int ranges;
1664*a6e0c5daSWarner Losh 
1665*a6e0c5daSWarner Losh 	ranges = ada_dsmtrim_req_create(softc, bp, req);
16661c80ec0aSAlexander Motin 	cam_fill_ataio(ataio,
16671c80ec0aSAlexander Motin 	    ada_retry_count,
16681c80ec0aSAlexander Motin 	    adadone,
16691c80ec0aSAlexander Motin 	    CAM_DIR_OUT,
16701c80ec0aSAlexander Motin 	    0,
16711c80ec0aSAlexander Motin 	    req->data,
1672c213c551SSteven Hartland 	    ((ranges + ATA_DSM_BLK_RANGES - 1) /
1673c213c551SSteven Hartland 	    ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
16741c80ec0aSAlexander Motin 	    ada_default_timeout * 1000);
16751c80ec0aSAlexander Motin 	ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT,
1676c213c551SSteven Hartland 	    ATA_DSM_TRIM, 0, (ranges + ATA_DSM_BLK_RANGES -
1677c213c551SSteven Hartland 	    1) / ATA_DSM_BLK_RANGES);
16787ddad071SWarner Losh }
16797ddad071SWarner Losh 
16807ddad071SWarner Losh static void
1681*a6e0c5daSWarner Losh ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
1682*a6e0c5daSWarner Losh {
1683*a6e0c5daSWarner Losh 	struct trim_request *req = &softc->trim_req;
1684*a6e0c5daSWarner Losh 	int ranges;
1685*a6e0c5daSWarner Losh 
1686*a6e0c5daSWarner Losh 	ranges = ada_dsmtrim_req_create(softc, bp, req);
1687*a6e0c5daSWarner Losh 	cam_fill_ataio(ataio,
1688*a6e0c5daSWarner Losh 	    ada_retry_count,
1689*a6e0c5daSWarner Losh 	    adadone,
1690*a6e0c5daSWarner Losh 	    CAM_DIR_OUT,
1691*a6e0c5daSWarner Losh 	    0,
1692*a6e0c5daSWarner Losh 	    req->data,
1693*a6e0c5daSWarner Losh 	    ((ranges + ATA_DSM_BLK_RANGES - 1) /
1694*a6e0c5daSWarner Losh 	    ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
1695*a6e0c5daSWarner Losh 	    ada_default_timeout * 1000);
1696*a6e0c5daSWarner Losh 	ata_ncq_cmd(ataio,
1697*a6e0c5daSWarner Losh 	    ATA_SEND_FPDMA_QUEUED,
1698*a6e0c5daSWarner Losh 	    0,
1699*a6e0c5daSWarner Losh 	    (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES);
1700*a6e0c5daSWarner Losh 	ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM;
1701*a6e0c5daSWarner Losh 	ataio->cmd.flags |= CAM_ATAIO_AUX_HACK;
1702*a6e0c5daSWarner Losh }
1703*a6e0c5daSWarner Losh 
1704*a6e0c5daSWarner Losh static void
17057ddad071SWarner Losh ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
17067ddad071SWarner Losh {
1707467298f5SSteven Hartland 	struct trim_request *req = &softc->trim_req;
17087ddad071SWarner Losh 	uint64_t lba = bp->bio_pblkno;
17097ddad071SWarner Losh 	uint16_t count = bp->bio_bcount / softc->params.secsize;
17107ddad071SWarner Losh 
1711467298f5SSteven Hartland 	bzero(req, sizeof(*req));
1712467298f5SSteven Hartland 	TAILQ_INIT(&req->bps);
1713467298f5SSteven Hartland 	TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue);
1714467298f5SSteven Hartland 
17157ddad071SWarner Losh 	cam_fill_ataio(ataio,
17167ddad071SWarner Losh 	    ada_retry_count,
17177ddad071SWarner Losh 	    adadone,
17187ddad071SWarner Losh 	    CAM_DIR_NONE,
17197ddad071SWarner Losh 	    0,
17207ddad071SWarner Losh 	    NULL,
17217ddad071SWarner Losh 	    0,
17227ddad071SWarner Losh 	    ada_default_timeout*1000);
17237ddad071SWarner Losh 
17247ddad071SWarner Losh 	if (count >= 256)
17257ddad071SWarner Losh 		count = 0;
17267ddad071SWarner Losh 	ata_28bit_cmd(ataio, ATA_CFA_ERASE, 0, lba, count);
17277ddad071SWarner Losh }
17287ddad071SWarner Losh 
17297ddad071SWarner Losh static void
17307ddad071SWarner Losh adastart(struct cam_periph *periph, union ccb *start_ccb)
17317ddad071SWarner Losh {
17327ddad071SWarner Losh 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
17337ddad071SWarner Losh 	struct ccb_ataio *ataio = &start_ccb->ataio;
17347ddad071SWarner Losh 
17357ddad071SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n"));
17367ddad071SWarner Losh 
17377ddad071SWarner Losh 	switch (softc->state) {
17387ddad071SWarner Losh 	case ADA_STATE_NORMAL:
17397ddad071SWarner Losh 	{
17407ddad071SWarner Losh 		struct bio *bp;
17417ddad071SWarner Losh 		u_int8_t tag_code;
17427ddad071SWarner Losh 
1743*a6e0c5daSWarner Losh 		bp = cam_iosched_next_bio(softc->cam_iosched);
17441c80ec0aSAlexander Motin 		if (bp == NULL) {
17451c80ec0aSAlexander Motin 			xpt_release_ccb(start_ccb);
17461c80ec0aSAlexander Motin 			break;
17471c80ec0aSAlexander Motin 		}
174852c9ce25SScott Long 
1749*a6e0c5daSWarner Losh 		if ((bp->bio_flags & BIO_ORDERED) != 0 ||
1750*a6e0c5daSWarner Losh 		    (bp->bio_cmd != BIO_DELETE && (softc->flags & ADA_FLAG_NEED_OTAG) != 0)) {
175152c9ce25SScott Long 			softc->flags &= ~ADA_FLAG_NEED_OTAG;
1752030844d1SAlexander Motin 			softc->flags |= ADA_FLAG_WAS_OTAG;
175346f118feSAlexander Motin 			tag_code = 0;
175452c9ce25SScott Long 		} else {
175546f118feSAlexander Motin 			tag_code = 1;
175652c9ce25SScott Long 		}
175752c9ce25SScott Long 		switch (bp->bio_cmd) {
175852c9ce25SScott Long 		case BIO_WRITE:
175969114bc0SAlexander Motin 		case BIO_READ:
176052c9ce25SScott Long 		{
176152c9ce25SScott Long 			uint64_t lba = bp->bio_pblkno;
176252c9ce25SScott Long 			uint16_t count = bp->bio_bcount / softc->params.secsize;
1763a9934668SKenneth D. Merry 			void *data_ptr;
1764a9934668SKenneth D. Merry 			int rw_op;
1765a9934668SKenneth D. Merry 
1766a9934668SKenneth D. Merry 			if (bp->bio_cmd == BIO_WRITE) {
1767a9934668SKenneth D. Merry 				softc->flags |= ADA_FLAG_DIRTY;
1768a9934668SKenneth D. Merry 				rw_op = CAM_DIR_OUT;
1769a9934668SKenneth D. Merry 			} else {
1770a9934668SKenneth D. Merry 				rw_op = CAM_DIR_IN;
1771a9934668SKenneth D. Merry 			}
1772a9934668SKenneth D. Merry 
1773a9934668SKenneth D. Merry 			data_ptr = bp->bio_data;
1774a9934668SKenneth D. Merry 			if ((bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0) {
1775a9934668SKenneth D. Merry 				rw_op |= CAM_DATA_BIO;
1776a9934668SKenneth D. Merry 				data_ptr = bp;
1777a9934668SKenneth D. Merry 			}
1778a9934668SKenneth D. Merry 
1779e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1780e3a6d3a4SAlexander Motin 			int fail = 0;
178152c9ce25SScott Long 
1782e3a6d3a4SAlexander Motin 			/*
1783e3a6d3a4SAlexander Motin 			 * Support the failure ioctls.  If the command is a
1784e3a6d3a4SAlexander Motin 			 * read, and there are pending forced read errors, or
1785e3a6d3a4SAlexander Motin 			 * if a write and pending write errors, then fail this
1786e3a6d3a4SAlexander Motin 			 * operation with EIO.  This is useful for testing
1787e3a6d3a4SAlexander Motin 			 * purposes.  Also, support having every Nth read fail.
1788e3a6d3a4SAlexander Motin 			 *
1789e3a6d3a4SAlexander Motin 			 * This is a rather blunt tool.
1790e3a6d3a4SAlexander Motin 			 */
1791e3a6d3a4SAlexander Motin 			if (bp->bio_cmd == BIO_READ) {
1792e3a6d3a4SAlexander Motin 				if (softc->force_read_error) {
1793e3a6d3a4SAlexander Motin 					softc->force_read_error--;
1794e3a6d3a4SAlexander Motin 					fail = 1;
1795e3a6d3a4SAlexander Motin 				}
1796e3a6d3a4SAlexander Motin 				if (softc->periodic_read_error > 0) {
1797e3a6d3a4SAlexander Motin 					if (++softc->periodic_read_count >=
1798e3a6d3a4SAlexander Motin 					    softc->periodic_read_error) {
1799e3a6d3a4SAlexander Motin 						softc->periodic_read_count = 0;
1800e3a6d3a4SAlexander Motin 						fail = 1;
1801e3a6d3a4SAlexander Motin 					}
1802e3a6d3a4SAlexander Motin 				}
1803e3a6d3a4SAlexander Motin 			} else {
1804e3a6d3a4SAlexander Motin 				if (softc->force_write_error) {
1805e3a6d3a4SAlexander Motin 					softc->force_write_error--;
1806e3a6d3a4SAlexander Motin 					fail = 1;
1807e3a6d3a4SAlexander Motin 				}
1808e3a6d3a4SAlexander Motin 			}
1809e3a6d3a4SAlexander Motin 			if (fail) {
18104beec135SAlexander Motin 				biofinish(bp, NULL, EIO);
1811e3a6d3a4SAlexander Motin 				xpt_release_ccb(start_ccb);
1812e3a6d3a4SAlexander Motin 				adaschedule(periph);
1813e3a6d3a4SAlexander Motin 				return;
1814e3a6d3a4SAlexander Motin 			}
1815e3a6d3a4SAlexander Motin #endif
1816abc1e60eSKonstantin Belousov 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
1817abc1e60eSKonstantin Belousov 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
1818abc1e60eSKonstantin Belousov 			    PAGE_SIZE == bp->bio_ma_n,
1819abc1e60eSKonstantin Belousov 			    ("Short bio %p", bp));
182052c9ce25SScott Long 			cam_fill_ataio(ataio,
182152c9ce25SScott Long 			    ada_retry_count,
182252c9ce25SScott Long 			    adadone,
1823a9934668SKenneth D. Merry 			    rw_op,
182452c9ce25SScott Long 			    tag_code,
1825a9934668SKenneth D. Merry 			    data_ptr,
182652c9ce25SScott Long 			    bp->bio_bcount,
182752c9ce25SScott Long 			    ada_default_timeout*1000);
182852c9ce25SScott Long 
182946f118feSAlexander Motin 			if ((softc->flags & ADA_FLAG_CAN_NCQ) && tag_code) {
183052c9ce25SScott Long 				if (bp->bio_cmd == BIO_READ) {
183152c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_READ_FPDMA_QUEUED,
183252c9ce25SScott Long 					    lba, count);
183352c9ce25SScott Long 				} else {
183452c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_WRITE_FPDMA_QUEUED,
183552c9ce25SScott Long 					    lba, count);
183652c9ce25SScott Long 				}
183752c9ce25SScott Long 			} else if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
183852c9ce25SScott Long 			    (lba + count >= ATA_MAX_28BIT_LBA ||
183946f118feSAlexander Motin 			    count > 256)) {
18402e1eb332SMarius Strobl 				if (softc->flags & ADA_FLAG_CAN_DMA48) {
184152c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
184252c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_READ_DMA48,
184352c9ce25SScott Long 						    0, lba, count);
184452c9ce25SScott Long 					} else {
184552c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_WRITE_DMA48,
184652c9ce25SScott Long 						    0, lba, count);
184752c9ce25SScott Long 					}
184852c9ce25SScott Long 				} else {
184952c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
185046f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_READ_MUL48,
185146f118feSAlexander Motin 						    0, lba, count);
185246f118feSAlexander Motin 					} else {
185346f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_WRITE_MUL48,
185446f118feSAlexander Motin 						    0, lba, count);
185546f118feSAlexander Motin 					}
185646f118feSAlexander Motin 				}
185746f118feSAlexander Motin 			} else {
185846f118feSAlexander Motin 				if (count == 256)
185946f118feSAlexander Motin 					count = 0;
186046f118feSAlexander Motin 				if (softc->flags & ADA_FLAG_CAN_DMA) {
186146f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
18627606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_DMA,
186352c9ce25SScott Long 						    0, lba, count);
186452c9ce25SScott Long 					} else {
18657606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_DMA,
186652c9ce25SScott Long 						    0, lba, count);
186752c9ce25SScott Long 					}
186846f118feSAlexander Motin 				} else {
186946f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
187046f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_MUL,
187146f118feSAlexander Motin 						    0, lba, count);
187246f118feSAlexander Motin 					} else {
187346f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_MUL,
187446f118feSAlexander Motin 						    0, lba, count);
187546f118feSAlexander Motin 					}
187646f118feSAlexander Motin 				}
187752c9ce25SScott Long 			}
187852c9ce25SScott Long 			break;
18791c80ec0aSAlexander Motin 		}
1880*a6e0c5daSWarner Losh 		case BIO_DELETE:
1881*a6e0c5daSWarner Losh 			switch (softc->delete_method) {
1882*a6e0c5daSWarner Losh 			case ADA_DELETE_NCQ_DSM_TRIM:
1883*a6e0c5daSWarner Losh 				ada_ncq_dsmtrim(softc, bp, ataio);
1884*a6e0c5daSWarner Losh 				break;
1885*a6e0c5daSWarner Losh 			case ADA_DELETE_DSM_TRIM:
1886*a6e0c5daSWarner Losh 				ada_dsmtrim(softc, bp, ataio);
1887*a6e0c5daSWarner Losh 				break;
1888*a6e0c5daSWarner Losh 			case ADA_DELETE_CFA_ERASE:
1889*a6e0c5daSWarner Losh 				ada_cfaerase(softc, bp, ataio);
1890*a6e0c5daSWarner Losh 				break;
1891*a6e0c5daSWarner Losh 			default:
1892*a6e0c5daSWarner Losh 				biofinish(bp, NULL, EOPNOTSUPP);
1893*a6e0c5daSWarner Losh 				xpt_release_ccb(start_ccb);
1894*a6e0c5daSWarner Losh 				adaschedule(periph);
1895*a6e0c5daSWarner Losh 				return;
1896*a6e0c5daSWarner Losh 			}
1897*a6e0c5daSWarner Losh 			start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM;
1898*a6e0c5daSWarner Losh 			start_ccb->ccb_h.flags |= CAM_UNLOCKED;
1899*a6e0c5daSWarner Losh 			cam_iosched_submit_trim(softc->cam_iosched);
1900*a6e0c5daSWarner Losh 			goto out;
190152c9ce25SScott Long 		case BIO_FLUSH:
190252c9ce25SScott Long 			cam_fill_ataio(ataio,
190352c9ce25SScott Long 			    1,
190452c9ce25SScott Long 			    adadone,
190552c9ce25SScott Long 			    CAM_DIR_NONE,
190646f118feSAlexander Motin 			    0,
190752c9ce25SScott Long 			    NULL,
190852c9ce25SScott Long 			    0,
190952c9ce25SScott Long 			    ada_default_timeout*1000);
191052c9ce25SScott Long 
191152c9ce25SScott Long 			if (softc->flags & ADA_FLAG_CAN_48BIT)
191252c9ce25SScott Long 				ata_48bit_cmd(ataio, ATA_FLUSHCACHE48, 0, 0, 0);
191352c9ce25SScott Long 			else
19147606b445SAlexander Motin 				ata_28bit_cmd(ataio, ATA_FLUSHCACHE, 0, 0, 0);
191552c9ce25SScott Long 			break;
191652c9ce25SScott Long 		}
191752c9ce25SScott Long 		start_ccb->ccb_h.ccb_state = ADA_CCB_BUFFER_IO;
1918227d67aaSAlexander Motin 		start_ccb->ccb_h.flags |= CAM_UNLOCKED;
19191c80ec0aSAlexander Motin out:
192052c9ce25SScott Long 		start_ccb->ccb_h.ccb_bp = bp;
1921c1bd46c2SAlexander Motin 		softc->outstanding_cmds++;
1922227d67aaSAlexander Motin 		softc->refcount++;
1923227d67aaSAlexander Motin 		cam_periph_unlock(periph);
192452c9ce25SScott Long 		xpt_action(start_ccb);
1925227d67aaSAlexander Motin 		cam_periph_lock(periph);
1926227d67aaSAlexander Motin 		softc->refcount--;
192752c9ce25SScott Long 
19281c80ec0aSAlexander Motin 		/* May have more work to do, so ensure we stay scheduled */
19291c80ec0aSAlexander Motin 		adaschedule(periph);
193052c9ce25SScott Long 		break;
193152c9ce25SScott Long 	}
19321ed6aaf9SAlexander Motin 	case ADA_STATE_RAHEAD:
1933f513d14cSAlexander Motin 	case ADA_STATE_WCACHE:
1934f513d14cSAlexander Motin 	{
1935f513d14cSAlexander Motin 		cam_fill_ataio(ataio,
1936f513d14cSAlexander Motin 		    1,
1937f513d14cSAlexander Motin 		    adadone,
1938f513d14cSAlexander Motin 		    CAM_DIR_NONE,
1939f513d14cSAlexander Motin 		    0,
1940f513d14cSAlexander Motin 		    NULL,
1941f513d14cSAlexander Motin 		    0,
1942f513d14cSAlexander Motin 		    ada_default_timeout*1000);
1943f513d14cSAlexander Motin 
19441ed6aaf9SAlexander Motin 		if (softc->state == ADA_STATE_RAHEAD) {
19451ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ?
19461ed6aaf9SAlexander Motin 			    ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0);
19471ed6aaf9SAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD;
19481ed6aaf9SAlexander Motin 		} else {
19491ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ?
1950f513d14cSAlexander Motin 			    ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0);
1951f513d14cSAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE;
19521ed6aaf9SAlexander Motin 		}
1953cccf4220SAlexander Motin 		start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
1954f513d14cSAlexander Motin 		xpt_action(start_ccb);
1955f513d14cSAlexander Motin 		break;
1956f513d14cSAlexander Motin 	}
195752c9ce25SScott Long 	}
195852c9ce25SScott Long }
195952c9ce25SScott Long 
196052c9ce25SScott Long static void
196152c9ce25SScott Long adadone(struct cam_periph *periph, union ccb *done_ccb)
196252c9ce25SScott Long {
196352c9ce25SScott Long 	struct ada_softc *softc;
196452c9ce25SScott Long 	struct ccb_ataio *ataio;
19651ed6aaf9SAlexander Motin 	struct ccb_getdev *cgd;
1966cccf4220SAlexander Motin 	struct cam_path *path;
19677651b989SAlexander Motin 	int state;
196852c9ce25SScott Long 
196952c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
197052c9ce25SScott Long 	ataio = &done_ccb->ataio;
1971cccf4220SAlexander Motin 	path = done_ccb->ccb_h.path;
1972fddde2b8SAlexander Motin 
1973cccf4220SAlexander Motin 	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("adadone\n"));
1974fddde2b8SAlexander Motin 
19757651b989SAlexander Motin 	state = ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK;
19767651b989SAlexander Motin 	switch (state) {
197752c9ce25SScott Long 	case ADA_CCB_BUFFER_IO:
19781c80ec0aSAlexander Motin 	case ADA_CCB_TRIM:
197952c9ce25SScott Long 	{
198052c9ce25SScott Long 		struct bio *bp;
198152c9ce25SScott Long 		int error;
198252c9ce25SScott Long 
1983227d67aaSAlexander Motin 		cam_periph_lock(periph);
1984*a6e0c5daSWarner Losh 		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
19857651b989SAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
198646f118feSAlexander Motin 			error = adaerror(done_ccb, 0, 0);
198752c9ce25SScott Long 			if (error == ERESTART) {
198846f118feSAlexander Motin 				/* A retry was scheduled, so just return. */
1989227d67aaSAlexander Motin 				cam_periph_unlock(periph);
199052c9ce25SScott Long 				return;
199152c9ce25SScott Long 			}
199252c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
1993cccf4220SAlexander Motin 				cam_release_devq(path,
199452c9ce25SScott Long 						 /*relsim_flags*/0,
199552c9ce25SScott Long 						 /*reduction*/0,
199652c9ce25SScott Long 						 /*timeout*/0,
199752c9ce25SScott Long 						 /*getcount_only*/0);
1998*a6e0c5daSWarner Losh 			/*
1999*a6e0c5daSWarner Losh 			 * If we get an error on an NCQ DSM TRIM, fall back
2000*a6e0c5daSWarner Losh 			 * to a non-NCQ DSM TRIM forever. Please note that if
2001*a6e0c5daSWarner Losh 			 * CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too.
2002*a6e0c5daSWarner Losh 			 * However, for this one trim, we treat it as advisory
2003*a6e0c5daSWarner Losh 			 * and return success up the stack.
2004*a6e0c5daSWarner Losh 			 */
2005*a6e0c5daSWarner Losh 			if (state == ADA_CCB_TRIM &&
2006*a6e0c5daSWarner Losh 			    error != 0 &&
2007*a6e0c5daSWarner Losh 			    (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) {
2008*a6e0c5daSWarner Losh 				softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM;
2009*a6e0c5daSWarner Losh 				error = 0;
2010*a6e0c5daSWarner Losh 				adasetdeletemethod(softc);
2011*a6e0c5daSWarner Losh 			}
201252c9ce25SScott Long 		} else {
201352c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
201452c9ce25SScott Long 				panic("REQ_CMP with QFRZN");
20157651b989SAlexander Motin 			error = 0;
20167651b989SAlexander Motin 		}
20177651b989SAlexander Motin 		bp->bio_error = error;
20187651b989SAlexander Motin 		if (error != 0) {
20197651b989SAlexander Motin 			bp->bio_resid = bp->bio_bcount;
20207651b989SAlexander Motin 			bp->bio_flags |= BIO_ERROR;
20217651b989SAlexander Motin 		} else {
20227651b989SAlexander Motin 			if (state == ADA_CCB_TRIM)
20237651b989SAlexander Motin 				bp->bio_resid = 0;
20247651b989SAlexander Motin 			else
202552c9ce25SScott Long 				bp->bio_resid = ataio->resid;
20267651b989SAlexander Motin 			if (bp->bio_resid > 0)
202752c9ce25SScott Long 				bp->bio_flags |= BIO_ERROR;
202852c9ce25SScott Long 		}
202952c9ce25SScott Long 		softc->outstanding_cmds--;
203052c9ce25SScott Long 		if (softc->outstanding_cmds == 0)
2031030844d1SAlexander Motin 			softc->flags |= ADA_FLAG_WAS_OTAG;
2032*a6e0c5daSWarner Losh 
2033*a6e0c5daSWarner Losh 		cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb);
2034227d67aaSAlexander Motin 		xpt_release_ccb(done_ccb);
20357651b989SAlexander Motin 		if (state == ADA_CCB_TRIM) {
20362030b294SAlexander Motin 			TAILQ_HEAD(, bio) queue;
20372030b294SAlexander Motin 			struct bio *bp1;
203852c9ce25SScott Long 
20392030b294SAlexander Motin 			TAILQ_INIT(&queue);
20402030b294SAlexander Motin 			TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue);
20410ac66574SWarner Losh 			/*
20420ac66574SWarner Losh 			 * Normally, the xpt_release_ccb() above would make sure
20430ac66574SWarner Losh 			 * that when we have more work to do, that work would
20440ac66574SWarner Losh 			 * get kicked off. However, we specifically keep
20450ac66574SWarner Losh 			 * trim_running set to 0 before the call above to allow
20460ac66574SWarner Losh 			 * other I/O to progress when many BIO_DELETE requests
20470ac66574SWarner Losh 			 * are pushed down. We set trim_running to 0 and call
20480ac66574SWarner Losh 			 * daschedule again so that we don't stall if there are
20490ac66574SWarner Losh 			 * no other I/Os pending apart from BIO_DELETEs.
20500ac66574SWarner Losh 			 */
2051*a6e0c5daSWarner Losh 			cam_iosched_trim_done(softc->cam_iosched);
2052227d67aaSAlexander Motin 			adaschedule(periph);
2053227d67aaSAlexander Motin 			cam_periph_unlock(periph);
20542030b294SAlexander Motin 			while ((bp1 = TAILQ_FIRST(&queue)) != NULL) {
20552030b294SAlexander Motin 				TAILQ_REMOVE(&queue, bp1, bio_queue);
20562030b294SAlexander Motin 				bp1->bio_error = error;
20572030b294SAlexander Motin 				if (error != 0) {
20581c80ec0aSAlexander Motin 					bp1->bio_flags |= BIO_ERROR;
20597651b989SAlexander Motin 					bp1->bio_resid = bp1->bio_bcount;
20607651b989SAlexander Motin 				} else
20617651b989SAlexander Motin 					bp1->bio_resid = 0;
20621c80ec0aSAlexander Motin 				biodone(bp1);
20631c80ec0aSAlexander Motin 			}
2064227d67aaSAlexander Motin 		} else {
2065*a6e0c5daSWarner Losh 			adaschedule(periph);
2066227d67aaSAlexander Motin 			cam_periph_unlock(periph);
206752c9ce25SScott Long 			biodone(bp);
2068227d67aaSAlexander Motin 		}
2069227d67aaSAlexander Motin 		return;
207052c9ce25SScott Long 	}
20711ed6aaf9SAlexander Motin 	case ADA_CCB_RAHEAD:
20721ed6aaf9SAlexander Motin 	{
20731ed6aaf9SAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
20741ed6aaf9SAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
2075cccf4220SAlexander Motin out:
2076cccf4220SAlexander Motin 				/* Drop freeze taken due to CAM_DEV_QFREEZE */
2077cccf4220SAlexander Motin 				cam_release_devq(path, 0, 0, 0, FALSE);
20781ed6aaf9SAlexander Motin 				return;
20791ed6aaf9SAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
2080cccf4220SAlexander Motin 				cam_release_devq(path,
20811ed6aaf9SAlexander Motin 				    /*relsim_flags*/0,
20821ed6aaf9SAlexander Motin 				    /*reduction*/0,
20831ed6aaf9SAlexander Motin 				    /*timeout*/0,
20841ed6aaf9SAlexander Motin 				    /*getcount_only*/0);
20851ed6aaf9SAlexander Motin 			}
20861ed6aaf9SAlexander Motin 		}
20871ed6aaf9SAlexander Motin 
20881ed6aaf9SAlexander Motin 		/*
20891ed6aaf9SAlexander Motin 		 * Since our peripheral may be invalidated by an error
20901ed6aaf9SAlexander Motin 		 * above or an external event, we must release our CCB
20911ed6aaf9SAlexander Motin 		 * before releasing the reference on the peripheral.
20921ed6aaf9SAlexander Motin 		 * The peripheral will only go away once the last reference
20931ed6aaf9SAlexander Motin 		 * is removed, and we need it around for the CCB release
20941ed6aaf9SAlexander Motin 		 * operation.
20951ed6aaf9SAlexander Motin 		 */
20961ed6aaf9SAlexander Motin 		cgd = (struct ccb_getdev *)done_ccb;
2097cccf4220SAlexander Motin 		xpt_setup_ccb(&cgd->ccb_h, path, CAM_PRIORITY_NORMAL);
20981ed6aaf9SAlexander Motin 		cgd->ccb_h.func_code = XPT_GDEV_TYPE;
20991ed6aaf9SAlexander Motin 		xpt_action((union ccb *)cgd);
21001ed6aaf9SAlexander Motin 		if (ADA_WC >= 0 &&
21011ed6aaf9SAlexander Motin 		    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
21021ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_WCACHE;
21031ed6aaf9SAlexander Motin 			xpt_release_ccb(done_ccb);
21041ed6aaf9SAlexander Motin 			xpt_schedule(periph, CAM_PRIORITY_DEV);
2105cccf4220SAlexander Motin 			goto out;
21061ed6aaf9SAlexander Motin 		}
21071ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_NORMAL;
21081ed6aaf9SAlexander Motin 		xpt_release_ccb(done_ccb);
2109cccf4220SAlexander Motin 		/* Drop freeze taken due to CAM_DEV_QFREEZE */
2110cccf4220SAlexander Motin 		cam_release_devq(path, 0, 0, 0, FALSE);
21111ed6aaf9SAlexander Motin 		adaschedule(periph);
21121ed6aaf9SAlexander Motin 		cam_periph_release_locked(periph);
21131ed6aaf9SAlexander Motin 		return;
21141ed6aaf9SAlexander Motin 	}
2115f513d14cSAlexander Motin 	case ADA_CCB_WCACHE:
2116f513d14cSAlexander Motin 	{
2117f513d14cSAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
2118f513d14cSAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
2119cccf4220SAlexander Motin 				goto out;
2120f513d14cSAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
2121cccf4220SAlexander Motin 				cam_release_devq(path,
2122f513d14cSAlexander Motin 				    /*relsim_flags*/0,
2123f513d14cSAlexander Motin 				    /*reduction*/0,
2124f513d14cSAlexander Motin 				    /*timeout*/0,
2125f513d14cSAlexander Motin 				    /*getcount_only*/0);
2126f513d14cSAlexander Motin 			}
2127f513d14cSAlexander Motin 		}
2128f513d14cSAlexander Motin 
2129f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
2130f513d14cSAlexander Motin 		/*
2131f513d14cSAlexander Motin 		 * Since our peripheral may be invalidated by an error
2132f513d14cSAlexander Motin 		 * above or an external event, we must release our CCB
2133f513d14cSAlexander Motin 		 * before releasing the reference on the peripheral.
2134f513d14cSAlexander Motin 		 * The peripheral will only go away once the last reference
2135f513d14cSAlexander Motin 		 * is removed, and we need it around for the CCB release
2136f513d14cSAlexander Motin 		 * operation.
2137f513d14cSAlexander Motin 		 */
2138f513d14cSAlexander Motin 		xpt_release_ccb(done_ccb);
2139cccf4220SAlexander Motin 		/* Drop freeze taken due to CAM_DEV_QFREEZE */
2140cccf4220SAlexander Motin 		cam_release_devq(path, 0, 0, 0, FALSE);
2141f513d14cSAlexander Motin 		adaschedule(periph);
2142f513d14cSAlexander Motin 		cam_periph_release_locked(periph);
2143f513d14cSAlexander Motin 		return;
2144f513d14cSAlexander Motin 	}
214552c9ce25SScott Long 	case ADA_CCB_DUMP:
214652c9ce25SScott Long 		/* No-op.  We're polling */
214752c9ce25SScott Long 		return;
214852c9ce25SScott Long 	default:
214952c9ce25SScott Long 		break;
215052c9ce25SScott Long 	}
215152c9ce25SScott Long 	xpt_release_ccb(done_ccb);
215252c9ce25SScott Long }
215352c9ce25SScott Long 
215452c9ce25SScott Long static int
215552c9ce25SScott Long adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
215652c9ce25SScott Long {
2157*a6e0c5daSWarner Losh 	struct ada_softc *softc;
2158*a6e0c5daSWarner Losh 	struct cam_periph *periph;
2159*a6e0c5daSWarner Losh 
2160*a6e0c5daSWarner Losh 	periph = xpt_path_periph(ccb->ccb_h.path);
2161*a6e0c5daSWarner Losh 	softc = (struct ada_softc *)periph->softc;
2162*a6e0c5daSWarner Losh 
2163*a6e0c5daSWarner Losh 	switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
2164*a6e0c5daSWarner Losh 	case CAM_CMD_TIMEOUT:
2165*a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
2166*a6e0c5daSWarner Losh 		softc->timeouts++;
2167*a6e0c5daSWarner Losh #endif
2168*a6e0c5daSWarner Losh 		break;
2169*a6e0c5daSWarner Losh 	case CAM_REQ_ABORTED:
2170*a6e0c5daSWarner Losh 	case CAM_REQ_CMP_ERR:
2171*a6e0c5daSWarner Losh 	case CAM_REQ_TERMIO:
2172*a6e0c5daSWarner Losh 	case CAM_UNREC_HBA_ERROR:
2173*a6e0c5daSWarner Losh 	case CAM_DATA_RUN_ERR:
2174*a6e0c5daSWarner Losh 	case CAM_ATA_STATUS_ERROR:
2175*a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
2176*a6e0c5daSWarner Losh 		softc->errors++;
2177*a6e0c5daSWarner Losh #endif
2178*a6e0c5daSWarner Losh 		break;
2179*a6e0c5daSWarner Losh 	default:
2180*a6e0c5daSWarner Losh 		break;
2181*a6e0c5daSWarner Losh 	}
218252c9ce25SScott Long 
2183a9b8edb1SAlexander Motin 	return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
218452c9ce25SScott Long }
218552c9ce25SScott Long 
218652c9ce25SScott Long static void
2187c1bd46c2SAlexander Motin adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd)
218852c9ce25SScott Long {
218952c9ce25SScott Long 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
219052c9ce25SScott Long 	struct disk_params *dp = &softc->params;
219152c9ce25SScott Long 	u_int64_t lbasize48;
219252c9ce25SScott Long 	u_int32_t lbasize;
219352c9ce25SScott Long 
2194c1bd46c2SAlexander Motin 	dp->secsize = ata_logical_sector_size(&cgd->ident_data);
219552c9ce25SScott Long 	if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) &&
219652c9ce25SScott Long 		cgd->ident_data.current_heads && cgd->ident_data.current_sectors) {
219752c9ce25SScott Long 		dp->heads = cgd->ident_data.current_heads;
219852c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.current_sectors;
219952c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
220052c9ce25SScott Long 		dp->sectors = (u_int32_t)cgd->ident_data.current_size_1 |
220152c9ce25SScott Long 			  ((u_int32_t)cgd->ident_data.current_size_2 << 16);
220252c9ce25SScott Long 	} else {
220352c9ce25SScott Long 		dp->heads = cgd->ident_data.heads;
220452c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.sectors;
220552c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
220652c9ce25SScott Long 		dp->sectors = cgd->ident_data.cylinders * dp->heads * dp->secs_per_track;
220752c9ce25SScott Long 	}
220852c9ce25SScott Long 	lbasize = (u_int32_t)cgd->ident_data.lba_size_1 |
220952c9ce25SScott Long 		  ((u_int32_t)cgd->ident_data.lba_size_2 << 16);
221052c9ce25SScott Long 
221152c9ce25SScott Long 	/* use the 28bit LBA size if valid or bigger than the CHS mapping */
221252c9ce25SScott Long 	if (cgd->ident_data.cylinders == 16383 || dp->sectors < lbasize)
221352c9ce25SScott Long 		dp->sectors = lbasize;
221452c9ce25SScott Long 
221552c9ce25SScott Long 	/* use the 48bit LBA size if valid */
221652c9ce25SScott Long 	lbasize48 = ((u_int64_t)cgd->ident_data.lba_size48_1) |
221752c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_2 << 16) |
221852c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_3 << 32) |
221952c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_4 << 48);
222052c9ce25SScott Long 	if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) &&
222152c9ce25SScott Long 	    lbasize48 > ATA_MAX_28BIT_LBA)
222252c9ce25SScott Long 		dp->sectors = lbasize48;
222352c9ce25SScott Long }
222452c9ce25SScott Long 
222552c9ce25SScott Long static void
222652c9ce25SScott Long adasendorderedtag(void *arg)
222752c9ce25SScott Long {
222852c9ce25SScott Long 	struct ada_softc *softc = arg;
222952c9ce25SScott Long 
223052c9ce25SScott Long 	if (ada_send_ordered) {
2231030844d1SAlexander Motin 		if (softc->outstanding_cmds > 0) {
2232030844d1SAlexander Motin 			if ((softc->flags & ADA_FLAG_WAS_OTAG) == 0)
223352c9ce25SScott Long 				softc->flags |= ADA_FLAG_NEED_OTAG;
2234030844d1SAlexander Motin 			softc->flags &= ~ADA_FLAG_WAS_OTAG;
223552c9ce25SScott Long 		}
223652c9ce25SScott Long 	}
223752c9ce25SScott Long 	/* Queue us up again */
223852c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
223947bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
224052c9ce25SScott Long 	    adasendorderedtag, softc);
224152c9ce25SScott Long }
224252c9ce25SScott Long 
224352c9ce25SScott Long /*
224452c9ce25SScott Long  * Step through all ADA peripheral drivers, and if the device is still open,
224552c9ce25SScott Long  * sync the disk cache to physical media.
224652c9ce25SScott Long  */
224752c9ce25SScott Long static void
2248c3d0d168SAlexander Motin adaflush(void)
224952c9ce25SScott Long {
225052c9ce25SScott Long 	struct cam_periph *periph;
225152c9ce25SScott Long 	struct ada_softc *softc;
225209cfadbeSAlexander Motin 	union ccb *ccb;
22530191d9b3SAlexander Motin 	int error;
225452c9ce25SScott Long 
2255f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
225652c9ce25SScott Long 		softc = (struct ada_softc *)periph->softc;
22572f87dfb0SAlexander Motin 		if (SCHEDULER_STOPPED()) {
22582f87dfb0SAlexander Motin 			/* If we paniced with the lock held, do not recurse. */
22592f87dfb0SAlexander Motin 			if (!cam_periph_owned(periph) &&
22602f87dfb0SAlexander Motin 			    (softc->flags & ADA_FLAG_OPEN)) {
22612f87dfb0SAlexander Motin 				adadump(softc->disk, NULL, 0, 0, 0);
22622f87dfb0SAlexander Motin 			}
22632f87dfb0SAlexander Motin 			continue;
22642f87dfb0SAlexander Motin 		}
22652f87dfb0SAlexander Motin 		cam_periph_lock(periph);
226652c9ce25SScott Long 		/*
226752c9ce25SScott Long 		 * We only sync the cache if the drive is still open, and
226852c9ce25SScott Long 		 * if the drive is capable of it..
226952c9ce25SScott Long 		 */
227052c9ce25SScott Long 		if (((softc->flags & ADA_FLAG_OPEN) == 0) ||
227152c9ce25SScott Long 		    (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) == 0) {
227252c9ce25SScott Long 			cam_periph_unlock(periph);
227352c9ce25SScott Long 			continue;
227452c9ce25SScott Long 		}
227552c9ce25SScott Long 
227609cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
227709cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
22780191d9b3SAlexander Motin 				    0,
227952c9ce25SScott Long 				    adadone,
228052c9ce25SScott Long 				    CAM_DIR_NONE,
228152c9ce25SScott Long 				    0,
228252c9ce25SScott Long 				    NULL,
228352c9ce25SScott Long 				    0,
228452c9ce25SScott Long 				    ada_default_timeout*1000);
228552c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
228609cfadbeSAlexander Motin 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
228752c9ce25SScott Long 		else
228809cfadbeSAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
228952c9ce25SScott Long 
229009cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
229109cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
229209cfadbeSAlexander Motin 		    softc->disk->d_devstat);
22930191d9b3SAlexander Motin 		if (error != 0)
22940191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
2295d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
229652c9ce25SScott Long 		cam_periph_unlock(periph);
229752c9ce25SScott Long 	}
2298c3d0d168SAlexander Motin }
2299fd104c15SRebecca Cran 
2300c3d0d168SAlexander Motin static void
2301c3d0d168SAlexander Motin adaspindown(uint8_t cmd, int flags)
2302c3d0d168SAlexander Motin {
2303c3d0d168SAlexander Motin 	struct cam_periph *periph;
2304c3d0d168SAlexander Motin 	struct ada_softc *softc;
230509cfadbeSAlexander Motin 	union ccb *ccb;
23060191d9b3SAlexander Motin 	int error;
2307fd104c15SRebecca Cran 
2308f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
2309fd104c15SRebecca Cran 		/* If we paniced with lock held - not recurse here. */
2310fd104c15SRebecca Cran 		if (cam_periph_owned(periph))
2311fd104c15SRebecca Cran 			continue;
2312fd104c15SRebecca Cran 		cam_periph_lock(periph);
2313fd104c15SRebecca Cran 		softc = (struct ada_softc *)periph->softc;
2314fd104c15SRebecca Cran 		/*
2315fd104c15SRebecca Cran 		 * We only spin-down the drive if it is capable of it..
2316fd104c15SRebecca Cran 		 */
2317fd104c15SRebecca Cran 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
2318fd104c15SRebecca Cran 			cam_periph_unlock(periph);
2319fd104c15SRebecca Cran 			continue;
2320fd104c15SRebecca Cran 		}
2321fd104c15SRebecca Cran 
2322fd104c15SRebecca Cran 		if (bootverbose)
2323fd104c15SRebecca Cran 			xpt_print(periph->path, "spin-down\n");
2324fd104c15SRebecca Cran 
232509cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
232609cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
23270191d9b3SAlexander Motin 				    0,
2328fd104c15SRebecca Cran 				    adadone,
2329c3d0d168SAlexander Motin 				    CAM_DIR_NONE | flags,
2330fd104c15SRebecca Cran 				    0,
2331fd104c15SRebecca Cran 				    NULL,
2332fd104c15SRebecca Cran 				    0,
2333fd104c15SRebecca Cran 				    ada_default_timeout*1000);
233409cfadbeSAlexander Motin 		ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, 0);
2335fd104c15SRebecca Cran 
233609cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
233709cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
233809cfadbeSAlexander Motin 		    softc->disk->d_devstat);
23390191d9b3SAlexander Motin 		if (error != 0)
23400191d9b3SAlexander Motin 			xpt_print(periph->path, "Spin-down disk failed\n");
2341d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
2342fd104c15SRebecca Cran 		cam_periph_unlock(periph);
2343fd104c15SRebecca Cran 	}
234452c9ce25SScott Long }
234552c9ce25SScott Long 
2346c3d0d168SAlexander Motin static void
2347c3d0d168SAlexander Motin adashutdown(void *arg, int howto)
2348c3d0d168SAlexander Motin {
2349c3d0d168SAlexander Motin 
2350c3d0d168SAlexander Motin 	adaflush();
2351c3d0d168SAlexander Motin 	if (ada_spindown_shutdown != 0 &&
2352c3d0d168SAlexander Motin 	    (howto & (RB_HALT | RB_POWEROFF)) != 0)
2353c3d0d168SAlexander Motin 		adaspindown(ATA_STANDBY_IMMEDIATE, 0);
2354c3d0d168SAlexander Motin }
2355c3d0d168SAlexander Motin 
2356c3d0d168SAlexander Motin static void
2357c3d0d168SAlexander Motin adasuspend(void *arg)
2358c3d0d168SAlexander Motin {
2359c3d0d168SAlexander Motin 
2360c3d0d168SAlexander Motin 	adaflush();
2361c3d0d168SAlexander Motin 	if (ada_spindown_suspend != 0)
2362c3d0d168SAlexander Motin 		adaspindown(ATA_SLEEP, CAM_DEV_QFREEZE);
2363c3d0d168SAlexander Motin }
2364c3d0d168SAlexander Motin 
2365c3d0d168SAlexander Motin static void
2366c3d0d168SAlexander Motin adaresume(void *arg)
2367c3d0d168SAlexander Motin {
2368c3d0d168SAlexander Motin 	struct cam_periph *periph;
2369c3d0d168SAlexander Motin 	struct ada_softc *softc;
2370c3d0d168SAlexander Motin 
2371c3d0d168SAlexander Motin 	if (ada_spindown_suspend == 0)
2372c3d0d168SAlexander Motin 		return;
2373c3d0d168SAlexander Motin 
2374f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
2375c3d0d168SAlexander Motin 		cam_periph_lock(periph);
2376c3d0d168SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
2377c3d0d168SAlexander Motin 		/*
2378c3d0d168SAlexander Motin 		 * We only spin-down the drive if it is capable of it..
2379c3d0d168SAlexander Motin 		 */
2380c3d0d168SAlexander Motin 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
2381c3d0d168SAlexander Motin 			cam_periph_unlock(periph);
2382c3d0d168SAlexander Motin 			continue;
2383c3d0d168SAlexander Motin 		}
2384c3d0d168SAlexander Motin 
2385c3d0d168SAlexander Motin 		if (bootverbose)
2386c3d0d168SAlexander Motin 			xpt_print(periph->path, "resume\n");
2387c3d0d168SAlexander Motin 
2388c3d0d168SAlexander Motin 		/*
2389c3d0d168SAlexander Motin 		 * Drop freeze taken due to CAM_DEV_QFREEZE flag set on
2390c3d0d168SAlexander Motin 		 * sleep request.
2391c3d0d168SAlexander Motin 		 */
2392c3d0d168SAlexander Motin 		cam_release_devq(periph->path,
2393c3d0d168SAlexander Motin 			 /*relsim_flags*/0,
2394c3d0d168SAlexander Motin 			 /*openings*/0,
2395c3d0d168SAlexander Motin 			 /*timeout*/0,
2396c3d0d168SAlexander Motin 			 /*getcount_only*/0);
2397c3d0d168SAlexander Motin 
2398c3d0d168SAlexander Motin 		cam_periph_unlock(periph);
2399c3d0d168SAlexander Motin 	}
2400c3d0d168SAlexander Motin }
2401c3d0d168SAlexander Motin 
240252c9ce25SScott Long #endif /* _KERNEL */
2403