xref: /freebsd/sys/cam/ata/ata_da.c (revision 2e1eb3321731aff4edf8d43bd61b5cb15eac6b6a)
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"
310d307e09SAlexander Motin #include "opt_ata.h"
32e3a6d3a4SAlexander Motin 
3352c9ce25SScott Long #include <sys/param.h>
3452c9ce25SScott Long 
3552c9ce25SScott Long #ifdef _KERNEL
3652c9ce25SScott Long #include <sys/systm.h>
3752c9ce25SScott Long #include <sys/kernel.h>
3852c9ce25SScott Long #include <sys/bio.h>
3952c9ce25SScott Long #include <sys/sysctl.h>
4052c9ce25SScott Long #include <sys/taskqueue.h>
4152c9ce25SScott Long #include <sys/lock.h>
4252c9ce25SScott Long #include <sys/mutex.h>
4352c9ce25SScott Long #include <sys/conf.h>
4452c9ce25SScott Long #include <sys/devicestat.h>
4552c9ce25SScott Long #include <sys/eventhandler.h>
4652c9ce25SScott Long #include <sys/malloc.h>
4752c9ce25SScott Long #include <sys/cons.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>
6252c9ce25SScott Long 
6352c9ce25SScott Long #include <cam/ata/ata_all.h>
6452c9ce25SScott Long 
654461491bSMarius Strobl #include <machine/md_var.h>	/* geometry translation */
664461491bSMarius Strobl 
6752c9ce25SScott Long #ifdef _KERNEL
6852c9ce25SScott Long 
6952c9ce25SScott Long #define ATA_MAX_28BIT_LBA               268435455UL
7052c9ce25SScott Long 
7152c9ce25SScott Long typedef enum {
721ed6aaf9SAlexander Motin 	ADA_STATE_RAHEAD,
73f513d14cSAlexander Motin 	ADA_STATE_WCACHE,
741e637ba6SAlexander Motin 	ADA_STATE_NORMAL
7552c9ce25SScott Long } ada_state;
7652c9ce25SScott Long 
7752c9ce25SScott Long typedef enum {
78*2e1eb332SMarius Strobl 	ADA_FLAG_PACK_INVALID	= 0x0001,
79*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_48BIT	= 0x0002,
80*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_FLUSHCACHE	= 0x0004,
81*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_NCQ	= 0x0008,
82*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_DMA	= 0x0010,
83*2e1eb332SMarius Strobl 	ADA_FLAG_NEED_OTAG	= 0x0020,
84*2e1eb332SMarius Strobl 	ADA_FLAG_WENT_IDLE	= 0x0040,
85*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_TRIM	= 0x0080,
86*2e1eb332SMarius Strobl 	ADA_FLAG_OPEN		= 0x0100,
87*2e1eb332SMarius Strobl 	ADA_FLAG_SCTX_INIT	= 0x0200,
88*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_CFA        = 0x0400,
89*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_POWERMGT   = 0x0800,
90*2e1eb332SMarius Strobl 	ADA_FLAG_CAN_DMA48	= 0x1000
9152c9ce25SScott Long } ada_flags;
9252c9ce25SScott Long 
9352c9ce25SScott Long typedef enum {
94d3a460d3SAlexander Motin 	ADA_Q_NONE		= 0x00,
95d3a460d3SAlexander Motin 	ADA_Q_4K		= 0x01,
9652c9ce25SScott Long } ada_quirks;
9752c9ce25SScott Long 
9852c9ce25SScott Long typedef enum {
991ed6aaf9SAlexander Motin 	ADA_CCB_RAHEAD		= 0x01,
1001ed6aaf9SAlexander Motin 	ADA_CCB_WCACHE		= 0x02,
10152c9ce25SScott Long 	ADA_CCB_BUFFER_IO	= 0x03,
10252c9ce25SScott Long 	ADA_CCB_WAITING		= 0x04,
10352c9ce25SScott Long 	ADA_CCB_DUMP		= 0x05,
1041c80ec0aSAlexander Motin 	ADA_CCB_TRIM		= 0x06,
10552c9ce25SScott Long 	ADA_CCB_TYPE_MASK	= 0x0F,
10652c9ce25SScott Long } ada_ccb_state;
10752c9ce25SScott Long 
10852c9ce25SScott Long /* Offsets into our private area for storing information */
10952c9ce25SScott Long #define ccb_state	ppriv_field0
11052c9ce25SScott Long #define ccb_bp		ppriv_ptr1
11152c9ce25SScott Long 
11252c9ce25SScott Long struct disk_params {
11352c9ce25SScott Long 	u_int8_t  heads;
11452c9ce25SScott Long 	u_int8_t  secs_per_track;
115c1bd46c2SAlexander Motin 	u_int32_t cylinders;
116c1bd46c2SAlexander Motin 	u_int32_t secsize;	/* Number of bytes/logical sector */
117c1bd46c2SAlexander Motin 	u_int64_t sectors;	/* Total number sectors */
11852c9ce25SScott Long };
11952c9ce25SScott Long 
1201524677aSAlexander Motin #define TRIM_MAX_BLOCKS	8
12137ddbd16SAlexander Motin #define TRIM_MAX_RANGES	(TRIM_MAX_BLOCKS * 64)
1221524677aSAlexander Motin #define TRIM_MAX_BIOS	(TRIM_MAX_RANGES * 4)
1231c80ec0aSAlexander Motin struct trim_request {
1241c80ec0aSAlexander Motin 	uint8_t		data[TRIM_MAX_RANGES * 8];
12537ddbd16SAlexander Motin 	struct bio	*bps[TRIM_MAX_BIOS];
1261c80ec0aSAlexander Motin };
1271c80ec0aSAlexander Motin 
12852c9ce25SScott Long struct ada_softc {
12952c9ce25SScott Long 	struct	 bio_queue_head bio_queue;
1301c80ec0aSAlexander Motin 	struct	 bio_queue_head trim_queue;
13152c9ce25SScott Long 	ada_state state;
13252c9ce25SScott Long 	ada_flags flags;
13352c9ce25SScott Long 	ada_quirks quirks;
1345f83aee5SSteven Hartland 	int	 sort_io_queue;
13552c9ce25SScott Long 	int	 ordered_tag_count;
13652c9ce25SScott Long 	int	 outstanding_cmds;
1371c80ec0aSAlexander Motin 	int	 trim_max_ranges;
1381c80ec0aSAlexander Motin 	int	 trim_running;
1391ed6aaf9SAlexander Motin 	int	 read_ahead;
140e3a6d3a4SAlexander Motin 	int	 write_cache;
141e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
142e3a6d3a4SAlexander Motin 	int      force_read_error;
143e3a6d3a4SAlexander Motin 	int      force_write_error;
144e3a6d3a4SAlexander Motin 	int      periodic_read_error;
145e3a6d3a4SAlexander Motin 	int      periodic_read_count;
146e3a6d3a4SAlexander Motin #endif
14752c9ce25SScott Long 	struct	 disk_params params;
14852c9ce25SScott Long 	struct	 disk *disk;
14952c9ce25SScott Long 	struct task		sysctl_task;
15052c9ce25SScott Long 	struct sysctl_ctx_list	sysctl_ctx;
15152c9ce25SScott Long 	struct sysctl_oid	*sysctl_tree;
15252c9ce25SScott Long 	struct callout		sendordered_c;
1531c80ec0aSAlexander Motin 	struct trim_request	trim_req;
15452c9ce25SScott Long };
15552c9ce25SScott Long 
15652c9ce25SScott Long struct ada_quirk_entry {
15752c9ce25SScott Long 	struct scsi_inquiry_pattern inq_pat;
15852c9ce25SScott Long 	ada_quirks quirks;
15952c9ce25SScott Long };
16052c9ce25SScott Long 
16130a4094fSAlexander Motin static struct ada_quirk_entry ada_quirk_table[] =
16230a4094fSAlexander Motin {
16330a4094fSAlexander Motin 	{
164d3a460d3SAlexander Motin 		/* Hitachi Advanced Format (4k) drives */
165d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Hitachi H??????????E3*", "*" },
166d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
167d3a460d3SAlexander Motin 	},
168d3a460d3SAlexander Motin 	{
169d3a460d3SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
170643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD155UI*", "*" },
171643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
172643d1826SAlexander Motin 	},
173643d1826SAlexander Motin 	{
174643d1826SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
175d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD204UI*", "*" },
176d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
177d3a460d3SAlexander Motin 	},
178d3a460d3SAlexander Motin 	{
179d3a460d3SAlexander Motin 		/* Seagate Barracuda Green Advanced Format (4k) drives */
180d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DL*", "*" },
181d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
182d3a460d3SAlexander Motin 	},
183d3a460d3SAlexander Motin 	{
184643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
185643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???DM*", "*" },
186643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
187643d1826SAlexander Motin 	},
188643d1826SAlexander Motin 	{
189643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
190643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DM*", "*" },
191643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
192643d1826SAlexander Motin 	},
193643d1826SAlexander Motin 	{
194d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
195d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500423AS*", "*" },
196d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
197d3a460d3SAlexander Motin 	},
198d3a460d3SAlexander Motin 	{
199d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
200d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500424AS*", "*" },
201d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
202d3a460d3SAlexander Motin 	},
203d3a460d3SAlexander Motin 	{
204d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
205643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640423AS*", "*" },
206643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
207643d1826SAlexander Motin 	},
208643d1826SAlexander Motin 	{
209643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
210643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640424AS*", "*" },
211643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
212643d1826SAlexander Motin 	},
213643d1826SAlexander Motin 	{
214643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
215d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750420AS*", "*" },
216d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
217d3a460d3SAlexander Motin 	},
218d3a460d3SAlexander Motin 	{
219d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
220d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750422AS*", "*" },
221d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
222d3a460d3SAlexander Motin 	},
223d3a460d3SAlexander Motin 	{
224643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
225643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750423AS*", "*" },
226643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
227643d1826SAlexander Motin 	},
228643d1826SAlexander Motin 	{
229d3a460d3SAlexander Motin 		/* Seagate Momentus Thin Advanced Format (4k) drives */
230d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???LT*", "*" },
231d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
232d3a460d3SAlexander Motin 	},
233d3a460d3SAlexander Motin 	{
234d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
235d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RS*", "*" },
236d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
237d3a460d3SAlexander Motin 	},
238d3a460d3SAlexander Motin 	{
239d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
240d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RX*", "*" },
241d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
242d3a460d3SAlexander Motin 	},
243d3a460d3SAlexander Motin 	{
244d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
245d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RS*", "*" },
246d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
247d3a460d3SAlexander Motin 	},
248d3a460d3SAlexander Motin 	{
249d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
250d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RX*", "*" },
251d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
252d3a460d3SAlexander Motin 	},
253d3a460d3SAlexander Motin 	{
254d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
255d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PKT*", "*" },
256d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
257d3a460d3SAlexander Motin 	},
258d3a460d3SAlexander Motin 	{
259d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
260d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PKT*", "*" },
261d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
262d3a460d3SAlexander Motin 	},
263d3a460d3SAlexander Motin 	{
264d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
265d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PVT*", "*" },
266d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
267d3a460d3SAlexander Motin 	},
268d3a460d3SAlexander Motin 	{
269d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
270d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PVT*", "*" },
271d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
272d3a460d3SAlexander Motin 	},
273d3a460d3SAlexander Motin 	{
2749d3334e1SEitan Adler 		/*
2759d3334e1SEitan Adler 		 * Corsair Force 2 SSDs
2769d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
2779d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
2789d3334e1SEitan Adler 		 * PR: 169974
2799d3334e1SEitan Adler 		 */
2809d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair CSSD-F*", "*" },
2819d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
2829d3334e1SEitan Adler 	},
2839d3334e1SEitan Adler 	{
2849d3334e1SEitan Adler 		/*
2859d3334e1SEitan Adler 		 * Corsair Force 3 SSDs
2869d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
2879d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
2889d3334e1SEitan Adler 		 * PR: 169974
2899d3334e1SEitan Adler 		 */
2909d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force 3*", "*" },
2919d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
2929d3334e1SEitan Adler 	},
2939d3334e1SEitan Adler 	{
2949d3334e1SEitan Adler 		/*
2959d3334e1SEitan Adler 		 * OCZ Agility 3 SSDs
2969d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
2979d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
2989d3334e1SEitan Adler 		 * PR: 169974
2999d3334e1SEitan Adler 		 */
3009d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY3*", "*" },
3019d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3029d3334e1SEitan Adler 	},
3039d3334e1SEitan Adler 	{
3049d3334e1SEitan Adler 		/*
3059d3334e1SEitan Adler 		 * OCZ Vertex 2 SSDs (inc pro series)
3069d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3079d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3089d3334e1SEitan Adler 		 * PR: 169974
3099d3334e1SEitan Adler 		 */
3109d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ?VERTEX2*", "*" },
3119d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3129d3334e1SEitan Adler 	},
3139d3334e1SEitan Adler 	{
3149d3334e1SEitan Adler 		/*
3159d3334e1SEitan Adler 		 * OCZ Vertex 3 SSDs
3169d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3179d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3189d3334e1SEitan Adler 		 * PR: 169974
3199d3334e1SEitan Adler 		 */
3209d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX3*", "*" },
3219d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3229d3334e1SEitan Adler 	},
3239d3334e1SEitan Adler 	{
3249d3334e1SEitan Adler 		/*
3259d3334e1SEitan Adler 		 * SuperTalent TeraDrive CT SSDs
3269d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3279d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3289d3334e1SEitan Adler 		 * PR: 169974
3299d3334e1SEitan Adler 		 */
3309d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "FTM??CT25H*", "*" },
3319d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3329d3334e1SEitan Adler 	},
3339d3334e1SEitan Adler 	{
3349d3334e1SEitan Adler 		/*
3359d3334e1SEitan Adler 		 * Crucial RealSSD C300 SSDs
3369d3334e1SEitan Adler 		 * 4k optimised
3379d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3389d3334e1SEitan Adler 		 * PR: 169974
3399d3334e1SEitan Adler 		 */
3409d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*",
3419d3334e1SEitan Adler 		"*" }, /*quirks*/ADA_Q_4K
3429d3334e1SEitan Adler 	},
3439d3334e1SEitan Adler 	{
3449d3334e1SEitan Adler 		/*
3459d3334e1SEitan Adler 		 * XceedIOPS SATA SSDs
3469d3334e1SEitan Adler 		 * 4k optimised
3479d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3489d3334e1SEitan Adler 		 * PR: 169974
3499d3334e1SEitan Adler 		 */
3509d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SG9XCS2D*", "*" },
3519d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3529d3334e1SEitan Adler 	},
3539d3334e1SEitan Adler 	{
3549d3334e1SEitan Adler 		/*
3559d3334e1SEitan Adler 		 * Intel 330 Series SSDs
3569d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3579d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3589d3334e1SEitan Adler 		 * PR: 169974
3599d3334e1SEitan Adler 		 */
3609d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2ct*", "*" },
3619d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3629d3334e1SEitan Adler 	},
3639d3334e1SEitan Adler 	{
3649d3334e1SEitan Adler 		/*
3659d3334e1SEitan Adler 		 * OCZ Deneva R Series SSDs
3669d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3679d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3689d3334e1SEitan Adler 		 * PR: 169974
3699d3334e1SEitan Adler 		 */
3709d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "DENRSTE251M45*", "*" },
3719d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3729d3334e1SEitan Adler 	},
3739d3334e1SEitan Adler 	{
3749d3334e1SEitan Adler 		/*
3759d3334e1SEitan Adler 		 * Kingston HyperX 3k SSDs
3769d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3779d3334e1SEitan Adler 		 * Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
3789d3334e1SEitan Adler 		 * PR: 169974
3799d3334e1SEitan Adler 		 */
3809d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SH103S3*", "*" },
3819d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3829d3334e1SEitan Adler 	},
3839d3334e1SEitan Adler 	{
38430a4094fSAlexander Motin 		/* Default */
38530a4094fSAlexander Motin 		{
38630a4094fSAlexander Motin 		  T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
38730a4094fSAlexander Motin 		  /*vendor*/"*", /*product*/"*", /*revision*/"*"
38830a4094fSAlexander Motin 		},
38930a4094fSAlexander Motin 		/*quirks*/0
39030a4094fSAlexander Motin 	},
39130a4094fSAlexander Motin };
39252c9ce25SScott Long 
39352c9ce25SScott Long static	disk_strategy_t	adastrategy;
39452c9ce25SScott Long static	dumper_t	adadump;
39552c9ce25SScott Long static	periph_init_t	adainit;
39652c9ce25SScott Long static	void		adaasync(void *callback_arg, u_int32_t code,
39752c9ce25SScott Long 				struct cam_path *path, void *arg);
39852c9ce25SScott Long static	void		adasysctlinit(void *context, int pending);
39952c9ce25SScott Long static	periph_ctor_t	adaregister;
40052c9ce25SScott Long static	periph_dtor_t	adacleanup;
40152c9ce25SScott Long static	periph_start_t	adastart;
40252c9ce25SScott Long static	periph_oninv_t	adaoninvalidate;
40352c9ce25SScott Long static	void		adadone(struct cam_periph *periph,
40452c9ce25SScott Long 			       union ccb *done_ccb);
40552c9ce25SScott Long static  int		adaerror(union ccb *ccb, u_int32_t cam_flags,
40652c9ce25SScott Long 				u_int32_t sense_flags);
407c1bd46c2SAlexander Motin static void		adagetparams(struct cam_periph *periph,
40852c9ce25SScott Long 				struct ccb_getdev *cgd);
40952c9ce25SScott Long static timeout_t	adasendorderedtag;
41052c9ce25SScott Long static void		adashutdown(void *arg, int howto);
411c3d0d168SAlexander Motin static void		adasuspend(void *arg);
412c3d0d168SAlexander Motin static void		adaresume(void *arg);
41352c9ce25SScott Long 
4140d307e09SAlexander Motin #ifndef	ADA_DEFAULT_LEGACY_ALIASES
4150d307e09SAlexander Motin #define	ADA_DEFAULT_LEGACY_ALIASES	1
4160d307e09SAlexander Motin #endif
4170d307e09SAlexander Motin 
41852c9ce25SScott Long #ifndef ADA_DEFAULT_TIMEOUT
41952c9ce25SScott Long #define ADA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
42052c9ce25SScott Long #endif
42152c9ce25SScott Long 
42252c9ce25SScott Long #ifndef	ADA_DEFAULT_RETRY
42352c9ce25SScott Long #define	ADA_DEFAULT_RETRY	4
42452c9ce25SScott Long #endif
42552c9ce25SScott Long 
42652c9ce25SScott Long #ifndef	ADA_DEFAULT_SEND_ORDERED
42752c9ce25SScott Long #define	ADA_DEFAULT_SEND_ORDERED	1
42852c9ce25SScott Long #endif
42952c9ce25SScott Long 
430fd104c15SRebecca Cran #ifndef	ADA_DEFAULT_SPINDOWN_SHUTDOWN
431fd104c15SRebecca Cran #define	ADA_DEFAULT_SPINDOWN_SHUTDOWN	1
432fd104c15SRebecca Cran #endif
433fd104c15SRebecca Cran 
434c3d0d168SAlexander Motin #ifndef	ADA_DEFAULT_SPINDOWN_SUSPEND
435c3d0d168SAlexander Motin #define	ADA_DEFAULT_SPINDOWN_SUSPEND	1
436c3d0d168SAlexander Motin #endif
437c3d0d168SAlexander Motin 
4381ed6aaf9SAlexander Motin #ifndef	ADA_DEFAULT_READ_AHEAD
4391ed6aaf9SAlexander Motin #define	ADA_DEFAULT_READ_AHEAD	1
4401ed6aaf9SAlexander Motin #endif
4411ed6aaf9SAlexander Motin 
442f513d14cSAlexander Motin #ifndef	ADA_DEFAULT_WRITE_CACHE
443f513d14cSAlexander Motin #define	ADA_DEFAULT_WRITE_CACHE	1
444f513d14cSAlexander Motin #endif
445f513d14cSAlexander Motin 
4461ed6aaf9SAlexander Motin #define	ADA_RA	(softc->read_ahead >= 0 ? \
4471ed6aaf9SAlexander Motin 		 softc->read_ahead : ada_read_ahead)
4481ed6aaf9SAlexander Motin #define	ADA_WC	(softc->write_cache >= 0 ? \
4491ed6aaf9SAlexander Motin 		 softc->write_cache : ada_write_cache)
4505f83aee5SSteven Hartland #define	ADA_SIO	(softc->sort_io_queue >= 0 ? \
4515f83aee5SSteven Hartland 		 softc->sort_io_queue : cam_sort_io_queues)
4521ed6aaf9SAlexander Motin 
4534461491bSMarius Strobl /*
4544461491bSMarius Strobl  * Most platforms map firmware geometry to actual, but some don't.  If
4554461491bSMarius Strobl  * not overridden, default to nothing.
4564461491bSMarius Strobl  */
4574461491bSMarius Strobl #ifndef ata_disk_firmware_geom_adjust
4584461491bSMarius Strobl #define	ata_disk_firmware_geom_adjust(disk)
4594461491bSMarius Strobl #endif
46052c9ce25SScott Long 
4610d307e09SAlexander Motin static int ada_legacy_aliases = ADA_DEFAULT_LEGACY_ALIASES;
46252c9ce25SScott Long static int ada_retry_count = ADA_DEFAULT_RETRY;
46352c9ce25SScott Long static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
46452c9ce25SScott Long static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
465fd104c15SRebecca Cran static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
466c3d0d168SAlexander Motin static int ada_spindown_suspend = ADA_DEFAULT_SPINDOWN_SUSPEND;
4671ed6aaf9SAlexander Motin static int ada_read_ahead = ADA_DEFAULT_READ_AHEAD;
468f513d14cSAlexander Motin static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE;
46952c9ce25SScott Long 
4706472ac3dSEd Schouten static SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
47152c9ce25SScott Long             "CAM Direct Access Disk driver");
4720d307e09SAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, legacy_aliases, CTLFLAG_RW,
4730d307e09SAlexander Motin            &ada_legacy_aliases, 0, "Create legacy-like device aliases");
4740d307e09SAlexander Motin TUNABLE_INT("kern.cam.ada.legacy_aliases", &ada_legacy_aliases);
47552c9ce25SScott Long SYSCTL_INT(_kern_cam_ada, OID_AUTO, retry_count, CTLFLAG_RW,
47652c9ce25SScott Long            &ada_retry_count, 0, "Normal I/O retry count");
47752c9ce25SScott Long TUNABLE_INT("kern.cam.ada.retry_count", &ada_retry_count);
47852c9ce25SScott Long SYSCTL_INT(_kern_cam_ada, OID_AUTO, default_timeout, CTLFLAG_RW,
47952c9ce25SScott Long            &ada_default_timeout, 0, "Normal I/O timeout (in seconds)");
48052c9ce25SScott Long TUNABLE_INT("kern.cam.ada.default_timeout", &ada_default_timeout);
481af010905SChristian Brueffer SYSCTL_INT(_kern_cam_ada, OID_AUTO, send_ordered, CTLFLAG_RW,
48252c9ce25SScott Long            &ada_send_ordered, 0, "Send Ordered Tags");
483af010905SChristian Brueffer TUNABLE_INT("kern.cam.ada.send_ordered", &ada_send_ordered);
484fd104c15SRebecca Cran SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RW,
485fd104c15SRebecca Cran            &ada_spindown_shutdown, 0, "Spin down upon shutdown");
486fd104c15SRebecca Cran TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown);
487c3d0d168SAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_suspend, CTLFLAG_RW,
488c3d0d168SAlexander Motin            &ada_spindown_suspend, 0, "Spin down upon suspend");
489c3d0d168SAlexander Motin TUNABLE_INT("kern.cam.ada.spindown_suspend", &ada_spindown_suspend);
4901ed6aaf9SAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, read_ahead, CTLFLAG_RW,
4911ed6aaf9SAlexander Motin            &ada_read_ahead, 0, "Enable disk read-ahead");
4921ed6aaf9SAlexander Motin TUNABLE_INT("kern.cam.ada.read_ahead", &ada_read_ahead);
493f513d14cSAlexander Motin SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RW,
494f513d14cSAlexander Motin            &ada_write_cache, 0, "Enable disk write cache");
495f513d14cSAlexander Motin TUNABLE_INT("kern.cam.ada.write_cache", &ada_write_cache);
49652c9ce25SScott Long 
49752c9ce25SScott Long /*
49852c9ce25SScott Long  * ADA_ORDEREDTAG_INTERVAL determines how often, relative
49952c9ce25SScott Long  * to the default timeout, we check to see whether an ordered
50052c9ce25SScott Long  * tagged transaction is appropriate to prevent simple tag
50152c9ce25SScott Long  * starvation.  Since we'd like to ensure that there is at least
50252c9ce25SScott Long  * 1/2 of the timeout length left for a starved transaction to
50352c9ce25SScott Long  * complete after we've sent an ordered tag, we must poll at least
50452c9ce25SScott Long  * four times in every timeout period.  This takes care of the worst
50552c9ce25SScott Long  * case where a starved transaction starts during an interval that
50652c9ce25SScott Long  * meets the requirement "don't send an ordered tag" test so it takes
50752c9ce25SScott Long  * us two intervals to determine that a tag must be sent.
50852c9ce25SScott Long  */
50952c9ce25SScott Long #ifndef ADA_ORDEREDTAG_INTERVAL
51052c9ce25SScott Long #define ADA_ORDEREDTAG_INTERVAL 4
51152c9ce25SScott Long #endif
51252c9ce25SScott Long 
51352c9ce25SScott Long static struct periph_driver adadriver =
51452c9ce25SScott Long {
51552c9ce25SScott Long 	adainit, "ada",
51652c9ce25SScott Long 	TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0
51752c9ce25SScott Long };
51852c9ce25SScott Long 
51952c9ce25SScott Long PERIPHDRIVER_DECLARE(ada, adadriver);
52052c9ce25SScott Long 
521d745c852SEd Schouten static MALLOC_DEFINE(M_ATADA, "ata_da", "ata_da buffers");
52252c9ce25SScott Long 
52352c9ce25SScott Long static int
52452c9ce25SScott Long adaopen(struct disk *dp)
52552c9ce25SScott Long {
52652c9ce25SScott Long 	struct cam_periph *periph;
52752c9ce25SScott Long 	struct ada_softc *softc;
52852c9ce25SScott Long 	int error;
52952c9ce25SScott Long 
53052c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
53152c9ce25SScott Long 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
53252c9ce25SScott Long 		return(ENXIO);
53352c9ce25SScott Long 	}
53452c9ce25SScott Long 
53552c9ce25SScott Long 	cam_periph_lock(periph);
53652c9ce25SScott Long 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
53752c9ce25SScott Long 		cam_periph_unlock(periph);
53852c9ce25SScott Long 		cam_periph_release(periph);
53952c9ce25SScott Long 		return (error);
54052c9ce25SScott Long 	}
54152c9ce25SScott Long 
54252c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
54352c9ce25SScott Long 	softc->flags |= ADA_FLAG_OPEN;
54452c9ce25SScott Long 
545fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
546fddde2b8SAlexander Motin 	    ("adaopen\n"));
54752c9ce25SScott Long 
54852c9ce25SScott Long 	if ((softc->flags & ADA_FLAG_PACK_INVALID) != 0) {
54952c9ce25SScott Long 		/* Invalidate our pack information. */
55052c9ce25SScott Long 		softc->flags &= ~ADA_FLAG_PACK_INVALID;
55152c9ce25SScott Long 	}
55252c9ce25SScott Long 
55352c9ce25SScott Long 	cam_periph_unhold(periph);
55452c9ce25SScott Long 	cam_periph_unlock(periph);
55552c9ce25SScott Long 	return (0);
55652c9ce25SScott Long }
55752c9ce25SScott Long 
55852c9ce25SScott Long static int
55952c9ce25SScott Long adaclose(struct disk *dp)
56052c9ce25SScott Long {
56152c9ce25SScott Long 	struct	cam_periph *periph;
56252c9ce25SScott Long 	struct	ada_softc *softc;
56352c9ce25SScott Long 	union ccb *ccb;
56452c9ce25SScott Long 
56552c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
56652c9ce25SScott Long 	cam_periph_lock(periph);
5676f487924SAlexander Motin 	if (cam_periph_hold(periph, PRIBIO) != 0) {
56852c9ce25SScott Long 		cam_periph_unlock(periph);
56952c9ce25SScott Long 		cam_periph_release(periph);
5706f487924SAlexander Motin 		return (0);
57152c9ce25SScott Long 	}
57252c9ce25SScott Long 
57352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
574fddde2b8SAlexander Motin 
575fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
576fddde2b8SAlexander Motin 	    ("adaclose\n"));
577fddde2b8SAlexander Motin 
57852c9ce25SScott Long 	/* We only sync the cache if the drive is capable of it. */
579c36bb43cSAlexander Motin 	if ((softc->flags & ADA_FLAG_CAN_FLUSHCACHE) != 0 &&
580c36bb43cSAlexander Motin 	    (softc->flags & ADA_FLAG_PACK_INVALID) == 0) {
58152c9ce25SScott Long 
582bbfa4aa1SAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
58352c9ce25SScott Long 		cam_fill_ataio(&ccb->ataio,
58452c9ce25SScott Long 				    1,
58552c9ce25SScott Long 				    adadone,
58652c9ce25SScott Long 				    CAM_DIR_NONE,
58752c9ce25SScott Long 				    0,
58852c9ce25SScott Long 				    NULL,
58952c9ce25SScott Long 				    0,
59052c9ce25SScott Long 				    ada_default_timeout*1000);
59152c9ce25SScott Long 
59252c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
59352c9ce25SScott Long 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
59452c9ce25SScott Long 		else
5957606b445SAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
5967642883fSAlexander Motin 		cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
59746f118feSAlexander Motin 		    /*sense_flags*/0, softc->disk->d_devstat);
59852c9ce25SScott Long 
59952c9ce25SScott Long 		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
60052c9ce25SScott Long 			xpt_print(periph->path, "Synchronize cache failed\n");
60152c9ce25SScott Long 		xpt_release_ccb(ccb);
60252c9ce25SScott Long 	}
60352c9ce25SScott Long 
60452c9ce25SScott Long 	softc->flags &= ~ADA_FLAG_OPEN;
60552c9ce25SScott Long 	cam_periph_unhold(periph);
60652c9ce25SScott Long 	cam_periph_unlock(periph);
60752c9ce25SScott Long 	cam_periph_release(periph);
60852c9ce25SScott Long 	return (0);
60952c9ce25SScott Long }
61052c9ce25SScott Long 
6111c80ec0aSAlexander Motin static void
6121c80ec0aSAlexander Motin adaschedule(struct cam_periph *periph)
6131c80ec0aSAlexander Motin {
6141c80ec0aSAlexander Motin 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
61510a6c358SAlexander Motin 	uint32_t prio;
6161c80ec0aSAlexander Motin 
61710a6c358SAlexander Motin 	/* Check if cam_periph_getccb() was called. */
61810a6c358SAlexander Motin 	prio = periph->immediate_priority;
61910a6c358SAlexander Motin 
62010a6c358SAlexander Motin 	/* Check if we have more work to do. */
6211c80ec0aSAlexander Motin 	if (bioq_first(&softc->bio_queue) ||
6221c80ec0aSAlexander Motin 	    (!softc->trim_running && bioq_first(&softc->trim_queue))) {
62310a6c358SAlexander Motin 		prio = CAM_PRIORITY_NORMAL;
6241c80ec0aSAlexander Motin 	}
62510a6c358SAlexander Motin 
62610a6c358SAlexander Motin 	/* Schedule CCB if any of above is true. */
62710a6c358SAlexander Motin 	if (prio != CAM_PRIORITY_NONE)
62810a6c358SAlexander Motin 		xpt_schedule(periph, prio);
6291c80ec0aSAlexander Motin }
6301c80ec0aSAlexander Motin 
63152c9ce25SScott Long /*
63252c9ce25SScott Long  * Actually translate the requested transfer into one the physical driver
63352c9ce25SScott Long  * can understand.  The transfer is described by a buf and will include
63452c9ce25SScott Long  * only one physical transfer.
63552c9ce25SScott Long  */
63652c9ce25SScott Long static void
63752c9ce25SScott Long adastrategy(struct bio *bp)
63852c9ce25SScott Long {
63952c9ce25SScott Long 	struct cam_periph *periph;
64052c9ce25SScott Long 	struct ada_softc *softc;
64152c9ce25SScott Long 
64252c9ce25SScott Long 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
64352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
64452c9ce25SScott Long 
64552c9ce25SScott Long 	cam_periph_lock(periph);
64652c9ce25SScott Long 
647fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastrategy(%p)\n", bp));
648fddde2b8SAlexander Motin 
64952c9ce25SScott Long 	/*
65052c9ce25SScott Long 	 * If the device has been made invalid, error out
65152c9ce25SScott Long 	 */
65252c9ce25SScott Long 	if ((softc->flags & ADA_FLAG_PACK_INVALID)) {
65352c9ce25SScott Long 		cam_periph_unlock(periph);
65452c9ce25SScott Long 		biofinish(bp, NULL, ENXIO);
65552c9ce25SScott Long 		return;
65652c9ce25SScott Long 	}
65752c9ce25SScott Long 
65852c9ce25SScott Long 	/*
65952c9ce25SScott Long 	 * Place it in the queue of disk activities for this disk
66052c9ce25SScott Long 	 */
6611c80ec0aSAlexander Motin 	if (bp->bio_cmd == BIO_DELETE &&
6625f83aee5SSteven Hartland 	    (softc->flags & ADA_FLAG_CAN_TRIM)) {
6635f83aee5SSteven Hartland 		if (ADA_SIO)
6641c80ec0aSAlexander Motin 		    bioq_disksort(&softc->trim_queue, bp);
6651c80ec0aSAlexander Motin 		else
6665f83aee5SSteven Hartland 		    bioq_insert_tail(&softc->trim_queue, bp);
6675f83aee5SSteven Hartland 	} else {
6685f83aee5SSteven Hartland 		if (ADA_SIO)
66952c9ce25SScott Long 		    bioq_disksort(&softc->bio_queue, bp);
6705f83aee5SSteven Hartland 		else
6715f83aee5SSteven Hartland 		    bioq_insert_tail(&softc->bio_queue, bp);
6725f83aee5SSteven Hartland 	}
67352c9ce25SScott Long 
67452c9ce25SScott Long 	/*
67552c9ce25SScott Long 	 * Schedule ourselves for performing the work.
67652c9ce25SScott Long 	 */
6771c80ec0aSAlexander Motin 	adaschedule(periph);
67852c9ce25SScott Long 	cam_periph_unlock(periph);
67952c9ce25SScott Long 
68052c9ce25SScott Long 	return;
68152c9ce25SScott Long }
68252c9ce25SScott Long 
68352c9ce25SScott Long static int
68452c9ce25SScott Long adadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
68552c9ce25SScott Long {
68652c9ce25SScott Long 	struct	    cam_periph *periph;
68752c9ce25SScott Long 	struct	    ada_softc *softc;
68852c9ce25SScott Long 	u_int	    secsize;
68952c9ce25SScott Long 	union	    ccb ccb;
69052c9ce25SScott Long 	struct	    disk *dp;
69152c9ce25SScott Long 	uint64_t    lba;
69252c9ce25SScott Long 	uint16_t    count;
6930191d9b3SAlexander Motin 	int	    error = 0;
69452c9ce25SScott Long 
69552c9ce25SScott Long 	dp = arg;
69652c9ce25SScott Long 	periph = dp->d_drv1;
69752c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
69852c9ce25SScott Long 	cam_periph_lock(periph);
69952c9ce25SScott Long 	secsize = softc->params.secsize;
70052c9ce25SScott Long 	lba = offset / secsize;
70152c9ce25SScott Long 	count = length / secsize;
70252c9ce25SScott Long 
70352c9ce25SScott Long 	if ((softc->flags & ADA_FLAG_PACK_INVALID) != 0) {
70452c9ce25SScott Long 		cam_periph_unlock(periph);
70552c9ce25SScott Long 		return (ENXIO);
70652c9ce25SScott Long 	}
70752c9ce25SScott Long 
70852c9ce25SScott Long 	if (length > 0) {
709bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
71052c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
71152c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
71252c9ce25SScott Long 		    0,
71352c9ce25SScott Long 		    adadone,
71452c9ce25SScott Long 		    CAM_DIR_OUT,
71552c9ce25SScott Long 		    0,
71652c9ce25SScott Long 		    (u_int8_t *) virtual,
71752c9ce25SScott Long 		    length,
71852c9ce25SScott Long 		    ada_default_timeout*1000);
71952c9ce25SScott Long 		if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
72052c9ce25SScott Long 		    (lba + count >= ATA_MAX_28BIT_LBA ||
72152c9ce25SScott Long 		    count >= 256)) {
72252c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_WRITE_DMA48,
72352c9ce25SScott Long 			    0, lba, count);
72452c9ce25SScott Long 		} else {
7257606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_WRITE_DMA,
72652c9ce25SScott Long 			    0, lba, count);
72752c9ce25SScott Long 		}
72852c9ce25SScott Long 		xpt_polled_action(&ccb);
72952c9ce25SScott Long 
7300191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
7310191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
7320191d9b3SAlexander Motin 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
7330191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
7340191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
7350191d9b3SAlexander Motin 		if (error != 0)
73652c9ce25SScott Long 			printf("Aborting dump due to I/O error.\n");
7370191d9b3SAlexander Motin 
73852c9ce25SScott Long 		cam_periph_unlock(periph);
7390191d9b3SAlexander Motin 		return (error);
74052c9ce25SScott Long 	}
74152c9ce25SScott Long 
74252c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) {
743bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
74452c9ce25SScott Long 
74552c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
74652c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
7470191d9b3SAlexander Motin 				    0,
74852c9ce25SScott Long 				    adadone,
74952c9ce25SScott Long 				    CAM_DIR_NONE,
75052c9ce25SScott Long 				    0,
75152c9ce25SScott Long 				    NULL,
75252c9ce25SScott Long 				    0,
75352c9ce25SScott Long 				    ada_default_timeout*1000);
75452c9ce25SScott Long 
75552c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
75652c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0);
75752c9ce25SScott Long 		else
7587606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_FLUSHCACHE, 0, 0, 0);
75952c9ce25SScott Long 		xpt_polled_action(&ccb);
76052c9ce25SScott Long 
7610191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
7620191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
76352c9ce25SScott Long 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
7640191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
7650191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
7660191d9b3SAlexander Motin 		if (error != 0)
7670191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
76852c9ce25SScott Long 	}
76952c9ce25SScott Long 	cam_periph_unlock(periph);
7700191d9b3SAlexander Motin 	return (error);
77152c9ce25SScott Long }
77252c9ce25SScott Long 
77352c9ce25SScott Long static void
77452c9ce25SScott Long adainit(void)
77552c9ce25SScott Long {
77652c9ce25SScott Long 	cam_status status;
77752c9ce25SScott Long 
77852c9ce25SScott Long 	/*
77952c9ce25SScott Long 	 * Install a global async callback.  This callback will
78052c9ce25SScott Long 	 * receive async callbacks like "new device found".
78152c9ce25SScott Long 	 */
78252c9ce25SScott Long 	status = xpt_register_async(AC_FOUND_DEVICE, adaasync, NULL, NULL);
78352c9ce25SScott Long 
78452c9ce25SScott Long 	if (status != CAM_REQ_CMP) {
78552c9ce25SScott Long 		printf("ada: Failed to attach master async callback "
78652c9ce25SScott Long 		       "due to status 0x%x!\n", status);
78752c9ce25SScott Long 	} else if (ada_send_ordered) {
78852c9ce25SScott Long 
789c3d0d168SAlexander Motin 		/* Register our event handlers */
790c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_suspend, adasuspend,
791c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
792c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
793c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_resume, adaresume,
794c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
795c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
79652c9ce25SScott Long 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, adashutdown,
79752c9ce25SScott Long 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
79852c9ce25SScott Long 		    printf("adainit: shutdown event registration failed!\n");
79952c9ce25SScott Long 	}
80052c9ce25SScott Long }
80152c9ce25SScott Long 
80252c9ce25SScott Long static void
80352c9ce25SScott Long adaoninvalidate(struct cam_periph *periph)
80452c9ce25SScott Long {
80552c9ce25SScott Long 	struct ada_softc *softc;
80652c9ce25SScott Long 
80752c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
80852c9ce25SScott Long 
80952c9ce25SScott Long 	/*
81052c9ce25SScott Long 	 * De-register any async callbacks.
81152c9ce25SScott Long 	 */
81252c9ce25SScott Long 	xpt_register_async(0, adaasync, periph, periph->path);
81352c9ce25SScott Long 
81452c9ce25SScott Long 	softc->flags |= ADA_FLAG_PACK_INVALID;
81552c9ce25SScott Long 
81652c9ce25SScott Long 	/*
81752c9ce25SScott Long 	 * Return all queued I/O with ENXIO.
81852c9ce25SScott Long 	 * XXX Handle any transactions queued to the card
81952c9ce25SScott Long 	 *     with XPT_ABORT_CCB.
82052c9ce25SScott Long 	 */
82152c9ce25SScott Long 	bioq_flush(&softc->bio_queue, NULL, ENXIO);
8221c80ec0aSAlexander Motin 	bioq_flush(&softc->trim_queue, NULL, ENXIO);
82352c9ce25SScott Long 
82452c9ce25SScott Long 	disk_gone(softc->disk);
82552c9ce25SScott Long 	xpt_print(periph->path, "lost device\n");
82652c9ce25SScott Long }
82752c9ce25SScott Long 
82852c9ce25SScott Long static void
82952c9ce25SScott Long adacleanup(struct cam_periph *periph)
83052c9ce25SScott Long {
83152c9ce25SScott Long 	struct ada_softc *softc;
83252c9ce25SScott Long 
83352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
83452c9ce25SScott Long 
83552c9ce25SScott Long 	xpt_print(periph->path, "removing device entry\n");
83652c9ce25SScott Long 	cam_periph_unlock(periph);
83752c9ce25SScott Long 
83852c9ce25SScott Long 	/*
83952c9ce25SScott Long 	 * If we can't free the sysctl tree, oh well...
84052c9ce25SScott Long 	 */
84152c9ce25SScott Long 	if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0
84252c9ce25SScott Long 	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
84352c9ce25SScott Long 		xpt_print(periph->path, "can't remove sysctl context\n");
84452c9ce25SScott Long 	}
84552c9ce25SScott Long 
84652c9ce25SScott Long 	disk_destroy(softc->disk);
84752c9ce25SScott Long 	callout_drain(&softc->sendordered_c);
84852c9ce25SScott Long 	free(softc, M_DEVBUF);
84952c9ce25SScott Long 	cam_periph_lock(periph);
85052c9ce25SScott Long }
85152c9ce25SScott Long 
85252c9ce25SScott Long static void
85352c9ce25SScott Long adaasync(void *callback_arg, u_int32_t code,
85452c9ce25SScott Long 	struct cam_path *path, void *arg)
85552c9ce25SScott Long {
856581b2e78SAlexander Motin 	struct ccb_getdev cgd;
85752c9ce25SScott Long 	struct cam_periph *periph;
858f513d14cSAlexander Motin 	struct ada_softc *softc;
85952c9ce25SScott Long 
86052c9ce25SScott Long 	periph = (struct cam_periph *)callback_arg;
86152c9ce25SScott Long 	switch (code) {
86252c9ce25SScott Long 	case AC_FOUND_DEVICE:
86352c9ce25SScott Long 	{
86452c9ce25SScott Long 		struct ccb_getdev *cgd;
86552c9ce25SScott Long 		cam_status status;
86652c9ce25SScott Long 
86752c9ce25SScott Long 		cgd = (struct ccb_getdev *)arg;
86852c9ce25SScott Long 		if (cgd == NULL)
86952c9ce25SScott Long 			break;
87052c9ce25SScott Long 
87152c9ce25SScott Long 		if (cgd->protocol != PROTO_ATA)
87252c9ce25SScott Long 			break;
87352c9ce25SScott Long 
87452c9ce25SScott Long 		/*
87552c9ce25SScott Long 		 * Allocate a peripheral instance for
87652c9ce25SScott Long 		 * this device and start the probe
87752c9ce25SScott Long 		 * process.
87852c9ce25SScott Long 		 */
87952c9ce25SScott Long 		status = cam_periph_alloc(adaregister, adaoninvalidate,
88052c9ce25SScott Long 					  adacleanup, adastart,
88152c9ce25SScott Long 					  "ada", CAM_PERIPH_BIO,
88252c9ce25SScott Long 					  cgd->ccb_h.path, adaasync,
88352c9ce25SScott Long 					  AC_FOUND_DEVICE, cgd);
88452c9ce25SScott Long 
88552c9ce25SScott Long 		if (status != CAM_REQ_CMP
88652c9ce25SScott Long 		 && status != CAM_REQ_INPROG)
88752c9ce25SScott Long 			printf("adaasync: Unable to attach to new device "
88852c9ce25SScott Long 				"due to status 0x%x\n", status);
88952c9ce25SScott Long 		break;
89052c9ce25SScott Long 	}
891581b2e78SAlexander Motin 	case AC_GETDEV_CHANGED:
892581b2e78SAlexander Motin 	{
893581b2e78SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
894581b2e78SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
895581b2e78SAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
896581b2e78SAlexander Motin 		xpt_action((union ccb *)&cgd);
897581b2e78SAlexander Motin 
898581b2e78SAlexander Motin 		if ((cgd.ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
899581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA))
900581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_DMA;
901581b2e78SAlexander Motin 		else
902581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_DMA;
903*2e1eb332SMarius Strobl 		if (cgd.ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
904*2e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_48BIT;
905*2e1eb332SMarius Strobl 			if (cgd.inq_flags & SID_DMA48)
906*2e1eb332SMarius Strobl 				softc->flags |= ADA_FLAG_CAN_DMA48;
907*2e1eb332SMarius Strobl 			else
908*2e1eb332SMarius Strobl 				softc->flags &= ~ADA_FLAG_CAN_DMA48;
909*2e1eb332SMarius Strobl 		} else
910*2e1eb332SMarius Strobl 			softc->flags &= ~(ADA_FLAG_CAN_48BIT |
911*2e1eb332SMarius Strobl 			    ADA_FLAG_CAN_DMA48);
912581b2e78SAlexander Motin 		if ((cgd.ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
913581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA) && (cgd.inq_flags & SID_CmdQue))
914581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_NCQ;
915581b2e78SAlexander Motin 		else
916581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_NCQ;
917581b2e78SAlexander Motin 		if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
918581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA))
919581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_TRIM;
920581b2e78SAlexander Motin 		else
921581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_TRIM;
922581b2e78SAlexander Motin 
923581b2e78SAlexander Motin 		cam_periph_async(periph, code, path, arg);
924581b2e78SAlexander Motin 		break;
925581b2e78SAlexander Motin 	}
9263089bb2eSAlexander Motin 	case AC_ADVINFO_CHANGED:
9273089bb2eSAlexander Motin 	{
9283089bb2eSAlexander Motin 		uintptr_t buftype;
9293089bb2eSAlexander Motin 
9303089bb2eSAlexander Motin 		buftype = (uintptr_t)arg;
9313089bb2eSAlexander Motin 		if (buftype == CDAI_TYPE_PHYS_PATH) {
9323089bb2eSAlexander Motin 			struct ada_softc *softc;
9333089bb2eSAlexander Motin 
9343089bb2eSAlexander Motin 			softc = periph->softc;
9353089bb2eSAlexander Motin 			disk_attr_changed(softc->disk, "GEOM::physpath",
9363089bb2eSAlexander Motin 					  M_NOWAIT);
9373089bb2eSAlexander Motin 		}
9383089bb2eSAlexander Motin 		break;
9393089bb2eSAlexander Motin 	}
940f513d14cSAlexander Motin 	case AC_SENT_BDR:
941f513d14cSAlexander Motin 	case AC_BUS_RESET:
942f513d14cSAlexander Motin 	{
943f513d14cSAlexander Motin 		softc = (struct ada_softc *)periph->softc;
944f513d14cSAlexander Motin 		cam_periph_async(periph, code, path, arg);
945f513d14cSAlexander Motin 		if (softc->state != ADA_STATE_NORMAL)
946f513d14cSAlexander Motin 			break;
947e3a6d3a4SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
948f513d14cSAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
949f513d14cSAlexander Motin 		xpt_action((union ccb *)&cgd);
9501ed6aaf9SAlexander Motin 		if (ADA_RA >= 0 &&
9511ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD)
9521ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_RAHEAD;
9531ed6aaf9SAlexander Motin 		else if (ADA_WC >= 0 &&
9541ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE)
955f513d14cSAlexander Motin 			softc->state = ADA_STATE_WCACHE;
9561ed6aaf9SAlexander Motin 		else
9571ed6aaf9SAlexander Motin 		    break;
958f513d14cSAlexander Motin 		cam_periph_acquire(periph);
959f513d14cSAlexander Motin 		cam_freeze_devq_arg(periph->path,
960f513d14cSAlexander Motin 		    RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
961f513d14cSAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
962f513d14cSAlexander Motin 	}
96352c9ce25SScott Long 	default:
96452c9ce25SScott Long 		cam_periph_async(periph, code, path, arg);
96552c9ce25SScott Long 		break;
96652c9ce25SScott Long 	}
96752c9ce25SScott Long }
96852c9ce25SScott Long 
96952c9ce25SScott Long static void
97052c9ce25SScott Long adasysctlinit(void *context, int pending)
97152c9ce25SScott Long {
97252c9ce25SScott Long 	struct cam_periph *periph;
97352c9ce25SScott Long 	struct ada_softc *softc;
97452c9ce25SScott Long 	char tmpstr[80], tmpstr2[80];
97552c9ce25SScott Long 
97652c9ce25SScott Long 	periph = (struct cam_periph *)context;
977e3a6d3a4SAlexander Motin 
978e3a6d3a4SAlexander Motin 	/* periph was held for us when this task was enqueued */
979e3a6d3a4SAlexander Motin 	if (periph->flags & CAM_PERIPH_INVALID) {
980e3a6d3a4SAlexander Motin 		cam_periph_release(periph);
98152c9ce25SScott Long 		return;
982e3a6d3a4SAlexander Motin 	}
98352c9ce25SScott Long 
98452c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
98552c9ce25SScott Long 	snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d", periph->unit_number);
98652c9ce25SScott Long 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
98752c9ce25SScott Long 
98852c9ce25SScott Long 	sysctl_ctx_init(&softc->sysctl_ctx);
98952c9ce25SScott Long 	softc->flags |= ADA_FLAG_SCTX_INIT;
99052c9ce25SScott Long 	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
99152c9ce25SScott Long 		SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2,
99252c9ce25SScott Long 		CTLFLAG_RD, 0, tmpstr);
99352c9ce25SScott Long 	if (softc->sysctl_tree == NULL) {
99452c9ce25SScott Long 		printf("adasysctlinit: unable to allocate sysctl tree\n");
99552c9ce25SScott Long 		cam_periph_release(periph);
99652c9ce25SScott Long 		return;
99752c9ce25SScott Long 	}
99852c9ce25SScott Long 
999e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
10001ed6aaf9SAlexander Motin 		OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE,
10011ed6aaf9SAlexander Motin 		&softc->read_ahead, 0, "Enable disk read ahead.");
10021ed6aaf9SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1003e3a6d3a4SAlexander Motin 		OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE,
1004e3a6d3a4SAlexander Motin 		&softc->write_cache, 0, "Enable disk write cache.");
10055f83aee5SSteven Hartland 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
10065f83aee5SSteven Hartland 		OID_AUTO, "sort_io_queue", CTLFLAG_RW | CTLFLAG_MPSAFE,
10075f83aee5SSteven Hartland 		&softc->sort_io_queue, 0,
10085f83aee5SSteven Hartland 		"Sort IO queue to try and optimise disk access patterns");
1009e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1010e3a6d3a4SAlexander Motin 	/*
1011e3a6d3a4SAlexander Motin 	 * Add a 'door bell' sysctl which allows one to set it from userland
1012e3a6d3a4SAlexander Motin 	 * and cause something bad to happen.  For the moment, we only allow
1013e3a6d3a4SAlexander Motin 	 * whacking the next read or write.
1014e3a6d3a4SAlexander Motin 	 */
1015e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1016e3a6d3a4SAlexander Motin 		OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1017e3a6d3a4SAlexander Motin 		&softc->force_read_error, 0,
1018e3a6d3a4SAlexander Motin 		"Force a read error for the next N reads.");
1019e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1020e3a6d3a4SAlexander Motin 		OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1021e3a6d3a4SAlexander Motin 		&softc->force_write_error, 0,
1022e3a6d3a4SAlexander Motin 		"Force a write error for the next N writes.");
1023e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1024e3a6d3a4SAlexander Motin 		OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1025e3a6d3a4SAlexander Motin 		&softc->periodic_read_error, 0,
1026e3a6d3a4SAlexander Motin 		"Force a read error every N reads (don't set too low).");
1027e3a6d3a4SAlexander Motin #endif
102852c9ce25SScott Long 	cam_periph_release(periph);
102952c9ce25SScott Long }
103052c9ce25SScott Long 
1031416494d7SJustin T. Gibbs static int
1032416494d7SJustin T. Gibbs adagetattr(struct bio *bp)
1033416494d7SJustin T. Gibbs {
10346884b662SAlexander Motin 	int ret;
1035416494d7SJustin T. Gibbs 	struct cam_periph *periph;
1036416494d7SJustin T. Gibbs 
1037416494d7SJustin T. Gibbs 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
10386884b662SAlexander Motin 	cam_periph_lock(periph);
1039416494d7SJustin T. Gibbs 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
1040416494d7SJustin T. Gibbs 	    periph->path);
10416884b662SAlexander Motin 	cam_periph_unlock(periph);
1042416494d7SJustin T. Gibbs 	if (ret == 0)
1043416494d7SJustin T. Gibbs 		bp->bio_completed = bp->bio_length;
1044416494d7SJustin T. Gibbs 	return ret;
1045416494d7SJustin T. Gibbs }
1046416494d7SJustin T. Gibbs 
104752c9ce25SScott Long static cam_status
104852c9ce25SScott Long adaregister(struct cam_periph *periph, void *arg)
104952c9ce25SScott Long {
105052c9ce25SScott Long 	struct ada_softc *softc;
105152c9ce25SScott Long 	struct ccb_pathinq cpi;
105252c9ce25SScott Long 	struct ccb_getdev *cgd;
10530d307e09SAlexander Motin 	char   announce_buf[80], buf1[32];
105452c9ce25SScott Long 	struct disk_params *dp;
105552c9ce25SScott Long 	caddr_t match;
105652c9ce25SScott Long 	u_int maxio;
1057d3a460d3SAlexander Motin 	int legacy_id, quirks;
105852c9ce25SScott Long 
105952c9ce25SScott Long 	cgd = (struct ccb_getdev *)arg;
106052c9ce25SScott Long 	if (cgd == NULL) {
106152c9ce25SScott Long 		printf("adaregister: no getdev CCB, can't register device\n");
106252c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
106352c9ce25SScott Long 	}
106452c9ce25SScott Long 
106552c9ce25SScott Long 	softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF,
106652c9ce25SScott Long 	    M_NOWAIT|M_ZERO);
106752c9ce25SScott Long 
106852c9ce25SScott Long 	if (softc == NULL) {
106952c9ce25SScott Long 		printf("adaregister: Unable to probe new device. "
107052c9ce25SScott Long 		    "Unable to allocate softc\n");
107152c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
107252c9ce25SScott Long 	}
107352c9ce25SScott Long 
107452c9ce25SScott Long 	bioq_init(&softc->bio_queue);
10751c80ec0aSAlexander Motin 	bioq_init(&softc->trim_queue);
107652c9ce25SScott Long 
1077581b2e78SAlexander Motin 	if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
1078cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA))
107946f118feSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_DMA;
1080*2e1eb332SMarius Strobl 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
108152c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_48BIT;
1082*2e1eb332SMarius Strobl 		if (cgd->inq_flags & SID_DMA48)
1083*2e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_DMA48;
1084*2e1eb332SMarius Strobl 	}
108552c9ce25SScott Long 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE)
108652c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_FLUSHCACHE;
1087fd104c15SRebecca Cran 	if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT)
1088fd104c15SRebecca Cran 		softc->flags |= ADA_FLAG_CAN_POWERMGT;
1089581b2e78SAlexander Motin 	if ((cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
1090cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA) && (cgd->inq_flags & SID_CmdQue))
109152c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_NCQ;
1092581b2e78SAlexander Motin 	if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
1093581b2e78SAlexander Motin 	    (cgd->inq_flags & SID_DMA)) {
10941c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_TRIM;
10951c80ec0aSAlexander Motin 		softc->trim_max_ranges = TRIM_MAX_RANGES;
10961c80ec0aSAlexander Motin 		if (cgd->ident_data.max_dsm_blocks != 0) {
10971c80ec0aSAlexander Motin 			softc->trim_max_ranges =
10981c80ec0aSAlexander Motin 			    min(cgd->ident_data.max_dsm_blocks * 64,
10991c80ec0aSAlexander Motin 				softc->trim_max_ranges);
11001c80ec0aSAlexander Motin 		}
11011c80ec0aSAlexander Motin 	}
11021c80ec0aSAlexander Motin 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
11031c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_CFA;
110452c9ce25SScott Long 
110552c9ce25SScott Long 	periph->softc = softc;
110652c9ce25SScott Long 
110752c9ce25SScott Long 	/*
110852c9ce25SScott Long 	 * See if this device has any quirks.
110952c9ce25SScott Long 	 */
111030a4094fSAlexander Motin 	match = cam_quirkmatch((caddr_t)&cgd->ident_data,
111130a4094fSAlexander Motin 			       (caddr_t)ada_quirk_table,
111230a4094fSAlexander Motin 			       sizeof(ada_quirk_table)/sizeof(*ada_quirk_table),
111330a4094fSAlexander Motin 			       sizeof(*ada_quirk_table), ata_identify_match);
111452c9ce25SScott Long 	if (match != NULL)
111552c9ce25SScott Long 		softc->quirks = ((struct ada_quirk_entry *)match)->quirks;
111652c9ce25SScott Long 	else
111752c9ce25SScott Long 		softc->quirks = ADA_Q_NONE;
111852c9ce25SScott Long 
111952c9ce25SScott Long 	bzero(&cpi, sizeof(cpi));
112083c5d981SAlexander Motin 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
112152c9ce25SScott Long 	cpi.ccb_h.func_code = XPT_PATH_INQ;
112252c9ce25SScott Long 	xpt_action((union ccb *)&cpi);
112352c9ce25SScott Long 
112452c9ce25SScott Long 	TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph);
112552c9ce25SScott Long 
112652c9ce25SScott Long 	/*
112752c9ce25SScott Long 	 * Register this media as a disk
112852c9ce25SScott Long 	 */
1129781338b6SAlexander Motin 	(void)cam_periph_hold(periph, PRIBIO);
1130edec59d9SAlexander Motin 	cam_periph_unlock(periph);
1131d3a460d3SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1132d3a460d3SAlexander Motin 	    "kern.cam.ada.%d.quirks", periph->unit_number);
1133d3a460d3SAlexander Motin 	quirks = softc->quirks;
1134d3a460d3SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &quirks);
1135d3a460d3SAlexander Motin 	softc->quirks = quirks;
11361ed6aaf9SAlexander Motin 	softc->read_ahead = -1;
11371ed6aaf9SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
11381ed6aaf9SAlexander Motin 	    "kern.cam.ada.%d.read_ahead", periph->unit_number);
11391ed6aaf9SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead);
1140781338b6SAlexander Motin 	softc->write_cache = -1;
1141781338b6SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1142781338b6SAlexander Motin 	    "kern.cam.ada.%d.write_cache", periph->unit_number);
1143781338b6SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
11445f83aee5SSteven Hartland 	softc->sort_io_queue = -1;
1145c1bd46c2SAlexander Motin 	adagetparams(periph, cgd);
114652c9ce25SScott Long 	softc->disk = disk_alloc();
1147b8b6b5d3SAlexander Motin 	softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
1148b8b6b5d3SAlexander Motin 			  periph->unit_number, softc->params.secsize,
1149b8b6b5d3SAlexander Motin 			  DEVSTAT_ALL_SUPPORTED,
1150b8b6b5d3SAlexander Motin 			  DEVSTAT_TYPE_DIRECT |
1151b8b6b5d3SAlexander Motin 			  XPORT_DEVSTAT_TYPE(cpi.transport),
1152b8b6b5d3SAlexander Motin 			  DEVSTAT_PRIORITY_DISK);
115352c9ce25SScott Long 	softc->disk->d_open = adaopen;
115452c9ce25SScott Long 	softc->disk->d_close = adaclose;
115552c9ce25SScott Long 	softc->disk->d_strategy = adastrategy;
1156416494d7SJustin T. Gibbs 	softc->disk->d_getattr = adagetattr;
115752c9ce25SScott Long 	softc->disk->d_dump = adadump;
115852c9ce25SScott Long 	softc->disk->d_name = "ada";
115952c9ce25SScott Long 	softc->disk->d_drv1 = periph;
116052c9ce25SScott Long 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
116152c9ce25SScott Long 	if (maxio == 0)
116252c9ce25SScott Long 		maxio = DFLTPHYS;	/* traditional default */
116352c9ce25SScott Long 	else if (maxio > MAXPHYS)
116452c9ce25SScott Long 		maxio = MAXPHYS;	/* for safety */
11651c80ec0aSAlexander Motin 	if (softc->flags & ADA_FLAG_CAN_48BIT)
1166c1bd46c2SAlexander Motin 		maxio = min(maxio, 65536 * softc->params.secsize);
116752c9ce25SScott Long 	else					/* 28bit ATA command limit */
1168c1bd46c2SAlexander Motin 		maxio = min(maxio, 256 * softc->params.secsize);
116952c9ce25SScott Long 	softc->disk->d_maxsize = maxio;
117052c9ce25SScott Long 	softc->disk->d_unit = periph->unit_number;
117152c9ce25SScott Long 	softc->disk->d_flags = 0;
117252c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
117352c9ce25SScott Long 		softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
11741c80ec0aSAlexander Motin 	if ((softc->flags & ADA_FLAG_CAN_TRIM) ||
11751c80ec0aSAlexander Motin 	    ((softc->flags & ADA_FLAG_CAN_CFA) &&
11761c80ec0aSAlexander Motin 	    !(softc->flags & ADA_FLAG_CAN_48BIT)))
11771c80ec0aSAlexander Motin 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
1178abc1e60eSKonstantin Belousov 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0)
1179abc1e60eSKonstantin Belousov 		softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
118065cb6238SNathan Whitehorn 	strlcpy(softc->disk->d_descr, cgd->ident_data.model,
118165cb6238SNathan Whitehorn 	    MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
1182d50aaa6dSAndriy Gapon 	strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
1183d50aaa6dSAndriy Gapon 	    MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial)));
11848edcf694SAlexander Motin 	softc->disk->d_hba_vendor = cpi.hba_vendor;
11858edcf694SAlexander Motin 	softc->disk->d_hba_device = cpi.hba_device;
11868edcf694SAlexander Motin 	softc->disk->d_hba_subvendor = cpi.hba_subvendor;
11878edcf694SAlexander Motin 	softc->disk->d_hba_subdevice = cpi.hba_subdevice;
118852c9ce25SScott Long 
118952c9ce25SScott Long 	softc->disk->d_sectorsize = softc->params.secsize;
1190c1bd46c2SAlexander Motin 	softc->disk->d_mediasize = (off_t)softc->params.sectors *
1191c1bd46c2SAlexander Motin 	    softc->params.secsize;
1192ce8332d4SAlexander Motin 	if (ata_physical_sector_size(&cgd->ident_data) !=
1193ce8332d4SAlexander Motin 	    softc->params.secsize) {
1194ce8332d4SAlexander Motin 		softc->disk->d_stripesize =
1195ce8332d4SAlexander Motin 		    ata_physical_sector_size(&cgd->ident_data);
1196ce8332d4SAlexander Motin 		softc->disk->d_stripeoffset = (softc->disk->d_stripesize -
1197ce8332d4SAlexander Motin 		    ata_logical_sector_offset(&cgd->ident_data)) %
1198ce8332d4SAlexander Motin 		    softc->disk->d_stripesize;
1199d3a460d3SAlexander Motin 	} else if (softc->quirks & ADA_Q_4K) {
1200d3a460d3SAlexander Motin 		softc->disk->d_stripesize = 4096;
1201d3a460d3SAlexander Motin 		softc->disk->d_stripeoffset = 0;
1202ce8332d4SAlexander Motin 	}
120352c9ce25SScott Long 	softc->disk->d_fwsectors = softc->params.secs_per_track;
120452c9ce25SScott Long 	softc->disk->d_fwheads = softc->params.heads;
12054461491bSMarius Strobl 	ata_disk_firmware_geom_adjust(softc->disk);
120652c9ce25SScott Long 
12070d307e09SAlexander Motin 	if (ada_legacy_aliases) {
12080d307e09SAlexander Motin #ifdef ATA_STATIC_ID
12090d307e09SAlexander Motin 		legacy_id = xpt_path_legacy_ata_id(periph->path);
12100d307e09SAlexander Motin #else
12110d307e09SAlexander Motin 		legacy_id = softc->disk->d_unit;
12120d307e09SAlexander Motin #endif
12130d307e09SAlexander Motin 		if (legacy_id >= 0) {
12140d307e09SAlexander Motin 			snprintf(announce_buf, sizeof(announce_buf),
12150d307e09SAlexander Motin 			    "kern.devalias.%s%d",
12160d307e09SAlexander Motin 			    softc->disk->d_name, softc->disk->d_unit);
12170d307e09SAlexander Motin 			snprintf(buf1, sizeof(buf1),
12180d307e09SAlexander Motin 			    "ad%d", legacy_id);
12190d307e09SAlexander Motin 			setenv(announce_buf, buf1);
12200d307e09SAlexander Motin 		}
12210d307e09SAlexander Motin 	} else
12220d307e09SAlexander Motin 		legacy_id = -1;
122352c9ce25SScott Long 	disk_create(softc->disk, DISK_VERSION);
1224edec59d9SAlexander Motin 	cam_periph_lock(periph);
1225781338b6SAlexander Motin 	cam_periph_unhold(periph);
122652c9ce25SScott Long 
122752c9ce25SScott Long 	dp = &softc->params;
122852c9ce25SScott Long 	snprintf(announce_buf, sizeof(announce_buf),
122952c9ce25SScott Long 		"%juMB (%ju %u byte sectors: %dH %dS/T %dC)",
123052c9ce25SScott Long 		(uintmax_t)(((uintmax_t)dp->secsize *
123152c9ce25SScott Long 		dp->sectors) / (1024*1024)),
123252c9ce25SScott Long 		(uintmax_t)dp->sectors,
123352c9ce25SScott Long 		dp->secsize, dp->heads,
123452c9ce25SScott Long 		dp->secs_per_track, dp->cylinders);
123552c9ce25SScott Long 	xpt_announce_periph(periph, announce_buf);
12360d307e09SAlexander Motin 	if (legacy_id >= 0)
12370d307e09SAlexander Motin 		printf("%s%d: Previously was known as ad%d\n",
12380d307e09SAlexander Motin 		       periph->periph_name, periph->unit_number, legacy_id);
1239e3a6d3a4SAlexander Motin 
1240e3a6d3a4SAlexander Motin 	/*
1241e3a6d3a4SAlexander Motin 	 * Create our sysctl variables, now that we know
1242e3a6d3a4SAlexander Motin 	 * we have successfully attached.
1243e3a6d3a4SAlexander Motin 	 */
1244e3a6d3a4SAlexander Motin 	cam_periph_acquire(periph);
1245e3a6d3a4SAlexander Motin 	taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
1246e3a6d3a4SAlexander Motin 
124752c9ce25SScott Long 	/*
124852c9ce25SScott Long 	 * Add async callbacks for bus reset and
124952c9ce25SScott Long 	 * bus device reset calls.  I don't bother
125052c9ce25SScott Long 	 * checking if this fails as, in most cases,
125152c9ce25SScott Long 	 * the system will function just fine without
125252c9ce25SScott Long 	 * them and the only alternative would be to
125352c9ce25SScott Long 	 * not attach the device on failure.
125452c9ce25SScott Long 	 */
12553089bb2eSAlexander Motin 	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
1256581b2e78SAlexander Motin 	    AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED,
1257581b2e78SAlexander Motin 	    adaasync, periph, periph->path);
125852c9ce25SScott Long 
125952c9ce25SScott Long 	/*
126052c9ce25SScott Long 	 * Schedule a periodic event to occasionally send an
126152c9ce25SScott Long 	 * ordered tag to a device.
126252c9ce25SScott Long 	 */
126352c9ce25SScott Long 	callout_init_mtx(&softc->sendordered_c, periph->sim->mtx, 0);
126452c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
126547bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
126652c9ce25SScott Long 	    adasendorderedtag, softc);
126752c9ce25SScott Long 
12681ed6aaf9SAlexander Motin 	if (ADA_RA >= 0 &&
12691ed6aaf9SAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) {
12701ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_RAHEAD;
12711ed6aaf9SAlexander Motin 		cam_periph_acquire(periph);
12721ed6aaf9SAlexander Motin 		cam_freeze_devq_arg(periph->path,
12731ed6aaf9SAlexander Motin 		    RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
12741ed6aaf9SAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
12751ed6aaf9SAlexander Motin 	} else if (ADA_WC >= 0 &&
1276f513d14cSAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
1277f513d14cSAlexander Motin 		softc->state = ADA_STATE_WCACHE;
1278f513d14cSAlexander Motin 		cam_periph_acquire(periph);
1279f513d14cSAlexander Motin 		cam_freeze_devq_arg(periph->path,
1280f513d14cSAlexander Motin 		    RELSIM_RELEASE_RUNLEVEL, CAM_RL_DEV + 1);
1281f513d14cSAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
1282f513d14cSAlexander Motin 	} else
1283f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1284f513d14cSAlexander Motin 
128552c9ce25SScott Long 	return(CAM_REQ_CMP);
128652c9ce25SScott Long }
128752c9ce25SScott Long 
128852c9ce25SScott Long static void
128952c9ce25SScott Long adastart(struct cam_periph *periph, union ccb *start_ccb)
129052c9ce25SScott Long {
129146f118feSAlexander Motin 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
129246f118feSAlexander Motin 	struct ccb_ataio *ataio = &start_ccb->ataio;
129352c9ce25SScott Long 
1294fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n"));
1295fddde2b8SAlexander Motin 
129652c9ce25SScott Long 	switch (softc->state) {
129752c9ce25SScott Long 	case ADA_STATE_NORMAL:
129852c9ce25SScott Long 	{
129952c9ce25SScott Long 		struct bio *bp;
13001c80ec0aSAlexander Motin 		u_int8_t tag_code;
130152c9ce25SScott Long 
13021c80ec0aSAlexander Motin 		/* Execute immediate CCB if waiting. */
130352c9ce25SScott Long 		if (periph->immediate_priority <= periph->pinfo.priority) {
1304fddde2b8SAlexander Motin 			CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
130552c9ce25SScott Long 					("queuing for immediate ccb\n"));
130652c9ce25SScott Long 			start_ccb->ccb_h.ccb_state = ADA_CCB_WAITING;
130752c9ce25SScott Long 			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
130852c9ce25SScott Long 					  periph_links.sle);
130952c9ce25SScott Long 			periph->immediate_priority = CAM_PRIORITY_NONE;
131052c9ce25SScott Long 			wakeup(&periph->ccb_list);
13111c80ec0aSAlexander Motin 			/* Have more work to do, so ensure we stay scheduled */
13121c80ec0aSAlexander Motin 			adaschedule(periph);
13131c80ec0aSAlexander Motin 			break;
13141c80ec0aSAlexander Motin 		}
13151c80ec0aSAlexander Motin 		/* Run TRIM if not running yet. */
13161c80ec0aSAlexander Motin 		if (!softc->trim_running &&
13171c80ec0aSAlexander Motin 		    (bp = bioq_first(&softc->trim_queue)) != 0) {
13181c80ec0aSAlexander Motin 			struct trim_request *req = &softc->trim_req;
13191c80ec0aSAlexander Motin 			struct bio *bp1;
132037ddbd16SAlexander Motin 			uint64_t lastlba = (uint64_t)-1;
132137ddbd16SAlexander Motin 			int bps = 0, c, lastcount = 0, off, ranges = 0;
132252c9ce25SScott Long 
13231c80ec0aSAlexander Motin 			softc->trim_running = 1;
13241c80ec0aSAlexander Motin 			bzero(req, sizeof(*req));
13251c80ec0aSAlexander Motin 			bp1 = bp;
13261c80ec0aSAlexander Motin 			do {
13271c80ec0aSAlexander Motin 				uint64_t lba = bp1->bio_pblkno;
13281c80ec0aSAlexander Motin 				int count = bp1->bio_bcount /
13291c80ec0aSAlexander Motin 				    softc->params.secsize;
13301c80ec0aSAlexander Motin 
13311c80ec0aSAlexander Motin 				bioq_remove(&softc->trim_queue, bp1);
13321c80ec0aSAlexander Motin 
133337ddbd16SAlexander Motin 				/* Try to extend the previous range. */
133437ddbd16SAlexander Motin 				if (lba == lastlba) {
133537ddbd16SAlexander Motin 					c = min(count, 0xffff - lastcount);
133637ddbd16SAlexander Motin 					lastcount += c;
133737ddbd16SAlexander Motin 					off = (ranges - 1) * 8;
133837ddbd16SAlexander Motin 					req->data[off + 6] = lastcount & 0xff;
133937ddbd16SAlexander Motin 					req->data[off + 7] =
134037ddbd16SAlexander Motin 					    (lastcount >> 8) & 0xff;
134137ddbd16SAlexander Motin 					count -= c;
134237ddbd16SAlexander Motin 					lba += c;
134337ddbd16SAlexander Motin 				}
134437ddbd16SAlexander Motin 
134537ddbd16SAlexander Motin 				while (count > 0) {
134637ddbd16SAlexander Motin 					c = min(count, 0xffff);
134737ddbd16SAlexander Motin 					off = ranges * 8;
13481c80ec0aSAlexander Motin 					req->data[off + 0] = lba & 0xff;
13491c80ec0aSAlexander Motin 					req->data[off + 1] = (lba >> 8) & 0xff;
13501c80ec0aSAlexander Motin 					req->data[off + 2] = (lba >> 16) & 0xff;
13511c80ec0aSAlexander Motin 					req->data[off + 3] = (lba >> 24) & 0xff;
13521c80ec0aSAlexander Motin 					req->data[off + 4] = (lba >> 32) & 0xff;
13531c80ec0aSAlexander Motin 					req->data[off + 5] = (lba >> 40) & 0xff;
13541c80ec0aSAlexander Motin 					req->data[off + 6] = c & 0xff;
13551c80ec0aSAlexander Motin 					req->data[off + 7] = (c >> 8) & 0xff;
13561c80ec0aSAlexander Motin 					lba += c;
13571c80ec0aSAlexander Motin 					count -= c;
135837ddbd16SAlexander Motin 					lastcount = c;
13591c80ec0aSAlexander Motin 					ranges++;
13601c80ec0aSAlexander Motin 				}
136137ddbd16SAlexander Motin 				lastlba = lba;
13621c80ec0aSAlexander Motin 				req->bps[bps++] = bp1;
13631c80ec0aSAlexander Motin 				bp1 = bioq_first(&softc->trim_queue);
136437ddbd16SAlexander Motin 				if (bps >= TRIM_MAX_BIOS ||
136537ddbd16SAlexander Motin 				    bp1 == NULL ||
13661c80ec0aSAlexander Motin 				    bp1->bio_bcount / softc->params.secsize >
13671c80ec0aSAlexander Motin 				    (softc->trim_max_ranges - ranges) * 0xffff)
13681c80ec0aSAlexander Motin 					break;
13691c80ec0aSAlexander Motin 			} while (1);
13701c80ec0aSAlexander Motin 			cam_fill_ataio(ataio,
13711c80ec0aSAlexander Motin 			    ada_retry_count,
13721c80ec0aSAlexander Motin 			    adadone,
13731c80ec0aSAlexander Motin 			    CAM_DIR_OUT,
13741c80ec0aSAlexander Motin 			    0,
13751c80ec0aSAlexander Motin 			    req->data,
13761c80ec0aSAlexander Motin 			    ((ranges + 63) / 64) * 512,
13771c80ec0aSAlexander Motin 			    ada_default_timeout * 1000);
13781c80ec0aSAlexander Motin 			ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT,
13791c80ec0aSAlexander Motin 			    ATA_DSM_TRIM, 0, (ranges + 63) / 64);
13801c80ec0aSAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM;
13811c80ec0aSAlexander Motin 			goto out;
13821c80ec0aSAlexander Motin 		}
13831c80ec0aSAlexander Motin 		/* Run regular command. */
13841c80ec0aSAlexander Motin 		bp = bioq_first(&softc->bio_queue);
13851c80ec0aSAlexander Motin 		if (bp == NULL) {
13861c80ec0aSAlexander Motin 			xpt_release_ccb(start_ccb);
13871c80ec0aSAlexander Motin 			break;
13881c80ec0aSAlexander Motin 		}
138952c9ce25SScott Long 		bioq_remove(&softc->bio_queue, bp);
139052c9ce25SScott Long 
1391f03f7a0cSJustin T. Gibbs 		if ((bp->bio_flags & BIO_ORDERED) != 0
1392f03f7a0cSJustin T. Gibbs 		 || (softc->flags & ADA_FLAG_NEED_OTAG) != 0) {
139352c9ce25SScott Long 			softc->flags &= ~ADA_FLAG_NEED_OTAG;
139452c9ce25SScott Long 			softc->ordered_tag_count++;
139546f118feSAlexander Motin 			tag_code = 0;
139652c9ce25SScott Long 		} else {
139746f118feSAlexander Motin 			tag_code = 1;
139852c9ce25SScott Long 		}
139952c9ce25SScott Long 		switch (bp->bio_cmd) {
140052c9ce25SScott Long 		case BIO_READ:
140152c9ce25SScott Long 		case BIO_WRITE:
140252c9ce25SScott Long 		{
140352c9ce25SScott Long 			uint64_t lba = bp->bio_pblkno;
140452c9ce25SScott Long 			uint16_t count = bp->bio_bcount / softc->params.secsize;
1405e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1406e3a6d3a4SAlexander Motin 			int fail = 0;
140752c9ce25SScott Long 
1408e3a6d3a4SAlexander Motin 			/*
1409e3a6d3a4SAlexander Motin 			 * Support the failure ioctls.  If the command is a
1410e3a6d3a4SAlexander Motin 			 * read, and there are pending forced read errors, or
1411e3a6d3a4SAlexander Motin 			 * if a write and pending write errors, then fail this
1412e3a6d3a4SAlexander Motin 			 * operation with EIO.  This is useful for testing
1413e3a6d3a4SAlexander Motin 			 * purposes.  Also, support having every Nth read fail.
1414e3a6d3a4SAlexander Motin 			 *
1415e3a6d3a4SAlexander Motin 			 * This is a rather blunt tool.
1416e3a6d3a4SAlexander Motin 			 */
1417e3a6d3a4SAlexander Motin 			if (bp->bio_cmd == BIO_READ) {
1418e3a6d3a4SAlexander Motin 				if (softc->force_read_error) {
1419e3a6d3a4SAlexander Motin 					softc->force_read_error--;
1420e3a6d3a4SAlexander Motin 					fail = 1;
1421e3a6d3a4SAlexander Motin 				}
1422e3a6d3a4SAlexander Motin 				if (softc->periodic_read_error > 0) {
1423e3a6d3a4SAlexander Motin 					if (++softc->periodic_read_count >=
1424e3a6d3a4SAlexander Motin 					    softc->periodic_read_error) {
1425e3a6d3a4SAlexander Motin 						softc->periodic_read_count = 0;
1426e3a6d3a4SAlexander Motin 						fail = 1;
1427e3a6d3a4SAlexander Motin 					}
1428e3a6d3a4SAlexander Motin 				}
1429e3a6d3a4SAlexander Motin 			} else {
1430e3a6d3a4SAlexander Motin 				if (softc->force_write_error) {
1431e3a6d3a4SAlexander Motin 					softc->force_write_error--;
1432e3a6d3a4SAlexander Motin 					fail = 1;
1433e3a6d3a4SAlexander Motin 				}
1434e3a6d3a4SAlexander Motin 			}
1435e3a6d3a4SAlexander Motin 			if (fail) {
1436e3a6d3a4SAlexander Motin 				bp->bio_error = EIO;
1437e3a6d3a4SAlexander Motin 				bp->bio_flags |= BIO_ERROR;
1438e3a6d3a4SAlexander Motin 				biodone(bp);
1439e3a6d3a4SAlexander Motin 				xpt_release_ccb(start_ccb);
1440e3a6d3a4SAlexander Motin 				adaschedule(periph);
1441e3a6d3a4SAlexander Motin 				return;
1442e3a6d3a4SAlexander Motin 			}
1443e3a6d3a4SAlexander Motin #endif
1444abc1e60eSKonstantin Belousov 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
1445abc1e60eSKonstantin Belousov 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
1446abc1e60eSKonstantin Belousov 			    PAGE_SIZE == bp->bio_ma_n,
1447abc1e60eSKonstantin Belousov 			    ("Short bio %p", bp));
144852c9ce25SScott Long 			cam_fill_ataio(ataio,
144952c9ce25SScott Long 			    ada_retry_count,
145052c9ce25SScott Long 			    adadone,
1451abc1e60eSKonstantin Belousov 			    (bp->bio_cmd == BIO_READ ? CAM_DIR_IN :
1452abc1e60eSKonstantin Belousov 				CAM_DIR_OUT) | ((bp->bio_flags & BIO_UNMAPPED)
1453abc1e60eSKonstantin Belousov 				!= 0 ? CAM_DATA_BIO : 0),
145452c9ce25SScott Long 			    tag_code,
1455abc1e60eSKonstantin Belousov 			    ((bp->bio_flags & BIO_UNMAPPED) != 0) ? (void *)bp :
145652c9ce25SScott Long 				bp->bio_data,
145752c9ce25SScott Long 			    bp->bio_bcount,
145852c9ce25SScott Long 			    ada_default_timeout*1000);
145952c9ce25SScott Long 
146046f118feSAlexander Motin 			if ((softc->flags & ADA_FLAG_CAN_NCQ) && tag_code) {
146152c9ce25SScott Long 				if (bp->bio_cmd == BIO_READ) {
146252c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_READ_FPDMA_QUEUED,
146352c9ce25SScott Long 					    lba, count);
146452c9ce25SScott Long 				} else {
146552c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_WRITE_FPDMA_QUEUED,
146652c9ce25SScott Long 					    lba, count);
146752c9ce25SScott Long 				}
146852c9ce25SScott Long 			} else if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
146952c9ce25SScott Long 			    (lba + count >= ATA_MAX_28BIT_LBA ||
147046f118feSAlexander Motin 			    count > 256)) {
1471*2e1eb332SMarius Strobl 				if (softc->flags & ADA_FLAG_CAN_DMA48) {
147252c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
147352c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_READ_DMA48,
147452c9ce25SScott Long 						    0, lba, count);
147552c9ce25SScott Long 					} else {
147652c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_WRITE_DMA48,
147752c9ce25SScott Long 						    0, lba, count);
147852c9ce25SScott Long 					}
147952c9ce25SScott Long 				} else {
148052c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
148146f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_READ_MUL48,
148246f118feSAlexander Motin 						    0, lba, count);
148346f118feSAlexander Motin 					} else {
148446f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_WRITE_MUL48,
148546f118feSAlexander Motin 						    0, lba, count);
148646f118feSAlexander Motin 					}
148746f118feSAlexander Motin 				}
148846f118feSAlexander Motin 			} else {
148946f118feSAlexander Motin 				if (count == 256)
149046f118feSAlexander Motin 					count = 0;
149146f118feSAlexander Motin 				if (softc->flags & ADA_FLAG_CAN_DMA) {
149246f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
14937606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_DMA,
149452c9ce25SScott Long 						    0, lba, count);
149552c9ce25SScott Long 					} else {
14967606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_DMA,
149752c9ce25SScott Long 						    0, lba, count);
149852c9ce25SScott Long 					}
149946f118feSAlexander Motin 				} else {
150046f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
150146f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_MUL,
150246f118feSAlexander Motin 						    0, lba, count);
150346f118feSAlexander Motin 					} else {
150446f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_MUL,
150546f118feSAlexander Motin 						    0, lba, count);
150646f118feSAlexander Motin 					}
150746f118feSAlexander Motin 				}
150852c9ce25SScott Long 			}
150952c9ce25SScott Long 			break;
15101c80ec0aSAlexander Motin 		}
15111c80ec0aSAlexander Motin 		case BIO_DELETE:
15121c80ec0aSAlexander Motin 		{
15131c80ec0aSAlexander Motin 			uint64_t lba = bp->bio_pblkno;
15141c80ec0aSAlexander Motin 			uint16_t count = bp->bio_bcount / softc->params.secsize;
15151c80ec0aSAlexander Motin 
15161c80ec0aSAlexander Motin 			cam_fill_ataio(ataio,
15171c80ec0aSAlexander Motin 			    ada_retry_count,
15181c80ec0aSAlexander Motin 			    adadone,
15191c80ec0aSAlexander Motin 			    CAM_DIR_NONE,
15201c80ec0aSAlexander Motin 			    0,
15211c80ec0aSAlexander Motin 			    NULL,
15221c80ec0aSAlexander Motin 			    0,
15231c80ec0aSAlexander Motin 			    ada_default_timeout*1000);
15241c80ec0aSAlexander Motin 
15251c80ec0aSAlexander Motin 			if (count >= 256)
15261c80ec0aSAlexander Motin 				count = 0;
15271c80ec0aSAlexander Motin 			ata_28bit_cmd(ataio, ATA_CFA_ERASE, 0, lba, count);
15281c80ec0aSAlexander Motin 			break;
15291c80ec0aSAlexander Motin 		}
153052c9ce25SScott Long 		case BIO_FLUSH:
153152c9ce25SScott Long 			cam_fill_ataio(ataio,
153252c9ce25SScott Long 			    1,
153352c9ce25SScott Long 			    adadone,
153452c9ce25SScott Long 			    CAM_DIR_NONE,
153546f118feSAlexander Motin 			    0,
153652c9ce25SScott Long 			    NULL,
153752c9ce25SScott Long 			    0,
153852c9ce25SScott Long 			    ada_default_timeout*1000);
153952c9ce25SScott Long 
154052c9ce25SScott Long 			if (softc->flags & ADA_FLAG_CAN_48BIT)
154152c9ce25SScott Long 				ata_48bit_cmd(ataio, ATA_FLUSHCACHE48, 0, 0, 0);
154252c9ce25SScott Long 			else
15437606b445SAlexander Motin 				ata_28bit_cmd(ataio, ATA_FLUSHCACHE, 0, 0, 0);
154452c9ce25SScott Long 			break;
154552c9ce25SScott Long 		}
154652c9ce25SScott Long 		start_ccb->ccb_h.ccb_state = ADA_CCB_BUFFER_IO;
15471c80ec0aSAlexander Motin out:
154852c9ce25SScott Long 		start_ccb->ccb_h.ccb_bp = bp;
1549c1bd46c2SAlexander Motin 		softc->outstanding_cmds++;
155052c9ce25SScott Long 		xpt_action(start_ccb);
155152c9ce25SScott Long 
15521c80ec0aSAlexander Motin 		/* May have more work to do, so ensure we stay scheduled */
15531c80ec0aSAlexander Motin 		adaschedule(periph);
155452c9ce25SScott Long 		break;
155552c9ce25SScott Long 	}
15561ed6aaf9SAlexander Motin 	case ADA_STATE_RAHEAD:
1557f513d14cSAlexander Motin 	case ADA_STATE_WCACHE:
1558f513d14cSAlexander Motin 	{
15591ed6aaf9SAlexander Motin 		if (softc->flags & ADA_FLAG_PACK_INVALID) {
15601ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_NORMAL;
15611ed6aaf9SAlexander Motin 			xpt_release_ccb(start_ccb);
15621ed6aaf9SAlexander Motin 			cam_release_devq(periph->path,
15631ed6aaf9SAlexander Motin 			    RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_DEV + 1, FALSE);
15641ed6aaf9SAlexander Motin 			adaschedule(periph);
15651ed6aaf9SAlexander Motin 			cam_periph_release_locked(periph);
15661ed6aaf9SAlexander Motin 			return;
15671ed6aaf9SAlexander Motin 		}
15681ed6aaf9SAlexander Motin 
1569f513d14cSAlexander Motin 		cam_fill_ataio(ataio,
1570f513d14cSAlexander Motin 		    1,
1571f513d14cSAlexander Motin 		    adadone,
1572f513d14cSAlexander Motin 		    CAM_DIR_NONE,
1573f513d14cSAlexander Motin 		    0,
1574f513d14cSAlexander Motin 		    NULL,
1575f513d14cSAlexander Motin 		    0,
1576f513d14cSAlexander Motin 		    ada_default_timeout*1000);
1577f513d14cSAlexander Motin 
15781ed6aaf9SAlexander Motin 		if (softc->state == ADA_STATE_RAHEAD) {
15791ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ?
15801ed6aaf9SAlexander Motin 			    ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0);
15811ed6aaf9SAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD;
15821ed6aaf9SAlexander Motin 		} else {
15831ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ?
1584f513d14cSAlexander Motin 			    ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0);
1585f513d14cSAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE;
15861ed6aaf9SAlexander Motin 		}
1587f513d14cSAlexander Motin 		xpt_action(start_ccb);
1588f513d14cSAlexander Motin 		break;
1589f513d14cSAlexander Motin 	}
159052c9ce25SScott Long 	}
159152c9ce25SScott Long }
159252c9ce25SScott Long 
159352c9ce25SScott Long static void
159452c9ce25SScott Long adadone(struct cam_periph *periph, union ccb *done_ccb)
159552c9ce25SScott Long {
159652c9ce25SScott Long 	struct ada_softc *softc;
159752c9ce25SScott Long 	struct ccb_ataio *ataio;
15981ed6aaf9SAlexander Motin 	struct ccb_getdev *cgd;
159952c9ce25SScott Long 
160052c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
160152c9ce25SScott Long 	ataio = &done_ccb->ataio;
1602fddde2b8SAlexander Motin 
1603fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adadone\n"));
1604fddde2b8SAlexander Motin 
160552c9ce25SScott Long 	switch (ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK) {
160652c9ce25SScott Long 	case ADA_CCB_BUFFER_IO:
16071c80ec0aSAlexander Motin 	case ADA_CCB_TRIM:
160852c9ce25SScott Long 	{
160952c9ce25SScott Long 		struct bio *bp;
161052c9ce25SScott Long 
161152c9ce25SScott Long 		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
161252c9ce25SScott Long 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
161352c9ce25SScott Long 			int error;
161452c9ce25SScott Long 
161546f118feSAlexander Motin 			error = adaerror(done_ccb, 0, 0);
161652c9ce25SScott Long 			if (error == ERESTART) {
161746f118feSAlexander Motin 				/* A retry was scheduled, so just return. */
161852c9ce25SScott Long 				return;
161952c9ce25SScott Long 			}
162052c9ce25SScott Long 			if (error != 0) {
1621c36bb43cSAlexander Motin 				if (error == ENXIO &&
1622c36bb43cSAlexander Motin 				    (softc->flags & ADA_FLAG_PACK_INVALID) == 0) {
162352c9ce25SScott Long 					/*
162452c9ce25SScott Long 					 * Catastrophic error.  Mark our pack as
162552c9ce25SScott Long 					 * invalid.
162652c9ce25SScott Long 					 */
162752c9ce25SScott Long 					/*
162852c9ce25SScott Long 					 * XXX See if this is really a media
162952c9ce25SScott Long 					 * XXX change first?
163052c9ce25SScott Long 					 */
163152c9ce25SScott Long 					xpt_print(periph->path,
163252c9ce25SScott Long 					    "Invalidating pack\n");
163352c9ce25SScott Long 					softc->flags |= ADA_FLAG_PACK_INVALID;
163452c9ce25SScott Long 				}
163552c9ce25SScott Long 				bp->bio_error = error;
163652c9ce25SScott Long 				bp->bio_resid = bp->bio_bcount;
163752c9ce25SScott Long 				bp->bio_flags |= BIO_ERROR;
163852c9ce25SScott Long 			} else {
163952c9ce25SScott Long 				bp->bio_resid = ataio->resid;
164052c9ce25SScott Long 				bp->bio_error = 0;
164152c9ce25SScott Long 				if (bp->bio_resid != 0)
164252c9ce25SScott Long 					bp->bio_flags |= BIO_ERROR;
164352c9ce25SScott Long 			}
164452c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
164552c9ce25SScott Long 				cam_release_devq(done_ccb->ccb_h.path,
164652c9ce25SScott Long 						 /*relsim_flags*/0,
164752c9ce25SScott Long 						 /*reduction*/0,
164852c9ce25SScott Long 						 /*timeout*/0,
164952c9ce25SScott Long 						 /*getcount_only*/0);
165052c9ce25SScott Long 		} else {
165152c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
165252c9ce25SScott Long 				panic("REQ_CMP with QFRZN");
165352c9ce25SScott Long 			bp->bio_resid = ataio->resid;
165452c9ce25SScott Long 			if (ataio->resid > 0)
165552c9ce25SScott Long 				bp->bio_flags |= BIO_ERROR;
165652c9ce25SScott Long 		}
165752c9ce25SScott Long 		softc->outstanding_cmds--;
165852c9ce25SScott Long 		if (softc->outstanding_cmds == 0)
165952c9ce25SScott Long 			softc->flags |= ADA_FLAG_WENT_IDLE;
16601c80ec0aSAlexander Motin 		if ((ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK) ==
16611c80ec0aSAlexander Motin 		    ADA_CCB_TRIM) {
16621c80ec0aSAlexander Motin 			struct trim_request *req =
16631c80ec0aSAlexander Motin 			    (struct trim_request *)ataio->data_ptr;
16641c80ec0aSAlexander Motin 			int i;
166552c9ce25SScott Long 
166637ddbd16SAlexander Motin 			for (i = 1; i < TRIM_MAX_BIOS && req->bps[i]; i++) {
16671c80ec0aSAlexander Motin 				struct bio *bp1 = req->bps[i];
16681c80ec0aSAlexander Motin 
16691c80ec0aSAlexander Motin 				bp1->bio_resid = bp->bio_resid;
16701c80ec0aSAlexander Motin 				bp1->bio_error = bp->bio_error;
16711c80ec0aSAlexander Motin 				if (bp->bio_flags & BIO_ERROR)
16721c80ec0aSAlexander Motin 					bp1->bio_flags |= BIO_ERROR;
16731c80ec0aSAlexander Motin 				biodone(bp1);
16741c80ec0aSAlexander Motin 			}
16751c80ec0aSAlexander Motin 			softc->trim_running = 0;
16761c80ec0aSAlexander Motin 			biodone(bp);
16771c80ec0aSAlexander Motin 			adaschedule(periph);
16781c80ec0aSAlexander Motin 		} else
167952c9ce25SScott Long 			biodone(bp);
168052c9ce25SScott Long 		break;
168152c9ce25SScott Long 	}
16821ed6aaf9SAlexander Motin 	case ADA_CCB_RAHEAD:
16831ed6aaf9SAlexander Motin 	{
16841ed6aaf9SAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
16851ed6aaf9SAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
16861ed6aaf9SAlexander Motin 				return;
16871ed6aaf9SAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
16881ed6aaf9SAlexander Motin 				cam_release_devq(done_ccb->ccb_h.path,
16891ed6aaf9SAlexander Motin 				    /*relsim_flags*/0,
16901ed6aaf9SAlexander Motin 				    /*reduction*/0,
16911ed6aaf9SAlexander Motin 				    /*timeout*/0,
16921ed6aaf9SAlexander Motin 				    /*getcount_only*/0);
16931ed6aaf9SAlexander Motin 			}
16941ed6aaf9SAlexander Motin 		}
16951ed6aaf9SAlexander Motin 
16961ed6aaf9SAlexander Motin 		/*
16971ed6aaf9SAlexander Motin 		 * Since our peripheral may be invalidated by an error
16981ed6aaf9SAlexander Motin 		 * above or an external event, we must release our CCB
16991ed6aaf9SAlexander Motin 		 * before releasing the reference on the peripheral.
17001ed6aaf9SAlexander Motin 		 * The peripheral will only go away once the last reference
17011ed6aaf9SAlexander Motin 		 * is removed, and we need it around for the CCB release
17021ed6aaf9SAlexander Motin 		 * operation.
17031ed6aaf9SAlexander Motin 		 */
17041ed6aaf9SAlexander Motin 		cgd = (struct ccb_getdev *)done_ccb;
17051ed6aaf9SAlexander Motin 		xpt_setup_ccb(&cgd->ccb_h, periph->path, CAM_PRIORITY_NORMAL);
17061ed6aaf9SAlexander Motin 		cgd->ccb_h.func_code = XPT_GDEV_TYPE;
17071ed6aaf9SAlexander Motin 		xpt_action((union ccb *)cgd);
17081ed6aaf9SAlexander Motin 		if (ADA_WC >= 0 &&
17091ed6aaf9SAlexander Motin 		    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
17101ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_WCACHE;
17111ed6aaf9SAlexander Motin 			xpt_release_ccb(done_ccb);
17121ed6aaf9SAlexander Motin 			xpt_schedule(periph, CAM_PRIORITY_DEV);
17131ed6aaf9SAlexander Motin 			return;
17141ed6aaf9SAlexander Motin 		}
17151ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_NORMAL;
17161ed6aaf9SAlexander Motin 		xpt_release_ccb(done_ccb);
17171ed6aaf9SAlexander Motin 		cam_release_devq(periph->path,
17181ed6aaf9SAlexander Motin 		    RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_DEV + 1, FALSE);
17191ed6aaf9SAlexander Motin 		adaschedule(periph);
17201ed6aaf9SAlexander Motin 		cam_periph_release_locked(periph);
17211ed6aaf9SAlexander Motin 		return;
17221ed6aaf9SAlexander Motin 	}
1723f513d14cSAlexander Motin 	case ADA_CCB_WCACHE:
1724f513d14cSAlexander Motin 	{
1725f513d14cSAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
1726f513d14cSAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
1727f513d14cSAlexander Motin 				return;
1728f513d14cSAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
1729f513d14cSAlexander Motin 				cam_release_devq(done_ccb->ccb_h.path,
1730f513d14cSAlexander Motin 				    /*relsim_flags*/0,
1731f513d14cSAlexander Motin 				    /*reduction*/0,
1732f513d14cSAlexander Motin 				    /*timeout*/0,
1733f513d14cSAlexander Motin 				    /*getcount_only*/0);
1734f513d14cSAlexander Motin 			}
1735f513d14cSAlexander Motin 		}
1736f513d14cSAlexander Motin 
1737f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1738f513d14cSAlexander Motin 		/*
1739f513d14cSAlexander Motin 		 * Since our peripheral may be invalidated by an error
1740f513d14cSAlexander Motin 		 * above or an external event, we must release our CCB
1741f513d14cSAlexander Motin 		 * before releasing the reference on the peripheral.
1742f513d14cSAlexander Motin 		 * The peripheral will only go away once the last reference
1743f513d14cSAlexander Motin 		 * is removed, and we need it around for the CCB release
1744f513d14cSAlexander Motin 		 * operation.
1745f513d14cSAlexander Motin 		 */
1746f513d14cSAlexander Motin 		xpt_release_ccb(done_ccb);
1747f513d14cSAlexander Motin 		cam_release_devq(periph->path,
1748f513d14cSAlexander Motin 		    RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_DEV + 1, FALSE);
1749f513d14cSAlexander Motin 		adaschedule(periph);
1750f513d14cSAlexander Motin 		cam_periph_release_locked(periph);
1751f513d14cSAlexander Motin 		return;
1752f513d14cSAlexander Motin 	}
175352c9ce25SScott Long 	case ADA_CCB_WAITING:
175452c9ce25SScott Long 	{
175552c9ce25SScott Long 		/* Caller will release the CCB */
175652c9ce25SScott Long 		wakeup(&done_ccb->ccb_h.cbfcnp);
175752c9ce25SScott Long 		return;
175852c9ce25SScott Long 	}
175952c9ce25SScott Long 	case ADA_CCB_DUMP:
176052c9ce25SScott Long 		/* No-op.  We're polling */
176152c9ce25SScott Long 		return;
176252c9ce25SScott Long 	default:
176352c9ce25SScott Long 		break;
176452c9ce25SScott Long 	}
176552c9ce25SScott Long 	xpt_release_ccb(done_ccb);
176652c9ce25SScott Long }
176752c9ce25SScott Long 
176852c9ce25SScott Long static int
176952c9ce25SScott Long adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
177052c9ce25SScott Long {
177152c9ce25SScott Long 
1772a9b8edb1SAlexander Motin 	return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
177352c9ce25SScott Long }
177452c9ce25SScott Long 
177552c9ce25SScott Long static void
1776c1bd46c2SAlexander Motin adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd)
177752c9ce25SScott Long {
177852c9ce25SScott Long 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
177952c9ce25SScott Long 	struct disk_params *dp = &softc->params;
178052c9ce25SScott Long 	u_int64_t lbasize48;
178152c9ce25SScott Long 	u_int32_t lbasize;
178252c9ce25SScott Long 
1783c1bd46c2SAlexander Motin 	dp->secsize = ata_logical_sector_size(&cgd->ident_data);
178452c9ce25SScott Long 	if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) &&
178552c9ce25SScott Long 		cgd->ident_data.current_heads && cgd->ident_data.current_sectors) {
178652c9ce25SScott Long 		dp->heads = cgd->ident_data.current_heads;
178752c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.current_sectors;
178852c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
178952c9ce25SScott Long 		dp->sectors = (u_int32_t)cgd->ident_data.current_size_1 |
179052c9ce25SScott Long 			  ((u_int32_t)cgd->ident_data.current_size_2 << 16);
179152c9ce25SScott Long 	} else {
179252c9ce25SScott Long 		dp->heads = cgd->ident_data.heads;
179352c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.sectors;
179452c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
179552c9ce25SScott Long 		dp->sectors = cgd->ident_data.cylinders * dp->heads * dp->secs_per_track;
179652c9ce25SScott Long 	}
179752c9ce25SScott Long 	lbasize = (u_int32_t)cgd->ident_data.lba_size_1 |
179852c9ce25SScott Long 		  ((u_int32_t)cgd->ident_data.lba_size_2 << 16);
179952c9ce25SScott Long 
180052c9ce25SScott Long 	/* use the 28bit LBA size if valid or bigger than the CHS mapping */
180152c9ce25SScott Long 	if (cgd->ident_data.cylinders == 16383 || dp->sectors < lbasize)
180252c9ce25SScott Long 		dp->sectors = lbasize;
180352c9ce25SScott Long 
180452c9ce25SScott Long 	/* use the 48bit LBA size if valid */
180552c9ce25SScott Long 	lbasize48 = ((u_int64_t)cgd->ident_data.lba_size48_1) |
180652c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_2 << 16) |
180752c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_3 << 32) |
180852c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_4 << 48);
180952c9ce25SScott Long 	if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) &&
181052c9ce25SScott Long 	    lbasize48 > ATA_MAX_28BIT_LBA)
181152c9ce25SScott Long 		dp->sectors = lbasize48;
181252c9ce25SScott Long }
181352c9ce25SScott Long 
181452c9ce25SScott Long static void
181552c9ce25SScott Long adasendorderedtag(void *arg)
181652c9ce25SScott Long {
181752c9ce25SScott Long 	struct ada_softc *softc = arg;
181852c9ce25SScott Long 
181952c9ce25SScott Long 	if (ada_send_ordered) {
182052c9ce25SScott Long 		if ((softc->ordered_tag_count == 0)
182152c9ce25SScott Long 		 && ((softc->flags & ADA_FLAG_WENT_IDLE) == 0)) {
182252c9ce25SScott Long 			softc->flags |= ADA_FLAG_NEED_OTAG;
182352c9ce25SScott Long 		}
182452c9ce25SScott Long 		if (softc->outstanding_cmds > 0)
182552c9ce25SScott Long 			softc->flags &= ~ADA_FLAG_WENT_IDLE;
182652c9ce25SScott Long 
182752c9ce25SScott Long 		softc->ordered_tag_count = 0;
182852c9ce25SScott Long 	}
182952c9ce25SScott Long 	/* Queue us up again */
183052c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
183147bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
183252c9ce25SScott Long 	    adasendorderedtag, softc);
183352c9ce25SScott Long }
183452c9ce25SScott Long 
183552c9ce25SScott Long /*
183652c9ce25SScott Long  * Step through all ADA peripheral drivers, and if the device is still open,
183752c9ce25SScott Long  * sync the disk cache to physical media.
183852c9ce25SScott Long  */
183952c9ce25SScott Long static void
1840c3d0d168SAlexander Motin adaflush(void)
184152c9ce25SScott Long {
184252c9ce25SScott Long 	struct cam_periph *periph;
184352c9ce25SScott Long 	struct ada_softc *softc;
184409cfadbeSAlexander Motin 	union ccb *ccb;
18450191d9b3SAlexander Motin 	int error;
184652c9ce25SScott Long 
1847f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
184821cc8587SAlexander Motin 		/* If we paniced with lock held - not recurse here. */
184921cc8587SAlexander Motin 		if (cam_periph_owned(periph))
185021cc8587SAlexander Motin 			continue;
185152c9ce25SScott Long 		cam_periph_lock(periph);
185252c9ce25SScott Long 		softc = (struct ada_softc *)periph->softc;
185352c9ce25SScott Long 		/*
185452c9ce25SScott Long 		 * We only sync the cache if the drive is still open, and
185552c9ce25SScott Long 		 * if the drive is capable of it..
185652c9ce25SScott Long 		 */
185752c9ce25SScott Long 		if (((softc->flags & ADA_FLAG_OPEN) == 0) ||
185852c9ce25SScott Long 		    (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) == 0) {
185952c9ce25SScott Long 			cam_periph_unlock(periph);
186052c9ce25SScott Long 			continue;
186152c9ce25SScott Long 		}
186252c9ce25SScott Long 
186309cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
186409cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
18650191d9b3SAlexander Motin 				    0,
186652c9ce25SScott Long 				    adadone,
186752c9ce25SScott Long 				    CAM_DIR_NONE,
186852c9ce25SScott Long 				    0,
186952c9ce25SScott Long 				    NULL,
187052c9ce25SScott Long 				    0,
187152c9ce25SScott Long 				    ada_default_timeout*1000);
187252c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
187309cfadbeSAlexander Motin 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
187452c9ce25SScott Long 		else
187509cfadbeSAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
187652c9ce25SScott Long 
187709cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
187809cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
187909cfadbeSAlexander Motin 		    softc->disk->d_devstat);
18800191d9b3SAlexander Motin 		if (error != 0)
18810191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
1882d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
188352c9ce25SScott Long 		cam_periph_unlock(periph);
188452c9ce25SScott Long 	}
1885c3d0d168SAlexander Motin }
1886fd104c15SRebecca Cran 
1887c3d0d168SAlexander Motin static void
1888c3d0d168SAlexander Motin adaspindown(uint8_t cmd, int flags)
1889c3d0d168SAlexander Motin {
1890c3d0d168SAlexander Motin 	struct cam_periph *periph;
1891c3d0d168SAlexander Motin 	struct ada_softc *softc;
189209cfadbeSAlexander Motin 	union ccb *ccb;
18930191d9b3SAlexander Motin 	int error;
1894fd104c15SRebecca Cran 
1895f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
1896fd104c15SRebecca Cran 		/* If we paniced with lock held - not recurse here. */
1897fd104c15SRebecca Cran 		if (cam_periph_owned(periph))
1898fd104c15SRebecca Cran 			continue;
1899fd104c15SRebecca Cran 		cam_periph_lock(periph);
1900fd104c15SRebecca Cran 		softc = (struct ada_softc *)periph->softc;
1901fd104c15SRebecca Cran 		/*
1902fd104c15SRebecca Cran 		 * We only spin-down the drive if it is capable of it..
1903fd104c15SRebecca Cran 		 */
1904fd104c15SRebecca Cran 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
1905fd104c15SRebecca Cran 			cam_periph_unlock(periph);
1906fd104c15SRebecca Cran 			continue;
1907fd104c15SRebecca Cran 		}
1908fd104c15SRebecca Cran 
1909fd104c15SRebecca Cran 		if (bootverbose)
1910fd104c15SRebecca Cran 			xpt_print(periph->path, "spin-down\n");
1911fd104c15SRebecca Cran 
191209cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
191309cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
19140191d9b3SAlexander Motin 				    0,
1915fd104c15SRebecca Cran 				    adadone,
1916c3d0d168SAlexander Motin 				    CAM_DIR_NONE | flags,
1917fd104c15SRebecca Cran 				    0,
1918fd104c15SRebecca Cran 				    NULL,
1919fd104c15SRebecca Cran 				    0,
1920fd104c15SRebecca Cran 				    ada_default_timeout*1000);
192109cfadbeSAlexander Motin 		ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, 0);
1922fd104c15SRebecca Cran 
192309cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
192409cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
192509cfadbeSAlexander Motin 		    softc->disk->d_devstat);
19260191d9b3SAlexander Motin 		if (error != 0)
19270191d9b3SAlexander Motin 			xpt_print(periph->path, "Spin-down disk failed\n");
1928d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
1929fd104c15SRebecca Cran 		cam_periph_unlock(periph);
1930fd104c15SRebecca Cran 	}
193152c9ce25SScott Long }
193252c9ce25SScott Long 
1933c3d0d168SAlexander Motin static void
1934c3d0d168SAlexander Motin adashutdown(void *arg, int howto)
1935c3d0d168SAlexander Motin {
1936c3d0d168SAlexander Motin 
1937c3d0d168SAlexander Motin 	adaflush();
1938c3d0d168SAlexander Motin 	if (ada_spindown_shutdown != 0 &&
1939c3d0d168SAlexander Motin 	    (howto & (RB_HALT | RB_POWEROFF)) != 0)
1940c3d0d168SAlexander Motin 		adaspindown(ATA_STANDBY_IMMEDIATE, 0);
1941c3d0d168SAlexander Motin }
1942c3d0d168SAlexander Motin 
1943c3d0d168SAlexander Motin static void
1944c3d0d168SAlexander Motin adasuspend(void *arg)
1945c3d0d168SAlexander Motin {
1946c3d0d168SAlexander Motin 
1947c3d0d168SAlexander Motin 	adaflush();
1948c3d0d168SAlexander Motin 	if (ada_spindown_suspend != 0)
1949c3d0d168SAlexander Motin 		adaspindown(ATA_SLEEP, CAM_DEV_QFREEZE);
1950c3d0d168SAlexander Motin }
1951c3d0d168SAlexander Motin 
1952c3d0d168SAlexander Motin static void
1953c3d0d168SAlexander Motin adaresume(void *arg)
1954c3d0d168SAlexander Motin {
1955c3d0d168SAlexander Motin 	struct cam_periph *periph;
1956c3d0d168SAlexander Motin 	struct ada_softc *softc;
1957c3d0d168SAlexander Motin 
1958c3d0d168SAlexander Motin 	if (ada_spindown_suspend == 0)
1959c3d0d168SAlexander Motin 		return;
1960c3d0d168SAlexander Motin 
1961f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
1962c3d0d168SAlexander Motin 		cam_periph_lock(periph);
1963c3d0d168SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
1964c3d0d168SAlexander Motin 		/*
1965c3d0d168SAlexander Motin 		 * We only spin-down the drive if it is capable of it..
1966c3d0d168SAlexander Motin 		 */
1967c3d0d168SAlexander Motin 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
1968c3d0d168SAlexander Motin 			cam_periph_unlock(periph);
1969c3d0d168SAlexander Motin 			continue;
1970c3d0d168SAlexander Motin 		}
1971c3d0d168SAlexander Motin 
1972c3d0d168SAlexander Motin 		if (bootverbose)
1973c3d0d168SAlexander Motin 			xpt_print(periph->path, "resume\n");
1974c3d0d168SAlexander Motin 
1975c3d0d168SAlexander Motin 		/*
1976c3d0d168SAlexander Motin 		 * Drop freeze taken due to CAM_DEV_QFREEZE flag set on
1977c3d0d168SAlexander Motin 		 * sleep request.
1978c3d0d168SAlexander Motin 		 */
1979c3d0d168SAlexander Motin 		cam_release_devq(periph->path,
1980c3d0d168SAlexander Motin 			 /*relsim_flags*/0,
1981c3d0d168SAlexander Motin 			 /*openings*/0,
1982c3d0d168SAlexander Motin 			 /*timeout*/0,
1983c3d0d168SAlexander Motin 			 /*getcount_only*/0);
1984c3d0d168SAlexander Motin 
1985c3d0d168SAlexander Motin 		cam_periph_unlock(periph);
1986c3d0d168SAlexander Motin 	}
1987c3d0d168SAlexander Motin }
1988c3d0d168SAlexander Motin 
198952c9ce25SScott Long #endif /* _KERNEL */
1990