Module Name: src
Committed By: jdolecek
Date: Tue Oct 10 17:19:39 UTC 2017
Modified Files:
src/sys/dev/ata: ata.c ataconf.h atavar.h files.ata
Added Files:
src/sys/dev/ata: ata_subr.c
Log Message:
split off functions used by 'wd* at umass?' into separate file, unfortunately
the previous approach with NATABUS doesn't work for kernels which include
MODULAR, but not atabus - such as macppc and evbarm
To generate a diff of this commit:
cvs rdiff -u -r1.135 -r1.136 src/sys/dev/ata/ata.c
cvs rdiff -u -r0 -r1.1 src/sys/dev/ata/ata_subr.c
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/ata/ataconf.h
cvs rdiff -u -r1.93 -r1.94 src/sys/dev/ata/atavar.h
cvs rdiff -u -r1.26 -r1.27 src/sys/dev/ata/files.ata
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.135 src/sys/dev/ata/ata.c:1.136
--- src/sys/dev/ata/ata.c:1.135 Sun Oct 8 19:00:29 2017
+++ src/sys/dev/ata/ata.c Tue Oct 10 17:19:38 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ata.c,v 1.135 2017/10/08 19:00:29 jdolecek Exp $ */
+/* $NetBSD: ata.c,v 1.136 2017/10/10 17:19:38 jdolecek Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
@@ -25,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.135 2017/10/08 19:00:29 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.136 2017/10/10 17:19:38 jdolecek Exp $");
#include "opt_ata.h"
@@ -83,7 +83,6 @@ int atadebug_mask = ATADEBUG_MASK;
#define ATADEBUG_PRINT(args, level)
#endif
-#if NATABUS
static ONCE_DECL(ata_init_ctrl);
/*
@@ -134,7 +133,6 @@ static void ata_channel_idle(struct ata_
static void ata_channel_thaw_locked(struct ata_channel *);
static void ata_activate_xfer_locked(struct ata_channel *, struct ata_xfer *);
static void ata_channel_freeze_locked(struct ata_channel *);
-static struct ata_xfer *ata_queue_get_active_xfer_locked(struct ata_channel *);
static void ata_thread_wake_locked(struct ata_channel *);
/*
@@ -186,177 +184,7 @@ ataprint(void *aux, const char *pnp)
return (UNCONF);
}
-#endif /* NATABUS */
-static void
-ata_queue_reset(struct ata_queue *chq)
-{
- /* make sure that we can use polled commands */
- TAILQ_INIT(&chq->queue_xfer);
- TAILQ_INIT(&chq->active_xfers);
- chq->queue_freeze = 0;
- chq->queue_active = 0;
- chq->active_xfers_used = 0;
- chq->queue_xfers_avail = __BIT(chq->queue_openings) - 1;
-}
-
-struct ata_xfer *
-ata_queue_hwslot_to_xfer(struct ata_channel *chp, int hwslot)
-{
- struct ata_queue *chq = chp->ch_queue;
- struct ata_xfer *xfer = NULL;
-
- ata_channel_lock(chp);
-
- KASSERTMSG(hwslot < chq->queue_openings, "hwslot %d > openings %d",
- hwslot, chq->queue_openings);
- KASSERTMSG((chq->active_xfers_used & __BIT(hwslot)) != 0,
- "hwslot %d not active", hwslot);
-
- /* Usually the first entry will be the one */
- TAILQ_FOREACH(xfer, &chq->active_xfers, c_activechain) {
- if (xfer->c_slot == hwslot)
- break;
- }
-
- ata_channel_unlock(chp);
-
- KASSERTMSG((xfer != NULL),
- "%s: xfer with slot %d not found (active %x)", __func__,
- hwslot, chq->active_xfers_used);
-
- return xfer;
-}
-
-static struct ata_xfer *
-ata_queue_get_active_xfer_locked(struct ata_channel *chp)
-{
- struct ata_xfer *xfer;
-
- KASSERT(mutex_owned(&chp->ch_lock));
- xfer = TAILQ_FIRST(&chp->ch_queue->active_xfers);
-
- if (xfer && ISSET(xfer->c_flags, C_NCQ)) {
- /* Spurious call, never return NCQ xfer from this interface */
- xfer = NULL;
- }
-
- return xfer;
-}
-
-/*
- * This interface is supposed only to be used when there is exactly
- * one outstanding command, when there is no information about the slot,
- * which triggered the command. ata_queue_hwslot_to_xfer() interface
- * is preferred in all NCQ cases.
- */
-struct ata_xfer *
-ata_queue_get_active_xfer(struct ata_channel *chp)
-{
- struct ata_xfer *xfer = NULL;
-
- ata_channel_lock(chp);
- xfer = ata_queue_get_active_xfer_locked(chp);
- ata_channel_unlock(chp);
-
- return xfer;
-}
-
-struct ata_xfer *
-ata_queue_drive_active_xfer(struct ata_channel *chp, int drive)
-{
- struct ata_xfer *xfer = NULL;
-
- ata_channel_lock(chp);
-
- TAILQ_FOREACH(xfer, &chp->ch_queue->active_xfers, c_activechain) {
- if (xfer->c_drive == drive)
- break;
- }
- KASSERT(xfer != NULL);
-
- ata_channel_unlock(chp);
-
- return xfer;
-}
-
-static void
-ata_xfer_init(struct ata_xfer *xfer, uint8_t slot)
-{
- memset(xfer, 0, sizeof(*xfer));
-
- xfer->c_slot = slot;
-
- cv_init(&xfer->c_active, "ataact");
- cv_init(&xfer->c_finish, "atafin");
- callout_init(&xfer->c_timo_callout, 0); /* XXX MPSAFE */
- callout_init(&xfer->c_retry_callout, 0); /* XXX MPSAFE */
-}
-
-static void
-ata_xfer_destroy(struct ata_xfer *xfer)
-{
- callout_halt(&xfer->c_timo_callout, NULL); /* XXX MPSAFE */
- callout_destroy(&xfer->c_timo_callout);
- callout_halt(&xfer->c_retry_callout, NULL); /* XXX MPSAFE */
- callout_destroy(&xfer->c_retry_callout);
- cv_destroy(&xfer->c_active);
- cv_destroy(&xfer->c_finish);
-}
-
-struct ata_queue *
-ata_queue_alloc(uint8_t openings)
-{
- if (openings == 0)
- openings = 1;
-
- if (openings > ATA_MAX_OPENINGS)
- openings = ATA_MAX_OPENINGS;
-
- struct ata_queue *chq = malloc(offsetof(struct ata_queue, queue_xfers[openings]),
- M_DEVBUF, M_WAITOK | M_ZERO);
-
- chq->queue_openings = openings;
- ata_queue_reset(chq);
-
- cv_init(&chq->queue_busy, "ataqbusy");
- cv_init(&chq->queue_drain, "atdrn");
- cv_init(&chq->queue_idle, "qidl");
-
- for (uint8_t i = 0; i < openings; i++)
- ata_xfer_init(&chq->queue_xfers[i], i);
-
- return chq;
-}
-
-void
-ata_queue_free(struct ata_queue *chq)
-{
- for (uint8_t i = 0; i < chq->queue_openings; i++)
- ata_xfer_destroy(&chq->queue_xfers[i]);
-
- cv_destroy(&chq->queue_busy);
- cv_destroy(&chq->queue_drain);
- cv_destroy(&chq->queue_idle);
-
- free(chq, M_DEVBUF);
-}
-
-void
-ata_channel_init(struct ata_channel *chp)
-{
- mutex_init(&chp->ch_lock, MUTEX_DEFAULT, IPL_BIO);
- cv_init(&chp->ch_thr_idle, "atath");
-}
-
-void
-ata_channel_destroy(struct ata_channel *chp)
-{
- mutex_destroy(&chp->ch_lock);
- cv_destroy(&chp->ch_thr_idle);
-}
-
-#if NATABUS
/*
* ata_channel_attach:
*
@@ -1431,129 +1259,7 @@ ata_xfer_start(struct ata_xfer *xfer)
return rv;
}
-#endif /* NATABUS */
-
-/*
- * Does it's own locking, does not require splbio().
- * flags - whether to block waiting for free xfer
- * openings - limit of openings supported by device, <= 0 means tag not
- * relevant, and any available xfer can be returned
- */
-struct ata_xfer *
-ata_get_xfer_ext(struct ata_channel *chp, int flags, uint8_t openings)
-{
- struct ata_queue *chq = chp->ch_queue;
- struct ata_xfer *xfer = NULL;
- uint32_t avail, slot, mask;
- int error;
-
- ATADEBUG_PRINT(("%s: channel %d flags %x openings %d\n",
- __func__, chp->ch_channel, flags, openings),
- DEBUG_XFERS);
-
- ata_channel_lock(chp);
-
- /*
- * When openings is just 1, can't reserve anything for
- * recovery. KASSERT() here is to catch code which naively
- * relies on C_RECOVERY to work under this condition.
- */
- KASSERT((flags & C_RECOVERY) == 0 || chq->queue_openings > 1);
-
- if (flags & C_RECOVERY) {
- mask = UINT32_MAX;
- } else {
- if (openings <= 0 || openings > chq->queue_openings)
- openings = chq->queue_openings;
-
- if (openings > 1) {
- mask = __BIT(openings - 1) - 1;
- } else {
- mask = UINT32_MAX;
- }
- }
-
-retry:
- avail = ffs32(chq->queue_xfers_avail & mask);
- if (avail == 0) {
- /*
- * Catch code which tries to get another recovery xfer while
- * already holding one (wrong recursion).
- */
- KASSERTMSG((flags & C_RECOVERY) == 0,
- "recovery xfer busy openings %d mask %x avail %x",
- openings, mask, chq->queue_xfers_avail);
-
- if (flags & C_WAIT) {
- chq->queue_flags |= QF_NEED_XFER;
- error = cv_wait_sig(&chq->queue_busy, &chp->ch_lock);
- if (error == 0)
- goto retry;
- }
-
- goto out;
- }
-
- slot = avail - 1;
- xfer = &chq->queue_xfers[slot];
- chq->queue_xfers_avail &= ~__BIT(slot);
-
- KASSERT((chq->active_xfers_used & __BIT(slot)) == 0);
-
- /* zero everything after the callout member */
- memset(&xfer->c_startzero, 0,
- sizeof(struct ata_xfer) - offsetof(struct ata_xfer, c_startzero));
-
-out:
- ata_channel_unlock(chp);
- return xfer;
-}
-
-/*
- * ata_deactivate_xfer() must be always called prior to ata_free_xfer()
- */
-void
-ata_free_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
-{
- struct ata_queue *chq = chp->ch_queue;
-
- ata_channel_lock(chp);
-
- if (xfer->c_flags & (C_WAITACT|C_WAITTIMO)) {
- /* Someone is waiting for this xfer, so we can't free now */
- xfer->c_flags |= C_FREE;
- cv_signal(&xfer->c_active);
- goto out;
- }
-
-#if NATA_PIOBM /* XXX wdc dependent code */
- if (xfer->c_flags & C_PIOBM) {
- struct wdc_softc *wdc = CHAN_TO_WDC(chp);
-
- /* finish the busmastering PIO */
- (*wdc->piobm_done)(wdc->dma_arg,
- chp->ch_channel, xfer->c_drive);
- chp->ch_flags &= ~(ATACH_DMA_WAIT | ATACH_PIOBM_WAIT);
- }
-#endif
-
- if (chp->ch_atac->atac_free_hw)
- chp->ch_atac->atac_free_hw(chp);
-
- KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) == 0);
- KASSERT((chq->queue_xfers_avail & __BIT(xfer->c_slot)) == 0);
- chq->queue_xfers_avail |= __BIT(xfer->c_slot);
-
-out:
- if (chq->queue_flags & QF_NEED_XFER) {
- chq->queue_flags &= ~QF_NEED_XFER;
- cv_broadcast(&chq->queue_busy);
- }
-
- ata_channel_unlock(chp);
-}
-#if NATABUS
static void
ata_activate_xfer_locked(struct ata_channel *chp, struct ata_xfer *xfer)
{
@@ -2552,74 +2258,7 @@ atacmd_toncq(struct ata_xfer *xfer, uint
if (xfer->c_bio.flags & ATA_FUA)
*device |= WDSD_FUA;
}
-#endif /* NATABUS */
-
-/*
- * Must be called without any locks, i.e. with both drive and channel locks
- * released.
- */
-void
-ata_channel_start(struct ata_channel *chp, int drive)
-{
- int i, s;
- struct ata_drive_datas *drvp;
-
- s = splbio();
-
- KASSERT(chp->ch_ndrives > 0);
-
-#define ATA_DRIVE_START(chp, drive) \
- do { \
- KASSERT(drive < chp->ch_ndrives); \
- drvp = &chp->ch_drive[drive]; \
- \
- if (drvp->drive_type != ATA_DRIVET_ATA && \
- drvp->drive_type != ATA_DRIVET_ATAPI && \
- drvp->drive_type != ATA_DRIVET_OLD) \
- continue; \
- \
- if (drvp->drv_start != NULL) \
- (*drvp->drv_start)(drvp->drv_softc); \
- } while (0)
-
- /*
- * Process drives in round robin fashion starting with next one after
- * the one which finished transfer. Thus no single drive would
- * completely starve other drives on same channel.
- * This loop processes all but the current drive, so won't do anything
- * if there is only one drive in channel.
- */
- for (i = (drive + 1) % chp->ch_ndrives; i != drive;
- i = (i + 1) % chp->ch_ndrives) {
- ATA_DRIVE_START(chp, i);
- }
-
- /* Now try to kick off xfers on the current drive */
- ATA_DRIVE_START(chp, drive);
-
- splx(s);
-#undef ATA_DRIVE_START
-}
-
-void
-ata_channel_lock(struct ata_channel *chp)
-{
- mutex_enter(&chp->ch_lock);
-}
-
-void
-ata_channel_unlock(struct ata_channel *chp)
-{
- mutex_exit(&chp->ch_lock);
-}
-
-void
-ata_channel_lock_owned(struct ata_channel *chp)
-{
- KASSERT(mutex_owned(&chp->ch_lock));
-}
-#if NATABUS
void
ata_wait_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
{
@@ -2635,4 +2274,3 @@ ata_wake_xfer(struct ata_channel *chp, s
cv_signal(&xfer->c_finish);
}
-#endif /* NATABUS */
Index: src/sys/dev/ata/ataconf.h
diff -u src/sys/dev/ata/ataconf.h:1.4 src/sys/dev/ata/ataconf.h:1.5
--- src/sys/dev/ata/ataconf.h:1.4 Sun Oct 8 19:00:29 2017
+++ src/sys/dev/ata/ataconf.h Tue Oct 10 17:19:38 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ataconf.h,v 1.4 2017/10/08 19:00:29 jdolecek Exp $ */
+/* $NetBSD: ataconf.h,v 1.5 2017/10/10 17:19:38 jdolecek Exp $ */
/*
* Written in 2006 by ITOH Yasufumi.
@@ -18,7 +18,6 @@
# define NATA_DMA 1
# define NATA_UDMA 1
# define NATA_PIOBM 1
-# define NATABUS 1
#else
Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.93 src/sys/dev/ata/atavar.h:1.94
--- src/sys/dev/ata/atavar.h:1.93 Sat Oct 7 16:05:32 2017
+++ src/sys/dev/ata/atavar.h Tue Oct 10 17:19:38 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: atavar.h,v 1.93 2017/10/07 16:05:32 jdolecek Exp $ */
+/* $NetBSD: atavar.h,v 1.94 2017/10/10 17:19:38 jdolecek Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -556,11 +556,14 @@ void ata_dmaerr(struct ata_drive_datas *
struct ata_queue *
ata_queue_alloc(uint8_t openings);
void ata_queue_free(struct ata_queue *);
+void ata_queue_reset(struct ata_queue *);
struct ata_xfer *
ata_queue_hwslot_to_xfer(struct ata_channel *, int);
struct ata_xfer *
ata_queue_get_active_xfer(struct ata_channel *);
struct ata_xfer *
+ ata_queue_get_active_xfer_locked(struct ata_channel *);
+struct ata_xfer *
ata_queue_drive_active_xfer(struct ata_channel *, int);
void ata_delay(struct ata_channel *, int, const char *, int);
Index: src/sys/dev/ata/files.ata
diff -u src/sys/dev/ata/files.ata:1.26 src/sys/dev/ata/files.ata:1.27
--- src/sys/dev/ata/files.ata:1.26 Sun Oct 8 19:00:29 2017
+++ src/sys/dev/ata/files.ata Tue Oct 10 17:19:38 2017
@@ -1,4 +1,4 @@
-# $NetBSD: files.ata,v 1.26 2017/10/08 19:00:29 jdolecek Exp $
+# $NetBSD: files.ata,v 1.27 2017/10/10 17:19:38 jdolecek Exp $
#
# Config file and device description for machine-independent devices
# which attach to ATA busses. Included by ports that need it. Ports
@@ -14,7 +14,8 @@ file dev/ata/ata_wdc.c wd & atabus & wd
defflag opt_wd.h WD_SOFTBADSECT
defflag opt_wd.h WD_CHAOS_MONKEY
-file dev/ata/ata.c (ata_hl | atapi)
+file dev/ata/ata.c (ata_hl | atapi) & atabus
+file dev/ata/ata_subr.c (ata_hl | atapi)
# ATA RAID configuration support
defpseudodev ataraid {[vendtype = -1], [unit = -1]}
Added files:
Index: src/sys/dev/ata/ata_subr.c
diff -u /dev/null src/sys/dev/ata/ata_subr.c:1.1
--- /dev/null Tue Oct 10 17:19:39 2017
+++ src/sys/dev/ata/ata_subr.c Tue Oct 10 17:19:38 2017
@@ -0,0 +1,420 @@
+/* $NetBSD: ata_subr.c,v 1.1 2017/10/10 17:19:38 jdolecek Exp $ */
+
+/*
+ * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ata_subr.c,v 1.1 2017/10/10 17:19:38 jdolecek Exp $");
+
+#include "opt_ata.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/kthread.h>
+#include <sys/errno.h>
+#include <sys/ataio.h>
+#include <sys/kmem.h>
+#include <sys/intr.h>
+#include <sys/bus.h>
+#include <sys/once.h>
+#include <sys/bitops.h>
+
+#define ATABUS_PRIVATE
+
+#include <dev/ata/ataconf.h>
+#include <dev/ata/atareg.h>
+#include <dev/ata/atavar.h>
+#include <dev/ic/wdcvar.h> /* for PIOBM */
+
+#define DEBUG_FUNCS 0x08
+#define DEBUG_PROBE 0x10
+#define DEBUG_DETACH 0x20
+#define DEBUG_XFERS 0x40
+#ifdef ATADEBUG
+extern int atadebug_mask;
+#define ATADEBUG_PRINT(args, level) \
+ if (atadebug_mask & (level)) \
+ printf args
+#else
+#define ATADEBUG_PRINT(args, level)
+#endif
+
+void
+ata_queue_reset(struct ata_queue *chq)
+{
+ /* make sure that we can use polled commands */
+ TAILQ_INIT(&chq->queue_xfer);
+ TAILQ_INIT(&chq->active_xfers);
+ chq->queue_freeze = 0;
+ chq->queue_active = 0;
+ chq->active_xfers_used = 0;
+ chq->queue_xfers_avail = __BIT(chq->queue_openings) - 1;
+}
+
+struct ata_xfer *
+ata_queue_hwslot_to_xfer(struct ata_channel *chp, int hwslot)
+{
+ struct ata_queue *chq = chp->ch_queue;
+ struct ata_xfer *xfer = NULL;
+
+ ata_channel_lock(chp);
+
+ KASSERTMSG(hwslot < chq->queue_openings, "hwslot %d > openings %d",
+ hwslot, chq->queue_openings);
+ KASSERTMSG((chq->active_xfers_used & __BIT(hwslot)) != 0,
+ "hwslot %d not active", hwslot);
+
+ /* Usually the first entry will be the one */
+ TAILQ_FOREACH(xfer, &chq->active_xfers, c_activechain) {
+ if (xfer->c_slot == hwslot)
+ break;
+ }
+
+ ata_channel_unlock(chp);
+
+ KASSERTMSG((xfer != NULL),
+ "%s: xfer with slot %d not found (active %x)", __func__,
+ hwslot, chq->active_xfers_used);
+
+ return xfer;
+}
+
+struct ata_xfer *
+ata_queue_get_active_xfer_locked(struct ata_channel *chp)
+{
+ struct ata_xfer *xfer;
+
+ KASSERT(mutex_owned(&chp->ch_lock));
+ xfer = TAILQ_FIRST(&chp->ch_queue->active_xfers);
+
+ if (xfer && ISSET(xfer->c_flags, C_NCQ)) {
+ /* Spurious call, never return NCQ xfer from this interface */
+ xfer = NULL;
+ }
+
+ return xfer;
+}
+
+/*
+ * This interface is supposed only to be used when there is exactly
+ * one outstanding command, when there is no information about the slot,
+ * which triggered the command. ata_queue_hwslot_to_xfer() interface
+ * is preferred in all NCQ cases.
+ */
+struct ata_xfer *
+ata_queue_get_active_xfer(struct ata_channel *chp)
+{
+ struct ata_xfer *xfer = NULL;
+
+ ata_channel_lock(chp);
+ xfer = ata_queue_get_active_xfer_locked(chp);
+ ata_channel_unlock(chp);
+
+ return xfer;
+}
+
+struct ata_xfer *
+ata_queue_drive_active_xfer(struct ata_channel *chp, int drive)
+{
+ struct ata_xfer *xfer = NULL;
+
+ ata_channel_lock(chp);
+
+ TAILQ_FOREACH(xfer, &chp->ch_queue->active_xfers, c_activechain) {
+ if (xfer->c_drive == drive)
+ break;
+ }
+ KASSERT(xfer != NULL);
+
+ ata_channel_unlock(chp);
+
+ return xfer;
+}
+
+static void
+ata_xfer_init(struct ata_xfer *xfer, uint8_t slot)
+{
+ memset(xfer, 0, sizeof(*xfer));
+
+ xfer->c_slot = slot;
+
+ cv_init(&xfer->c_active, "ataact");
+ cv_init(&xfer->c_finish, "atafin");
+ callout_init(&xfer->c_timo_callout, 0); /* XXX MPSAFE */
+ callout_init(&xfer->c_retry_callout, 0); /* XXX MPSAFE */
+}
+
+static void
+ata_xfer_destroy(struct ata_xfer *xfer)
+{
+ callout_halt(&xfer->c_timo_callout, NULL); /* XXX MPSAFE */
+ callout_destroy(&xfer->c_timo_callout);
+ callout_halt(&xfer->c_retry_callout, NULL); /* XXX MPSAFE */
+ callout_destroy(&xfer->c_retry_callout);
+ cv_destroy(&xfer->c_active);
+ cv_destroy(&xfer->c_finish);
+}
+
+struct ata_queue *
+ata_queue_alloc(uint8_t openings)
+{
+ if (openings == 0)
+ openings = 1;
+
+ if (openings > ATA_MAX_OPENINGS)
+ openings = ATA_MAX_OPENINGS;
+
+ struct ata_queue *chq = malloc(offsetof(struct ata_queue, queue_xfers[openings]),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ chq->queue_openings = openings;
+ ata_queue_reset(chq);
+
+ cv_init(&chq->queue_busy, "ataqbusy");
+ cv_init(&chq->queue_drain, "atdrn");
+ cv_init(&chq->queue_idle, "qidl");
+
+ for (uint8_t i = 0; i < openings; i++)
+ ata_xfer_init(&chq->queue_xfers[i], i);
+
+ return chq;
+}
+
+void
+ata_queue_free(struct ata_queue *chq)
+{
+ for (uint8_t i = 0; i < chq->queue_openings; i++)
+ ata_xfer_destroy(&chq->queue_xfers[i]);
+
+ cv_destroy(&chq->queue_busy);
+ cv_destroy(&chq->queue_drain);
+ cv_destroy(&chq->queue_idle);
+
+ free(chq, M_DEVBUF);
+}
+
+void
+ata_channel_init(struct ata_channel *chp)
+{
+ mutex_init(&chp->ch_lock, MUTEX_DEFAULT, IPL_BIO);
+ cv_init(&chp->ch_thr_idle, "atath");
+}
+
+void
+ata_channel_destroy(struct ata_channel *chp)
+{
+ mutex_destroy(&chp->ch_lock);
+ cv_destroy(&chp->ch_thr_idle);
+}
+
+/*
+ * Does it's own locking, does not require splbio().
+ * flags - whether to block waiting for free xfer
+ * openings - limit of openings supported by device, <= 0 means tag not
+ * relevant, and any available xfer can be returned
+ */
+struct ata_xfer *
+ata_get_xfer_ext(struct ata_channel *chp, int flags, uint8_t openings)
+{
+ struct ata_queue *chq = chp->ch_queue;
+ struct ata_xfer *xfer = NULL;
+ uint32_t avail, slot, mask;
+ int error;
+
+ ATADEBUG_PRINT(("%s: channel %d flags %x openings %d\n",
+ __func__, chp->ch_channel, flags, openings),
+ DEBUG_XFERS);
+
+ ata_channel_lock(chp);
+
+ /*
+ * When openings is just 1, can't reserve anything for
+ * recovery. KASSERT() here is to catch code which naively
+ * relies on C_RECOVERY to work under this condition.
+ */
+ KASSERT((flags & C_RECOVERY) == 0 || chq->queue_openings > 1);
+
+ if (flags & C_RECOVERY) {
+ mask = UINT32_MAX;
+ } else {
+ if (openings <= 0 || openings > chq->queue_openings)
+ openings = chq->queue_openings;
+
+ if (openings > 1) {
+ mask = __BIT(openings - 1) - 1;
+ } else {
+ mask = UINT32_MAX;
+ }
+ }
+
+retry:
+ avail = ffs32(chq->queue_xfers_avail & mask);
+ if (avail == 0) {
+ /*
+ * Catch code which tries to get another recovery xfer while
+ * already holding one (wrong recursion).
+ */
+ KASSERTMSG((flags & C_RECOVERY) == 0,
+ "recovery xfer busy openings %d mask %x avail %x",
+ openings, mask, chq->queue_xfers_avail);
+
+ if (flags & C_WAIT) {
+ chq->queue_flags |= QF_NEED_XFER;
+ error = cv_wait_sig(&chq->queue_busy, &chp->ch_lock);
+ if (error == 0)
+ goto retry;
+ }
+
+ goto out;
+ }
+
+ slot = avail - 1;
+ xfer = &chq->queue_xfers[slot];
+ chq->queue_xfers_avail &= ~__BIT(slot);
+
+ KASSERT((chq->active_xfers_used & __BIT(slot)) == 0);
+
+ /* zero everything after the callout member */
+ memset(&xfer->c_startzero, 0,
+ sizeof(struct ata_xfer) - offsetof(struct ata_xfer, c_startzero));
+
+out:
+ ata_channel_unlock(chp);
+ return xfer;
+}
+
+/*
+ * ata_deactivate_xfer() must be always called prior to ata_free_xfer()
+ */
+void
+ata_free_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+ struct ata_queue *chq = chp->ch_queue;
+
+ ata_channel_lock(chp);
+
+ if (xfer->c_flags & (C_WAITACT|C_WAITTIMO)) {
+ /* Someone is waiting for this xfer, so we can't free now */
+ xfer->c_flags |= C_FREE;
+ cv_signal(&xfer->c_active);
+ goto out;
+ }
+
+#if NATA_PIOBM /* XXX wdc dependent code */
+ if (xfer->c_flags & C_PIOBM) {
+ struct wdc_softc *wdc = CHAN_TO_WDC(chp);
+
+ /* finish the busmastering PIO */
+ (*wdc->piobm_done)(wdc->dma_arg,
+ chp->ch_channel, xfer->c_drive);
+ chp->ch_flags &= ~(ATACH_DMA_WAIT | ATACH_PIOBM_WAIT);
+ }
+#endif
+
+ if (chp->ch_atac->atac_free_hw)
+ chp->ch_atac->atac_free_hw(chp);
+
+ KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) == 0);
+ KASSERT((chq->queue_xfers_avail & __BIT(xfer->c_slot)) == 0);
+ chq->queue_xfers_avail |= __BIT(xfer->c_slot);
+
+out:
+ if (chq->queue_flags & QF_NEED_XFER) {
+ chq->queue_flags &= ~QF_NEED_XFER;
+ cv_broadcast(&chq->queue_busy);
+ }
+
+ ata_channel_unlock(chp);
+}
+
+/*
+ * Must be called without any locks, i.e. with both drive and channel locks
+ * released.
+ */
+void
+ata_channel_start(struct ata_channel *chp, int drive)
+{
+ int i, s;
+ struct ata_drive_datas *drvp;
+
+ s = splbio();
+
+ KASSERT(chp->ch_ndrives > 0);
+
+#define ATA_DRIVE_START(chp, drive) \
+ do { \
+ KASSERT(drive < chp->ch_ndrives); \
+ drvp = &chp->ch_drive[drive]; \
+ \
+ if (drvp->drive_type != ATA_DRIVET_ATA && \
+ drvp->drive_type != ATA_DRIVET_ATAPI && \
+ drvp->drive_type != ATA_DRIVET_OLD) \
+ continue; \
+ \
+ if (drvp->drv_start != NULL) \
+ (*drvp->drv_start)(drvp->drv_softc); \
+ } while (0)
+
+ /*
+ * Process drives in round robin fashion starting with next one after
+ * the one which finished transfer. Thus no single drive would
+ * completely starve other drives on same channel.
+ * This loop processes all but the current drive, so won't do anything
+ * if there is only one drive in channel.
+ */
+ for (i = (drive + 1) % chp->ch_ndrives; i != drive;
+ i = (i + 1) % chp->ch_ndrives) {
+ ATA_DRIVE_START(chp, i);
+ }
+
+ /* Now try to kick off xfers on the current drive */
+ ATA_DRIVE_START(chp, drive);
+
+ splx(s);
+#undef ATA_DRIVE_START
+}
+
+void
+ata_channel_lock(struct ata_channel *chp)
+{
+ mutex_enter(&chp->ch_lock);
+}
+
+void
+ata_channel_unlock(struct ata_channel *chp)
+{
+ mutex_exit(&chp->ch_lock);
+}
+
+void
+ata_channel_lock_owned(struct ata_channel *chp)
+{
+ KASSERT(mutex_owned(&chp->ch_lock));
+}