ata_pmp.c revision 198708
1/*-
2 * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/cam/ata/ata_pmp.c 198708 2009-10-31 10:43:38Z mav $");
29
30#include <sys/param.h>
31
32#ifdef _KERNEL
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/bio.h>
36#include <sys/sysctl.h>
37#include <sys/taskqueue.h>
38#include <sys/lock.h>
39#include <sys/mutex.h>
40#include <sys/conf.h>
41#include <sys/devicestat.h>
42#include <sys/eventhandler.h>
43#include <sys/malloc.h>
44#include <sys/cons.h>
45#include <geom/geom_disk.h>
46#endif /* _KERNEL */
47
48#ifndef _KERNEL
49#include <stdio.h>
50#include <string.h>
51#endif /* _KERNEL */
52
53#include <cam/cam.h>
54#include <cam/cam_ccb.h>
55#include <cam/cam_periph.h>
56#include <cam/cam_xpt_periph.h>
57#include <cam/cam_sim.h>
58
59#include <cam/ata/ata_all.h>
60
61#ifdef _KERNEL
62
63typedef enum {
64	PMP_STATE_NORMAL,
65	PMP_STATE_PORTS,
66	PMP_STATE_CONFIG,
67	PMP_STATE_RESET,
68	PMP_STATE_CONNECT,
69	PMP_STATE_CHECK,
70	PMP_STATE_CLEAR,
71	PMP_STATE_SCAN
72} pmp_state;
73
74typedef enum {
75	PMP_FLAG_SCTX_INIT	= 0x200
76} pmp_flags;
77
78typedef enum {
79	PMP_CCB_PROBE		= 0x01,
80} pmp_ccb_state;
81
82/* Offsets into our private area for storing information */
83#define ccb_state	ppriv_field0
84#define ccb_bp		ppriv_ptr1
85
86struct pmp_softc {
87	SLIST_ENTRY(pmp_softc)	links;
88	pmp_state		state;
89	pmp_flags		flags;
90	uint32_t		pm_pid;
91	uint32_t		pm_prv;
92	int			pm_ports;
93	int			pm_step;
94	int			pm_try;
95	int			found;
96	int			reset;
97	int			frozen;
98	int			restart;
99	union			ccb saved_ccb;
100	struct task		sysctl_task;
101	struct sysctl_ctx_list	sysctl_ctx;
102	struct sysctl_oid	*sysctl_tree;
103};
104
105static	periph_init_t	pmpinit;
106static	void		pmpasync(void *callback_arg, u_int32_t code,
107				struct cam_path *path, void *arg);
108static	void		pmpsysctlinit(void *context, int pending);
109static	periph_ctor_t	pmpregister;
110static	periph_dtor_t	pmpcleanup;
111static	periph_start_t	pmpstart;
112static	periph_oninv_t	pmponinvalidate;
113static	void		pmpdone(struct cam_periph *periph,
114			       union ccb *done_ccb);
115
116#ifndef PMP_DEFAULT_TIMEOUT
117#define PMP_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
118#endif
119
120#ifndef	PMP_DEFAULT_RETRY
121#define	PMP_DEFAULT_RETRY	1
122#endif
123
124static int pmp_retry_count = PMP_DEFAULT_RETRY;
125static int pmp_default_timeout = PMP_DEFAULT_TIMEOUT;
126
127SYSCTL_NODE(_kern_cam, OID_AUTO, pmp, CTLFLAG_RD, 0,
128            "CAM Direct Access Disk driver");
129SYSCTL_INT(_kern_cam_pmp, OID_AUTO, retry_count, CTLFLAG_RW,
130           &pmp_retry_count, 0, "Normal I/O retry count");
131TUNABLE_INT("kern.cam.pmp.retry_count", &pmp_retry_count);
132SYSCTL_INT(_kern_cam_pmp, OID_AUTO, default_timeout, CTLFLAG_RW,
133           &pmp_default_timeout, 0, "Normal I/O timeout (in seconds)");
134TUNABLE_INT("kern.cam.pmp.default_timeout", &pmp_default_timeout);
135
136static struct periph_driver pmpdriver =
137{
138	pmpinit, "pmp",
139	TAILQ_HEAD_INITIALIZER(pmpdriver.units), /* generation */ 0,
140	CAM_PERIPH_DRV_EARLY
141};
142
143PERIPHDRIVER_DECLARE(pmp, pmpdriver);
144
145MALLOC_DEFINE(M_ATPMP, "ata_pmp", "ata_pmp buffers");
146
147static void
148pmpinit(void)
149{
150	cam_status status;
151
152	/*
153	 * Install a global async callback.  This callback will
154	 * receive async callbacks like "new device found".
155	 */
156	status = xpt_register_async(AC_FOUND_DEVICE, pmpasync, NULL, NULL);
157
158	if (status != CAM_REQ_CMP) {
159		printf("pmp: Failed to attach master async callback "
160		       "due to status 0x%x!\n", status);
161	}
162}
163
164static void
165pmpfreeze(struct cam_periph *periph, int mask)
166{
167	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
168	struct cam_path *dpath;
169	int i;
170
171	mask &= ~softc->frozen;
172	for (i = 0; i < 15; i++) {
173		if ((mask & (1 << i)) == 0)
174			continue;
175		if (xpt_create_path(&dpath, periph,
176		    xpt_path_path_id(periph->path),
177		    i, 0) == CAM_REQ_CMP) {
178printf("PMP freeze: %d\n", i);
179			softc->frozen |= (1 << i);
180			cam_freeze_devq(dpath);
181			xpt_free_path(dpath);
182		}
183	}
184}
185
186static void
187pmprelease(struct cam_periph *periph, int mask)
188{
189	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
190	struct cam_path *dpath;
191	int i;
192
193	mask &= softc->frozen;
194	for (i = 0; i < 15; i++) {
195		if ((mask & (1 << i)) == 0)
196			continue;
197		if (xpt_create_path(&dpath, periph,
198		    xpt_path_path_id(periph->path),
199		    i, 0) == CAM_REQ_CMP) {
200printf("PMP release: %d\n", i);
201			softc->frozen &= ~(1 << i);
202			cam_release_devq(dpath, 0, 0, 0, FALSE);
203			xpt_free_path(dpath);
204		}
205	}
206}
207
208static void
209pmponinvalidate(struct cam_periph *periph)
210{
211	struct pmp_softc *softc;
212	struct cam_path *dpath;
213	int i;
214
215	softc = (struct pmp_softc *)periph->softc;
216
217	/*
218	 * De-register any async callbacks.
219	 */
220	xpt_register_async(0, pmpasync, periph, periph->path);
221
222	for (i = 0; i < 15; i++) {
223		if (xpt_create_path(&dpath, periph,
224		    xpt_path_path_id(periph->path),
225		    i, 0) == CAM_REQ_CMP) {
226			xpt_async(AC_LOST_DEVICE, dpath, NULL);
227			xpt_free_path(dpath);
228		}
229	}
230	xpt_print(periph->path, "lost device\n");
231}
232
233static void
234pmpcleanup(struct cam_periph *periph)
235{
236	struct pmp_softc *softc;
237
238	softc = (struct pmp_softc *)periph->softc;
239
240	xpt_print(periph->path, "removing device entry\n");
241	cam_periph_unlock(periph);
242
243	/*
244	 * If we can't free the sysctl tree, oh well...
245	 */
246	if ((softc->flags & PMP_FLAG_SCTX_INIT) != 0
247	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
248		xpt_print(periph->path, "can't remove sysctl context\n");
249	}
250
251	free(softc, M_DEVBUF);
252	cam_periph_lock(periph);
253}
254
255static void
256pmpasync(void *callback_arg, u_int32_t code,
257	struct cam_path *path, void *arg)
258{
259	struct cam_periph *periph;
260	struct pmp_softc *softc;
261
262	periph = (struct cam_periph *)callback_arg;
263	switch (code) {
264	case AC_FOUND_DEVICE:
265	{
266		struct ccb_getdev *cgd;
267		cam_status status;
268
269		cgd = (struct ccb_getdev *)arg;
270		if (cgd == NULL)
271			break;
272
273		if (cgd->protocol != PROTO_SATAPM)
274			break;
275
276		/*
277		 * Allocate a peripheral instance for
278		 * this device and start the probe
279		 * process.
280		 */
281		status = cam_periph_alloc(pmpregister, pmponinvalidate,
282					  pmpcleanup, pmpstart,
283					  "pmp", CAM_PERIPH_BIO,
284					  cgd->ccb_h.path, pmpasync,
285					  AC_FOUND_DEVICE, cgd);
286
287		if (status != CAM_REQ_CMP
288		 && status != CAM_REQ_INPROG)
289			printf("pmpasync: Unable to attach to new device "
290				"due to status 0x%x\n", status);
291		break;
292	}
293	case AC_SCSI_AEN:
294	case AC_SENT_BDR:
295	case AC_BUS_RESET:
296		softc = (struct pmp_softc *)periph->softc;
297		cam_periph_async(periph, code, path, arg);
298		if (code == AC_SCSI_AEN && softc->state != PMP_STATE_NORMAL &&
299		    softc->state != PMP_STATE_SCAN)
300			break;
301		if (softc->state != PMP_STATE_SCAN)
302			pmpfreeze(periph, softc->found);
303		else
304			pmpfreeze(periph, softc->found & ~(1 << softc->pm_step));
305		if (code == AC_SENT_BDR || code == AC_BUS_RESET)
306			softc->found = 0; /* We have to reset everything. */
307		if (softc->state == PMP_STATE_NORMAL) {
308			softc->state = PMP_STATE_PORTS;
309			cam_periph_acquire(periph);
310			xpt_schedule(periph, CAM_PRIORITY_BUS);
311		} else
312			softc->restart = 1;
313		break;
314	default:
315		cam_periph_async(periph, code, path, arg);
316		break;
317	}
318}
319
320static void
321pmpsysctlinit(void *context, int pending)
322{
323	struct cam_periph *periph;
324	struct pmp_softc *softc;
325	char tmpstr[80], tmpstr2[80];
326
327	periph = (struct cam_periph *)context;
328	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
329		return;
330
331	softc = (struct pmp_softc *)periph->softc;
332	snprintf(tmpstr, sizeof(tmpstr), "CAM PMP unit %d", periph->unit_number);
333	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
334
335	sysctl_ctx_init(&softc->sysctl_ctx);
336	softc->flags |= PMP_FLAG_SCTX_INIT;
337	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
338		SYSCTL_STATIC_CHILDREN(_kern_cam_pmp), OID_AUTO, tmpstr2,
339		CTLFLAG_RD, 0, tmpstr);
340	if (softc->sysctl_tree == NULL) {
341		printf("pmpsysctlinit: unable to allocate sysctl tree\n");
342		cam_periph_release(periph);
343		return;
344	}
345
346	cam_periph_release(periph);
347}
348
349static cam_status
350pmpregister(struct cam_periph *periph, void *arg)
351{
352	struct pmp_softc *softc;
353	struct ccb_pathinq cpi;
354	struct ccb_getdev *cgd;
355
356	cgd = (struct ccb_getdev *)arg;
357	if (periph == NULL) {
358		printf("pmpregister: periph was NULL!!\n");
359		return(CAM_REQ_CMP_ERR);
360	}
361
362	if (cgd == NULL) {
363		printf("pmpregister: no getdev CCB, can't register device\n");
364		return(CAM_REQ_CMP_ERR);
365	}
366
367	softc = (struct pmp_softc *)malloc(sizeof(*softc), M_DEVBUF,
368	    M_NOWAIT|M_ZERO);
369
370	if (softc == NULL) {
371		printf("pmpregister: Unable to probe new device. "
372		       "Unable to allocate softc\n");
373		return(CAM_REQ_CMP_ERR);
374	}
375	periph->softc = softc;
376
377	softc->state = PMP_STATE_PORTS;
378	softc->pm_pid = ((uint32_t *)&cgd->ident_data)[0];
379	softc->pm_prv = ((uint32_t *)&cgd->ident_data)[1];
380
381	/* Check if the SIM does not want queued commands */
382	bzero(&cpi, sizeof(cpi));
383	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
384	cpi.ccb_h.func_code = XPT_PATH_INQ;
385	xpt_action((union ccb *)&cpi);
386
387	TASK_INIT(&softc->sysctl_task, 0, pmpsysctlinit, periph);
388
389	xpt_announce_periph(periph, NULL);
390
391	/*
392	 * Add async callbacks for bus reset and
393	 * bus device reset calls.  I don't bother
394	 * checking if this fails as, in most cases,
395	 * the system will function just fine without
396	 * them and the only alternative would be to
397	 * not attach the device on failure.
398	 */
399	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
400		AC_SCSI_AEN, pmpasync, periph, periph->path);
401
402	/*
403	 * Take an exclusive refcount on the periph while pmpstart is called
404	 * to finish the probe.  The reference will be dropped in pmpdone at
405	 * the end of probe.
406	 */
407	(void)cam_periph_acquire(periph);
408	xpt_schedule(periph, CAM_PRIORITY_BUS);
409
410	return(CAM_REQ_CMP);
411}
412
413static void
414pmpstart(struct cam_periph *periph, union ccb *start_ccb)
415{
416	struct ccb_ataio *ataio;
417	struct pmp_softc *softc;
418
419	softc = (struct pmp_softc *)periph->softc;
420	ataio = &start_ccb->ataio;
421
422	if (softc->restart) {
423		softc->restart = 0;
424		softc->state = PMP_STATE_PORTS;
425	}
426
427	switch (softc->state) {
428	case PMP_STATE_PORTS:
429		cam_fill_ataio(ataio,
430		      pmp_retry_count,
431		      pmpdone,
432		      /*flags*/CAM_DIR_NONE,
433		      0,
434		      /*data_ptr*/NULL,
435		      /*dxfer_len*/0,
436		      pmp_default_timeout * 1000);
437		ata_pm_read_cmd(ataio, 2, 15);
438		break;
439	case PMP_STATE_CONFIG:
440		cam_fill_ataio(ataio,
441		      pmp_retry_count,
442		      pmpdone,
443		      /*flags*/CAM_DIR_NONE,
444		      0,
445		      /*data_ptr*/NULL,
446		      /*dxfer_len*/0,
447		      pmp_default_timeout * 1000);
448		ata_pm_write_cmd(ataio, 0x60, 15, 0xf);
449		break;
450	case PMP_STATE_RESET:
451		cam_fill_ataio(ataio,
452		      pmp_retry_count,
453		      pmpdone,
454		      /*flags*/CAM_DIR_NONE,
455		      0,
456		      /*data_ptr*/NULL,
457		      /*dxfer_len*/0,
458		      pmp_default_timeout * 1000);
459		ata_pm_write_cmd(ataio, 2, softc->pm_step,
460		    (softc->found & (1 << softc->pm_step)) ? 0 : 1);
461printf("PM RESET %d%s\n", softc->pm_step,
462    (softc->found & (1 << softc->pm_step)) ? " skipping" : "");
463		break;
464	case PMP_STATE_CONNECT:
465		cam_fill_ataio(ataio,
466		      pmp_retry_count,
467		      pmpdone,
468		      /*flags*/CAM_DIR_NONE,
469		      0,
470		      /*data_ptr*/NULL,
471		      /*dxfer_len*/0,
472		      pmp_default_timeout * 1000);
473		ata_pm_write_cmd(ataio, 2, softc->pm_step, 0);
474		break;
475	case PMP_STATE_CHECK:
476		cam_fill_ataio(ataio,
477		      pmp_retry_count,
478		      pmpdone,
479		      /*flags*/CAM_DIR_NONE,
480		      0,
481		      /*data_ptr*/NULL,
482		      /*dxfer_len*/0,
483		      pmp_default_timeout * 1000);
484		ata_pm_read_cmd(ataio, 0, softc->pm_step);
485		break;
486	case PMP_STATE_CLEAR:
487		softc->reset = 0;
488		cam_fill_ataio(ataio,
489		      pmp_retry_count,
490		      pmpdone,
491		      /*flags*/CAM_DIR_NONE,
492		      0,
493		      /*data_ptr*/NULL,
494		      /*dxfer_len*/0,
495		      pmp_default_timeout * 1000);
496		ata_pm_write_cmd(ataio, 1, softc->pm_step, 0xFFFFFFFF);
497		break;
498	default:
499		break;
500	}
501	xpt_action(start_ccb);
502}
503
504static void
505pmpdone(struct cam_periph *periph, union ccb *done_ccb)
506{
507	struct pmp_softc *softc;
508	struct ccb_ataio *ataio;
509	union ccb *work_ccb;
510	struct cam_path *path, *dpath;
511	u_int32_t  priority, res;
512
513	softc = (struct pmp_softc *)periph->softc;
514	ataio = &done_ccb->ataio;
515
516	CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("pmpdone\n"));
517
518	path = done_ccb->ccb_h.path;
519	priority = done_ccb->ccb_h.pinfo.priority;
520
521	if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
522		if (cam_periph_error(done_ccb, 0, 0,
523		    &softc->saved_ccb) == ERESTART) {
524			return;
525		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
526			cam_release_devq(done_ccb->ccb_h.path,
527			    /*relsim_flags*/0,
528			    /*reduction*/0,
529			    /*timeout*/0,
530			    /*getcount_only*/0);
531		}
532		goto done;
533	}
534
535	if (softc->restart) {
536		softc->restart = 0;
537		if (softc->state == PMP_STATE_SCAN) {
538			pmpfreeze(periph, 1 << softc->pm_step);
539			work_ccb = done_ccb;
540			done_ccb = (union ccb*)work_ccb->ccb_h.ppriv_ptr0;
541			/* Free the current request path- we're done with it. */
542		    	xpt_free_path(work_ccb->ccb_h.path);
543			xpt_free_ccb(work_ccb);
544		}
545		xpt_release_ccb(done_ccb);
546		softc->state = PMP_STATE_PORTS;
547		xpt_schedule(periph, priority);
548		return;
549	}
550
551	switch (softc->state) {
552	case PMP_STATE_PORTS:
553		softc->pm_ports = (done_ccb->ataio.res.lba_high << 24) +
554		    (done_ccb->ataio.res.lba_mid << 16) +
555		    (done_ccb->ataio.res.lba_low << 8) +
556		    done_ccb->ataio.res.sector_count;
557		/* This PM declares 6 ports, while only 5 of them are real.
558		 * Port 5 is enclosure management bridge port, which has implementation
559		 * problems, causing probe faults. Hide it for now. */
560		if (softc->pm_pid == 0x37261095 && softc->pm_ports == 6)
561			softc->pm_ports = 5;
562		/* This PM declares 7 ports, while only 5 of them are real.
563		 * Port 5 is some fake "Config  Disk" with 640 sectors size,
564		 * port 6 is enclosure management bridge port.
565		 * Both fake ports has implementation problems, causing
566		 * probe faults. Hide them for now. */
567		if (softc->pm_pid == 0x47261095 && softc->pm_ports == 7)
568			softc->pm_ports = 5;
569		printf("PM ports: %d\n", softc->pm_ports);
570		softc->state = PMP_STATE_CONFIG;
571		xpt_release_ccb(done_ccb);
572		xpt_schedule(periph, priority);
573		return;
574	case PMP_STATE_CONFIG:
575		softc->pm_step = 0;
576		softc->state = PMP_STATE_RESET;
577		softc->reset |= ~softc->found;
578		xpt_release_ccb(done_ccb);
579		xpt_schedule(periph, priority);
580		return;
581	case PMP_STATE_RESET:
582		softc->pm_step++;
583		if (softc->pm_step >= softc->pm_ports) {
584			softc->pm_step = 0;
585			cam_freeze_devq(periph->path);
586			cam_release_devq(periph->path,
587			    RELSIM_RELEASE_AFTER_TIMEOUT,
588			    /*reduction*/0,
589			    /*timeout*/5,
590			    /*getcount_only*/0);
591			printf("PM reset done\n");
592			softc->state = PMP_STATE_CONNECT;
593		}
594		xpt_release_ccb(done_ccb);
595		xpt_schedule(periph, priority);
596		return;
597	case PMP_STATE_CONNECT:
598		softc->pm_step++;
599		if (softc->pm_step >= softc->pm_ports) {
600			softc->pm_step = 0;
601			softc->pm_try = 0;
602			cam_freeze_devq(periph->path);
603			cam_release_devq(periph->path,
604			    RELSIM_RELEASE_AFTER_TIMEOUT,
605			    /*reduction*/0,
606			    /*timeout*/10,
607			    /*getcount_only*/0);
608			printf("PM connect done\n");
609			softc->state = PMP_STATE_CHECK;
610		}
611		xpt_release_ccb(done_ccb);
612		xpt_schedule(periph, priority);
613		return;
614	case PMP_STATE_CHECK:
615		res = (done_ccb->ataio.res.lba_high << 24) +
616		    (done_ccb->ataio.res.lba_mid << 16) +
617		    (done_ccb->ataio.res.lba_low << 8) +
618		    done_ccb->ataio.res.sector_count;
619		if ((res & 0xf0f) == 0x103 && (res & 0x0f0) != 0) {
620			printf("PM status: %d - %08x\n", softc->pm_step, res);
621			softc->found |= (1 << softc->pm_step);
622			softc->pm_step++;
623		} else {
624			if (softc->pm_try < 10) {
625				cam_freeze_devq(periph->path);
626				cam_release_devq(periph->path,
627				    RELSIM_RELEASE_AFTER_TIMEOUT,
628				    /*reduction*/0,
629				    /*timeout*/10,
630				    /*getcount_only*/0);
631				softc->pm_try++;
632			} else {
633				printf("PM status: %d - %08x\n", softc->pm_step, res);
634				softc->found &= ~(1 << softc->pm_step);
635				if (xpt_create_path(&dpath, periph,
636				    done_ccb->ccb_h.path_id,
637				    softc->pm_step, 0) == CAM_REQ_CMP) {
638					xpt_async(AC_LOST_DEVICE, dpath, NULL);
639					xpt_free_path(dpath);
640				}
641				softc->pm_step++;
642			}
643		}
644		if (softc->pm_step >= softc->pm_ports) {
645			if (softc->reset & softc->found) {
646				cam_freeze_devq(periph->path);
647				cam_release_devq(periph->path,
648				    RELSIM_RELEASE_AFTER_TIMEOUT,
649				    /*reduction*/0,
650				    /*timeout*/1000,
651				    /*getcount_only*/0);
652			}
653			softc->state = PMP_STATE_CLEAR;
654			softc->pm_step = 0;
655		}
656		xpt_release_ccb(done_ccb);
657		xpt_schedule(periph, priority);
658		return;
659	case PMP_STATE_CLEAR:
660		softc->pm_step++;
661		if (softc->pm_step < softc->pm_ports) {
662			xpt_release_ccb(done_ccb);
663			xpt_schedule(periph, priority);
664			return;
665		} else if (softc->found) {
666			softc->pm_step = 0;
667			softc->state = PMP_STATE_SCAN;
668			work_ccb = xpt_alloc_ccb_nowait();
669			if (work_ccb != NULL)
670				goto do_scan;
671			xpt_release_ccb(done_ccb);
672		}
673		break;
674	case PMP_STATE_SCAN:
675		work_ccb = done_ccb;
676		done_ccb = (union ccb*)work_ccb->ccb_h.ppriv_ptr0;
677		/* Free the current request path- we're done with it. */
678		xpt_free_path(work_ccb->ccb_h.path);
679		softc->pm_step++;
680do_scan:
681		while (softc->pm_step < softc->pm_ports &&
682		    (softc->found & (1 << softc->pm_step)) == 0) {
683			softc->pm_step++;
684		}
685		if (softc->pm_step >= softc->pm_ports) {
686			xpt_free_ccb(work_ccb);
687			break;
688		}
689		if (xpt_create_path(&dpath, periph,
690		    done_ccb->ccb_h.path_id,
691		    softc->pm_step, 0) != CAM_REQ_CMP) {
692			printf("pmpdone: xpt_create_path failed"
693			    ", bus scan halted\n");
694			xpt_free_ccb(work_ccb);
695			break;
696		}
697		xpt_setup_ccb(&work_ccb->ccb_h, dpath,
698		    done_ccb->ccb_h.pinfo.priority);
699		work_ccb->ccb_h.func_code = XPT_SCAN_LUN;
700		work_ccb->ccb_h.cbfcnp = pmpdone;
701		work_ccb->ccb_h.ppriv_ptr0 = done_ccb;
702		work_ccb->crcn.flags = done_ccb->crcn.flags;
703		xpt_action(work_ccb);
704		pmprelease(periph, 1 << softc->pm_step);
705		return;
706	default:
707		break;
708	}
709done:
710	xpt_release_ccb(done_ccb);
711	softc->state = PMP_STATE_NORMAL;
712	pmprelease(periph, -1);
713	cam_periph_release_locked(periph);
714}
715
716#endif /* _KERNEL */
717