--- sys/conf/files.amd64.orig Tue Oct 17 11:39:31 2006 +++ sys/conf/files.amd64 Tue Oct 17 11:40:25 2006 @@ -183,6 +183,7 @@ dev/kbd/kbd.c optional sc dev/kbd/kbd.c optional ukbd dev/mem/memutil.c optional mem +dev/nve/if_nfe.c optional nfe pci dev/nve/if_nve.c optional nve pci dev/rr232x/os_bsd.c optional rr232x dev/rr232x/osm_bsd.c optional rr232x --- sys/conf/files.i386.orig Tue Oct 17 11:39:24 2006 +++ sys/conf/files.i386 Tue Oct 17 11:40:07 2006 @@ -217,6 +217,7 @@ dev/mem/memutil.c optional mem dev/mse/mse.c optional mse dev/mse/mse_isa.c optional mse isa +dev/nve/if_nfe.c optional nfe pci dev/nve/if_nve.c optional nve pci dev/ppc/ppc.c optional ppc dev/ppc/ppc_puc.c optional ppc puc pci --- sys/dev/nfe/if_nfe.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/nfe/if_nfe.c Tue Oct 17 11:41:45 2006 @@ -0,0 +1,2084 @@ +/* $OpenBSD: if_nfe.c,v 1.54 2006/04/07 12:38:12 jsg Exp $ */ + +/*- + * Copyright (c) 2006 Shigeaki Tagashira + * Copyright (c) 2006 Damien Bergamini + * Copyright (c) 2005, 2006 Jonathan Gray + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Driver for NVIDIA nForce MCP Fast Ethernet and Gigabit Ethernet */ + +#include +__FBSDID("$FreeBSD: src/sys/dev/nfe/if_nfe.c,v 1.14 2006/11/27 04:47:26 obrien Exp $"); + +/* Uncomment the following line to enable polling. */ +/* #define DEVICE_POLLING */ + +#define NFE_JUMBO +#define NFE_CSUM +#define NVLAN 0 + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +MODULE_DEPEND(nfe, pci, 1, 1, 1); +MODULE_DEPEND(nfe, ether, 1, 1, 1); +MODULE_DEPEND(nfe, miibus, 1, 1, 1); +#include "miibus_if.h" + +static int nfe_probe(device_t); +static int nfe_attach(device_t); +static int nfe_detach(device_t); +static void nfe_shutdown(device_t); +static int nfe_miibus_readreg(device_t, int, int); +static int nfe_miibus_writereg(device_t, int, int, int); +static void nfe_miibus_statchg(device_t); +static int nfe_ioctl(struct ifnet *, u_long, caddr_t); +static void nfe_intr(void *); +static void nfe_txdesc32_sync(struct nfe_softc *, struct nfe_desc32 *, int); +static void nfe_txdesc64_sync(struct nfe_softc *, struct nfe_desc64 *, int); +static void nfe_txdesc32_rsync(struct nfe_softc *, int, int, int); +static void nfe_txdesc64_rsync(struct nfe_softc *, int, int, int); +static void nfe_rxdesc32_sync(struct nfe_softc *, struct nfe_desc32 *, int); +static void nfe_rxdesc64_sync(struct nfe_softc *, struct nfe_desc64 *, int); +static void nfe_rxeof(struct nfe_softc *); +static void nfe_txeof(struct nfe_softc *); +static int nfe_encap(struct nfe_softc *, struct mbuf *); +static void nfe_setmulti(struct nfe_softc *); +static void nfe_start(struct ifnet *); +static void nfe_start_locked(struct ifnet *); +static void nfe_watchdog(struct ifnet *); +static void nfe_init(void *); +static void nfe_init_locked(void *); +static void nfe_stop(struct ifnet *, int); +static int nfe_alloc_rx_ring(struct nfe_softc *, struct nfe_rx_ring *); +static void nfe_reset_rx_ring(struct nfe_softc *, struct nfe_rx_ring *); +static void nfe_free_rx_ring(struct nfe_softc *, struct nfe_rx_ring *); +static int nfe_alloc_tx_ring(struct nfe_softc *, struct nfe_tx_ring *); +static void nfe_reset_tx_ring(struct nfe_softc *, struct nfe_tx_ring *); +static void nfe_free_tx_ring(struct nfe_softc *, struct nfe_tx_ring *); +static int nfe_ifmedia_upd(struct ifnet *); +static int nfe_ifmedia_upd_locked(struct ifnet *); +static void nfe_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static void nfe_tick(void *); +static void nfe_tick_locked(struct nfe_softc *); +static void nfe_get_macaddr(struct nfe_softc *, u_char *); +static void nfe_set_macaddr(struct nfe_softc *, u_char *); +static void nfe_dma_map_segs (void *, bus_dma_segment_t *, int, int); +#ifdef DEVICE_POLLING +static void nfe_poll_locked(struct ifnet *, enum poll_cmd, int); +#endif + +#ifdef NFE_DEBUG +int nfedebug = 0; +#define DPRINTF(x) do { if (nfedebug) printf x; } while (0) +#define DPRINTFN(n,x) do { if (nfedebug >= (n)) printf x; } while (0) +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define NFE_LOCK(_sc) mtx_lock(&(_sc)->nfe_mtx) +#define NFE_UNLOCK(_sc) mtx_unlock(&(_sc)->nfe_mtx) +#define NFE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->nfe_mtx, MA_OWNED) + +#define letoh16(x) le16toh(x) + +#define NV_RID 0x10 + +static device_method_t nfe_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nfe_probe), + DEVMETHOD(device_attach, nfe_attach), + DEVMETHOD(device_detach, nfe_detach), + DEVMETHOD(device_shutdown, nfe_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, nfe_miibus_readreg), + DEVMETHOD(miibus_writereg, nfe_miibus_writereg), + DEVMETHOD(miibus_statchg, nfe_miibus_statchg), + + { 0, 0 } +}; + +static driver_t nfe_driver = { + "nfe", + nfe_methods, + sizeof(struct nfe_softc) +}; + +static devclass_t nfe_devclass; + +DRIVER_MODULE(nfe, pci, nfe_driver, nfe_devclass, 0, 0); +DRIVER_MODULE(miibus, nfe, miibus_driver, miibus_devclass, 0, 0); + +static struct nfe_type nfe_devs[] = { + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_LAN, + "NVIDIA nForce MCP Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_LAN, + "NVIDIA nForce2 MCP2 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN1, + "NVIDIA nForce2 400 MCP4 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN2, + "NVIDIA nForce2 400 MCP5 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN1, + "NVIDIA nForce3 MCP3 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_250_LAN, + "NVIDIA nForce3 250 MCP6 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN4, + "NVIDIA nForce3 MCP7 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_LAN1, + "NVIDIA nForce4 CK804 MCP8 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_LAN2, + "NVIDIA nForce4 CK804 MCP9 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP04_LAN1, + "NVIDIA nForce MCP04 Networking Adapter"}, // MCP10 + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP04_LAN2, + "NVIDIA nForce MCP04 Networking Adapter"}, // MCP11 + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE430_LAN1, + "NVIDIA nForce 430 MCP12 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE430_LAN2, + "NVIDIA nForce 430 MCP13 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP55_LAN1, + "NVIDIA nForce MCP55 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP55_LAN2, + "NVIDIA nForce MCP55 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN1, + "NVIDIA nForce MCP61 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN2, + "NVIDIA nForce MCP61 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN3, + "NVIDIA nForce MCP61 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN2, + "NVIDIA nForce MCP61 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN1, + "NVIDIA nForce MCP65 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN2, + "NVIDIA nForce MCP65 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN3, + "NVIDIA nForce MCP65 Networking Adapter"}, + {PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN2, + "NVIDIA nForce MCP65 Networking Adapter"}, + {0, 0, NULL} +}; + + +/* Probe for supported hardware ID's */ +static int +nfe_probe(device_t dev) +{ + struct nfe_type *t; + + t = nfe_devs; + /* Check for matching PCI DEVICE ID's */ + while (t->name != NULL) { + if ((pci_get_vendor(dev) == t->vid_id) && + (pci_get_device(dev) == t->dev_id)) { + device_set_desc(dev, t->name); + return (0); + } + t++; + } + + return (ENXIO); +} + + +static int +nfe_attach(device_t dev) +{ + struct nfe_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + sc->nfe_dev = dev; + sc->nfe_unit = unit; + + mtx_init(&sc->nfe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + callout_init_mtx(&sc->nfe_stat_ch, &sc->nfe_mtx, 0); + + pci_enable_busmaster(dev); + + rid = NV_RID; + sc->nfe_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->nfe_res == NULL) { + printf ("nfe%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->nfe_memt = rman_get_bustag(sc->nfe_res); + sc->nfe_memh = rman_get_bushandle(sc->nfe_res); + + /* Allocate interrupt */ + rid = 0; + sc->nfe_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + + if (sc->nfe_irq == NULL) { + printf("nfe%d: couldn't map interrupt\n", unit); + error = ENXIO; + goto fail; + } + + nfe_get_macaddr(sc, sc->eaddr); + + sc->nfe_flags = 0; + + switch (pci_get_device(dev)) { + case PCI_PRODUCT_NVIDIA_NFORCE3_LAN2: + case PCI_PRODUCT_NVIDIA_NFORCE3_LAN3: + case PCI_PRODUCT_NVIDIA_NFORCE3_LAN4: + case PCI_PRODUCT_NVIDIA_NFORCE3_LAN5: + sc->nfe_flags |= NFE_JUMBO_SUP | NFE_HW_CSUM; + break; + case PCI_PRODUCT_NVIDIA_MCP51_LAN1: + case PCI_PRODUCT_NVIDIA_MCP51_LAN2: + sc->nfe_flags |= NFE_40BIT_ADDR; + break; + case PCI_PRODUCT_NVIDIA_CK804_LAN1: + case PCI_PRODUCT_NVIDIA_CK804_LAN2: + case PCI_PRODUCT_NVIDIA_MCP04_LAN1: + case PCI_PRODUCT_NVIDIA_MCP04_LAN2: + sc->nfe_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR | NFE_HW_CSUM; + break; + case PCI_PRODUCT_NVIDIA_MCP55_LAN1: + case PCI_PRODUCT_NVIDIA_MCP55_LAN2: + sc->nfe_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR | NFE_HW_CSUM | + NFE_HW_VLAN; + break; + case PCI_PRODUCT_NVIDIA_MCP61_LAN1: + case PCI_PRODUCT_NVIDIA_MCP61_LAN2: + case PCI_PRODUCT_NVIDIA_MCP61_LAN3: + case PCI_PRODUCT_NVIDIA_MCP61_LAN4: + sc->nfe_flags |= NFE_40BIT_ADDR; + break; + case PCI_PRODUCT_NVIDIA_MCP65_LAN1: + case PCI_PRODUCT_NVIDIA_MCP65_LAN2: + case PCI_PRODUCT_NVIDIA_MCP65_LAN3: + case PCI_PRODUCT_NVIDIA_MCP65_LAN4: + sc->nfe_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR | NFE_HW_CSUM; + break; + } + + /* + * Allocate the parent bus DMA tag appropriate for PCI. + */ +#define NFE_NSEG_NEW 32 + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, NFE_NSEG_NEW, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->nfe_parent_tag); + if (error) + goto fail; + + ifp = sc->nfe_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + printf("nfe%d: can not if_alloc()\n", unit); + error = ENOSPC; + goto fail; + } + sc->nfe_mtu = ifp->if_mtu = ETHERMTU; + + /* + * Allocate Tx and Rx rings. + */ + if (nfe_alloc_tx_ring(sc, &sc->txq) != 0) { + printf("nfe%d: could not allocate Tx ring\n", unit); + error = ENXIO; + goto fail; + } + + if (nfe_alloc_rx_ring(sc, &sc->rxq) != 0) { + printf("nfe%d: could not allocate Rx ring\n", unit); + nfe_free_tx_ring(sc, &sc->txq); + error = ENXIO; + goto fail; + } + + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = nfe_ioctl; + ifp->if_start = nfe_start; + /* ifp->if_hwassist = NFE_CSUM_FEATURES; */ + ifp->if_watchdog = nfe_watchdog; + ifp->if_init = nfe_init; + ifp->if_baudrate = IF_Gbps(1); + ifp->if_snd.ifq_maxlen = NFE_IFQ_MAXLEN; + + ifp->if_capabilities = IFCAP_VLAN_MTU; + +#ifdef NFE_JUMBO + ifp->if_capabilities |= IFCAP_JUMBO_MTU; +#else + ifp->if_capabilities &= ~IFCAP_JUMBO_MTU; + sc->nfe_flags &= ~NFE_JUMBO_SUP; +#endif + +#if NVLAN > 0 + if (sc->nfe_flags & NFE_HW_VLAN) + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; +#endif +#ifdef NFE_CSUM + if (sc->nfe_flags & NFE_HW_CSUM) { + ifp->if_capabilities |= IFCAP_HWCSUM; + ifp->if_capenable |= IFCAP_HWCSUM; + ifp->if_hwassist = NFE_CSUM_FEATURES; + } +#else + sc->nfe_flags &= ~NFE_HW_CSUM; +#endif + ifp->if_capenable = ifp->if_capabilities; + +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif + + /* Do MII setup */ + if (mii_phy_probe(dev, &sc->nfe_miibus, nfe_ifmedia_upd, + nfe_ifmedia_sts)) { + printf("nfe%d: MII without any phy!\n", unit); + error = ENXIO; + goto fail; + } + + ether_ifattach(ifp, sc->eaddr); + + error = bus_setup_intr(dev, sc->nfe_irq, INTR_TYPE_NET | INTR_MPSAFE, + nfe_intr, sc, &sc->nfe_intrhand); + + if (error) { + printf("nfe%d: couldn't set up irq\n", unit); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + nfe_detach(dev); + + return (error); +} + + +static int +nfe_detach(device_t dev) +{ + struct nfe_softc *sc; + struct ifnet *ifp; + u_char eaddr[ETHER_ADDR_LEN]; + int i; + + sc = device_get_softc(dev); + KASSERT(mtx_initialized(&sc->nfe_mtx), ("nfe mutex not initialized")); + ifp = sc->nfe_ifp; + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(ifp); +#endif + + for (i = 0; i < ETHER_ADDR_LEN; i++) { + eaddr[i] = sc->eaddr[5 - i]; + } + nfe_set_macaddr(sc, eaddr); + + if (device_is_attached(dev)) { + NFE_LOCK(sc); + nfe_stop(ifp, 1); + ifp->if_flags &= ~IFF_UP; + NFE_UNLOCK(sc); + callout_drain(&sc->nfe_stat_ch); + ether_ifdetach(ifp); + } + + if (ifp) + if_free(ifp); + if (sc->nfe_miibus) + device_delete_child(dev, sc->nfe_miibus); + bus_generic_detach(dev); + + if (sc->nfe_intrhand) + bus_teardown_intr(dev, sc->nfe_irq, sc->nfe_intrhand); + if (sc->nfe_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nfe_irq); + if (sc->nfe_res) + bus_release_resource(dev, SYS_RES_MEMORY, NV_RID, sc->nfe_res); + + nfe_free_tx_ring(sc, &sc->txq); + nfe_free_rx_ring(sc, &sc->rxq); + + if (sc->nfe_parent_tag) + bus_dma_tag_destroy(sc->nfe_parent_tag); + + mtx_destroy(&sc->nfe_mtx); + + return (0); +} + + +static void +nfe_miibus_statchg(device_t dev) +{ + struct nfe_softc *sc; + struct mii_data *mii; + u_int32_t phy, seed, misc = NFE_MISC1_MAGIC, link = NFE_MEDIA_SET; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->nfe_miibus); + + phy = NFE_READ(sc, NFE_PHY_IFACE); + phy &= ~(NFE_PHY_HDX | NFE_PHY_100TX | NFE_PHY_1000T); + + seed = NFE_READ(sc, NFE_RNDSEED); + seed &= ~NFE_SEED_MASK; + + if ((mii->mii_media_active & IFM_GMASK) == IFM_HDX) { + phy |= NFE_PHY_HDX; /* half-duplex */ + misc |= NFE_MISC1_HDX; + } + + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: /* full-duplex only */ + link |= NFE_MEDIA_1000T; + seed |= NFE_SEED_1000T; + phy |= NFE_PHY_1000T; + break; + case IFM_100_TX: + link |= NFE_MEDIA_100TX; + seed |= NFE_SEED_100TX; + phy |= NFE_PHY_100TX; + break; + case IFM_10_T: + link |= NFE_MEDIA_10T; + seed |= NFE_SEED_10T; + break; + } + + NFE_WRITE(sc, NFE_RNDSEED, seed); /* XXX: gigabit NICs only? */ + + NFE_WRITE(sc, NFE_PHY_IFACE, phy); + NFE_WRITE(sc, NFE_MISC1, misc); + NFE_WRITE(sc, NFE_LINKSPEED, link); +} + + +static int +nfe_miibus_readreg(device_t dev, int phy, int reg) +{ + struct nfe_softc *sc = device_get_softc(dev); + u_int32_t val; + int ntries; + + NFE_WRITE(sc, NFE_PHY_STATUS, 0xf); + + if (NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY) { + NFE_WRITE(sc, NFE_PHY_CTL, NFE_PHY_BUSY); + DELAY(100); + } + + NFE_WRITE(sc, NFE_PHY_CTL, (phy << NFE_PHYADD_SHIFT) | reg); + + for (ntries = 0; ntries < 1000; ntries++) { + DELAY(100); + if (!(NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY)) + break; + } + if (ntries == 1000) { + DPRINTFN(2, ("nfe%d: timeout waiting for PHY\n", sc->nfe_unit)); + return 0; + } + + if (NFE_READ(sc, NFE_PHY_STATUS) & NFE_PHY_ERROR) { + DPRINTFN(2, ("nfe%d: could not read PHY\n", sc->nfe_unit)); + return 0; + } + + val = NFE_READ(sc, NFE_PHY_DATA); + if (val != 0xffffffff && val != 0) + sc->mii_phyaddr = phy; + + DPRINTFN(2, ("nfe%d: mii read phy %d reg 0x%x ret 0x%x\n", + sc->nfe_unit, phy, reg, val)); + + return val; +} + + +static int +nfe_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct nfe_softc *sc = device_get_softc(dev); + u_int32_t ctl; + int ntries; + + NFE_WRITE(sc, NFE_PHY_STATUS, 0xf); + + if (NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY) { + NFE_WRITE(sc, NFE_PHY_CTL, NFE_PHY_BUSY); + DELAY(100); + } + + NFE_WRITE(sc, NFE_PHY_DATA, val); + ctl = NFE_PHY_WRITE | (phy << NFE_PHYADD_SHIFT) | reg; + NFE_WRITE(sc, NFE_PHY_CTL, ctl); + + for (ntries = 0; ntries < 1000; ntries++) { + DELAY(100); + if (!(NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY)) + break; + } +#ifdef NFE_DEBUG + if (nfedebug >= 2 && ntries == 1000) + printf("could not write to PHY\n"); +#endif + return 0; +} + + +static int +nfe_alloc_rx_ring(struct nfe_softc *sc, struct nfe_rx_ring *ring) +{ + struct nfe_desc32 *desc32; + struct nfe_desc64 *desc64; + struct nfe_rx_data *data; + void **desc; + bus_addr_t physaddr; + int i, error, descsize; + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc = (void **)&ring->desc64; + descsize = sizeof (struct nfe_desc64); + } else { + desc = (void **)&ring->desc32; + descsize = sizeof (struct nfe_desc32); + } + + ring->cur = ring->next = 0; + ring->bufsz = (sc->nfe_mtu + NFE_RX_HEADERS <= MCLBYTES) ? + MCLBYTES : MJUM9BYTES; + + error = bus_dma_tag_create(sc->nfe_parent_tag, + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + NFE_RX_RING_COUNT * descsize, 1, /* maxsize, nsegments */ + NFE_RX_RING_COUNT * descsize, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &ring->rx_desc_tag); + if (error != 0) { + printf("nfe%d: could not create desc DMA tag\n", sc->nfe_unit); + goto fail; + } + + /* allocate memory to desc */ + error = bus_dmamem_alloc(ring->rx_desc_tag, (void **)desc, + BUS_DMA_NOWAIT, &ring->rx_desc_map); + if (error != 0) { + printf("nfe%d: could not create desc DMA map\n", sc->nfe_unit); + goto fail; + } + + /* map desc to device visible address space */ + error = bus_dmamap_load(ring->rx_desc_tag, ring->rx_desc_map, *desc, + NFE_RX_RING_COUNT * descsize, nfe_dma_map_segs, + &ring->rx_desc_segs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("nfe%d: could not load desc DMA map\n", sc->nfe_unit); + goto fail; + } + + bzero(*desc, NFE_RX_RING_COUNT * descsize); + ring->rx_desc_addr = ring->rx_desc_segs.ds_addr; + ring->physaddr = ring->rx_desc_addr; + + /* + * Pre-allocate Rx buffers and populate Rx ring. + */ + for (i = 0; i < NFE_RX_RING_COUNT; i++) { + data = &sc->rxq.data[i]; + + MGETHDR(data->m, M_DONTWAIT, MT_DATA); + if (data->m == NULL) { + printf("nfe%d: could not allocate rx mbuf\n", + sc->nfe_unit); + error = ENOMEM; + goto fail; + } + + error = bus_dma_tag_create(sc->nfe_parent_tag, + ETHER_ALIGN, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, 1, /* maxsize, nsegments */ + MCLBYTES, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &data->rx_data_tag); + if (error != 0) { + printf("nfe%d: could not create DMA map\n", + sc->nfe_unit); + goto fail; + } + + error = bus_dmamap_create(data->rx_data_tag, 0, + &data->rx_data_map); + if (error != 0) { + printf("nfe%d: could not allocate mbuf cluster\n", + sc->nfe_unit); + goto fail; + } + + MCLGET(data->m, M_DONTWAIT); + if (!(data->m->m_flags & M_EXT)) { + error = ENOMEM; + goto fail; + } + + error = bus_dmamap_load(data->rx_data_tag, + data->rx_data_map, mtod(data->m, void *), + ring->bufsz, nfe_dma_map_segs, &data->rx_data_segs, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("nfe%d: could not load rx buf DMA map\n", + sc->nfe_unit); + goto fail; + } + + data->rx_data_addr = data->rx_data_segs.ds_addr; + physaddr = data->rx_data_addr; + + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc64 = &sc->rxq.desc64[i]; +#if defined(__LP64__) + desc64->physaddr[0] = htole32(physaddr >> 32); +#endif + desc64->physaddr[1] = htole32(physaddr & 0xffffffff); + desc64->length = htole16(sc->rxq.bufsz); + desc64->flags = htole16(NFE_RX_READY); + } else { + desc32 = &sc->rxq.desc32[i]; + desc32->physaddr = htole32(physaddr); + desc32->length = htole16(sc->rxq.bufsz); + desc32->flags = htole16(NFE_RX_READY); + } + + } + + bus_dmamap_sync(ring->rx_desc_tag, ring->rx_desc_map, + BUS_DMASYNC_PREWRITE); + + return 0; + +fail: nfe_free_rx_ring(sc, ring); + + return error; +} + + +static void +nfe_reset_rx_ring(struct nfe_softc *sc, struct nfe_rx_ring *ring) +{ + int i; + + for (i = 0; i < NFE_RX_RING_COUNT; i++) { + if (sc->nfe_flags & NFE_40BIT_ADDR) { + ring->desc64[i].length = htole16(ring->bufsz); + ring->desc64[i].flags = htole16(NFE_RX_READY); + } else { + ring->desc32[i].length = htole16(ring->bufsz); + ring->desc32[i].flags = htole16(NFE_RX_READY); + } + } + + bus_dmamap_sync(ring->rx_desc_tag, ring->rx_desc_map, + BUS_DMASYNC_PREWRITE); + + ring->cur = ring->next = 0; +} + + +static void +nfe_free_rx_ring(struct nfe_softc *sc, struct nfe_rx_ring *ring) +{ + struct nfe_rx_data *data; + void *desc; + int i, descsize; + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc = ring->desc64; + descsize = sizeof (struct nfe_desc64); + } else { + desc = ring->desc32; + descsize = sizeof (struct nfe_desc32); + } + + if (desc != NULL) { + bus_dmamap_sync(ring->rx_desc_tag, ring->rx_desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->rx_desc_tag, ring->rx_desc_map); + bus_dmamem_free(ring->rx_desc_tag, desc, ring->rx_desc_map); + bus_dma_tag_destroy(ring->rx_desc_tag); + } + + for (i = 0; i < NFE_RX_RING_COUNT; i++) { + data = &ring->data[i]; + + if (data->rx_data_map != NULL) { + bus_dmamap_sync(data->rx_data_tag, + data->rx_data_map, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(data->rx_data_tag, + data->rx_data_map); + bus_dmamap_destroy(data->rx_data_tag, + data->rx_data_map); + bus_dma_tag_destroy(data->rx_data_tag); + } + + if (data->m != NULL) + m_freem(data->m); + } +} + + +static int +nfe_alloc_tx_ring(struct nfe_softc *sc, struct nfe_tx_ring *ring) +{ + int i, error; + void **desc; + int descsize; + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc = (void **)&ring->desc64; + descsize = sizeof (struct nfe_desc64); + } else { + desc = (void **)&ring->desc32; + descsize = sizeof (struct nfe_desc32); + } + + ring->queued = 0; + ring->cur = ring->next = 0; + + error = bus_dma_tag_create(sc->nfe_parent_tag, + PAGE_SIZE, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + NFE_TX_RING_COUNT * descsize, 1, /* maxsize, nsegments */ + NFE_TX_RING_COUNT * descsize, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &ring->tx_desc_tag); + if (error != 0) { + printf("nfe%d: could not create desc DMA tag\n", sc->nfe_unit); + goto fail; + } + + error = bus_dmamem_alloc(ring->tx_desc_tag, (void **)desc, + BUS_DMA_NOWAIT, &ring->tx_desc_map); + if (error != 0) { + printf("nfe%d: could not create desc DMA map\n", sc->nfe_unit); + goto fail; + } + + error = bus_dmamap_load(ring->tx_desc_tag, ring->tx_desc_map, *desc, + NFE_TX_RING_COUNT * descsize, nfe_dma_map_segs, &ring->tx_desc_segs, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("nfe%d: could not load desc DMA map\n", sc->nfe_unit); + goto fail; + } + + bzero(*desc, NFE_TX_RING_COUNT * descsize); + + ring->tx_desc_addr = ring->tx_desc_segs.ds_addr; + ring->physaddr = ring->tx_desc_addr; + + error = bus_dma_tag_create(sc->nfe_parent_tag, + ETHER_ALIGN, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, + NULL, NULL, + NFE_JBYTES, NFE_MAX_SCATTER, + NFE_JBYTES, + BUS_DMA_ALLOCNOW, + NULL, NULL, + &ring->tx_data_tag); + if (error != 0) { + printf("nfe%d: could not create DMA tag\n", sc->nfe_unit); + goto fail; + } + + for (i = 0; i < NFE_TX_RING_COUNT; i++) { + error = bus_dmamap_create(ring->tx_data_tag, 0, + &ring->data[i].tx_data_map); + if (error != 0) { + printf("nfe%d: could not create DMA map\n", + sc->nfe_unit); + goto fail; + } + } + + return 0; + +fail: nfe_free_tx_ring(sc, ring); + return error; +} + + +static void +nfe_reset_tx_ring(struct nfe_softc *sc, struct nfe_tx_ring *ring) +{ + struct nfe_tx_data *data; + int i; + + for (i = 0; i < NFE_TX_RING_COUNT; i++) { + if (sc->nfe_flags & NFE_40BIT_ADDR) + ring->desc64[i].flags = 0; + else + ring->desc32[i].flags = 0; + + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->tx_data_tag, data->active, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->tx_data_tag, data->active); + m_freem(data->m); + data->m = NULL; + } + } + + bus_dmamap_sync(ring->tx_desc_tag, ring->tx_desc_map, + BUS_DMASYNC_PREWRITE); + + ring->queued = 0; + ring->cur = ring->next = 0; +} + + +static void +nfe_free_tx_ring(struct nfe_softc *sc, struct nfe_tx_ring *ring) +{ + struct nfe_tx_data *data; + void *desc; + int i, descsize; + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc = ring->desc64; + descsize = sizeof (struct nfe_desc64); + } else { + desc = ring->desc32; + descsize = sizeof (struct nfe_desc32); + } + + if (desc != NULL) { + bus_dmamap_sync(ring->tx_desc_tag, ring->tx_desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->tx_desc_tag, ring->tx_desc_map); + bus_dmamem_free(ring->tx_desc_tag, desc, ring->tx_desc_map); + bus_dma_tag_destroy(ring->tx_desc_tag); + } + + for (i = 0; i < NFE_TX_RING_COUNT; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->tx_data_tag, data->active, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->tx_data_tag, data->active); + m_freem(data->m); + } + } + + /* ..and now actually destroy the DMA mappings */ + for (i = 0; i < NFE_TX_RING_COUNT; i++) { + data = &ring->data[i]; + if (data->tx_data_map == NULL) + continue; + bus_dmamap_destroy(ring->tx_data_tag, data->tx_data_map); + } + + bus_dma_tag_destroy(ring->tx_data_tag); +} + +#ifdef DEVICE_POLLING +static poll_handler_t nfe_poll; + + +static void +nfe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct nfe_softc *sc = ifp->if_softc; + + NFE_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + nfe_poll_locked(ifp, cmd, count); + NFE_UNLOCK(sc); +} + + +static void +nfe_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct nfe_softc *sc = ifp->if_softc; + u_int32_t r; + + NFE_LOCK_ASSERT(sc); + + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + return; + } + + sc->rxcycles = count; + nfe_rxeof(sc); + nfe_txeof(sc); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + nfe_start_locked(ifp); + + if (cmd == POLL_AND_CHECK_STATUS) { + if ((r = NFE_READ(sc, NFE_IRQ_STATUS)) == 0) { + return; + } + NFE_WRITE(sc, NFE_IRQ_STATUS, r); + + if (r & NFE_IRQ_LINK) { + NFE_READ(sc, NFE_PHY_STATUS); + NFE_WRITE(sc, NFE_PHY_STATUS, 0xf); + DPRINTF(("nfe%d: link state changed\n", sc->nfe_unit)); + } + } +} +#endif /* DEVICE_POLLING */ + + +static int +nfe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct nfe_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + switch (cmd) { + case SIOCSIFMTU: + if (ifr->ifr_mtu == ifp->if_mtu) { + error = EINVAL; + break; + } + if ((sc->nfe_flags & NFE_JUMBO_SUP) && (ifr->ifr_mtu >= + ETHERMIN && ifr->ifr_mtu <= NV_PKTLIMIT_2)) { + NFE_LOCK(sc); + sc->nfe_mtu = ifp->if_mtu = ifr->ifr_mtu; + nfe_stop(ifp, 1); + nfe_free_tx_ring(sc, &sc->txq); + nfe_free_rx_ring(sc, &sc->rxq); + NFE_UNLOCK(sc); + + /* Reallocate Tx and Rx rings. */ + if (nfe_alloc_tx_ring(sc, &sc->txq) != 0) { + printf("nfe%d: could not allocate Tx ring\n", + sc->nfe_unit); + error = ENXIO; + break; + } + + if (nfe_alloc_rx_ring(sc, &sc->rxq) != 0) { + printf("nfe%d: could not allocate Rx ring\n", + sc->nfe_unit); + nfe_free_tx_ring(sc, &sc->txq); + error = ENXIO; + break; + } + NFE_LOCK(sc); + nfe_init_locked(sc); + NFE_UNLOCK(sc); + } else { + error = EINVAL; + } + break; + case SIOCSIFFLAGS: + NFE_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + /* + * If only the PROMISC or ALLMULTI flag changes, then + * don't do a full re-init of the chip, just update + * the Rx filter. + */ + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && + ((ifp->if_flags ^ sc->nfe_if_flags) & + (IFF_ALLMULTI | IFF_PROMISC)) != 0) + nfe_setmulti(sc); + else + nfe_init_locked(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + nfe_stop(ifp, 1); + } + sc->nfe_if_flags = ifp->if_flags; + NFE_UNLOCK(sc); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + NFE_LOCK(sc); + nfe_setmulti(sc); + NFE_UNLOCK(sc); + error = 0; + } + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + mii = device_get_softc(sc->nfe_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + case SIOCSIFCAP: + { + int init = 0; + int mask = ifr->ifr_reqcap ^ ifp->if_capenable; +#ifdef DEVICE_POLLING + if (mask & IFCAP_POLLING) { + if (ifr->ifr_reqcap & IFCAP_POLLING) { + error = ether_poll_register(nfe_poll, ifp); + if (error) + return(error); + NFE_LOCK(sc); + NFE_WRITE(sc, NFE_IRQ_MASK, 0); + ifp->if_capenable |= IFCAP_POLLING; + NFE_UNLOCK(sc); + } else { + error = ether_poll_deregister(ifp); + /* Enable interrupt even in error case */ + NFE_LOCK(sc); + NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_WANTED); + ifp->if_capenable &= ~IFCAP_POLLING; + NFE_UNLOCK(sc); + } + } +#endif /* DEVICE_POLLING */ +#ifdef NFE_CSUM + if (mask & IFCAP_HWCSUM) { + ifp->if_capenable ^= IFCAP_HWCSUM; + if (IFCAP_HWCSUM & ifp->if_capenable && + IFCAP_HWCSUM & ifp->if_capabilities) + ifp->if_hwassist = NFE_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + sc->nfe_flags ^= NFE_HW_CSUM; + init = 1; + } +#endif + if (init && ifp->if_drv_flags & IFF_DRV_RUNNING) + nfe_init(sc); + } + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return error; +} + + +static void +nfe_intr(void *arg) +{ + struct nfe_softc *sc = arg; + struct ifnet *ifp = sc->nfe_ifp; + u_int32_t r; + + NFE_LOCK(sc); + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + NFE_UNLOCK(sc); + return; + } +#endif + + if ((r = NFE_READ(sc, NFE_IRQ_STATUS)) == 0) { + NFE_UNLOCK(sc); + return; /* not for us */ + } + NFE_WRITE(sc, NFE_IRQ_STATUS, r); + + DPRINTFN(5, ("nfe_intr: interrupt register %x\n", r)); + + NFE_WRITE(sc, NFE_IRQ_MASK, 0); + + if (r & NFE_IRQ_LINK) { + NFE_READ(sc, NFE_PHY_STATUS); + NFE_WRITE(sc, NFE_PHY_STATUS, 0xf); + DPRINTF(("nfe%d: link state changed\n", sc->nfe_unit)); + } + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + /* check Rx ring */ + nfe_rxeof(sc); + /* check Tx ring */ + nfe_txeof(sc); + } + + NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_WANTED); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + nfe_start_locked(ifp); + + NFE_UNLOCK(sc); + + return; +} + + +static void +nfe_txdesc32_sync(struct nfe_softc *sc, struct nfe_desc32 *desc32, int ops) +{ + + bus_dmamap_sync(sc->txq.tx_desc_tag, sc->txq.tx_desc_map, ops); +} + + +static void +nfe_txdesc64_sync(struct nfe_softc *sc, struct nfe_desc64 *desc64, int ops) +{ + + bus_dmamap_sync(sc->txq.tx_desc_tag, sc->txq.tx_desc_map, ops); +} + + +static void +nfe_txdesc32_rsync(struct nfe_softc *sc, int start, int end, int ops) +{ + + bus_dmamap_sync(sc->txq.tx_desc_tag, sc->txq.tx_desc_map, ops); +} + + +static void +nfe_txdesc64_rsync(struct nfe_softc *sc, int start, int end, int ops) +{ + + bus_dmamap_sync(sc->txq.tx_desc_tag, sc->txq.tx_desc_map, ops); +} + + +static void +nfe_rxdesc32_sync(struct nfe_softc *sc, struct nfe_desc32 *desc32, int ops) +{ + + bus_dmamap_sync(sc->rxq.rx_desc_tag, sc->rxq.rx_desc_map, ops); +} + + +static void +nfe_rxdesc64_sync(struct nfe_softc *sc, struct nfe_desc64 *desc64, int ops) +{ + + bus_dmamap_sync(sc->rxq.rx_desc_tag, sc->rxq.rx_desc_map, ops); +} + + +static void +nfe_rxeof(struct nfe_softc *sc) +{ + struct ifnet *ifp = sc->nfe_ifp; + struct nfe_desc32 *desc32=NULL; + struct nfe_desc64 *desc64=NULL; + struct nfe_rx_data *data; + struct mbuf *m, *mnew; + bus_addr_t physaddr; + u_int16_t flags; + int error, len; +#if NVLAN > 1 + u_int16_t vlan_tag = 0; + int have_tag = 0; +#endif + + NFE_LOCK_ASSERT(sc); + + for (;;) { + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + if (sc->rxcycles <= 0) + break; + sc->rxcycles--; + } +#endif + + data = &sc->rxq.data[sc->rxq.cur]; + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc64 = &sc->rxq.desc64[sc->rxq.cur]; + nfe_rxdesc64_sync(sc, desc64, BUS_DMASYNC_POSTREAD); + + flags = letoh16(desc64->flags); + len = letoh16(desc64->length) & 0x3fff; + +#if NVLAN > 1 + if (flags & NFE_TX_VLAN_TAG) { + have_tag = 1; + vlan_tag = desc64->vtag; + } +#endif + + } else { + desc32 = &sc->rxq.desc32[sc->rxq.cur]; + nfe_rxdesc32_sync(sc, desc32, BUS_DMASYNC_POSTREAD); + + flags = letoh16(desc32->flags); + len = letoh16(desc32->length) & 0x3fff; + } + + if (flags & NFE_RX_READY) + break; + + if ((sc->nfe_flags & (NFE_JUMBO_SUP | NFE_40BIT_ADDR)) == 0) { + if (!(flags & NFE_RX_VALID_V1)) + goto skip; + if ((flags & NFE_RX_FIXME_V1) == NFE_RX_FIXME_V1) { + flags &= ~NFE_RX_ERROR; + len--; /* fix buffer length */ + } + } else { + if (!(flags & NFE_RX_VALID_V2)) + goto skip; + + if ((flags & NFE_RX_FIXME_V2) == NFE_RX_FIXME_V2) { + flags &= ~NFE_RX_ERROR; + len--; /* fix buffer length */ + } + } + + if (flags & NFE_RX_ERROR) { + ifp->if_ierrors++; + goto skip; + } + + /* + * Try to allocate a new mbuf for this ring element and load + * it before processing the current mbuf. If the ring element + * cannot be loaded, drop the received packet and reuse the + * old mbuf. In the unlikely case that the old mbuf can't be + * reloaded either, explicitly panic. + */ + MGETHDR(mnew, M_DONTWAIT, MT_DATA); + if (mnew == NULL) { + ifp->if_ierrors++; + goto skip; + } + + MCLGET(mnew, M_DONTWAIT); + if (!(mnew->m_flags & M_EXT)) { + m_freem(mnew); + ifp->if_ierrors++; + goto skip; + } + + bus_dmamap_sync(data->rx_data_tag, data->rx_data_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(data->rx_data_tag, data->rx_data_map); + error = bus_dmamap_load(data->rx_data_tag, + data->rx_data_map, mtod(mnew, void *), MCLBYTES, + nfe_dma_map_segs, &data->rx_data_segs, + BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(mnew); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(data->rx_data_tag, + data->rx_data_map, mtod(data->m, void *), + MCLBYTES, nfe_dma_map_segs, + &data->rx_data_segs, BUS_DMA_NOWAIT); + if (error != 0) { + /* very unlikely that it will fail.. */ + panic("nfe%d: could not load old rx mbuf", + sc->nfe_unit); + } + ifp->if_ierrors++; + goto skip; + } + data->rx_data_addr = data->rx_data_segs.ds_addr; + physaddr = data->rx_data_addr; + + /* + * New mbuf successfully loaded, update Rx ring and continue + * processing. + */ + m = data->m; + data->m = mnew; + + /* finalize mbuf */ + m->m_pkthdr.len = m->m_len = len; + m->m_pkthdr.rcvif = ifp; + + if ((sc->nfe_flags & NFE_HW_CSUM) && (flags & NFE_RX_CSUMOK)) { + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + if (flags & NFE_RX_IP_CSUMOK_V2) { + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + } + if (flags & NFE_RX_UDP_CSUMOK_V2 || + flags & NFE_RX_TCP_CSUMOK_V2) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + +#if NVLAN > 1 + if (have_tag) { + m->m_pkthdr.ether_vtag = vlan_tag; + m->m_flags |= M_VLANTAG; + } +#endif + ifp->if_ipackets++; + + NFE_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + NFE_LOCK(sc); + + /* update mapping address in h/w descriptor */ + if (sc->nfe_flags & NFE_40BIT_ADDR) { +#if defined(__LP64__) + desc64->physaddr[0] = htole32(physaddr >> 32); +#endif + desc64->physaddr[1] = htole32(physaddr & 0xffffffff); + } else { + desc32->physaddr = htole32(physaddr); + } + +skip: if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc64->length = htole16(sc->rxq.bufsz); + desc64->flags = htole16(NFE_RX_READY); + nfe_rxdesc64_sync(sc, desc64, BUS_DMASYNC_PREWRITE); + } else { + desc32->length = htole16(sc->rxq.bufsz); + desc32->flags = htole16(NFE_RX_READY); + nfe_rxdesc32_sync(sc, desc32, BUS_DMASYNC_PREWRITE); + } + + sc->rxq.cur = (sc->rxq.cur + 1) % NFE_RX_RING_COUNT; + } //end for(;;) +} + + +static void +nfe_txeof(struct nfe_softc *sc) +{ + struct ifnet *ifp = sc->nfe_ifp; + struct nfe_desc32 *desc32; + struct nfe_desc64 *desc64; + struct nfe_tx_data *data = NULL; + u_int16_t flags; + + NFE_LOCK_ASSERT(sc); + + while (sc->txq.next != sc->txq.cur) { + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc64 = &sc->txq.desc64[sc->txq.next]; + nfe_txdesc64_sync(sc, desc64, BUS_DMASYNC_POSTREAD); + + flags = letoh16(desc64->flags); + } else { + desc32 = &sc->txq.desc32[sc->txq.next]; + nfe_txdesc32_sync(sc, desc32, BUS_DMASYNC_POSTREAD); + + flags = letoh16(desc32->flags); + } + + if (flags & NFE_TX_VALID) + break; + + data = &sc->txq.data[sc->txq.next]; + + if ((sc->nfe_flags & (NFE_JUMBO_SUP | NFE_40BIT_ADDR)) == 0) { + if (!(flags & NFE_TX_LASTFRAG_V1) && data->m == NULL) + goto skip; + + if ((flags & NFE_TX_ERROR_V1) != 0) { + printf("nfe%d: tx v1 error 0x%4b\n", + sc->nfe_unit, flags, NFE_V1_TXERR); + + ifp->if_oerrors++; + } else + ifp->if_opackets++; + } else { + if (!(flags & NFE_TX_LASTFRAG_V2) && data->m == NULL) + goto skip; + + if ((flags & NFE_TX_ERROR_V2) != 0) { + printf("nfe%d: tx v1 error 0x%4b\n", + sc->nfe_unit, flags, NFE_V2_TXERR); + + ifp->if_oerrors++; + } else + ifp->if_opackets++; + } + + if (data->m == NULL) { /* should not get there */ + printf("nfe%d: last fragment bit w/o associated mbuf!\n", + sc->nfe_unit); + goto skip; + } + + /* last fragment of the mbuf chain transmitted */ + bus_dmamap_sync(sc->txq.tx_data_tag, data->active, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->txq.tx_data_tag, data->active); + m_freem(data->m); + data->m = NULL; + + ifp->if_timer = 0; + +skip: sc->txq.queued--; + sc->txq.next = (sc->txq.next + 1) % NFE_TX_RING_COUNT; + } + + if (data != NULL) { /* at least one slot freed */ + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + nfe_start_locked(ifp); + } +} + + +static int +nfe_encap(struct nfe_softc *sc, struct mbuf *m0) +{ + struct nfe_desc32 *desc32=NULL; + struct nfe_desc64 *desc64=NULL; + struct nfe_tx_data *data=NULL; + bus_dmamap_t map; + bus_dma_segment_t segs[NFE_MAX_SCATTER]; + int error, i, nsegs; + u_int16_t flags = NFE_TX_VALID; + + map = sc->txq.data[sc->txq.cur].tx_data_map; + + error = bus_dmamap_load_mbuf_sg(sc->txq.tx_data_tag, map, m0, segs, + &nsegs, BUS_DMA_NOWAIT); + + if (error != 0) { + printf("nfe%d: could not map mbuf (error %d)\n", sc->nfe_unit, + error); + return error; + } + + if (sc->txq.queued + nsegs >= NFE_TX_RING_COUNT - 1) { + bus_dmamap_unload(sc->txq.tx_data_tag, map); + return ENOBUFS; + } + + if(sc->nfe_flags & NFE_HW_CSUM){ + if (m0->m_pkthdr.csum_flags & CSUM_IP) + flags |= NFE_TX_IP_CSUM; + if (m0->m_pkthdr.csum_flags & CSUM_TCP) + flags |= NFE_TX_TCP_CSUM; + if (m0->m_pkthdr.csum_flags & CSUM_UDP) + flags |= NFE_TX_TCP_CSUM; + } + + for (i = 0; i < nsegs; i++) { + data = &sc->txq.data[sc->txq.cur]; + + if (sc->nfe_flags & NFE_40BIT_ADDR) { + desc64 = &sc->txq.desc64[sc->txq.cur]; +#if defined(__LP64__) + desc64->physaddr[0] = htole32(segs[i].ds_addr >> 32); +#endif + desc64->physaddr[1] = htole32(segs[i].ds_addr & + 0xffffffff); + desc64->length = htole16(segs[i].ds_len - 1); + desc64->flags = htole16(flags); +#if NVLAN > 0 + if (m0->m_flags & M_VLANTAG) + desc64->vtag = htole32(NFE_TX_VTAG | + m0->m_pkthdr.ether_vtag); +#endif + } else { + desc32 = &sc->txq.desc32[sc->txq.cur]; + + desc32->physaddr = htole32(segs[i].ds_addr); + desc32->length = htole16(segs[i].ds_len - 1); + desc32->flags = htole16(flags); + } + + /* csum flags and vtag belong to the first fragment only */ + if (nsegs > 1) { + flags &= ~(NFE_TX_IP_CSUM | NFE_TX_TCP_CSUM); + } + + sc->txq.queued++; + sc->txq.cur = (sc->txq.cur + 1) % NFE_TX_RING_COUNT; + } + + /* the whole mbuf chain has been DMA mapped, fix last descriptor */ + if (sc->nfe_flags & NFE_40BIT_ADDR) { + flags |= NFE_TX_LASTFRAG_V2; + desc64->flags = htole16(flags); + } else { + if (sc->nfe_flags & NFE_JUMBO_SUP) + flags |= NFE_TX_LASTFRAG_V2; + else + flags |= NFE_TX_LASTFRAG_V1; + desc32->flags = htole16(flags); + } + + data->m = m0; + data->active = map; + data->nsegs = nsegs; + + bus_dmamap_sync(sc->txq.tx_data_tag, map, BUS_DMASYNC_PREWRITE); + + return 0; +} + + +static void +nfe_setmulti(struct nfe_softc *sc) +{ + struct ifnet *ifp = sc->nfe_ifp; + struct ifmultiaddr *ifma; + int i; + u_int32_t filter = NFE_RXFILTER_MAGIC; + u_int8_t addr[ETHER_ADDR_LEN], mask[ETHER_ADDR_LEN]; + u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + NFE_LOCK_ASSERT(sc); + + if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) { + bzero(addr, ETHER_ADDR_LEN); + bzero(mask, ETHER_ADDR_LEN); + goto done; + } + + bcopy(etherbroadcastaddr, addr, ETHER_ADDR_LEN); + bcopy(etherbroadcastaddr, mask, ETHER_ADDR_LEN); + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + u_char *addrp; + + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + addrp = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); + for (i = 0; i < ETHER_ADDR_LEN; i++) { + u_int8_t mcaddr = addrp[i]; + addr[i] &= mcaddr; + mask[i] &= ~mcaddr; + } + } + IF_ADDR_UNLOCK(ifp); + + for (i = 0; i < ETHER_ADDR_LEN; i++) { + mask[i] |= addr[i]; + } + +done: + addr[0] |= 0x01; /* make sure multicast bit is set */ + + NFE_WRITE(sc, NFE_MULTIADDR_HI, + addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]); + NFE_WRITE(sc, NFE_MULTIADDR_LO, + addr[5] << 8 | addr[4]); + NFE_WRITE(sc, NFE_MULTIMASK_HI, + mask[3] << 24 | mask[2] << 16 | mask[1] << 8 | mask[0]); + NFE_WRITE(sc, NFE_MULTIMASK_LO, + mask[5] << 8 | mask[4]); + + filter |= (ifp->if_flags & IFF_PROMISC) ? NFE_PROMISC : NFE_U2M; + NFE_WRITE(sc, NFE_RXFILTER, filter); +} + + +static void +nfe_start(struct ifnet *ifp) +{ + struct nfe_softc *sc; + + sc = ifp->if_softc; + NFE_LOCK(sc); + nfe_start_locked(ifp); + NFE_UNLOCK(sc); +} + + +static void +nfe_start_locked(struct ifnet *ifp) +{ + struct nfe_softc *sc = ifp->if_softc; + struct mbuf *m0; + int old = sc->txq.cur; + + if (!sc->nfe_link || ifp->if_drv_flags & IFF_DRV_OACTIVE) { + return; + } + + for (;;) { + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + + if (nfe_encap(sc, m0) != 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + /* packet put in h/w queue, remove from s/w queue */ + IFQ_DEQUEUE(&ifp->if_snd, m0); + + BPF_MTAP(ifp, m0); + } + if (sc->txq.cur == old) { /* nothing sent */ + return; + } + + if (sc->nfe_flags & NFE_40BIT_ADDR) + nfe_txdesc64_rsync(sc, old, sc->txq.cur, BUS_DMASYNC_PREWRITE); + else + nfe_txdesc32_rsync(sc, old, sc->txq.cur, BUS_DMASYNC_PREWRITE); + + /* kick Tx */ + NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_KICKTX | sc->rxtxctl); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + return; +} + + +static void +nfe_watchdog(struct ifnet *ifp) +{ + struct nfe_softc *sc = ifp->if_softc; + + printf("nfe%d: watchdog timeout\n", sc->nfe_unit); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + nfe_init(sc); + ifp->if_oerrors++; + + return; +} + + +static void +nfe_init(void *xsc) +{ + struct nfe_softc *sc = xsc; + + NFE_LOCK(sc); + nfe_init_locked(sc); + NFE_UNLOCK(sc); + + return; +} + + +static void +nfe_init_locked(void *xsc) +{ + struct nfe_softc *sc = xsc; + struct ifnet *ifp = sc->nfe_ifp; + struct mii_data *mii; + u_int32_t tmp; + + NFE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->nfe_miibus); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + return; + } + + nfe_stop(ifp, 0); + + NFE_WRITE(sc, NFE_TX_UNK, 0); + NFE_WRITE(sc, NFE_STATUS, 0); + + sc->rxtxctl = NFE_RXTX_BIT2; + if (sc->nfe_flags & NFE_40BIT_ADDR) + sc->rxtxctl |= NFE_RXTX_V3MAGIC; + else if (sc->nfe_flags & NFE_JUMBO_SUP) + sc->rxtxctl |= NFE_RXTX_V2MAGIC; + + if (sc->nfe_flags & NFE_HW_CSUM) + sc->rxtxctl |= NFE_RXTX_RXCSUM; + +#if NVLAN > 0 + /* + * Although the adapter is capable of stripping VLAN tags from received + * frames (NFE_RXTX_VTAG_STRIP), we do not enable this functionality on + * purpose. This will be done in software by our network stack. + */ + if (sc->nfe_flags & NFE_HW_VLAN) + sc->rxtxctl |= NFE_RXTX_VTAG_INSERT; +#endif + + NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_RESET | sc->rxtxctl); + DELAY(10); + NFE_WRITE(sc, NFE_RXTX_CTL, sc->rxtxctl); + +#if NVLAN + if (sc->nfe_flags & NFE_HW_VLAN) + NFE_WRITE(sc, NFE_VTAG_CTL, NFE_VTAG_ENABLE); +#endif + + NFE_WRITE(sc, NFE_SETUP_R6, 0); + + /* set MAC address */ + nfe_set_macaddr(sc, sc->eaddr); + + /* tell MAC where rings are in memory */ +#ifdef __LP64__ + NFE_WRITE(sc, NFE_RX_RING_ADDR_HI, sc->rxq.physaddr >> 32); +#endif + NFE_WRITE(sc, NFE_RX_RING_ADDR_LO, sc->rxq.physaddr & 0xffffffff); +#ifdef __LP64__ + NFE_WRITE(sc, NFE_TX_RING_ADDR_HI, sc->txq.physaddr >> 32); +#endif + NFE_WRITE(sc, NFE_TX_RING_ADDR_LO, sc->txq.physaddr & 0xffffffff); + + NFE_WRITE(sc, NFE_RING_SIZE, + (NFE_RX_RING_COUNT - 1) << 16 | + (NFE_TX_RING_COUNT - 1)); + + NFE_WRITE(sc, NFE_RXBUFSZ, sc->rxq.bufsz); + + /* force MAC to wakeup */ + tmp = NFE_READ(sc, NFE_PWR_STATE); + NFE_WRITE(sc, NFE_PWR_STATE, tmp | NFE_PWR_WAKEUP); + DELAY(10); + tmp = NFE_READ(sc, NFE_PWR_STATE); + NFE_WRITE(sc, NFE_PWR_STATE, tmp | NFE_PWR_VALID); + +#if 1 + /* configure interrupts coalescing/mitigation */ + NFE_WRITE(sc, NFE_IMTIMER, NFE_IM_DEFAULT); +#else + /* no interrupt mitigation: one interrupt per packet */ + NFE_WRITE(sc, NFE_IMTIMER, 970); +#endif + + NFE_WRITE(sc, NFE_SETUP_R1, NFE_R1_MAGIC); + NFE_WRITE(sc, NFE_SETUP_R2, NFE_R2_MAGIC); + NFE_WRITE(sc, NFE_SETUP_R6, NFE_R6_MAGIC); + + /* update MAC knowledge of PHY; generates a NFE_IRQ_LINK interrupt */ + NFE_WRITE(sc, NFE_STATUS, sc->mii_phyaddr << 24 | NFE_STATUS_MAGIC); + + NFE_WRITE(sc, NFE_SETUP_R4, NFE_R4_MAGIC); + NFE_WRITE(sc, NFE_WOL_CTL, NFE_WOL_MAGIC); + + sc->rxtxctl &= ~NFE_RXTX_BIT2; + NFE_WRITE(sc, NFE_RXTX_CTL, sc->rxtxctl); + DELAY(10); + NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_BIT1 | sc->rxtxctl); + + /* set Rx filter */ + nfe_setmulti(sc); + + nfe_ifmedia_upd(ifp); + + nfe_tick_locked(sc); + + /* enable Rx */ + NFE_WRITE(sc, NFE_RX_CTL, NFE_RX_START); + + /* enable Tx */ + NFE_WRITE(sc, NFE_TX_CTL, NFE_TX_START); + + NFE_WRITE(sc, NFE_PHY_STATUS, 0xf); + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + NFE_WRITE(sc, NFE_IRQ_MASK, 0); + else +#endif + NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_WANTED); /* enable interrupts */ + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + sc->nfe_link = 0; + + return; +} + + +static void +nfe_stop(struct ifnet *ifp, int disable) +{ + struct nfe_softc *sc = ifp->if_softc; + struct mii_data *mii; + + NFE_LOCK_ASSERT(sc); + + ifp->if_timer = 0; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + mii = device_get_softc(sc->nfe_miibus); + + callout_stop(&sc->nfe_stat_ch); + + /* abort Tx */ + NFE_WRITE(sc, NFE_TX_CTL, 0); + + /* disable Rx */ + NFE_WRITE(sc, NFE_RX_CTL, 0); + + /* disable interrupts */ + NFE_WRITE(sc, NFE_IRQ_MASK, 0); + + sc->nfe_link = 0; + + /* reset Tx and Rx rings */ + nfe_reset_tx_ring(sc, &sc->txq); + nfe_reset_rx_ring(sc, &sc->rxq); + + return; +} + + +static int +nfe_ifmedia_upd(struct ifnet *ifp) +{ + struct nfe_softc *sc = ifp->if_softc; + + NFE_LOCK(sc); + nfe_ifmedia_upd_locked(ifp); + NFE_UNLOCK(sc); + return (0); +} + + +static int +nfe_ifmedia_upd_locked(struct ifnet *ifp) +{ + struct nfe_softc *sc = ifp->if_softc; + struct mii_data *mii; + + NFE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->nfe_miibus); + + if (mii->mii_instance) { + struct mii_softc *miisc; + for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; + miisc = LIST_NEXT(miisc, mii_list)) { + mii_phy_reset(miisc); + } + } + mii_mediachg(mii); + + return (0); +} + + +static void +nfe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct nfe_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + NFE_LOCK(sc); + mii = device_get_softc(sc->nfe_miibus); + mii_pollstat(mii); + NFE_UNLOCK(sc); + + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + + +static void +nfe_tick(void *xsc) +{ + struct nfe_softc *sc; + + sc = xsc; + + NFE_LOCK(sc); + nfe_tick_locked(sc); + NFE_UNLOCK(sc); +} + + +void +nfe_tick_locked(struct nfe_softc *arg) +{ + struct nfe_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + + sc = arg; + + NFE_LOCK_ASSERT(sc); + + ifp = sc->nfe_ifp; + + mii = device_get_softc(sc->nfe_miibus); + mii_tick(mii); + + if (!sc->nfe_link) { + if (mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->nfe_link++; + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T + && bootverbose) + if_printf(sc->nfe_ifp, "gigabit link up\n"); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + nfe_start_locked(ifp); + } + } + callout_reset(&sc->nfe_stat_ch, hz, nfe_tick, sc); + + return; +} + + +static void +nfe_shutdown(device_t dev) +{ + struct nfe_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + + NFE_LOCK(sc); + ifp = sc->nfe_ifp; + nfe_stop(ifp,0); + /* nfe_reset(sc); */ + NFE_UNLOCK(sc); + + return; +} + + +static void +nfe_get_macaddr(struct nfe_softc *sc, u_char *addr) +{ + uint32_t tmp; + + tmp = NFE_READ(sc, NFE_MACADDR_LO); + addr[0] = (tmp >> 8) & 0xff; + addr[1] = (tmp & 0xff); + + tmp = NFE_READ(sc, NFE_MACADDR_HI); + addr[2] = (tmp >> 24) & 0xff; + addr[3] = (tmp >> 16) & 0xff; + addr[4] = (tmp >> 8) & 0xff; + addr[5] = (tmp & 0xff); +} + + +static void +nfe_set_macaddr(struct nfe_softc *sc, u_char *addr) +{ + + NFE_WRITE(sc, NFE_MACADDR_LO, addr[5] << 8 | addr[4]); + NFE_WRITE(sc, NFE_MACADDR_HI, addr[3] << 24 | addr[2] << 16 | + addr[1] << 8 | addr[0]); +} + + +/* + * Map a single buffer address. + */ + +static void +nfe_dma_map_segs(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int error, nseg; +{ + + if (error) + return; + + KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + + *(bus_dma_segment_t *)arg = *segs; + + return; +} --- sys/dev/nfe/if_nfereg.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/nfe/if_nfereg.h Tue Oct 17 11:41:28 2006 @@ -0,0 +1,254 @@ +/* $OpenBSD: if_nfereg.h,v 1.16 2006/02/22 19:23:44 damien Exp $ */ + +/*- + * Copyright (c) 2005 Jonathan Gray + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: src/sys/dev/nfe/if_nfereg.h,v 1.7 2006/11/27 04:47:27 obrien Exp $ + */ + +#define NFE_PCI_BA 0x10 + +#define NFE_RX_RING_COUNT 128 +#define NFE_TX_RING_COUNT 256 + +/* RX/TX MAC addr + type + VLAN + align + slack */ +#define NFE_RX_HEADERS 64 + +/* Maximum MTU size. */ +#define NV_PKTLIMIT_1 ETH_DATA_LEN /* Hard limit not known. */ +#define NV_PKTLIMIT_2 9100 /* Actual limit according to NVidia:9202 */ +#define NFE_JBYTES (ETHER_MAX_LEN_JUMBO + ETHER_ALIGN) +#define NFE_JPOOL_COUNT (NFE_RX_RING_COUNT + NFE_RX_HEADERS) + +#define NFE_MAX_SCATTER (NFE_TX_RING_COUNT - 2) + +#define NFE_IRQ_STATUS 0x000 +#define NFE_IRQ_MASK 0x004 +#define NFE_SETUP_R6 0x008 +#define NFE_IMTIMER 0x00c +#define NFE_MISC1 0x080 +#define NFE_TX_CTL 0x084 +#define NFE_TX_STATUS 0x088 +#define NFE_RXFILTER 0x08c +#define NFE_RXBUFSZ 0x090 +#define NFE_RX_CTL 0x094 +#define NFE_RX_STATUS 0x098 +#define NFE_RNDSEED 0x09c +#define NFE_SETUP_R1 0x0a0 +#define NFE_SETUP_R2 0x0a4 +#define NFE_MACADDR_HI 0x0a8 +#define NFE_MACADDR_LO 0x0ac +#define NFE_MULTIADDR_HI 0x0b0 +#define NFE_MULTIADDR_LO 0x0b4 +#define NFE_MULTIMASK_HI 0x0b8 +#define NFE_MULTIMASK_LO 0x0bc +#define NFE_PHY_IFACE 0x0c0 +#define NFE_TX_RING_ADDR_LO 0x100 +#define NFE_RX_RING_ADDR_LO 0x104 +#define NFE_RING_SIZE 0x108 +#define NFE_TX_UNK 0x10c +#define NFE_LINKSPEED 0x110 +#define NFE_SETUP_R5 0x130 +#define NFE_SETUP_R3 0x13C +#define NFE_SETUP_R7 0x140 +#define NFE_RXTX_CTL 0x144 +#define NFE_TX_RING_ADDR_HI 0x148 +#define NFE_RX_RING_ADDR_HI 0x14c +#define NFE_PHY_STATUS 0x180 +#define NFE_SETUP_R4 0x184 +#define NFE_STATUS 0x188 +#define NFE_PHY_SPEED 0x18c +#define NFE_PHY_CTL 0x190 +#define NFE_PHY_DATA 0x194 +#define NFE_WOL_CTL 0x200 +#define NFE_PATTERN_CRC 0x204 +#define NFE_PATTERN_MASK 0x208 +#define NFE_PWR_CAP 0x268 +#define NFE_PWR_STATE 0x26c +#define NFE_VTAG_CTL 0x300 + +#define NFE_PHY_ERROR 0x00001 +#define NFE_PHY_WRITE 0x00400 +#define NFE_PHY_BUSY 0x08000 +#define NFE_PHYADD_SHIFT 5 + +#define NFE_STATUS_MAGIC 0x140000 + +#define NFE_R1_MAGIC 0x16070f +#define NFE_R2_MAGIC 0x16 +#define NFE_R4_MAGIC 0x08 +#define NFE_R6_MAGIC 0x03 +#define NFE_WOL_MAGIC 0x1111 +#define NFE_RX_START 0x01 +#define NFE_TX_START 0x01 + +#define NFE_IRQ_RXERR 0x0001 +#define NFE_IRQ_RX 0x0002 +#define NFE_IRQ_RX_NOBUF 0x0004 +#define NFE_IRQ_TXERR 0x0008 +#define NFE_IRQ_TX_DONE 0x0010 +#define NFE_IRQ_TIMER 0x0020 +#define NFE_IRQ_LINK 0x0040 +#define NFE_IRQ_TXERR2 0x0080 +#define NFE_IRQ_TX1 0x0100 + +#define NFE_IRQ_WANTED \ + (NFE_IRQ_RXERR | NFE_IRQ_RX_NOBUF | NFE_IRQ_RX | \ + NFE_IRQ_TXERR | NFE_IRQ_TXERR2 | NFE_IRQ_TX_DONE | \ + NFE_IRQ_LINK) + +#define NFE_RXTX_KICKTX 0x0001 +#define NFE_RXTX_BIT1 0x0002 +#define NFE_RXTX_BIT2 0x0004 +#define NFE_RXTX_RESET 0x0010 +#define NFE_RXTX_VTAG_STRIP 0x0040 +#define NFE_RXTX_VTAG_INSERT 0x0080 +#define NFE_RXTX_RXCSUM 0x0400 +#define NFE_RXTX_V2MAGIC 0x2100 +#define NFE_RXTX_V3MAGIC 0x2200 +#define NFE_RXFILTER_MAGIC 0x007f0008 +#define NFE_U2M (1 << 5) +#define NFE_PROMISC (1 << 7) +#define NFE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +/* default interrupt moderation timer of 128us */ +#define NFE_IM_DEFAULT ((128 * 100) / 1024) + +#define NFE_VTAG_ENABLE (1 << 13) + +#define NFE_PWR_VALID (1 << 8) +#define NFE_PWR_WAKEUP (1 << 15) + +#define NFE_MEDIA_SET 0x10000 +#define NFE_MEDIA_1000T 0x00032 +#define NFE_MEDIA_100TX 0x00064 +#define NFE_MEDIA_10T 0x003e8 + +#define NFE_PHY_100TX (1 << 0) +#define NFE_PHY_1000T (1 << 1) +#define NFE_PHY_HDX (1 << 8) + +#define NFE_MISC1_MAGIC 0x003b0f3c +#define NFE_MISC1_HDX (1 << 1) + +#define NFE_SEED_MASK 0x0003ff00 +#define NFE_SEED_10T 0x00007f00 +#define NFE_SEED_100TX 0x00002d00 +#define NFE_SEED_1000T 0x00007400 + +/* Rx/Tx descriptor */ +struct nfe_desc32 { + uint32_t physaddr; + uint16_t length; + uint16_t flags; +#define NFE_RX_FIXME_V1 0x6004 +#define NFE_RX_VALID_V1 (1 << 0) +#define NFE_TX_ERROR_V1 0x7808 +#define NFE_TX_LASTFRAG_V1 (1 << 0) +#define NFE_RX_ERROR1_V1 (1<<7) +#define NFE_RX_ERROR2_V1 (1<<8) +#define NFE_RX_ERROR3_V1 (1<<9) +#define NFE_RX_ERROR4_V1 (1<<10) +} __packed; + +#define NFE_V1_TXERR "\020" \ + "\14TXERROR\13UNDERFLOW\12LATECOLLISION\11LOSTCARRIER\10DEFERRED" \ + "\08FORCEDINT\03RETRY\00LASTPACKET" + +/* V2 Rx/Tx descriptor */ +struct nfe_desc64 { + uint32_t physaddr[2]; + uint32_t vtag; +#define NFE_RX_VTAG (1 << 16) +#define NFE_TX_VTAG (1 << 18) + uint16_t length; + uint16_t flags; +#define NFE_RX_FIXME_V2 0x4300 +#define NFE_RX_VALID_V2 (1 << 13) +#define NFE_TX_ERROR_V2 0x5c04 +#define NFE_TX_LASTFRAG_V2 (1 << 13) +#define NFE_RX_IP_CSUMOK_V2 0x1000 +#define NFE_RX_UDP_CSUMOK_V2 0x1400 +#define NFE_RX_TCP_CSUMOK_V2 0x1800 +#define NFE_RX_ERROR1_V2 (1<<2) +#define NFE_RX_ERROR2_V2 (1<<3) +#define NFE_RX_ERROR3_V2 (1<<4) +#define NFE_RX_ERROR4_V2 (1<<5) +} __packed; + +#define NFE_V2_TXERR "\020" \ + "\14FORCEDINT\13LASTPACKET\12UNDERFLOW\10LOSTCARRIER\09DEFERRED\02RETRY" + +/* flags common to V1/V2 descriptors */ +#define NFE_RX_CSUMOK 0x1c00 +#define NFE_RX_ERROR (1 << 14) +#define NFE_RX_READY (1 << 15) +#define NFE_TX_TCP_CSUM (1 << 10) +#define NFE_TX_IP_CSUM (1 << 11) +#define NFE_TX_VALID (1 << 15) + +#define NFE_READ(sc, reg) \ + bus_space_read_4((sc)->nfe_memt, (sc)->nfe_memh, (reg)) + +#define NFE_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->nfe_memt, (sc)->nfe_memh, (reg), (val)) + +#ifndef PCI_VENDOR_NVIDIA +#define PCI_VENDOR_NVIDIA 0x10DE +#endif + +#define PCI_PRODUCT_NVIDIA_NFORCE_LAN 0x01C3 +#define PCI_PRODUCT_NVIDIA_NFORCE2_LAN 0x0066 +#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN1 0x00D6 +#define PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN1 0x0086 +#define PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN2 0x008C +#define PCI_PRODUCT_NVIDIA_NFORCE3_250_LAN 0x00E6 +#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN4 0x00DF +#define PCI_PRODUCT_NVIDIA_NFORCE4_LAN1 0x0056 +#define PCI_PRODUCT_NVIDIA_NFORCE4_LAN2 0x0057 +#define PCI_PRODUCT_NVIDIA_MCP04_LAN1 0x0037 +#define PCI_PRODUCT_NVIDIA_MCP04_LAN2 0x0038 +#define PCI_PRODUCT_NVIDIA_NFORCE430_LAN1 0x0268 +#define PCI_PRODUCT_NVIDIA_NFORCE430_LAN2 0x0269 +#define PCI_PRODUCT_NVIDIA_MCP55_LAN1 0x0372 +#define PCI_PRODUCT_NVIDIA_MCP55_LAN2 0x0373 +#define PCI_PRODUCT_NVIDIA_MCP61_LAN1 0x03e5 +#define PCI_PRODUCT_NVIDIA_MCP61_LAN2 0x03e6 +#define PCI_PRODUCT_NVIDIA_MCP61_LAN3 0x03ee +#define PCI_PRODUCT_NVIDIA_MCP61_LAN4 0x03ef +#define PCI_PRODUCT_NVIDIA_MCP65_LAN1 0x0450 +#define PCI_PRODUCT_NVIDIA_MCP65_LAN2 0x0451 +#define PCI_PRODUCT_NVIDIA_MCP65_LAN3 0x0452 +#define PCI_PRODUCT_NVIDIA_MCP65_LAN4 0x0453 + +#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN2 PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN1 +#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN3 PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN2 +#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN5 PCI_PRODUCT_NVIDIA_NFORCE3_250_LAN +#define PCI_PRODUCT_NVIDIA_CK804_LAN1 PCI_PRODUCT_NVIDIA_NFORCE4_LAN1 +#define PCI_PRODUCT_NVIDIA_CK804_LAN2 PCI_PRODUCT_NVIDIA_NFORCE4_LAN2 +#define PCI_PRODUCT_NVIDIA_MCP51_LAN1 PCI_PRODUCT_NVIDIA_NFORCE430_LAN1 +#define PCI_PRODUCT_NVIDIA_MCP51_LAN2 PCI_PRODUCT_NVIDIA_NFORCE430_LAN2 + +#define NFE_DEBUG 0x0000 +#define NFE_DEBUG_INIT 0x0001 +#define NFE_DEBUG_RUNNING 0x0002 +#define NFE_DEBUG_DEINIT 0x0004 +#define NFE_DEBUG_IOCTL 0x0008 +#define NFE_DEBUG_INTERRUPT 0x0010 +#define NFE_DEBUG_API 0x0020 +#define NFE_DEBUG_LOCK 0x0040 +#define NFE_DEBUG_BROKEN 0x0080 +#define NFE_DEBUG_MII 0x0100 +#define NFE_DEBUG_ALL 0xFFFF --- sys/dev/nfe/if_nfevar.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/nfe/if_nfevar.h Tue Oct 17 11:41:28 2006 @@ -0,0 +1,127 @@ +/* $OpenBSD: if_nfevar.h,v 1.11 2006/02/19 13:57:02 damien Exp $ */ + +/*- + * Copyright (c) 2005 Jonathan Gray + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD: src/sys/dev/nfe/if_nfevar.h,v 1.3 2006/11/27 00:16:47 obrien Exp $ + */ + +#define NFE_IFQ_MAXLEN 64 + +struct nfe_tx_data { + bus_dmamap_t tx_data_map; + bus_dmamap_t active; + int nsegs; + struct mbuf *m; +}; + +struct nfe_tx_ring { + bus_dmamap_t tx_desc_map; + bus_dma_segment_t tx_desc_segs; + bus_addr_t physaddr; + struct nfe_desc32 *desc32; + struct nfe_desc64 *desc64; + struct nfe_tx_data data[NFE_TX_RING_COUNT]; + int queued; + int cur; + int next; + bus_addr_t tx_desc_addr; + bus_addr_t tx_data_addr; + bus_dma_tag_t tx_desc_tag; + bus_dma_tag_t tx_data_tag; +}; + +struct nfe_jbuf { + caddr_t buf; + bus_addr_t physaddr; + SLIST_ENTRY(nfe_jbuf) jnext; +}; + +struct nfe_rx_data { + bus_dmamap_t rx_data_map; + bus_dma_tag_t rx_data_tag; + bus_addr_t rx_data_addr; + bus_dma_segment_t rx_data_segs; + struct mbuf *m; +}; + +struct nfe_rx_ring { + bus_dmamap_t rx_desc_map; + bus_dma_segment_t rx_desc_segs; + bus_dma_tag_t rx_desc_tag; + bus_addr_t rx_desc_addr; +#ifndef JMBUF + bus_dmamap_t rx_jumbo_map; + bus_dma_segment_t rx_jumbo_segs; + bus_dma_tag_t rx_jumbo_tag; + bus_addr_t rx_jumbo_addr; + caddr_t jpool; + struct nfe_jbuf jbuf[NFE_JPOOL_COUNT]; + SLIST_HEAD(, nfe_jbuf) jfreelist; +#endif + bus_addr_t physaddr; + struct nfe_desc32 *desc32; + struct nfe_desc64 *desc64; + struct nfe_rx_data data[NFE_RX_RING_COUNT]; + int bufsz; + int cur; + int next; +}; + +struct nfe_softc { + struct ifnet *nfe_ifp; + device_t nfe_dev; + device_t nfe_miibus; + struct mtx nfe_mtx; + bus_space_handle_t nfe_memh; + bus_space_tag_t nfe_memt; + struct resource *nfe_res; + struct resource *nfe_irq; + void *nfe_intrhand; + struct mii_data nfe_mii; + u_int8_t nfe_unit; + struct callout nfe_stat_ch; + + struct arpcom nfe_arpcom; + bus_dma_tag_t nfe_parent_tag; + /* struct timeout nfe_tick_ch; */ + void *nfe_powerhook; + + int nfe_if_flags; + u_int nfe_flags; +#define NFE_JUMBO_SUP 0x01 +#define NFE_40BIT_ADDR 0x02 +#define NFE_HW_CSUM 0x04 +#define NFE_HW_VLAN 0x08 + u_int32_t rxtxctl; + u_int32_t nfe_mtu; + u_int8_t mii_phyaddr; + u_char eaddr[ETHER_ADDR_LEN]; + struct task nfe_txtask; + int nfe_link; + + struct nfe_tx_ring txq; + struct nfe_rx_ring rxq; + +#ifdef DEVICE_POLLING + int rxcycles; +#endif +}; + +struct nfe_type { + u_int16_t vid_id; + u_int16_t dev_id; + char *name; +}; --- sys/modules/Makefile.orig Tue Oct 17 11:46:24 2006 +++ sys/modules/Makefile Tue Oct 17 11:46:27 2006 @@ -175,6 +175,7 @@ ${_ncv} \ ${_ndis} \ netgraph \ + ${_nfe} \ nfsclient \ nfsserver \ nge \ @@ -424,6 +425,7 @@ _ixgb= ixgb _mly= mly _mxge= mxge +_nfe= nfe _nve= nve .if !defined(NO_CRYPT) || defined(ALL_MODULES) .if exists(${.CURDIR}/../crypto/via) @@ -483,6 +485,7 @@ _mly= mly _mxge= mxge _ndis= ndis +_nfe= nfe _nve= nve _ppc= ppc _rr232x= rr232x --- sys/modules/nfe/Makefile.orig Thu Jan 1 07:30:00 1970 +++ sys/modules/nfe/Makefile Tue Oct 17 11:42:32 2006 @@ -0,0 +1,8 @@ +# $FreeBSD: src/sys/modules/nfe/Makefile,v 1.1 2006/06/26 23:41:07 obrien Exp $ + +.PATH: ${.CURDIR}/../../dev/nfe + +KMOD= if_nfe +SRCS= if_nfe.c opt_bdg.h device_if.h bus_if.h pci_if.h miibus_if.h + +.include --- share/man/man4/Makefile.orig Sun Oct 15 21:14:47 2006 +++ share/man/man4/Makefile Tue Oct 17 11:59:44 2006 @@ -177,6 +177,7 @@ ncv.4 \ netgraph.4 \ netintro.4 \ + ${_nfe.4} \ ${_nfsmb.4} \ ng_async.4 \ ng_atm.4 \ @@ -303,6 +304,7 @@ snd_csa.4 \ snd_ds1.4 \ snd_emu10k1.4 \ + snd_emu10kx.4 \ snd_envy24.4 \ snd_envy24ht.4 \ snd_es137x.4 \ @@ -466,6 +468,7 @@ MLINKS+=my.4 if_my.4 MLINKS+=netintro.4 net.4 \ netintro.4 networking.4 +MLINKS+=${_nfe.4} ${_if_nfe.4} MLINKS+=nge.4 if_nge.4 MLINKS+=${_nve.4} ${_if_nve.4} MLINKS+=oldcard.4 card.4 @@ -491,6 +494,8 @@ MLINKS+=sl.4 if_sl.4 MLINKS+=smp.4 SMP.4 MLINKS+=sn.4 if_sn.4 +MLINKS+=snd_sbc.4 snd_sb16.4 \ + snd_sbc.4 snd_sb8.4 MLINKS+=${_spkr.4} ${_speaker.4} MLINKS+=splash.4 screensaver.4 MLINKS+=ste.4 if_ste.4 @@ -518,8 +523,10 @@ .if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" _amdsmb.4= amdsmb.4 +_if_nfe.4= if_nfe.4 _if_nve.4= if_nve.4 _ipmi.4= ipmi.4 +_nfe.4= nfe.4 _nfsmb.4= nfsmb.4 _nve.4= nve.4 _rr232x.4= rr232x.4 --- share/man/man4/nfe.4.orig Thu Jan 1 07:30:00 1970 +++ share/man/man4/nfe.4 Sat Sep 30 22:30:07 2006 @@ -0,0 +1,92 @@ +.\" $OpenBSD: nfe.4,v 1.7 2006/02/28 08:13:47 jsg Exp $ +.\" +.\" Copyright (c) 2006 Jonathan Gray +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD: src/share/man/man4/nfe.4,v 1.3 2006/09/30 14:30:07 ru Exp $ +.\" +.Dd June 28, 2006 +.Dt NFE 4 +.Os +.Sh NAME +.Nm nfe +.Nd "NVIDIA nForce MCP Ethernet driver" +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device miibus" +.Cd "device nfe" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_nfe_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports PCI Ethernet adapters based on the NVIDIA +nForce Media and Communications Processors (MCP), such as +the nForce, nForce 2, nForce 3, CK804, MCP04, MCP51 and MCP55 +Ethernet controller chips. +.Pp +The +.Nm +driver supports the following media types: +.Pp +.Bl -tag -width ".Cm autoselect" -compact +.It Cm autoselect +Enable autoselection of the media type and options. +.It Cm 10baseT +Set 10Mbps operation. +.It Cm 100baseTX +Set 100Mbps (Fast Ethernet) operation. +.It Cm 1000baseT +Set 1000Mbps (Gigabit Ethernet) operation (recent models only). +.El +.Sh SEE ALSO +.Xr arp 4 , +.Xr intro 4 , +.Xr miibus 4 , +.Xr netintro 4 , +.Xr pci 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Ox 3.9 , +and then in +.Fx 6.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Jonathan Gray +.Aq jsg@openbsd.org +and +.An Damien Bergamini +.Aq damien@openbsd.org . +The +.Nm +driver was ported to +.Fx +by +.An Shigeaki Tagashira +.Aq shigeaki@se.hiroshima-u.ac.jp . --- sys/conf/files.orig Mon Oct 16 13:36:19 2006 +++ sys/conf/files Mon Oct 16 18:23:38 2006 @@ -60,6 +60,21 @@ compile-with "CC=${CC} AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h emu10k1-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "emu10k1-alsa%diked.h" +emu10k1-alsa%diked.h optional snd_emu10kx pci \ + dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h" \ + compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h emu10k1-alsa%diked.h" \ + no-obj no-implicit-rule before-depend \ + clean "emu10k1-alsa%diked.h" +p16v-alsa%diked.h optional snd_emu10kx pci \ + dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p16v-alsa.h" \ + compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p16v-alsa.h p16v-alsa%diked.h" \ + no-obj no-implicit-rule before-depend \ + clean "p16v-alsa%diked.h" +p17v-alsa%diked.h optional snd_emu10kx pci \ + dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h" \ + compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \ + no-obj no-implicit-rule before-depend \ + clean "p17v-alsa%diked.h" miidevs.h optional miibus \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ @@ -869,8 +884,9 @@ dev/sn/if_sn_isa.c optional sn isa dev/sn/if_sn_pccard.c optional sn pccard dev/snp/snp.c optional snp +dev/sound/clone.c optional sound +dev/sound/unit.c optional sound dev/sound/isa/ad1816.c optional snd_ad1816 isa -dev/sound/isa/es1888.c optional snd_ess isa dev/sound/isa/ess.c optional snd_ess isa dev/sound/isa/gusc.c optional snd_gusc isa dev/sound/isa/mss.c optional snd_mss isa @@ -883,18 +899,33 @@ #dev/sound/pci/au88x0.c optional snd_au88x0 pci dev/sound/pci/cmi.c optional snd_cmi pci dev/sound/pci/cs4281.c optional snd_cs4281 pci -dev/sound/pci/csa.c optional snd_csa pci +dev/sound/pci/csa.c optional snd_csa pci \ + warning "kernel contains GPL contaminated csaimg.h header" dev/sound/pci/csapcm.c optional snd_csa pci dev/sound/pci/ds1.c optional snd_ds1 pci dev/sound/pci/emu10k1.c optional snd_emu10k1 pci \ dependency "emu10k1-alsa%diked.h" +dev/sound/pci/emu10kx.c optional snd_emu10kx pci \ + dependency "emu10k1-alsa%diked.h" \ + dependency "p16v-alsa%diked.h" \ + dependency "p17v-alsa%diked.h" \ + warning "kernel contains GPL contaminated emu10kx headers" +dev/sound/pci/emu10kx-pcm.c optional snd_emu10kx pci \ + dependency "emu10k1-alsa%diked.h" \ + dependency "p16v-alsa%diked.h" \ + dependency "p17v-alsa%diked.h" \ + warning "kernel contains GPL contaminated emu10kx headers" +dev/sound/pci/emu10kx-midi.c optional snd_emu10kx pci \ + dependency "emu10k1-alsa%diked.h" \ + warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/envy24.c optional snd_envy24 pci dev/sound/pci/envy24ht.c optional snd_envy24ht pci dev/sound/pci/es137x.c optional snd_es137x pci dev/sound/pci/fm801.c optional snd_fm801 pci dev/sound/pci/ich.c optional snd_ich pci dev/sound/pci/maestro.c optional snd_maestro pci -dev/sound/pci/maestro3.c optional snd_maestro3 pci +dev/sound/pci/maestro3.c optional snd_maestro3 pci \ + warning "kernel contains GPL contaminated maestro3 headers" dev/sound/pci/neomagic.c optional snd_neomagic pci dev/sound/pci/solo.c optional snd_solo pci dev/sound/pci/spicds.c optional snd_spicds pci @@ -924,6 +955,12 @@ #dev/sound/usb/upcm.c optional snd_upcm usb dev/sound/usb/uaudio.c optional snd_uaudio usb dev/sound/usb/uaudio_pcm.c optional snd_uaudio usb +dev/sound/midi/midi.c optional sound +dev/sound/midi/mpu401.c optional sound +dev/sound/midi/mpu_if.m optional sound +dev/sound/midi/mpufoi_if.m optional sound +dev/sound/midi/sequencer.c optional sound +dev/sound/midi/synth_if.m optional sound dev/sr/if_sr.c optional sr dev/sr/if_sr_pci.c optional sr pci dev/stg/tmc18c30.c optional stg --- sys/conf/kmod.mk.orig Wed Sep 13 16:40:20 2006 +++ sys/conf/kmod.mk Sat Oct 14 22:01:21 2006 @@ -336,7 +336,8 @@ dev/pci/pcib_if.m dev/ppbus/ppbus_if.m dev/smbus/smbus_if.m \ dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \ dev/sound/pcm/feeder_if.m dev/sound/pcm/mixer_if.m dev/uart/uart_if.m \ - dev/usb/usb_if.m isa/isa_if.m \ + dev/sound/midi/mpu_if.m dev/sound/midi/mpufoi_if.m \ + dev/sound/midi/synth_if.m dev/usb/usb_if.m isa/isa_if.m \ kern/bus_if.m kern/cpufreq_if.m kern/device_if.m \ libkern/iconv_converter_if.m opencrypto/crypto_if.m \ pc98/pc98/canbus_if.m pci/agp_if.m --- sys/conf/options.orig Tue Oct 17 01:17:54 2006 +++ sys/conf/options Tue Oct 17 01:19:23 2006 @@ -727,3 +727,6 @@ # XBOX options for FreeBSD/i386, but some files are MI XBOX opt_xbox.h + +# snd_emu10kx sound driver options +SND_EMU10KX_MULTICHANNEL opt_emu10kx.h --- sys/sys/soundcard.h.orig Tue Jul 10 04:51:37 2007 +++ sys/sys/soundcard.h Thu Jul 12 12:04:19 2007 @@ -3,7 +3,7 @@ */ /*- - * Copyright by Hannu Savolainen 1993 + * Copyright by Hannu Savolainen 1993 / 4Front Technologies 1993-2006 * Modified for the new FreeBSD sound driver by Luigi Rizzo, 1997 * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,13 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/sys/soundcard.h,v 1.44.2.1 2005/12/30 19:55:52 netchild Exp $ + * $FreeBSD: src/sys/sys/soundcard.h,v 1.48 2006/11/26 11:55:48 netchild Exp $ + */ + +/* + * Unless coordinating changes with 4Front Technologies, do NOT make any + * modifications to ioctl commands, types, etc. that would break + * compatibility with the OSS API. */ #ifndef _SYS_SOUNDCARD_H_ @@ -719,8 +725,6 @@ int caps; }; -#define MIDI_CAP_MPU401 1 /* MPU-401 intelligent mode */ - struct midi_info { char name[30]; int device; /* 0-N. INITIALIZE BEFORE CALLING */ @@ -1436,5 +1440,439 @@ #define SOUND_PCM_GETOPTR SNDCTL_DSP_GETOPTR #define SOUND_PCM_MAPINBUF SNDCTL_DSP_MAPINBUF #define SOUND_PCM_MAPOUTBUF SNDCTL_DSP_MAPOUTBUF + +/***********************************************************************/ + +/** + * XXX OSSv4 defines -- some bits taken straight out of the new + * sys/soundcard.h bundled with recent OSS releases. + * + * NB: These macros and structures will be reorganized and inserted + * in appropriate places throughout this file once the code begins + * to take shape. + * + * @todo reorganize layout more like the 4Front version + * @todo ask about maintaining __SIOWR vs. _IOWR ioctl cmd defines + */ + +/** + * @note The @c OSSV4_EXPERIMENT macro is meant to wrap new development code + * in the sound system relevant to adopting 4Front's OSSv4 specification. + * Users should not enable this! Really! + */ +#if 0 +# define OSSV4_EXPERIMENT 1 +#else +# undef OSSV4_EXPERIMENT +#endif + +#ifdef SOUND_VERSION +# undef SOUND_VERSION +# define SOUND_VERSION 0x040000 +#endif /* !SOUND_VERSION */ + +#define OSS_LONGNAME_SIZE 64 +#define OSS_LABEL_SIZE 16 +#define OSS_DEVNODE_SIZE 32 +typedef char oss_longname_t[OSS_LONGNAME_SIZE]; +typedef char oss_label_t[OSS_LABEL_SIZE]; +typedef char oss_devnode_t[OSS_DEVNODE_SIZE]; + +typedef struct audio_errinfo +{ + int play_underruns; + int rec_overruns; + unsigned int play_ptradjust; + unsigned int rec_ptradjust; + int play_errorcount; + int rec_errorcount; + int play_lasterror; + int rec_lasterror; + long play_errorparm; + long rec_errorparm; + int filler[16]; +} audio_errinfo; + +#define SNDCTL_DSP_GETPLAYVOL _IOR ('P', 24, int) +#define SNDCTL_DSP_SETPLAYVOL _IOWR('P', 24, int) +#define SNDCTL_DSP_GETERROR _IOR ('P', 25, audio_errinfo) + + +/* + **************************************************************************** + * Sync groups for audio devices + */ +typedef struct oss_syncgroup +{ + int id; + int mode; + int filler[16]; +} oss_syncgroup; + +#define SNDCTL_DSP_SYNCGROUP _IOWR('P', 28, oss_syncgroup) +#define SNDCTL_DSP_SYNCSTART _IOW ('P', 29, int) + +/* + ************************************************************************** + * "cooked" mode enables software based conversions for sample rate, sample + * format (bits) and number of channels (mono/stereo). These conversions are + * required with some devices that support only one sample rate or just stereo + * to let the applications to use other formats. The cooked mode is enabled by + * default. However it's necessary to disable this mode when mmap() is used or + * when very deterministic timing is required. SNDCTL_DSP_COOKEDMODE is an + * optional call introduced in OSS 3.9.6f. It's _error return must be ignored_ + * since normally this call will return erno=EINVAL. + * + * SNDCTL_DSP_COOKEDMODE must be called immediately after open before doing + * anything else. Otherwise the call will not have any effect. + */ +#define SNDCTL_DSP_COOKEDMODE _IOW ('P', 30, int) + +/* + ************************************************************************** + * SNDCTL_DSP_SILENCE and SNDCTL_DSP_SKIP are new calls in OSS 3.99.0 + * that can be used to implement pause/continue during playback (no effect + * on recording). + */ +#define SNDCTL_DSP_SILENCE _IO ('P', 31) +#define SNDCTL_DSP_SKIP _IO ('P', 32) + +/* + **************************************************************************** + * Abort transfer (reset) functions for input and output + */ +#define SNDCTL_DSP_HALT_INPUT _IO ('P', 33) +#define SNDCTL_DSP_RESET_INPUT SNDCTL_DSP_HALT_INPUT /* Old name */ +#define SNDCTL_DSP_HALT_OUTPUT _IO ('P', 34) +#define SNDCTL_DSP_RESET_OUTPUT SNDCTL_DSP_HALT_OUTPUT /* Old name */ + +/* + **************************************************************************** + * Low water level control + */ +#define SNDCTL_DSP_LOW_WATER _IOW ('P', 34, int) + +/** @todo Get rid of OSS_NO_LONG_LONG references? */ + +/* + **************************************************************************** + * 64 bit pointer support. Only available in environments that support + * the 64 bit (long long) integer type. + */ +#ifndef OSS_NO_LONG_LONG +typedef struct +{ + long long samples; + int fifo_samples; + int filler[32]; /* For future use */ +} oss_count_t; + +#define SNDCTL_DSP_CURRENT_IPTR _IOR ('P', 35, oss_count_t) +#define SNDCTL_DSP_CURRENT_OPTR _IOR ('P', 36, oss_count_t) +#endif + +/* + **************************************************************************** + * Interface for selecting recording sources and playback output routings. + */ +#define SNDCTL_DSP_GET_RECSRC_NAMES _IOR ('P', 37, oss_mixer_enuminfo) +#define SNDCTL_DSP_GET_RECSRC _IOR ('P', 38, int) +#define SNDCTL_DSP_SET_RECSRC _IOWR('P', 38, int) + +#define SNDCTL_DSP_GET_PLAYTGT_NAMES _IOR ('P', 39, oss_mixer_enuminfo) +#define SNDCTL_DSP_GET_PLAYTGT _IOR ('P', 40, int) +#define SNDCTL_DSP_SET_PLAYTGT _IOWR('P', 40, int) +#define SNDCTL_DSP_GETRECVOL _IOR ('P', 41, int) +#define SNDCTL_DSP_SETRECVOL _IOWR('P', 41, int) + +/* + *************************************************************************** + * Some calls for setting the channel assignment with multi channel devices + * (see the manual for details). */ +#define SNDCTL_DSP_GET_CHNORDER _IOR ('P', 42, unsigned long long) +#define SNDCTL_DSP_SET_CHNORDER _IOWR('P', 42, unsigned long long) +# define CHID_UNDEF 0 +# define CHID_L 1 # define CHID_R 2 +# define CHID_C 3 +# define CHID_LFE 4 +# define CHID_LS 5 +# define CHID_RS 6 +# define CHID_LR 7 +# define CHID_RR 8 +#define CHNORDER_UNDEF 0x0000000000000000ULL +#define CHNORDER_NORMAL 0x0000000087654321ULL + +#define MAX_PEAK_CHANNELS 128 +typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS]; +#define SNDCTL_DSP_GETIPEAKS _IOR('P', 43, oss_peaks_t) +#define SNDCTL_DSP_GETOPEAKS _IOR('P', 44, oss_peaks_t) +#define SNDCTL_DSP_POLICY _IOW('P', 45, int) /* See the manual */ + +/* + * OSS_SYSIFO is obsolete. Use SNDCTL_SYSINFO insteads. + */ +#define OSS_GETVERSION _IOR ('M', 118, int) + +/** + * @brief Argument for SNDCTL_SYSINFO ioctl. + * + * For use w/ the SNDCTL_SYSINFO ioctl available on audio (/dev/dsp*), + * mixer, and MIDI devices. + */ +typedef struct oss_sysinfo +{ + char product[32]; /* For example OSS/Free, OSS/Linux or + OSS/Solaris */ + char version[32]; /* For example 4.0a */ + int versionnum; /* See OSS_GETVERSION */ + char options[128]; /* Reserved */ + + int numaudios; /* # of audio/dsp devices */ + int openedaudio[8]; /* Bit mask telling which audio devices + are busy */ + + int numsynths; /* # of availavle synth devices */ + int nummidis; /* # of available MIDI ports */ + int numtimers; /* # of available timer devices */ + int nummixers; /* # of mixer devices */ + + int openedmidi[8]; /* Bit mask telling which midi devices + are busy */ + int numcards; /* Number of sound cards in the system */ + int filler[241]; /* For future expansion (set to -1) */ +} oss_sysinfo; + +typedef struct oss_mixext +{ + int dev; /* Mixer device number */ + int ctrl; /* Controller number */ + int type; /* Entry type */ +# define MIXT_DEVROOT 0 /* Device root entry */ +# define MIXT_GROUP 1 /* Controller group */ +# define MIXT_ONOFF 2 /* OFF (0) or ON (1) */ +# define MIXT_ENUM 3 /* Enumerated (0 to maxvalue) */ +# define MIXT_MONOSLIDER 4 /* Mono slider (0 to 100) */ +# define MIXT_STEREOSLIDER 5 /* Stereo slider (dual 0 to 100) */ +# define MIXT_MESSAGE 6 /* (Readable) textual message */ +# define MIXT_MONOVU 7 /* VU meter value (mono) */ +# define MIXT_STEREOVU 8 /* VU meter value (stereo) */ +# define MIXT_MONOPEAK 9 /* VU meter peak value (mono) */ +# define MIXT_STEREOPEAK 10 /* VU meter peak value (stereo) */ +# define MIXT_RADIOGROUP 11 /* Radio button group */ +# define MIXT_MARKER 12 /* Separator between normal and extension entries */ +# define MIXT_VALUE 13 /* Decimal value entry */ +# define MIXT_HEXVALUE 14 /* Hexadecimal value entry */ +# define MIXT_MONODB 15 /* Mono atten. slider (0 to -144) */ +# define MIXT_STEREODB 16 /* Stereo atten. slider (dual 0 to -144) */ +# define MIXT_SLIDER 17 /* Slider (mono) with full integer range */ +# define MIXT_3D 18 + + /* Possible value range (minvalue to maxvalue) */ + /* Note that maxvalue may also be smaller than minvalue */ + int maxvalue; + int minvalue; + + int flags; +# define MIXF_READABLE 0x00000001 /* Has readable value */ +# define MIXF_WRITEABLE 0x00000002 /* Has writeable value */ +# define MIXF_POLL 0x00000004 /* May change itself */ +# define MIXF_HZ 0x00000008 /* Herz scale */ +# define MIXF_STRING 0x00000010 /* Use dynamic extensions for value */ +# define MIXF_DYNAMIC 0x00000010 /* Supports dynamic extensions */ +# define MIXF_OKFAIL 0x00000020 /* Interpret value as 1=OK, 0=FAIL */ +# define MIXF_FLAT 0x00000040 /* Flat vertical space requirements */ +# define MIXF_LEGACY 0x00000080 /* Legacy mixer control group */ + char id[16]; /* Mnemonic ID (mainly for internal use) */ + int parent; /* Entry# of parent (group) node (-1 if root) */ + + int dummy; /* Internal use */ + + int timestamp; + + char data[64]; /* Misc data (entry type dependent) */ + unsigned char enum_present[32]; /* Mask of allowed enum values */ + int control_no; /* SOUND_MIXER_VOLUME..SOUND_MIXER_MIDI */ + /* (-1 means not indicated) */ + +/* + * The desc field is reserved for internal purposes of OSS. It should not be + * used by applications. + */ + unsigned int desc; +#define MIXEXT_SCOPE_MASK 0x0000003f +#define MIXEXT_SCOPE_OTHER 0x00000000 +#define MIXEXT_SCOPE_INPUT 0x00000001 +#define MIXEXT_SCOPE_OUTPUT 0x00000002 +#define MIXEXT_SCOPE_MONITOR 0x00000003 +#define MIXEXT_SCOPE_RECSWITCH 0x00000004 + + char extname[32]; + int update_counter; + int filler[7]; +} oss_mixext; + +typedef struct oss_mixext_root +{ + char id[16]; + char name[48]; +} oss_mixext_root; + +typedef struct oss_mixer_value +{ + int dev; + int ctrl; + int value; + int flags; /* Reserved for future use. Initialize to 0 */ + int timestamp; /* Must be set to oss_mixext.timestamp */ + int filler[8]; /* Reserved for future use. Initialize to 0 */ +} oss_mixer_value; + +#define OSS_ENUM_MAXVALUE 255 +typedef struct oss_mixer_enuminfo +{ + int dev; + int ctrl; + int nvalues; + int version; /* Read the manual */ + short strindex[OSS_ENUM_MAXVALUE]; + char strings[3000]; +} oss_mixer_enuminfo; + +#define OPEN_READ PCM_ENABLE_INPUT +#define OPEN_WRITE PCM_ENABLE_OUTPUT +#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE) + +/** + * @brief Argument for SNDCTL_AUDIOINFO ioctl. + * + * For use w/ the SNDCTL_AUDIOINFO ioctl available on audio (/dev/dsp*) + * devices. + */ +typedef struct oss_audioinfo +{ + int dev; /* Audio device number */ + char name[64]; + int busy; /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */ + int pid; + int caps; /* DSP_CAP_INPUT, DSP_CAP_OUTPUT */ + int iformats; + int oformats; + int magic; /* Reserved for internal use */ + char cmd[64]; /* Command using the device (if known) */ + int card_number; + int port_number; + int mixer_dev; + int real_device; /* Obsolete field. Replaced by devnode */ + int enabled; /* 1=enabled, 0=device not ready at this + moment */ + int flags; /* For internal use only - no practical + meaning */ + int min_rate; /* Sample rate limits */ + int max_rate; + int min_channels; /* Number of channels supported */ + int max_channels; + int binding; /* DSP_BIND_FRONT, etc. 0 means undefined */ + int rate_source; + char handle[32]; + #define OSS_MAX_SAMPLE_RATES 20 /* Cannot be changed */ + unsigned int nrates; + unsigned int rates[OSS_MAX_SAMPLE_RATES]; /* Please read the manual before using these */ + oss_longname_t song_name; /* Song name (if given) */ + oss_label_t label; /* Device label (if given) */ + int latency; /* In usecs, -1=unknown */ + oss_devnode_t devnode; /* Device special file name (inside + /dev) */ + int filler[186]; +} oss_audioinfo; + +typedef struct oss_mixerinfo +{ + int dev; + char id[16]; + char name[32]; + int modify_counter; + int card_number; + int port_number; + char handle[32]; + int magic; /* Reserved */ + int enabled; /* Reserved */ + int caps; +#define MIXER_CAP_VIRTUAL 0x00000001 + int flags; /* Reserved */ + int nrext; + /* + * The priority field can be used to select the default (motherboard) + * mixer device. The mixer with the highest priority is the + * most preferred one. -2 or less means that this device cannot be used + * as the default mixer. + */ + int priority; + int filler[254]; /* Reserved */ +} oss_mixerinfo; + +typedef struct oss_midi_info +{ + int dev; /* Midi device number */ + char name[64]; + int busy; /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */ + int pid; + char cmd[64]; /* Command using the device (if known) */ + int caps; +#define MIDI_CAP_MPU401 0x00000001 /**** OBSOLETE ****/ +#define MIDI_CAP_INPUT 0x00000002 +#define MIDI_CAP_OUTPUT 0x00000004 +#define MIDI_CAP_INOUT (MIDI_CAP_INPUT|MIDI_CAP_OUTPUT) +#define MIDI_CAP_VIRTUAL 0x00000008 /* Pseudo device */ +#define MIDI_CAP_MTCINPUT 0x00000010 /* Supports SNDCTL_MIDI_MTCINPUT */ +#define MIDI_CAP_CLIENT 0x00000020 /* Virtual client side device */ +#define MIDI_CAP_SERVER 0x00000040 /* Virtual server side device */ +#define MIDI_CAP_INTERNAL 0x00000080 /* Internal (synth) device */ +#define MIDI_CAP_EXTERNAL 0x00000100 /* external (MIDI port) device */ +#define MIDI_CAP_PTOP 0x00000200 /* Point to point link to one device */ +#define MIDI_CAP_MTC 0x00000400 /* MTC/SMPTE (control) device */ + int magic; /* Reserved for internal use */ + int card_number; + int port_number; + int enabled; /* 1=enabled, 0=device not ready at this moment */ + int flags; /* For internal use only - no practical meaning */ + char handle[32]; + oss_longname_t song_name; /* Song name (if known) */ + oss_label_t label; /* Device label (if given) */ + int latency; /* In usecs, -1=unknown */ + int filler[244]; +} oss_midi_info; + +typedef struct oss_card_info +{ + int card; + char shortname[16]; + char longname[128]; + int flags; + int filler[256]; +} oss_card_info; + +#define SNDCTL_SYSINFO _IOR ('X', 1, oss_sysinfo) +#define OSS_SYSINFO SNDCTL_SYSINFO /* Old name */ + +#define SNDCTL_MIX_NRMIX _IOR ('X', 2, int) +#define SNDCTL_MIX_NREXT _IOWR('X', 3, int) +#define SNDCTL_MIX_EXTINFO _IOWR('X', 4, oss_mixext) +#define SNDCTL_MIX_READ _IOWR('X', 5, oss_mixer_value) +#define SNDCTL_MIX_WRITE _IOWR('X', 6, oss_mixer_value) + +#define SNDCTL_AUDIOINFO _IOWR('X', 7, oss_audioinfo) +#define SNDCTL_MIX_ENUMINFO _IOWR('X', 8, oss_mixer_enuminfo) +#define SNDCTL_MIDIINFO _IOWR('X', 9, oss_midi_info) +#define SNDCTL_MIXERINFO _IOWR('X',10, oss_mixerinfo) +#define SNDCTL_CARDINFO _IOWR('X',11, oss_card_info) + +/* + * Few more "globally" available ioctl calls. + */ +#define SNDCTL_SETSONG _IOW ('Y', 2, oss_longname_t) +#define SNDCTL_GETSONG _IOR ('Y', 2, oss_longname_t) +#define SNDCTL_SETNAME _IOW ('Y', 3, oss_longname_t) +#define SNDCTL_SETLABEL _IOW ('Y', 4, oss_label_t) +#define SNDCTL_GETLABEL _IOR ('Y', 4, oss_label_t) #endif /* !_SYS_SOUNDCARD_H_ */ --- sys/dev/sound/clone.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/clone.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,795 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/clone.c,v 1.4 2007/06/14 11:10:21 ariff Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG) +#include +#endif + +#include + +/* + * So here we go again, another clonedevs manager. Unlike default clonedevs, + * this clone manager is designed to withstand various abusive behavior + * (such as 'while : ; do ls /dev/whatever ; done', etc.), reusable object + * after reaching certain expiration threshold, aggressive garbage collector, + * transparent device allocator and concurrency handling across multiple + * thread/proc. Due to limited information given by dev_clone EVENTHANDLER, + * we don't have much clues whether the caller wants a real open() or simply + * making fun of us with things like stat(), mtime() etc. Assuming that: + * 1) Time window between dev_clone EH <-> real open() should be small + * enough and 2) mtime()/stat() etc. always looks like a half way / stalled + * operation, we can decide whether a new cdev must be created, old + * (expired) cdev can be reused or an existing cdev can be shared. + * + * Most of the operations and logics are generic enough and can be applied + * on other places (such as if_tap, snp, etc). Perhaps this can be + * rearranged to complement clone_*(). However, due to this still being + * specific to the sound driver (and as a proof of concept on how it can be + * done), si_drv2 is used to keep the pointer of the clone list entry to + * avoid expensive lookup. + */ + +/* clone entry */ +struct snd_clone_entry { + TAILQ_ENTRY(snd_clone_entry) link; + struct snd_clone *parent; + struct cdev *devt; + struct timespec tsp; + uint32_t flags; + pid_t pid; + int unit; +}; + +/* clone manager */ +struct snd_clone { + TAILQ_HEAD(link_head, snd_clone_entry) head; + struct timespec tsp; + int refcount; + int size; + int typemask; + int maxunit; + int deadline; + uint32_t flags; +}; + +#ifdef SND_DIAGNOSTIC +#define SND_CLONE_ASSERT(x, y) do { \ + if (!(x)) \ + panic y; \ +} while(0) +#else +#define SND_CLONE_ASSERT(x...) KASSERT(x) +#endif + +/* + * Shamelessly ripped off from vfs_subr.c + * We need at least 1/HZ precision as default timestamping. + */ +enum { SND_TSP_SEC, SND_TSP_HZ, SND_TSP_USEC, SND_TSP_NSEC }; + +static int snd_timestamp_precision = SND_TSP_HZ; +TUNABLE_INT("hw.snd.timestamp_precision", &snd_timestamp_precision); + +void +snd_timestamp(struct timespec *tsp) +{ + struct timeval tv; + + switch (snd_timestamp_precision) { + case SND_TSP_SEC: + tsp->tv_sec = time_second; + tsp->tv_nsec = 0; + break; + case SND_TSP_HZ: + getnanouptime(tsp); + break; + case SND_TSP_USEC: + microuptime(&tv); + TIMEVAL_TO_TIMESPEC(&tv, tsp); + break; + case SND_TSP_NSEC: + nanouptime(tsp); + break; + default: + snd_timestamp_precision = SND_TSP_HZ; + getnanouptime(tsp); + break; + } +} + +#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG) +static int +sysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = snd_timestamp_precision; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err == 0 && req->newptr != NULL) { + switch (val) { + case SND_TSP_SEC: + case SND_TSP_HZ: + case SND_TSP_USEC: + case SND_TSP_NSEC: + snd_timestamp_precision = val; + break; + default: + break; + } + } + + return (err); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, timestamp_precision, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_timestamp_precision, "I", + "timestamp precision (0=s 1=hz 2=us 3=ns)"); +#endif + +/* + * snd_clone_create() : Return opaque allocated clone manager. + */ +struct snd_clone * +snd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags) +{ + struct snd_clone *c; + + SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT), + ("invalid typemask: 0x%08x", typemask)); + SND_CLONE_ASSERT(maxunit == -1 || + !(maxunit & ~(~typemask & SND_CLONE_MAXUNIT)), + ("maxunit overflow: typemask=0x%08x maxunit=%d", + typemask, maxunit)); + SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK), + ("invalid clone flags=0x%08x", flags)); + + c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO); + c->refcount = 0; + c->size = 0; + c->typemask = typemask; + c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) : + maxunit; + c->deadline = deadline; + c->flags = flags; + snd_timestamp(&c->tsp); + TAILQ_INIT(&c->head); + + return (c); +} + +int +snd_clone_busy(struct snd_clone *c) +{ + struct snd_clone_entry *ce; + + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + if (c->size == 0) + return (0); + + TAILQ_FOREACH(ce, &c->head, link) { + if ((ce->flags & SND_CLONE_BUSY) || + (ce->devt != NULL && ce->devt->si_threadcount != 0)) + return (EBUSY); + } + + return (0); +} + +/* + * snd_clone_enable()/disable() : Suspend/resume clone allocation through + * snd_clone_alloc(). Everything else will not be affected by this. + */ +int +snd_clone_enable(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + if (c->flags & SND_CLONE_ENABLE) + return (EINVAL); + + c->flags |= SND_CLONE_ENABLE; + + return (0); +} + +int +snd_clone_disable(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + if (!(c->flags & SND_CLONE_ENABLE)) + return (EINVAL); + + c->flags &= ~SND_CLONE_ENABLE; + + return (0); +} + +/* + * Getters / Setters. Not worth explaining :) + */ +int +snd_clone_getsize(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + return (c->size); +} + +int +snd_clone_getmaxunit(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + return (c->maxunit); +} + +int +snd_clone_setmaxunit(struct snd_clone *c, int maxunit) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + SND_CLONE_ASSERT(maxunit == -1 || + !(maxunit & ~(~c->typemask & SND_CLONE_MAXUNIT)), + ("maxunit overflow: typemask=0x%08x maxunit=%d", + c->typemask, maxunit)); + + c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) : + maxunit; + + return (c->maxunit); +} + +int +snd_clone_getdeadline(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + return (c->deadline); +} + +int +snd_clone_setdeadline(struct snd_clone *c, int deadline) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + c->deadline = deadline; + + return (c->deadline); +} + +int +snd_clone_gettime(struct snd_clone *c, struct timespec *tsp) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec")); + + *tsp = c->tsp; + + return (0); +} + +uint32_t +snd_clone_getflags(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + return (c->flags); +} + +uint32_t +snd_clone_setflags(struct snd_clone *c, uint32_t flags) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK), + ("invalid clone flags=0x%08x", flags)); + + c->flags = flags; + + return (c->flags); +} + +int +snd_clone_getdevtime(struct cdev *dev, struct timespec *tsp) +{ + struct snd_clone_entry *ce; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec")); + + ce = dev->si_drv2; + if (ce == NULL) + return (ENODEV); + + SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); + + *tsp = ce->tsp; + + return (0); +} + +uint32_t +snd_clone_getdevflags(struct cdev *dev) +{ + struct snd_clone_entry *ce; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + + ce = dev->si_drv2; + if (ce == NULL) + return (0xffffffff); + + SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); + + return (ce->flags); +} + +uint32_t +snd_clone_setdevflags(struct cdev *dev, uint32_t flags) +{ + struct snd_clone_entry *ce; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK), + ("invalid clone dev flags=0x%08x", flags)); + + ce = dev->si_drv2; + if (ce == NULL) + return (0xffffffff); + + SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); + + ce->flags = flags; + + return (ce->flags); +} + +/* Elapsed time conversion to ms */ +#define SND_CLONE_ELAPSED(x, y) \ + ((((x)->tv_sec - (y)->tv_sec) * 1000) + \ + (((y)->tv_nsec > (x)->tv_nsec) ? \ + (((1000000000L + (x)->tv_nsec - \ + (y)->tv_nsec) / 1000000) - 1000) : \ + (((x)->tv_nsec - (y)->tv_nsec) / 1000000))) + +#define SND_CLONE_EXPIRED(x, y, z) \ + ((x)->deadline < 1 || \ + ((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) || \ + SND_CLONE_ELAPSED(y, z) > (x)->deadline) + +/* + * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to + * clone.h for explanations on GC settings. + */ +int +snd_clone_gc(struct snd_clone *c) +{ + struct snd_clone_entry *ce, *tce; + struct timespec now; + int pruned; + + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0) + return (0); + + snd_timestamp(&now); + + /* + * Bail out if the last clone handler was invoked below the deadline + * threshold. + */ + if ((c->flags & SND_CLONE_GC_EXPIRED) && + !SND_CLONE_EXPIRED(c, &now, &c->tsp)) + return (0); + + pruned = 0; + + /* + * Visit each object in reverse order. If the object is still being + * referenced by a valid open(), skip it. Look for expired objects + * and either revoke its clone invocation status or mercilessly + * throw it away. + */ + TAILQ_FOREACH_REVERSE_SAFE(ce, &c->head, link_head, link, tce) { + if (!(ce->flags & SND_CLONE_BUSY) && + (!(ce->flags & SND_CLONE_INVOKE) || + SND_CLONE_EXPIRED(c, &now, &ce->tsp))) { + if ((c->flags & SND_CLONE_GC_REVOKE) || + ce->devt->si_threadcount != 0) { + ce->flags &= ~SND_CLONE_INVOKE; + ce->pid = -1; + } else { + TAILQ_REMOVE(&c->head, ce, link); + destroy_dev(ce->devt); + free(ce, M_DEVBUF); + c->size--; + } + pruned++; + } + } + + /* return total pruned objects */ + return (pruned); +} + +void +snd_clone_destroy(struct snd_clone *c) +{ + struct snd_clone_entry *ce, *tmp; + + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + ce = TAILQ_FIRST(&c->head); + while (ce != NULL) { + tmp = TAILQ_NEXT(ce, link); + if (ce->devt != NULL) + destroy_dev(ce->devt); + free(ce, M_DEVBUF); + ce = tmp; + } + + free(c, M_DEVBUF); +} + +/* + * snd_clone_acquire() : The vital part of concurrency management. Must be + * called somewhere at the beginning of open() handler. ENODEV is not really + * fatal since it just tell the caller that this is not cloned stuff. + * EBUSY is *real*, don't forget that! + */ +int +snd_clone_acquire(struct cdev *dev) +{ + struct snd_clone_entry *ce; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + + ce = dev->si_drv2; + if (ce == NULL) + return (ENODEV); + + SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); + + ce->flags &= ~SND_CLONE_INVOKE; + + if (ce->flags & SND_CLONE_BUSY) + return (EBUSY); + + ce->flags |= SND_CLONE_BUSY; + + return (0); +} + +/* + * snd_clone_release() : Release busy status. Must be called somewhere at + * the end of close() handler, or somewhere after fail open(). + */ +int +snd_clone_release(struct cdev *dev) +{ + struct snd_clone_entry *ce; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + + ce = dev->si_drv2; + if (ce == NULL) + return (ENODEV); + + SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); + + ce->flags &= ~SND_CLONE_INVOKE; + + if (!(ce->flags & SND_CLONE_BUSY)) + return (EBADF); + + ce->flags &= ~SND_CLONE_BUSY; + ce->pid = -1; + + return (0); +} + +/* + * snd_clone_ref/unref() : Garbage collector reference counter. To make + * garbage collector run automatically, the sequence must be something like + * this (both in open() and close() handlers): + * + * open() - 1) snd_clone_acquire() + * 2) .... check check ... if failed, snd_clone_release() + * 3) Success. Call snd_clone_ref() + * + * close() - 1) .... check check check .... + * 2) Success. snd_clone_release() + * 3) snd_clone_unref() . Garbage collector will run at this point + * if this is the last referenced object. + */ +int +snd_clone_ref(struct cdev *dev) +{ + struct snd_clone_entry *ce; + struct snd_clone *c; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + + ce = dev->si_drv2; + if (ce == NULL) + return (0); + + c = ce->parent; + SND_CLONE_ASSERT(c != NULL, ("NULL parent")); + SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0")); + + return (++c->refcount); +} + +int +snd_clone_unref(struct cdev *dev) +{ + struct snd_clone_entry *ce; + struct snd_clone *c; + + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + + ce = dev->si_drv2; + if (ce == NULL) + return (0); + + c = ce->parent; + SND_CLONE_ASSERT(c != NULL, ("NULL parent")); + SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0")); + + c->refcount--; + + /* + * Run automatic garbage collector, if needed. + */ + if ((c->flags & SND_CLONE_GC_UNREF) && + (!(c->flags & SND_CLONE_GC_LASTREF) || + (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF)))) + (void)snd_clone_gc(c); + + return (c->refcount); +} + +void +snd_clone_register(struct snd_clone_entry *ce, struct cdev *dev) +{ + SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry")); + SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); + SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL")); + SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC, + ("invalid clone alloc flags=0x%08x", ce->flags)); + SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL")); + SND_CLONE_ASSERT(ce->unit == dev2unit(dev), + ("invalid unit ce->unit=0x%08x dev2unit=0x%08x", + ce->unit, dev2unit(dev))); + + SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); + + dev->si_drv2 = ce; + ce->devt = dev; + ce->flags &= ~SND_CLONE_ALLOC; + ce->flags |= SND_CLONE_INVOKE; +} + +struct snd_clone_entry * +snd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask) +{ + struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce; + struct timespec now; + int cunit, allocunit; + pid_t curpid; + + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer")); + SND_CLONE_ASSERT((c->typemask & tmask) == tmask, + ("invalid tmask: typemask=0x%08x tmask=0x%08x", + c->typemask, tmask)); + SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer")); + SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)), + ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d", + c->typemask, tmask, *unit)); + + if (!(c->flags & SND_CLONE_ENABLE) || + (*unit != -1 && *unit > c->maxunit)) + return (NULL); + + ce = NULL; + after = NULL; + bce = NULL; /* "b"usy candidate */ + cce = NULL; /* "c"urthread/proc candidate */ + nce = NULL; /* "n"ull, totally unbusy candidate */ + tce = NULL; /* Last "t"ry candidate */ + cunit = 0; + allocunit = (*unit == -1) ? 0 : *unit; + curpid = curthread->td_proc->p_pid; + + snd_timestamp(&now); + + TAILQ_FOREACH(ce, &c->head, link) { + /* + * Sort incrementally according to device type. + */ + if (tmask > (ce->unit & c->typemask)) { + if (cunit == 0) + after = ce; + continue; + } else if (tmask < (ce->unit & c->typemask)) + break; + + /* + * Shoot.. this is where the grumpiness begin. Just + * return immediately. + */ + if (*unit != -1 && *unit == (ce->unit & ~tmask)) + goto snd_clone_alloc_out; + + cunit++; + /* + * Simmilar device type. Sort incrementally according + * to allocation unit. While here, look for free slot + * and possible collision for new / future allocation. + */ + if (*unit == -1 && (ce->unit & ~tmask) == allocunit) + allocunit++; + if ((ce->unit & ~tmask) < allocunit) + after = ce; + /* + * Clone logic: + * 1. Look for non busy, but keep track of the best + * possible busy cdev. + * 2. Look for the best (oldest referenced) entry that is + * in a same process / thread. + * 3. Look for the best (oldest referenced), absolute free + * entry. + * 4. Lastly, look for the best (oldest referenced) + * any entries that doesn't fit with anything above. + */ + if (ce->flags & SND_CLONE_BUSY) { + if (ce->devt != NULL && (bce == NULL || + timespeccmp(&ce->tsp, &bce->tsp, <))) + bce = ce; + continue; + } + if (ce->pid == curpid && + (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <))) + cce = ce; + else if (!(ce->flags & SND_CLONE_INVOKE) && + (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <))) + nce = ce; + else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <)) + tce = ce; + } + if (*unit != -1) + goto snd_clone_alloc_new; + else if (cce != NULL) { + /* Same proc entry found, go for it */ + ce = cce; + goto snd_clone_alloc_out; + } else if (nce != NULL) { + /* + * Next, try absolute free entry. If the calculated + * allocunit is smaller, create new entry instead. + */ + if (allocunit < (nce->unit & ~tmask)) + goto snd_clone_alloc_new; + ce = nce; + goto snd_clone_alloc_out; + } else if (allocunit > c->maxunit) { + /* + * Maximum allowable unit reached. Try returning any + * available cdev and hope for the best. If the lookup is + * done for things like stat(), mtime() etc. , things should + * be ok. Otherwise, open() handler should do further checks + * and decide whether to return correct error code or not. + */ + if (tce != NULL) { + ce = tce; + goto snd_clone_alloc_out; + } else if (bce != NULL) { + ce = bce; + goto snd_clone_alloc_out; + } + return (NULL); + } + +snd_clone_alloc_new: + /* + * No free entries found, and we still haven't reached maximum + * allowable units. Allocate, setup a minimal unique entry with busy + * status so nobody will monkey on this new entry. Unit magic is set + * right here to avoid collision with other contesting handler. + * The caller must be carefull here to maintain its own + * synchronization, as long as it will not conflict with malloc(9) + * operations. + * + * That said, go figure. + */ + ce = malloc(sizeof(*ce), M_DEVBUF, + ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); + if (ce == NULL) { + if (*unit != -1) + return (NULL); + /* + * We're being dense, ignorance is bliss, + * Super Regulatory Measure (TM).. TRY AGAIN! + */ + if (nce != NULL) { + ce = nce; + goto snd_clone_alloc_out; + } else if (tce != NULL) { + ce = tce; + goto snd_clone_alloc_out; + } else if (bce != NULL) { + ce = bce; + goto snd_clone_alloc_out; + } + return (NULL); + } + /* Setup new entry */ + ce->parent = c; + ce->unit = tmask | allocunit; + ce->pid = curpid; + ce->tsp = now; + ce->flags |= SND_CLONE_ALLOC; + if (after != NULL) { + TAILQ_INSERT_AFTER(&c->head, after, ce, link); + } else { + TAILQ_INSERT_HEAD(&c->head, ce, link); + } + c->size++; + c->tsp = now; + /* + * Save new allocation unit for caller which will be used + * by make_dev(). + */ + *unit = allocunit; + + return (ce); + +snd_clone_alloc_out: + /* + * Set, mark, timestamp the entry if this is a truly free entry. + * Leave busy entry alone. + */ + if (!(ce->flags & SND_CLONE_BUSY)) { + ce->pid = curpid; + ce->tsp = now; + ce->flags |= SND_CLONE_INVOKE; + } + c->tsp = now; + *dev = ce->devt; + + return (NULL); +} --- sys/dev/sound/clone.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/clone.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/clone.h,v 1.2 2007/06/14 11:10:21 ariff Exp $ + */ + +#ifndef _SND_CLONE_H_ +#define _SND_CLONE_H_ + +struct snd_clone_entry; +struct snd_clone; + +/* + * 750 milisecond default deadline. Short enough to not cause excessive + * garbage collection, long enough to indicate stalled VFS. + */ +#define SND_CLONE_DEADLINE_DEFAULT 750 + +/* + * Fit within 24bit MAXMINOR. + */ +#define SND_CLONE_MAXUNIT 0xffffff + +/* + * Creation flags, mostly related to the behaviour of garbage collector. + * + * SND_CLONE_ENABLE - Enable clone allocation. + * SND_CLONE_GC_ENABLE - Enable garbage collector operation, automatically + * or if explicitly called upon. + * SND_CLONE_GC_UNREF - Garbage collect during unref operation. + * SND_CLONE_GC_LASTREF - Garbage collect during last reference + * (refcount = 0) + * SND_CLONE_GC_EXPIRED - Don't garbage collect unless the global clone + * handler has been expired. + * SND_CLONE_GC_REVOKE - Revoke clone invocation status which has been + * expired instead of removing and freeing it. + * SND_CLONE_WAITOK - malloc() is allowed to sleep while allocating + * clone entry. + */ +#define SND_CLONE_ENABLE 0x00000001 +#define SND_CLONE_GC_ENABLE 0x00000002 +#define SND_CLONE_GC_UNREF 0x00000004 +#define SND_CLONE_GC_LASTREF 0x00000008 +#define SND_CLONE_GC_EXPIRED 0x00000010 +#define SND_CLONE_GC_REVOKE 0x00000020 +#define SND_CLONE_WAITOK 0x80000000 + +#define SND_CLONE_GC_MASK (SND_CLONE_GC_ENABLE | \ + SND_CLONE_GC_UNREF | \ + SND_CLONE_GC_LASTREF | \ + SND_CLONE_GC_EXPIRED | \ + SND_CLONE_GC_REVOKE) + +#define SND_CLONE_MASK (SND_CLONE_ENABLE | SND_CLONE_GC_MASK | \ + SND_CLONE_WAITOK) + +/* + * Runtime clone device flags + * + * These are mostly private to the clone manager operation: + * + * SND_CLONE_NEW - New clone allocation in progress. + * SND_CLONE_INVOKE - Cloning being invoked, waiting for next VFS operation. + * SND_CLONE_BUSY - In progress, being referenced by living thread/proc. + */ +#define SND_CLONE_NEW 0x00000001 +#define SND_CLONE_INVOKE 0x00000002 +#define SND_CLONE_BUSY 0x00000004 + +/* + * Nothing important, just for convenience. + */ +#define SND_CLONE_ALLOC (SND_CLONE_NEW | SND_CLONE_INVOKE | \ + SND_CLONE_BUSY) + +#define SND_CLONE_DEVMASK SND_CLONE_ALLOC + + +void snd_timestamp(struct timespec *); + +struct snd_clone *snd_clone_create(int, int, int, uint32_t); +int snd_clone_busy(struct snd_clone *); +int snd_clone_enable(struct snd_clone *); +int snd_clone_disable(struct snd_clone *); +int snd_clone_getsize(struct snd_clone *); +int snd_clone_getmaxunit(struct snd_clone *); +int snd_clone_setmaxunit(struct snd_clone *, int); +int snd_clone_getdeadline(struct snd_clone *); +int snd_clone_setdeadline(struct snd_clone *, int); +int snd_clone_gettime(struct snd_clone *, struct timespec *); +uint32_t snd_clone_getflags(struct snd_clone *); +uint32_t snd_clone_setflags(struct snd_clone *, uint32_t); +int snd_clone_getdevtime(struct cdev *, struct timespec *); +uint32_t snd_clone_getdevflags(struct cdev *); +uint32_t snd_clone_setdevflags(struct cdev *, uint32_t); +int snd_clone_gc(struct snd_clone *); +void snd_clone_destroy(struct snd_clone *); +int snd_clone_acquire(struct cdev *); +int snd_clone_release(struct cdev *); +int snd_clone_ref(struct cdev *); +int snd_clone_unref(struct cdev *); +void snd_clone_register(struct snd_clone_entry *, struct cdev *); +struct snd_clone_entry *snd_clone_alloc(struct snd_clone *, struct cdev **, + int *, int); + +#define snd_clone_enabled(x) ((x) != NULL && \ + (snd_clone_getflags(x) & SND_CLONE_ENABLE)) +#define snd_clone_disabled(x) (!snd_clone_enabled(x)) + +#endif /* !_SND_CLONE_H */ --- sys/dev/sound/driver.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/driver.c Thu Jul 12 12:04:19 2007 @@ -18,12 +18,12 @@ * 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, WHETHERIN CONTRACT, STRICT + * 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 THEPOSSIBILITY OF + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/driver.c,v 1.13.2.3 2007/05/13 21:11:40 ariff Exp $ + * $FreeBSD: src/sys/dev/sound/driver.c,v 1.21 2007/01/31 08:53:45 joel Exp $ */ #include @@ -61,11 +61,10 @@ MODULE_DEPEND(snd_driver, snd_csa, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_csapcm, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_ds1, 1, 1, 1); -MODULE_DEPEND(snd_driver, snd_emu10k1, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_emu10kx, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_envy24, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_envy24ht, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_es137x, 1, 1, 1); -MODULE_DEPEND(snd_driver, snd_es1888, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_ess, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_fm801, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_gusc, 1, 1, 1); --- sys/dev/sound/isa/ad1816.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/ad1816.c Thu Jul 12 12:04:19 2007 @@ -1,7 +1,7 @@ /*- * Copyright (c) 1999 Cameron Grant - * Copyright Luigi Rizzo, 1997,1998 - * Copyright by Hannu Savolainen 1994, 1995 + * Copyright (c) 1997,1998 Luigi Rizzo + * Copyright (c) 1994,1995 Hannu Savolainen * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,7 +33,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/ad1816.c,v 1.37.2.2 2006/04/04 17:23:24 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/ad1816.c,v 1.45 2007/06/17 06:10:40 ariff Exp $"); struct ad1816_info; @@ -318,7 +318,7 @@ ch->parent = ad1816; ch->channel = c; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, ad1816->bufsize) != 0) + if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, 0, ad1816->bufsize) != 0) return NULL; return ch; } @@ -411,7 +411,7 @@ struct ad1816_info *ad1816 = ch->parent; int wr, reg; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; sndbuf_dma(ch->buffer, go); @@ -598,10 +598,9 @@ struct ad1816_info *ad1816; char status[SND_STATUSLEN], status2[SND_STATUSLEN]; - ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO); - if (!ad1816) return ENXIO; - - ad1816->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + ad1816 = malloc(sizeof(*ad1816), M_DEVBUF, M_WAITOK | M_ZERO); + ad1816->lock = snd_mtxcreate(device_get_nameunit(dev), + "snd_ad1816 softc"); ad1816->io_rid = 2; ad1816->irq_rid = 0; ad1816->drq1_rid = 0; @@ -613,7 +612,8 @@ if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; snd_setup_intr(dev, ad1816->irq, 0, ad1816_intr, ad1816, &ad1816->ih); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/isa/ad1816.h.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/ad1816.h Thu Jul 12 12:04:19 2007 @@ -1,10 +1,34 @@ /*- - * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) + * Copyright (c) 1997 Luigi Rizzo + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/isa/ad1816.h,v 1.4 2007/02/02 13:44:09 joel Exp $ + */ + +/* * This file contains information and macro definitions for * the ad1816 chip - * - * $FreeBSD: src/sys/dev/sound/isa/ad1816.h,v 1.2 2005/01/06 01:43:17 imp Exp $ */ /* AD1816 register macros */ --- sys/dev/sound/isa/es1888.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/es1888.c Thu Jan 1 07:30:00 1970 @@ -1,177 +0,0 @@ -/*- - * Copyright (c) 1999 Doug Rabson - * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 -#include - -#include - -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/es1888.c,v 1.15 2005/01/06 01:43:17 imp Exp $"); - -#ifdef __alpha__ -static int -es1888_dspready(u_int32_t port) -{ - return ((inb(port + SBDSP_STATUS) & 0x80) == 0); -} - -static int -es1888_dspwr(u_int32_t port, u_char val) -{ - int i; - - for (i = 0; i < 1000; i++) { - if (es1888_dspready(port)) { - outb(port + SBDSP_CMD, val); - return 0; - } - if (i > 10) DELAY((i > 100)? 1000 : 10); - } - return ENXIO; -} - -static u_int -es1888_get_byte(u_int32_t port) -{ - int i; - - for (i = 1000; i > 0; i--) { - if (inb(port + DSP_DATA_AVAIL) & 0x80) - return inb(port + DSP_READ); - else - DELAY(20); - } - return 0xffff; -} - -static int -es1888_reset(u_int32_t port) -{ - outb(port + SBDSP_RST, 3); - DELAY(100); - outb(port + SBDSP_RST, 0); - if (es1888_get_byte(port) != 0xAA) { - return ENXIO; /* Sorry */ - } - return 0; -} - -static void -es1888_configuration_mode(void) -{ - /* - * Emit the Read-Sequence-Key to enter configuration - * mode. Note this only works after a reset (or after bit 2 of - * mixer register 0x40 is set). - * - * 3 reads from 0x229 in a row guarantees reset of key - * sequence to beginning. - */ - inb(0x229); - inb(0x229); - inb(0x229); - - inb(0x22b); /* state 1 */ - inb(0x229); /* state 2 */ - inb(0x22b); /* state 3 */ - inb(0x229); /* state 4 */ - inb(0x229); /* state 5 */ - inb(0x22b); /* state 6 */ - inb(0x229); /* state 7 */ -} - -static void -es1888_set_port(u_int32_t port) -{ - es1888_configuration_mode(); - inb(port); -} -#endif - -static void -es1888_identify(driver_t *driver, device_t parent) -{ -/* - * Only use this on alpha since PNPBIOS is a better solution on x86. - */ -#ifdef __alpha__ - u_int32_t lo, hi; - device_t dev; - - es1888_set_port(0x220); - if (es1888_reset(0x220)) - return; - - /* - * Check identification bytes for es1888. - */ - if (es1888_dspwr(0x220, 0xe7)) - return; - hi = es1888_get_byte(0x220); - lo = es1888_get_byte(0x220); - if (hi != 0x68 || (lo & 0xf0) != 0x80) - return; - - /* - * Program irq and drq. - */ - if (es1888_dspwr(0x220, 0xc6) /* enter extended mode */ - || es1888_dspwr(0x220, 0xb1) /* write register b1 */ - || es1888_dspwr(0x220, 0x14) /* enable irq 5 */ - || es1888_dspwr(0x220, 0xb2) /* write register b1 */ - || es1888_dspwr(0x220, 0x18)) /* enable drq 1 */ - return; - - /* - * Create the device and program its resources. - */ - dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNPBIOS, NULL, -1); - bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x220, 0x10); - bus_set_resource(dev, SYS_RES_IRQ, 0, 5, 1); - bus_set_resource(dev, SYS_RES_DRQ, 0, 1, 1); - isa_set_vendorid(dev, PNP_EISAID("ESS1888")); - isa_set_logicalid(dev, PNP_EISAID("ESS1888")); -#endif -} - -static device_method_t es1888_methods[] = { - /* Device interface */ - DEVMETHOD(device_identify, es1888_identify), - - { 0, 0 } -}; - -static driver_t es1888_driver = { - "pcm", - es1888_methods, - 1, /* no softc */ -}; - -DRIVER_MODULE(snd_es1888, isa, es1888_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_es1888, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); -MODULE_VERSION(snd_es1888, 1); - - --- sys/dev/sound/isa/ess.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/ess.c Thu Jul 12 12:04:19 2007 @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant - * Copyright 1997,1998 Luigi Rizzo. + * Copyright (c) 1997,1998 Luigi Rizzo * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -38,7 +38,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/ess.c,v 1.34.2.2 2006/01/19 01:17:00 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/ess.c,v 1.42 2007/06/17 06:10:40 ariff Exp $"); #define ESS_BUFFSIZE (4096) #define ABS(x) (((x) < 0)? -(x) : (x)) @@ -97,7 +97,8 @@ bus_dma_tag_t parent_dmat; unsigned int bufsize; - int type, duplex:1, newspeed:1; + int type; + unsigned int duplex:1, newspeed:1; u_long bd_flags; /* board-specific flags */ struct ess_chinfo pch, rch; }; @@ -563,7 +564,7 @@ ch->parent = sc; ch->channel = c; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsize) != 0) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsize) != 0) return NULL; ch->dir = dir; ch->hwch = 1; @@ -610,7 +611,7 @@ { struct ess_chinfo *ch = data; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; switch (go) { @@ -808,10 +809,7 @@ char status[SND_STATUSLEN], buf[64]; int ver; - sc = (struct ess_info *)malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO); - if (!sc) - return ENXIO; - + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->parent_dev = device_get_parent(dev); sc->bufsize = pcm_getbuffersize(dev, 4096, ESS_BUFFSIZE, 65536); if (ess_alloc_resources(sc, dev)) @@ -851,7 +849,8 @@ if (!sc->duplex) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/isa/gusc.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/gusc.c Thu Jul 12 12:04:19 2007 @@ -41,11 +41,8 @@ #include #include -#ifdef __alpha__ /* XXX workaround a stupid warning */ -#include -#endif -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/gusc.c,v 1.16 2005/01/06 01:43:17 imp Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/gusc.c,v 1.19 2007/02/23 19:40:13 ariff Exp $"); #define LOGICALID_NOPNP 0 #define LOGICALID_PCM 0x0000561e @@ -319,7 +316,7 @@ } if (scp->irq != NULL) - bus_setup_intr(dev, scp->irq, INTR_TYPE_AV, gusc_intr, scp, &ih); + snd_setup_intr(dev, scp->irq, 0, gusc_intr, scp, &ih); bus_generic_attach(dev); return (0); @@ -421,12 +418,21 @@ } static int -gusc_setup_intr(device_t dev, device_t child, struct resource *irq, - int flags, driver_intr_t *intr, void *arg, void **cookiep) +gusc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, +#if __FreeBSD_version >= 700031 + driver_filter_t *filter, +#endif + driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp = (sc_p)device_get_softc(dev); devclass_t devclass; +#if __FreeBSD_version >= 700031 + if (filter != NULL) { + printf("gusc.c: we cannot use a filter here\n"); + return (EINVAL); + } +#endif devclass = device_get_devclass(child); if (strcmp(devclass_get_name(devclass), "midi") == 0) { scp->midi_intr.intr = intr; @@ -437,8 +443,11 @@ scp->pcm_intr.arg = arg; return 0; } - return bus_generic_setup_intr(dev, child, irq, flags, intr, - arg, cookiep); + return bus_generic_setup_intr(dev, child, irq, flags, +#if __FreeBSD_version >= 700031 + filter, +#endif + intr, arg, cookiep); } static device_t --- sys/dev/sound/isa/mss.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/mss.c Thu Jul 12 12:04:19 2007 @@ -1,8 +1,8 @@ /*- * Copyright (c) 2001 George Reid * Copyright (c) 1999 Cameron Grant - * Copyright Luigi Rizzo, 1997,1998 - * Copyright by Hannu Savolainen 1994, 1995 + * Copyright (c) 1997,1998 Luigi Rizzo + * Copyright (c) 1994,1995 Hannu Savolainen * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/mss.c,v 1.95.2.3 2006/04/04 17:30:59 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/mss.c,v 1.112 2007/06/17 06:10:40 ariff Exp $"); /* board-specific include files */ #include @@ -1152,7 +1152,7 @@ ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, mss->parent_dmat, mss->bufsize) != 0) + if (sndbuf_alloc(ch->buffer, mss->parent_dmat, 0, mss->bufsize) != 0) return NULL; sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? mss->drq1 : mss->drq2); return ch; @@ -1201,7 +1201,7 @@ struct mss_chinfo *ch = data; struct mss_info *mss = ch->parent; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; sndbuf_dma(ch->buffer, go); @@ -1320,7 +1320,7 @@ goto mss_probe_end; } tmp &= 0x3f; - if (!(tmp == 0x04 || tmp == 0x0f || tmp == 0x00)) { + if (!(tmp == 0x04 || tmp == 0x0f || tmp == 0x00 || tmp == 0x05)) { BVDDB(printf("No MSS signature detected on port 0x%lx (0x%x)\n", rman_get_start(mss->io_base), tmpx)); goto no; @@ -1696,7 +1696,7 @@ int pdma, rdma, flags = device_get_flags(dev); char status[SND_STATUSLEN], status2[SND_STATUSLEN]; - mss->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + mss->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_mss softc"); mss->bufsize = pcm_getbuffersize(dev, 4096, MSS_DEFAULT_BUFSZ, 65536); if (!mss_alloc_resources(mss, dev)) goto no; mss_init(mss, dev); @@ -1744,7 +1744,8 @@ } if (pdma == rdma) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -1978,10 +1979,7 @@ { struct mss_info *mss; - mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT | M_ZERO); - if (!mss) - return ENXIO; - + mss = malloc(sizeof(*mss), M_DEVBUF, M_WAITOK | M_ZERO); mss->io_rid = 0; mss->conf_rid = -1; mss->irq_rid = 0; --- sys/dev/sound/isa/mss.h.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/mss.h Thu Jul 12 12:04:19 2007 @@ -1,15 +1,6 @@ /*- - * file: mss.h - * - * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) - * - * This file contains information and macro definitions for - * AD1848-compatible devices, used in the MSS/WSS compatible boards. - * - */ - -/*- * Copyright (c) 1999 Doug Rabson + * Copyright (c) 1997 Luigi Rizzo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,7 +24,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/mss.h,v 1.12 2005/01/06 01:43:17 imp Exp $ + * $FreeBSD: src/sys/dev/sound/isa/mss.h,v 1.14 2007/02/02 13:44:09 joel Exp $ + */ + +/* + * This file contains information and macro definitions for + * AD1848-compatible devices, used in the MSS/WSS compatible boards. */ /* --- sys/dev/sound/isa/sb.h.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/sb.h Thu Jul 12 12:04:19 2007 @@ -1,6 +1,29 @@ -/* - * file: sbcard.h - * $FreeBSD: src/sys/dev/sound/isa/sb.h,v 1.15 2004/05/13 11:32:54 truckman Exp $ +/*- + * Copyright (c) 1997,1998 Luigi Rizzo + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/isa/sb.h,v 1.16 2007/02/02 13:33:35 joel Exp $ */ #ifndef SB_H --- sys/dev/sound/isa/sb16.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/sb16.c Thu Jul 12 12:04:19 2007 @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant - * Copyright 1997,1998 Luigi Rizzo. + * Copyright (c) 1997,1998 Luigi Rizzo * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -38,7 +38,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sb16.c,v 1.90.2.1 2005/12/30 19:55:53 netchild Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sb16.c,v 1.97 2007/06/17 06:10:40 ariff Exp $"); #define SB16_BUFFSIZE 4096 #define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) @@ -681,7 +681,7 @@ ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) != 0) + if (sndbuf_alloc(ch->buffer, sb->parent_dmat, 0, sb->bufsize) != 0) return NULL; return ch; @@ -724,7 +724,7 @@ struct sb_chinfo *ch = data; struct sb_info *sb = ch->parent; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; if (go == PCMTRIG_START) @@ -813,10 +813,7 @@ uintptr_t ver; char status[SND_STATUSLEN], status2[SND_STATUSLEN]; - sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT | M_ZERO); - if (!sb) - return ENXIO; - + sb = malloc(sizeof(*sb), M_DEVBUF, M_WAITOK | M_ZERO); sb->parent_dev = device_get_parent(dev); BUS_READ_IVAR(sb->parent_dev, dev, 1, &ver); sb->bd_id = ver & 0x0000ffff; @@ -841,7 +838,8 @@ sb->prio = 0; - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/isa/sb8.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/sb8.c Thu Jul 12 12:04:19 2007 @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant - * Copyright 1997,1998 Luigi Rizzo. + * Copyright (c) 1997,1998 Luigi Rizzo * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -38,7 +38,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sb8.c,v 1.79.2.1 2005/12/30 19:55:53 netchild Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sb8.c,v 1.86 2007/06/17 06:10:41 ariff Exp $"); #define SB_DEFAULT_BUFSZ 4096 @@ -599,7 +599,7 @@ ch->channel = c; ch->dir = dir; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) != 0) + if (sndbuf_alloc(ch->buffer, sb->parent_dmat, 0, sb->bufsize) != 0) return NULL; sndbuf_dmasetup(ch->buffer, sb->drq); return ch; @@ -637,7 +637,7 @@ { struct sb_chinfo *ch = data; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; sndbuf_dma(ch->buffer, go); @@ -714,10 +714,7 @@ char status[SND_STATUSLEN]; uintptr_t ver; - sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT | M_ZERO); - if (!sb) - return ENXIO; - + sb = malloc(sizeof(*sb), M_DEVBUF, M_WAITOK | M_ZERO); sb->parent_dev = device_get_parent(dev); BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); sb->bd_id = ver & 0x0000ffff; @@ -735,7 +732,8 @@ pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/isa/sbc.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/isa/sbc.c Thu Jul 12 12:04:19 2007 @@ -30,7 +30,7 @@ #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sbc.c,v 1.44.2.1 2005/12/30 19:55:53 netchild Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/isa/sbc.c,v 1.48 2007/03/15 16:41:25 ariff Exp $"); #define IO_MAX 3 #define IRQ_MAX 1 @@ -80,8 +80,12 @@ static int sbc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, - int flags, driver_intr_t *intr, void *arg, - void **cookiep); + int flags, +#if __FreeBSD_version >= 700031 + driver_filter_t *filter, +#endif + driver_intr_t *intr, + void *arg, void **cookiep); static int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); @@ -116,7 +120,8 @@ static void sbc_lockinit(struct sbc_softc *scp) { - scp->lock = snd_mtxcreate(device_get_nameunit(scp->dev), "sound softc"); + scp->lock = snd_mtxcreate(device_get_nameunit(scp->dev), + "snd_sbc softc"); } static void @@ -502,14 +507,23 @@ } static int -sbc_setup_intr(device_t dev, device_t child, struct resource *irq, - int flags, driver_intr_t *intr, void *arg, - void **cookiep) +sbc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, +#if __FreeBSD_version >= 700031 + driver_filter_t *filter, +#endif + driver_intr_t *intr, + void *arg, void **cookiep) { struct sbc_softc *scp = device_get_softc(dev); struct sbc_ihl *ihl = NULL; int i, ret; +#if __FreeBSD_version >= 700031 + if (filter != NULL) { + printf("sbc.c: we cannot use a filter here\n"); + return (EINVAL); + } +#endif sbc_lock(scp); i = 0; while (i < IRQ_MAX) { --- sys/dev/sound/lpmap.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/lpmap.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,307 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + * + * "Little-pmap": Lost Technology for RELENG_5/RELENG_6. + * + */ + +#ifndef _LPMAP_C_ +#define _LPMAP_C_ + +#if !(defined(__i386__) || defined(__amd64__)) +#error "Not applicable for non-i386/non-amd64" +#endif + +/* For cpu_feature, VM_MAXUSER_ADDRESS */ +#include +#include +#include + +#ifndef PG_PTE_PAT +#define PG_PTE_PAT 0x0080 +#endif + +#ifndef PG_PDE_PAT +#define PG_PDE_PAT 0x1000 +#endif + +#ifndef PAT_UNCACHEABLE +#define PAT_UNCACHEABLE 0x00 +#endif + +#ifndef PAT_WRITE_COMBINING +#define PAT_WRITE_COMBINING 0x01 +#endif + +#ifndef PAT_WRITE_THROUGH +#define PAT_WRITE_THROUGH 0x04 +#endif + +#ifndef PAT_WRITE_PROTECTED +#define PAT_WRITE_PROTECTED 0x05 +#endif + +#ifndef PAT_WRITE_BACK +#define PAT_WRITE_BACK 0x06 +#endif + +#ifndef PAT_UNCACHED +#define PAT_UNCACHED 0x07 +#endif + +#define lpmap_p(x, y...) /*printf("%s(): "x, __func__, y)*/ + +#undef pmap_change_attr +#define pmap_change_attr lpmap_change_attr + +#if defined(__i386__) +#define lpmap_pde(m, v) (&((m)->pm_pdir[(vm_offset_t)(v) >> PDRSHIFT])) +#elif defined(__amd64__) +static __inline vm_pindex_t +lpmap_pde_index(vm_offset_t va) +{ + return ((va >> PDRSHIFT) & ((1UL << NPDEPGSHIFT) - 1)); +} + +static __inline vm_pindex_t +lpmap_pdpe_index(vm_offset_t va) +{ + return ((va >> PDPSHIFT) & ((1UL << NPDPEPGSHIFT) - 1)); +} + +static __inline vm_pindex_t +lpmap_pml4e_index(vm_offset_t va) +{ + return ((va >> PML4SHIFT) & ((1UL << NPML4EPGSHIFT) - 1)); +} + +static __inline pd_entry_t * +lpmap_pdpe_to_pde(pdp_entry_t *pdpe, vm_offset_t va) +{ + pd_entry_t *pde; + + pde = (pd_entry_t *)PHYS_TO_DMAP(*pdpe & PG_FRAME); + return (&pde[lpmap_pde_index(va)]); +} + +static __inline pml4_entry_t * +lpmap_pml4e(pmap_t pmap, vm_offset_t va) +{ + if (pmap == NULL) + return (NULL); + return (&pmap->pm_pml4[lpmap_pml4e_index(va)]); +} + +static __inline pdp_entry_t * +lpmap_pml4e_to_pdpe(pml4_entry_t *pml4e, vm_offset_t va) +{ + pdp_entry_t *pdpe; + + pdpe = (pdp_entry_t *)PHYS_TO_DMAP(*pml4e & PG_FRAME); + return (&pdpe[lpmap_pdpe_index(va)]); +} + +static __inline pdp_entry_t * +lpmap_pdpe(pmap_t pmap, vm_offset_t va) +{ + pml4_entry_t *pml4e; + + pml4e = lpmap_pml4e(pmap, va); + if (pml4e == NULL || (*pml4e & PG_V) == 0) + return (NULL); + return (lpmap_pml4e_to_pdpe(pml4e, va)); +} + +static __inline pd_entry_t * +lpmap_pde(pmap_t pmap, vm_offset_t va) +{ + pdp_entry_t *pdpe; + + pdpe = lpmap_pdpe(pmap, va); + if (pdpe == NULL || (*pdpe & PG_V) == 0) + return (NULL); + return (lpmap_pdpe_to_pde(pdpe, va)); +} +#endif + +static __inline int +lpmap_validate_range(vm_offset_t base, vm_size_t size) +{ + vm_offset_t va; + pt_entry_t *pte; + pd_entry_t *pde; + + if (base <= VM_MAXUSER_ADDRESS) { + lpmap_p("base <= VM_MAXUSER_ADDRESS : base=0x%jx\n", + (uintmax_t)base); + return (EINVAL); + } + + va = base; + while (va < (base + size)) { + pde = lpmap_pde(kernel_pmap, va); + if (pde == NULL || *pde == 0) { + lpmap_p("Failed: %s : va=0x%jx\n", + (pde == NULL) ? "pde == NULL" : "*pde == 0", + (uintmax_t)va); + return (EINVAL); + } + if (*pde & PG_PS) { +#if defined(__amd64__) + if (size < NBPDR) { + lpmap_p("Failed: size < NBPDR : va=0x%jx\n", + (uintmax_t)va); + return (EINVAL); + } + va += NBPDR; +#else + lpmap_p("Failed: (*pde & PG_PS) != 0 : va=0x%jx\n", + (uintmax_t)va); + return (EINVAL); +#endif + } else { + pte = vtopte(va); + if (pte == NULL || *pte == 0) { + lpmap_p("Failed: %s : va=0x%jx\n", + (pte == NULL) ? "pte == NULL" : "*pte == 0", + (uintmax_t)va); + return (EINVAL); + } + va += PAGE_SIZE; + } + } + + return (0); +} + +extern int osreldate; + +static int +lpmap_change_attr(vm_offset_t va, vm_size_t size, int mode) +{ + vm_offset_t base, offset; + pt_entry_t *pte; +#if defined(__amd64__) + pd_entry_t *pde; +#endif + u_int opxe, npxe, ptefl, pdefl, attr; + int err, flushtlb; + + switch (mode) { + case PAT_UNCACHED: + case PAT_WRITE_COMBINING: + case PAT_WRITE_PROTECTED: + if (!(cpu_feature & CPUID_PAT)) + mode = PAT_UNCACHEABLE; + break; + default: + break; + } + + attr = 0; + + switch (mode) { + case PAT_UNCACHED: + case PAT_UNCACHEABLE: + case PAT_WRITE_PROTECTED: + attr |= PG_NC_PCD; + case PAT_WRITE_THROUGH: + attr |= PG_NC_PWT; + break; + case PAT_WRITE_COMBINING: + attr |= PG_NC_PCD; + break; + case PAT_WRITE_BACK: + break; + default: + lpmap_p("Unsupported mode=0x%08x\n", mode); + return (EINVAL); + break; + } + + ptefl = PG_NC_PCD | PG_NC_PWT; + pdefl = PG_NC_PCD | PG_NC_PWT; + if (osreldate >= 602110) { + ptefl |= PG_PTE_PAT; + pdefl |= PG_PDE_PAT; + } + + base = va & PG_FRAME; + offset = va & PAGE_MASK; + size = roundup(offset + size, PAGE_SIZE); + + err = lpmap_validate_range(base, size); + if (err != 0) { + lpmap_p("Validation failed! " + "vm_offset_t=0x%jx vm_size_t=%ju attr=0x%04x\n", + (uintmax_t)va, (uintmax_t)size, attr); + return (err); + } + + lpmap_p("vm_offset_t=0x%jx vm_size_t=%ju attr=0x%04x\n", + (uintmax_t)va, (uintmax_t)size, attr); + + flushtlb = 0; + + while (size > 0) { +#if defined(__amd64__) + pde = lpmap_pde(kernel_pmap, base); + if (*pde & PG_PS) { + do { + opxe = *(u_int *)pde; + npxe = opxe & ~pdefl; + npxe |= attr; + } while (npxe != opxe && ++flushtlb && + !atomic_cmpset_int((u_int *)pde, opxe, npxe)); + size -= NBPDR; + base += NBPDR; + } else { +#endif + pte = vtopte(base); + do { + opxe = *(u_int *)pte; + npxe = opxe & ~ptefl; + npxe |= attr; + } while (npxe != opxe && ++flushtlb && + !atomic_cmpset_int((u_int *)pte, opxe, npxe)); + size -= PAGE_SIZE; + base += PAGE_SIZE; +#if defined(__amd64__) + } +#endif + } + + /* XXX Gross!! */ + if (flushtlb != 0) { + lpmap_p("flushtlb=%d\n", flushtlb); + pmap_invalidate_all(kernel_pmap); + } + + return (0); +} + +#endif /* !_LPMAP_C_ */ --- sys/dev/sound/midi/midi.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/midi.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,1528 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@netbsd.org). + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + + /* + * Parts of this file started out as NetBSD: midi.c 1.31 + * They are mostly gone. Still the most obvious will be the state + * machine midi_in + */ + +#include +__FBSDID("$FreeBSD: src/sys/dev/sound/midi/midi.c,v 1.24 2007/04/02 06:03:47 ariff Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mpu_if.h" + +#include +#include "synth_if.h" +MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area"); + + +#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) +#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c) + +#define MIDI_DEV_RAW 2 +#define MIDI_DEV_MIDICTL 12 + +enum midi_states { + MIDI_IN_START, MIDI_IN_SYSEX, MIDI_IN_DATA +}; + +/* + * The MPU interface current has init() uninit() inqsize(( outqsize() + * callback() : fiddle with the tx|rx status. + */ + +#include "mpu_if.h" + +/* + * /dev/rmidi Structure definitions + */ + +#define MIDI_NAMELEN 16 +struct snd_midi { + KOBJ_FIELDS; + struct mtx lock; /* Protects all but queues */ + void *cookie; + + int unit; /* Should only be used in midistat */ + int channel; /* Should only be used in midistat */ + + int busy; + int flags; /* File flags */ + char name[MIDI_NAMELEN]; + struct mtx qlock; /* Protects inq, outq and flags */ + MIDIQ_HEAD(, char) inq, outq; + int rchan, wchan; + struct selinfo rsel, wsel; + int hiwat; /* QLEN(outq)>High-water -> disable + * writes from userland */ + enum midi_states inq_state; + int inq_status, inq_left; /* Variables for the state machine in + * Midi_in, this is to provide that + * signals only get issued only + * complete command packets. */ + struct proc *async; + struct cdev *dev; + struct synth_midi *synth; + int synth_flags; + TAILQ_ENTRY(snd_midi) link; +}; + +struct synth_midi { + KOBJ_FIELDS; + struct snd_midi *m; +}; + +static synth_open_t midisynth_open; +static synth_close_t midisynth_close; +static synth_writeraw_t midisynth_writeraw; +static synth_killnote_t midisynth_killnote; +static synth_startnote_t midisynth_startnote; +static synth_setinstr_t midisynth_setinstr; +static synth_alloc_t midisynth_alloc; +static synth_controller_t midisynth_controller; +static synth_bender_t midisynth_bender; + + +static kobj_method_t midisynth_methods[] = { + KOBJMETHOD(synth_open, midisynth_open), + KOBJMETHOD(synth_close, midisynth_close), + KOBJMETHOD(synth_writeraw, midisynth_writeraw), + KOBJMETHOD(synth_setinstr, midisynth_setinstr), + KOBJMETHOD(synth_startnote, midisynth_startnote), + KOBJMETHOD(synth_killnote, midisynth_killnote), + KOBJMETHOD(synth_alloc, midisynth_alloc), + KOBJMETHOD(synth_controller, midisynth_controller), + KOBJMETHOD(synth_bender, midisynth_bender), + {0, 0} +}; + +DEFINE_CLASS(midisynth, midisynth_methods, 0); + +/* + * Module Exports & Interface + * + * struct midi_chan *midi_init(MPU_CLASS cls, int unit, int chan) int + * midi_uninit(struct snd_midi *) 0 == no error EBUSY or other error int + * Midi_in(struct midi_chan *, char *buf, int count) int Midi_out(struct + * midi_chan *, char *buf, int count) + * + * midi_{in,out} return actual size transfered + * + */ + + +/* + * midi_devs tailq, holder of all rmidi instances protected by midistat_lock + */ + +TAILQ_HEAD(, snd_midi) midi_devs; + +/* + * /dev/midistat variables and declarations, protected by midistat_lock + */ + +static struct mtx midistat_lock; +static int midistat_isopen = 0; +static struct sbuf midistat_sbuf; +static int midistat_bufptr; +static struct cdev *midistat_dev; + +/* + * /dev/midistat dev_t declarations + */ + +static d_open_t midistat_open; +static d_close_t midistat_close; +static d_read_t midistat_read; + +static struct cdevsw midistat_cdevsw = { + .d_version = D_VERSION, + .d_open = midistat_open, + .d_close = midistat_close, + .d_read = midistat_read, + .d_name = "midistat", +}; + + +/* + * /dev/rmidi dev_t declarations, struct variable access is protected by + * locks contained within the structure. + */ + +static d_open_t midi_open; +static d_close_t midi_close; +static d_ioctl_t midi_ioctl; +static d_read_t midi_read; +static d_write_t midi_write; +static d_poll_t midi_poll; + +static struct cdevsw midi_cdevsw = { + .d_version = D_VERSION, + .d_open = midi_open, + .d_close = midi_close, + .d_read = midi_read, + .d_write = midi_write, + .d_ioctl = midi_ioctl, + .d_poll = midi_poll, + .d_name = "rmidi", +}; + +/* + * Prototypes of library functions + */ + +static int midi_destroy(struct snd_midi *, int); +static int midistat_prepare(struct sbuf * s); +static int midi_load(void); +static int midi_unload(void); + +/* + * Misc declr. + */ +SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD, 0, "Midi driver"); +SYSCTL_NODE(_hw_midi, OID_AUTO, stat, CTLFLAG_RD, 0, "Status device"); + +int midi_debug; +/* XXX: should this be moved into debug.midi? */ +SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, ""); + +int midi_dumpraw; +SYSCTL_INT(_hw_midi, OID_AUTO, dumpraw, CTLFLAG_RW, &midi_dumpraw, 0, ""); + +int midi_instroff; +SYSCTL_INT(_hw_midi, OID_AUTO, instroff, CTLFLAG_RW, &midi_instroff, 0, ""); + +int midistat_verbose; +SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW, + &midistat_verbose, 0, ""); + +#define MIDI_DEBUG(l,a) if(midi_debug>=l) a +/* + * CODE START + */ + +/* + * Register a new rmidi device. cls midi_if interface unit == 0 means + * auto-assign new unit number unit != 0 already assigned a unit number, eg. + * not the first channel provided by this device. channel, sub-unit + * cookie is passed back on MPU calls Typical device drivers will call with + * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care + * what unit number is used. + * + * It is an error to call midi_init with an already used unit/channel combo. + * + * Returns NULL on error + * + */ +struct snd_midi * +midi_init(kobj_class_t cls, int unit, int channel, void *cookie) +{ + struct snd_midi *m; + int i; + int inqsize, outqsize; + MIDI_TYPE *buf; + + MIDI_DEBUG(1, printf("midiinit: unit %d/%d.\n", unit, channel)); + mtx_lock(&midistat_lock); + /* + * Protect against call with existing unit/channel or auto-allocate a + * new unit number. + */ + i = -1; + TAILQ_FOREACH(m, &midi_devs, link) { + mtx_lock(&m->lock); + if (unit != 0) { + if (m->unit == unit && m->channel == channel) { + mtx_unlock(&m->lock); + goto err0; + } + } else { + /* + * Find a better unit number + */ + if (m->unit > i) + i = m->unit; + } + mtx_unlock(&m->lock); + } + + if (unit == 0) + unit = i + 1; + + MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel)); + m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO); + if (m == NULL) + goto err0; + + m->synth = malloc(sizeof(*m->synth), M_MIDI, M_NOWAIT | M_ZERO); + kobj_init((kobj_t)m->synth, &midisynth_class); + m->synth->m = m; + kobj_init((kobj_t)m, cls); + inqsize = MPU_INQSIZE(m, cookie); + outqsize = MPU_OUTQSIZE(m, cookie); + + MIDI_DEBUG(1, printf("midiinit queues %d/%d.\n", inqsize, outqsize)); + if (!inqsize && !outqsize) + goto err1; + + mtx_init(&m->lock, "raw midi", NULL, 0); + mtx_init(&m->qlock, "q raw midi", NULL, 0); + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (inqsize) + buf = malloc(sizeof(MIDI_TYPE) * inqsize, M_MIDI, M_NOWAIT); + else + buf = NULL; + + MIDIQ_INIT(m->inq, buf, inqsize); + + if (outqsize) + buf = malloc(sizeof(MIDI_TYPE) * outqsize, M_MIDI, M_NOWAIT); + else + buf = NULL; + m->hiwat = outqsize / 2; + + MIDIQ_INIT(m->outq, buf, outqsize); + + if ((inqsize && !MIDIQ_BUF(m->inq)) || + (outqsize && !MIDIQ_BUF(m->outq))) + goto err2; + + + m->busy = 0; + m->flags = 0; + m->unit = unit; + m->channel = channel; + m->cookie = cookie; + + if (MPU_INIT(m, cookie)) + goto err2; + + mtx_unlock(&m->lock); + mtx_unlock(&m->qlock); + + TAILQ_INSERT_TAIL(&midi_devs, m, link); + + mtx_unlock(&midistat_lock); + + m->dev = make_dev(&midi_cdevsw, + MIDIMKMINOR(unit, MIDI_DEV_RAW, channel), + UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel); + m->dev->si_drv1 = m; + + return m; + +err2: mtx_destroy(&m->qlock); + mtx_destroy(&m->lock); + + if (MIDIQ_BUF(m->inq)) + free(MIDIQ_BUF(m->inq), M_MIDI); + if (MIDIQ_BUF(m->outq)) + free(MIDIQ_BUF(m->outq), M_MIDI); +err1: free(m, M_MIDI); +err0: mtx_unlock(&midistat_lock); + MIDI_DEBUG(1, printf("midi_init ended in error\n")); + return NULL; +} + +/* + * midi_uninit does not call MIDI_UNINIT, as since this is the implementors + * entry point. midi_unint if fact, does not send any methods. A call to + * midi_uninit is a defacto promise that you won't manipulate ch anymore + * + */ + +int +midi_uninit(struct snd_midi *m) +{ + int err; + + err = ENXIO; + mtx_lock(&midistat_lock); + mtx_lock(&m->lock); + if (m->busy) { + if (!(m->rchan || m->wchan)) + goto err; + + if (m->rchan) { + wakeup(&m->rchan); + m->rchan = 0; + } + if (m->wchan) { + wakeup(&m->wchan); + m->wchan = 0; + } + } + err = midi_destroy(m, 0); + if (!err) + goto exit; + +err: mtx_unlock(&m->lock); +exit: mtx_unlock(&midistat_lock); + return err; +} + +/* + * midi_in: process all data until the queue is full, then discards the rest. + * Since midi_in is a state machine, data discards can cause it to get out of + * whack. Process as much as possible. It calls, wakeup, selnotify and + * psignal at most once. + */ + +#ifdef notdef +static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0}; + +#endif /* notdef */ +/* Number of bytes in a MIDI command */ +#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7]) +#define MIDI_ACK 0xfe +#define MIDI_IS_STATUS(d) ((d) >= 0x80) +#define MIDI_IS_COMMON(d) ((d) >= 0xf0) + +#define MIDI_SYSEX_START 0xF0 +#define MIDI_SYSEX_END 0xF7 + + +int +midi_in(struct snd_midi *m, MIDI_TYPE *buf, int size) +{ + /* int i, sig, enq; */ + int used; + + /* MIDI_TYPE data; */ + MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size)); + +/* + * XXX: locking flub + */ + if (!(m->flags & M_RX)) + return size; + + used = 0; + + mtx_lock(&m->qlock); +#if 0 + /* + * Don't bother queuing if not in read mode. Discard everything and + * return size so the caller doesn't freak out. + */ + + if (!(m->flags & M_RX)) + return size; + + for (i = sig = 0; i < size; i++) { + + data = buf[i]; + enq = 0; + if (data == MIDI_ACK) + continue; + + switch (m->inq_state) { + case MIDI_IN_START: + if (MIDI_IS_STATUS(data)) { + switch (data) { + case 0xf0: /* Sysex */ + m->inq_state = MIDI_IN_SYSEX; + break; + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* Song select */ + m->inq_state = MIDI_IN_DATA; + enq = 1; + m->inq_left = 1; + break; + case 0xf2: /* Song position pointer */ + m->inq_state = MIDI_IN_DATA; + enq = 1; + m->inq_left = 2; + break; + default: + if (MIDI_IS_COMMON(data)) { + enq = 1; + sig = 1; + } else { + m->inq_state = MIDI_IN_DATA; + enq = 1; + m->inq_status = data; + m->inq_left = MIDI_LENGTH(data); + } + break; + } + } else if (MIDI_IS_STATUS(m->inq_status)) { + m->inq_state = MIDI_IN_DATA; + if (!MIDIQ_FULL(m->inq)) { + used++; + MIDIQ_ENQ(m->inq, &m->inq_status, 1); + } + enq = 1; + m->inq_left = MIDI_LENGTH(m->inq_status) - 1; + } + break; + /* + * End of case MIDI_IN_START: + */ + + case MIDI_IN_DATA: + enq = 1; + if (--m->inq_left <= 0) + sig = 1;/* deliver data */ + break; + case MIDI_IN_SYSEX: + if (data == MIDI_SYSEX_END) + m->inq_state = MIDI_IN_START; + break; + } + + if (enq) + if (!MIDIQ_FULL(m->inq)) { + MIDIQ_ENQ(m->inq, &data, 1); + used++; + } + /* + * End of the state machines main "for loop" + */ + } + if (sig) { +#endif + MIDI_DEBUG(6, printf("midi_in: len %jd avail %jd\n", + (intmax_t)MIDIQ_LEN(m->inq), + (intmax_t)MIDIQ_AVAIL(m->inq))); + if (MIDIQ_AVAIL(m->inq) > size) { + used = size; + MIDIQ_ENQ(m->inq, buf, size); + } else { + MIDI_DEBUG(4, printf("midi_in: Discarding data qu\n")); + mtx_unlock(&m->qlock); + return 0; + } + if (m->rchan) { + wakeup(&m->rchan); + m->rchan = 0; + } + selwakeup(&m->rsel); + if (m->async) { + PROC_LOCK(m->async); + psignal(m->async, SIGIO); + PROC_UNLOCK(m->async); + } +#if 0 + } +#endif + mtx_unlock(&m->qlock); + return used; +} + +/* + * midi_out: The only clearer of the M_TXEN flag. + */ +int +midi_out(struct snd_midi *m, MIDI_TYPE *buf, int size) +{ + int used; + +/* + * XXX: locking flub + */ + if (!(m->flags & M_TXEN)) + return 0; + + MIDI_DEBUG(2, printf("midi_out: %p\n", m)); + mtx_lock(&m->qlock); + used = MIN(size, MIDIQ_LEN(m->outq)); + MIDI_DEBUG(3, printf("midi_out: used %d\n", used)); + if (used) + MIDIQ_DEQ(m->outq, buf, used); + if (MIDIQ_EMPTY(m->outq)) { + m->flags &= ~M_TXEN; + MPU_CALLBACKP(m, m->cookie, m->flags); + } + if (used && MIDIQ_AVAIL(m->outq) > m->hiwat) { + if (m->wchan) { + wakeup(&m->wchan); + m->wchan = 0; + } + selwakeup(&m->wsel); + if (m->async) { + PROC_LOCK(m->async); + psignal(m->async, SIGIO); + PROC_UNLOCK(m->async); + } + } + mtx_unlock(&m->qlock); + return used; +} + + +/* + * /dev/rmidi#.# device access functions + */ +int +midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct snd_midi *m = i_dev->si_drv1; + int retval; + + MIDI_DEBUG(1, printf("midiopen %p %s %s\n", td, + flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : "")); + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + retval = 0; + + if (flags & FREAD) { + if (MIDIQ_SIZE(m->inq) == 0) + retval = ENXIO; + else if (m->flags & M_RX) + retval = EBUSY; + if (retval) + goto err; + } + if (flags & FWRITE) { + if (MIDIQ_SIZE(m->outq) == 0) + retval = ENXIO; + else if (m->flags & M_TX) + retval = EBUSY; + if (retval) + goto err; + } + m->busy++; + + m->rchan = 0; + m->wchan = 0; + m->async = 0; + + if (flags & FREAD) { + m->flags |= M_RX | M_RXEN; + /* + * Only clear the inq, the outq might still have data to drain + * from a previous session + */ + MIDIQ_CLEAR(m->inq); + }; + + if (flags & FWRITE) + m->flags |= M_TX; + + MPU_CALLBACK(m, m->cookie, m->flags); + + MIDI_DEBUG(2, printf("midi_open: opened.\n")); + +err: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + return retval; +} + +int +midi_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct snd_midi *m = i_dev->si_drv1; + int retval; + int oldflags; + + MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td, + flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : "")); + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if ((flags & FREAD && !(m->flags & M_RX)) || + (flags & FWRITE && !(m->flags & M_TX))) { + retval = ENXIO; + goto err; + } + m->busy--; + + oldflags = m->flags; + + if (flags & FREAD) + m->flags &= ~(M_RX | M_RXEN); + if (flags & FWRITE) + m->flags &= ~M_TX; + + if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN))) + MPU_CALLBACK(m, m->cookie, m->flags); + + MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy)); + + mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + retval = 0; +err: return retval; +} + +/* + * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon + * as data is available. + */ +int +midi_read(struct cdev *i_dev, struct uio *uio, int ioflag) +{ +#define MIDI_RSIZE 32 + struct snd_midi *m = i_dev->si_drv1; + int retval; + int used; + char buf[MIDI_RSIZE]; + + MIDI_DEBUG(5, printf("midiread: count=%lu\n", + (unsigned long)uio->uio_resid)); + + retval = EIO; + + if (m == NULL) + goto err0; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (!(m->flags & M_RX)) + goto err1; + + while (uio->uio_resid > 0) { + while (MIDIQ_EMPTY(m->inq)) { + retval = EWOULDBLOCK; + if (ioflag & O_NONBLOCK) + goto err1; + mtx_unlock(&m->lock); + m->rchan = 1; + retval = msleep(&m->rchan, &m->qlock, + PCATCH | PDROP, "midi RX", 0); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; + if (m != i_dev->si_drv1) + retval = ENXIO; + /* if (retval && retval != ERESTART) */ + if (retval) + goto err0; + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + m->rchan = 0; + if (!m->busy) + goto err1; + } + MIDI_DEBUG(6, printf("midi_read start\n")); + /* + * At this point, it is certain that m->inq has data + */ + + used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid); + used = MIN(used, MIDI_RSIZE); + + MIDI_DEBUG(6, printf("midiread: uiomove cc=%d\n", used)); + MIDIQ_DEQ(m->inq, buf, used); + retval = uiomove(buf, used, uio); + if (retval) + goto err1; + } + + /* + * If we Made it here then transfer is good + */ + retval = 0; +err1: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); +err0: MIDI_DEBUG(4, printf("midi_read: ret %d\n", retval)); + return retval; +} + +/* + * midi_write: The only setter of M_TXEN + */ + +int +midi_write(struct cdev *i_dev, struct uio *uio, int ioflag) +{ +#define MIDI_WSIZE 32 + struct snd_midi *m = i_dev->si_drv1; + int retval; + int used; + char buf[MIDI_WSIZE]; + + + MIDI_DEBUG(4, printf("midi_write\n")); + retval = 0; + if (m == NULL) + goto err0; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (!(m->flags & M_TX)) + goto err1; + + while (uio->uio_resid > 0) { + while (MIDIQ_AVAIL(m->outq) == 0) { + retval = EWOULDBLOCK; + if (ioflag & O_NONBLOCK) + goto err1; + mtx_unlock(&m->lock); + m->wchan = 1; + MIDI_DEBUG(3, printf("midi_write msleep\n")); + retval = msleep(&m->wchan, &m->qlock, + PCATCH | PDROP, "midi TX", 0); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; + if (m != i_dev->si_drv1) + retval = ENXIO; + if (retval) + goto err0; + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + m->wchan = 0; + if (!m->busy) + goto err1; + } + + /* + * We are certain than data can be placed on the queue + */ + + used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid); + used = MIN(used, MIDI_WSIZE); + MIDI_DEBUG(5, printf("midiout: resid %d len %jd avail %jd\n", + uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq), + (intmax_t)MIDIQ_AVAIL(m->outq))); + + + MIDI_DEBUG(5, printf("midi_write: uiomove cc=%d\n", used)); + retval = uiomove(buf, used, uio); + if (retval) + goto err1; + MIDIQ_ENQ(m->outq, buf, used); + /* + * Inform the bottom half that data can be written + */ + if (!(m->flags & M_TXEN)) { + m->flags |= M_TXEN; + MPU_CALLBACK(m, m->cookie, m->flags); + } + } + /* + * If we Made it here then transfer is good + */ + retval = 0; +err1: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); +err0: return retval; +} + +int +midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + return ENXIO; +} + +int +midi_poll(struct cdev *i_dev, int events, struct thread *td) +{ + struct snd_midi *m = i_dev->si_drv1; + int revents; + + if (m == NULL) + return 0; + + revents = 0; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (events & (POLLIN | POLLRDNORM)) + if (!MIDIQ_EMPTY(m->inq)) + events |= events & (POLLIN | POLLRDNORM); + + if (events & (POLLOUT | POLLWRNORM)) + if (MIDIQ_AVAIL(m->outq) < m->hiwat) + events |= events & (POLLOUT | POLLWRNORM); + + if (revents == 0) { + if (events & (POLLIN | POLLRDNORM)) + selrecord(td, &m->rsel); + + if (events & (POLLOUT | POLLWRNORM)) + selrecord(td, &m->wsel); + } + mtx_unlock(&m->lock); + mtx_unlock(&m->qlock); + + return (revents); +} + +/* + * /dev/midistat device functions + * + */ +static int +midistat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + int error; + + MIDI_DEBUG(1, printf("midistat_open\n")); + mtx_lock(&midistat_lock); + + if (midistat_isopen) { + mtx_unlock(&midistat_lock); + return EBUSY; + } + midistat_isopen = 1; + mtx_unlock(&midistat_lock); + + if (sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { + error = ENXIO; + mtx_lock(&midistat_lock); + goto out; + } + mtx_lock(&midistat_lock); + midistat_bufptr = 0; + error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM; + +out: if (error) + midistat_isopen = 0; + mtx_unlock(&midistat_lock); + return error; +} + +static int +midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + MIDI_DEBUG(1, printf("midistat_close\n")); + mtx_lock(&midistat_lock); + if (!midistat_isopen) { + mtx_unlock(&midistat_lock); + return EBADF; + } + sbuf_delete(&midistat_sbuf); + midistat_isopen = 0; + + mtx_unlock(&midistat_lock); + return 0; +} + +static int +midistat_read(struct cdev *i_dev, struct uio *buf, int flag) +{ + int l, err; + + MIDI_DEBUG(4, printf("midistat_read\n")); + mtx_lock(&midistat_lock); + if (!midistat_isopen) { + mtx_unlock(&midistat_lock); + return EBADF; + } + l = min(buf->uio_resid, sbuf_len(&midistat_sbuf) - midistat_bufptr); + err = 0; + if (l > 0) { + mtx_unlock(&midistat_lock); + err = uiomove(sbuf_data(&midistat_sbuf) + midistat_bufptr, l, + buf); + mtx_lock(&midistat_lock); + } else + l = 0; + midistat_bufptr += l; + mtx_unlock(&midistat_lock); + return err; +} + +/* + * Module library functions + */ + +static int +midistat_prepare(struct sbuf *s) +{ + struct snd_midi *m; + + mtx_assert(&midistat_lock, MA_OWNED); + + sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n"); + if (TAILQ_EMPTY(&midi_devs)) { + sbuf_printf(s, "No devices installed.\n"); + sbuf_finish(s); + return sbuf_len(s); + } + sbuf_printf(s, "Installed devices:\n"); + + TAILQ_FOREACH(m, &midi_devs, link) { + mtx_lock(&m->lock); + sbuf_printf(s, "%s [%d/%d:%s]", m->name, m->unit, m->channel, + MPU_PROVIDER(m, m->cookie)); + sbuf_printf(s, "%s", MPU_DESCR(m, m->cookie, midistat_verbose)); + sbuf_printf(s, "\n"); + mtx_unlock(&m->lock); + } + + sbuf_finish(s); + return sbuf_len(s); +} + +#ifdef notdef +/* + * Convert IOCTL command to string for debugging + */ + +static char * +midi_cmdname(int cmd) +{ + static struct { + int cmd; + char *name; + } *tab, cmdtab_midiioctl[] = { +#define A(x) {x, ## x} + /* + * Once we have some real IOCTLs define, the following will + * be relavant. + * + * A(SNDCTL_MIDI_PRETIME), A(SNDCTL_MIDI_MPUMODE), + * A(SNDCTL_MIDI_MPUCMD), A(SNDCTL_SYNTH_INFO), + * A(SNDCTL_MIDI_INFO), A(SNDCTL_SYNTH_MEMAVL), + * A(SNDCTL_FM_LOAD_INSTR), A(SNDCTL_FM_4OP_ENABLE), + * A(MIOSPASSTHRU), A(MIOGPASSTHRU), A(AIONWRITE), + * A(AIOGSIZE), A(AIOSSIZE), A(AIOGFMT), A(AIOSFMT), + * A(AIOGMIX), A(AIOSMIX), A(AIOSTOP), A(AIOSYNC), + * A(AIOGCAP), + */ +#undef A + { + -1, "unknown" + }, + }; + + for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++); + return tab->name; +} + +#endif /* notdef */ + +/* + * midisynth + */ + + +int +midisynth_open(void *n, void *arg, int flags) +{ + struct snd_midi *m = ((struct synth_midi *)n)->m; + int retval; + + MIDI_DEBUG(1, printf("midisynth_open %s %s\n", + flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : "")); + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + retval = 0; + + if (flags & FREAD) { + if (MIDIQ_SIZE(m->inq) == 0) + retval = ENXIO; + else if (m->flags & M_RX) + retval = EBUSY; + if (retval) + goto err; + } + if (flags & FWRITE) { + if (MIDIQ_SIZE(m->outq) == 0) + retval = ENXIO; + else if (m->flags & M_TX) + retval = EBUSY; + if (retval) + goto err; + } + m->busy++; + + /* + * TODO: Consider m->async = 0; + */ + + if (flags & FREAD) { + m->flags |= M_RX | M_RXEN; + /* + * Only clear the inq, the outq might still have data to drain + * from a previous session + */ + MIDIQ_CLEAR(m->inq); + m->rchan = 0; + }; + + if (flags & FWRITE) { + m->flags |= M_TX; + m->wchan = 0; + } + m->synth_flags = flags & (FREAD | FWRITE); + + MPU_CALLBACK(m, m->cookie, m->flags); + + +err: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + MIDI_DEBUG(2, printf("midisynth_open: return %d.\n", retval)); + return retval; +} + +int +midisynth_close(void *n) +{ + struct snd_midi *m = ((struct synth_midi *)n)->m; + int retval; + int oldflags; + + MIDI_DEBUG(1, printf("midisynth_close %s %s\n", + m->synth_flags & FREAD ? "M_RX" : "", + m->synth_flags & FWRITE ? "M_TX" : "")); + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if ((m->synth_flags & FREAD && !(m->flags & M_RX)) || + (m->synth_flags & FWRITE && !(m->flags & M_TX))) { + retval = ENXIO; + goto err; + } + m->busy--; + + oldflags = m->flags; + + if (m->synth_flags & FREAD) + m->flags &= ~(M_RX | M_RXEN); + if (m->synth_flags & FWRITE) + m->flags &= ~M_TX; + + if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN))) + MPU_CALLBACK(m, m->cookie, m->flags); + + MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy)); + + mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + retval = 0; +err: return retval; +} + +/* + * Always blocking. + */ + +int +midisynth_writeraw(void *n, uint8_t *buf, size_t len) +{ + struct snd_midi *m = ((struct synth_midi *)n)->m; + int retval; + int used; + int i; + + MIDI_DEBUG(4, printf("midisynth_writeraw\n")); + + retval = 0; + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (!(m->flags & M_TX)) + goto err1; + + if (midi_dumpraw) + printf("midi dump: "); + + while (len > 0) { + while (MIDIQ_AVAIL(m->outq) == 0) { + if (!(m->flags & M_TXEN)) { + m->flags |= M_TXEN; + MPU_CALLBACK(m, m->cookie, m->flags); + } + mtx_unlock(&m->lock); + m->wchan = 1; + MIDI_DEBUG(3, printf("midisynth_writeraw msleep\n")); + retval = msleep(&m->wchan, &m->qlock, + PCATCH | PDROP, "midi TX", 0); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; + + if (retval) + goto err0; + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + m->wchan = 0; + if (!m->busy) + goto err1; + } + + /* + * We are certain than data can be placed on the queue + */ + + used = MIN(MIDIQ_AVAIL(m->outq), len); + used = MIN(used, MIDI_WSIZE); + MIDI_DEBUG(5, + printf("midi_synth: resid %zu len %jd avail %jd\n", + len, (intmax_t)MIDIQ_LEN(m->outq), + (intmax_t)MIDIQ_AVAIL(m->outq))); + + if (midi_dumpraw) + for (i = 0; i < used; i++) + printf("%x ", buf[i]); + + MIDIQ_ENQ(m->outq, buf, used); + len -= used; + + /* + * Inform the bottom half that data can be written + */ + if (!(m->flags & M_TXEN)) { + m->flags |= M_TXEN; + MPU_CALLBACK(m, m->cookie, m->flags); + } + } + /* + * If we Made it here then transfer is good + */ + if (midi_dumpraw) + printf("\n"); + + retval = 0; +err1: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); +err0: return retval; +} + +static int +midisynth_killnote(void *n, uint8_t chn, uint8_t note, uint8_t vel) +{ + u_char c[3]; + + + if (note > 127 || chn > 15) + return (EINVAL); + + if (vel > 127) + vel = 127; + + if (vel == 64) { + c[0] = 0x90 | (chn & 0x0f); /* Note on. */ + c[1] = (u_char)note; + c[2] = 0; + } else { + c[0] = 0x80 | (chn & 0x0f); /* Note off. */ + c[1] = (u_char)note; + c[2] = (u_char)vel; + } + + return midisynth_writeraw(n, c, 3); +} + +static int +midisynth_setinstr(void *n, uint8_t chn, uint16_t instr) +{ + u_char c[2]; + + if (instr > 127 || chn > 15) + return EINVAL; + + c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */ + c[1] = instr + midi_instroff; + + return midisynth_writeraw(n, c, 2); +} + +static int +midisynth_startnote(void *n, uint8_t chn, uint8_t note, uint8_t vel) +{ + u_char c[3]; + + if (note > 127 || chn > 15) + return EINVAL; + + if (vel > 127) + vel = 127; + + c[0] = 0x90 | (chn & 0x0f); /* Note on. */ + c[1] = (u_char)note; + c[2] = (u_char)vel; + + return midisynth_writeraw(n, c, 3); +} +static int +midisynth_alloc(void *n, uint8_t chan, uint8_t note) +{ + return chan; +} + +static int +midisynth_controller(void *n, uint8_t chn, uint8_t ctrlnum, uint16_t val) +{ + u_char c[3]; + + if (ctrlnum > 127 || chn > 15) + return EINVAL; + + c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */ + c[1] = ctrlnum; + c[2] = val; + return midisynth_writeraw(n, c, 3); +} + +static int +midisynth_bender(void *n, uint8_t chn, uint16_t val) +{ + u_char c[3]; + + + if (val > 16383 || chn > 15) + return EINVAL; + + c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */ + c[1] = (u_char)val & 0x7f; + c[2] = (u_char)(val >> 7) & 0x7f; + + return midisynth_writeraw(n, c, 3); +} + +/* + * Single point of midi destructions. + */ +static int +midi_destroy(struct snd_midi *m, int midiuninit) +{ + + mtx_assert(&midistat_lock, MA_OWNED); + mtx_assert(&m->lock, MA_OWNED); + + MIDI_DEBUG(3, printf("midi_destroy\n")); + m->dev->si_drv1 = NULL; + destroy_dev(m->dev); + TAILQ_REMOVE(&midi_devs, m, link); + if (midiuninit) + MPU_UNINIT(m, m->cookie); + free(MIDIQ_BUF(m->inq), M_MIDI); + free(MIDIQ_BUF(m->outq), M_MIDI); + mtx_destroy(&m->qlock); + mtx_destroy(&m->lock); + free(m, M_MIDI); + return 0; +} + +/* + * Load and unload functions, creates the /dev/midistat device + */ + +static int +midi_load() +{ + mtx_init(&midistat_lock, "midistat lock", NULL, 0); + TAILQ_INIT(&midi_devs); /* Initialize the queue. */ + + midistat_dev = make_dev(&midistat_cdevsw, + MIDIMKMINOR(0, MIDI_DEV_MIDICTL, 0), + UID_ROOT, GID_WHEEL, 0666, "midistat"); + + return 0; +} + +static int +midi_unload() +{ + struct snd_midi *m; + int retval; + + MIDI_DEBUG(1, printf("midi_unload()\n")); + retval = EBUSY; + mtx_lock(&midistat_lock); + if (midistat_isopen) + goto exit0; + + TAILQ_FOREACH(m, &midi_devs, link) { + mtx_lock(&m->lock); + if (m->busy) + retval = EBUSY; + else + retval = midi_destroy(m, 1); + if (retval) + goto exit1; + } + + destroy_dev(midistat_dev); + /* + * Made it here then unload is complete + */ + mtx_destroy(&midistat_lock); + return 0; + +exit1: + mtx_unlock(&m->lock); +exit0: + mtx_unlock(&midistat_lock); + if (retval) + MIDI_DEBUG(2, printf("midi_unload: failed\n")); + return retval; +} + +extern int seq_modevent(module_t mod, int type, void *data); + +static int +midi_modevent(module_t mod, int type, void *data) +{ + int retval; + + retval = 0; + + switch (type) { + case MOD_LOAD: + retval = midi_load(); +#if 0 + if (retval == 0) + retval = seq_modevent(mod, type, data); +#endif + break; + + case MOD_UNLOAD: + retval = midi_unload(); +#if 0 + if (retval == 0) + retval = seq_modevent(mod, type, data); +#endif + break; + + default: + break; + } + + return retval; +} + +kobj_t +midimapper_addseq(void *arg1, int *unit, void **cookie) +{ + unit = 0; + + return (kobj_t)arg1; +} + +int +midimapper_open(void *arg1, void **cookie) +{ + int retval = 0; + struct snd_midi *m; + + mtx_lock(&midistat_lock); + + TAILQ_FOREACH(m, &midi_devs, link) { + retval++; + } + + mtx_unlock(&midistat_lock); + return retval; +} + +int +midimapper_close(void *arg1, void *cookie) +{ + return 0; +} + +kobj_t +midimapper_fetch_synth(void *arg, void *cookie, int unit) +{ + struct snd_midi *m; + int retval = 0; + + mtx_lock(&midistat_lock); + + TAILQ_FOREACH(m, &midi_devs, link) { + if (unit == retval) { + mtx_unlock(&midistat_lock); + return (kobj_t)m->synth; + } + retval++; + } + + mtx_unlock(&midistat_lock); + return NULL; +} + +DEV_MODULE(midi, midi_modevent, NULL); +MODULE_VERSION(midi, 1); --- sys/dev/sound/midi/midi.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/midi.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/midi/midi.h,v 1.16 2007/02/25 13:51:51 netchild Exp $ + */ + +#ifndef MIDI_H +#define MIDI_H + +#include +#include + +MALLOC_DECLARE(M_MIDI); + +#define M_RX 0x01 +#define M_TX 0x02 +#define M_RXEN 0x04 +#define M_TXEN 0x08 + +#define MIDI_TYPE unsigned char + +struct snd_midi; + +struct snd_midi * +midi_init(kobj_class_t _mpu_cls, int _unit, int _channel, void *cookie); +int midi_uninit(struct snd_midi *_m); +int midi_out(struct snd_midi *_m, MIDI_TYPE *_buf, int _size); +int midi_in(struct snd_midi *_m, MIDI_TYPE *_buf, int _size); + +kobj_t midimapper_addseq(void *arg1, int *unit, void **cookie); +int midimapper_open(void *arg1, void **cookie); +int midimapper_close(void *arg1, void *cookie); +kobj_t midimapper_fetch_synth(void *arg, void *cookie, int unit); + +#endif --- sys/dev/sound/midi/midiq.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/midiq.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/midi/midiq.h,v 1.3 2007/02/25 13:51:51 netchild Exp $ + */ + +#ifndef MIDIQ_H +#define MIDIQ_H + +#define MIDIQ_MOVE(a,b,c) bcopy(b,a,c) + +#define MIDIQ_HEAD(name, type) \ +struct name { \ + int h, t, s; \ + type * b; \ +} + +#define MIDIQ_INIT(head, buf, size) do { \ + (head).h=(head).t=0; \ + (head).s=size; \ + (head).b=buf; \ +} while (0) + +#define MIDIQ_EMPTY(head) ((head).h == (head).t ) + +#define MIDIQ_LENBASE(head) ((head).h - (head).t < 0 ? \ + (head).h - (head).t + (head).s : \ + (head).h - (head).t) + +#define MIDIQ_FULL(head) ((head).h == -1) +#define MIDIQ_AVAIL(head) (MIDIQ_FULL(head) ? 0 : (head).s - MIDIQ_LENBASE(head)) +#define MIDIQ_LEN(head) ((head).s - MIDIQ_AVAIL(head)) +#define MIDIQ_DEBUG 0 +/* + * No protection against overflow, underflow + */ +#define MIDIQ_ENQ(head, buf, size) do { \ + if(MIDIQ_DEBUG)\ + printf("#1 %p %p bytes copied %jd tran req s %d h %d t %d\n", \ + &(head).b[(head).h], (buf), \ + (intmax_t)(sizeof(*(head).b) * \ + MIN( (size), (head).s - (head).h) ), \ + (size), (head).h, (head).t); \ + MIDIQ_MOVE(&(head).b[(head).h], (buf), sizeof(*(head).b) * MIN((size), (head).s - (head).h)); \ + if( (head).s - (head).h < (size) ) { \ + if(MIDIQ_DEBUG) \ + printf("#2 %p %p bytes copied %jd\n", (head).b, (buf) + (head).s - (head).h, (intmax_t)sizeof(*(head).b) * ((size) - (head).s + (head).h) ); \ + MIDIQ_MOVE((head).b, (buf) + (head).s - (head).h, sizeof(*(head).b) * ((size) - (head).s + (head).h) ); \ + } \ + (head).h+=(size); \ + (head).h%=(head).s; \ + if(MIDIQ_EMPTY(head)) (head).h=-1; \ + if(MIDIQ_DEBUG)\ + printf("#E h %d t %d\n", (head).h, (head).t); \ +} while (0) + +#define MIDIQ_DEQ_I(head, buf, size, move, update) do { \ + if(MIDIQ_FULL(head)) (head).h=(head).t; \ + if(MIDIQ_DEBUG)\ + printf("#1 %p %p bytes copied %jd tran req s %d h %d t %d\n", &(head).b[(head).t], (buf), (intmax_t)sizeof(*(head).b) * MIN((size), (head).s - (head).t), (size), (head).h, (head).t); \ + if (move) MIDIQ_MOVE((buf), &(head).b[(head).t], sizeof(*(head).b) * MIN((size), (head).s - (head).t)); \ + if( (head).s - (head).t < (size) ) { \ + if(MIDIQ_DEBUG) \ + printf("#2 %p %p bytes copied %jd\n", (head).b, (buf) + (head).s - (head).t, (intmax_t)sizeof(*(head).b) * ((size) - (head).s + (head).t) ); \ + if (move) MIDIQ_MOVE((buf) + (head).s - (head).t, (head).b, sizeof(*(head).b) * ((size) - (head).s + (head).t) ); \ + } \ + if (update) { \ + (head).t+=(size); \ + (head).t%=(head).s; \ + } else { \ + if (MIDIQ_EMPTY(head)) (head).h=-1; \ + } \ + if(MIDIQ_DEBUG)\ + printf("#E h %d t %d\n", (head).h, (head).t); \ +} while (0) + +#define MIDIQ_SIZE(head) ((head).s) +#define MIDIQ_CLEAR(head) ((head).h = (head).t = 0) +#define MIDIQ_BUF(head) ((head).b) +#define MIDIQ_DEQ(head, buf, size) MIDIQ_DEQ_I(head, buf, size, 1, 1) +#define MIDIQ_PEEK(head, buf, size) MIDIQ_DEQ_I(head, buf, size, 1, 0) +#define MIDIQ_POP(head, size) MIDIQ_DEQ_I(head, &head, size, 0, 1) + +#endif --- sys/dev/sound/midi/mpu401.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/mpu401.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,287 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 +__FBSDID("$FreeBSD: src/sys/dev/sound/midi/mpu401.c,v 1.3 2007/02/25 13:51:51 netchild Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* to get driver_intr_t */ + +#include +#include + +#include "mpu_if.h" +#include "mpufoi_if.h" + +#define MPU_DATAPORT 0 +#define MPU_CMDPORT 1 +#define MPU_STATPORT 1 +#define MPU_RESET 0xff +#define MPU_UART 0x3f +#define MPU_ACK 0xfe +#define MPU_STATMASK 0xc0 +#define MPU_OUTPUTBUSY 0x40 +#define MPU_INPUTBUSY 0x80 +#define MPU_TRYDATA 50 +#define MPU_DELAY 2500 + +#define CMD(m,d) MPUFOI_WRITE(m, m->cookie, MPU_CMDPORT,d) +#define STATUS(m) MPUFOI_READ(m, m->cookie, MPU_STATPORT) +#define READ(m) MPUFOI_READ(m, m->cookie, MPU_DATAPORT) +#define WRITE(m,d) MPUFOI_WRITE(m, m->cookie, MPU_DATAPORT,d) + +struct mpu401 { + KOBJ_FIELDS; + struct snd_midi *mid; + int flags; + driver_intr_t *si; + void *cookie; + struct callout timer; +}; + +static void mpu401_timeout(void *m); +static mpu401_intr_t mpu401_intr; + +static int mpu401_minit(kobj_t obj, struct mpu401 *m); +static int mpu401_muninit(kobj_t obj, struct mpu401 *m); +static int mpu401_minqsize(kobj_t obj, struct mpu401 *m); +static int mpu401_moutqsize(kobj_t obj, struct mpu401 *m); +static void mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags); +static void mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags); +static const char *mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity); +static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m); + +static kobj_method_t mpu401_methods[] = { + KOBJMETHOD(mpu_init, mpu401_minit), + KOBJMETHOD(mpu_uninit, mpu401_muninit), + KOBJMETHOD(mpu_inqsize, mpu401_minqsize), + KOBJMETHOD(mpu_outqsize, mpu401_moutqsize), + KOBJMETHOD(mpu_callback, mpu401_mcallback), + KOBJMETHOD(mpu_callbackp, mpu401_mcallbackp), + KOBJMETHOD(mpu_descr, mpu401_mdescr), + KOBJMETHOD(mpu_provider, mpu401_mprovider), + {0, 0} +}; + +DEFINE_CLASS(mpu401, mpu401_methods, 0); + +void +mpu401_timeout(void *a) +{ + struct mpu401 *m = (struct mpu401 *)a; + + if (m->si) + (m->si)(m->cookie); + +} +static int +mpu401_intr(struct mpu401 *m) +{ +#define MPU_INTR_BUF 16 + MIDI_TYPE b[MPU_INTR_BUF]; + int i; + int s; + +/* + printf("mpu401_intr\n"); +*/ +#define RXRDY(m) ( (STATUS(m) & MPU_INPUTBUSY) == 0) +#define TXRDY(m) ( (STATUS(m) & MPU_OUTPUTBUSY) == 0) +#if 0 +#define D(x,l) printf("mpu401_intr %d %x %s %s\n",l, x, x&MPU_INPUTBUSY?"RX":"", x&MPU_OUTPUTBUSY?"TX":"") +#else +#define D(x,l) +#endif + i = 0; + s = STATUS(m); + D(s, 1); + while ((s & MPU_INPUTBUSY) == 0 && i < MPU_INTR_BUF) { + b[i] = READ(m); +/* + printf("mpu401_intr in i %d d %d\n", i, b[i]); +*/ + i++; + s = STATUS(m); + } + if (i) + midi_in(m->mid, b, i); + i = 0; + while (!(s & MPU_OUTPUTBUSY) && i < MPU_INTR_BUF) { + if (midi_out(m->mid, b, 1)) { +/* + printf("mpu401_intr out i %d d %d\n", i, b[0]); +*/ + + WRITE(m, *b); + } else { +/* + printf("mpu401_intr write: no output\n"); +*/ + return 0; + } + i++; + /* DELAY(100); */ + s = STATUS(m); + } + + if ((m->flags & M_TXEN) && (m->si)) { + callout_reset(&m->timer, 1, mpu401_timeout, m); + } + return (m->flags & M_TXEN) == M_TXEN; +} + +struct mpu401 * +mpu401_init(kobj_class_t cls, void *cookie, driver_intr_t softintr, + mpu401_intr_t ** cb) +{ + struct mpu401 *m; + + *cb = NULL; + m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO); + + if (!m) + return NULL; + + kobj_init((kobj_t)m, cls); + + callout_init(&m->timer, 1); + + m->si = softintr; + m->cookie = cookie; + m->flags = 0; + + m->mid = midi_init(&mpu401_class, 0, 0, m); + if (!m->mid) + goto err; + *cb = mpu401_intr; + return m; +err: + printf("mpu401_init error\n"); + free(m, M_MIDI); + return NULL; +} + +int +mpu401_uninit(struct mpu401 *m) +{ + int retval; + + CMD(m, MPU_RESET); + retval = midi_uninit(m->mid); + if (retval) + return retval; + free(m, M_MIDI); + return 0; +} + +static int +mpu401_minit(kobj_t obj, struct mpu401 *m) +{ + int i; + + CMD(m, MPU_RESET); + CMD(m, MPU_UART); + return 0; + i = 0; + while (++i < 2000) { + if (RXRDY(m)) + if (READ(m) == MPU_ACK) + break; + } + + if (i < 2000) { + CMD(m, MPU_UART); + return 0; + } + printf("mpu401_minit failed active sensing\n"); + return 1; +} + + +int +mpu401_muninit(kobj_t obj, struct mpu401 *m) +{ + + return MPUFOI_UNINIT(m, m->cookie); +} + +int +mpu401_minqsize(kobj_t obj, struct mpu401 *m) +{ + return 128; +} + +int +mpu401_moutqsize(kobj_t obj, struct mpu401 *m) +{ + return 128; +} + +static void +mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags) +{ +#if 0 + printf("mpu401_callback %s %s %s %s\n", + flags & M_RX ? "M_RX" : "", + flags & M_TX ? "M_TX" : "", + flags & M_RXEN ? "M_RXEN" : "", + flags & M_TXEN ? "M_TXEN" : ""); +#endif + if (flags & M_TXEN && m->si) { + callout_reset(&m->timer, 1, mpu401_timeout, m); + } + m->flags = flags; +} + +static void +mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags) +{ +/* printf("mpu401_callbackp\n"); */ + mpu401_mcallback(obj, m, flags); +} + +static const char * +mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity) +{ + + return "descr mpu401"; +} + +static const char * +mpu401_mprovider(kobj_t obj, struct mpu401 *m) +{ + return "provider mpu401"; +} --- sys/dev/sound/midi/mpu401.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/mpu401.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/midi/mpu401.h,v 1.3 2007/02/25 13:51:51 netchild Exp $ + */ + +#ifndef MPU401_H +#define MPU401_H + +struct mpu401; + +typedef int mpu401_intr_t(struct mpu401 *_obj); + +extern struct mpu401 * +mpu401_init(kobj_class_t _cls, void *cookie, driver_intr_t *_softintr, + mpu401_intr_t ** _cb); +extern int mpu401_uninit(struct mpu401 *_obj); + +#endif --- sys/dev/sound/midi/mpu_if.m.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/mpu_if.m Thu Jul 12 12:04:19 2007 @@ -0,0 +1,74 @@ +#- +# Copyright (c) 2003 Mathew Kanner +# 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +# +# $FreeBSD: src/sys/dev/sound/midi/mpu_if.m,v 1.3 2007/02/25 13:51:51 netchild Exp $ +# + +#include + +INTERFACE mpu; + +METHOD int inqsize { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD int outqsize { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD int init { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD void callbackp { + struct snd_midi *_kobj; + void *_cookie; + int _flags; +}; + +METHOD void callback { + struct snd_midi *_kobj; + void *_cookie; + int _flags; +}; + +METHOD const char * provider { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD const char * descr { + struct snd_midi *_kobj; + void *_cookie; + int _verbosity; +}; + +METHOD int uninit { + struct snd_midi *_kobj; + void *_cookie; +}; --- sys/dev/sound/midi/mpufoi_if.m.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/mpufoi_if.m Thu Jul 12 12:04:19 2007 @@ -0,0 +1,50 @@ +#- +# Copyright (c) 2003 Mathew Kanner +# 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +# +# $FreeBSD: src/sys/dev/sound/midi/mpufoi_if.m,v 1.3 2007/02/25 13:51:52 netchild Exp $ +# + +#include +#include + +INTERFACE mpufoi; + +METHOD unsigned char read { + struct mpu401 *_kobj; + void *_cookie; + int _reg; +}; + +METHOD void write { + struct mpu401 *_kobj; + void *_cookie; + int _reg; + unsigned char _d; +}; + +METHOD int uninit { + struct mpu401 *_kobj; + void *_cookie; +}; --- sys/dev/sound/midi/sequencer.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/sequencer.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,2088 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * Copyright (c) 1993 Hannu Savolainen + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +/* + * The sequencer personality manager. + */ + +#include +__FBSDID("$FreeBSD: src/sys/dev/sound/midi/sequencer.c,v 1.26 2007/03/15 14:57:54 ariff Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include /* for DATA_SET */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for DELAY */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include "synth_if.h" + +#include + +#define TMR_TIMERBASE 13 + +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + * synthesizer and MIDI output) */ +#define SND_DEV_MUSIC 8 /* /dev/music, level 2 interface */ + +/* Length of a sequencer event. */ +#define EV_SZ 8 +#define IEV_SZ 8 + +/* Lookup modes */ +#define LOOKUP_EXIST (0) +#define LOOKUP_OPEN (1) +#define LOOKUP_CLOSE (2) + +#define PCMMKMINOR(u, d, c) \ + ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) +#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c) +#define MIDIUNIT(y) ((minor(y) >> 4) & 0x0f) +#define MIDIDEV(y) (minor(y) & 0x0f) + +/* These are the entries to the sequencer driver. */ +static d_open_t seq_open; +static d_close_t seq_close; +static d_ioctl_t seq_ioctl; +static d_read_t seq_read; +static d_write_t seq_write; +static d_poll_t seq_poll; + +static struct cdevsw seq_cdevsw = { + .d_version = D_VERSION, + .d_open = seq_open, + .d_close = seq_close, + .d_read = seq_read, + .d_write = seq_write, + .d_ioctl = seq_ioctl, + .d_poll = seq_poll, + .d_name = "sequencer", +}; + +struct seq_softc { + KOBJ_FIELDS; + + struct mtx seq_lock, q_lock; + struct cv empty_cv, reset_cv, in_cv, out_cv, state_cv, th_cv; + + MIDIQ_HEAD(, u_char) in_q, out_q; + + u_long flags; + /* Flags (protected by flag_mtx of mididev_info) */ + int fflags; /* Access mode */ + int music; + + int out_water; /* Sequence output threshould */ + snd_sync_parm sync_parm; /* AIOSYNC parameter set */ + struct thread *sync_thread; /* AIOSYNCing thread */ + struct selinfo in_sel, out_sel; + int midi_number; + struct cdev *seqdev, *musicdev; + int unit; + int maxunits; + kobj_t *midis; + int *midi_flags; + kobj_t mapper; + void *mapper_cookie; + struct timeval timerstop, timersub; + int timerbase, tempo; + int timerrun; + int done; + int playing; + int recording; + int busy; + int pre_event_timeout; + int waiting; +}; + +/* + * Module specific stuff, including how many sequecers + * we currently own. + */ + +SYSCTL_NODE(_hw_midi, OID_AUTO, seq, CTLFLAG_RD, 0, "Midi sequencer"); + +int seq_debug; +/* XXX: should this be moved into debug.midi? */ +SYSCTL_INT(_hw_midi_seq, OID_AUTO, debug, CTLFLAG_RW, &seq_debug, 0, ""); + +midi_cmdtab cmdtab_seqevent[] = { + {SEQ_NOTEOFF, "SEQ_NOTEOFF"}, + {SEQ_NOTEON, "SEQ_NOTEON"}, + {SEQ_WAIT, "SEQ_WAIT"}, + {SEQ_PGMCHANGE, "SEQ_PGMCHANGE"}, + {SEQ_SYNCTIMER, "SEQ_SYNCTIMER"}, + {SEQ_MIDIPUTC, "SEQ_MIDIPUTC"}, + {SEQ_DRUMON, "SEQ_DRUMON"}, + {SEQ_DRUMOFF, "SEQ_DRUMOFF"}, + {SEQ_ECHO, "SEQ_ECHO"}, + {SEQ_AFTERTOUCH, "SEQ_AFTERTOUCH"}, + {SEQ_CONTROLLER, "SEQ_CONTROLLER"}, + {SEQ_BALANCE, "SEQ_BALANCE"}, + {SEQ_VOLMODE, "SEQ_VOLMODE"}, + {SEQ_FULLSIZE, "SEQ_FULLSIZE"}, + {SEQ_PRIVATE, "SEQ_PRIVATE"}, + {SEQ_EXTENDED, "SEQ_EXTENDED"}, + {EV_SEQ_LOCAL, "EV_SEQ_LOCAL"}, + {EV_TIMING, "EV_TIMING"}, + {EV_CHN_COMMON, "EV_CHN_COMMON"}, + {EV_CHN_VOICE, "EV_CHN_VOICE"}, + {EV_SYSEX, "EV_SYSEX"}, + {-1, NULL}, +}; + +midi_cmdtab cmdtab_seqioctl[] = { + {SNDCTL_SEQ_RESET, "SNDCTL_SEQ_RESET"}, + {SNDCTL_SEQ_SYNC, "SNDCTL_SEQ_SYNC"}, + {SNDCTL_SYNTH_INFO, "SNDCTL_SYNTH_INFO"}, + {SNDCTL_SEQ_CTRLRATE, "SNDCTL_SEQ_CTRLRATE"}, + {SNDCTL_SEQ_GETOUTCOUNT, "SNDCTL_SEQ_GETOUTCOUNT"}, + {SNDCTL_SEQ_GETINCOUNT, "SNDCTL_SEQ_GETINCOUNT"}, + {SNDCTL_SEQ_PERCMODE, "SNDCTL_SEQ_PERCMODE"}, + {SNDCTL_FM_LOAD_INSTR, "SNDCTL_FM_LOAD_INSTR"}, + {SNDCTL_SEQ_TESTMIDI, "SNDCTL_SEQ_TESTMIDI"}, + {SNDCTL_SEQ_RESETSAMPLES, "SNDCTL_SEQ_RESETSAMPLES"}, + {SNDCTL_SEQ_NRSYNTHS, "SNDCTL_SEQ_NRSYNTHS"}, + {SNDCTL_SEQ_NRMIDIS, "SNDCTL_SEQ_NRMIDIS"}, + {SNDCTL_SEQ_GETTIME, "SNDCTL_SEQ_GETTIME"}, + {SNDCTL_MIDI_INFO, "SNDCTL_MIDI_INFO"}, + {SNDCTL_SEQ_THRESHOLD, "SNDCTL_SEQ_THRESHOLD"}, + {SNDCTL_SYNTH_MEMAVL, "SNDCTL_SYNTH_MEMAVL"}, + {SNDCTL_FM_4OP_ENABLE, "SNDCTL_FM_4OP_ENABLE"}, + {SNDCTL_PMGR_ACCESS, "SNDCTL_PMGR_ACCESS"}, + {SNDCTL_SEQ_PANIC, "SNDCTL_SEQ_PANIC"}, + {SNDCTL_SEQ_OUTOFBAND, "SNDCTL_SEQ_OUTOFBAND"}, + {SNDCTL_TMR_TIMEBASE, "SNDCTL_TMR_TIMEBASE"}, + {SNDCTL_TMR_START, "SNDCTL_TMR_START"}, + {SNDCTL_TMR_STOP, "SNDCTL_TMR_STOP"}, + {SNDCTL_TMR_CONTINUE, "SNDCTL_TMR_CONTINUE"}, + {SNDCTL_TMR_TEMPO, "SNDCTL_TMR_TEMPO"}, + {SNDCTL_TMR_SOURCE, "SNDCTL_TMR_SOURCE"}, + {SNDCTL_TMR_METRONOME, "SNDCTL_TMR_METRONOME"}, + {SNDCTL_TMR_SELECT, "SNDCTL_TMR_SELECT"}, + {SNDCTL_MIDI_PRETIME, "SNDCTL_MIDI_PRETIME"}, + {AIONWRITE, "AIONWRITE"}, + {AIOGSIZE, "AIOGSIZE"}, + {AIOSSIZE, "AIOSSIZE"}, + {AIOGFMT, "AIOGFMT"}, + {AIOSFMT, "AIOSFMT"}, + {AIOGMIX, "AIOGMIX"}, + {AIOSMIX, "AIOSMIX"}, + {AIOSTOP, "AIOSTOP"}, + {AIOSYNC, "AIOSYNC"}, + {AIOGCAP, "AIOGCAP"}, + {-1, NULL}, +}; + +midi_cmdtab cmdtab_timer[] = { + {TMR_WAIT_REL, "TMR_WAIT_REL"}, + {TMR_WAIT_ABS, "TMR_WAIT_ABS"}, + {TMR_STOP, "TMR_STOP"}, + {TMR_START, "TMR_START"}, + {TMR_CONTINUE, "TMR_CONTINUE"}, + {TMR_TEMPO, "TMR_TEMPO"}, + {TMR_ECHO, "TMR_ECHO"}, + {TMR_CLOCK, "TMR_CLOCK"}, + {TMR_SPP, "TMR_SPP"}, + {TMR_TIMESIG, "TMR_TIMESIG"}, + {-1, NULL}, +}; + +midi_cmdtab cmdtab_seqcv[] = { + {MIDI_NOTEOFF, "MIDI_NOTEOFF"}, + {MIDI_NOTEON, "MIDI_NOTEON"}, + {MIDI_KEY_PRESSURE, "MIDI_KEY_PRESSURE"}, + {-1, NULL}, +}; + +midi_cmdtab cmdtab_seqccmn[] = { + {MIDI_CTL_CHANGE, "MIDI_CTL_CHANGE"}, + {MIDI_PGM_CHANGE, "MIDI_PGM_CHANGE"}, + {MIDI_CHN_PRESSURE, "MIDI_CHN_PRESSURE"}, + {MIDI_PITCH_BEND, "MIDI_PITCH_BEND"}, + {MIDI_SYSTEM_PREFIX, "MIDI_SYSTEM_PREFIX"}, + {-1, NULL}, +}; + +/* + * static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m); + */ + +static kobj_method_t seq_methods[] = { + /* KOBJMETHOD(mpu_provider,mpu401_mprovider), */ + {0, 0} +}; + +DEFINE_CLASS(sequencer, seq_methods, 0); + +/* The followings are the local function. */ +static int seq_convertold(u_char *event, u_char *out); + +/* + * static void seq_midiinput(struct seq_softc * scp, void *md); + */ +static void seq_reset(struct seq_softc *scp); +static int seq_sync(struct seq_softc *scp); + +static int seq_processevent(struct seq_softc *scp, u_char *event); + +static int seq_timing(struct seq_softc *scp, u_char *event); +static int seq_local(struct seq_softc *scp, u_char *event); + +static int seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event); +static int seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event); +static int seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event); + +static int seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md); +void seq_copytoinput(struct seq_softc *scp, u_char *event, int len); +int seq_modevent(module_t mod, int type, void *data); +struct seq_softc *seqs[10]; +static struct mtx seqinfo_mtx; +static u_long nseq = 0; + +static void timer_start(struct seq_softc *t); +static void timer_stop(struct seq_softc *t); +static void timer_setvals(struct seq_softc *t, int tempo, int timerbase); +static void timer_wait(struct seq_softc *t, int ticks, int wait_abs); +static int timer_now(struct seq_softc *t); + + +static void +timer_start(struct seq_softc *t) +{ + t->timerrun = 1; + getmicrotime(&t->timersub); +} + +static void +timer_continue(struct seq_softc *t) +{ + struct timeval now; + + if (t->timerrun == 1) + return; + t->timerrun = 1; + getmicrotime(&now); + timevalsub(&now, &t->timerstop); + timevaladd(&t->timersub, &now); +} + +static void +timer_stop(struct seq_softc *t) +{ + t->timerrun = 0; + getmicrotime(&t->timerstop); +} + +static void +timer_setvals(struct seq_softc *t, int tempo, int timerbase) +{ + t->tempo = tempo; + t->timerbase = timerbase; +} + +static void +timer_wait(struct seq_softc *t, int ticks, int wait_abs) +{ + struct timeval now, when; + int ret; + unsigned long long i; + + while (t->timerrun == 0) { + SEQ_DEBUG(2, printf("Timer wait when timer isn't running\n")); + /* + * The old sequencer used timeouts that only increased + * the timer when the timer was running. + * Hence the sequencer would stick (?) if the + * timer was disabled. + */ + cv_wait(&t->reset_cv, &t->seq_lock); + if (t->playing == 0) + return; + } + + i = ticks * 60ull * 1000000ull / (t->tempo * t->timerbase); + + when.tv_sec = i / 1000000; + when.tv_usec = i % 1000000; + +#if 0 + printf("timer_wait tempo %d timerbase %d ticks %d abs %d u_sec %llu\n", + t->tempo, t->timerbase, ticks, wait_abs, i); +#endif + + if (wait_abs != 0) { + getmicrotime(&now); + timevalsub(&now, &t->timersub); + timevalsub(&when, &now); + } + if (when.tv_sec < 0 || when.tv_usec < 0) { + SEQ_DEBUG(3, + printf("seq_timer error negative time %lds.%06lds\n", + (long)when.tv_sec, (long)when.tv_usec)); + return; + } + i = when.tv_sec * 1000000ull; + i += when.tv_usec; + i *= hz; + i /= 1000000ull; +#if 0 + printf("seq_timer usec %llu ticks %llu\n", + when.tv_sec * 1000000ull + when.tv_usec, i); +#endif + t->waiting = 1; + ret = cv_timedwait(&t->reset_cv, &t->seq_lock, i + 1); + t->waiting = 0; + + if (ret != EWOULDBLOCK) + SEQ_DEBUG(3, printf("seq_timer didn't timeout\n")); + +} + +static int +timer_now(struct seq_softc *t) +{ + struct timeval now; + unsigned long long i; + int ret; + + if (t->timerrun == 0) + now = t->timerstop; + else + getmicrotime(&now); + + timevalsub(&now, &t->timersub); + + i = now.tv_sec * 1000000ull; + i += now.tv_usec; + i *= t->timerbase; +/* i /= t->tempo; */ + i /= 1000000ull; + + ret = i; + /* + * printf("timer_now: %llu %d\n", i, ret); + */ + + return ret; +} + +static void +seq_eventthread(void *arg) +{ + struct seq_softc *scp = arg; + char event[EV_SZ]; + + mtx_lock(&scp->seq_lock); + SEQ_DEBUG(2, printf("seq_eventthread started\n")); + while (scp->done == 0) { +restart: + while (scp->playing == 0) { + cv_wait(&scp->state_cv, &scp->seq_lock); + if (scp->done) + goto done; + } + + while (MIDIQ_EMPTY(scp->out_q)) { + cv_broadcast(&scp->empty_cv); + cv_wait(&scp->out_cv, &scp->seq_lock); + if (scp->playing == 0) + goto restart; + if (scp->done) + goto done; + } + + MIDIQ_DEQ(scp->out_q, event, EV_SZ); + + if (MIDIQ_AVAIL(scp->out_q) < scp->out_water) { + cv_broadcast(&scp->out_cv); + selwakeup(&scp->out_sel); + } + seq_processevent(scp, event); + } + +done: + cv_broadcast(&scp->th_cv); + mtx_unlock(&scp->seq_lock); + mtx_lock(&Giant); + SEQ_DEBUG(2, printf("seq_eventthread finished\n")); + kthread_exit(0); +} + +/* + * seq_processevent: This maybe called by the event thread or the IOCTL + * handler for queued and out of band events respectively. + */ +static int +seq_processevent(struct seq_softc *scp, u_char *event) +{ + int ret; + kobj_t m; + + ret = 0; + + if (event[0] == EV_SEQ_LOCAL) + ret = seq_local(scp, event); + else if (event[0] == EV_TIMING) + ret = seq_timing(scp, event); + else if (event[0] != EV_CHN_VOICE && + event[0] != EV_CHN_COMMON && + event[0] != EV_SYSEX && + event[0] != SEQ_MIDIPUTC) { + ret = 1; + SEQ_DEBUG(2, printf("seq_processevent not known %d\n", + event[0])); + } else if (seq_fetch_mid(scp, event[1], &m) != 0) { + ret = 1; + SEQ_DEBUG(2, printf("seq_processevent midi unit not found %d\n", + event[1])); + } else + switch (event[0]) { + case EV_CHN_VOICE: + ret = seq_chnvoice(scp, m, event); + break; + case EV_CHN_COMMON: + ret = seq_chncommon(scp, m, event); + break; + case EV_SYSEX: + ret = seq_sysex(scp, m, event); + break; + case SEQ_MIDIPUTC: + mtx_unlock(&scp->seq_lock); + ret = SYNTH_WRITERAW(m, &event[2], 1); + mtx_lock(&scp->seq_lock); + break; + } + return ret; +} + +static int +seq_addunit(void) +{ + struct seq_softc *scp; + int ret; + u_char *buf; + + /* Allocate the softc. */ + ret = ENOMEM; + scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO); + if (scp == NULL) { + SEQ_DEBUG(1, printf("seq_addunit: softc allocation failed.\n")); + goto err; + } + kobj_init((kobj_t)scp, &sequencer_class); + + buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO); + if (buf == NULL) + goto err; + MIDIQ_INIT(scp->in_q, buf, EV_SZ * 1024); + buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO); + if (buf == NULL) + goto err; + MIDIQ_INIT(scp->out_q, buf, EV_SZ * 1024); + ret = EINVAL; + + scp->midis = malloc(sizeof(kobj_t) * 32, M_TEMP, M_NOWAIT | M_ZERO); + scp->midi_flags = malloc(sizeof(*scp->midi_flags) * 32, M_TEMP, + M_NOWAIT | M_ZERO); + + if (scp->midis == NULL || scp->midi_flags == NULL) + goto err; + + scp->flags = 0; + + mtx_init(&scp->seq_lock, "seqflq", NULL, 0); + cv_init(&scp->state_cv, "seqstate"); + cv_init(&scp->empty_cv, "seqempty"); + cv_init(&scp->reset_cv, "seqtimer"); + cv_init(&scp->out_cv, "seqqout"); + cv_init(&scp->in_cv, "seqqin"); + cv_init(&scp->th_cv, "seqstart"); + + /* + * Init the damn timer + */ + + scp->mapper = midimapper_addseq(scp, &scp->unit, &scp->mapper_cookie); + if (scp->mapper == NULL) + goto err; + + scp->seqdev = make_dev(&seq_cdevsw, + MIDIMKMINOR(scp->unit, SND_DEV_SEQ, 0), UID_ROOT, + GID_WHEEL, 0666, "sequencer%d", scp->unit); + + scp->musicdev = make_dev(&seq_cdevsw, + MIDIMKMINOR(scp->unit, SND_DEV_MUSIC, 0), UID_ROOT, + GID_WHEEL, 0666, "music%d", scp->unit); + + if (scp->seqdev == NULL || scp->musicdev == NULL) + goto err; + /* + * TODO: Add to list of sequencers this module provides + */ + + ret = kthread_create(seq_eventthread, scp, NULL, RFHIGHPID, 0, + "sequencer %02d", scp->unit); + + if (ret) + goto err; + + scp->seqdev->si_drv1 = scp->musicdev->si_drv1 = scp; + + SEQ_DEBUG(2, printf("sequencer %d created scp %p\n", scp->unit, scp)); + + ret = 0; + + mtx_lock(&seqinfo_mtx); + seqs[nseq++] = scp; + mtx_unlock(&seqinfo_mtx); + + goto ok; + +err: + if (scp != NULL) { + if (scp->seqdev != NULL) + destroy_dev(scp->seqdev); + if (scp->musicdev != NULL) + destroy_dev(scp->musicdev); + /* + * TODO: Destroy mutex and cv + */ + if (scp->midis != NULL) + free(scp->midis, M_TEMP); + if (scp->midi_flags != NULL) + free(scp->midi_flags, M_TEMP); + if (scp->out_q.b) + free(scp->out_q.b, M_TEMP); + if (scp->in_q.b) + free(scp->in_q.b, M_TEMP); + free(scp, M_DEVBUF); + } +ok: + return ret; +} + +static int +seq_delunit(int unit) +{ + struct seq_softc *scp = seqs[unit]; + int i; + + //SEQ_DEBUG(4, printf("seq_delunit: %d\n", unit)); + SEQ_DEBUG(1, printf("seq_delunit: 1 \n")); + mtx_lock(&scp->seq_lock); + + scp->playing = 0; + scp->done = 1; + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->reset_cv); + SEQ_DEBUG(1, printf("seq_delunit: 2 \n")); + cv_wait(&scp->th_cv, &scp->seq_lock); + SEQ_DEBUG(1, printf("seq_delunit: 3.0 \n")); + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(1, printf("seq_delunit: 3.1 \n")); + + cv_destroy(&scp->state_cv); + SEQ_DEBUG(1, printf("seq_delunit: 4 \n")); + cv_destroy(&scp->empty_cv); + SEQ_DEBUG(1, printf("seq_delunit: 5 \n")); + cv_destroy(&scp->reset_cv); + SEQ_DEBUG(1, printf("seq_delunit: 6 \n")); + cv_destroy(&scp->out_cv); + SEQ_DEBUG(1, printf("seq_delunit: 7 \n")); + cv_destroy(&scp->in_cv); + SEQ_DEBUG(1, printf("seq_delunit: 8 \n")); + cv_destroy(&scp->th_cv); + + SEQ_DEBUG(1, printf("seq_delunit: 10 \n")); + if (scp->seqdev) + destroy_dev(scp->seqdev); + SEQ_DEBUG(1, printf("seq_delunit: 11 \n")); + if (scp->musicdev) + destroy_dev(scp->musicdev); + SEQ_DEBUG(1, printf("seq_delunit: 12 \n")); + scp->seqdev = scp->musicdev = NULL; + if (scp->midis != NULL) + free(scp->midis, M_TEMP); + SEQ_DEBUG(1, printf("seq_delunit: 13 \n")); + if (scp->midi_flags != NULL) + free(scp->midi_flags, M_TEMP); + SEQ_DEBUG(1, printf("seq_delunit: 14 \n")); + free(scp->out_q.b, M_TEMP); + SEQ_DEBUG(1, printf("seq_delunit: 15 \n")); + free(scp->in_q.b, M_TEMP); + + SEQ_DEBUG(1, printf("seq_delunit: 16 \n")); + + mtx_destroy(&scp->seq_lock); + SEQ_DEBUG(1, printf("seq_delunit: 17 \n")); + free(scp, M_DEVBUF); + + mtx_lock(&seqinfo_mtx); + for (i = unit; i < (nseq - 1); i++) + seqs[i] = seqs[i + 1]; + nseq--; + mtx_unlock(&seqinfo_mtx); + + return 0; +} + +int +seq_modevent(module_t mod, int type, void *data) +{ + int retval, r; + + retval = 0; + + switch (type) { + case MOD_LOAD: + mtx_init(&seqinfo_mtx, "seqmod", NULL, 0); + retval = seq_addunit(); + break; + + case MOD_UNLOAD: + while (nseq) { + r = seq_delunit(nseq - 1); + if (r) { + retval = r; + break; + } + } + if (nseq == 0) { + retval = 0; + mtx_destroy(&seqinfo_mtx); + } + break; + + default: + break; + } + + return retval; +} + +static int +seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md) +{ + + if (unit > scp->midi_number || unit < 0) + return EINVAL; + + *md = scp->midis[unit]; + + return 0; +} + +int +seq_open(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct seq_softc *scp = i_dev->si_drv1; + int i; + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(3, printf("seq_open: scp %p unit %d, flags 0x%x.\n", + scp, scp->unit, flags)); + + /* + * Mark this device busy. + */ + + mtx_lock(&scp->seq_lock); + if (scp->busy) { + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(2, printf("seq_open: unit %d is busy.\n", scp->unit)); + return EBUSY; + } + scp->fflags = flags; + /* + if ((scp->fflags & O_NONBLOCK) != 0) + scp->flags |= SEQ_F_NBIO; + */ + scp->music = MIDIDEV(i_dev) == SND_DEV_MUSIC; + + /* + * Enumerate the available midi devices + */ + scp->midi_number = 0; + scp->maxunits = midimapper_open(scp->mapper, &scp->mapper_cookie); + + if (scp->maxunits == 0) + SEQ_DEBUG(2, printf("seq_open: no midi devices\n")); + + for (i = 0; i < scp->maxunits; i++) { + scp->midis[scp->midi_number] = + midimapper_fetch_synth(scp->mapper, scp->mapper_cookie, i); + if (scp->midis[scp->midi_number]) { + if (SYNTH_OPEN(scp->midis[scp->midi_number], scp, + scp->fflags) != 0) + scp->midis[scp->midi_number] = NULL; + else { + scp->midi_flags[scp->midi_number] = + SYNTH_QUERY(scp->midis[scp->midi_number]); + scp->midi_number++; + } + } + } + + timer_setvals(scp, 60, 100); + + timer_start(scp); + timer_stop(scp); + /* + * actually, if we're in rdonly mode, we should start the timer + */ + /* + * TODO: Handle recording now + */ + + scp->out_water = MIDIQ_SIZE(scp->out_q) / 2; + + scp->busy = 1; + mtx_unlock(&scp->seq_lock); + + SEQ_DEBUG(2, printf("seq_open: opened, mode %s.\n", + scp->music ? "music" : "sequencer")); + SEQ_DEBUG(2, + printf("Sequencer %d %p opened maxunits %d midi_number %d:\n", + scp->unit, scp, scp->maxunits, scp->midi_number)); + for (i = 0; i < scp->midi_number; i++) + SEQ_DEBUG(3, printf(" midi %d %p\n", i, scp->midis[i])); + + return 0; +} + +/* + * seq_close + */ +int +seq_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + int i; + struct seq_softc *scp = i_dev->si_drv1; + int ret; + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(2, printf("seq_close: unit %d.\n", scp->unit)); + + mtx_lock(&scp->seq_lock); + + ret = ENXIO; + if (scp->busy == 0) + goto err; + + seq_reset(scp); + seq_sync(scp); + + for (i = 0; i < scp->midi_number; i++) + if (scp->midis[i]) + SYNTH_CLOSE(scp->midis[i]); + + midimapper_close(scp->mapper, scp->mapper_cookie); + + timer_stop(scp); + + scp->busy = 0; + ret = 0; + +err: + SEQ_DEBUG(3, printf("seq_close: closed ret = %d.\n", ret)); + mtx_unlock(&scp->seq_lock); + return ret; +} + +int +seq_read(struct cdev *i_dev, struct uio *uio, int ioflag) +{ + int retval, used; + struct seq_softc *scp = i_dev->si_drv1; + +#define SEQ_RSIZE 32 + u_char buf[SEQ_RSIZE]; + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(7, printf("seq_read: unit %d, resid %d.\n", + scp->unit, uio->uio_resid)); + + mtx_lock(&scp->seq_lock); + if ((scp->fflags & FREAD) == 0) { + SEQ_DEBUG(2, printf("seq_read: unit %d is not for reading.\n", + scp->unit)); + retval = EIO; + goto err1; + } + /* + * Begin recording. + */ + /* + * if ((scp->flags & SEQ_F_READING) == 0) + */ + /* + * TODO, start recording if not alread + */ + + /* + * I think the semantics are to return as soon + * as possible. + * Second thought, it doens't seem like midimoutain + * expects that at all. + * TODO: Look up in some sort of spec + */ + + while (uio->uio_resid > 0) { + while (MIDIQ_EMPTY(scp->in_q)) { + retval = EWOULDBLOCK; + /* + * I wish I knew which one to care about + */ + + if (scp->fflags & O_NONBLOCK) + goto err1; + if (ioflag & O_NONBLOCK) + goto err1; + + retval = cv_wait_sig(&scp->in_cv, &scp->seq_lock); + if (retval == EINTR) + goto err1; + } + + used = MIN(MIDIQ_LEN(scp->in_q), uio->uio_resid); + used = MIN(used, SEQ_RSIZE); + + SEQ_DEBUG(8, printf("midiread: uiomove cc=%d\n", used)); + MIDIQ_DEQ(scp->in_q, buf, used); + retval = uiomove(buf, used, uio); + if (retval) + goto err1; + } + + retval = 0; +err1: + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(6, printf("seq_read: ret %d, resid %d.\n", + retval, uio->uio_resid)); + + return retval; +} + +int +seq_write(struct cdev *i_dev, struct uio *uio, int ioflag) +{ + u_char event[EV_SZ], newevent[EV_SZ], ev_code; + struct seq_softc *scp = i_dev->si_drv1; + int retval; + int used; + + SEQ_DEBUG(7, printf("seq_write: unit %d, resid %d.\n", + scp->unit, uio->uio_resid)); + + if (scp == NULL) + return ENXIO; + + mtx_lock(&scp->seq_lock); + + if ((scp->fflags & FWRITE) == 0) { + SEQ_DEBUG(2, printf("seq_write: unit %d is not for writing.\n", + scp->unit)); + retval = EIO; + goto err0; + } + while (uio->uio_resid > 0) { + while (MIDIQ_AVAIL(scp->out_q) == 0) { + retval = EWOULDBLOCK; + if (scp->fflags & O_NONBLOCK) + goto err0; + if (ioflag & O_NONBLOCK) + goto err0; + SEQ_DEBUG(8, printf("seq_write cvwait\n")); + + scp->playing = 1; + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->state_cv); + + retval = cv_wait_sig(&scp->out_cv, &scp->seq_lock); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; +#if 0 + /* + * Useless test + */ + if (scp != i_dev->si_drv1) + retval = ENXIO; +#endif + } + + used = MIN(uio->uio_resid, 4); + + SEQ_DEBUG(8, printf("seqout: resid %d len %jd avail %jd\n", + uio->uio_resid, (intmax_t)MIDIQ_LEN(scp->out_q), + (intmax_t)MIDIQ_AVAIL(scp->out_q))); + + if (used != 4) { + retval = ENXIO; + goto err0; + } + retval = uiomove(event, used, uio); + if (retval) + goto err0; + + ev_code = event[0]; + SEQ_DEBUG(8, printf("seq_write: unit %d, event %s.\n", + scp->unit, midi_cmdname(ev_code, cmdtab_seqevent))); + + /* Have a look at the event code. */ + if (ev_code == SEQ_FULLSIZE) { + + /* + * TODO: restore code for SEQ_FULLSIZE + */ +#if 0 + /* + * A long event, these are the patches/samples for a + * synthesizer. + */ + midiunit = *(u_short *)&event[2]; + mtx_lock(&sd->seq_lock); + ret = lookup_mididev(scp, midiunit, LOOKUP_OPEN, &md); + mtx_unlock(&sd->seq_lock); + if (ret != 0) + return (ret); + + SEQ_DEBUG(printf("seq_write: loading a patch to the unit %d.\n", midiunit)); + + ret = md->synth.loadpatch(md, *(short *)&event[0], buf, + p + 4, count, 0); + return (ret); +#else + /* + * For now, just flush the darn buffer + */ + SEQ_DEBUG(2, + printf("seq_write: SEQ_FULLSIZE flusing buffer.\n")); + while (uio->uio_resid > 0) { + retval = uiomove(event, EV_SZ, uio); + if (retval) + goto err0; + + } + retval = 0; + goto err0; +#endif + } + retval = EINVAL; + if (ev_code >= 128) { + + /* + * Some sort of an extended event. The size is eight + * bytes. scoop extra info. + */ + if (scp->music && ev_code == SEQ_EXTENDED) { + SEQ_DEBUG(2, printf("seq_write: invalid level two event %x.\n", ev_code)); + goto err0; + } + if (uiomove((caddr_t)&event[4], 4, uio)) { + SEQ_DEBUG(2, + printf("seq_write: user memory mangled?\n")); + goto err0; + } + } else { + /* + * Size four event. + */ + if (scp->music) { + SEQ_DEBUG(2, printf("seq_write: four byte event in music mode.\n")); + goto err0; + } + } + if (ev_code == SEQ_MIDIPUTC) { + /* + * TODO: event[2] is unit number to receive char. + * Range check it. + */ + } + if (scp->music) { +#ifdef not_ever_ever + if (event[0] == EV_TIMING && + (event[1] == TMR_START || event[1] == TMR_STOP)) { + /* + * For now, try to make midimoutain work by + * forcing these events to be processed + * immediatly. + */ + seq_processevent(scp, event); + } else + MIDIQ_ENQ(scp->out_q, event, EV_SZ); +#else + MIDIQ_ENQ(scp->out_q, event, EV_SZ); +#endif + } else { + if (seq_convertold(event, newevent) > 0) + MIDIQ_ENQ(scp->out_q, newevent, EV_SZ); +#if 0 + else + goto err0; +#endif + } + + } + + scp->playing = 1; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + + retval = 0; + +err0: + SEQ_DEBUG(6, + printf("seq_write done: leftover buffer length %d retval %d\n", + uio->uio_resid, retval)); + mtx_unlock(&scp->seq_lock); + return retval; +} + +int +seq_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + int midiunit, ret, tmp; + struct seq_softc *scp = i_dev->si_drv1; + struct synth_info *synthinfo; + struct midi_info *midiinfo; + u_char event[EV_SZ]; + u_char newevent[EV_SZ]; + + kobj_t md; + + /* + * struct snd_size *sndsize; + */ + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(6, printf("seq_ioctl: unit %d, cmd %s.\n", + scp->unit, midi_cmdname(cmd, cmdtab_seqioctl))); + + ret = 0; + + switch (cmd) { + case SNDCTL_SEQ_GETTIME: + /* + * ioctl needed by libtse + */ + mtx_lock(&scp->seq_lock); + *(int *)arg = timer_now(scp); + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(6, printf("seq_ioctl: gettime %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_TMR_METRONOME: + /* fallthrough */ + case SNDCTL_TMR_SOURCE: + /* + * Not implemented + */ + ret = 0; + break; + case SNDCTL_TMR_TEMPO: + event[1] = TMR_TEMPO; + event[4] = *(int *)arg & 0xFF; + event[5] = (*(int *)arg >> 8) & 0xFF; + event[6] = (*(int *)arg >> 16) & 0xFF; + event[7] = (*(int *)arg >> 24) & 0xFF; + goto timerevent; + case SNDCTL_TMR_TIMEBASE: + event[1] = TMR_TIMERBASE; + event[4] = *(int *)arg & 0xFF; + event[5] = (*(int *)arg >> 8) & 0xFF; + event[6] = (*(int *)arg >> 16) & 0xFF; + event[7] = (*(int *)arg >> 24) & 0xFF; + goto timerevent; + case SNDCTL_TMR_START: + event[1] = TMR_START; + goto timerevent; + case SNDCTL_TMR_STOP: + event[1] = TMR_STOP; + goto timerevent; + case SNDCTL_TMR_CONTINUE: + event[1] = TMR_CONTINUE; +timerevent: + event[0] = EV_TIMING; + mtx_lock(&scp->seq_lock); + if (!scp->music) { + ret = EINVAL; + mtx_unlock(&scp->seq_lock); + break; + } + seq_processevent(scp, event); + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_TMR_SELECT: + SEQ_DEBUG(2, + printf("seq_ioctl: SNDCTL_TMR_SELECT not supported\n")); + ret = EINVAL; + break; + case SNDCTL_SEQ_SYNC: + if (mode == O_RDONLY) { + ret = 0; + break; + } + mtx_lock(&scp->seq_lock); + ret = seq_sync(scp); + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_SEQ_PANIC: + /* fallthrough */ + case SNDCTL_SEQ_RESET: + /* + * SNDCTL_SEQ_PANIC == SNDCTL_SEQ_RESET + */ + mtx_lock(&scp->seq_lock); + seq_reset(scp); + mtx_unlock(&scp->seq_lock); + ret = 0; + break; + case SNDCTL_SEQ_TESTMIDI: + mtx_lock(&scp->seq_lock); + /* + * TODO: SNDCTL_SEQ_TESTMIDI now means "can I write to the + * device?". + */ + mtx_unlock(&scp->seq_lock); + break; +#if 0 + case SNDCTL_SEQ_GETINCOUNT: + if (mode == O_WRONLY) + *(int *)arg = 0; + else { + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->in_q.rl; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(printf("seq_ioctl: incount %d.\n", + *(int *)arg)); + } + ret = 0; + break; + case SNDCTL_SEQ_GETOUTCOUNT: + if (mode == O_RDONLY) + *(int *)arg = 0; + else { + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->out_q.fl; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(printf("seq_ioctl: outcount %d.\n", + *(int *)arg)); + } + ret = 0; + break; +#endif + case SNDCTL_SEQ_CTRLRATE: + if (*(int *)arg != 0) { + ret = EINVAL; + break; + } + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->timerbase; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: ctrlrate %d.\n", *(int *)arg)); + ret = 0; + break; + /* + * TODO: ioctl SNDCTL_SEQ_RESETSAMPLES + */ +#if 0 + case SNDCTL_SEQ_RESETSAMPLES: + mtx_lock(&scp->seq_lock); + ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md); + mtx_unlock(&scp->seq_lock); + if (ret != 0) + break; + ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg, + SND_DEV_MIDIN), cmd, arg, mode, td); + break; +#endif + case SNDCTL_SEQ_NRSYNTHS: + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->midi_number; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: synths %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_SEQ_NRMIDIS: + mtx_lock(&scp->seq_lock); + if (scp->music) + *(int *)arg = 0; + else { + /* + * TODO: count the numbder of devices that can WRITERAW + */ + *(int *)arg = scp->midi_number; + } + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: midis %d.\n", *(int *)arg)); + ret = 0; + break; + /* + * TODO: ioctl SNDCTL_SYNTH_MEMAVL + */ +#if 0 + case SNDCTL_SYNTH_MEMAVL: + mtx_lock(&scp->seq_lock); + ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md); + mtx_unlock(&scp->seq_lock); + if (ret != 0) + break; + ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg, + SND_DEV_MIDIN), cmd, arg, mode, td); + break; +#endif + case SNDCTL_SEQ_OUTOFBAND: + for (ret = 0; ret < EV_SZ; ret++) + event[ret] = (u_char)arg[0]; + + mtx_lock(&scp->seq_lock); + if (scp->music) + ret = seq_processevent(scp, event); + else { + if (seq_convertold(event, newevent) > 0) + ret = seq_processevent(scp, newevent); + else + ret = EINVAL; + } + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_SYNTH_INFO: + synthinfo = (struct synth_info *)arg; + midiunit = synthinfo->device; + mtx_lock(&scp->seq_lock); + if (seq_fetch_mid(scp, midiunit, &md) == 0) { + bzero(synthinfo, sizeof(*synthinfo)); + synthinfo->name[0] = 'f'; + synthinfo->name[1] = 'a'; + synthinfo->name[2] = 'k'; + synthinfo->name[3] = 'e'; + synthinfo->name[4] = 's'; + synthinfo->name[5] = 'y'; + synthinfo->name[6] = 'n'; + synthinfo->name[7] = 't'; + synthinfo->name[8] = 'h'; + synthinfo->device = midiunit; + synthinfo->synth_type = SYNTH_TYPE_MIDI; + synthinfo->capabilities = scp->midi_flags[midiunit]; + ret = 0; + } else + ret = EINVAL; + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_MIDI_INFO: + midiinfo = (struct midi_info *)arg; + midiunit = midiinfo->device; + mtx_lock(&scp->seq_lock); + if (seq_fetch_mid(scp, midiunit, &md) == 0) { + bzero(midiinfo, sizeof(*midiinfo)); + midiinfo->name[0] = 'f'; + midiinfo->name[1] = 'a'; + midiinfo->name[2] = 'k'; + midiinfo->name[3] = 'e'; + midiinfo->name[4] = 'm'; + midiinfo->name[5] = 'i'; + midiinfo->name[6] = 'd'; + midiinfo->name[7] = 'i'; + midiinfo->device = midiunit; + midiinfo->capabilities = scp->midi_flags[midiunit]; + /* + * TODO: What devtype? + */ + midiinfo->dev_type = 0x01; + ret = 0; + } else + ret = EINVAL; + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_SEQ_THRESHOLD: + mtx_lock(&scp->seq_lock); + RANGE(*(int *)arg, 1, MIDIQ_SIZE(scp->out_q) - 1); + scp->out_water = *(int *)arg; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: water %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_MIDI_PRETIME: + tmp = *(int *)arg; + if (tmp < 0) + tmp = 0; + mtx_lock(&scp->seq_lock); + scp->pre_event_timeout = (hz * tmp) / 10; + *(int *)arg = scp->pre_event_timeout; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: pretime %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_FM_4OP_ENABLE: + case SNDCTL_PMGR_IFACE: + case SNDCTL_PMGR_ACCESS: + /* + * Patch manager and fm are ded, ded, ded. + */ + /* fallthrough */ + default: + /* + * TODO: Consider ioctl default case. + * Old code used to + * if ((scp->fflags & O_ACCMODE) == FREAD) { + * ret = EIO; + * break; + * } + * Then pass on the ioctl to device 0 + */ + SEQ_DEBUG(2, + printf("seq_ioctl: unsupported IOCTL %ld.\n", cmd)); + ret = EINVAL; + break; + } + + return ret; +} + +int +seq_poll(struct cdev *i_dev, int events, struct thread *td) +{ + int ret, lim; + struct seq_softc *scp = i_dev->si_drv1; + + SEQ_DEBUG(3, printf("seq_poll: unit %d.\n", scp->unit)); + SEQ_DEBUG(1, printf("seq_poll: unit %d.\n", scp->unit)); + + mtx_lock(&scp->seq_lock); + + ret = 0; + + /* Look up the apropriate queue and select it. */ + if ((events & (POLLOUT | POLLWRNORM)) != 0) { + /* Start playing. */ + scp->playing = 1; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + + lim = scp->out_water; + + if (MIDIQ_AVAIL(scp->out_q) < lim) + /* No enough space, record select. */ + selrecord(td, &scp->out_sel); + else + /* We can write now. */ + ret |= events & (POLLOUT | POLLWRNORM); + } + if ((events & (POLLIN | POLLRDNORM)) != 0) { + /* TODO: Start recording. */ + + /* Find out the boundary. */ + lim = 1; + if (MIDIQ_LEN(scp->in_q) < lim) + /* No data ready, record select. */ + selrecord(td, &scp->in_sel); + else + /* We can read now. */ + ret |= events & (POLLIN | POLLRDNORM); + } + mtx_unlock(&scp->seq_lock); + + return (ret); +} + +#if 0 +static void +sein_qtr(void *p, void /* mididev_info */ *md) +{ + struct seq_softc *scp; + + scp = (struct seq_softc *)p; + + mtx_lock(&scp->seq_lock); + + /* Restart playing if we have the data to output. */ + if (scp->queueout_pending) + seq_callback(scp, SEQ_CB_START | SEQ_CB_WR); + /* Check the midi device if we are reading. */ + if ((scp->flags & SEQ_F_READING) != 0) + seq_midiinput(scp, md); + + mtx_unlock(&scp->seq_lock); +} + +#endif +/* + * seq_convertold + * Was the old playevent. Use this to convert and old + * style /dev/sequencer event to a /dev/music event + */ +static int +seq_convertold(u_char *event, u_char *out) +{ + int used; + u_char dev, chn, note, vel; + + out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = + out[7] = 0; + + dev = 0; + chn = event[1]; + note = event[2]; + vel = event[3]; + + used = 0; + +restart: + /* + * TODO: Debug statement + */ + switch (event[0]) { + case EV_TIMING: + case EV_CHN_VOICE: + case EV_CHN_COMMON: + case EV_SYSEX: + case EV_SEQ_LOCAL: + out[0] = event[0]; + out[1] = event[1]; + out[2] = event[2]; + out[3] = event[3]; + out[4] = event[4]; + out[5] = event[5]; + out[6] = event[6]; + out[7] = event[7]; + used += 8; + break; + case SEQ_NOTEOFF: + out[0] = EV_CHN_VOICE; + out[1] = dev; + out[2] = MIDI_NOTEOFF; + out[3] = chn; + out[4] = note; + out[5] = 255; + used += 4; + break; + + case SEQ_NOTEON: + out[0] = EV_CHN_VOICE; + out[1] = dev; + out[2] = MIDI_NOTEON; + out[3] = chn; + out[4] = note; + out[5] = vel; + used += 4; + break; + + /* + * wait delay = (event[2] << 16) + (event[3] << 8) + event[4] + */ + + case SEQ_PGMCHANGE: + out[0] = EV_CHN_COMMON; + out[1] = dev; + out[2] = MIDI_PGM_CHANGE; + out[3] = chn; + out[4] = note; + out[5] = vel; + used += 4; + break; +/* + out[0] = EV_TIMING; + out[1] = dev; + out[2] = MIDI_PGM_CHANGE; + out[3] = chn; + out[4] = note; + out[5] = vel; + SEQ_DEBUG(4,printf("seq_playevent: synctimer\n")); + break; +*/ + + case SEQ_MIDIPUTC: + SEQ_DEBUG(4, + printf("seq_playevent: put data 0x%02x, unit %d.\n", + event[1], event[2])); + /* + * Pass through to the midi device. + * device = event[2] + * data = event[1] + */ + out[0] = SEQ_MIDIPUTC; + out[1] = dev; + out[2] = chn; + used += 4; + break; +#ifdef notyet + case SEQ_ECHO: + /* + * This isn't handled here yet because I don't know if I can + * just use four bytes events. There might be consequences + * in the _read routing + */ + if (seq_copytoinput(scp, event, 4) == EAGAIN) { + ret = QUEUEFULL; + break; + } + ret = MORE; + break; +#endif + case SEQ_EXTENDED: + switch (event[1]) { + case SEQ_NOTEOFF: + case SEQ_NOTEON: + case SEQ_PGMCHANGE: + event++; + used = 4; + goto restart; + break; + case SEQ_AFTERTOUCH: + /* + * SYNTH_AFTERTOUCH(md, event[3], event[4]) + */ + case SEQ_BALANCE: + /* + * SYNTH_PANNING(md, event[3], (char)event[4]) + */ + case SEQ_CONTROLLER: + /* + * SYNTH_CONTROLLER(md, event[3], event[4], *(short *)&event[5]) + */ + case SEQ_VOLMODE: + /* + * SYNTH_VOLUMEMETHOD(md, event[3]) + */ + default: + SEQ_DEBUG(2, + printf("seq_convertold: SEQ_EXTENDED type %d" + "not handled\n", event[1])); + break; + } + break; + case SEQ_WAIT: + out[0] = EV_TIMING; + out[1] = TMR_WAIT_REL; + out[4] = event[2]; + out[5] = event[3]; + out[6] = event[4]; + + SEQ_DEBUG(5, printf("SEQ_WAIT %d", + event[2] + (event[3] << 8) + (event[4] << 24))); + + used += 4; + break; + + case SEQ_ECHO: + case SEQ_SYNCTIMER: + case SEQ_PRIVATE: + default: + SEQ_DEBUG(2, + printf("seq_convertold: event type %d not handled %d %d %d\n", + event[0], event[1], event[2], event[3])); + break; + } + return used; +} + +/* + * Writting to the sequencer buffer never blocks and drops + * input which cannot be queued + */ +void +seq_copytoinput(struct seq_softc *scp, u_char *event, int len) +{ + + mtx_assert(&scp->seq_lock, MA_OWNED); + + if (MIDIQ_AVAIL(scp->in_q) < len) { + /* + * ENOROOM? EINPUTDROPPED? ETOUGHLUCK? + */ + SEQ_DEBUG(2, printf("seq_copytoinput: queue full\n")); + } else { + MIDIQ_ENQ(scp->in_q, event, len); + selwakeup(&scp->in_sel); + cv_broadcast(&scp->in_cv); + } + +} + +static int +seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event) +{ + int ret, voice; + u_char cmd, chn, note, parm; + + ret = 0; + cmd = event[2]; + chn = event[3]; + note = event[4]; + parm = event[5]; + + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(5, printf("seq_chnvoice: unit %d, dev %d, cmd %s," + " chn %d, note %d, parm %d.\n", scp->unit, event[1], + midi_cmdname(cmd, cmdtab_seqcv), chn, note, parm)); + + voice = SYNTH_ALLOC(md, chn, note); + + mtx_unlock(&scp->seq_lock); + + switch (cmd) { + case MIDI_NOTEON: + if (note < 128 || note == 255) { +#if 0 + if (scp->music && chn == 9) { + /* + * This channel is a percussion. The note + * number is the patch number. + */ + /* + mtx_unlock(&scp->seq_lock); + if (SYNTH_SETINSTR(md, voice, 128 + note) + == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + */ + note = 60; /* Middle C. */ + } +#endif + if (scp->music) { + /* + mtx_unlock(&scp->seq_lock); + if (SYNTH_SETUPVOICE(md, voice, chn) + == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + */ + } + SYNTH_STARTNOTE(md, voice, note, parm); + } + break; + case MIDI_NOTEOFF: + SYNTH_KILLNOTE(md, voice, note, parm); + break; + case MIDI_KEY_PRESSURE: + SYNTH_AFTERTOUCH(md, voice, parm); + break; + default: + ret = 1; + SEQ_DEBUG(2, printf("seq_chnvoice event type %d not handled\n", + event[1])); + break; + } + + mtx_lock(&scp->seq_lock); + return ret; +} + +static int +seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event) +{ + int ret; + u_short w14; + u_char cmd, chn, p1; + + ret = 0; + cmd = event[2]; + chn = event[3]; + p1 = event[4]; + w14 = *(u_short *)&event[6]; + + SEQ_DEBUG(5, printf("seq_chncommon: unit %d, dev %d, cmd %s, chn %d," + " p1 %d, w14 %d.\n", scp->unit, event[1], + midi_cmdname(cmd, cmdtab_seqccmn), chn, p1, w14)); + mtx_unlock(&scp->seq_lock); + switch (cmd) { + case MIDI_PGM_CHANGE: + SEQ_DEBUG(4, printf("seq_chncommon pgmchn chn %d pg %d\n", + chn, p1)); + SYNTH_SETINSTR(md, chn, p1); + break; + case MIDI_CTL_CHANGE: + SEQ_DEBUG(4, printf("seq_chncommon ctlch chn %d pg %d %d\n", + chn, p1, w14)); + SYNTH_CONTROLLER(md, chn, p1, w14); + break; + case MIDI_PITCH_BEND: + if (scp->music) { + /* + * TODO: MIDI_PITCH_BEND + */ +#if 0 + mtx_lock(&md->synth.vc_mtx); + md->synth.chn_info[chn].bender_value = w14; + if (md->midiunit >= 0) { + /* + * Handle all of the notes playing on this + * channel. + */ + key = ((int)chn << 8); + for (i = 0; i < md->synth.alloc.max_voice; i++) + if ((md->synth.alloc.map[i] & 0xff00) == key) { + mtx_unlock(&md->synth.vc_mtx); + mtx_unlock(&scp->seq_lock); + if (md->synth.bender(md, i, w14) == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + } + } else { + mtx_unlock(&md->synth.vc_mtx); + mtx_unlock(&scp->seq_lock); + if (md->synth.bender(md, chn, w14) == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + } +#endif + } else + SYNTH_BENDER(md, chn, w14); + break; + default: + ret = 1; + SEQ_DEBUG(2, + printf("seq_chncommon event type %d not handled.\n", + event[1])); + break; + + } + mtx_lock(&scp->seq_lock); + return ret; +} + +static int +seq_timing(struct seq_softc *scp, u_char *event) +{ + int param; + int ret; + + ret = 0; + param = event[4] + (event[5] << 8) + + (event[6] << 16) + (event[7] << 24); + + SEQ_DEBUG(5, printf("seq_timing: unit %d, cmd %d, param %d.\n", + scp->unit, event[1], param)); + switch (event[1]) { + case TMR_WAIT_REL: + timer_wait(scp, param, 0); + break; + case TMR_WAIT_ABS: + timer_wait(scp, param, 1); + break; + case TMR_START: + timer_start(scp); + cv_broadcast(&scp->reset_cv); + break; + case TMR_STOP: + timer_stop(scp); + /* + * The following cv_broadcast isn't needed since we only + * wait for 0->1 transitions. It probably won't hurt + */ + cv_broadcast(&scp->reset_cv); + break; + case TMR_CONTINUE: + timer_continue(scp); + cv_broadcast(&scp->reset_cv); + break; + case TMR_TEMPO: + if (param < 8) + param = 8; + if (param > 360) + param = 360; + SEQ_DEBUG(4, printf("Timer set tempo %d\n", param)); + timer_setvals(scp, param, scp->timerbase); + break; + case TMR_TIMERBASE: + if (param < 1) + param = 1; + if (param > 1000) + param = 1000; + SEQ_DEBUG(4, printf("Timer set timerbase %d\n", param)); + timer_setvals(scp, scp->tempo, param); + break; + case TMR_ECHO: + /* + * TODO: Consider making 4-byte events for /dev/sequencer + * PRO: Maybe needed by legacy apps + * CON: soundcard.h has been warning for a while many years + * to expect 8 byte events. + */ +#if 0 + if (scp->music) + seq_copytoinput(scp, event, 8); + else { + param = (param << 8 | SEQ_ECHO); + seq_copytoinput(scp, (u_char *)¶m, 4); + } +#else + seq_copytoinput(scp, event, 8); +#endif + break; + default: + SEQ_DEBUG(2, printf("seq_timing event type %d not handled.\n", + event[1])); + ret = 1; + break; + } + return ret; +} + +static int +seq_local(struct seq_softc *scp, u_char *event) +{ + int ret; + + ret = 0; + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(5, printf("seq_local: unit %d, cmd %d\n", scp->unit, + event[1])); + switch (event[1]) { + default: + SEQ_DEBUG(1, printf("seq_local event type %d not handled\n", + event[1])); + ret = 1; + break; + } + return ret; +} + +static int +seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event) +{ + int i, l; + + mtx_assert(&scp->seq_lock, MA_OWNED); + SEQ_DEBUG(5, printf("seq_sysex: unit %d device %d\n", scp->unit, + event[1])); + l = 0; + for (i = 0; i < 6 && event[i + 2] != 0xff; i++) + l = i + 1; + if (l > 0) { + mtx_unlock(&scp->seq_lock); + if (SYNTH_SENDSYSEX(md, &event[2], l) == EAGAIN) { + mtx_lock(&scp->seq_lock); + return 1; + } + mtx_lock(&scp->seq_lock); + } + return 0; +} + +/* + * Reset no longer closes the raw devices nor seq_sync's + * Callers are IOCTL and seq_close + */ +static void +seq_reset(struct seq_softc *scp) +{ + int chn, i; + kobj_t m; + + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(5, printf("seq_reset: unit %d.\n", scp->unit)); + + /* + * Stop reading and writing. + */ + + /* scp->recording = 0; */ + scp->playing = 0; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->reset_cv); + + /* + * For now, don't reset the timers. + */ + MIDIQ_CLEAR(scp->in_q); + MIDIQ_CLEAR(scp->out_q); + + for (i = 0; i < scp->midi_number; i++) { + m = scp->midis[i]; + mtx_unlock(&scp->seq_lock); + SYNTH_RESET(m); + for (chn = 0; chn < 16; chn++) { + SYNTH_CONTROLLER(m, chn, 123, 0); + SYNTH_CONTROLLER(m, chn, 121, 0); + SYNTH_BENDER(m, chn, 1 << 13); + } + mtx_lock(&scp->seq_lock); + } +} + +/* + * seq_sync + * *really* flush the output queue + * flush the event queue, then flush the synthsisers. + * Callers are IOCTL and close + */ + +#define SEQ_SYNC_TIMEOUT 8 +static int +seq_sync(struct seq_softc *scp) +{ + int i, rl, sync[16], done; + + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(4, printf("seq_sync: unit %d.\n", scp->unit)); + + /* + * Wait until output queue is empty. Check every so often to see if + * the queue is moving along. If it isn't just abort. + */ + while (!MIDIQ_EMPTY(scp->out_q)) { + + if (!scp->playing) { + scp->playing = 1; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + } + rl = MIDIQ_LEN(scp->out_q); + + i = cv_timedwait_sig(&scp->out_cv, + &scp->seq_lock, SEQ_SYNC_TIMEOUT * hz); + + if (i == EINTR || i == ERESTART) { + if (i == EINTR) { + /* + * XXX: I don't know why we stop playing + */ + scp->playing = 0; + cv_broadcast(&scp->out_cv); + } + return i; + } + if (i == EWOULDBLOCK && rl == MIDIQ_LEN(scp->out_q) && + scp->waiting == 0) { + /* + * A queue seems to be stuck up. Give up and clear + * queues. + */ + MIDIQ_CLEAR(scp->out_q); + scp->playing = 0; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->reset_cv); + + /* + * TODO: Consider if the raw devices need to be flushed + */ + + SEQ_DEBUG(1, printf("seq_sync queue stuck, aborting\n")); + + return i; + } + } + + scp->playing = 0; + /* + * Since syncing a midi device might block, unlock scp->seq_lock. + */ + + mtx_unlock(&scp->seq_lock); + for (i = 0; i < scp->midi_number; i++) + sync[i] = 1; + + do { + done = 1; + for (i = 0; i < scp->midi_number; i++) + if (sync[i]) { + if (SYNTH_INSYNC(scp->midis[i]) == 0) + sync[i] = 0; + else + done = 0; + } + if (!done) + DELAY(5000); + + } while (!done); + + mtx_lock(&scp->seq_lock); + return 0; +} + +char * +midi_cmdname(int cmd, midi_cmdtab *tab) +{ + while (tab->name != NULL) { + if (cmd == tab->cmd) + return (tab->name); + tab++; + } + + return ("unknown"); +} --- sys/dev/sound/midi/sequencer.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/sequencer.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * Copyright (c) 1999 Seigo Tanimura + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/midi/sequencer.h,v 1.9 2007/02/25 13:51:52 netchild Exp $ + */ + +/* + * Include file for the midi sequence driver. + */ + +#ifndef _SEQUENCER_H_ +#define _SEQUENCER_H_ + + +#define NSEQ_MAX 16 + +/* + * many variables should be reduced to a range. Here define a macro + */ + +#define RANGE(var, low, high) (var) = \ +((var)<(low)?(low) : (var)>(high)?(high) : (var)) + +#ifdef _KERNEL + +void seq_timer(void *arg); + +SYSCTL_DECL(_hw_midi_seq); + +extern int seq_debug; + +#define SEQ_DEBUG(y, x) \ + do { \ + if (seq_debug >= y) { \ + (x); \ + } \ + } while(0) + +SYSCTL_DECL(_hw_midi); + +#endif /* _KERNEL */ + +#define SYNTHPROP_MIDI 1 +#define SYNTHPROP_SYNTH 2 +#define SYNTHPROP_RX 4 +#define SYNTHPROP_TX 8 + +struct _midi_cmdtab { + int cmd; + char *name; +}; +typedef struct _midi_cmdtab midi_cmdtab; +extern midi_cmdtab cmdtab_seqevent[]; +extern midi_cmdtab cmdtab_seqioctl[]; +extern midi_cmdtab cmdtab_timer[]; +extern midi_cmdtab cmdtab_seqcv[]; +extern midi_cmdtab cmdtab_seqccmn[]; + +char *midi_cmdname(int cmd, midi_cmdtab * tab); + +enum { + MORE, + TIMERARMED, + QUEUEFULL +}; + +#endif --- sys/dev/sound/midi/synth_if.m.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/midi/synth_if.m Thu Jul 12 12:04:19 2007 @@ -0,0 +1,313 @@ +#- +# Copyright (c) 2003 Mathew Kanner +# 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +# +# $FreeBSD: src/sys/dev/sound/midi/synth_if.m,v 1.3 2007/02/25 13:51:52 netchild Exp $ +# + +INTERFACE synth; + +#include + +CODE { + +synth_killnote_t nokillnote; +synth_startnote_t nostartnote; +synth_setinstr_t nosetinstr; +synth_hwcontrol_t nohwcontrol; +synth_aftertouch_t noaftertouch; +synth_panning_t nopanning; +synth_controller_t nocontroller; +synth_volumemethod_t novolumemethod; +synth_bender_t nobender; +synth_setupvoice_t nosetupvoice; +synth_sendsysex_t nosendsysex; +synth_allocvoice_t noallocvoice; +synth_writeraw_t nowriteraw; +synth_reset_t noreset; +synth_shortname_t noshortname; +synth_open_t noopen; +synth_close_t noclose; +synth_query_t noquery; +synth_insync_t noinsync; +synth_alloc_t noalloc; + + int + nokillnote(void *_kobj, uint8_t _chn, uint8_t _note, uint8_t _vel) + { + printf("nokillnote\n"); + return 0; + } + + int + noopen(void *_kobj, void *_arg, int mode) + { + printf("noopen\n"); + return 0; + } + + int + noquery(void *_kboj) + { + printf("noquery\n"); + return 0; + } + + int + nostartnote(void *_kb, uint8_t _voice, uint8_t _note, uint8_t _parm) + { + printf("nostartnote\n"); + return 0; + } + + int + nosetinstr(void *_kb, uint8_t _chn, uint16_t _patchno) + { + printf("nosetinstr\n"); + return 0; + } + + int + nohwcontrol(void *_kb, uint8_t *_event) + { + printf("nohwcontrol\n"); + return 0; + } + + int + noaftertouch ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2) + { + printf("noaftertouch\n"); + return 0; + } + + int + nopanning ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2) + { + printf("nopanning\n"); + return 0; + } + + int + nocontroller ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2, uint16_t _x3) + { + printf("nocontroller\n"); + return 0; + } + + int + novolumemethod ( + void /* X */ * _kobj, + uint8_t _x1) + { + printf("novolumemethod\n"); + return 0; + } + + int + nobender ( void /* X */ * _kobj, uint8_t _voice, uint16_t _bend) + { + printf("nobender\n"); + return 0; + } + + int + nosetupvoice ( void /* X */ * _kobj, uint8_t _voice, uint8_t _chn) + { + + printf("nosetupvoice\n"); + return 0; + } + + int + nosendsysex ( void /* X */ * _kobj, void * _buf, size_t _len) + { + printf("nosendsysex\n"); + return 0; + } + + int + noallocvoice ( void /* X */ * _kobj, uint8_t _chn, uint8_t _note, void *_x) + { + printf("noallocvoice\n"); + return 0; + } + + int + nowriteraw ( void /* X */ * _kobjt, uint8_t * _buf, size_t _len) + { + printf("nowriteraw\n"); + return 1; + } + + int + noreset ( void /* X */ * _kobjt) + { + + printf("noreset\n"); + return 0; + } + + char * + noshortname (void /* X */ * _kobjt) + { + printf("noshortname\n"); + return "noshortname"; + } + + int + noclose ( void /* X */ * _kobjt) + { + + printf("noclose\n"); + return 0; + } + + int + noinsync (void /* X */ * _kobjt) + { + + printf("noinsync\n"); + return 0; + } + + int + noalloc ( void /* x */ * _kbojt, uint8_t _chn, uint8_t _note) + { + printf("noalloc\n"); + return 0; + } +} + +METHOD int killnote { + void /* X */ *_kobj; + uint8_t _chan; + uint8_t _note; + uint8_t _vel; +} DEFAULT nokillnote; + +METHOD int startnote { + void /* X */ *_kobj; + uint8_t _voice; + uint8_t _note; + uint8_t _parm; +} DEFAULT nostartnote; + +METHOD int setinstr { + void /* X */ *_kobj; + uint8_t _chn; + uint16_t _patchno; +} DEFAULT nosetinstr; + +METHOD int hwcontrol { + void /* X */ *_kobj; + uint8_t *_event; +} DEFAULT nohwcontrol; + +METHOD int aftertouch { + void /* X */ *_kobj; + uint8_t _x1; + uint8_t _x2; +} DEFAULT noaftertouch; + +METHOD int panning { + void /* X */ *_kobj; + uint8_t _x1; + uint8_t _x2; +} DEFAULT nopanning; + +METHOD int controller { + void /* X */ *_kobj; + uint8_t _x1; + uint8_t _x2; + uint16_t _x3; +} DEFAULT nocontroller; + +METHOD int volumemethod { + void /* X */ *_kobj; + uint8_t _x1; +} DEFAULT novolumemethod; + +METHOD int bender { + void /* X */ *_kobj; + uint8_t _voice; + uint16_t _bend; +} DEFAULT nobender; + +METHOD int setupvoice { + void /* X */ *_kobj; + uint8_t _voice; + uint8_t _chn; +} DEFAULT nosetupvoice; + +METHOD int sendsysex { + void /* X */ *_kobj; + void *_buf; + size_t _len; +} DEFAULT nosendsysex; + +METHOD int allocvoice { + void /* X */ *_kobj; + uint8_t _chn; + uint8_t _note; + void *_x; +} DEFAULT noallocvoice; + +METHOD int writeraw { + void /* X */ *_kobjt; + uint8_t *_buf; + size_t _len; +} DEFAULT nowriteraw; + +METHOD int reset { + void /* X */ *_kobjt; +} DEFAULT noreset; + +METHOD char * shortname { + void /* X */ *_kobjt; +} DEFAULT noshortname; + +METHOD int open { + void /* X */ *_kobjt; + void *_sythn; + int _mode; +} DEFAULT noopen; + +METHOD int close { + void /* X */ *_kobjt; +} DEFAULT noclose; + +METHOD int query { + void /* X */ *_kobjt; +} DEFAULT noquery; + +METHOD int insync { + void /* X */ *_kobjt; +} DEFAULT noinsync; + +METHOD int alloc { + void /* x */ *_kbojt; + uint8_t _chn; + uint8_t _note; +} DEFAULT noalloc; --- sys/dev/sound/null.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/null.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,610 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +#define SNDNULL_DESC "NULL Audio" + +#define SNDNULL_RATE_MIN 4000 +#define SNDNULL_RATE_MAX 192000 + +#define SNDNULL_RATE_DEFAULT 48000 +#define SNDNULL_FMT_DEFAULT (AFMT_STEREO | AFMT_S16_LE) +#define SNDNULL_FMTSTR_DEFAULT "s16le" + +#define SNDNULL_NPCHAN 1 +#define SNDNULL_NRCHAN 1 +#define SNDNULL_MAXCHAN (SNDNULL_NPCHAN + SNDNULL_NRCHAN) + +#define SNDNULL_BUFSZ_MIN 4096 +#define SNDNULL_BUFSZ_MAX 65536 +#define SNDNULL_BUFSZ_DEFAULT 4096 + +#define SNDNULL_BLKCNT_MIN 2 +#define SNDNULL_BLKCNT_MAX 512 +#define SNDNULL_BLKCNT_DEFAULT SNDNULL_BLKCNT_MIN + +#define SNDNULL_LOCK(sc) snd_mtxlock((sc)->lock) +#define SNDNULL_UNLOCK(sc) snd_mtxunlock((sc)->lock) + +struct sndnull_info; + +struct sndnull_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct pcmchan_caps *caps; + struct sndnull_info *parent; + uint32_t ptr, intrcnt; + int dir, active; +}; + +struct sndnull_info { + device_t dev; + struct sndnull_chinfo ch[SNDNULL_MAXCHAN]; + struct pcmchan_caps caps; + uint32_t bufsz; + uint32_t blkcnt; + uint32_t fmtlist[2]; + struct mtx *lock; + uint8_t *ringbuffer; + int chnum; + + struct callout poll_timer; + int poll_ticks, polling; +}; + +static void * +sndnull_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct sndnull_info *sc = devinfo; + struct sndnull_chinfo *ch; + + SNDNULL_LOCK(sc); + + ch = &sc->ch[sc->chnum++]; + ch->buffer = b; + ch->parent = sc; + ch->channel = c; + ch->dir = dir; + ch->caps = &sc->caps; + + SNDNULL_UNLOCK(sc); + + if (sndbuf_setup(ch->buffer, sc->ringbuffer, sc->bufsz) == -1) + return (NULL); + + return (ch); +} + +static int +sndnull_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct sndnull_chinfo *ch = data; + + if (ch->caps->fmtlist[0] != format) + return (-1); + + return (0); +} + +static int +sndnull_chan_setspeed(kobj_t obj, void *data, uint32_t spd) +{ + struct sndnull_chinfo *ch = data; + + if (spd < ch->caps->minspeed) + spd = ch->caps->minspeed; + if (spd > ch->caps->maxspeed) + spd = ch->caps->maxspeed; + + return (spd); +} + +static int +sndnull_chan_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + + blkcnt = sc->blkcnt; + blksz = sndbuf_getmaxsize(ch->buffer) / blkcnt; + blksz -= blksz % sndbuf_getbps(ch->buffer); + + if ((sndbuf_getblksz(ch->buffer) != blksz || + sndbuf_getblkcnt(ch->buffer) != blkcnt) && + sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + return (1); +} + +static int +sndnull_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + + sndnull_chan_setfragments(obj, data, blksz, sc->blkcnt); + + return (sndbuf_getblksz(ch->buffer)); +} + +#define SNDNULL_CHAN_ACTIVE(ch) ((ch)->active != 0) + +static __inline int +sndnull_anychan_active(struct sndnull_info *sc) +{ + int i; + + for (i = 0; i < sc->chnum; i++) { + if (SNDNULL_CHAN_ACTIVE(&sc->ch[i])) + return (1); + } + + return (0); +} + +static void +sndnull_poll_callback(void *arg) +{ + struct sndnull_info *sc = arg; + struct sndnull_chinfo *ch; + int i; + + if (sc == NULL) + return; + + SNDNULL_LOCK(sc); + + if (!sndnull_anychan_active(sc)) { + SNDNULL_UNLOCK(sc); + return; + } + + for (i = 0; i < sc->chnum; i++) { + ch = &sc->ch[i]; + if (SNDNULL_CHAN_ACTIVE(ch)) { + ch->ptr += sndbuf_getblksz(ch->buffer); + ch->intrcnt += 1; + SNDNULL_UNLOCK(sc); + chn_intr(ch->channel); + SNDNULL_LOCK(sc); + } + } + + callout_reset(&sc->poll_timer, sc->poll_ticks, + sndnull_poll_callback, sc); + + SNDNULL_UNLOCK(sc); +} + +static int +sndnull_chan_trigger(kobj_t obj, void *data, int go) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + int pollticks; + + if (!PCMTRIG_COMMON(go)) + return (0); + + SNDNULL_LOCK(sc); + + switch (go) { + case PCMTRIG_START: + if (!sndnull_anychan_active(sc)) { + pollticks = ((uint64_t)hz * + sndbuf_getblksz(ch->buffer)) / + ((uint64_t)sndbuf_getbps(ch->buffer) * + sndbuf_getspd(ch->buffer)); + if (pollticks < 1) + pollticks = 1; + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_timer, 1, + sndnull_poll_callback, sc); + if (bootverbose) + device_printf(sc->dev, + "PCMTRIG_START: pollticks=%d\n", + pollticks); + } + if (ch->dir == PCMDIR_REC) + memset(sc->ringbuffer, sndbuf_zerodata( + sndbuf_getfmt(ch->buffer)), + sndbuf_getmaxsize(ch->buffer)); + ch->ptr = 0; + ch->intrcnt = 0; + ch->active = 1; + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + ch->active = 0; + if (!sndnull_anychan_active(sc)) + callout_stop(&sc->poll_timer); + if (ch->dir == PCMDIR_PLAY) + memset(sc->ringbuffer, sndbuf_zerodata( + sndbuf_getfmt(ch->buffer)), + sndbuf_getmaxsize(ch->buffer)); + break; + default: + break; + } + + SNDNULL_UNLOCK(sc); + + return (0); +} + +static int +sndnull_chan_getptr(kobj_t obj, void *data) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + uint32_t ptr; + + SNDNULL_LOCK(sc); + ptr = (SNDNULL_CHAN_ACTIVE(ch)) ? ch->ptr : 0; + SNDNULL_UNLOCK(sc); + + return (ptr); +} + +static struct pcmchan_caps * +sndnull_chan_getcaps(kobj_t obj, void *data) +{ + return (((struct sndnull_chinfo *)data)->caps); +} + +static kobj_method_t sndnull_chan_methods[] = { + KOBJMETHOD(channel_init, sndnull_chan_init), + KOBJMETHOD(channel_setformat, sndnull_chan_setformat), + KOBJMETHOD(channel_setspeed, sndnull_chan_setspeed), + KOBJMETHOD(channel_setblocksize, sndnull_chan_setblocksize), + KOBJMETHOD(channel_setfragments, sndnull_chan_setfragments), + KOBJMETHOD(channel_trigger, sndnull_chan_trigger), + KOBJMETHOD(channel_getptr, sndnull_chan_getptr), + KOBJMETHOD(channel_getcaps, sndnull_chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(sndnull_chan); + +static const struct { + int ctl; + int rec; +} sndnull_mixer_ctls[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 1, 0 }, + [SOUND_MIXER_BASS] = { 1, 0 }, + [SOUND_MIXER_TREBLE] = { 1, 0 }, + [SOUND_MIXER_SYNTH] = { 1, 1 }, + [SOUND_MIXER_PCM] = { 1, 1 }, + [SOUND_MIXER_SPEAKER] = { 1, 0 }, + [SOUND_MIXER_LINE] = { 1, 1 }, + [SOUND_MIXER_MIC] = { 1, 1 }, + [SOUND_MIXER_CD] = { 1, 1 }, + [SOUND_MIXER_IMIX] = { 1, 1 }, + [SOUND_MIXER_RECLEV] = { 1, 0 }, +}; + +static int +sndnull_mixer_init(struct snd_mixer *m) +{ + uint32_t mask, recmask; + int i; + + mask = 0; + recmask = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (sndnull_mixer_ctls[i].ctl != 0) + mask |= 1 << i; + if (sndnull_mixer_ctls[i].rec != 0) + recmask |= 1 << i; + } + + mix_setdevs(m, mask); + mix_setrecdevs(m, recmask); + + return (0); +} + +static int +sndnull_mixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + if (!(dev < SOUND_MIXER_NRDEVICES && sndnull_mixer_ctls[dev].ctl != 0)) + return (-1); + + return (left | (right << 8)); +} + +static int +sndnull_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + uint32_t recsrc; + int i; + + recsrc = src; + + if (recsrc & SOUND_MASK_IMIX) + recsrc &= SOUND_MASK_IMIX; + else { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (sndnull_mixer_ctls[i].rec == 0) + recsrc &= ~(1 << i); + } + } + + return (recsrc); +} + +static kobj_method_t sndnull_mixer_methods[] = { + KOBJMETHOD(mixer_init, sndnull_mixer_init), + KOBJMETHOD(mixer_set, sndnull_mixer_set), + KOBJMETHOD(mixer_setrecsrc, sndnull_mixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(sndnull_mixer); + +static int +sysctl_sndnull_rate(SYSCTL_HANDLER_ARGS) +{ + struct sndnull_info *sc; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + + SNDNULL_LOCK(sc); + val = sc->caps.maxspeed; + SNDNULL_UNLOCK(sc); + + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL) + return (err); + + if (val < SNDNULL_RATE_MIN) + val = SNDNULL_RATE_MIN; + if (val > SNDNULL_RATE_MAX) + val = SNDNULL_RATE_MAX; + + SNDNULL_LOCK(sc); + if (sndnull_anychan_active(sc)) + err = EBUSY; + else { + sc->caps.minspeed = (uint32_t)val; + sc->caps.maxspeed = sc->caps.minspeed; + } + SNDNULL_UNLOCK(sc); + + return (err); +} + +static int +sysctl_sndnull_format(SYSCTL_HANDLER_ARGS) +{ + struct sndnull_info *sc; + device_t dev; + int err; + char fmtstr[AFMTSTR_MAXSZ]; + uint32_t fmt; + + dev = oidp->oid_arg1; + + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + + SNDNULL_LOCK(sc); + fmt = sc->fmtlist[0]; + if (fmt != afmt2afmtstr(NULL, fmt, fmtstr, sizeof(fmtstr), + AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) + strlcpy(fmtstr, SNDNULL_FMTSTR_DEFAULT, sizeof(fmtstr)); + SNDNULL_UNLOCK(sc); + + err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); + + if (err != 0 || req->newptr == NULL) + return (err); + + fmt = afmtstr2afmt(NULL, fmtstr, AFMTSTR_STEREO_RETURN); + if (fmt == 0) + return (EINVAL); + + SNDNULL_LOCK(sc); + if (fmt != sc->fmtlist[0]) { + if (sndnull_anychan_active(sc)) + err = EBUSY; + else + sc->fmtlist[0] = fmt; + } + SNDNULL_UNLOCK(sc); + + return (err); +} + +static device_t sndnull_dev = NULL; + +static void +sndnull_dev_identify(driver_t *driver, device_t parent) +{ + if (sndnull_dev == NULL) + sndnull_dev = BUS_ADD_CHILD(parent, 0, "pcm", -1); +} + +static int +sndnull_dev_probe(device_t dev) +{ + if (dev != NULL && dev == sndnull_dev) { + device_set_desc(dev, SNDNULL_DESC); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +sndnull_dev_attach(device_t dev) +{ + struct sndnull_info *sc; + char status[SND_STATUSLEN]; + int i; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_null softc"); + sc->dev = dev; + + callout_init(&sc->poll_timer, CALLOUT_MPSAFE); + sc->poll_ticks = 1; + + sc->caps.minspeed = SNDNULL_RATE_DEFAULT; + sc->caps.maxspeed = SNDNULL_RATE_DEFAULT; + sc->fmtlist[0] = SNDNULL_FMT_DEFAULT; + sc->fmtlist[1] = 0; + sc->caps.fmtlist = sc->fmtlist; + + sc->bufsz = pcm_getbuffersize(dev, SNDNULL_BUFSZ_MIN, + SNDNULL_BUFSZ_DEFAULT, SNDNULL_BUFSZ_MAX); + sc->blkcnt = SNDNULL_BLKCNT_DEFAULT; + + sc->ringbuffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO); + + if (mixer_init(dev, &sndnull_mixer_class, sc) != 0) + device_printf(dev, "mixer_init() failed\n"); + + if (pcm_register(dev, sc, SNDNULL_NPCHAN, SNDNULL_NRCHAN)) + return (ENXIO); + + for (i = 0; i < SNDNULL_NPCHAN; i++) + pcm_addchan(dev, PCMDIR_PLAY, &sndnull_chan_class, sc); + for (i = 0; i < SNDNULL_NRCHAN; i++) + pcm_addchan(dev, PCMDIR_REC, &sndnull_chan_class, sc); + + snprintf(status, SND_STATUSLEN, "at %s %s", + device_get_nameunit(device_get_parent(dev)), + PCM_KLDSTRING(snd_null)); + + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + pcm_setstatus(dev, status); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rate", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_sndnull_rate, "I", "runtime rate"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "format", CTLTYPE_STRING | CTLFLAG_RW, dev, sizeof(dev), + sysctl_sndnull_format, "A", "runtime format"); + + return (0); +} + +static void +sndnull_release_resources(struct sndnull_info *sc) +{ + if (sc == NULL) + return; + if (sc->chnum != 0) { + SNDNULL_LOCK(sc); + callout_stop(&sc->poll_timer); + SNDNULL_UNLOCK(sc); + callout_drain(&sc->poll_timer); + } + if (sc->ringbuffer != NULL) { + free(sc->ringbuffer, M_DEVBUF); + sc->ringbuffer = NULL; + } + if (sc->lock != NULL) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } + free(sc, M_DEVBUF); +} + +static int +sndnull_dev_detach(device_t dev) +{ + struct sndnull_info *sc; + int err; + + sc = pcm_getdevinfo(dev); + if (sc != NULL) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + sndnull_release_resources(sc); + } + + return (0); +} + +static device_method_t sndnull_methods[] = { + DEVMETHOD(device_identify, sndnull_dev_identify), + DEVMETHOD(device_probe, sndnull_dev_probe), + DEVMETHOD(device_attach, sndnull_dev_attach), + DEVMETHOD(device_detach, sndnull_dev_detach), + { 0, 0 } +}; + +static driver_t sndnull_driver = { + "pcm", + sndnull_methods, + PCM_SOFTC_SIZE, +}; + +static int +sndnull_modevent(module_t mod, int type, void *data) +{ + switch (type) { + case MOD_UNLOAD: + if (sndnull_dev != NULL) + device_delete_child(device_get_parent(sndnull_dev), + sndnull_dev); + sndnull_dev = NULL; + case MOD_LOAD: + return (0); + break; + default: + break; + } + + return (EOPNOTSUPP); +} + +DRIVER_MODULE(snd_null, nexus, sndnull_driver, pcm_devclass, sndnull_modevent, + 0); +MODULE_DEPEND(snd_null, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_null, 1); --- sys/dev/sound/pci/als4000.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/als4000.c Thu Jul 12 12:04:19 2007 @@ -42,7 +42,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/als4000.c,v 1.18.2.1 2005/12/30 19:55:53 netchild Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/als4000.c,v 1.27 2007/06/17 06:10:41 ariff Exp $"); /* Debugging macro's */ #undef DEB @@ -221,7 +221,7 @@ ch->buffer = b; snd_mtxunlock(sc->lock); - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) return NULL; return ch; @@ -391,14 +391,20 @@ struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; + if (!PCMTRIG_COMMON(go)) + return 0; + snd_mtxlock(sc->lock); switch(go) { case PCMTRIG_START: als_playback_start(ch); break; + case PCMTRIG_STOP: case PCMTRIG_ABORT: als_playback_stop(ch); break; + default: + break; } snd_mtxunlock(sc->lock); return 0; @@ -489,6 +495,7 @@ case PCMTRIG_START: als_capture_start(ch); break; + case PCMTRIG_STOP: case PCMTRIG_ABORT: als_capture_stop(ch); break; @@ -773,7 +780,7 @@ sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536); - if (bus_dma_tag_create(/*parent*/NULL, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, @@ -798,12 +805,8 @@ u_int32_t data; char status[SND_STATUSLEN]; - if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_als4000 softc"); sc->dev = dev; data = pci_read_config(dev, PCIR_COMMAND, 2); --- sys/dev/sound/pci/atiixp.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/atiixp.c Thu Jul 12 12:04:19 2007 @@ -63,7 +63,19 @@ #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.2.2.7 2007/04/26 08:21:44 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.19 2007/07/09 20:41:23 ariff Exp $"); + +#define ATI_IXP_DMA_RETRY_MAX 100 + +#define ATI_IXP_BUFSZ_MIN 4096 +#define ATI_IXP_BUFSZ_MAX 65536 +#define ATI_IXP_BUFSZ_DEFAULT 16384 + +#define ATI_IXP_BLK_MIN 32 +#define ATI_IXP_BLK_ALIGN (~(ATI_IXP_BLK_MIN - 1)) + +#define ATI_IXP_CHN_RUNNING 0x00000001 +#define ATI_IXP_CHN_SUSPEND 0x00000002 struct atiixp_dma_op { volatile uint32_t addr; @@ -80,10 +92,12 @@ struct atiixp_info *parent; struct atiixp_dma_op *sgd_table; bus_addr_t sgd_addr; - uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit; - uint32_t dma_segs; + uint32_t enable_bit, flush_bit, linkptr_bit, dt_cur_bit; + uint32_t blksz, blkcnt; + uint32_t ptr, prevptr; uint32_t fmt; - int caps_32bit, dir, active; + uint32_t flags; + int caps_32bit, dir; }; struct atiixp_info { @@ -108,10 +122,12 @@ uint32_t bufsz; uint32_t codec_not_ready_bits, codec_idx, codec_found; - uint32_t dma_segs; + uint32_t blkcnt; int registered_channels; struct mtx *lock; + struct callout poll_timer; + int poll_ticks, polling; }; #define atiixp_rd(_sc, _reg) \ @@ -141,7 +157,7 @@ }; static struct pcmchan_caps atiixp_caps = { - ATI_IXP_BASE_RATE, + ATI_IXP_BASE_RATE, ATI_IXP_BASE_RATE, atiixp_fmt, 0 }; @@ -159,9 +175,9 @@ static void atiixp_enable_interrupts(struct atiixp_info *); static void atiixp_disable_interrupts(struct atiixp_info *); static void atiixp_reset_aclink(struct atiixp_info *); -static void atiixp_flush_dma(struct atiixp_info *, struct atiixp_chinfo *); -static void atiixp_enable_dma(struct atiixp_info *, struct atiixp_chinfo *); -static void atiixp_disable_dma(struct atiixp_info *, struct atiixp_chinfo *); +static void atiixp_flush_dma(struct atiixp_chinfo *); +static void atiixp_enable_dma(struct atiixp_chinfo *); +static void atiixp_disable_dma(struct atiixp_chinfo *); static int atiixp_waitready_codec(struct atiixp_info *); static int atiixp_rdcd(kobj_t, void *, int); @@ -171,9 +187,11 @@ struct pcm_channel *, int); static int atiixp_chan_setformat(kobj_t, void *, uint32_t); static int atiixp_chan_setspeed(kobj_t, void *, uint32_t); +static int atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t); static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t); static void atiixp_buildsgdt(struct atiixp_chinfo *); static int atiixp_chan_trigger(kobj_t, void *, int); +static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *); static int atiixp_chan_getptr(kobj_t, void *); static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *); @@ -208,7 +226,10 @@ * Disable / ignore internal xrun/spdf interrupt flags * since it doesn't interest us (for now). */ -#if 0 +#if 1 + value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN | + ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN); +#else value |= ATI_REG_IER_IN_XRUN_EN; value |= ATI_REG_IER_OUT_XRUN_EN; @@ -243,7 +264,7 @@ /* have to wait at least 10 usec for it to initialise */ DELAY(20); - }; + } /* perform a soft reset */ value = atiixp_rd(sc, ATI_REG_CMD); @@ -262,8 +283,7 @@ /* check if the ac-link is working; reset device otherwise */ timeout = 10; value = atiixp_rd(sc, ATI_REG_CMD); - while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) - && --timeout) { + while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) && --timeout) { #if 0 device_printf(sc->dev, "not up; resetting aclink hardware\n"); #endif @@ -284,7 +304,7 @@ /* check if its active now */ value = atiixp_rd(sc, ATI_REG_CMD); - }; + } if (timeout == 0) device_printf(sc->dev, "giving up aclink reset\n"); @@ -300,32 +320,32 @@ } static void -atiixp_flush_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +atiixp_flush_dma(struct atiixp_chinfo *ch) { - atiixp_wr(sc, ATI_REG_FIFO_FLUSH, ch->flush_bit); + atiixp_wr(ch->parent, ATI_REG_FIFO_FLUSH, ch->flush_bit); } static void -atiixp_enable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +atiixp_enable_dma(struct atiixp_chinfo *ch) { uint32_t value; - value = atiixp_rd(sc, ATI_REG_CMD); + value = atiixp_rd(ch->parent, ATI_REG_CMD); if (!(value & ch->enable_bit)) { value |= ch->enable_bit; - atiixp_wr(sc, ATI_REG_CMD, value); + atiixp_wr(ch->parent, ATI_REG_CMD, value); } } -static void -atiixp_disable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +static void +atiixp_disable_dma(struct atiixp_chinfo *ch) { uint32_t value; - value = atiixp_rd(sc, ATI_REG_CMD); + value = atiixp_rd(ch->parent, ATI_REG_CMD); if (value & ch->enable_bit) { value &= ~ch->enable_bit; - atiixp_wr(sc, ATI_REG_CMD, value); + atiixp_wr(ch->parent, ATI_REG_CMD, value); } } @@ -339,12 +359,12 @@ do { if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) & - ATI_REG_PHYS_OUT_ADDR_EN) == 0) - return 0; + ATI_REG_PHYS_OUT_ADDR_EN) == 0) + return (0); DELAY(1); - } while (timeout--); + } while (--timeout); - return -1; + return (-1); } static int @@ -355,29 +375,28 @@ int timeout; if (atiixp_waitready_codec(sc)) - return -1; + return (-1); data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | - ATI_REG_PHYS_OUT_ADDR_EN | - ATI_REG_PHYS_OUT_RW | sc->codec_idx; + ATI_REG_PHYS_OUT_ADDR_EN | ATI_REG_PHYS_OUT_RW | sc->codec_idx; atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data); if (atiixp_waitready_codec(sc)) - return -1; + return (-1); timeout = 500; do { data = atiixp_rd(sc, ATI_REG_PHYS_IN_ADDR); if (data & ATI_REG_PHYS_IN_READ_FLAG) - return data >> ATI_REG_PHYS_IN_DATA_SHIFT; + return (data >> ATI_REG_PHYS_IN_DATA_SHIFT); DELAY(1); - } while (timeout--); + } while (--timeout); if (reg < 0x7c) device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg); - return -1; + return (-1); } static int @@ -386,20 +405,20 @@ struct atiixp_info *sc = devinfo; if (atiixp_waitready_codec(sc)) - return -1; + return (-1); data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) | - (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) | - ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx; + (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) | + ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx; atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data); - return 0; + return (0); } static kobj_method_t atiixp_ac97_methods[] = { - KOBJMETHOD(ac97_read, atiixp_rdcd), - KOBJMETHOD(ac97_write, atiixp_wrcd), + KOBJMETHOD(ac97_read, atiixp_rdcd), + KOBJMETHOD(ac97_write, atiixp_wrcd), { 0, 0 } }; AC97_DECLARE(atiixp_ac97); @@ -422,15 +441,15 @@ ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR; ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN; ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH; - ch->dma_dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR; + ch->dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR; /* Native 32bit playback working properly */ ch->caps_32bit = 1; } else { ch = &sc->rch; ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR; - ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN; + ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN; ch->flush_bit = ATI_REG_FIFO_IN_FLUSH; - ch->dma_dt_cur_bit = ATI_REG_IN_DMA_DT_CUR; + ch->dt_cur_bit = ATI_REG_IN_DMA_DT_CUR; /* XXX Native 32bit recording appear to be broken */ ch->caps_32bit = 1; } @@ -439,22 +458,23 @@ ch->parent = sc; ch->channel = c; ch->dir = dir; - ch->dma_segs = sc->dma_segs; + ch->blkcnt = sc->blkcnt; + ch->blksz = sc->bufsz / ch->blkcnt; atiixp_unlock(sc); - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1) - return NULL; + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) == -1) + return (NULL); atiixp_lock(sc); num = sc->registered_channels++; - ch->sgd_table = &sc->sgd_table[num * ch->dma_segs]; - ch->sgd_addr = sc->sgd_addr + - (num * ch->dma_segs * sizeof(struct atiixp_dma_op)); - atiixp_disable_dma(sc, ch); + ch->sgd_table = &sc->sgd_table[num * ATI_IXP_DMA_CHSEGS_MAX]; + ch->sgd_addr = sc->sgd_addr + (num * ATI_IXP_DMA_CHSEGS_MAX * + sizeof(struct atiixp_dma_op)); + atiixp_disable_dma(ch); atiixp_unlock(sc); - return ch; + return (ch); } static int @@ -476,7 +496,7 @@ value &= ~ATI_REG_OUT_DMA_SLOT_MASK; /* We do not have support for more than 2 channels, _yet_. */ value |= ATI_REG_OUT_DMA_SLOT_BIT(3) | - ATI_REG_OUT_DMA_SLOT_BIT(4); + ATI_REG_OUT_DMA_SLOT_BIT(4); value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT; atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value); value = atiixp_rd(sc, ATI_REG_CMD); @@ -491,83 +511,302 @@ ch->fmt = format; atiixp_unlock(sc); - return 0; + return (0); } static int atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd) { /* XXX We're supposed to do VRA/DRA processing right here */ - return ATI_IXP_BASE_RATE; + return (ATI_IXP_BASE_RATE); } static int -atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +atiixp_chan_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) { struct atiixp_chinfo *ch = data; struct atiixp_info *sc = ch->parent; - if (blksz > (sc->bufsz / ch->dma_segs)) - blksz = sc->bufsz / ch->dma_segs; + blksz &= ATI_IXP_BLK_ALIGN; + + if (blksz > (sndbuf_getmaxsize(ch->buffer) / ATI_IXP_DMA_CHSEGS_MIN)) + blksz = sndbuf_getmaxsize(ch->buffer) / ATI_IXP_DMA_CHSEGS_MIN; + if (blksz < ATI_IXP_BLK_MIN) + blksz = ATI_IXP_BLK_MIN; + if (blkcnt > ATI_IXP_DMA_CHSEGS_MAX) + blkcnt = ATI_IXP_DMA_CHSEGS_MAX; + if (blkcnt < ATI_IXP_DMA_CHSEGS_MIN) + blkcnt = ATI_IXP_DMA_CHSEGS_MIN; + + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) { + if ((blkcnt >> 1) >= ATI_IXP_DMA_CHSEGS_MIN) + blkcnt >>= 1; + else if ((blksz >> 1) >= ATI_IXP_BLK_MIN) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->buffer) != blksz || + sndbuf_getblkcnt(ch->buffer) != blkcnt) && + sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); - sndbuf_resize(ch->buffer, ch->dma_segs, blksz); + ch->blksz = sndbuf_getblksz(ch->buffer); + ch->blkcnt = sndbuf_getblkcnt(ch->buffer); - return sndbuf_getblksz(ch->buffer); + return (1); +} + +static int +atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct atiixp_chinfo *ch = data; + struct atiixp_info *sc = ch->parent; + + atiixp_chan_setfragments(obj, data, blksz, sc->blkcnt); + + return (ch->blksz); } static void atiixp_buildsgdt(struct atiixp_chinfo *ch) { - uint32_t addr, blksz; + struct atiixp_info *sc = ch->parent; + uint32_t addr, blksz, blkcnt; int i; addr = sndbuf_getbufaddr(ch->buffer); - blksz = sndbuf_getblksz(ch->buffer); - for (i = 0; i < ch->dma_segs; i++) { + if (sc->polling != 0) { + blksz = ch->blksz * ch->blkcnt; + blkcnt = 1; + } else { + blksz = ch->blksz; + blkcnt = ch->blkcnt; + } + + for (i = 0; i < blkcnt; i++) { ch->sgd_table[i].addr = htole32(addr + (i * blksz)); ch->sgd_table[i].status = htole16(0); ch->sgd_table[i].size = htole16(blksz >> 2); - ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr + - (((i + 1) % ch->dma_segs) * - sizeof(struct atiixp_dma_op))); + ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr + + (((i + 1) % blkcnt) * sizeof(struct atiixp_dma_op))); } } +static __inline uint32_t +atiixp_dmapos(struct atiixp_chinfo *ch) +{ + struct atiixp_info *sc = ch->parent; + uint32_t reg, addr, sz, retry; + volatile uint32_t ptr; + + reg = ch->dt_cur_bit; + addr = sndbuf_getbufaddr(ch->buffer); + sz = ch->blkcnt * ch->blksz; + retry = ATI_IXP_DMA_RETRY_MAX; + + do { + ptr = atiixp_rd(sc, reg); + if (ptr < addr) + continue; + ptr -= addr; + if (ptr < sz) { +#if 0 +#ifdef ATI_IXP_DEBUG + if ((ptr & ~(ch->blksz - 1)) != ch->ptr) { + uint32_t delta; + + delta = (sz + ptr - ch->prevptr) % sz; +#ifndef ATI_IXP_DEBUG_VERBOSE + if (delta < ch->blksz) +#endif + device_printf(sc->dev, + "PCMDIR_%s: incoherent DMA " + "prevptr=%u ptr=%u " + "ptr=%u blkcnt=%u " + "[delta=%u != blksz=%u] " + "(%s)\n", + (ch->dir == PCMDIR_PLAY) ? + "PLAY" : "REC", + ch->prevptr, ptr, + ch->ptr, ch->blkcnt, + delta, ch->blksz, + (delta < ch->blksz) ? + "OVERLAPPED!" : "Ok"); + ch->ptr = ptr & ~(ch->blksz - 1); + } + ch->prevptr = ptr; +#endif +#endif + return (ptr); + } + } while (--retry); + + device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr); + + return (0); +} + +static __inline int +atiixp_poll_channel(struct atiixp_chinfo *ch) +{ + uint32_t sz, delta; + volatile uint32_t ptr; + + if (!(ch->flags & ATI_IXP_CHN_RUNNING)) + return (0); + + sz = ch->blksz * ch->blkcnt; + ptr = atiixp_dmapos(ch); + ch->ptr = ptr; + ptr %= sz; + ptr &= ~(ch->blksz - 1); + delta = (sz + ptr - ch->prevptr) % sz; + + if (delta < ch->blksz) + return (0); + + ch->prevptr = ptr; + + return (1); +} + +#define atiixp_chan_active(sc) (((sc)->pch.flags | (sc)->rch.flags) & \ + ATI_IXP_CHN_RUNNING) + +static void +atiixp_poll_callback(void *arg) +{ + struct atiixp_info *sc = arg; + uint32_t trigger = 0; + + if (sc == NULL) + return; + + atiixp_lock(sc); + if (sc->polling == 0 || atiixp_chan_active(sc) == 0) { + atiixp_unlock(sc); + return; + } + + trigger |= (atiixp_poll_channel(&sc->pch) != 0) ? 1 : 0; + trigger |= (atiixp_poll_channel(&sc->rch) != 0) ? 2 : 0; + + /* XXX */ + callout_reset(&sc->poll_timer, 1/*sc->poll_ticks*/, + atiixp_poll_callback, sc); + + atiixp_unlock(sc); + + if (trigger & 1) + chn_intr(sc->pch.channel); + if (trigger & 2) + chn_intr(sc->rch.channel); +} + static int atiixp_chan_trigger(kobj_t obj, void *data, int go) { struct atiixp_chinfo *ch = data; struct atiixp_info *sc = ch->parent; uint32_t value; + int pollticks; + + if (!PCMTRIG_COMMON(go)) + return (0); atiixp_lock(sc); switch (go) { - case PCMTRIG_START: - atiixp_flush_dma(sc, ch); - atiixp_buildsgdt(ch); - atiixp_wr(sc, ch->linkptr_bit, 0); - atiixp_enable_dma(sc, ch); - atiixp_wr(sc, ch->linkptr_bit, - (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN); - break; - case PCMTRIG_STOP: - case PCMTRIG_ABORT: - atiixp_disable_dma(sc, ch); - atiixp_flush_dma(sc, ch); - break; - default: - atiixp_unlock(sc); - return 0; - break; + case PCMTRIG_START: + atiixp_flush_dma(ch); + atiixp_buildsgdt(ch); + atiixp_wr(sc, ch->linkptr_bit, 0); + atiixp_enable_dma(ch); + atiixp_wr(sc, ch->linkptr_bit, + (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN); + if (sc->polling != 0) { + ch->ptr = 0; + ch->prevptr = 0; + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->buffer) * + sndbuf_getspd(ch->buffer)); + pollticks >>= 2; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) + pollticks = 1; + if (atiixp_chan_active(sc) == 0 || + pollticks < sc->poll_ticks) { + if (bootverbose) { + if (atiixp_chan_active(sc) == 0) + device_printf(sc->dev, + "%s: pollticks=%d\n", + __func__, pollticks); + else + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, + pollticks); + } + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_timer, 1, + atiixp_poll_callback, sc); + } + } + ch->flags |= ATI_IXP_CHN_RUNNING; + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + atiixp_disable_dma(ch); + atiixp_flush_dma(ch); + ch->flags &= ~ATI_IXP_CHN_RUNNING; + if (sc->polling != 0) { + if (atiixp_chan_active(sc) == 0) { + callout_stop(&sc->poll_timer); + sc->poll_ticks = 1; + } else { + if (sc->pch.flags & ATI_IXP_CHN_RUNNING) + ch = &sc->pch; + else + ch = &sc->rch; + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->buffer) * + sndbuf_getspd(ch->buffer)); + pollticks >>= 2; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) + pollticks = 1; + if (pollticks > sc->poll_ticks) { + if (bootverbose) + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, + pollticks); + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_timer, + 1, atiixp_poll_callback, + sc); + } + } + } + break; + default: + atiixp_unlock(sc); + return (0); + break; } /* Update bus busy status */ value = atiixp_rd(sc, ATI_REG_IER); - if (atiixp_rd(sc, ATI_REG_CMD) & ( - ATI_REG_CMD_SEND_EN | ATI_REG_CMD_RECEIVE_EN | - ATI_REG_CMD_SPDF_OUT_EN)) + if (atiixp_rd(sc, ATI_REG_CMD) & (ATI_REG_CMD_SEND_EN | + ATI_REG_CMD_RECEIVE_EN | ATI_REG_CMD_SPDF_OUT_EN)) value |= ATI_REG_IER_SET_BUS_BUSY; else value &= ~ATI_REG_IER_SET_BUS_BUSY; @@ -575,7 +814,7 @@ atiixp_unlock(sc); - return 0; + return (0); } static int @@ -583,36 +822,16 @@ { struct atiixp_chinfo *ch = data; struct atiixp_info *sc = ch->parent; - uint32_t addr, align, retry, sz; - volatile uint32_t ptr; - - addr = sndbuf_getbufaddr(ch->buffer); - align = (ch->fmt & AFMT_32BIT) ? 7 : 3; - retry = 100; - sz = sndbuf_getblksz(ch->buffer) * ch->dma_segs; + uint32_t ptr; atiixp_lock(sc); - do { - ptr = atiixp_rd(sc, ch->dma_dt_cur_bit); - if (ptr < addr) - continue; - ptr -= addr; - if (ptr < sz && !(ptr & align)) - break; - } while (--retry); + if (sc->polling != 0) + ptr = ch->ptr; + else + ptr = atiixp_dmapos(ch); atiixp_unlock(sc); -#if 0 - if (retry != 100) { - device_printf(sc->dev, - "%saligned hwptr: dir=PCMDIR_%s ptr=%u fmt=0x%08x retry=%d\n", - (ptr & align) ? "un" : "", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr, - ch->fmt, 100 - retry); - } -#endif - - return (retry > 0) ? ptr : 0; + return (ptr); } static struct pcmchan_caps * @@ -621,8 +840,8 @@ struct atiixp_chinfo *ch = data; if (ch->caps_32bit) - return &atiixp_caps_32bit; - return &atiixp_caps; + return (&atiixp_caps_32bit); + return (&atiixp_caps); } static kobj_method_t atiixp_chan_methods[] = { @@ -630,6 +849,7 @@ KOBJMETHOD(channel_setformat, atiixp_chan_setformat), KOBJMETHOD(channel_setspeed, atiixp_chan_setspeed), KOBJMETHOD(channel_setblocksize, atiixp_chan_setblocksize), + KOBJMETHOD(channel_setfragments, atiixp_chan_setfragments), KOBJMETHOD(channel_trigger, atiixp_chan_trigger), KOBJMETHOD(channel_getptr, atiixp_chan_getptr), KOBJMETHOD(channel_getcaps, atiixp_chan_getcaps), @@ -645,8 +865,13 @@ { struct atiixp_info *sc = p; uint32_t status, enable, detected_codecs; + uint32_t trigger = 0; atiixp_lock(sc); + if (sc->polling != 0) { + atiixp_unlock(sc); + return; + } status = atiixp_rd(sc, ATI_REG_ISR); if (status == 0) { @@ -654,16 +879,12 @@ return; } - if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) { - atiixp_unlock(sc); - chn_intr(sc->rch.channel); - atiixp_lock(sc); - } - if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) { - atiixp_unlock(sc); - chn_intr(sc->pch.channel); - atiixp_lock(sc); - } + if ((status & ATI_REG_ISR_OUT_STATUS) && + (sc->pch.flags & ATI_IXP_CHN_RUNNING)) + trigger |= 1; + if ((status & ATI_REG_ISR_IN_STATUS) && + (sc->rch.flags & ATI_IXP_CHN_RUNNING)) + trigger |= 2; #if 0 if (status & ATI_REG_ISR_IN_XRUN) { @@ -685,11 +906,17 @@ enable = atiixp_rd(sc, ATI_REG_IER); enable &= ~detected_codecs; atiixp_wr(sc, ATI_REG_IER, enable); + wakeup(sc); } /* acknowledge */ atiixp_wr(sc, ATI_REG_ISR, status); atiixp_unlock(sc); + + if (trigger & 1) + chn_intr(sc->pch.channel); + if (trigger & 2) + chn_intr(sc->rch.channel); } static void @@ -712,7 +939,7 @@ /* clear all DMA enables (preserving rest of settings) */ value = atiixp_rd(sc, ATI_REG_CMD); value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN | - ATI_REG_CMD_SPDF_OUT_EN ); + ATI_REG_CMD_SPDF_OUT_EN ); atiixp_wr(sc, ATI_REG_CMD, value); /* reset aclink */ @@ -726,12 +953,54 @@ atiixp_unlock(sc); } +#ifdef SND_DYNSYSCTL +static int +sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS) +{ + struct atiixp_info *sc; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + atiixp_lock(sc); + val = sc->polling; + atiixp_unlock(sc); + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err || req->newptr == NULL) + return (err); + if (val < 0 || val > 1) + return (EINVAL); + + atiixp_lock(sc); + if (val != sc->polling) { + if (atiixp_chan_active(sc) != 0) + err = EBUSY; + else if (val == 0) { + atiixp_enable_interrupts(sc); + sc->polling = 0; + DELAY(1000); + } else { + atiixp_disable_interrupts(sc); + sc->polling = 1; + DELAY(1000); + } + } + atiixp_unlock(sc); + + return (err); +} +#endif + static void atiixp_chip_post_init(void *arg) { struct atiixp_info *sc = (struct atiixp_info *)arg; uint32_t subdev; - int i, timeout, found; + int i, timeout, found, polling; char status[SND_STATUSLEN]; atiixp_lock(sc); @@ -741,17 +1010,23 @@ sc->delayed_attach.ich_func = NULL; } - /* wait for the interrupts to happen */ - timeout = 100; - while (--timeout) { - msleep(sc, sc->lock, PWAIT, "ixpslp", 1); - if (sc->codec_not_ready_bits) - break; + polling = sc->polling; + sc->polling = 0; + + timeout = 10; + if (sc->codec_not_ready_bits == 0) { + /* wait for the interrupts to happen */ + do { + msleep(sc, sc->lock, PWAIT, "ixpslp", max(hz / 10, 1)); + if (sc->codec_not_ready_bits != 0) + break; + } while (--timeout); } + sc->polling = polling; atiixp_disable_interrupts(sc); - if (timeout == 0) { + if (sc->codec_not_ready_bits == 0 && timeout == 0) { device_printf(sc->dev, "WARNING: timeout during codec detection; " "codecs might be present but haven't interrupted\n"); @@ -765,22 +1040,19 @@ * ATI IXP can have upto 3 codecs, but single codec should be * suffice for now. */ - if (!(sc->codec_not_ready_bits & - ATI_REG_ISR_CODEC0_NOT_READY)) { + if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC0_NOT_READY)) { /* codec 0 present */ sc->codec_found++; sc->codec_idx = 0; found++; } - if (!(sc->codec_not_ready_bits & - ATI_REG_ISR_CODEC1_NOT_READY)) { + if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC1_NOT_READY)) { /* codec 1 present */ sc->codec_found++; } - if (!(sc->codec_not_ready_bits & - ATI_REG_ISR_CODEC2_NOT_READY)) { + if (!(sc->codec_not_ready_bits & ATI_REG_ISR_CODEC2_NOT_READY)) { /* codec 2 present */ sc->codec_found++; } @@ -795,11 +1067,13 @@ if (sc->codec == NULL) goto postinitbad; - subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev); + subdev = (pci_get_subdevice(sc->dev) << 16) | + pci_get_subvendor(sc->dev); switch (subdev) { case 0x11831043: /* ASUS A6R */ case 0x2043161f: /* Maxselect x710s - http://maxselect.ru/ */ - ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV); + ac97_setflags(sc->codec, ac97_getflags(sc->codec) | + AC97_F_EAPD_INV); break; default: break; @@ -815,14 +1089,22 @@ for (i = 0; i < ATI_IXP_NRCHAN; i++) pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc); - snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", - rman_get_start(sc->reg), rman_get_start(sc->irq), - PCM_KLDSTRING(snd_atiixp)); +#ifdef SND_DYNSYSCTL + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_atiixp_polling, "I", "Enable polling mode"); +#endif + + snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", + rman_get_start(sc->reg), rman_get_start(sc->irq), + PCM_KLDSTRING(snd_atiixp)); pcm_setstatus(sc->dev, status); atiixp_lock(sc); - atiixp_enable_interrupts(sc); + if (sc->polling == 0) + atiixp_enable_interrupts(sc); atiixp_unlock(sc); return; @@ -836,6 +1118,13 @@ { if (sc == NULL) return; + if (sc->registered_channels != 0) { + atiixp_lock(sc); + sc->polling = 0; + callout_stop(&sc->poll_timer); + atiixp_unlock(sc); + callout_drain(&sc->poll_timer); + } if (sc->codec) { ac97_destroy(sc->codec); sc->codec = NULL; @@ -871,6 +1160,7 @@ snd_mtxfree(sc->lock); sc->lock = NULL; } + free(sc, M_DEVBUF); } static int @@ -881,15 +1171,15 @@ vendor = pci_get_vendor(dev); devid = pci_get_device(dev); - for (i = 0; i < sizeof(atiixp_hw)/sizeof(atiixp_hw[0]); i++) { + for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) { if (vendor == atiixp_hw[i].vendor && - devid == atiixp_hw[i].devid) { + devid == atiixp_hw[i].devid) { device_set_desc(dev, atiixp_hw[i].desc); - return BUS_PROBE_DEFAULT; + return (BUS_PROBE_DEFAULT); } } - return ENXIO; + return (ENXIO); } static int @@ -898,25 +1188,26 @@ struct atiixp_info *sc; int i; - if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_atiixp softc"); sc->dev = dev; - /* - * Default DMA segments per playback / recording channel - */ - sc->dma_segs = ATI_IXP_DMA_CHSEGS; + + callout_init(&sc->poll_timer, CALLOUT_MPSAFE); + sc->poll_ticks = 1; + + if (resource_int_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "polling", &i) == 0 && i != 0) + sc->polling = 1; + else + sc->polling = 0; pci_set_powerstate(dev, PCI_POWERSTATE_D0); pci_enable_busmaster(dev); sc->regid = PCIR_BAR(0); sc->regtype = SYS_RES_MEMORY; - sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid, - RF_ACTIVE); + sc->reg = bus_alloc_resource_any(dev, sc->regtype, + &sc->regid, RF_ACTIVE); if (!sc->reg) { device_printf(dev, "unable to allocate register space\n"); @@ -926,14 +1217,14 @@ sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); - sc->bufsz = pcm_getbuffersize(dev, 4096, ATI_IXP_DEFAULT_BUFSZ, 65536); + sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN, + ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX); sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, - RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || - snd_setup_intr(dev, sc->irq, INTR_MPSAFE, - atiixp_intr, sc, &sc->ih)) { + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, + atiixp_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -941,32 +1232,29 @@ /* * Let the user choose the best DMA segments. */ - if (resource_int_value(device_get_name(dev), - device_get_unit(dev), "dma_segs", - &i) == 0) { - if (i < ATI_IXP_DMA_CHSEGS_MIN) - i = ATI_IXP_DMA_CHSEGS_MIN; - if (i > ATI_IXP_DMA_CHSEGS_MAX) - i = ATI_IXP_DMA_CHSEGS_MAX; - sc->dma_segs = i; - } + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + i &= ATI_IXP_BLK_ALIGN; + if (i < ATI_IXP_BLK_MIN) + i = ATI_IXP_BLK_MIN; + sc->blkcnt = sc->bufsz / i; + i = 0; + while (sc->blkcnt >> i) + i++; + sc->blkcnt = 1 << (i - 1); + if (sc->blkcnt < ATI_IXP_DMA_CHSEGS_MIN) + sc->blkcnt = ATI_IXP_DMA_CHSEGS_MIN; + else if (sc->blkcnt > ATI_IXP_DMA_CHSEGS_MAX) + sc->blkcnt = ATI_IXP_DMA_CHSEGS_MAX; - /* - * round the value to the nearest ^2 - */ - i = 0; - while (sc->dma_segs >> i) - i++; - sc->dma_segs = 1 << (i - 1); - if (sc->dma_segs < ATI_IXP_DMA_CHSEGS_MIN) - sc->dma_segs = ATI_IXP_DMA_CHSEGS_MIN; - else if (sc->dma_segs > ATI_IXP_DMA_CHSEGS_MAX) - sc->dma_segs = ATI_IXP_DMA_CHSEGS_MAX; + } else + sc->blkcnt = ATI_IXP_DMA_CHSEGS; /* * DMA tag for scatter-gather buffers and link pointers */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -977,12 +1265,13 @@ goto bad; } - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/sc->dma_segs * ATI_IXP_NCHANS * - sizeof(struct atiixp_dma_op), + /*maxsize*/ATI_IXP_DMA_CHSEGS_MAX * ATI_IXP_NCHANS * + sizeof(struct atiixp_dma_op), /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, /*lockfunc*/NULL, /*lockarg*/NULL, &sc->sgd_dmat) != 0) { @@ -990,14 +1279,13 @@ goto bad; } - if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table, - BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1) + if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table, + BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1) goto bad; - if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table, - sc->dma_segs * ATI_IXP_NCHANS * - sizeof(struct atiixp_dma_op), - atiixp_dma_cb, sc, 0)) + if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table, + ATI_IXP_DMA_CHSEGS_MAX * ATI_IXP_NCHANS * + sizeof(struct atiixp_dma_op), atiixp_dma_cb, sc, 0)) goto bad; @@ -1006,16 +1294,16 @@ sc->delayed_attach.ich_func = atiixp_chip_post_init; sc->delayed_attach.ich_arg = sc; if (cold == 0 || - config_intrhook_establish(&sc->delayed_attach) != 0) { + config_intrhook_establish(&sc->delayed_attach) != 0) { sc->delayed_attach.ich_func = NULL; atiixp_chip_post_init(sc); } - return 0; + return (0); bad: atiixp_release_resource(sc); - return ENXIO; + return (ENXIO); } static int @@ -1029,14 +1317,14 @@ if (sc->codec != NULL) { r = pcm_unregister(dev); if (r) - return r; + return (r); } sc->codec = NULL; - atiixp_disable_interrupts(sc); + if (sc->st != 0 && sc->sh != 0) + atiixp_disable_interrupts(sc); atiixp_release_resource(sc); - free(sc, M_DEVBUF); } - return 0; + return (0); } static int @@ -1048,16 +1336,17 @@ /* quickly disable interrupts and save channels active state */ atiixp_lock(sc); atiixp_disable_interrupts(sc); - value = atiixp_rd(sc, ATI_REG_CMD); - sc->pch.active = (value & ATI_REG_CMD_SEND_EN) ? 1 : 0; - sc->rch.active = (value & ATI_REG_CMD_RECEIVE_EN) ? 1 : 0; atiixp_unlock(sc); /* stop everything */ - if (sc->pch.channel && sc->pch.active) + if (sc->pch.flags & ATI_IXP_CHN_RUNNING) { atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP); - if (sc->rch.channel && sc->rch.active) + sc->pch.flags |= ATI_IXP_CHN_SUSPEND; + } + if (sc->rch.flags & ATI_IXP_CHN_RUNNING) { atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP); + sc->rch.flags |= ATI_IXP_CHN_SUSPEND; + } /* power down aclink and pci bus */ atiixp_lock(sc); @@ -1067,7 +1356,7 @@ pci_set_powerstate(dev, PCI_POWERSTATE_D3); atiixp_unlock(sc); - return 0; + return (0); } static int @@ -1086,32 +1375,37 @@ if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); - return ENXIO; + return (ENXIO); } /* * Resume channel activities. Reset channel format regardless * of its previous state. */ - if (sc->pch.channel) { - if (sc->pch.fmt) + if (sc->pch.channel != NULL) { + if (sc->pch.fmt != 0) atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt); - if (sc->pch.active) + if (sc->pch.flags & ATI_IXP_CHN_SUSPEND) { + sc->pch.flags &= ~ATI_IXP_CHN_SUSPEND; atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START); + } } - if (sc->rch.channel) { - if (sc->rch.fmt) + if (sc->rch.channel != NULL) { + if (sc->rch.fmt != 0) atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt); - if (sc->rch.active) + if (sc->rch.flags & ATI_IXP_CHN_SUSPEND) { + sc->rch.flags &= ~ATI_IXP_CHN_SUSPEND; atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START); + } } /* enable interrupts */ atiixp_lock(sc); - atiixp_enable_interrupts(sc); + if (sc->polling == 0) + atiixp_enable_interrupts(sc); atiixp_unlock(sc); - return 0; + return (0); } static device_method_t atiixp_methods[] = { --- sys/dev/sound/pci/atiixp.h.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/atiixp.h Thu Jul 12 12:04:19 2007 @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/atiixp.h,v 1.1.2.2 2006/03/02 00:09:30 ariff Exp $ + * $FreeBSD: src/sys/dev/sound/pci/atiixp.h,v 1.3 2006/10/01 13:30:30 ariff Exp $ */ #ifndef _ATIIXP_H_ @@ -46,9 +46,8 @@ #define ATI_IXP_DMA_CHSEGS_MIN 2 #define ATI_IXP_DMA_CHSEGS_MAX 256 -#define ATI_IXP_DEFAULT_BUFSZ (1 << 13) /* 8192 */ - #define ATI_VENDOR_ID 0x1002 /* ATI Technologies */ + #define ATI_IXP_200_ID 0x4341 #define ATI_IXP_300_ID 0x4361 #define ATI_IXP_400_ID 0x4370 --- sys/dev/sound/pci/au88x0.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/au88x0.c Thu Jul 12 12:04:19 2007 @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/au88x0.c,v 1.10 2005/03/01 08:58:05 imp Exp $ + * $FreeBSD: src/sys/dev/sound/pci/au88x0.c,v 1.13 2007/06/17 06:10:41 ariff Exp $ */ #include @@ -332,7 +332,7 @@ struct au88x0_info *aui = arg; struct au88x0_chan_info *auci = au88x0_channel(aui, dir); - if (sndbuf_alloc(buf, aui->aui_dmat, aui->aui_bufsize) != 0) + if (sndbuf_alloc(buf, aui->aui_dmat, 0, aui->aui_bufsize) != 0) return (NULL); auci->auci_aui = aui; auci->auci_pcmchan = chan; @@ -572,10 +572,7 @@ uint32_t config; int error; - if ((aui = malloc(sizeof *aui, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) { - device_printf(dev, "failed to allocate softc\n"); - return (ENXIO); - } + aui = malloc(sizeof(*aui), M_DEVBUF, M_WAITOK | M_ZERO); aui->aui_dev = dev; /* Model-specific parameters */ @@ -636,7 +633,7 @@ /* DMA mapping */ aui->aui_bufsize = pcm_getbuffersize(dev, AU88X0_BUFSIZE_MIN, AU88X0_BUFSIZE_DFLT, AU88X0_BUFSIZE_MAX); - error = bus_dma_tag_create(NULL, + error = bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0, /* 16-bit alignment, no boundary */ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* restrict to 4GB */ NULL, NULL, /* no filter */ --- sys/dev/sound/pci/aureal.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/aureal.c Thu Jul 12 12:04:19 2007 @@ -31,7 +31,7 @@ #include #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/aureal.c,v 1.32 2005/03/01 08:58:05 imp Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/aureal.c,v 1.36 2007/06/17 06:10:41 ariff Exp $"); /* PCI IDs of supported chips */ #define AU8820_PCI_ID 0x000112eb @@ -303,7 +303,7 @@ ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, au->parent_dmat, AU_BUFFSIZE) != 0) + if (sndbuf_alloc(ch->buffer, au->parent_dmat, 0, AU_BUFFSIZE) != 0) return NULL; return ch; } @@ -339,12 +339,12 @@ struct au_chinfo *ch = data; struct au_info *au = ch->parent; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; if (ch->dir == PCMDIR_PLAY) { au_setadb(au, 0x11, (go)? 1 : 0); - if (!go) { + if (go != PCMTRIG_START) { au_wr(au, 0, 0xf800, 0, 4); au_wr(au, 0, 0xf804, 0, 4); au_delroute(au, 0x58); @@ -558,11 +558,7 @@ struct ac97_info *codec; char status[SND_STATUSLEN]; - if ((au = malloc(sizeof(*au), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - + au = malloc(sizeof(*au), M_DEVBUF, M_WAITOK | M_ZERO); au->unit = device_get_unit(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); @@ -637,7 +633,8 @@ if (codec == NULL) goto bad; if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/pci/cmi.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/cmi.c Thu Jul 12 12:04:19 2007 @@ -48,10 +48,12 @@ #include #include +#include #include "mixer_if.h" +#include "mpufoi_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/cmi.c,v 1.32.2.2 2006/01/24 18:54:22 joel Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/cmi.c,v 1.44 2007/06/17 06:10:41 ariff Exp $"); /* Supported chip ID's */ #define CMI8338A_PCI_ID 0x010013f6 @@ -112,6 +114,13 @@ int spdif_enabled; unsigned int bufsz; struct sc_chinfo pch, rch; + + struct mpu401 *mpu; + mpu401_intr_t *mpu_intr; + struct resource *mpu_reg; + int mpu_regid; + bus_space_tag_t mpu_bt; + bus_space_handle_t mpu_bh; }; /* Channel caps */ @@ -340,7 +349,7 @@ ch->spd = DSP_DEFAULT_SPEED; ch->buffer = b; ch->dma_active = 0; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) { + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) { DEB(printf("cmichan_init failed\n")); return NULL; } @@ -466,12 +475,16 @@ struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; + if (!PCMTRIG_COMMON(go)) + return 0; + snd_mtxlock(sc->lock); if (ch->dir == PCMDIR_PLAY) { switch(go) { case PCMTRIG_START: cmi_ch0_start(sc, ch); break; + case PCMTRIG_STOP: case PCMTRIG_ABORT: cmi_ch0_stop(sc, ch); break; @@ -481,6 +494,7 @@ case PCMTRIG_START: cmi_ch1_start(sc, ch); break; + case PCMTRIG_STOP: case PCMTRIG_ABORT: cmi_ch1_stop(sc, ch); break; @@ -551,6 +565,9 @@ } } + if(sc->mpu_intr) { + (sc->mpu_intr)(sc->mpu); + } snd_mtxunlock(sc->lock); return; } @@ -729,8 +746,13 @@ cmi_initsys(struct sc_info* sc) { #ifdef SND_DYNSYSCTL - SYSCTL_ADD_INT(snd_sysctl_tree(sc->dev), - SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)), + /* XXX: an user should be able to set this with a control tool, + if not done before 7.0-RELEASE, this needs to be converted + to a device specific sysctl "dev.pcm.X.yyy" via + device_get_sysctl_*() as discussed on multimedia@ in msg-id + <861wujij2q.fsf@xps.des.no> */ + SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "spdif_enabled", CTLFLAG_RW, &sc->spdif_enabled, 0, "enable SPDIF output at 44.1 kHz and above"); @@ -747,6 +769,74 @@ }; MIXER_DECLARE(cmi_mixer); +/* + * mpu401 functions + */ + +static unsigned char +cmi_mread(void *arg, struct sc_info *sc, int reg) +{ + unsigned int d; + + d = bus_space_read_1(0,0, 0x330 + reg); + /* printf("cmi_mread: reg %x %x\n",reg, d); + */ + return d; +} + +static void +cmi_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b) +{ + + bus_space_write_1(0,0,0x330 + reg , b); +} + +static int +cmi_muninit(void *arg, struct sc_info *sc) +{ + + snd_mtxlock(sc->lock); + sc->mpu_intr = 0; + sc->mpu = 0; + snd_mtxunlock(sc->lock); + + return 0; +} + +static kobj_method_t cmi_mpu_methods[] = { + KOBJMETHOD(mpufoi_read, cmi_mread), + KOBJMETHOD(mpufoi_write, cmi_mwrite), + KOBJMETHOD(mpufoi_uninit, cmi_muninit), + { 0, 0 } +}; + +static DEFINE_CLASS(cmi_mpu, cmi_mpu_methods, 0); + +static void +cmi_midiattach(struct sc_info *sc) { +/* + const struct { + int port,bits; + } *p, ports[] = { + {0x330,0}, + {0x320,1}, + {0x310,2}, + {0x300,3}, + {0,0} } ; + Notes, CMPCI_REG_VMPUSEL sets the io port for the mpu. Does + anyone know how to bus_space tag? +*/ + cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE); + cmi_clr4(sc, CMPCI_REG_LEGACY_CTRL, + CMPCI_REG_VMPUSEL_MASK << CMPCI_REG_VMPUSEL_SHIFT); + cmi_set4(sc, CMPCI_REG_LEGACY_CTRL, + 0 << CMPCI_REG_VMPUSEL_SHIFT ); + cmi_set4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE); + sc->mpu = mpu401_init(&cmi_mpu_class, sc, cmi_intr, &sc->mpu_intr); +} + + + /* ------------------------------------------------------------------------- */ /* Power and reset */ @@ -802,6 +892,10 @@ CMPCI_REG_TDMA_INTR_ENABLE); cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE); + cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE); + + if( sc->mpu ) + sc->mpu_intr = 0; } /* ------------------------------------------------------------------------- */ @@ -834,13 +928,8 @@ u_int32_t data; char status[SND_STATUSLEN]; - sc = malloc(sizeof(struct sc_info), M_DEVBUF, M_NOWAIT | M_ZERO); - if (sc == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_cmi softc"); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); @@ -857,6 +946,9 @@ sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); + if (0) + cmi_midiattach(sc); + sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE | RF_SHAREABLE); @@ -868,7 +960,8 @@ sc->bufsz = pcm_getbuffersize(dev, 4096, CMI_DEFAULT_BUFSZ, 65536); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -936,7 +1029,12 @@ bus_dma_tag_destroy(sc->parent_dmat); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + if(sc->mpu) + mpu401_uninit(sc->mpu); bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg); + if (sc->mpu_reg) + bus_release_resource(dev, SYS_RES_IOPORT, sc->mpu_regid, sc->mpu_reg); + snd_mtxfree(sc->lock); free(sc, M_DEVBUF); @@ -1007,4 +1105,5 @@ DRIVER_MODULE(snd_cmi, pci, cmi_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_cmi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_DEPEND(snd_cmi, midi, 1,1,1); MODULE_VERSION(snd_cmi, 1); --- sys/dev/sound/pci/cs4281.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/cs4281.c Thu Jul 12 12:04:19 2007 @@ -37,7 +37,7 @@ #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/cs4281.c,v 1.22 2005/03/01 08:58:05 imp Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/cs4281.c,v 1.26 2007/06/17 06:10:41 ariff Exp $"); #define CS4281_DEFAULT_BUFSZ 16384 @@ -314,7 +314,7 @@ struct sc_chinfo *ch = (dir == PCMDIR_PLAY) ? &sc->pch : &sc->rch; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) { + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) { return NULL; } ch->parent = sc; @@ -425,6 +425,7 @@ adcdac_prog(ch); adcdac_go(ch, 1); break; + case PCMTRIG_STOP: case PCMTRIG_ABORT: adcdac_go(ch, 0); break; @@ -753,11 +754,7 @@ u_int32_t data; char status[SND_STATUSLEN]; - if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->dev = dev; sc->type = pci_get_devid(dev); @@ -824,7 +821,8 @@ sc->bufsz = pcm_getbuffersize(dev, 4096, CS4281_DEFAULT_BUFSZ, 65536); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/pci/csa.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/csa.c Thu Jul 12 12:04:19 2007 @@ -48,7 +48,7 @@ #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/csa.c,v 1.33.2.1 2005/12/30 19:55:53 netchild Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/csa.c,v 1.37 2007/03/17 19:37:09 ariff Exp $"); /* This is the pci device id. */ #define CS4610_PCI_ID 0x60011013 @@ -82,7 +82,10 @@ struct resource *r); static int csa_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, - driver_intr_t *intr, void *arg, void **cookiep); +#if __FreeBSD_version >= 700031 + driver_filter_t *filter, +#endif + driver_intr_t *intr, void *arg, void **cookiep); static int csa_teardown_intr(device_t bus, device_t child, struct resource *irq, void *cookie); static driver_intr_t csa_intr; @@ -336,23 +339,31 @@ { csa_res *resp; sc_p scp; + struct sndcard_func *func; int err; scp = device_get_softc(dev); resp = &scp->res; - err = 0; - if (scp->midi != NULL) + if (scp->midi != NULL) { + func = device_get_ivars(scp->midi); err = device_delete_child(dev, scp->midi); - if (err) - return err; - scp->midi = NULL; + if (err != 0) + return err; + if (func != NULL) + free(func, M_DEVBUF); + scp->midi = NULL; + } - if (scp->pcm != NULL) + if (scp->pcm != NULL) { + func = device_get_ivars(scp->pcm); err = device_delete_child(dev, scp->pcm); - if (err) - return err; - scp->pcm = NULL; + if (err != 0) + return err; + if (func != NULL) + free(func, M_DEVBUF); + scp->pcm = NULL; + } bus_teardown_intr(dev, resp->irq, scp->ih); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); @@ -439,12 +450,21 @@ static int csa_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, +#if __FreeBSD_version >= 700031 + driver_filter_t *filter, +#endif driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp; csa_res *resp; struct sndcard_func *func; +#if __FreeBSD_version >= 700031 + if (filter != NULL) { + printf("ata-csa.c: we cannot use a filter here\n"); + return (EINVAL); + } +#endif scp = device_get_softc(bus); resp = &scp->res; --- sys/dev/sound/pci/csapcm.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/csapcm.c Thu Jul 12 12:04:19 2007 @@ -38,7 +38,7 @@ #include #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/csapcm.c,v 1.34.2.2 2006/04/04 17:32:48 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/csapcm.c,v 1.41 2007/06/17 06:10:41 ariff Exp $"); /* Buffer size on dma transfer. Fixed for CS416x. */ #define CS461x_BUFFSIZE (4 * 1024) @@ -534,7 +534,7 @@ ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, csa->parent_dmat, CS461x_BUFFSIZE) != 0) + if (sndbuf_alloc(ch->buffer, csa->parent_dmat, 0, CS461x_BUFFSIZE) != 0) return NULL; return ch; } @@ -569,7 +569,7 @@ struct csa_chinfo *ch = data; struct csa_info *csa = ch->parent; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; if (go == PCMTRIG_START) { @@ -704,7 +704,9 @@ if (resp->irq == NULL) return (1); } - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/CS461x_BUFFSIZE, /*boundary*/CS461x_BUFFSIZE, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), + /*alignment*/CS461x_BUFFSIZE, + /*boundary*/CS461x_BUFFSIZE, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -775,9 +777,7 @@ struct ac97_info *codec; struct sndcard_func *func; - csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT | M_ZERO); - if (csa == NULL) - return (ENOMEM); + csa = malloc(sizeof(*csa), M_DEVBUF, M_WAITOK | M_ZERO); unit = device_get_unit(dev); func = device_get_ivars(dev); csa->binfo = func->varinfo; --- sys/dev/sound/pci/ds1.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/ds1.c Thu Jul 12 12:04:19 2007 @@ -33,7 +33,7 @@ #include #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/ds1.c,v 1.43.2.1 2006/01/18 01:05:34 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/ds1.c,v 1.52 2007/06/17 06:10:41 ariff Exp $"); /* -------------------------------------------------------------------- */ @@ -490,7 +490,7 @@ ch->fmt = AFMT_U8; ch->spd = 8000; ch->run = 0; - if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) != 0) + if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0) return NULL; else { ch->lsnum = sc->pslotfree; @@ -545,7 +545,7 @@ struct sc_info *sc = ch->parent; int stereo; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; if (go == PCMTRIG_START) { @@ -621,7 +621,7 @@ ch->dir = dir; ch->fmt = AFMT_U8; ch->spd = 8000; - if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) != 0) + if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0) return NULL; else { ch->slot = (ch->num == DS1_RECPRIMARY)? sc->rbank + 2: sc->rbank; @@ -673,7 +673,7 @@ struct sc_info *sc = ch->parent; u_int32_t x; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; if (go == PCMTRIG_START) { ch->run = 1; @@ -833,7 +833,9 @@ memsz += (64 + 1) * 4; if (sc->regbase == NULL) { - if (bus_dma_tag_create(NULL, 2, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + if (bus_dma_tag_create(bus_get_dma_tag(sc->dev), 2, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, memsz, 1, memsz, 0, NULL, NULL, &sc->control_dmat)) return -1; @@ -941,12 +943,8 @@ struct ac97_info *codec = NULL; char status[SND_STATUSLEN]; - if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_ds1 softc"); sc->dev = dev; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); sc->type = ds_finddev(pci_get_devid(dev), subdev); @@ -970,7 +968,8 @@ sc->bufsz = pcm_getbuffersize(dev, 4096, DS1_BUFFSIZE, 65536); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, --- sys/dev/sound/pci/emu10k1.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/emu10k1.c Thu Jul 12 12:04:19 2007 @@ -28,14 +28,16 @@ #include #include -#include #include "emu10k1-alsa%diked.h" #include #include #include -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/emu10k1.c,v 1.55.2.1 2005/12/30 19:55:53 netchild Exp $"); +#include +#include "mpufoi_if.h" + +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/emu10k1.c,v 1.69 2007/06/17 06:10:42 ariff Exp $"); /* -------------------------------------------------------------------- */ @@ -85,7 +87,7 @@ struct emu_voice { int vnum; - int b16:1, stereo:1, busy:1, running:1, ismaster:1; + unsigned int b16:1, stereo:1, busy:1, running:1, ismaster:1; int speed; int start, end, vol; int fxrt1; /* FX routing */ @@ -137,6 +139,9 @@ struct emu_voice voice[64]; struct sc_pchinfo pch[EMU_MAX_CHANS]; struct sc_rchinfo rch[3]; + struct mpu401 *mpu; + mpu401_intr_t *mpu_intr; + int mputx; }; /* -------------------------------------------------------------------- */ @@ -272,7 +277,7 @@ static void emu_wrefx(struct sc_info *sc, unsigned int pc, unsigned int data) { - pc += sc->audigy ? AUDIGY_CODEBASE : MICROCODEBASE; + pc += sc->audigy ? A_MICROCODEBASE : MICROCODEBASE; emu_wrptr(sc, 0, pc, data); } @@ -790,7 +795,7 @@ struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; snd_mtxlock(sc->lock); @@ -890,7 +895,7 @@ break; } sc->rnum++; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) return NULL; else { snd_mtxlock(sc->lock); @@ -953,6 +958,9 @@ struct sc_info *sc = ch->parent; u_int32_t val, sz; + if (!PCMTRIG_COMMON(go)) + return 0; + switch(sc->bufsz) { case 4096: sz = ADCBS_BUFSIZE_4096; @@ -1059,8 +1067,65 @@ }; CHANNEL_DECLARE(emurchan); +static unsigned char +emu_mread(void *arg, struct sc_info *sc, int reg) +{ + unsigned int d; + + d = emu_rd(sc, 0x18 + reg, 1); + return d; +} + +static void +emu_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b) +{ + + emu_wr(sc, 0x18 + reg, b, 1); +} + +static int +emu_muninit(void *arg, struct sc_info *sc) +{ + + snd_mtxlock(sc->lock); + sc->mpu_intr = 0; + snd_mtxunlock(sc->lock); + + return 0; +} + +static kobj_method_t emu_mpu_methods[] = { + KOBJMETHOD(mpufoi_read, emu_mread), + KOBJMETHOD(mpufoi_write, emu_mwrite), + KOBJMETHOD(mpufoi_uninit, emu_muninit), + { 0, 0 } +}; + +static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); + +static void +emu_intr2(void *p) +{ + struct sc_info *sc = (struct sc_info *)p; + + if (sc->mpu_intr) + (sc->mpu_intr)(sc->mpu); +} + +static void +emu_midiattach(struct sc_info *sc) +{ + int i; + + i = emu_rd(sc, INTE, 4); + i |= INTE_MIDIRXENABLE; + emu_wr(sc, INTE, i, 4); + + sc->mpu = mpu401_init(&emu_mpu_class, sc, emu_intr2, &sc->mpu_intr); +} /* -------------------------------------------------------------------- */ /* The interrupt handler */ + static void emu_intr(void *data) { @@ -1100,6 +1165,11 @@ #endif } + if (stat & IPR_MIDIRECVBUFEMPTY) + if (sc->mpu_intr) { + (sc->mpu_intr)(sc->mpu); + ack |= IPR_MIDIRECVBUFEMPTY | IPR_MIDITRANSBUFEMPTY; + } if (stat & ~ack) device_printf(sc->dev, "dodgy irq: %x (harmless)\n", stat & ~ack); @@ -1312,12 +1382,12 @@ /* Audigy 2 (EMU10K2) DSP Registers: FX Bus - 0x000-0x00f : 16 registers (???) + 0x000-0x00f : 16 registers (?) Input 0x040/0x041 : AC97 Codec (l/r) 0x042/0x043 : ADC, S/PDIF (l/r) 0x044/0x045 : Optical S/PDIF in (l/r) - 0x046/0x047 : ??? + 0x046/0x047 : ? 0x048/0x049 : Line/Mic 2 (l/r) 0x04a/0x04b : RCA S/PDIF (l/r) 0x04c/0x04d : Aux 2 (l/r) @@ -1328,11 +1398,11 @@ 0x066/0x067 : Digital Rear (l/r) 0x068/0x069 : Analog Front (l/r) 0x06a/0x06b : Analog Center/LFE - 0x06c/0x06d : ??? + 0x06c/0x06d : ? 0x06e/0x06f : Analog Rear (l/r) 0x070/0x071 : AC97 Output (l/r) - 0x072/0x073 : ??? - 0x074/0x075 : ??? + 0x072/0x073 : ? + 0x074/0x075 : ? 0x076/0x077 : ADC Recording Buffer (l/r) Constants 0x0c0 - 0x0c4 = 0 - 4 @@ -1341,9 +1411,9 @@ 0x0cb = 0x10000000, 0x0cc = 0x20000000, 0x0cd = 0x40000000 0x0ce = 0x80000000, 0x0cf = 0x7fffffff, 0x0d0 = 0xffffffff 0x0d1 = 0xfffffffe, 0x0d2 = 0xc0000000, 0x0d3 = 0x41fbbcdc - 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (???) + 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (?) Temporary Values - 0x0d6 : Accumulator (???) + 0x0d6 : Accumulator (?) 0x0d7 : Condition Register 0x0d8 : Noise source 0x0d9 : Noise source @@ -1560,10 +1630,10 @@ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ALFE), C_00000000, GPR(1), GPR(2), &pc); /* Digital Center = GPR[0] + GPR[2] */ - emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_CENTER), C_00000000, + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_CENTER), C_00000000, GPR(0), GPR(2), &pc); /* Digital Sub = GPR[1] + GPR[2] */ - emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_LFE), C_00000000, + emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_LFE), C_00000000, GPR(1), GPR(2), &pc); /* Headphones[l/r] = GPR[0/1] */ @@ -1871,6 +1941,8 @@ emu_free(sc, sc->mem.ptb_pages); emu_free(sc, sc->mem.silent_page); + if(sc->mpu) + mpu401_uninit(sc->mpu); return 0; } @@ -1900,7 +1972,7 @@ } device_set_desc(dev, s); - return BUS_PROBE_DEFAULT; + return BUS_PROBE_LOW_PRIORITY; } static int @@ -1912,12 +1984,8 @@ int i, gotmic; char status[SND_STATUSLEN]; - if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10k1 softc"); sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); @@ -1942,7 +2010,8 @@ sc->bufsz = pcm_getbuffersize(dev, 4096, EMU_DEFAULT_BUFSZ, 65536); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, + /*boundary*/0, /*lowaddr*/1 << 31, /* can only access 0-2gb */ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -1963,6 +2032,8 @@ gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0; if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; + emu_midiattach(sc); + i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); @@ -2041,6 +2112,7 @@ DRIVER_MODULE(snd_emu10k1, cardbus, emu_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_emu10k1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_emu10k1, 1); +MODULE_DEPEND(snd_emu10k1, midi, 1, 1, 1); /* dummy driver to silence the joystick device */ static int --- sys/dev/sound/pci/emu10kx-midi.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/emu10kx-midi.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,250 @@ +/*- + * Copyright (c) 1999 Seigo Tanimura + * (c) 2003 Mathew Kanner + * Copyright (c) 2003-2006 Yuriy Tsibizov + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/sound/pci/emu10kx-midi.c,v 1.3 2006/07/16 20:10:08 netchild Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "mpufoi_if.h" + +#include "opt_emu10kx.h" +#include +#include "emu10k1-alsa%diked.h" + +struct emu_midi_softc { + struct mtx mtx; + device_t dev; + struct mpu401 *mpu; + mpu401_intr_t *mpu_intr; + struct emu_sc_info *card; + int port; /* I/O port or I/O ptr reg */ + int is_emu10k1; + int fflags; /* File flags */ + int ihandle; /* interrupt manager handle */ +}; + +static uint32_t emu_midi_card_intr(void *p, uint32_t arg); +static devclass_t emu_midi_devclass; + +static unsigned char +emu_mread(void *arg __unused, struct emu_midi_softc *sc, int reg) +{ + unsigned int d; + + d = 0; + if (sc->is_emu10k1) + d = emu_rd(sc->card, 0x18 + reg, 1); + else + d = emu_rdptr(sc->card, 0, sc->port + reg); + + return (d); +} + +static void +emu_mwrite(void *arg __unused, struct emu_midi_softc *sc, int reg, unsigned char b) +{ + + if (sc->is_emu10k1) + emu_wr(sc->card, 0x18 + reg, b, 1); + else + emu_wrptr(sc->card, 0, sc->port + reg, b); +} + +static int +emu_muninit(void *arg __unused, struct emu_midi_softc *sc) +{ + + mtx_lock(&sc->mtx); + sc->mpu_intr = NULL; + mtx_unlock(&sc->mtx); + + return (0); +} + +static kobj_method_t emu_mpu_methods[] = { + KOBJMETHOD(mpufoi_read, emu_mread), + KOBJMETHOD(mpufoi_write, emu_mwrite), + KOBJMETHOD(mpufoi_uninit, emu_muninit), + {0, 0} +}; +static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); + +static uint32_t +emu_midi_card_intr(void *p, uint32_t intr_status) +{ + struct emu_midi_softc *sc = (struct emu_midi_softc *)p; + if (sc->mpu_intr) + (sc->mpu_intr) (sc->mpu); + if (sc->mpu_intr == NULL) { + /* We should read MIDI event to unlock card after + * interrupt. XXX - check, why this happens. */ +#ifdef SND_EMU10KX_DEBUG + device_printf(sc->dev, "midi interrupt %08x without interrupt handler, force mread!\n", intr_status); +#endif + (void)emu_mread((void *)(NULL), sc, 0); + } + return (intr_status); /* Acknowledge everything */ +} + +static void +emu_midi_intr(void *p) +{ + (void)emu_midi_card_intr(p, 0); +} + +static int +emu_midi_probe(device_t dev) +{ + struct emu_midi_softc *scp; + uintptr_t func, r, is_emu10k1; + + r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); + if (func != SCF_MIDI) + return (ENXIO); + + scp = device_get_softc(dev); + bzero(scp, sizeof(*scp)); + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &is_emu10k1); + scp->is_emu10k1 = is_emu10k1 ? 1 : 0; + + device_set_desc(dev, "EMU10Kx MIDI Interface"); + return (0); +} + +static int +emu_midi_attach(device_t dev) +{ + struct emu_midi_softc * scp; + struct sndcard_func *func; + struct emu_midiinfo *midiinfo; + uint32_t inte_val, ipr_val; + + scp = device_get_softc(dev); + func = device_get_ivars(dev); + + scp->dev = dev; + midiinfo = (struct emu_midiinfo *)func->varinfo; + scp->port = midiinfo->port; + scp->card = midiinfo->card; + + mtx_init(&scp->mtx, "emu10kx_midi", NULL, MTX_DEF); + + if (scp->is_emu10k1) { + /* SB Live! - only one MIDI device here */ + inte_val = 0; + /* inte_val |= INTE_MIDITXENABLE;*/ + inte_val |= INTE_MIDIRXENABLE; + ipr_val = IPR_MIDITRANSBUFEMPTY; + ipr_val |= IPR_MIDIRECVBUFEMPTY; + } else { + if (scp->port == A_MUDATA1) { + /* EXTERNAL MIDI (AudigyDrive) */ + inte_val = 0; + /* inte_val |= A_INTE_MIDITXENABLE1;*/ + inte_val |= INTE_MIDIRXENABLE; + ipr_val = IPR_MIDITRANSBUFEMPTY; + ipr_val |= IPR_MIDIRECVBUFEMPTY; + } else { + /* MIDI hw config port 2 */ + inte_val = 0; + /* inte_val |= A_INTE_MIDITXENABLE2;*/ + inte_val |= INTE_A_MIDIRXENABLE2; + ipr_val = IPR_A_MIDITRANSBUFEMPTY2; + ipr_val |= IPR_A_MIDIRECVBUFEMPTY2; + } + } + if (inte_val == 0) + return (ENXIO); + + scp->ihandle = emu_intr_register(scp->card, inte_val, ipr_val, &emu_midi_card_intr, scp); + /* Init the interface. */ + scp->mpu = mpu401_init(&emu_mpu_class, scp, emu_midi_intr, &scp->mpu_intr); + if (scp->mpu == NULL) { + emu_intr_unregister(scp->card, scp->ihandle); + mtx_destroy(&scp->mtx); + return (ENOMEM); + } + /* + * XXX I don't know how to check for Live!Drive / AudigyDrive + * presence. Let's hope that IR enabling code will not harm if + * it is not present. + */ + if (scp->is_emu10k1) + emu_enable_ir(scp->card); + else { + if (scp->port == A_MUDATA1) + emu_enable_ir(scp->card); + } + + return (0); +} + + +static int +emu_midi_detach(device_t dev) +{ + struct emu_midi_softc *scp; + + scp = device_get_softc(dev); + mpu401_uninit(scp->mpu); + emu_intr_unregister(scp->card, scp->ihandle); + mtx_destroy(&scp->mtx); + return (0); +} + +static device_method_t emu_midi_methods[] = { + DEVMETHOD(device_probe, emu_midi_probe), + DEVMETHOD(device_attach, emu_midi_attach), + DEVMETHOD(device_detach, emu_midi_detach), + + {0, 0}, +}; + +static driver_t emu_midi_driver = { + "midi", + emu_midi_methods, + sizeof(struct emu_midi_softc), +}; +DRIVER_MODULE(snd_emu10kx_midi, emu10kx, emu_midi_driver, emu_midi_devclass, 0, 0); +MODULE_DEPEND(snd_emu10kx_midi, snd_emu10kx, SND_EMU10KX_MINVER, SND_EMU10KX_PREFVER, SND_EMU10KX_MAXVER); +MODULE_DEPEND(snd_emu10kx_midi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_emu10kx_midi, SND_EMU10KX_PREFVER); --- sys/dev/sound/pci/emu10kx-pcm.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/emu10kx-pcm.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,1190 @@ +/*- + * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2003-2006 Yuriy Tsibizov + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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, WHETHERIN 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. + * + * $FreeBSD: src/sys/dev/sound/pci/emu10kx-pcm.c,v 1.10 2007/06/17 06:10:42 ariff Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mixer_if.h" + +#include "opt_emu10kx.h" +#include +#include "emu10k1-alsa%diked.h" + +struct emu_pcm_pchinfo { + int spd; + int fmt; + int blksz; + int run; + struct emu_voice *master; + struct emu_voice *slave; + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct emu_pcm_info *pcm; + int timer; +}; + +struct emu_pcm_rchinfo { + int spd; + int fmt; + int blksz; + int run; + uint32_t idxreg; + uint32_t basereg; + uint32_t sizereg; + uint32_t setupreg; + uint32_t irqmask; + uint32_t iprmask; + int ihandle; + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct emu_pcm_info *pcm; +}; + +/* XXX Hardware playback channels */ +#define MAX_CHANNELS 4 + +#if MAX_CHANNELS > 13 +#error Too many hardware channels defined. 13 is the maximum +#endif + +struct emu_pcm_info { + struct mtx *lock; + device_t dev; /* device information */ + struct snddev_info *devinfo; /* pcm device information */ + struct emu_sc_info *card; + struct emu_pcm_pchinfo pch[MAX_CHANNELS]; /* hardware channels */ + int pnum; /* next free channel number */ + struct emu_pcm_rchinfo rch_adc; + struct emu_pcm_rchinfo rch_efx; + struct emu_route rt; + struct emu_route rt_mono; + int route; + int ihandle; /* interrupt handler */ + unsigned int bufsz; + int is_emu10k1; + struct ac97_info *codec; + uint32_t ac97_state[0x7F]; +}; + + +static uint32_t emu_rfmt_adc[] = { + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + 0 +}; +static struct pcmchan_caps emu_reccaps_adc = { + 8000, 48000, emu_rfmt_adc, 0 +}; + +static uint32_t emu_rfmt_efx[] = { + AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps emu_reccaps_efx_live = { + 48000*32, 48000*32, emu_rfmt_efx, 0 +}; + +static struct pcmchan_caps emu_reccaps_efx_audigy = { + 48000*64, 48000*64, emu_rfmt_efx, 0 +}; + +static uint32_t emu_pfmt[] = { + AFMT_U8, + AFMT_STEREO | AFMT_U8, + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + 0 +}; +static uint32_t emu_pfmt_mono[] = { + AFMT_U8, + AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0}; +static struct pcmchan_caps emu_playcaps_mono = {4000, 48000, emu_pfmt_mono, 0}; + +static int emu10k1_adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; +/* audigy supports 12kHz. */ +static int emu10k2_adcspeed[9] = {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000}; + +static uint32_t emu_pcm_intr(void *pcm, uint32_t stat); + +static const struct emu_dspmix_props { + u_int8_t present; +} dspmix [SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = {1}, + [SOUND_MIXER_PCM] = {1}, +}; + +static int +emu_dspmixer_init(struct snd_mixer *m) +{ + int i; + int v; + + v = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (dspmix[i].present) + v |= 1 << i; + } + mix_setdevs(m, v); + + mix_setrecdevs(m, 0); + return (0); +} + +static int +emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct emu_pcm_info *sc; + + sc = mix_getdevinfo(m); + + switch (dev) { + case SOUND_MIXER_VOLUME: + switch (sc->route) { + case RT_REAR: + emumix_set_volume(sc->card, M_MASTER_REAR_L, left); + emumix_set_volume(sc->card, M_MASTER_REAR_R, right); + break; + case RT_CENTER: + emumix_set_volume(sc->card, M_MASTER_CENTER, (left+right)/2); + break; + case RT_SUB: + emumix_set_volume(sc->card, M_MASTER_SUBWOOFER, (left+right)/2); + break; + } + break; + case SOUND_MIXER_PCM: + switch (sc->route) { + case RT_REAR: + emumix_set_volume(sc->card, M_FX2_REAR_L, left); + emumix_set_volume(sc->card, M_FX3_REAR_R, right); + break; + case RT_CENTER: + emumix_set_volume(sc->card, M_FX4_CENTER, (left+right)/2); + break; + case RT_SUB: + emumix_set_volume(sc->card, M_FX5_SUBWOOFER, (left+right)/2); + break; + } + break; + default: + device_printf(sc->dev, "mixer error: unknown device %d\n", dev); + } + return (0); +} + +static int +emu_dspmixer_setrecsrc(struct snd_mixer *m __unused, u_int32_t src __unused) +{ + return (0); +} + +static kobj_method_t emudspmixer_methods[] = { + KOBJMETHOD(mixer_init, emu_dspmixer_init), + KOBJMETHOD(mixer_set, emu_dspmixer_set), + KOBJMETHOD(mixer_setrecsrc, emu_dspmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(emudspmixer); + +/* + * AC97 emulation code for Audigy and later cards. + * Some parts of AC97 codec are not used by hardware, but can be used + * to change some DSP controls via AC97 mixer interface. This includes: + * - master volume controls MASTER_FRONT_[R|L] + * - pcm volume controls FX[0|1]_FRONT_[R|L] + * - rec volume controls MASTER_REC_[R|L] + * We do it because we need to put it under user control.... + * We also keep some parts of AC97 disabled to get better sound quality + */ + +#define AC97LEFT(x) ((x & 0x7F00)>>8) +#define AC97RIGHT(x) (x & 0x007F) +#define AC97MUTE(x) ((x & 0x8000)>>15) +#define BIT4_TO100(x) (100-(x)*100/(0x0f)) +#define BIT6_TO100(x) (100-(x)*100/(0x3f)) +#define BIT4_TO255(x) (255-(x)*255/(0x0f)) +#define BIT6_TO255(x) (255-(x)*255/(0x3f)) +#define V100_TOBIT6(x) (0x3f*(100-x)/100) +#define V100_TOBIT4(x) (0x0f*(100-x)/100) +#define AC97ENCODE(x_muted,x_left,x_right) (((x_muted&1)<<15) | ((x_left&0x3f)<<8) | (x_right&0x3f)) + +static int +emu_ac97_read_emulation(struct emu_pcm_info *sc, int regno) +{ + int use_ac97; + int emulated; + int tmp; + + use_ac97 = 1; + emulated = 0; + + switch (regno) { + case AC97_MIX_MASTER: + emulated = sc->ac97_state[AC97_MIX_MASTER]; + use_ac97 = 0; + break; + case AC97_MIX_PCM: + emulated = sc->ac97_state[AC97_MIX_PCM]; + use_ac97 = 0; + break; + case AC97_REG_RECSEL: + emulated = 0x0505; + use_ac97 = 0; + break; + case AC97_MIX_RGAIN: + emulated = sc->ac97_state[AC97_MIX_RGAIN]; + use_ac97 = 0; + break; + } + + emu_wr(sc->card, AC97ADDRESS, regno, 1); + tmp = emu_rd(sc->card, AC97DATA, 2); + + if (use_ac97) + emulated = tmp; + + return (emulated); +} + +static void +emu_ac97_write_emulation(struct emu_pcm_info *sc, int regno, uint32_t data) +{ + int write_ac97; + int left, right; + uint32_t emu_left, emu_right; + int is_mute; + + write_ac97 = 1; + + left = AC97LEFT(data); + emu_left = BIT6_TO100(left); /* We show us as 6-bit AC97 mixer */ + right = AC97RIGHT(data); + emu_right = BIT6_TO100(right); + is_mute = AC97MUTE(data); + if (is_mute) + emu_left = emu_right = 0; + + switch (regno) { + /* TODO: reset emulator on AC97_RESET */ + case AC97_MIX_MASTER: + emumix_set_volume(sc->card, M_MASTER_FRONT_L, emu_left); + emumix_set_volume(sc->card, M_MASTER_FRONT_R, emu_right); + sc->ac97_state[AC97_MIX_MASTER] = data & (0x8000 | 0x3f3f); + data = 0x8000; /* Mute AC97 main out */ + break; + case AC97_MIX_PCM: /* PCM OUT VOL */ + emumix_set_volume(sc->card, M_FX0_FRONT_L, emu_left); + emumix_set_volume(sc->card, M_FX1_FRONT_R, emu_right); + sc->ac97_state[AC97_MIX_PCM] = data & (0x8000 | 0x3f3f); + data = 0x8000; /* Mute AC97 PCM out */ + break; + case AC97_REG_RECSEL: + /* + * PCM recording source is set to "stereo mix" (labeled "vol" + * in mixer) XXX !I can't remember why! + */ + data = 0x0505; + break; + case AC97_MIX_RGAIN: /* RECORD GAIN */ + emu_left = BIT4_TO100(left); /* rgain is 4-bit */ + emu_right = BIT4_TO100(right); + emumix_set_volume(sc->card, M_MASTER_REC_L, 100-emu_left); + emumix_set_volume(sc->card, M_MASTER_REC_R, 100-emu_right); + /* + * Record gain on AC97 should stay zero to get AC97 sound on + * AC97_[RL] connectors on EMU10K2 chip. AC97 on Audigy is not + * directly connected to any output, only to EMU10K2 chip Use + * this control to set AC97 mix volume inside EMU10K2 chip + */ + sc->ac97_state[AC97_MIX_RGAIN] = data & (0x8000 | 0x0f0f); + data = 0x0000; + break; + } + if (write_ac97) { + emu_wr(sc->card, AC97ADDRESS, regno, 1); + emu_wr(sc->card, AC97DATA, data, 2); + } +} + +static int +emu_erdcd(kobj_t obj __unused, void *devinfo, int regno) +{ + struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo; + + return (emu_ac97_read_emulation(sc, regno)); +} + +static int +emu_ewrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data) +{ + struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo; + + emu_ac97_write_emulation(sc, regno, data); + return (0); +} + +static kobj_method_t emu_eac97_methods[] = { + KOBJMETHOD(ac97_read, emu_erdcd), + KOBJMETHOD(ac97_write, emu_ewrcd), + {0, 0} +}; +AC97_DECLARE(emu_eac97); + +/* real ac97 codec */ +static int +emu_rdcd(kobj_t obj __unused, void *devinfo, int regno) +{ + int rd; + struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo; + + KASSERT(sc->card != NULL, ("emu_rdcd: no soundcard")); + emu_wr(sc->card, AC97ADDRESS, regno, 1); + rd = emu_rd(sc->card, AC97DATA, 2); + return (rd); +} + +static int +emu_wrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data) +{ + struct emu_pcm_info *sc = (struct emu_pcm_info *)devinfo; + + KASSERT(sc->card != NULL, ("emu_wrcd: no soundcard")); + emu_wr(sc->card, AC97ADDRESS, regno, 1); + emu_wr(sc->card, AC97DATA, data, 2); + return (0); +} + +static kobj_method_t emu_ac97_methods[] = { + KOBJMETHOD(ac97_read, emu_rdcd), + KOBJMETHOD(ac97_write, emu_wrcd), + {0, 0} +}; +AC97_DECLARE(emu_ac97); + + +static int +emu_k1_recval(int speed) +{ + int val; + + val = 0; + while ((val < 7) && (speed < emu10k1_adcspeed[val])) + val++; + if (val == 6) val=5; /* XXX 8kHz does not work */ + return (val); +} + +static int +emu_k2_recval(int speed) +{ + int val; + + val = 0; + while ((val < 8) && (speed < emu10k2_adcspeed[val])) + val++; + if (val == 7) val=6; /* XXX 8kHz does not work */ + return (val); +} + +static void * +emupchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir __unused) +{ + struct emu_pcm_info *sc = devinfo; + struct emu_pcm_pchinfo *ch; + void *r; + + KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction")); + KASSERT(sc->card != NULL, ("empchan_init: no soundcard")); + + + if (sc->pnum >= MAX_CHANNELS) + return (NULL); + ch = &(sc->pch[sc->pnum++]); + ch->buffer = b; + ch->pcm = sc; + ch->channel = c; + ch->blksz = sc->bufsz; + ch->fmt = AFMT_U8; + ch->spd = 8000; + ch->master = emu_valloc(sc->card); + /* + * XXX we have to allocate slave even for mono channel until we + * fix emu_vfree to handle this case. + */ + ch->slave = emu_valloc(sc->card); + ch->timer = emu_timer_create(sc->card); + r = (emu_vinit(sc->card, ch->master, ch->slave, EMU_PLAY_BUFSZ, ch->buffer)) ? NULL : ch; + return (r); +} + +static int +emupchan_free(kobj_t obj __unused, void *c_devinfo) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + emu_timer_clear(sc->card, ch->timer); + if (ch->slave != NULL) + emu_vfree(sc->card, ch->slave); + emu_vfree(sc->card, ch->master); + return (0); +} + +static int +emupchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + + ch->fmt = format; + return (0); +} + +static int +emupchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + + ch->spd = speed; + return (ch->spd); +} + +static int +emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + if (blocksize > ch->pcm->bufsz) + blocksize = ch->pcm->bufsz; + snd_mtxlock(sc->lock); + ch->blksz = blocksize; + emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); + snd_mtxunlock(sc->lock); + return (blocksize); +} + +static int +emupchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + if (!PCMTRIG_COMMON(go)) + return (0); + + snd_mtxlock(sc->lock); /* XXX can we trigger on parallel threads ? */ + if (go == PCMTRIG_START) { + emu_vsetup(ch->master, ch->fmt, ch->spd); + if ((ch->fmt & AFMT_STEREO) == AFMT_STEREO) + emu_vroute(sc->card, &(sc->rt), ch->master); + else + emu_vroute(sc->card, &(sc->rt_mono), ch->master); + emu_vwrite(sc->card, ch->master); + emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); + emu_timer_enable(sc->card, ch->timer, 1); + } + /* PCM interrupt handler will handle PCMTRIG_STOP event */ + ch->run = (go == PCMTRIG_START) ? 1 : 0; + emu_vtrigger(sc->card, ch->master, ch->run); + snd_mtxunlock(sc->lock); + return (0); +} + +static int +emupchan_getptr(kobj_t obj __unused, void *c_devinfo) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + int r; + + r = emu_vpos(sc->card, ch->master); + + return (r); +} + +static struct pcmchan_caps * +emupchan_getcaps(kobj_t obj __unused, void *c_devinfo __unused) +{ + struct emu_pcm_pchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + switch (sc->route) { + case RT_FRONT: + /* FALLTHROUGH */ + case RT_REAR: + /* FALLTHROUGH */ + case RT_SIDE: + return (&emu_playcaps); + break; + case RT_CENTER: + /* FALLTHROUGH */ + case RT_SUB: + return (&emu_playcaps_mono); + break; + } + return (NULL); +} + +static kobj_method_t emupchan_methods[] = { + KOBJMETHOD(channel_init, emupchan_init), + KOBJMETHOD(channel_free, emupchan_free), + KOBJMETHOD(channel_setformat, emupchan_setformat), + KOBJMETHOD(channel_setspeed, emupchan_setspeed), + KOBJMETHOD(channel_setblocksize, emupchan_setblocksize), + KOBJMETHOD(channel_trigger, emupchan_trigger), + KOBJMETHOD(channel_getptr, emupchan_getptr), + KOBJMETHOD(channel_getcaps, emupchan_getcaps), + {0, 0} +}; +CHANNEL_DECLARE(emupchan); + +static void * +emurchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir __unused) +{ + struct emu_pcm_info *sc = devinfo; + struct emu_pcm_rchinfo *ch; + + KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction")); + ch = &sc->rch_adc; + ch->buffer = b; + ch->pcm = sc; + ch->channel = c; + ch->blksz = sc->bufsz / 2; /* We rise interrupt for half-full buffer */ + ch->fmt = AFMT_U8; + ch->spd = 8000; + ch->idxreg = sc->is_emu10k1 ? ADCIDX : A_ADCIDX; + ch->basereg = ADCBA; + ch->sizereg = ADCBS; + ch->setupreg = ADCCR; + ch->irqmask = INTE_ADCBUFENABLE; + ch->iprmask = IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL; + + if (sndbuf_alloc(ch->buffer, emu_gettag(sc->card), 0, sc->bufsz) != 0) + return (NULL); + else { + emu_wrptr(sc->card, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer)); + emu_wrptr(sc->card, 0, ch->sizereg, 0); /* off */ + return (ch); + } +} + +static int +emurchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + + ch->fmt = format; + return (0); +} + +static int +emurchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + + if (ch->pcm->is_emu10k1) { + speed = emu10k1_adcspeed[emu_k1_recval(speed)]; + } else { + speed = emu10k2_adcspeed[emu_k2_recval(speed)]; + } + ch->spd = speed; + return (ch->spd); +} + +static int +emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + + ch->blksz = blocksize; + /* If blocksize is less than half of buffer size we will not get + interrupt in time and channel will die due to interrupt timeout */ + if(ch->blksz < (ch->pcm->bufsz / 2)) + ch->blksz = ch->pcm->bufsz / 2; + return (ch->blksz); +} + +static int +emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + uint32_t val, sz; + + if (!PCMTRIG_COMMON(go)) + return (0); + + switch (sc->bufsz) { + case 4096: + sz = ADCBS_BUFSIZE_4096; + break; + case 8192: + sz = ADCBS_BUFSIZE_8192; + break; + case 16384: + sz = ADCBS_BUFSIZE_16384; + break; + case 32768: + sz = ADCBS_BUFSIZE_32768; + break; + case 65536: + sz = ADCBS_BUFSIZE_65536; + break; + default: + sz = ADCBS_BUFSIZE_4096; + } + + snd_mtxlock(sc->lock); + switch (go) { + case PCMTRIG_START: + ch->run = 1; + emu_wrptr(sc->card, 0, ch->sizereg, sz); + val = sc->is_emu10k1 ? ADCCR_LCHANENABLE : A_ADCCR_LCHANENABLE; + if (ch->fmt & AFMT_STEREO) + val |= sc->is_emu10k1 ? ADCCR_RCHANENABLE : A_ADCCR_RCHANENABLE; + val |= sc->is_emu10k1 ? emu_k1_recval(ch->spd) : emu_k2_recval(ch->spd); + emu_wrptr(sc->card, 0, ch->setupreg, 0); + emu_wrptr(sc->card, 0, ch->setupreg, val); + ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc); + break; + case PCMTRIG_STOP: + /* FALLTHROUGH */ + case PCMTRIG_ABORT: + ch->run = 0; + emu_wrptr(sc->card, 0, ch->sizereg, 0); + if (ch->setupreg) + emu_wrptr(sc->card, 0, ch->setupreg, 0); + (void)emu_intr_unregister(sc->card, ch->ihandle); + break; + case PCMTRIG_EMLDMAWR: + /* FALLTHROUGH */ + case PCMTRIG_EMLDMARD: + /* FALLTHROUGH */ + default: + break; + } + snd_mtxunlock(sc->lock); + + return (0); +} + +static int +emurchan_getptr(kobj_t obj __unused, void *c_devinfo) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + int r; + + r = emu_rdptr(sc->card, 0, ch->idxreg) & 0x0000ffff; + + return (r); +} + +static struct pcmchan_caps * +emurchan_getcaps(kobj_t obj __unused, void *c_devinfo __unused) +{ + return (&emu_reccaps_adc); +} + +static kobj_method_t emurchan_methods[] = { + KOBJMETHOD(channel_init, emurchan_init), + KOBJMETHOD(channel_setformat, emurchan_setformat), + KOBJMETHOD(channel_setspeed, emurchan_setspeed), + KOBJMETHOD(channel_setblocksize, emurchan_setblocksize), + KOBJMETHOD(channel_trigger, emurchan_trigger), + KOBJMETHOD(channel_getptr, emurchan_getptr), + KOBJMETHOD(channel_getcaps, emurchan_getcaps), + {0, 0} +}; +CHANNEL_DECLARE(emurchan); + +static void * +emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir __unused) +{ + struct emu_pcm_info *sc = devinfo; + struct emu_pcm_rchinfo *ch; + + KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction")); + + if (sc == NULL) return (NULL); + + ch = &(sc->rch_efx); + ch->fmt = AFMT_S16_LE; + ch->spd = sc->is_emu10k1 ? 48000*32 : 48000 * 64; + ch->idxreg = FXIDX; + ch->basereg = FXBA; + ch->sizereg = FXBS; + ch->irqmask = INTE_EFXBUFENABLE; + ch->iprmask = IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL; + ch->buffer = b; + ch->pcm = sc; + ch->channel = c; + ch->blksz = sc->bufsz; + + if (sndbuf_alloc(ch->buffer, emu_gettag(sc->card), 0, sc->bufsz) != 0) + return (NULL); + else { + emu_wrptr(sc->card, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer)); + emu_wrptr(sc->card, 0, ch->sizereg, 0); /* off */ + return (ch); + } +} + +static int +emufxrchan_setformat(kobj_t obj __unused, void *c_devinfo __unused, uint32_t format) +{ + if (format == AFMT_S16_LE) return (0); + return (-1); +} + +static int +emufxrchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + + /* FIXED RATE CHANNEL */ + return (ch->spd); +} + +static int +emufxrchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + + ch->blksz = blocksize; + /* If blocksize is less than half of buffer size we will not get + interrupt in time and channel will die due to interrupt timeout */ + if(ch->blksz < (ch->pcm->bufsz / 2)) + ch->blksz = ch->pcm->bufsz / 2; + return (ch->blksz); +} + +static int +emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + uint32_t sz; + + if (!PCMTRIG_COMMON(go)) + return (0); + + switch (sc->bufsz) { + case 4096: + sz = ADCBS_BUFSIZE_4096; + break; + case 8192: + sz = ADCBS_BUFSIZE_8192; + break; + case 16384: + sz = ADCBS_BUFSIZE_16384; + break; + case 32768: + sz = ADCBS_BUFSIZE_32768; + break; + case 65536: + sz = ADCBS_BUFSIZE_65536; + break; + default: + sz = ADCBS_BUFSIZE_4096; + } + + snd_mtxlock(sc->lock); + switch (go) { + case PCMTRIG_START: + ch->run = 1; + emu_wrptr(sc->card, 0, ch->sizereg, sz); + ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc); + /* + SB Live! is limited to 32 mono channels. Audigy + has 64 mono channels, each of them is selected from + one of two A_FXWC[1|2] registers. + */ + /* XXX there is no way to demultiplex this streams for now */ + if(sc->is_emu10k1) { + emu_wrptr(sc->card, 0, FXWC, 0xffffffff); + } else { + emu_wrptr(sc->card, 0, A_FXWC1, 0xffffffff); + emu_wrptr(sc->card, 0, A_FXWC2, 0xffffffff); + } + break; + case PCMTRIG_STOP: + /* FALLTHROUGH */ + case PCMTRIG_ABORT: + ch->run = 0; + if(sc->is_emu10k1) { + emu_wrptr(sc->card, 0, FXWC, 0x0); + } else { + emu_wrptr(sc->card, 0, A_FXWC1, 0x0); + emu_wrptr(sc->card, 0, A_FXWC2, 0x0); + } + emu_wrptr(sc->card, 0, ch->sizereg, 0); + (void)emu_intr_unregister(sc->card, ch->ihandle); + break; + case PCMTRIG_EMLDMAWR: + /* FALLTHROUGH */ + case PCMTRIG_EMLDMARD: + /* FALLTHROUGH */ + default: + break; + } + snd_mtxunlock(sc->lock); + + return (0); +} + +static int +emufxrchan_getptr(kobj_t obj __unused, void *c_devinfo) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + int r; + + r = emu_rdptr(sc->card, 0, ch->idxreg) & 0x0000ffff; + + return (r); +} + +static struct pcmchan_caps * +emufxrchan_getcaps(kobj_t obj __unused, void *c_devinfo) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + if(sc->is_emu10k1) + return (&emu_reccaps_efx_live); + return (&emu_reccaps_efx_audigy); + +} + +static kobj_method_t emufxrchan_methods[] = { + KOBJMETHOD(channel_init, emufxrchan_init), + KOBJMETHOD(channel_setformat, emufxrchan_setformat), + KOBJMETHOD(channel_setspeed, emufxrchan_setspeed), + KOBJMETHOD(channel_setblocksize, emufxrchan_setblocksize), + KOBJMETHOD(channel_trigger, emufxrchan_trigger), + KOBJMETHOD(channel_getptr, emufxrchan_getptr), + KOBJMETHOD(channel_getcaps, emufxrchan_getcaps), + {0, 0} +}; +CHANNEL_DECLARE(emufxrchan); + + +static uint32_t +emu_pcm_intr(void *pcm, uint32_t stat) +{ + struct emu_pcm_info *sc = (struct emu_pcm_info *)pcm; + uint32_t ack; + int i; + + ack = 0; + + if (stat & IPR_INTERVALTIMER) { + ack |= IPR_INTERVALTIMER; + for (i = 0; i < MAX_CHANNELS; i++) + if (sc->pch[i].channel) { + if (sc->pch[i].run == 1) + chn_intr(sc->pch[i].channel); + else + emu_timer_enable(sc->card, sc->pch[i].timer, 0); + } + } + + + if (stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL)) { + ack |= stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL); + if (sc->rch_adc.channel) + chn_intr(sc->rch_adc.channel); + } + + if (stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)) { + ack |= stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL); + if (sc->rch_efx.channel) + chn_intr(sc->rch_efx.channel); + } + return (ack); +} + +static int +emu_pcm_init(struct emu_pcm_info *sc) +{ + sc->bufsz = pcm_getbuffersize(sc->dev, EMUPAGESIZE, EMU_REC_BUFSZ, EMU_MAX_BUFSZ); + return (0); +} + +static int +emu_pcm_uninit(struct emu_pcm_info *sc __unused) +{ + return (0); +} + +static int +emu_pcm_probe(device_t dev) +{ + uintptr_t func, route, r; + const char *rt; + char buffer[255]; + + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_FUNC, &func); + + if (func != SCF_PCM) + return (ENXIO); + + rt = "UNKNOWN"; + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ROUTE, &route); + switch (route) { + case RT_FRONT: + rt = "front"; + break; + case RT_REAR: + rt = "rear"; + break; + case RT_CENTER: + rt = "center"; + break; + case RT_SUB: + rt = "subwoofer"; + break; + case RT_SIDE: + rt = "side"; + break; + case RT_MCHRECORD: + rt = "multichannel recording"; + break; + } + + snprintf(buffer, 255, "EMU10Kx DSP %s PCM interface", rt); + device_set_desc_copy(dev, buffer); + return (0); +} + +static int +emu_pcm_attach(device_t dev) +{ + struct emu_pcm_info *sc; + unsigned int i; + char status[SND_STATUSLEN]; + uint32_t inte, ipr; + uintptr_t route, r, is_emu10k1; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + sc->card = (struct emu_sc_info *)(device_get_softc(device_get_parent(dev))); + if (sc->card == NULL) { + device_printf(dev, "cannot get bridge conf\n"); + free(sc, M_DEVBUF); + return (ENXIO); + } + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10kx softc"); + sc->dev = dev; + + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &is_emu10k1); + sc->is_emu10k1 = is_emu10k1 ? 1 : 0; + + sc->codec = NULL; + + for (i = 0; i < 8; i++) { + sc->rt.routing_left[i] = i; + sc->rt.amounts_left[i] = 0x00; + sc->rt.routing_right[i] = i; + sc->rt.amounts_right[i] = 0x00; + } + + for (i = 0; i < 8; i++) { + sc->rt_mono.routing_left[i] = i; + sc->rt_mono.amounts_left[i] = 0x00; + sc->rt_mono.routing_right[i] = i; + sc->rt_mono.amounts_right[i] = 0x00; + } + + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ROUTE, &route); + sc->route = route; + switch (route) { + case RT_FRONT: + sc->rt.amounts_left[0] = 0xff; + sc->rt.amounts_right[1] = 0xff; + sc->rt_mono.amounts_left[0] = 0xff; + sc->rt_mono.amounts_left[1] = 0xff; + if (sc->is_emu10k1) + sc->codec = AC97_CREATE(dev, sc, emu_ac97); + else + sc->codec = AC97_CREATE(dev, sc, emu_eac97); + if (sc->codec == NULL) { + if (mixer_init(dev, &emudspmixer_class, sc)) { + device_printf(dev, "failed to initialize DSP mixer\n"); + goto bad; + } + } else + if (mixer_init(dev, ac97_getmixerclass(), sc->codec) == -1) { + device_printf(dev, "can't initialize AC97 mixer!\n"); + goto bad; + } + break; + case RT_REAR: + sc->rt.amounts_left[2] = 0xff; + sc->rt.amounts_right[3] = 0xff; + sc->rt_mono.amounts_left[2] = 0xff; + sc->rt_mono.amounts_left[3] = 0xff; + if (mixer_init(dev, &emudspmixer_class, sc)) { + device_printf(dev, "failed to initialize mixer\n"); + goto bad; + } + break; + case RT_CENTER: + sc->rt.amounts_left[4] = 0xff; + sc->rt_mono.amounts_left[4] = 0xff; + if (mixer_init(dev, &emudspmixer_class, sc)) { + device_printf(dev, "failed to initialize mixer\n"); + goto bad; + } + break; + case RT_SUB: + sc->rt.amounts_left[5] = 0xff; + sc->rt_mono.amounts_left[5] = 0xff; + if (mixer_init(dev, &emudspmixer_class, sc)) { + device_printf(dev, "failed to initialize mixer\n"); + goto bad; + } + break; + case RT_SIDE: + sc->rt.amounts_left[6] = 0xff; + sc->rt.amounts_right[7] = 0xff; + sc->rt_mono.amounts_left[6] = 0xff; + sc->rt_mono.amounts_left[7] = 0xff; + if (mixer_init(dev, &emudspmixer_class, sc)) { + device_printf(dev, "failed to initialize mixer\n"); + goto bad; + } + break; + case RT_MCHRECORD: + /* XXX add mixer here */ + break; + default: + device_printf(dev, "invalid default route\n"); + goto bad; + } + + inte = INTE_INTERVALTIMERENB; + ipr = IPR_INTERVALTIMER; /* Used by playback */ + sc->ihandle = emu_intr_register(sc->card, inte, ipr, &emu_pcm_intr, sc); + + if (emu_pcm_init(sc) == -1) { + device_printf(dev, "unable to initialize PCM part of the card\n"); + goto bad; + } + + /* XXX we should better get number of available channels from parent */ + if (pcm_register(dev, sc, (route == RT_FRONT) ? MAX_CHANNELS : 1, (route == RT_FRONT) ? 1 : 0)) { + device_printf(dev, "can't register PCM channels!\n"); + goto bad; + } + sc->pnum = 0; + if (route != RT_MCHRECORD) + pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc); + if (route == RT_FRONT) { + for (i = 1; i < MAX_CHANNELS; i++) + pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc); + pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc); + } + if (route == RT_MCHRECORD) + pcm_addchan(dev, PCMDIR_REC, &emufxrchan_class, sc); + + snprintf(status, SND_STATUSLEN, "on %s", device_get_nameunit(device_get_parent(dev))); + pcm_setstatus(dev, status); + + return (0); + +bad: + if (sc->codec) + ac97_destroy(sc->codec); + if (sc->lock) + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + return (ENXIO); +} + +static int +emu_pcm_detach(device_t dev) +{ + int r; + struct emu_pcm_info *sc; + + sc = pcm_getdevinfo(dev); + + r = pcm_unregister(dev); + + if (r) return (r); + + emu_pcm_uninit(sc); + + if (sc->lock) + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + + return (0); +} + +static device_method_t emu_pcm_methods[] = { + DEVMETHOD(device_probe, emu_pcm_probe), + DEVMETHOD(device_attach, emu_pcm_attach), + DEVMETHOD(device_detach, emu_pcm_detach), + + {0, 0} +}; + +static driver_t emu_pcm_driver = { + "pcm", + emu_pcm_methods, + PCM_SOFTC_SIZE, + NULL, + 0, + NULL +}; +DRIVER_MODULE(snd_emu10kx_pcm, emu10kx, emu_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_emu10kx_pcm, snd_emu10kx, SND_EMU10KX_MINVER, SND_EMU10KX_PREFVER, SND_EMU10KX_MAXVER); +MODULE_DEPEND(snd_emu10kx_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_emu10kx_pcm, SND_EMU10KX_PREFVER); --- sys/dev/sound/pci/emu10kx.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/emu10kx.c Thu Jul 12 12:04:19 2007 @@ -0,0 +1,3167 @@ +/*- + * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2003-2006 Yuriy Tsibizov + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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, WHETHERIN 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. + * + * $FreeBSD: src/sys/dev/sound/pci/emu10kx.c,v 1.11 2007/06/04 18:25:04 dwmalone Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include /* for DELAY */ + +#include +#include +#include + +#include "opt_emu10kx.h" +#include + +/* hw flags */ +#define HAS_51 0x0001 +#define HAS_71 0x0002 +#define HAS_AC97 0x0004 + +#define IS_EMU10K1 0x0008 +#define IS_EMU10K2 0x0010 +#define IS_CA0102 0x0020 +#define IS_CA0108 0x0040 +#define IS_UNKNOWN 0x0080 + +#define BROKEN_DIGITAL 0x0100 +#define DIGITAL_ONLY 0x0200 + +#define IS_CARDBUS 0x0400 + +#define MODE_ANALOG 1 +#define MODE_DIGITAL 2 +#define SPDIF_MODE_PCM 1 +#define SPDIF_MODE_AC3 2 + +#define MACS 0x0 +#define MACS1 0x1 +#define MACW 0x2 +#define MACW1 0x3 +#define MACINTS 0x4 +#define MACINTW 0x5 +#define ACC3 0x6 +#define MACMV 0x7 +#define ANDXOR 0x8 +#define TSTNEG 0x9 +#define LIMIT 0xA +#define LIMIT1 0xB +#define LOG 0xC +#define EXP 0xD +#define INTERP 0xE +#define SKIP 0xF + +#define GPR(i) (sc->gpr_base+(i)) +#define INP(i) (sc->input_base+(i)) +#define OUTP(i) (sc->output_base+(i)) +#define FX(i) (i) +#define FX2(i) (sc->efxc_base+(i)) +#define DSP_CONST(i) (sc->dsp_zero+(i)) + +#define COND_NORMALIZED DSP_CONST(0x1) +#define COND_BORROW DSP_CONST(0x2) +#define COND_MINUS DSP_CONST(0x3) +#define COND_LESS_ZERO DSP_CONST(0x4) +#define COND_EQ_ZERO DSP_CONST(0x5) +#define COND_SATURATION DSP_CONST(0x6) +#define COND_NEQ_ZERO DSP_CONST(0x8) + +/* Live! Inputs */ +#define IN_AC97_L 0x00 +#define IN_AC97_R 0x01 +#define IN_AC97 IN_AC97_L +#define IN_SPDIF_CD_L 0x02 +#define IN_SPDIF_CD_R 0x03 +#define IN_SPDIF_CD IN_SPDIF_CD_L +#define IN_ZOOM_L 0x04 +#define IN_ZOOM_R 0x05 +#define IN_ZOOM IN_ZOOM_L +#define IN_TOSLINK_L 0x06 +#define IN_TOSLINK_R 0x07 +#define IN_TOSLINK IN_TOSLINK_L +#define IN_LINE1_L 0x08 +#define IN_LINE1_R 0x09 +#define IN_LINE1 IN_LINE1_L +#define IN_COAX_SPDIF_L 0x0a +#define IN_COAX_SPDIF_R 0x0b +#define IN_COAX_SPDIF IN_COAX_SPDIF_L +#define IN_LINE2_L 0x0c +#define IN_LINE2_R 0x0d +#define IN_LINE2 IN_LINE2_L +#define IN_0E 0x0e +#define IN_0F 0x0f + +/* Outputs */ +#define OUT_AC97_L 0x00 +#define OUT_AC97_R 0x01 +#define OUT_AC97 OUT_AC97_L +#define OUT_A_FRONT OUT_AC97 +#define OUT_TOSLINK_L 0x02 +#define OUT_TOSLINK_R 0x03 +#define OUT_TOSLINK OUT_TOSLINK_L +#define OUT_D_CENTER 0x04 +#define OUT_D_SUB 0x05 +#define OUT_HEADPHONE_L 0x06 +#define OUT_HEADPHONE_R 0x07 +#define OUT_HEADPHONE OUT_HEADPHONE_L +#define OUT_REAR_L 0x08 +#define OUT_REAR_R 0x09 +#define OUT_REAR OUT_REAR_L +#define OUT_ADC_REC_L 0x0a +#define OUT_ADC_REC_R 0x0b +#define OUT_ADC_REC OUT_ADC_REC_L +#define OUT_MIC_CAP 0x0c + +/* Live! 5.1 Digital, non-standart 5.1 (center & sub) outputs */ +#define OUT_A_CENTER 0x11 +#define OUT_A_SUB 0x12 + +/* Audigy Inputs */ +#define A_IN_AC97_L 0x00 +#define A_IN_AC97_R 0x01 +#define A_IN_AC97 A_IN_AC97_L +#define A_IN_SPDIF_CD_L 0x02 +#define A_IN_SPDIF_CD_R 0x03 +#define A_IN_SPDIF_CD A_IN_SPDIF_CD_L +#define A_IN_O_SPDIF_L 0x04 +#define A_IN_O_SPDIF_R 0x05 +#define A_IN_O_SPDIF A_IN_O_SPDIF_L +#define A_IN_LINE2_L 0x08 +#define A_IN_LINE2_R 0x09 +#define A_IN_LINE2 A_IN_LINE2_L +#define A_IN_R_SPDIF_L 0x0a +#define A_IN_R_SPDIF_R 0x0b +#define A_IN_R_SPDIF A_IN_R_SPDIF_L +#define A_IN_AUX2_L 0x0c +#define A_IN_AUX2_R 0x0d +#define A_IN_AUX2 A_IN_AUX2_L + +/* Audigiy Outputs */ +#define A_OUT_D_FRONT_L 0x00 +#define A_OUT_D_FRONT_R 0x01 +#define A_OUT_D_FRONT A_OUT_D_FRONT_L +#define A_OUT_D_CENTER 0x02 +#define A_OUT_D_SUB 0x03 +#define A_OUT_D_SIDE_L 0x04 +#define A_OUT_D_SIDE_R 0x05 +#define A_OUT_D_SIDE A_OUT_D_SIDE_L +#define A_OUT_D_REAR_L 0x06 +#define A_OUT_D_REAR_R 0x07 +#define A_OUT_D_REAR A_OUT_D_REAR_L + +/* on Audigy Platinum only */ +#define A_OUT_HPHONE_L 0x04 +#define A_OUT_HPHONE_R 0x05 +#define A_OUT_HPHONE A_OUT_HPHONE_L + +#define A_OUT_A_FRONT_L 0x08 +#define A_OUT_A_FRONT_R 0x09 +#define A_OUT_A_FRONT A_OUT_A_FRONT_L +#define A_OUT_A_CENTER 0x0a +#define A_OUT_A_SUB 0x0b +#define A_OUT_A_SIDE_L 0x0c +#define A_OUT_A_SIDE_R 0x0d +#define A_OUT_A_SIDE A_OUT_A_SIDE_L +#define A_OUT_A_REAR_L 0x0e +#define A_OUT_A_REAR_R 0x0f +#define A_OUT_A_REAR A_OUT_A_REAR_L +#define A_OUT_AC97_L 0x10 +#define A_OUT_AC97_R 0x11 +#define A_OUT_AC97 A_OUT_AC97_L +#define A_OUT_ADC_REC_L 0x16 +#define A_OUT_ADC_REC_R 0x17 +#define A_OUT_ADC_REC A_OUT_ADC_REC_L + +#include "emu10k1-alsa%diked.h" +#include "p16v-alsa%diked.h" +#include "p17v-alsa%diked.h" + +#define C_FRONT_L 0 +#define C_FRONT_R 1 +#define C_REC_L 2 +#define C_REC_R 3 +#define C_REAR_L 4 +#define C_REAR_R 5 +#define C_CENTER 6 +#define C_SUB 7 +#define C_SIDE_L 8 +#define C_SIDE_R 9 +#define NUM_CACHES 10 + +#define NUM_DUMMIES 64 + +#define EMU_MAX_GPR 512 +#define EMU_MAX_IRQ_CONSUMERS 32 + +struct emu_voice { + int vnum; + unsigned int b16:1, stereo:1, busy:1, running:1, ismaster:1; + int speed; + int start; + int end; + int vol; + uint32_t buf; + void *vbuf; + struct emu_voice *slave; + uint32_t sa; + uint32_t ea; +}; + +struct emu_memblk { + SLIST_ENTRY(emu_memblk) link; + void *buf; + char owner[16]; + bus_addr_t buf_addr; + uint32_t pte_start, pte_size; +}; + +struct emu_mem { + uint8_t bmap[EMU_MAXPAGES / 8]; + uint32_t *ptb_pages; + void *silent_page; + bus_addr_t silent_page_addr; + bus_addr_t ptb_pages_addr; + bus_dma_tag_t dmat; + SLIST_HEAD(, emu_memblk) blocks; +}; + +/* rm */ +struct emu_rm { + struct emu_sc_info *card; + struct mtx gpr_lock; + signed int allocmap[EMU_MAX_GPR]; + int num_gprs; + int last_free_gpr; + int num_used; +}; + +struct emu_intr_handler { + void* softc; + uint32_t intr_mask; + uint32_t inte_mask; + uint32_t(*irq_func) (void *softc, uint32_t irq); +}; + +struct emu_sc_info { + struct mtx lock; + struct mtx rw; /* Hardware exclusive access lock */ + + /* Hardware and subdevices */ + device_t dev; + device_t pcm[RT_COUNT]; + device_t midi[2]; + uint32_t type; + uint32_t rev; + + bus_space_tag_t st; + bus_space_handle_t sh; + + struct cdev *cdev; /* /dev/emu10k character device */ + struct mtx emu10kx_lock; + int emu10kx_isopen; + struct sbuf emu10kx_sbuf; + int emu10kx_bufptr; + + + /* Resources */ + struct resource *reg; + struct resource *irq; + void *ih; + + /* IRQ handlers */ + struct emu_intr_handler ihandler[EMU_MAX_IRQ_CONSUMERS]; + + /* Card HW configuration */ + unsigned int mchannel_fx; + unsigned int dsp_zero; + unsigned int code_base; + unsigned int code_size; + unsigned int gpr_base; + unsigned int num_gprs; + unsigned int input_base; + unsigned int output_base; + unsigned int efxc_base; + unsigned int opcode_shift; + unsigned int high_operand_shift; + unsigned int address_mask; + uint32_t is_emu10k1:1, is_emu10k2, is_ca0102, is_ca0108:1, + has_ac97:1, has_51:1, has_71:1, + enable_ir:1, enable_debug:1, + broken_digital:1, is_cardbus:1; + + unsigned int num_inputs; + unsigned int num_outputs; + unsigned int num_fxbuses; + unsigned int routing_code_start; + unsigned int routing_code_end; + + /* HW resources */ + struct emu_voice voice[NUM_G]; /* Hardware voices */ + uint32_t irq_mask[EMU_MAX_IRQ_CONSUMERS]; /* IRQ manager data */ + int timer[EMU_MAX_IRQ_CONSUMERS]; /* timer */ + int timerinterval; + struct emu_rm *rm; + struct emu_mem mem; /* memory */ + + /* Mixer */ + int mixer_gpr[NUM_MIXERS]; + int mixer_volcache[NUM_MIXERS]; + int cache_gpr[NUM_CACHES]; + int dummy_gpr[NUM_DUMMIES]; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *root; +}; + +static void emu_setmap(void *arg, bus_dma_segment_t * segs, int nseg, int error); +static void* emu_malloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr); +static void emu_free(struct emu_mem *mem, void *dmabuf); +static void* emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char * owner); +static int emu_memfree(struct emu_mem *mem, void *membuf); +static int emu_memstart(struct emu_mem *mem, void *membuf); + +/* /dev */ +static int emu10kx_dev_init(struct emu_sc_info *sc); +static int emu10kx_dev_uninit(struct emu_sc_info *sc); +static int emu10kx_prepare(struct emu_sc_info *sc, struct sbuf *s); + +static void emumix_set_mode(struct emu_sc_info *sc, int mode); +static void emumix_set_spdif_mode(struct emu_sc_info *sc, int mode); +static void emumix_set_fxvol(struct emu_sc_info *sc, unsigned gpr, int32_t vol); +static void emumix_set_gpr(struct emu_sc_info *sc, unsigned gpr, int32_t val); +static int sysctl_emu_mixer_control(SYSCTL_HANDLER_ARGS); + +static int emu_rm_init(struct emu_sc_info *sc); +static int emu_rm_uninit(struct emu_sc_info *sc); +static int emu_rm_gpr_alloc(struct emu_rm *rm, int count); + +static unsigned int emu_getcard(device_t dev); +static uint32_t emu_rd_nolock(struct emu_sc_info *sc, unsigned int regno, unsigned int size); +static void emu_wr_nolock(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size); +static void emu_wr_cbptr(struct emu_sc_info *sc, uint32_t data); + +static void emu_vstop(struct emu_sc_info *sc, char channel, int enable); + +static void emu_intr(void *p); +static void emu_wrefx(struct emu_sc_info *sc, unsigned int pc, unsigned int data); +static void emu_addefxop(struct emu_sc_info *sc, unsigned int op, unsigned int z, unsigned int w, unsigned int x, unsigned int y, uint32_t * pc); +static void emu_initefx(struct emu_sc_info *sc); + +static int emu_cardbus_init(struct emu_sc_info *sc); +static int emu_init(struct emu_sc_info *sc); +static int emu_uninit(struct emu_sc_info *sc); + +static int emu_read_ivar(device_t bus __unused, device_t dev, int ivar_index, uintptr_t * result); +static int emu_write_ivar(device_t bus __unused, device_t dev __unused, + int ivar_index, uintptr_t value __unused); + +static int emu_pci_probe(device_t dev); +static int emu_pci_attach(device_t dev); +static int emu_pci_detach(device_t dev); +static int emu_modevent(module_t mod __unused, int cmd, void *data __unused); + +/* Supported cards */ +struct emu_hwinfo { + uint16_t vendor; + uint16_t device; + uint16_t subvendor; + uint16_t subdevice; + char SBcode[8]; + char desc[32]; + int flags; +}; + +static struct emu_hwinfo emu_cards[] = { + {0xffff, 0xffff, 0xffff, 0xffff, "BADCRD", "Not a compatible card", 0}, + /* 0x0020..0x002f 4.0 EMU10K1 cards */ + {0x1102, 0x0002, 0x1102, 0x0020, "CT4850", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x0021, "CT4620", "SBLive!", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x002f, "CT????", "SBLive! mainboard implementation", HAS_AC97 | IS_EMU10K1}, + + /* (range unknown) 5.1 EMU10K1 cards */ + {0x1102, 0x0002, 0x1102, 0x100a, "CT????", "SBLive! 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, + + /* 0x80??..0x805? 4.0 EMU10K1 cards */ + {0x1102, 0x0002, 0x1102, 0x8022, "CT4780", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8023, "CT4790", "SB PCI512", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8024, "CT4760", "SBLive!", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8025, "CT????", "SBLive! Mainboard Implementation", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8026, "CT4830", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8027, "CT4832", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8028, "CT4760", "SBLive! OEM version", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8031, "CT4831", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8040, "CT4760", "SBLive!", HAS_AC97 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8051, "CT4850", "SBLive! Value", HAS_AC97 | IS_EMU10K1}, + + /* 0x8061..0x???? 5.1 EMU10K1 cards */ + {0x1102, 0x0002, 0x1102, 0x8061, "SB????", "SBLive! Player 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8064, "SB????", "SBLive! 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8065, "SB0220", "SBLive! 5.1 Digital", HAS_AC97 | HAS_51 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8066, "CT4780", "SBLive! 5.1 Digital", HAS_AC97 | HAS_51 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8067, "SB????", "SBLive!", HAS_AC97 | HAS_51 | IS_EMU10K1}, + + /* Generic SB Live! */ + {0x1102, 0x0002, 0x1102, 0x0000, "SB????", "SBLive! (Unknown model)", HAS_AC97 | IS_EMU10K1}, + + /* 0x0041..0x0043 EMU10K2 (some kind of Audigy) cards */ + + /* 0x0051..0x0051 5.1 CA0100-IAF cards */ + {0x1102, 0x0004, 0x1102, 0x0051, "SB0090", "Audigy", HAS_AC97 | HAS_51 | IS_EMU10K2}, + /* ES is CA0100-IDF chip that don't work in digital mode */ + {0x1102, 0x0004, 0x1102, 0x0052, "SB0160", "Audigy ES", HAS_AC97 | HAS_71 | IS_EMU10K2 | BROKEN_DIGITAL}, + /* 0x0053..0x005C 5.1 CA0101-NAF cards */ + {0x1102, 0x0004, 0x1102, 0x0053, "SB0090", "Audigy Player/OEM", HAS_AC97 | HAS_51 | IS_EMU10K2}, + {0x1102, 0x0004, 0x1102, 0x0058, "SB0090", "Audigy Player/OEM", HAS_AC97 | HAS_51 | IS_EMU10K2}, + + /* 0x1002..0x1009 5.1 CA0102-IAT cards */ + {0x1102, 0x0004, 0x1102, 0x1002, "SB????", "Audigy 2 Platinum", HAS_51 | IS_CA0102}, + {0x1102, 0x0004, 0x1102, 0x1005, "SB????", "Audigy 2 Platinum EX", HAS_51 | IS_CA0102}, + {0x1102, 0x0004, 0x1102, 0x1007, "SB0240", "Audigy 2", HAS_AC97 | HAS_51 | IS_CA0102}, + + /* 0x2001..0x2003 7.1 CA0102-ICT cards */ + {0x1102, 0x0004, 0x1102, 0x2001, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, + {0x1102, 0x0004, 0x1102, 0x2002, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, + /* XXX No reports about 0x2003 & 0x2004 cards */ + {0x1102, 0x0004, 0x1102, 0x2003, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, + {0x1102, 0x0004, 0x1102, 0x2004, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, + {0x1102, 0x0004, 0x1102, 0x2005, "SB0350", "Audigy 2 ZS", HAS_AC97 | HAS_71 | IS_CA0102}, + + /* (range unknown) 7.1 CA0102-xxx Audigy 4 cards */ + {0x1102, 0x0004, 0x1102, 0x2007, "SB0380", "Audigy 4 Pro", HAS_AC97 | HAS_71 | IS_CA0102}, + + /* Generic Audigy or Audigy 2 */ + {0x1102, 0x0004, 0x1102, 0x0000, "SB????", "Audigy (Unknown model)", HAS_AC97 | HAS_51 | IS_EMU10K2}, + + /* We don't support CA0103-DAT (Audigy LS) cards */ + /* There is NO CA0104-xxx cards */ + /* There is NO CA0105-xxx cards */ + /* We don't support CA0106-DAT (SB Live! 24 bit) cards */ + /* There is NO CA0107-xxx cards */ + + /* 0x1000..0x1001 7.1 CA0108-IAT cards */ + {0x1102, 0x0008, 0x1102, 0x1000, "SB????", "Audigy 2 LS", HAS_AC97 | HAS_51 | IS_CA0108 | DIGITAL_ONLY}, + {0x1102, 0x0008, 0x1102, 0x1001, "SB0400", "Audigy 2 Value", HAS_AC97 | HAS_71 | IS_CA0108 | DIGITAL_ONLY}, + {0x1102, 0x0008, 0x1102, 0x1021, "SB0610", "Audigy 4", HAS_AC97 | HAS_71 | IS_CA0108 | DIGITAL_ONLY}, + + {0x1102, 0x0008, 0x1102, 0x2001, "SB0530", "Audigy 2 ZS CardBus", HAS_AC97 | HAS_71 | IS_CA0108 | IS_CARDBUS}, + + {0x1102, 0x0008, 0x0000, 0x0000, "SB????", "Audigy 2 Value (Unknown model)", HAS_AC97 | HAS_51 | IS_CA0108}, +}; +/* Unsupported cards */ + +static struct emu_hwinfo emu_bad_cards[] = { + /* APS cards should be possible to support */ + {0x1102, 0x0002, 0x1102, 0x4001, "EMUAPS", "E-mu APS", 0}, + {0x1102, 0x0002, 0x1102, 0x4002, "EMUAPS", "E-mu APS", 0}, + {0x1102, 0x0004, 0x1102, 0x4001, "EMU???", "E-mu 1212m [4001]", 0}, + /* Similar-named ("Live!" or "Audigy") cards on different chipsets */ + {0x1102, 0x8064, 0x0000, 0x0000, "SB0100", "SBLive! 5.1 OEM", 0}, + {0x1102, 0x0006, 0x0000, 0x0000, "SB0200", "DELL OEM SBLive! Value", 0}, + {0x1102, 0x0007, 0x0000, 0x0000, "SB0310", "Audigy LS", 0}, +}; + +/* + * Get best known information about device. + */ +static unsigned int +emu_getcard(device_t dev) +{ + uint16_t device; + uint16_t subdevice; + int n_cards; + unsigned int thiscard; + int i; + + device = pci_read_config(dev, PCIR_DEVICE, /* bytes */ 2); + subdevice = pci_read_config(dev, PCIR_SUBDEV_0, /* bytes */ 2); + + n_cards = sizeof(emu_cards) / sizeof(struct emu_hwinfo); + thiscard = 0; + for (i = 1; i < n_cards; i++) { + if (device == emu_cards[i].device) { + if (subdevice == emu_cards[i].subdevice) { + thiscard = i; + break; + } + if (0x0000 == emu_cards[i].subdevice) { + thiscard = i; + /* don't break, we can get more specific card + * later in the list */ + } + } + } + + n_cards = sizeof(emu_bad_cards) / sizeof(struct emu_hwinfo); + for (i = 0; i < n_cards; i++) { + if (device == emu_bad_cards[i].device) { + if (subdevice == emu_bad_cards[i].subdevice) { + thiscard = 0; + break; + } + if (0x0000 == emu_bad_cards[i].subdevice) { + thiscard = 0; + break; /* we avoid all this cards */ + } + } + } + return (thiscard); +} + + +/* + * Base hardware interface are 32 (Audigy) or 64 (Audigy2) registers. + * Some of them are used directly, some of them provide pointer / data pairs. + */ +static uint32_t +emu_rd_nolock(struct emu_sc_info *sc, unsigned int regno, unsigned int size) +{ + + KASSERT(sc != NULL, ("emu_rd: NULL sc")); + switch (size) { + case 1: + return (bus_space_read_1(sc->st, sc->sh, regno)); + case 2: + return (bus_space_read_2(sc->st, sc->sh, regno)); + case 4: + return (bus_space_read_4(sc->st, sc->sh, regno)); + } + return (0xffffffff); +} + +static void +emu_wr_nolock(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size) +{ + + KASSERT(sc != NULL, ("emu_rd: NULL sc")); + switch (size) { + case 1: + bus_space_write_1(sc->st, sc->sh, regno, data); + break; + case 2: + bus_space_write_2(sc->st, sc->sh, regno, data); + break; + case 4: + bus_space_write_4(sc->st, sc->sh, regno, data); + break; + } +} +/* + * PTR / DATA interface. Access to EMU10Kx is made + * via (channel, register) pair. Some registers are channel-specific, + * some not. + */ +uint32_t +emu_rdptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg) +{ + uint32_t ptr, val, mask, size, offset; + + ptr = ((reg << 16) & sc->address_mask) | (chn & PTR_CHANNELNUM_MASK); + mtx_lock(&sc->rw); + emu_wr_nolock(sc, PTR, ptr, 4); + val = emu_rd_nolock(sc, DATA, 4); + mtx_unlock(&sc->rw); + /* + * XXX Some register numbers has data size and offset encoded in + * it to get only part of 32bit register. This use is not described + * in register name, be careful! + */ + if (reg & 0xff000000) { + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + val &= mask; + val >>= offset; + } + return (val); +} + +void +emu_wrptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg, uint32_t data) +{ + uint32_t ptr, mask, size, offset; + ptr = ((reg << 16) & sc->address_mask) | (chn & PTR_CHANNELNUM_MASK); + mtx_lock(&sc->rw); + emu_wr_nolock(sc, PTR, ptr, 4); + /* + * XXX Another kind of magic encoding in register number. This can + * give you side effect - it will read previous data from register + * and change only required bits. + */ + if (reg & 0xff000000) { + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data <<= offset; + data &= mask; + data |= emu_rd_nolock(sc, DATA, 4) & ~mask; + } + emu_wr_nolock(sc, DATA, data, 4); + mtx_unlock(&sc->rw); +} +/* + * PTR2 / DATA2 interface. Access to P16v is made + * via (channel, register) pair. Some registers are channel-specific, + * some not. This interface is supported by CA0102 and CA0108 chips only. + */ +uint32_t +emu_rd_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg) +{ + uint32_t val; + + mtx_lock(&sc->rw); + emu_wr_nolock(sc, PTR2, (reg << 16) | chn, 4); + val = emu_rd_nolock(sc, DATA2, 4); + mtx_unlock(&sc->rw); + return (val); +} + +void +emu_wr_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg, uint32_t data) +{ + + mtx_lock(&sc->rw); + emu_wr_nolock(sc, PTR2, (reg << 16) | chn, 4); + emu_wr_nolock(sc, DATA2, data, 4); + mtx_unlock(&sc->rw); +} +/* + * XXX CardBus interface. Not tested on any real hardware. + */ +static void +emu_wr_cbptr(struct emu_sc_info *sc, uint32_t data) +{ + uint32_t val; + + /* 0x38 is IPE3 (CD S/PDIF interrupt pending register) on CA0102 Seems + * to be some reg/value accessible kind of config register on CardBus + * CA0108, with value(?) in top 16 bit, address(?) in low 16 */ + mtx_lock(&sc->rw); + val = emu_rd_nolock(sc, 0x38, 4); + emu_wr_nolock(sc, 0x38, data, 4); + val = emu_rd_nolock(sc, 0x38, 4); + mtx_unlock(&sc->rw); +} + +/* + * Direct hardware register access + */ +void +emu_wr(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size) +{ + + mtx_lock(&sc->rw); + emu_wr_nolock(sc, regno, data, size); + mtx_unlock(&sc->rw); +} + +uint32_t +emu_rd(struct emu_sc_info *sc, unsigned int regno, unsigned int size) +{ + uint32_t rd; + + mtx_lock(&sc->rw); + rd = emu_rd_nolock(sc, regno, size); + mtx_unlock(&sc->rw); + return (rd); +} + +/* + * Enabling IR MIDI messages is another kind of black magic. It just + * has to be made this way. It really do it. + */ +void +emu_enable_ir(struct emu_sc_info *sc) +{ + uint32_t iocfg; + + mtx_lock(&sc->rw); + if (sc->is_emu10k2 || sc->is_ca0102) { + iocfg = emu_rd_nolock(sc, A_IOCFG, 2); + emu_wr_nolock(sc, A_IOCFG, iocfg | A_IOCFG_GPOUT2, 2); + DELAY(500); + emu_wr_nolock(sc, A_IOCFG, iocfg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, 2); + DELAY(500); + emu_wr_nolock(sc, A_IOCFG, iocfg | A_IOCFG_GPOUT1, 2); + DELAY(100); + emu_wr_nolock(sc, A_IOCFG, iocfg, 2); + device_printf(sc->dev, "Audigy IR MIDI events enabled.\n"); + sc->enable_ir = 1; + } + if (sc->is_emu10k1) { + iocfg = emu_rd_nolock(sc, HCFG, 4); + emu_wr_nolock(sc, HCFG, iocfg | HCFG_GPOUT2, 4); + DELAY(500); + emu_wr_nolock(sc, HCFG, iocfg | HCFG_GPOUT1 | HCFG_GPOUT2, 4); + DELAY(100); + emu_wr_nolock(sc, HCFG, iocfg, 4); + device_printf(sc->dev, "SB Live! IR MIDI events enabled.\n"); + sc->enable_ir = 1; + } + mtx_unlock(&sc->rw); +} + + +/* + * emu_timer_ - HW timer managment + */ +int +emu_timer_create(struct emu_sc_info *sc) +{ + int i, timer; + + timer = -1; + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) + if (sc->timer[i] == 0) { + sc->timer[i] = -1; /* disable it */ + timer = i; + return (timer); + } + + return (-1); +} + +int +emu_timer_set(struct emu_sc_info *sc, int timer, int delay) +{ + int i; + + if(timer < 0) + return (-1); + + RANGE(delay, 16, 1024); + RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); + + sc->timer[timer] = delay; + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) + if (sc->timerinterval > sc->timer[i]) + sc->timerinterval = sc->timer[i]; + + emu_wr(sc, TIMER, sc->timerinterval & 0x03ff, 2); + return (timer); +} + +int +emu_timer_enable(struct emu_sc_info *sc, int timer, int go) +{ + uint32_t x; + int ena_int; + int i; + + if(timer < 0) + return (-1); + + RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); + + mtx_lock(&sc->lock); + + if ((go == 1) && (sc->timer[timer] < 0)) + sc->timer[timer] = -sc->timer[timer]; + if ((go == 0) && (sc->timer[timer] > 0)) + sc->timer[timer] = -sc->timer[timer]; + + ena_int = 0; + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) { + if (sc->timerinterval > sc->timer[i]) + sc->timerinterval = sc->timer[i]; + if (sc->timer[i] > 0) + ena_int = 1; + } + + emu_wr(sc, TIMER, sc->timerinterval & 0x03ff, 2); + + if (ena_int == 1) { + x = emu_rd(sc, INTE, 4); + x |= INTE_INTERVALTIMERENB; + emu_wr(sc, INTE, x, 4); + } else { + x = emu_rd(sc, INTE, 4); + x &= ~INTE_INTERVALTIMERENB; + emu_wr(sc, INTE, x, 4); + } + mtx_unlock(&sc->lock); + return (0); +} + +int +emu_timer_clear(struct emu_sc_info *sc, int timer) +{ + if(timer < 0) + return (-1); + + RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); + + emu_timer_enable(sc, timer, 0); + + mtx_lock(&sc->lock); + if (sc->timer[timer] != 0) + sc->timer[timer] = 0; + mtx_unlock(&sc->lock); + + return (timer); +} + +/* + * emu_intr_ - HW interrupt handler managment + */ +int +emu_intr_register(struct emu_sc_info *sc, uint32_t inte_mask, uint32_t intr_mask, uint32_t(*func) (void *softc, uint32_t irq), void *isc) +{ + int i; + uint32_t x; + + mtx_lock(&sc->lock); + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) + if (sc->ihandler[i].inte_mask == 0) { + sc->ihandler[i].inte_mask = inte_mask; + sc->ihandler[i].intr_mask = intr_mask; + sc->ihandler[i].softc = isc; + sc->ihandler[i].irq_func = func; + x = emu_rd(sc, INTE, 4); + x |= inte_mask; + emu_wr(sc, INTE, x, 4); + mtx_unlock(&sc->lock); +#ifdef SND_EMU10KX_DEBUG + device_printf(sc->dev, "ihandle %d registered\n", i); +#endif + return (i); + } + mtx_unlock(&sc->lock); +#ifdef SND_EMU10KX_DEBUG + device_printf(sc->dev, "ihandle not registered\n"); +#endif + return (-1); +} + +int +emu_intr_unregister(struct emu_sc_info *sc, int hnumber) +{ + uint32_t x; + int i; + + mtx_lock(&sc->lock); + + if (sc->ihandler[hnumber].inte_mask == 0) { + mtx_unlock(&sc->lock); + return (-1); + } + + x = emu_rd(sc, INTE, 4); + x &= ~sc->ihandler[hnumber].inte_mask; + + sc->ihandler[hnumber].inte_mask = 0; + sc->ihandler[hnumber].intr_mask = 0; + sc->ihandler[hnumber].softc = NULL; + sc->ihandler[hnumber].irq_func = NULL; + + /* other interupt handlers may use this INTE value */ + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) + if (sc->ihandler[i].inte_mask != 0) + x |= sc->ihandler[i].inte_mask; + + emu_wr(sc, INTE, x, 4); + + mtx_unlock(&sc->lock); + return (hnumber); +} + +static void +emu_intr(void *p) +{ + struct emu_sc_info *sc = (struct emu_sc_info *)p; + uint32_t stat, ack; + int i; + + for (;;) { + stat = emu_rd(sc, IPR, 4); + ack = 0; + if (stat == 0) + break; + emu_wr(sc, IPR, stat, 4); + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) { + if ((((sc->ihandler[i].intr_mask) & stat) != 0) && + (((void *)sc->ihandler[i].irq_func) != NULL)) { + ack |= sc->ihandler[i].irq_func(sc->ihandler[i].softc, + (sc->ihandler[i].intr_mask) & stat); + } + } +#ifdef SND_EMU10KX_DEBUG + if(stat & (~ack)) + device_printf(sc->dev, "Unhandled interrupt: %08x\n", stat & (~ack)); +#endif + } + + if ((sc->is_ca0102) || (sc->is_ca0108)) + for (;;) { + stat = emu_rd(sc, IPR2, 4); + ack = 0; + if (stat == 0) + break; + emu_wr(sc, IPR2, stat, 4); + device_printf(sc->dev, "IPR2: %08x\n", stat); + break; /* to avoid infinite loop. shoud be removed + * after completion of P16V interface. */ + } + + if (sc->is_ca0102) + for (;;) { + stat = emu_rd(sc, IPR3, 4); + ack = 0; + if (stat == 0) + break; + emu_wr(sc, IPR3, stat, 4); + device_printf(sc->dev, "IPR3: %08x\n", stat); + break; /* to avoid infinite loop. should be removed + * after completion of S/PDIF interface */ + } +} + + +/* + * Get data from private emu10kx structure for PCM buffer allocation. + * Used by PCM code only. + */ +bus_dma_tag_t +emu_gettag(struct emu_sc_info *sc) +{ + return (sc->mem.dmat); +} + +static void +emu_setmap(void *arg, bus_dma_segment_t * segs, int nseg, int error) +{ + bus_addr_t *phys = (bus_addr_t *) arg; + + *phys = error ? 0 : (bus_addr_t) segs->ds_addr; + + if (bootverbose) { + printf("emu10kx: setmap (%lx, %lx), nseg=%d, error=%d\n", + (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, + nseg, error); + } +} + +static void * +emu_malloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr) +{ + void *dmabuf; + bus_dmamap_t map; + + *addr = 0; + if (bus_dmamem_alloc(mem->dmat, &dmabuf, BUS_DMA_NOWAIT, &map)) + return (NULL); + if (bus_dmamap_load(mem->dmat, map, dmabuf, sz, emu_setmap, addr, 0) || !*addr) + return (NULL); + return (dmabuf); +} + +static void +emu_free(struct emu_mem *mem, void *dmabuf) +{ + bus_dmamem_free(mem->dmat, dmabuf, NULL); +} + +static void * +emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char *owner) +{ + uint32_t blksz, start, idx, ofs, tmp, found; + struct emu_memblk *blk; + void *membuf; + + blksz = sz / EMUPAGESIZE; + if (sz > (blksz * EMUPAGESIZE)) + blksz++; + if (blksz > EMU_MAX_BUFSZ / EMUPAGESIZE) + return (NULL); + /* find a free block in the bitmap */ + found = 0; + start = 1; + while (!found && start + blksz < EMU_MAXPAGES) { + found = 1; + for (idx = start; idx < start + blksz; idx++) + if (mem->bmap[idx >> 3] & (1 << (idx & 7))) + found = 0; + if (!found) + start++; + } + if (!found) + return (NULL); + blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT); + if (blk == NULL) + return (NULL); + bzero(blk, sizeof(*blk)); + membuf = emu_malloc(mem, sz, &blk->buf_addr); + *addr = blk->buf_addr; + if (membuf == NULL) { + free(blk, M_DEVBUF); + return (NULL); + } + blk->buf = membuf; + blk->pte_start = start; + blk->pte_size = blksz; + strncpy(blk->owner, owner, 15); + blk->owner[15] = '\0'; +#ifdef SND_EMU10KX_DEBUG + printf("emu10kx emu_memalloc: allocating %d for %s\n", blk->pte_size, blk->owner); +#endif + ofs = 0; + for (idx = start; idx < start + blksz; idx++) { + mem->bmap[idx >> 3] |= 1 << (idx & 7); + tmp = (uint32_t) (u_long) ((uint8_t *) blk->buf_addr + ofs); + mem->ptb_pages[idx] = (tmp << 1) | idx; + ofs += EMUPAGESIZE; + } + SLIST_INSERT_HEAD(&mem->blocks, blk, link); + return (membuf); +} + +static int +emu_memfree(struct emu_mem *mem, void *membuf) +{ + uint32_t idx, tmp; + struct emu_memblk *blk, *i; + + blk = NULL; + SLIST_FOREACH(i, &mem->blocks, link) { + if (i->buf == membuf) + blk = i; + } + if (blk == NULL) + return (EINVAL); +#ifdef SND_EMU10KX_DEBUG + printf("emu10kx emu_memfree: freeing %d for %s\n", blk->pte_size, blk->owner); +#endif + SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link); + emu_free(mem, membuf); + tmp = (uint32_t) (mem->silent_page_addr) << 1; + for (idx = blk->pte_start; idx < blk->pte_start + blk->pte_size; idx++) { + mem->bmap[idx >> 3] &= ~(1 << (idx & 7)); + mem->ptb_pages[idx] = tmp | idx; + } + free(blk, M_DEVBUF); + return (0); +} + +static int +emu_memstart(struct emu_mem *mem, void *membuf) +{ + struct emu_memblk *blk, *i; + + blk = NULL; + SLIST_FOREACH(i, &mem->blocks, link) { + if (i->buf == membuf) + blk = i; + } + if (blk == NULL) + return (-1); + return (blk->pte_start); +} + + +static uint32_t +emu_rate_to_pitch(uint32_t rate) +{ + static uint32_t logMagTable[128] = { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, + 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, + 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, + 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, + 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, + 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, + 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, + 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, + 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, + 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, + 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, + 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, + 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, + 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df + }; + static char logSlopeTable[128] = { + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f + }; + int i; + + if (rate == 0) + return (0); + rate *= 11185; /* Scale 48000 to 0x20002380 */ + for (i = 31; i > 0; i--) { + if (rate & 0x80000000) { /* Detect leading "1" */ + return (((uint32_t) (i - 15) << 20) + + logMagTable[0x7f & (rate >> 24)] + + (0x7f & (rate >> 17)) * + logSlopeTable[0x7f & (rate >> 24)]); + } + rate <<= 1; + } + /* NOTREACHED */ + return (0); +} + +static uint32_t +emu_rate_to_linearpitch(uint32_t rate) +{ + rate = (rate << 8) / 375; + return ((rate >> 1) + (rate & 1)); +} + +struct emu_voice * +emu_valloc(struct emu_sc_info *sc) +{ + struct emu_voice *v; + int i; + + v = NULL; + mtx_lock(&sc->lock); + for (i = 0; i < NUM_G && sc->voice[i].busy; i++); + if (i < NUM_G) { + v = &sc->voice[i]; + v->busy = 1; + } + mtx_unlock(&sc->lock); + return (v); +} + +void +emu_vfree(struct emu_sc_info *sc, struct emu_voice *v) +{ + int i, r; + + mtx_lock(&sc->lock); + for (i = 0; i < NUM_G; i++) { + if (v == &sc->voice[i] && sc->voice[i].busy) { + v->busy = 0; + /* XXX What we should do with mono channels? + See -pcm.c emupchan_init for other side of + this problem */ + if (v->slave != NULL) + r = emu_memfree(&sc->mem, v->vbuf); + } + } + mtx_unlock(&sc->lock); +} + +int +emu_vinit(struct emu_sc_info *sc, struct emu_voice *m, struct emu_voice *s, + uint32_t sz, struct snd_dbuf *b) +{ + void *vbuf; + bus_addr_t tmp_addr; + + vbuf = emu_memalloc(&sc->mem, sz, &tmp_addr, "vinit"); + if (vbuf == NULL) + return (ENOMEM); + if (b != NULL) + sndbuf_setup(b, vbuf, sz); + m->start = emu_memstart(&sc->mem, vbuf) * EMUPAGESIZE; + if (m->start == -1) { + emu_memfree(&sc->mem, vbuf); + return (ENOMEM); + } + m->end = m->start + sz; + m->speed = 0; + m->b16 = 0; + m->stereo = 0; + m->running = 0; + m->ismaster = 1; + m->vol = 0xff; + m->buf = tmp_addr; + m->vbuf = vbuf; + m->slave = s; + if (s != NULL) { + s->start = m->start; + s->end = m->end; + s->speed = 0; + s->b16 = 0; + s->stereo = 0; + s->running = 0; + s->ismaster = 0; + s->vol = m->vol; + s->buf = m->buf; + s->vbuf = NULL; + s->slave = NULL; + } + return (0); +} + +void +emu_vsetup(struct emu_voice *v, int fmt, int spd) +{ + if (fmt) { + v->b16 = (fmt & AFMT_16BIT) ? 1 : 0; + v->stereo = (fmt & AFMT_STEREO) ? 1 : 0; + if (v->slave != NULL) { + v->slave->b16 = v->b16; + v->slave->stereo = v->stereo; + } + } + if (spd) { + v->speed = spd; + if (v->slave != NULL) + v->slave->speed = v->speed; + } +} + +void +emu_vroute(struct emu_sc_info *sc, struct emu_route *rt, struct emu_voice *v) +{ + unsigned int routing[8], amounts[8]; + int i; + + for (i = 0; i < 8; i++) { + routing[i] = rt->routing_left[i]; + amounts[i] = rt->amounts_left[i]; + } + if ((v->stereo) && (v->ismaster == 0)) + for (i = 0; i < 8; i++) { + routing[i] = rt->routing_right[i]; + amounts[i] = rt->amounts_right[i]; + } + + if (sc->is_emu10k1) { + emu_wrptr(sc, v->vnum, FXRT, ((routing[3] << 12) | + (routing[2] << 8) | + (routing[1] << 4) | + (routing[0] << 0)) << 16); + } else { + emu_wrptr(sc, v->vnum, A_FXRT1, (routing[3] << 24) | + (routing[2] << 16) | + (routing[1] << 8) | + (routing[0] << 0)); + emu_wrptr(sc, v->vnum, A_FXRT2, (routing[7] << 24) | + (routing[6] << 16) | + (routing[5] << 8) | + (routing[4] << 0)); + emu_wrptr(sc, v->vnum, A_SENDAMOUNTS, (amounts[7] << 24) | + (amounts[6] << 26) | + (amounts[5] << 8) | + (amounts[4] << 0)); + } + emu_wrptr(sc, v->vnum, PTRX, (amounts[0] << 8) | (amounts[1] << 0)); + emu_wrptr(sc, v->vnum, DSL, v->ea | (amounts[3] << 24)); + emu_wrptr(sc, v->vnum, PSST, v->sa | (amounts[2] << 24)); + if ((v->stereo) && (v->slave != NULL)) + emu_vroute(sc, rt, v->slave); +} + +void +emu_vwrite(struct emu_sc_info *sc, struct emu_voice *v) +{ + int s; + uint32_t am_2, am_3, start, val, silent_page; + + s = (v->stereo ? 1 : 0) + (v->b16 ? 1 : 0); + + v->sa = v->start >> s; + v->ea = v->end >> s; + + + if (v->stereo) { + emu_wrptr(sc, v->vnum, CPF, CPF_STEREO_MASK); + } else { + emu_wrptr(sc, v->vnum, CPF, 0); + } + val = v->stereo ? 28 : 30; + val *= v->b16 ? 1 : 2; + start = v->sa + val; + + am_3 = emu_rdptr(sc, v->vnum, DSL) & 0xff000000; + emu_wrptr(sc, v->vnum, DSL, v->ea | am_3); + am_2 = emu_rdptr(sc, v->vnum, PSST) & 0xff000000; + emu_wrptr(sc, v->vnum, PSST, v->sa | am_2); + + emu_wrptr(sc, v->vnum, CCCA, start | (v->b16 ? 0 : CCCA_8BITSELECT)); + emu_wrptr(sc, v->vnum, Z1, 0); + emu_wrptr(sc, v->vnum, Z2, 0); + + silent_page = ((uint32_t) (sc->mem.silent_page_addr) << 1) | MAP_PTI_MASK; + emu_wrptr(sc, v->vnum, MAPA, silent_page); + emu_wrptr(sc, v->vnum, MAPB, silent_page); + + emu_wrptr(sc, v->vnum, CVCF, CVCF_CURRENTFILTER_MASK); + emu_wrptr(sc, v->vnum, VTFT, VTFT_FILTERTARGET_MASK); + emu_wrptr(sc, v->vnum, ATKHLDM, 0); + emu_wrptr(sc, v->vnum, DCYSUSM, DCYSUSM_DECAYTIME_MASK); + emu_wrptr(sc, v->vnum, LFOVAL1, 0x8000); + emu_wrptr(sc, v->vnum, LFOVAL2, 0x8000); + emu_wrptr(sc, v->vnum, FMMOD, 0); + emu_wrptr(sc, v->vnum, TREMFRQ, 0); + emu_wrptr(sc, v->vnum, FM2FRQ2, 0); + emu_wrptr(sc, v->vnum, ENVVAL, 0x8000); + + emu_wrptr(sc, v->vnum, ATKHLDV, ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); + emu_wrptr(sc, v->vnum, ENVVOL, 0x8000); + + emu_wrptr(sc, v->vnum, PEFE_FILTERAMOUNT, 0x7f); + emu_wrptr(sc, v->vnum, PEFE_PITCHAMOUNT, 0); + if ((v->stereo) && (v->slave != NULL)) + emu_vwrite(sc, v->slave); +} + +static void +emu_vstop(struct emu_sc_info *sc, char channel, int enable) +{ + int reg; + + reg = (channel & 0x20) ? SOLEH : SOLEL; + channel &= 0x1f; + reg |= 1 << 24; + reg |= channel << 16; + emu_wrptr(sc, 0, reg, enable); +} + +void +emu_vtrigger(struct emu_sc_info *sc, struct emu_voice *v, int go) +{ + uint32_t pitch_target, initial_pitch; + uint32_t cra, cs, ccis; + uint32_t sample, i; + + if (go) { + cra = 64; + cs = v->stereo ? 4 : 2; + ccis = v->stereo ? 28 : 30; + ccis *= v->b16 ? 1 : 2; + sample = v->b16 ? 0x00000000 : 0x80808080; + for (i = 0; i < cs; i++) + emu_wrptr(sc, v->vnum, CD0 + i, sample); + emu_wrptr(sc, v->vnum, CCR_CACHEINVALIDSIZE, 0); + emu_wrptr(sc, v->vnum, CCR_READADDRESS, cra); + emu_wrptr(sc, v->vnum, CCR_CACHEINVALIDSIZE, ccis); + + emu_wrptr(sc, v->vnum, IFATN, 0xff00); + emu_wrptr(sc, v->vnum, VTFT, 0xffffffff); + emu_wrptr(sc, v->vnum, CVCF, 0xffffffff); + emu_wrptr(sc, v->vnum, DCYSUSV, 0x00007f7f); + emu_vstop(sc, v->vnum, 0); + + pitch_target = emu_rate_to_linearpitch(v->speed); + initial_pitch = emu_rate_to_pitch(v->speed) >> 8; + emu_wrptr(sc, v->vnum, PTRX_PITCHTARGET, pitch_target); + emu_wrptr(sc, v->vnum, CPF_CURRENTPITCH, pitch_target); + emu_wrptr(sc, v->vnum, IP, initial_pitch); + } else { + emu_wrptr(sc, v->vnum, PTRX_PITCHTARGET, 0); + emu_wrptr(sc, v->vnum, CPF_CURRENTPITCH, 0); + emu_wrptr(sc, v->vnum, IFATN, 0xffff); + emu_wrptr(sc, v->vnum, VTFT, 0x0000ffff); + emu_wrptr(sc, v->vnum, CVCF, 0x0000ffff); + emu_wrptr(sc, v->vnum, IP, 0); + emu_vstop(sc, v->vnum, 1); + } + if ((v->stereo) && (v->slave != NULL)) + emu_vtrigger(sc, v->slave, go); +} + +int +emu_vpos(struct emu_sc_info *sc, struct emu_voice *v) +{ + int s, ptr; + + s = (v->b16 ? 1 : 0) + (v->stereo ? 1 : 0); + ptr = (emu_rdptr(sc, v->vnum, CCCA_CURRADDR) - (v->start >> s)) << s; + return (ptr & ~0x0000001f); +} + + +/* fx */ +static void +emu_wrefx(struct emu_sc_info *sc, unsigned int pc, unsigned int data) +{ + emu_wrptr(sc, 0, sc->code_base + pc, data); +} + + +static void +emu_addefxop(struct emu_sc_info *sc, unsigned int op, unsigned int z, unsigned int w, unsigned int x, unsigned int y, uint32_t * pc) +{ + if ((*pc) + 1 > sc->code_size) { + device_printf(sc->dev, "DSP CODE OVERRUN: attept to write past code_size (pc=%d)\n", (*pc)); + return; + } + emu_wrefx(sc, (*pc) * 2, (x << sc->high_operand_shift) | y); + emu_wrefx(sc, (*pc) * 2 + 1, (op << sc->opcode_shift) | (z << sc->high_operand_shift) | w); + (*pc)++; +} + +static int +sysctl_emu_mixer_control(SYSCTL_HANDLER_ARGS) +{ + struct emu_sc_info *sc; + int mixer_id; + int new_vol; + int err; + + sc = arg1; + mixer_id = arg2; + + new_vol = emumix_get_volume(sc, mixer_id); + err = sysctl_handle_int(oidp, &new_vol, 0, req); + + if (err || req->newptr == NULL) + return (err); + if (new_vol < 0 || new_vol > 100) + return (EINVAL); + emumix_set_volume(sc, mixer_id, new_vol); + + return (0); +} + +static int +emu_addefxmixer(struct emu_sc_info *sc, const char *mix_name, const int mix_id, uint32_t defvolume) +{ + int volgpr; + char sysctl_name[32]; + + volgpr = emu_rm_gpr_alloc(sc->rm, 1); + emumix_set_fxvol(sc, volgpr, defvolume); + /* Mixer controls with NULL mix_name are handled by AC97 emulation + code or PCM mixer. */ + if (mix_name != NULL) { + /* Temporary sysctls should start with underscore, + * see freebsd-current mailing list, emu10kx driver + * discussion around 2006-05-24. */ + snprintf(sysctl_name, 32, "_%s", mix_name); + SYSCTL_ADD_PROC(sc->ctx, + SYSCTL_CHILDREN(sc->root), + OID_AUTO, sysctl_name, + CTLTYPE_INT | CTLFLAG_RW, sc, mix_id, + sysctl_emu_mixer_control, "I",""); + } + + return (volgpr); +} + +/* allocate cache GPRs that will hold mixed output channels + * and clear it on every DSP run. + */ +#define EFX_CACHE(CACHE_IDX) do { \ + sc->cache_gpr[CACHE_IDX] = emu_rm_gpr_alloc(sc->rm, 1); \ + emu_addefxop(sc, ACC3, \ + GPR(sc->cache_gpr[CACHE_IDX]), \ + DSP_CONST(0), \ + DSP_CONST(0), \ + DSP_CONST(0), \ + &pc); \ +} while (0) + +/* Allocate GPR for volume control and route sound: OUT = OUT + IN * VOL */ +#define EFX_ROUTE(TITLE, INP_NR, IN_GPR_IDX, OUT_CACHE_IDX, DEF) do { \ + sc->mixer_gpr[IN_GPR_IDX] = emu_addefxmixer(sc, TITLE, IN_GPR_IDX, DEF); \ + sc->mixer_volcache[IN_GPR_IDX] = DEF; \ + emu_addefxop(sc, MACS, \ + GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ + GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ + INP_NR, \ + GPR(sc->mixer_gpr[IN_GPR_IDX]), \ + &pc); \ +} while (0) + +/* allocate GPR, OUT = IN * VOL */ +#define EFX_OUTPUT(TITLE,OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR, DEF) do { \ + sc->mixer_gpr[OUT_GPR_IDX] = emu_addefxmixer(sc, TITLE, OUT_GPR_IDX, DEF); \ + sc->mixer_volcache[OUT_GPR_IDX] = DEF; \ + emu_addefxop(sc, MACS, \ + OUTP(OUTP_NR), \ + DSP_CONST(0), \ + GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ + GPR(sc->mixer_gpr[OUT_GPR_IDX]), \ + &pc); \ +} while (0) + +/* like EFX_OUTPUT, but don't allocate mixer gpr */ +#define EFX_OUTPUTD(OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR) do{ \ + emu_addefxop(sc, MACS, \ + OUTP(OUTP_NR), \ + DSP_CONST(0), \ + GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ + GPR(sc->mixer_gpr[OUT_GPR_IDX]), \ + &pc); \ +} while(0) + +/* mute, if FLAG != 0 */ +/* XXX */ +#define EFX_MUTEIF(GPR_IDX, FLAG) do { \ +} while(0) + +/* allocate dummy GPR. It's content will be used somewhere */ +#define EFX_DUMMY(DUMMY_IDX, DUMMY_VALUE) do { \ + sc->dummy_gpr[DUMMY_IDX] = emu_rm_gpr_alloc(sc->rm, 1); \ + emumix_set_gpr(sc, sc->dummy_gpr[DUMMY_IDX], DUMMY_VALUE); \ + emu_addefxop(sc, ACC3, \ + FX2(DUMMY_IDX), \ + GPR(sc->dummy_gpr[DUMMY_IDX]), \ + DSP_CONST(0), \ + DSP_CONST(0), \ + &pc); \ +} while (0) + + +static void +emu_initefx(struct emu_sc_info *sc) +{ + unsigned int i; + uint32_t pc; + + /* stop DSP */ + if (sc->is_emu10k1) { + emu_wrptr(sc, 0, DBG, EMU10K1_DBG_SINGLE_STEP); + } else { + emu_wrptr(sc, 0, A_DBG, A_DBG_SINGLE_STEP); + } + + /* code size is in instructions */ + pc = 0; + for (i = 0; i < sc->code_size; i++) { + if (sc->is_emu10k1) { + emu_addefxop(sc, ACC3, DSP_CONST(0x0), DSP_CONST(0x0), DSP_CONST(0x0), DSP_CONST(0x0), &pc); + } else { + emu_addefxop(sc, SKIP, DSP_CONST(0x0), DSP_CONST(0x0), DSP_CONST(0xf), DSP_CONST(0x0), &pc); + } + } + + pc = 0; + + /* + * DSP code below is not good, because: + * 1. It can be written smaller, if it can use DSP accumulator register + * instead of cache_gpr[]. + * 2. It can be more careful when volume is 100%, because in DSP + * x*0x7fffffff may not be equal to x ! + */ + + /* clean outputs */ + for (i = 0; i < 16 ; i++) { + emu_addefxop(sc, ACC3, OUTP(i), DSP_CONST(0), DSP_CONST(0), DSP_CONST(0), &pc); + } + + + if (sc->is_emu10k1) { + EFX_CACHE(C_FRONT_L); + EFX_CACHE(C_FRONT_R); + EFX_CACHE(C_REC_L); + EFX_CACHE(C_REC_R); + + /* fx0 to front/record, 100%/muted by default */ + EFX_ROUTE("pcm_front_l", FX(0), M_FX0_FRONT_L, C_FRONT_L, 100); + EFX_ROUTE("pcm_front_r", FX(1), M_FX1_FRONT_R, C_FRONT_R, 100); + EFX_ROUTE("pcm_rec_l", FX(0), M_FX0_REC_L, C_REC_L, 0); + EFX_ROUTE("pcm_rec_r", FX(1), M_FX1_REC_R, C_REC_R, 0); + + /* in0, from AC97 codec output */ + EFX_ROUTE("ac97_front_l", INP(IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("ac97_front_r", INP(IN_AC97_R), M_IN0_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("ac97_rec_l", INP(IN_AC97_L), M_IN0_REC_L, C_REC_L, 0); + EFX_ROUTE("ac97_rec_r", INP(IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); + + /* in1, from CD S/PDIF */ + EFX_ROUTE("cdspdif_front_l", INP(IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); + EFX_MUTEIF(M_IN1_FRONT_L, CDSPDIFMUTE); + EFX_ROUTE("cdspdif_front_r", INP(IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); + EFX_MUTEIF(M_IN1_FRONT_R, CDSPDIFMUTE); + EFX_ROUTE("cdspdif_rec_l", INP(IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); + EFX_MUTEIF(M_IN1_REC_L, CDSPDIFMUTE); + EFX_ROUTE("cdspdif_rec_r", INP(IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); + EFX_MUTEIF(M_IN1_REC_L, CDSPDIFMUTE); +#ifdef SND_EMU10KX_DEBUG_OUTPUTS + /* in2, ZoomVide (???) */ + EFX_ROUTE("zoom_front_l", INP(IN_ZOOM_L), M_IN2_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("zoom_front_r", INP(IN_ZOOM_R), M_IN2_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("zoom_rec_l", INP(IN_ZOOM_L), M_IN2_REC_L, C_REC_L, 0); + EFX_ROUTE("zoom_rec_r", INP(IN_ZOOM_R), M_IN2_REC_R, C_REC_R, 0); +#endif +#ifdef SND_EMU10KX_DEBUG_OUTPUTS + /* in3, TOSLink (???) */ + EFX_ROUTE("toslink_front_l", INP(IN_TOSLINK_L), M_IN3_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("toslink_front_r", INP(IN_TOSLINK_R), M_IN3_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("toslink_rec_l", INP(IN_TOSLINK_L), M_IN3_REC_L, C_REC_L, 0); + EFX_ROUTE("toslink_rec_r", INP(IN_TOSLINK_R), M_IN3_REC_R, C_REC_R, 0); +#endif + /* in4, LineIn */ + EFX_ROUTE("linein_front_l", INP(IN_LINE1_L), M_IN4_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("linein_front_r", INP(IN_LINE1_R), M_IN4_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("linein_rec_l", INP(IN_LINE1_L), M_IN4_REC_L, C_REC_L, 0); + EFX_ROUTE("linein_rec_r", INP(IN_LINE1_R), M_IN4_REC_R, C_REC_R, 0); + + /* in5, on-card S/PDIF */ + EFX_ROUTE("spdif_front_l", INP(IN_COAX_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("spdif_front_r", INP(IN_COAX_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("spdif_rec_l", INP(IN_COAX_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); + EFX_ROUTE("spdif_rec_r", INP(IN_COAX_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); + + /* in6, Line2 on Live!Drive */ + EFX_ROUTE("line2_front_l", INP(IN_LINE2_L), M_IN6_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("line2_front_r", INP(IN_LINE2_R), M_IN6_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("line2_rec_l", INP(IN_LINE2_L), M_IN6_REC_L, C_REC_L, 0); + EFX_ROUTE("line2_rec_r", INP(IN_LINE2_R), M_IN6_REC_R, C_REC_R, 0); +#ifdef SND_EMU10KX_DEBUG_OUTPUTS + /* in7, unknown */ + EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); + EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); +#endif + /* front output to hedaphones and both analog and digital */ + EFX_OUTPUT("master_front_l", C_FRONT_L, M_MASTER_FRONT_L, OUT_AC97_L, 100); + EFX_OUTPUT("master_front_r", C_FRONT_R, M_MASTER_FRONT_R, OUT_AC97_R, 100); + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_HEADPHONE_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_HEADPHONE_R); + + /* rec output to "ADC" */ + EFX_OUTPUT("master_rec_l", C_REC_L, M_MASTER_REC_L, OUT_ADC_REC_L, 100); + EFX_OUTPUT("master_rec_r", C_REC_R, M_MASTER_REC_R, OUT_ADC_REC_R, 100); +#ifdef SND_EMU10KX_MULTICHANNEL + /* + * Additional channel volume is controlled by mixer in + * emu_dspmixer_set() in -pcm.c + */ + + /* fx2/3 (pcm1) to rear */ + EFX_CACHE(C_REAR_L); + EFX_CACHE(C_REAR_R); + EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); + EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); + + EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, OUT_REAR_L, 100); + EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, OUT_REAR_R, 100); + if (sc->has_51) { + /* fx4 (pcm2) to center */ + EFX_CACHE(C_CENTER); + EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); + EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, OUT_D_CENTER, 100); +#if 0 + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, OUT_A_CENTER); +#endif + /* fx5 (pcm3) to sub */ + EFX_CACHE(C_SUB); + EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); + EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, OUT_D_SUB, 100); +#if 0 + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, OUT_A_SUB); +#endif + } +#ifdef SND_EMU10KX_MCH_RECORDING + /* MCH RECORDING , hight 16 slots. On 5.1 cards first 4 slots are used + as outputs and already filled with data */ + for(i = (sc->has_51 ? 4 : 0); i < 16; i++) { + /* XXX fill with dummy data */ + EFX_DUMMY(i,i*0x10000); + emu_addefxop(sc, ACC3, + FX2(i), + DSP_CONST(0), + DSP_CONST(0), + GPR(sc->dummy_gpr[i]), + &pc); + + } +#endif +#else /* !SND_EMU10KX_MULTICHANNEL */ + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_REAR_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_REAR_R); +#endif + } else /* emu10k2 and later */ { + EFX_CACHE(C_FRONT_L); + EFX_CACHE(C_FRONT_R); + EFX_CACHE(C_REC_L); + EFX_CACHE(C_REC_R); + + /* fx0 to front/record, 100%/muted by default */ + /* + * FRONT_[L|R] is controlled by AC97 emulation in + * emu_ac97_[read|write]_emulation in -pcm.c + */ + EFX_ROUTE(NULL, FX(0), M_FX0_FRONT_L, C_FRONT_L, 100); + EFX_ROUTE(NULL, FX(1), M_FX1_FRONT_R, C_FRONT_R, 100); + EFX_ROUTE("pcm_rec_l", FX(0), M_FX0_REC_L, C_REC_L, 0); + EFX_ROUTE("pcm_rec_r", FX(1), M_FX1_REC_R, C_REC_R, 0); + + /* in0, from AC97 codec output */ + EFX_ROUTE("ac97_front_l", INP(A_IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 100); + EFX_ROUTE("ac97_front_r", INP(A_IN_AC97_R), M_IN0_FRONT_R, C_FRONT_R, 100); + EFX_ROUTE("ac97_rec_l", INP(A_IN_AC97_L), M_IN0_REC_L, C_REC_L, 0); + EFX_ROUTE("ac97_rec_r", INP(A_IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); + + /* in1, from CD S/PDIF */ + EFX_ROUTE("cdspdif_front_l", INP(A_IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("cdspdif_front_r", INP(A_IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("cdspdif_rec_l", INP(A_IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); + EFX_ROUTE("cdspdif_rec_r", INP(A_IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); + + /* in2, optical & coax S/PDIF on AudigyDrive*/ + /* XXX Should be muted when GPRSCS valid stream == 0 */ + EFX_ROUTE("ospdif_front_l", INP(A_IN_O_SPDIF_L), M_IN2_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("ospdif_front_r", INP(A_IN_O_SPDIF_R), M_IN2_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("ospdif_rec_l", INP(A_IN_O_SPDIF_L), M_IN2_REC_L, C_REC_L, 0); + EFX_ROUTE("ospdif_rec_r", INP(A_IN_O_SPDIF_R), M_IN2_REC_R, C_REC_R, 0); +#ifdef SND_EMU10KX_DEBUG_OUTPUTS + /* in3, unknown */ + EFX_ROUTE("in3_front_l", INP(0x6), M_IN3_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("in3_front_r", INP(0x7), M_IN3_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("in3_rec_l", INP(0x6), M_IN3_REC_L, C_REC_L, 0); + EFX_ROUTE("in3_rec_r", INP(0x7), M_IN3_REC_R, C_REC_R, 0); +#endif + /* in4, LineIn 2 on AudigyDrive */ + EFX_ROUTE("linein2_front_l", INP(A_IN_LINE2_L), M_IN4_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("linein2_front_r", INP(A_IN_LINE2_R), M_IN4_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("linein2_rec_l", INP(A_IN_LINE2_L), M_IN4_REC_L, C_REC_L, 0); + EFX_ROUTE("linein2_rec_r", INP(A_IN_LINE2_R), M_IN4_REC_R, C_REC_R, 0); + + /* in5, on-card S/PDIF */ + EFX_ROUTE("spdif_front_l", INP(A_IN_R_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("spdif_front_r", INP(A_IN_R_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("spdif_rec_l", INP(A_IN_R_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); + EFX_ROUTE("spdif_rec_r", INP(A_IN_R_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); + + /* in6, AUX2 on AudigyDrive */ + EFX_ROUTE("aux2_front_l", INP(A_IN_AUX2_L), M_IN6_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("aux2_front_r", INP(A_IN_AUX2_R), M_IN6_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("aux2_rec_l", INP(A_IN_AUX2_L), M_IN6_REC_L, C_REC_L, 0); + EFX_ROUTE("aux2_rec_r", INP(A_IN_AUX2_R), M_IN6_REC_R, C_REC_R, 0); +#ifdef SND_EMU10KX_DEBUG_OUTPUTS + /* in7, unknown */ + EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); + EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); +#endif + /* front output to headphones and alog and digital *front */ + /* volume controlled by AC97 emulation */ + EFX_OUTPUT(NULL, C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_FRONT_L, 100); + EFX_OUTPUT(NULL, C_FRONT_R, M_MASTER_FRONT_R, A_OUT_A_FRONT_R, 100); + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_D_FRONT_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_D_FRONT_R); + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_HPHONE_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_HPHONE_R); + + /* rec output to "ADC" */ + /* volume controlled by AC97 emulation */ + EFX_OUTPUT(NULL, C_REC_L, M_MASTER_REC_L, A_OUT_ADC_REC_L, 100); + EFX_OUTPUT(NULL, C_REC_R, M_MASTER_REC_R, A_OUT_ADC_REC_R, 100); +#ifdef SND_EMU10KX_MULTICHANNEL + /* + * Additional channel volume is controlled by mixer in + * emu_dspmixer_set() in -pcm.c + */ + + /* fx2/3 (pcm1) to rear */ + EFX_CACHE(C_REAR_L); + EFX_CACHE(C_REAR_R); + EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); + EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); + + EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, A_OUT_A_REAR_L, 100); + EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, A_OUT_A_REAR_R, 100); + EFX_OUTPUTD(C_REAR_L, M_MASTER_REAR_L, A_OUT_D_REAR_L); + EFX_OUTPUTD(C_REAR_R, M_MASTER_REAR_R, A_OUT_D_REAR_R); + + /* fx4 (pcm2) to center */ + EFX_CACHE(C_CENTER); + EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); + EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, A_OUT_D_CENTER, 100); +#if 0 + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, A_OUT_A_CENTER); +#endif + /* fx5 (pcm3) to sub */ + EFX_CACHE(C_SUB); + EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); + EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, A_OUT_D_SUB, 100); +#if 0 + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, A_OUT_A_SUB); +#endif + if (sc->has_71) { + /* XXX this will broke headphones on AudigyDrive */ + /* fx6/7 (pcm4) to side */ + EFX_CACHE(C_SIDE_L); + EFX_CACHE(C_SIDE_R); + EFX_ROUTE(NULL, FX(6), M_FX6_SIDE_L, C_SIDE_L, 100); + EFX_ROUTE(NULL, FX(7), M_FX7_SIDE_R, C_SIDE_R, 100); + EFX_OUTPUT(NULL, C_SIDE_L, M_MASTER_SIDE_L, A_OUT_A_SIDE_L, 100); + EFX_OUTPUT(NULL, C_SIDE_R, M_MASTER_SIDE_R, A_OUT_A_SIDE_R, 100); + EFX_OUTPUTD(C_SIDE_L, M_MASTER_SIDE_L, A_OUT_D_SIDE_L); + EFX_OUTPUTD(C_SIDE_R, M_MASTER_SIDE_R, A_OUT_D_SIDE_R); + } +#ifdef SND_EMU10KX_MCH_RECORDING + /* MCH RECORDING, high 32 slots */ + for(i = 0; i < 32; i++) { + /* XXX fill with dummy data */ + EFX_DUMMY(i,i*0x10000); + emu_addefxop(sc, ACC3, + FX2(i), + DSP_CONST(0), + DSP_CONST(0), + GPR(sc->dummy_gpr[i]), + &pc); + } +#endif +#else /* !SND_EMU10KX_MULTICHANNEL */ + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_REAR_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_A_REAR_R); + + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_D_REAR_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_D_REAR_R); +#endif + } + + sc->routing_code_end = pc; + + /* start DSP */ + if (sc->is_emu10k1) { + emu_wrptr(sc, 0, DBG, 0); + } else { + emu_wrptr(sc, 0, A_DBG, 0); + } +} + +/* /dev/em10kx */ +static d_open_t emu10kx_open; +static d_close_t emu10kx_close; +static d_read_t emu10kx_read; + +static struct cdevsw emu10kx_cdevsw = { + .d_open = emu10kx_open, + .d_close = emu10kx_close, + .d_read = emu10kx_read, + .d_name = "emu10kx", + .d_version = D_VERSION, +}; + + +static int +emu10kx_open(struct cdev *i_dev, int flags __unused, int mode __unused, struct thread *td __unused) +{ + int error; + struct emu_sc_info *sc; + + sc = i_dev->si_drv1; + mtx_lock(&sc->emu10kx_lock); + if (sc->emu10kx_isopen) { + mtx_unlock(&sc->emu10kx_lock); + return (EBUSY); + } + sc->emu10kx_isopen = 1; + mtx_unlock(&sc->emu10kx_lock); + if (sbuf_new(&sc->emu10kx_sbuf, NULL, 4096, 0) == NULL) { + error = ENXIO; + goto out; + } + sc->emu10kx_bufptr = 0; + error = (emu10kx_prepare(sc, &sc->emu10kx_sbuf) > 0) ? 0 : ENOMEM; +out: + if (error) { + mtx_lock(&sc->emu10kx_lock); + sc->emu10kx_isopen = 0; + mtx_unlock(&sc->emu10kx_lock); + } + return (error); +} + +static int +emu10kx_close(struct cdev *i_dev, int flags __unused, int mode __unused, struct thread *td __unused) +{ + struct emu_sc_info *sc; + + sc = i_dev->si_drv1; + + mtx_lock(&sc->emu10kx_lock); + if (!(sc->emu10kx_isopen)) { + mtx_unlock(&sc->emu10kx_lock); + return (EBADF); + } + sbuf_delete(&sc->emu10kx_sbuf); + sc->emu10kx_isopen = 0; + mtx_unlock(&sc->emu10kx_lock); + + return (0); +} + +static int +emu10kx_read(struct cdev *i_dev, struct uio *buf, int flag __unused) +{ + int l, err; + struct emu_sc_info *sc; + + sc = i_dev->si_drv1; + mtx_lock(&sc->emu10kx_lock); + if (!(sc->emu10kx_isopen)) { + mtx_unlock(&sc->emu10kx_lock); + return (EBADF); + } + mtx_unlock(&sc->emu10kx_lock); + + l = min(buf->uio_resid, sbuf_len(&sc->emu10kx_sbuf) - sc->emu10kx_bufptr); + err = (l > 0) ? uiomove(sbuf_data(&sc->emu10kx_sbuf) + sc->emu10kx_bufptr, l, buf) : 0; + sc->emu10kx_bufptr += l; + + return (err); +} + +static int +emu10kx_prepare(struct emu_sc_info *sc, struct sbuf *s) +{ + int i; + + sbuf_printf(s, "FreeBSD EMU10Kx Audio Driver\n"); + sbuf_printf(s, "\nHardware resource usage:\n"); + sbuf_printf(s, "DSP General Purpose Registers: %d used, %d total\n", sc->rm->num_used, sc->rm->num_gprs); + sbuf_printf(s, "DSP Instruction Registers: %d used, %d total\n", sc->routing_code_end, sc->code_size); + sbuf_printf(s, "Card supports"); + if (sc->has_ac97) { + sbuf_printf(s, " AC97 codec"); + } else { + sbuf_printf(s, " NO AC97 codec"); + } + if (sc->has_51) { + if (sc->has_71) + sbuf_printf(s, " and 7.1 output"); + else + sbuf_printf(s, " and 5.1 output"); + } + if (sc->is_emu10k1) + sbuf_printf(s, ", SBLive! DSP code"); + if (sc->is_emu10k2) + sbuf_printf(s, ", Audigy DSP code"); + if (sc->is_ca0102) + sbuf_printf(s, ", Audigy DSP code with Audigy2 hacks"); + if (sc->is_ca0108) + sbuf_printf(s, ", Audigy DSP code with Audigy2Value hacks"); + sbuf_printf(s, "\n"); + if (sc->broken_digital) + sbuf_printf(s, "Digital mode unsupported\n"); + sbuf_printf(s, "\nInstalled devices:\n"); + for (i = 0; i < RT_COUNT; i++) + if (sc->pcm[i] != NULL) + if (device_is_attached(sc->pcm[i])) { + sbuf_printf(s, "%s on %s\n", device_get_desc(sc->pcm[i]), device_get_nameunit(sc->pcm[i])); + } + if (sc->midi[0] != NULL) + if (device_is_attached(sc->midi[0])) { + sbuf_printf(s, "EMU10Kx MIDI Interface\n"); + sbuf_printf(s, "\tOn-card connector on %s\n", device_get_nameunit(sc->midi[0])); + } + if (sc->midi[1] != NULL) + if (device_is_attached(sc->midi[1])) { + sbuf_printf(s, "\tOn-Drive connector on %s\n", device_get_nameunit(sc->midi[1])); + } + if (sc->midi[0] != NULL) + if (device_is_attached(sc->midi[0])) { + sbuf_printf(s, "\tIR reciever MIDI events %s\n", sc->enable_ir ? "enabled" : "disabled"); + } + sbuf_finish(s); + return (sbuf_len(s)); +} + +/* INIT & UNINIT */ +static int +emu10kx_dev_init(struct emu_sc_info *sc) +{ + int unit; + + mtx_init(&sc->emu10kx_lock, "kxdevlock", NULL, 0); + unit = device_get_unit(sc->dev); + + sc->cdev = make_dev(&emu10kx_cdevsw, unit2minor(unit), UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit); + if (sc->cdev != NULL) { + sc->cdev->si_drv1 = sc; + return (0); + } + return (ENXIO); +} + +static int +emu10kx_dev_uninit(struct emu_sc_info *sc) +{ + intrmask_t s; + + s = spltty(); + mtx_lock(&sc->emu10kx_lock); + if (sc->emu10kx_isopen) { + mtx_unlock(&sc->emu10kx_lock); + splx(s); + return (EBUSY); + } + if (sc->cdev) + destroy_dev(sc->cdev); + sc->cdev = 0; + + splx(s); + mtx_destroy(&sc->emu10kx_lock); + return (0); +} + +/* resource manager */ +int +emu_rm_init(struct emu_sc_info *sc) +{ + int i; + int maxcount; + struct emu_rm *rm; + + rm = malloc(sizeof(struct emu_rm), M_DEVBUF, M_NOWAIT | M_ZERO); + if (rm == NULL) { + return (ENOMEM); + } + sc->rm = rm; + rm->card = sc; + maxcount = sc->num_gprs; + rm->num_used = 0; + mtx_init(&(rm->gpr_lock), "emu10k", "gpr alloc", MTX_DEF); + rm->num_gprs = (maxcount < EMU_MAX_GPR ? maxcount : EMU_MAX_GPR); + for (i = 0; i < rm->num_gprs; i++) + rm->allocmap[i] = 0; + rm->last_free_gpr = 0; + + return (0); +} + +int +emu_rm_uninit(struct emu_sc_info *sc) +{ +#ifdef SND_EMU10KX_DEBUG + int i; + + mtx_lock(&(sc->rm->gpr_lock)); + for (i = 0; i < sc->rm->last_free_gpr; i++) + if (sc->rm->allocmap[i] > 0) + device_printf(sc->dev, "rm: gpr %d not free before uninit\n", i); + mtx_unlock(&(sc->rm->gpr_lock)); +#endif + mtx_destroy(&(sc->rm->gpr_lock)); + free(sc->rm, M_DEVBUF); + return (0); +} + +static int +emu_rm_gpr_alloc(struct emu_rm *rm, int count) +{ + int i, j; + int allocated_gpr; + + allocated_gpr = rm->num_gprs; + /* try fast way first */ + mtx_lock(&(rm->gpr_lock)); + if (rm->last_free_gpr + count <= rm->num_gprs) { + allocated_gpr = rm->last_free_gpr; + rm->last_free_gpr += count; + rm->allocmap[allocated_gpr] = count; + for (i = 1; i < count; i++) + rm->allocmap[allocated_gpr + i] = -(count - i); + } else { + /* longer */ + i = 0; + allocated_gpr = rm->num_gprs; + while (i < rm->last_free_gpr - count) { + if (rm->allocmap[i] > 0) { + i += rm->allocmap[i]; + } else { + allocated_gpr = i; + for (j = 1; j < count; j++) { + if (rm->allocmap[i + j] != 0) + allocated_gpr = rm->num_gprs; + } + if (allocated_gpr == i) + break; + } + } + if (allocated_gpr + count < rm->last_free_gpr) { + rm->allocmap[allocated_gpr] = count; + for (i = 1; i < count; i++) + rm->allocmap[allocated_gpr + i] = -(count - i); + + } + } + if (allocated_gpr == rm->num_gprs) + allocated_gpr = (-1); + if (allocated_gpr >= 0) + rm->num_used += count; + mtx_unlock(&(rm->gpr_lock)); + return (allocated_gpr); +} + +/* mixer */ +void +emumix_set_mode(struct emu_sc_info *sc, int mode) +{ + uint32_t a_iocfg; + uint32_t hcfg; + uint32_t tmp; + + switch (mode) { + case MODE_DIGITAL: + /* FALLTHROUGH */ + case MODE_ANALOG: + break; + default: + return; + } + + hcfg = HCFG_AUDIOENABLE | HCFG_AUTOMUTE; + a_iocfg = 0; + + if (sc->rev >= 6) + hcfg |= HCFG_JOYENABLE; + + if (sc->is_emu10k1) + hcfg |= HCFG_LOCKTANKCACHE_MASK; + else + hcfg |= HCFG_CODECFORMAT_I2S | HCFG_JOYENABLE; + + + if (mode == MODE_DIGITAL) { + if (sc->broken_digital) { + device_printf(sc->dev, "Digital mode is reported as broken on this card,\n"); + } + a_iocfg |= A_IOCFG_ENABLE_DIGITAL; + hcfg |= HCFG_GPOUT0; + } + + if (mode == MODE_ANALOG) + emumix_set_spdif_mode(sc, SPDIF_MODE_PCM); + + if (sc->is_emu10k2) + a_iocfg |= 0x80; /* XXX */ + + if ((sc->is_ca0102) || (sc->is_ca0108)) + a_iocfg |= A_IOCFG_DISABLE_ANALOG; /* means "don't disable" + on this two cards. Means "disable" on emu10k2. */ + + if (sc->is_ca0108) + a_iocfg |= 0x20; /* XXX */ + + emu_wr(sc, HCFG, hcfg, 4); + + if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { + tmp = emu_rd(sc, A_IOCFG, 2); + tmp = a_iocfg; + emu_wr(sc, A_IOCFG, tmp, 2); + } + + /* + * XXX Mute center/sub if we go digital on Audigy or later card. + * Route to analog center / sub in emu_initef should be disabled + * until this problem is fixed. + */ +} + +void +emumix_set_spdif_mode(struct emu_sc_info *sc, int mode) +{ + uint32_t spcs; + + switch (mode) { + case SPDIF_MODE_PCM: + break; + case SPDIF_MODE_AC3: + device_printf(sc->dev, "AC3 mode does not work and disabled\n"); + return; + default: + return; + } + + spcs = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | + SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; + + mode = SPDIF_MODE_PCM; + + emu_wrptr(sc, 0, SPCS0, spcs); + emu_wrptr(sc, 0, SPCS1, spcs); + emu_wrptr(sc, 0, SPCS2, spcs); +} + +#define L2L_POINTS 10 + +static int l2l_df[L2L_POINTS] = { + 0x572C5CA, /* 100..90 */ + 0x3211625, /* 90..80 */ + 0x1CC1A76, /* 80..70 */ + 0x108428F, /* 70..60 */ + 0x097C70A, /* 60..50 */ + 0x0572C5C, /* 50..40 */ + 0x0321162, /* 40..30 */ + 0x01CC1A7, /* 30..20 */ + 0x0108428, /* 20..10 */ + 0x016493D /* 10..0 */ +}; + +static int l2l_f[L2L_POINTS] = { + 0x4984461A, /* 90 */ + 0x2A3968A7, /* 80 */ + 0x18406003, /* 70 */ + 0x0DEDC66D, /* 60 */ + 0x07FFFFFF, /* 50 */ + 0x04984461, /* 40 */ + 0x02A3968A, /* 30 */ + 0x01840600, /* 20 */ + 0x00DEDC66, /* 10 */ + 0x00000000 /* 0 */ +}; + + +static int +log2lin(int log_t) +{ + int lin_t; + int idx, lin; + + if (log_t <= 0) { + lin_t = 0x00000000; + return (lin_t); + } + + if (log_t >= 100) { + lin_t = 0x7fffffff; + return (lin_t); + } + + idx = (L2L_POINTS - 1) - log_t / (L2L_POINTS); + lin = log_t % (L2L_POINTS); + lin_t = l2l_df[idx] * lin + l2l_f[idx]; + return (lin_t); +} + + +void +emumix_set_fxvol(struct emu_sc_info *sc, unsigned gpr, int32_t vol) +{ + + vol = log2lin(vol); + emumix_set_gpr(sc, gpr, vol); +} + +void +emumix_set_gpr(struct emu_sc_info *sc, unsigned gpr, int32_t val) +{ + + emu_wrptr(sc, 0, GPR(gpr), val); +} + +void +emumix_set_volume(struct emu_sc_info *sc, int mixer_idx, int volume) +{ + + RANGE(volume, 0, 100); + if (mixer_idx < NUM_MIXERS) { + sc->mixer_volcache[mixer_idx] = volume; + emumix_set_fxvol(sc, sc->mixer_gpr[mixer_idx], volume); + } +} + +int +emumix_get_volume(struct emu_sc_info *sc, int mixer_idx) +{ + if ((mixer_idx < NUM_MIXERS) && (mixer_idx >= 0)) + return (sc->mixer_volcache[mixer_idx]); + return (-1); +} + +/* Init CardBus part */ +static int +emu_cardbus_init(struct emu_sc_info *sc) +{ + + /* + * XXX May not need this if we have IPR3 handler. + * Is it a real init calls, or IPR3 interrupt acknowledgments? + * Looks much like "(data << 16) | register". + */ + emu_wr_cbptr(sc, (0x00d0 << 16) | 0x0000); + emu_wr_cbptr(sc, (0x00d0 << 16) | 0x0001); + emu_wr_cbptr(sc, (0x00d0 << 16) | 0x005f); + emu_wr_cbptr(sc, (0x00d0 << 16) | 0x007f); + + emu_wr_cbptr(sc, (0x0090 << 16) | 0x007f); + + return (0); +} + +/* Probe and attach the card */ +static int +emu_init(struct emu_sc_info *sc) +{ + uint32_t ch, tmp; + uint32_t spdif_sr; + uint32_t ac97slot; + int def_mode; + int i; + + /* disable audio and lock cache */ + emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, 4); + + /* reset recording buffers */ + emu_wrptr(sc, 0, MICBS, ADCBS_BUFSIZE_NONE); + emu_wrptr(sc, 0, MICBA, 0); + emu_wrptr(sc, 0, FXBS, ADCBS_BUFSIZE_NONE); + emu_wrptr(sc, 0, FXBA, 0); + emu_wrptr(sc, 0, ADCBS, ADCBS_BUFSIZE_NONE); + emu_wrptr(sc, 0, ADCBA, 0); + + /* disable channel interrupt */ + emu_wr(sc, INTE, INTE_INTERVALTIMERENB | INTE_SAMPLERATETRACKER | INTE_PCIERRORENABLE, 4); + emu_wrptr(sc, 0, CLIEL, 0); + emu_wrptr(sc, 0, CLIEH, 0); + emu_wrptr(sc, 0, SOLEL, 0); + emu_wrptr(sc, 0, SOLEH, 0); + + /* disable P16V and S/PDIF interrupts */ + if ((sc->is_ca0102) || (sc->is_ca0108)) + emu_wr(sc, INTE2, 0, 4); + + if (sc->is_ca0102) + emu_wr(sc, INTE3, 0, 4); + + /* init phys inputs and outputs */ + ac97slot = 0; + if (sc->has_51) + ac97slot = AC97SLOT_CNTR | AC97SLOT_LFE; + if (sc->has_71) + ac97slot = AC97SLOT_CNTR | AC97SLOT_LFE | AC97SLOT_REAR_LEFT | AC97SLOT_REAR_RIGHT; + if (sc->is_emu10k2) + ac97slot |= 0x40; + emu_wrptr(sc, 0, AC97SLOT, ac97slot); + + if (sc->is_emu10k2) /* XXX for later cards? */ + emu_wrptr(sc, 0, SPBYPASS, 0xf00); /* What will happen if + * we write 1 here? */ + + if (bus_dma_tag_create( /* parent */ bus_get_dma_tag(sc->dev), + /* alignment */ 2, /* boundary */ 0, + /* lowaddr */ 1 << 31, /* can only access 0-2gb */ + /* highaddr */ BUS_SPACE_MAXADDR, + /* filter */ NULL, /* filterarg */ NULL, + /* maxsize */ EMU_MAX_BUFSZ, /* nsegments */ 1, /* maxsegz */ 0x3ffff, + /* flags */ 0, /* lockfunc */ busdma_lock_mutex, + /* lockarg */ &Giant, &(sc->mem.dmat)) != 0) { + device_printf(sc->dev, "unable to create dma tag\n"); + bus_dma_tag_destroy(sc->mem.dmat); + return (ENOMEM); + } + + SLIST_INIT(&sc->mem.blocks); + sc->mem.ptb_pages = emu_malloc(&sc->mem, EMU_MAXPAGES * sizeof(uint32_t), &sc->mem.ptb_pages_addr); + if (sc->mem.ptb_pages == NULL) + return (ENOMEM); + + sc->mem.silent_page = emu_malloc(&sc->mem, EMUPAGESIZE, &sc->mem.silent_page_addr); + if (sc->mem.silent_page == NULL) { + emu_free(&sc->mem, sc->mem.ptb_pages); + return (ENOMEM); + } + /* Clear page with silence & setup all pointers to this page */ + bzero(sc->mem.silent_page, EMUPAGESIZE); + tmp = (uint32_t) (sc->mem.silent_page_addr) << 1; + for (i = 0; i < EMU_MAXPAGES; i++) + sc->mem.ptb_pages[i] = tmp | i; + + for (ch = 0; ch < NUM_G; ch++) { + emu_wrptr(sc, ch, MAPA, tmp | MAP_PTI_MASK); + emu_wrptr(sc, ch, MAPB, tmp | MAP_PTI_MASK); + } + emu_wrptr(sc, 0, PTB, (sc->mem.ptb_pages_addr)); + emu_wrptr(sc, 0, TCB, 0); /* taken from original driver */ + emu_wrptr(sc, 0, TCBS, 0); /* taken from original driver */ + + /* init envelope engine */ + for (ch = 0; ch < NUM_G; ch++) { + emu_wrptr(sc, ch, DCYSUSV, 0); + emu_wrptr(sc, ch, IP, 0); + emu_wrptr(sc, ch, VTFT, 0xffff); + emu_wrptr(sc, ch, CVCF, 0xffff); + emu_wrptr(sc, ch, PTRX, 0); + emu_wrptr(sc, ch, CPF, 0); + emu_wrptr(sc, ch, CCR, 0); + + emu_wrptr(sc, ch, PSST, 0); + emu_wrptr(sc, ch, DSL, 0x10); + emu_wrptr(sc, ch, CCCA, 0); + emu_wrptr(sc, ch, Z1, 0); + emu_wrptr(sc, ch, Z2, 0); + emu_wrptr(sc, ch, FXRT, 0xd01c0000); + + emu_wrptr(sc, ch, ATKHLDM, 0); + emu_wrptr(sc, ch, DCYSUSM, 0); + emu_wrptr(sc, ch, IFATN, 0xffff); + emu_wrptr(sc, ch, PEFE, 0); + emu_wrptr(sc, ch, FMMOD, 0); + emu_wrptr(sc, ch, TREMFRQ, 24); /* 1 Hz */ + emu_wrptr(sc, ch, FM2FRQ2, 24); /* 1 Hz */ + emu_wrptr(sc, ch, TEMPENV, 0); + + /*** these are last so OFF prevents writing ***/ + emu_wrptr(sc, ch, LFOVAL2, 0); + emu_wrptr(sc, ch, LFOVAL1, 0); + emu_wrptr(sc, ch, ATKHLDV, 0); + emu_wrptr(sc, ch, ENVVOL, 0); + emu_wrptr(sc, ch, ENVVAL, 0); + + if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { + emu_wrptr(sc, ch, 0x4c, 0x0); + emu_wrptr(sc, ch, 0x4d, 0x0); + emu_wrptr(sc, ch, 0x4e, 0x0); + emu_wrptr(sc, ch, 0x4f, 0x0); + emu_wrptr(sc, ch, A_FXRT1, 0x3f3f3f3f); + emu_wrptr(sc, ch, A_FXRT2, 0x3f3f3f3f); + emu_wrptr(sc, ch, A_SENDAMOUNTS, 0x0); + } + } + + emumix_set_spdif_mode(sc, SPDIF_MODE_PCM); + + if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) + emu_wrptr(sc, 0, A_SPDIF_SAMPLERATE, A_SPDIF_48000); + + /* + * CAxxxx cards needs additional setup: + * 1. Set I2S capture sample rate to 96000 + * 2. Disable P16v / P17v proceesing + * 3. Allow EMU10K DSP inputs + */ + if ((sc->is_ca0102) || (sc->is_ca0108)) { + + spdif_sr = emu_rdptr(sc, 0, A_SPDIF_SAMPLERATE); + spdif_sr &= 0xfffff1ff; + spdif_sr |= A_I2S_CAPTURE_96000; + emu_wrptr(sc, 0, A_SPDIF_SAMPLERATE, spdif_sr); + + /* Disable P16v processing */ + emu_wr_p16vptr(sc, 0, SRCSel, 0x14); + + /* Setup P16v/P17v sound routing */ + if (sc->is_ca0102) + emu_wr_p16vptr(sc, 0, SRCMULTI_ENABLE, 0xFF00FF00); + else { + emu_wr_p16vptr(sc, 0, P17V_MIXER_I2S_ENABLE, 0xFF000000); + emu_wr_p16vptr(sc, 0, P17V_MIXER_SPDIF_ENABLE, 0xFF000000); + + tmp = emu_rd(sc, A_IOCFG, 2); + emu_wr(sc, A_IOCFG, tmp & ~0x8, 2); + } + } + emu_initefx(sc); + + def_mode = MODE_ANALOG; + if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) + def_mode = MODE_DIGITAL; + if (((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) && (sc->broken_digital)) { + device_printf(sc->dev, "Audigy card initialized in analog mode.\n"); + def_mode = MODE_ANALOG; + } + emumix_set_mode(sc, def_mode); + + if (bootverbose) { + tmp = emu_rd(sc, HCFG, 4); + device_printf(sc->dev, "Card Configuration ( 0x%08x )\n", tmp); + device_printf(sc->dev, "Card Configuration ( & 0xff000000 ) : %s%s%s%s%s%s%s%s\n", + (tmp & 0x80000000 ? "[Legacy MPIC] " : ""), + (tmp & 0x40000000 ? "[0x40] " : ""), + (tmp & 0x20000000 ? "[0x20] " : ""), + (tmp & 0x10000000 ? "[0x10] " : ""), + (tmp & 0x08000000 ? "[0x08] " : ""), + (tmp & 0x04000000 ? "[0x04] " : ""), + (tmp & 0x02000000 ? "[0x02] " : ""), + (tmp & 0x01000000 ? "[0x01]" : " ")); + device_printf(sc->dev, "Card Configuration ( & 0x00ff0000 ) : %s%s%s%s%s%s%s%s\n", + (tmp & 0x00800000 ? "[0x80] " : ""), + (tmp & 0x00400000 ? "[0x40] " : ""), + (tmp & 0x00200000 ? "[Legacy INT] " : ""), + (tmp & 0x00100000 ? "[0x10] " : ""), + (tmp & 0x00080000 ? "[0x08] " : ""), + (tmp & 0x00040000 ? "[Codec4] " : ""), + (tmp & 0x00020000 ? "[Codec2] " : ""), + (tmp & 0x00010000 ? "[I2S Codec]" : " ")); + device_printf(sc->dev, "Card Configuration ( & 0x0000ff00 ) : %s%s%s%s%s%s%s%s\n", + (tmp & 0x00008000 ? "[0x80] " : ""), + (tmp & 0x00004000 ? "[GPINPUT0] " : ""), + (tmp & 0x00002000 ? "[GPINPUT1] " : ""), + (tmp & 0x00001000 ? "[GPOUT0] " : ""), + (tmp & 0x00000800 ? "[GPOUT1] " : ""), + (tmp & 0x00000400 ? "[GPOUT2] " : ""), + (tmp & 0x00000200 ? "[Joystick] " : ""), + (tmp & 0x00000100 ? "[0x01]" : " ")); + device_printf(sc->dev, "Card Configuration ( & 0x000000ff ) : %s%s%s%s%s%s%s%s\n", + (tmp & 0x00000080 ? "[0x80] " : ""), + (tmp & 0x00000040 ? "[0x40] " : ""), + (tmp & 0x00000020 ? "[0x20] " : ""), + (tmp & 0x00000010 ? "[AUTOMUTE] " : ""), + (tmp & 0x00000008 ? "[LOCKSOUNDCACHE] " : ""), + (tmp & 0x00000004 ? "[LOCKTANKCACHE] " : ""), + (tmp & 0x00000002 ? "[MUTEBUTTONENABLE] " : ""), + (tmp & 0x00000001 ? "[AUDIOENABLE]" : " ")); + + if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { + tmp = emu_rd(sc, A_IOCFG, 2); + device_printf(sc->dev, "Audigy Card Configuration ( 0x%04x )\n", tmp); + device_printf(sc->dev, "Audigy Card Configuration ( & 0xff00 )"); + printf(" : %s%s%s%s%s%s%s%s\n", + (tmp & 0x8000 ? "[Rear Speakers] " : ""), + (tmp & 0x4000 ? "[Front Speakers] " : ""), + (tmp & 0x2000 ? "[0x20] " : ""), + (tmp & 0x1000 ? "[0x10] " : ""), + (tmp & 0x0800 ? "[0x08] " : ""), + (tmp & 0x0400 ? "[0x04] " : ""), + (tmp & 0x0200 ? "[0x02] " : ""), + (tmp & 0x0100 ? "[AudigyDrive Phones]" : " ")); + device_printf(sc->dev, "Audigy Card Configuration ( & 0x00ff )"); + printf(" : %s%s%s%s%s%s%s%s\n", + (tmp & 0x0080 ? "[0x80] " : ""), + (tmp & 0x0040 ? "[Mute AnalogOut] " : ""), + (tmp & 0x0020 ? "[0x20] " : ""), + (tmp & 0x0010 ? "[0x10] " : ""), + (tmp & 0x0008 ? "[0x08] " : ""), + (tmp & 0x0004 ? "[GPOUT0] " : ""), + (tmp & 0x0002 ? "[GPOUT1] " : ""), + (tmp & 0x0001 ? "[GPOUT2]" : " ")); + } /* is_emu10k2 or ca* */ + } /* bootverbose */ + return (0); +} + +static int +emu_uninit(struct emu_sc_info *sc) +{ + uint32_t ch; + struct emu_memblk *blk; + + emu_wr(sc, INTE, 0, 4); + for (ch = 0; ch < NUM_G; ch++) + emu_wrptr(sc, ch, DCYSUSV, 0); + for (ch = 0; ch < NUM_G; ch++) { + emu_wrptr(sc, ch, VTFT, 0); + emu_wrptr(sc, ch, CVCF, 0); + emu_wrptr(sc, ch, PTRX, 0); + emu_wrptr(sc, ch, CPF, 0); + } + + /* disable audio and lock cache */ + emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, 4); + + emu_wrptr(sc, 0, PTB, 0); + /* reset recording buffers */ + emu_wrptr(sc, 0, MICBS, ADCBS_BUFSIZE_NONE); + emu_wrptr(sc, 0, MICBA, 0); + emu_wrptr(sc, 0, FXBS, ADCBS_BUFSIZE_NONE); + emu_wrptr(sc, 0, FXBA, 0); + emu_wrptr(sc, 0, FXWC, 0); + emu_wrptr(sc, 0, ADCBS, ADCBS_BUFSIZE_NONE); + emu_wrptr(sc, 0, ADCBA, 0); + emu_wrptr(sc, 0, TCB, 0); + emu_wrptr(sc, 0, TCBS, 0); + + /* disable channel interrupt */ + emu_wrptr(sc, 0, CLIEL, 0); + emu_wrptr(sc, 0, CLIEH, 0); + emu_wrptr(sc, 0, SOLEL, 0); + emu_wrptr(sc, 0, SOLEH, 0); + + if (!SLIST_EMPTY(&sc->mem.blocks)) + device_printf(sc->dev, "warning: memblock list not empty\n"); + + SLIST_FOREACH(blk, &sc->mem.blocks, link) + if (blk != NULL) + device_printf(sc->dev, "lost %d for %s\n", blk->pte_size, blk->owner); + + emu_free(&sc->mem, sc->mem.ptb_pages); + emu_free(&sc->mem, sc->mem.silent_page); + + return (0); +} + +static int +emu_read_ivar(device_t bus, device_t dev, int ivar_index, uintptr_t * result) +{ + struct sndcard_func *func = device_get_ivars(dev); + struct emu_sc_info *sc = device_get_softc(bus); + + switch (ivar_index) { + case EMU_VAR_FUNC: + *result = func->func; + break; + case EMU_VAR_ROUTE: + *result = ((struct emu_pcminfo *)func->varinfo)->route; + break; + case EMU_VAR_ISEMU10K1: + *result = sc->is_emu10k1; + break; + default: + return (ENOENT); + } + + return (0); +} + +static int +emu_write_ivar(device_t bus __unused, device_t dev __unused, + int ivar_index, uintptr_t value __unused) +{ + + switch (ivar_index) { + case 0: + return (EINVAL); + + default: + return (ENOENT); + } +} + +static int +emu_pci_probe(device_t dev) +{ + struct sbuf *s; + unsigned int thiscard = 0; + uint16_t vendor; + + vendor = pci_read_config(dev, PCIR_DEVVENDOR, /* bytes */ 2); + if (vendor != 0x1102) + return (ENXIO); /* Not Creative */ + + thiscard = emu_getcard(dev); + if (thiscard == 0) + return (ENXIO); + + s = sbuf_new(NULL, NULL, 4096, 0); + if (s == NULL) + return (ENOMEM); + sbuf_printf(s, "Creative %s [%s]", emu_cards[thiscard].desc, emu_cards[thiscard].SBcode); + sbuf_finish(s); + + device_set_desc_copy(dev, sbuf_data(s)); + return (BUS_PROBE_DEFAULT); +} + + +static int +emu_pci_attach(device_t dev) +{ + struct sndcard_func *func; + struct emu_sc_info *sc; + struct emu_pcminfo *pcminfo; + struct emu_midiinfo *midiinfo[3]; + uint32_t data; + int i; + int device_flags; + char status[255]; + int error = ENXIO; + + sc = device_get_softc(dev); + + /* Fill in the softc. */ + mtx_init(&sc->lock, "emu10kx", "bridge conf", MTX_DEF); + mtx_init(&sc->rw, "emu10kx", "atomic op", MTX_DEF); + sc->dev = dev; + sc->type = pci_get_devid(dev); + sc->rev = pci_get_revid(dev); + sc->enable_ir = 0; + sc->enable_debug = 0; + sc->has_ac97 = 0; + sc->has_51 = 0; + sc->has_71 = 0; + sc->broken_digital = 0; + sc->is_emu10k1 = 0; + sc->is_emu10k2 = 0; + sc->is_ca0102 = 0; + sc->is_ca0108 = 0; + sc->is_cardbus = 0; + + device_flags = emu_cards[emu_getcard(dev)].flags; + if (device_flags & HAS_51) + sc->has_51 = 1; + if (device_flags & HAS_71) { + sc->has_51 = 1; + sc->has_71 = 1; + } + if (device_flags & IS_EMU10K1) + sc->is_emu10k1 = 1; + if (device_flags & IS_EMU10K2) + sc->is_emu10k2 = 1; + if (device_flags & IS_CA0102) + sc->is_ca0102 = 1; + if (device_flags & IS_CA0108) + sc->is_ca0108 = 1; + if ((sc->is_emu10k2) && (sc->rev == 4)) { + sc->is_emu10k2 = 0; + sc->is_ca0102 = 1; /* for unknown Audigy 2 cards */ + } + if ((sc->is_ca0102 == 1) || (sc->is_ca0108 == 1)) + if (device_flags & IS_CARDBUS) + sc->is_cardbus = 1; + + if ((sc->is_emu10k1 + sc->is_emu10k2 + sc->is_ca0102 + sc->is_ca0108) != 1) { + device_printf(sc->dev, "Unable to detect HW chipset\n"); + goto bad; + } + if (device_flags & BROKEN_DIGITAL) + sc->broken_digital = 1; + if (device_flags & HAS_AC97) + sc->has_ac97 = 1; + + sc->opcode_shift = 0; + if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { + sc->opcode_shift = 24; + sc->high_operand_shift = 12; + + /* DSP map */ + /* sc->fx_base = 0x0 */ + sc->input_base = 0x40; + /* sc->p16vinput_base = 0x50; */ + sc->output_base = 0x60; + sc->efxc_base = 0x80; + /* sc->output32h_base = 0xa0; */ + /* sc->output32l_base = 0xb0; */ + sc->dsp_zero = 0xc0; + /* 0xe0...0x100 are unknown */ + /* sc->tram_base = 0x200 */ + /* sc->tram_addr_base = 0x300 */ + sc->gpr_base = A_FXGPREGBASE; + sc->num_gprs = 0x200; + sc->code_base = A_MICROCODEBASE; + sc->code_size = 0x800 / 2; /* 0x600-0xdff, 2048 words, + * 1024 instructions */ + + sc->mchannel_fx = 8; + sc->num_fxbuses = 16; + sc->num_inputs = 8; + sc->num_outputs = 16; + sc->address_mask = A_PTR_ADDRESS_MASK; + } + if (sc->is_emu10k1) { + sc->has_51 = 0; /* We don't support 5.1 sound Live! 5.1 */ + sc->opcode_shift = 20; + sc->high_operand_shift = 10; + sc->code_base = MICROCODEBASE; + sc->code_size = 0x400 / 2; /* 0x400-0x7ff, 1024 words, + * 512 instructions */ + sc->gpr_base = FXGPREGBASE; + sc->num_gprs = 0x100; + sc->input_base = 0x10; + sc->output_base = 0x20; + /* + * XXX 5.1 Analog outputs are inside efxc address space! + * They use ouput+0x11/+0x12 (=efxc+1/+2). + * Don't use this efx registers for recording on SB Live! 5.1! + */ + sc->efxc_base = 0x30; + sc->dsp_zero = 0x40; + sc->mchannel_fx = 0; + sc->num_fxbuses = 8; + sc->num_inputs = 8; + sc->num_outputs = 16; + sc->address_mask = PTR_ADDRESS_MASK; + } + if (sc->opcode_shift == 0) + goto bad; + + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + pci_enable_busmaster(dev); + + i = PCIR_BAR(0); + sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE); + if (sc->reg == NULL) { + device_printf(dev, "unable to map register space\n"); + goto bad; + } + sc->st = rman_get_bustag(sc->reg); + sc->sh = rman_get_bushandle(sc->reg); + + for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) + sc->timer[i] = 0; /* disable it */ + + i = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); + if ((sc->irq == NULL) || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) { + device_printf(dev, "unable to map interrupt\n"); + goto bad; + } + if (emu_rm_init(sc) != 0) { + device_printf(dev, "unable to create resource manager\n"); + goto bad; + } + if (sc->is_cardbus) + if (emu_cardbus_init(sc) != 0) { + device_printf(dev, "unable to initialize CardBus interface\n"); + goto bad; + } + sc->ctx = device_get_sysctl_ctx(dev); + if (sc->ctx == NULL) + goto bad; + sc->root = device_get_sysctl_tree(dev); + if (sc->root == NULL) + goto bad; + if (emu_init(sc) == -1) { + device_printf(dev, "unable to initialize the card\n"); + goto bad; + } + if (emu10kx_dev_init(sc) == ENXIO) { + device_printf(dev, "unable to create control device\n"); + goto bad; + } + snprintf(status, 255, "rev %d at io 0x%lx irq %ld", sc->rev, rman_get_start(sc->reg), rman_get_start(sc->irq)); + + /* Voices */ + for (i = 0; i < NUM_G; i++) { + sc->voice[i].vnum = i; + sc->voice[i].slave = NULL; + sc->voice[i].busy = 0; + sc->voice[i].ismaster = 0; + sc->voice[i].running = 0; + sc->voice[i].b16 = 0; + sc->voice[i].stereo = 0; + sc->voice[i].speed = 0; + sc->voice[i].start = 0; + sc->voice[i].end = 0; + } + + /* PCM Audio */ + /* FRONT */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_FRONT; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_FRONT], func); + +#ifdef SND_EMU10KX_MULTICHANNEL + /* REAR */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_REAR; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_REAR] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_REAR], func); + if (sc->has_51) { + /* CENTER */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_CENTER; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_CENTER], func); + /* SUB */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_SUB; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_SUB] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_SUB], func); + } + if (sc->has_71) { + /* SIDE */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_SIDE; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_SIDE], func); + }; +#ifdef SND_EMU10KX_MCH_RECORDING + /* MULTICHANNEL RECORDING */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_MCHRECORD; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_MCHRECORD], func); + +#endif /* SMD_EMU10KX_MCH_RECORDING */ +#endif /* SND_EMU10KX_MULTICHANNEL */ + + /* Midi Interface 1: Live!, Audigy, Audigy 2 */ + if ((sc->is_emu10k1) || (sc->is_emu10k2) || (sc->is_ca0102)) { + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + midiinfo[0] = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (midiinfo[0] == NULL) { + error = ENOMEM; + goto bad; + } + midiinfo[0]->card = sc; + if (sc->is_emu10k2 || (sc->is_ca0102)) { + midiinfo[0]->port = A_MUDATA1; + midiinfo[0]->portnr = 1; + } + if (sc->is_emu10k1) { + midiinfo[0]->port = MUDATA; + midiinfo[0]->portnr = 1; + } + func->func = SCF_MIDI; + func->varinfo = midiinfo[0]; + sc->midi[0] = device_add_child(dev, "midi", -1); + device_set_ivars(sc->midi[0], func); + } + /* Midi Interface 2: Audigy, Audigy 2 (on AudigyDrive) */ + if (sc->is_emu10k2 || (sc->is_ca0102)) { + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + midiinfo[1] = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (midiinfo[1] == NULL) { + error = ENOMEM; + goto bad; + } + midiinfo[1]->card = sc; + + midiinfo[1]->port = A_MUDATA2; + midiinfo[1]->portnr = 2; + + func->func = SCF_MIDI; + func->varinfo = midiinfo[1]; + sc->midi[1] = device_add_child(dev, "midi", -1); + device_set_ivars(sc->midi[1], func); + } + + return (bus_generic_attach(dev)); + +bad: + /* XXX can we just call emu_pci_detach here? */ + if (sc->cdev) + emu10kx_dev_uninit(sc); + if (sc->rm != NULL) + emu_rm_uninit(sc); + if (sc->reg) + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); + if (sc->ih) + bus_teardown_intr(dev, sc->irq, sc->ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + mtx_destroy(&sc->lock); + mtx_destroy(&sc->rw); + return (error); +} + +static int +emu_pci_detach(device_t dev) +{ + struct emu_sc_info *sc; + int devcount, i; + device_t *childlist; + int r = 0; + + sc = device_get_softc(dev); + + for (i = 0; i < RT_COUNT; i++) { + if (sc->pcm[i] != NULL) + r = device_delete_child(dev, sc->pcm[i]); + if (r) + return (r); + } + if (sc->midi[0] != NULL) + r = device_delete_child(dev, sc->midi[0]); + if (r) + return (r); + if (sc->midi[1] != NULL) + r = device_delete_child(dev, sc->midi[1]); + if (r) + return (r); + (void)device_get_children(dev, &childlist, &devcount); + for (i = 0; i < devcount - 1; i++) { + device_printf(dev, "removing stale child %d (unit %d)\n", i, device_get_unit(childlist[i])); + device_delete_child(dev, childlist[i]); + } + free(childlist, M_TEMP); + + /* shutdown chip */ + emu_uninit(sc); + r = emu10kx_dev_uninit(sc); + if (r) + return (r); + emu_rm_uninit(sc); + + if (sc->mem.dmat) + bus_dma_tag_destroy(sc->mem.dmat); + + if (sc->reg) + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + mtx_destroy(&sc->lock); + mtx_destroy(&sc->rw); + return (bus_generic_detach(dev)); +} +/* add suspend, resume */ +static device_method_t emu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, emu_pci_probe), + DEVMETHOD(device_attach, emu_pci_attach), + DEVMETHOD(device_detach, emu_pci_detach), + /* Bus methods */ + DEVMETHOD(bus_read_ivar, emu_read_ivar), + DEVMETHOD(bus_write_ivar, emu_write_ivar), + + {0, 0} +}; + + +static driver_t emu_driver = { + "emu10kx", + emu_methods, + sizeof(struct emu_sc_info), + NULL, + 0, + NULL +}; + +static int +emu_modevent(module_t mod __unused, int cmd, void *data __unused) +{ + int err = 0; + + switch (cmd) { + case MOD_LOAD: + break; /* Success */ + + case MOD_UNLOAD: + case MOD_SHUTDOWN: + + /* XXX Should we check state of pcm & midi subdevices here? */ + + break; /* Success */ + + default: + err = EINVAL; + break; + } + + return (err); + +} + +static devclass_t emu_devclass; + +DRIVER_MODULE(snd_emu10kx, pci, emu_driver, emu_devclass, emu_modevent, NULL); +DRIVER_MODULE(snd_emu10kx, cardbus, emu_driver, emu_devclass, emu_modevent, NULL); +MODULE_VERSION(snd_emu10kx, SND_EMU10KX_PREFVER); --- sys/dev/sound/pci/emu10kx.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/emu10kx.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2003-2006 Yuriy Tsibizov + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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, WHETHERIN 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. + * + * $FreeBSD: src/sys/dev/sound/pci/emu10kx.h,v 1.3 2007/01/06 18:59:35 netchild Exp $ + */ + +#ifndef EMU10KX_H +#define EMU10KX_H + +#define SND_EMU10KX_MINVER 1 +#define SND_EMU10KX_PREFVER 1 +#define SND_EMU10KX_MAXVER 1 + +#ifdef _KERNEL + +#define EMUPAGESIZE 4096 +#define NUM_G 64 +#define EMU_PLAY_BUFSZ EMUPAGESIZE*16 +/* Recording is limited by EMUPAGESIZE*16=64K buffer */ +#define EMU_REC_BUFSZ EMUPAGESIZE*16 +#define EMU_MAX_BUFSZ EMUPAGESIZE*16 +#define EMU_MAXPAGES 8192 + + +#define EMU_VAR_FUNC 0 +#define EMU_VAR_ROUTE 1 +#define EMU_VAR_ISEMU10K1 2 + +#define RT_FRONT 0 +#define RT_REAR 1 +#define RT_CENTER 2 +#define RT_SUB 3 +#define RT_SIDE 4 +#define RT_MCHRECORD 5 +#define RT_COUNT 6 + +/* mixer controls */ +/* fx play */ +#define M_FX0_FRONT_L 0 +#define M_FX1_FRONT_R 1 +#define M_FX2_REAR_L 2 +#define M_FX3_REAR_R 3 +#define M_FX4_CENTER 4 +#define M_FX5_SUBWOOFER 5 +#define M_FX6_SIDE_L 6 +#define M_FX7_SIDE_R 7 +/* fx rec */ +#define M_FX0_REC_L 8 +#define M_FX1_REC_R 9 +/* inputs play */ +#define M_IN0_FRONT_L 10 +#define M_IN0_FRONT_R 11 +#define M_IN1_FRONT_L 12 +#define M_IN1_FRONT_R 13 +#define M_IN2_FRONT_L 14 +#define M_IN2_FRONT_R 15 +#define M_IN3_FRONT_L 16 +#define M_IN3_FRONT_R 17 +#define M_IN4_FRONT_L 18 +#define M_IN4_FRONT_R 19 +#define M_IN5_FRONT_L 20 +#define M_IN5_FRONT_R 21 +#define M_IN6_FRONT_L 22 +#define M_IN6_FRONT_R 23 +#define M_IN7_FRONT_L 24 +#define M_IN7_FRONT_R 25 +/* inputs rec */ +#define M_IN0_REC_L 26 +#define M_IN0_REC_R 27 +#define M_IN1_REC_L 28 +#define M_IN1_REC_R 29 +#define M_IN2_REC_L 30 +#define M_IN2_REC_R 31 +#define M_IN3_REC_L 32 +#define M_IN3_REC_R 33 +#define M_IN4_REC_L 34 +#define M_IN4_REC_R 35 +#define M_IN5_REC_L 36 +#define M_IN5_REC_R 37 +#define M_IN6_REC_L 38 +#define M_IN6_REC_R 39 +#define M_IN7_REC_L 40 +#define M_IN7_REC_R 41 +/* master volume */ +#define M_MASTER_FRONT_L 42 +#define M_MASTER_FRONT_R 43 +#define M_MASTER_REAR_L 44 +#define M_MASTER_REAR_R 45 +#define M_MASTER_CENTER 46 +#define M_MASTER_SUBWOOFER 47 +#define M_MASTER_SIDE_L 48 +#define M_MASTER_SIDE_R 49 +/* master rec volume */ +#define M_MASTER_REC_L 50 +#define M_MASTER_REC_R 51 + +#define NUM_MIXERS 52 + +struct emu_sc_info; + +/* MIDI device parameters */ +struct emu_midiinfo { + struct emu_sc_info *card; + int port; + int portnr; +}; + +/* PCM device parameters */ +struct emu_pcminfo { + struct emu_sc_info *card; + int route; +}; + +int emu_intr_register(struct emu_sc_info *sc, uint32_t inte_mask, uint32_t intr_mask, uint32_t(*func) (void *softc, uint32_t irq), void *isc); +int emu_intr_unregister(struct emu_sc_info *sc, int ihandle); + +uint32_t emu_rd(struct emu_sc_info *sc, unsigned int regno, unsigned int size); +void emu_wr(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size); + +uint32_t emu_rdptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg); +void emu_wrptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg, uint32_t data); + +uint32_t emu_rd_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg); +void emu_wr_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg, uint32_t data); + +int emu_timer_create(struct emu_sc_info *sc); +int emu_timer_set(struct emu_sc_info *sc, int timer, int delay); +int emu_timer_enable(struct emu_sc_info *sc, int timer, int go); +int emu_timer_clear(struct emu_sc_info *sc, int timer); + +struct emu_voice; + +struct emu_route { + int routing_left[8]; + int amounts_left[8]; + int routing_right[8]; + int amounts_right[8]; +}; + +struct emu_voice* emu_valloc(struct emu_sc_info *sc); +void emu_vfree(struct emu_sc_info *sc, struct emu_voice *v); +int emu_vinit(struct emu_sc_info *sc, struct emu_voice *m, struct emu_voice *s, + uint32_t sz, struct snd_dbuf *b); +void emu_vroute(struct emu_sc_info *sc, struct emu_route *rt, struct emu_voice *v); +void emu_vsetup(struct emu_voice *v, int fmt, int spd); +void emu_vwrite(struct emu_sc_info *sc, struct emu_voice *v); +void emu_vtrigger(struct emu_sc_info *sc, struct emu_voice *v, int go); +int emu_vpos(struct emu_sc_info *sc, struct emu_voice *v); + +bus_dma_tag_t emu_gettag(struct emu_sc_info *sc); + +void emumix_set_volume(struct emu_sc_info *sc, int mixer_idx, int volume); +int emumix_get_volume(struct emu_sc_info *sc, int mixer_idx); + +void emu_enable_ir(struct emu_sc_info *sc); +#endif /* _KERNEL */ +#endif /* EMU10K1_H */ --- sys/dev/sound/pci/envy24.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/envy24.c Thu Jul 12 12:04:19 2007 @@ -35,7 +35,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/envy24.c,v 1.11.2.2 2007/06/11 19:33:27 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/envy24.c,v 1.13 2007/05/27 19:58:39 joel Exp $"); MALLOC_DEFINE(M_ENVY24, "envy24", "envy24 audio"); --- sys/dev/sound/pci/envy24.h.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/envy24.h Thu Jul 12 12:04:19 2007 @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/envy24.h,v 1.1.2.2 2007/06/11 19:33:27 ariff Exp $ + * $FreeBSD: src/sys/dev/sound/pci/envy24.h,v 1.2 2007/05/27 19:58:39 joel Exp $ */ --- sys/dev/sound/pci/envy24ht.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/envy24ht.c Thu Jul 12 12:04:19 2007 @@ -47,7 +47,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/envy24ht.c,v 1.11.2.2 2007/06/11 19:33:27 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/envy24ht.c,v 1.15 2007/06/02 17:28:26 ariff Exp $"); MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio"); --- sys/dev/sound/pci/envy24ht.h.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/envy24ht.h Thu Jul 12 12:04:19 2007 @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/envy24ht.h,v 1.4.2.2 2007/06/11 19:33:27 ariff Exp $ + * $FreeBSD: src/sys/dev/sound/pci/envy24ht.h,v 1.5 2007/05/27 19:58:39 joel Exp $ */ --- sys/dev/sound/pci/es137x.c.orig Tue Jul 10 04:51:37 2007 +++ sys/dev/sound/pci/es137x.c Thu Jul 12 12:04:19 2007 @@ -59,7 +59,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/es137x.c,v 1.55.2.2 2006/01/16 02:08:56 ariff Exp $"); +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/es137x.c,v 1.71 2007/07/05 10:22:37 ariff Exp $"); #define MEM_MAP_REG 0x14 @@ -93,6 +93,11 @@ #define ES_ADC 2 #define ES_NCHANS 3 +#define ES_DMA_SEGS_MIN 2 +#define ES_DMA_SEGS_MAX 256 +#define ES_BLK_MIN 64 +#define ES_BLK_ALIGN (~(ES_BLK_MIN - 1)) + #define ES1370_DAC1_MINSPEED 5512 #define ES1370_DAC1_MAXSPEED 44100 @@ -105,7 +110,9 @@ struct snd_dbuf *buffer; struct pcmchan_caps caps; int dir, num, index; - u_int32_t fmt, blksz, bufsz; + uint32_t fmt, blksz, blkcnt, bufsz; + uint32_t ptr, prevptr; + int active; }; /* @@ -168,7 +175,7 @@ * 2 = Enable single DAC (DAC2) * 3 = Enable both DACs, swap position (DAC2 comes first instead of DAC1) */ -#define ES_DEFAULT_DAC_CFG 2 +#define ES_DEFAULT_DAC_CFG 0 struct es_info { bus_space_tag_t st; @@ -181,7 +188,7 @@ device_t dev; int num; - unsigned int bufsz; + unsigned int bufsz, blkcnt; /* Contents of board's registers */ uint32_t ctrl; @@ -189,6 +196,8 @@ uint32_t escfg; struct es_chinfo ch[ES_NCHANS]; struct mtx *lock; + struct callout poll_timer; + int poll_ticks, polling; }; #define ES_LOCK(sc) snd_mtxlock((sc)->lock) @@ -198,14 +207,15 @@ /* prototypes */ static void es_intr(void *); static uint32_t es1371_wait_src_ready(struct es_info *); -static void es1371_src_write(struct es_info *, u_short, unsigned short); -static u_int es1371_adc_rate(struct es_info *, u_int, int); -static u_int es1371_dac_rate(struct es_info *, u_int, int); +static void es1371_src_write(struct es_info *, + unsigned short, unsigned short); +static unsigned int es1371_adc_rate(struct es_info *, unsigned int, int); +static unsigned int es1371_dac_rate(struct es_info *, unsigned int, int); static int es1371_init(struct es_info *); static int es1370_init(struct es_info *); -static int es1370_wrcodec(struct es_info *, u_char, u_char); +static int es1370_wrcodec(struct es_info *, unsigned char, unsigned char); -static u_int32_t es_fmt[] = { +static uint32_t es_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, @@ -234,23 +244,23 @@ [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } }; -static __inline u_int32_t +static __inline uint32_t es_rd(struct es_info *es, int regno, int size) { switch (size) { case 1: - return bus_space_read_1(es->st, es->sh, regno); + return (bus_space_read_1(es->st, es->sh, regno)); case 2: - return bus_space_read_2(es->st, es->sh, regno); + return (bus_space_read_2(es->st, es->sh, regno)); case 4: - return bus_space_read_4(es->st, es->sh, regno); + return (bus_space_read_4(es->st, es->sh, regno)); default: - return 0xFFFFFFFF; + return (0xFFFFFFFF); } } static __inline void -es_wr(struct es_info *es, int regno, u_int32_t data, int size) +es_wr(struct es_info *es, int regno, uint32_t data, int size) { switch (size) { @@ -274,12 +284,14 @@ { struct es_info *es; int i; - u_int32_t v; + uint32_t v; es = mix_getdevinfo(m); v = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].avail) v |= (1 << i); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (mixtable[i].avail) + v |= (1 << i); + } /* * Each DAC1/2 for ES1370 can be controlled independently * DAC1 = controlled by synth @@ -291,12 +303,14 @@ v &= ~(1 << SOUND_MIXER_SYNTH); mix_setdevs(m, v); v = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].recmask) v |= (1 << i); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (mixtable[i].recmask) + v |= (1 << i); + } if (ES_SINGLE_PCM_MIX(es->escfg)) /* ditto */ v &= ~(1 << SOUND_MIXER_SYNTH); mix_setrecdevs(m, v); - return 0; + return (0); } static int @@ -305,38 +319,38 @@ struct es_info *es; int l, r, rl, rr, set_dac1; - if (!mixtable[dev].avail) return -1; + if (!mixtable[dev].avail) + return (-1); l = left; - r = mixtable[dev].stereo? right : l; - if (mixtable[dev].left == 0xf) { - rl = (l < 2)? 0x80 : 7 - (l - 2) / 14; - } else { - rl = (l < 10)? 0x80 : 15 - (l - 10) / 6; - } + r = (mixtable[dev].stereo) ? right : l; + if (mixtable[dev].left == 0xf) + rl = (l < 2) ? 0x80 : 7 - (l - 2) / 14; + else + rl = (l < 10) ? 0x80 : 15 - (l - 10) / 6; es = mix_getdevinfo(m); ES_LOCK(es); if (dev == SOUND_MIXER_PCM && (ES_SINGLE_PCM_MIX(es->escfg)) && - ES_DAC1_ENABLED(es->escfg)) { + ES_DAC1_ENABLED(es->escfg)) set_dac1 = 1; - } else { + else set_dac1 = 0; - } if (mixtable[dev].stereo) { - rr = (r < 10)? 0x80 : 15 - (r - 10) / 6; + rr = (r < 10) ? 0x80 : 15 - (r - 10) / 6; es1370_wrcodec(es, mixtable[dev].right, rr); if (set_dac1 && mixtable[SOUND_MIXER_SYNTH].stereo) - es1370_wrcodec(es, mixtable[SOUND_MIXER_SYNTH].right, rr); + es1370_wrcodec(es, + mixtable[SOUND_MIXER_SYNTH].right, rr); } es1370_wrcodec(es, mixtable[dev].left, rl); if (set_dac1) es1370_wrcodec(es, mixtable[SOUND_MIXER_SYNTH].left, rl); ES_UNLOCK(es); - return l | (r << 8); + return (l | (r << 8)); } static int -es1370_mixsetrecsrc(struct snd_mixer *m, u_int32_t src) +es1370_mixsetrecsrc(struct snd_mixer *m, uint32_t src) { struct es_info *es; int i, j = 0; @@ -349,9 +363,8 @@ ES_LOCK(es); if ((src & (1 << SOUND_MIXER_PCM)) && ES_SINGLE_PCM_MIX(es->escfg) && - ES_DAC1_ENABLED(es->escfg)) { + ES_DAC1_ENABLED(es->escfg)) j |= mixtable[SOUND_MIXER_SYNTH].recmask; - } es1370_wrcodec(es, CODEC_LIMIX1, j & 0x55); es1370_wrcodec(es, CODEC_RIMIX1, j & 0xaa); es1370_wrcodec(es, CODEC_LIMIX2, (j >> 8) & 0x17); @@ -360,13 +373,13 @@ es1370_wrcodec(es, CODEC_OMIX2, 0x3f); ES_UNLOCK(es); - return src; + return (src); } static kobj_method_t es1370_mixer_methods[] = { - KOBJMETHOD(mixer_init, es1370_mixinit), - KOBJMETHOD(mixer_set, es1370_mixset), - KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc), + KOBJMETHOD(mixer_init, es1370_mixinit), + KOBJMETHOD(mixer_set, es1370_mixset), + KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc), { 0, 0 } }; MIXER_DECLARE(es1370_mixer); @@ -374,30 +387,31 @@ /* -------------------------------------------------------------------- */ static int -es1370_wrcodec(struct es_info *es, u_char i, u_char data) +es1370_wrcodec(struct es_info *es, unsigned char i, unsigned char data) { - u_int t; + unsigned int t; ES_LOCK_ASSERT(es); for (t = 0; t < 0x1000; t++) { if ((es_rd(es, ES1370_REG_STATUS, 4) & - STAT_CSTAT) == 0) { + STAT_CSTAT) == 0) { es_wr(es, ES1370_REG_CODEC, - ((u_short)i << CODEC_INDEX_SHIFT) | data, 2); - return 0; + ((unsigned short)i << CODEC_INDEX_SHIFT) | data, 2); + return (0); } DELAY(1); } device_printf(es->dev, "%s: timed out\n", __func__); - return -1; + return (-1); } /* -------------------------------------------------------------------- */ /* channel interface */ static void * -eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct es_info *es = devinfo; struct es_chinfo *ch; @@ -408,26 +422,26 @@ if (dir == PCMDIR_PLAY) { index = ES_GP(es->escfg); es->escfg = ES_SET_GP(es->escfg, index + 1); - if (index == 0) { + if (index == 0) index = ES_DAC_FIRST(es->escfg); - } else if (index == 1) { + else if (index == 1) index = ES_DAC_SECOND(es->escfg); - } else { - device_printf(es->dev, "Invalid ES_GP index: %d\n", index); + else { + device_printf(es->dev, + "Invalid ES_GP index: %d\n", index); ES_UNLOCK(es); - return NULL; + return (NULL); } if (!(index == ES_DAC1 || index == ES_DAC2)) { - device_printf(es->dev, "Unknown DAC: %d\n", - index + 1); + device_printf(es->dev, "Unknown DAC: %d\n", index + 1); ES_UNLOCK(es); - return NULL; + return (NULL); } if (es->ch[index].channel != NULL) { device_printf(es->dev, "DAC%d already initialized!\n", - index + 1); + index + 1); ES_UNLOCK(es); - return NULL; + return (NULL); } } else index = ES_ADC; @@ -443,7 +457,7 @@ } else { uint32_t fixed_rate = ES_FIXED_RATE(es->escfg); if (!(fixed_rate < es_caps.minspeed || - fixed_rate > es_caps.maxspeed)) { + fixed_rate > es_caps.maxspeed)) { ch->caps.maxspeed = fixed_rate; ch->caps.minspeed = fixed_rate; } @@ -453,33 +467,42 @@ ch->channel = c; ch->buffer = b; ch->bufsz = es->bufsz; - ch->blksz = ch->bufsz / 2; + ch->blkcnt = es->blkcnt; + ch->blksz = ch->bufsz / ch->blkcnt; ch->dir = dir; ES_UNLOCK(es); - if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) != 0) - return NULL; + if (sndbuf_alloc(ch->buffer, es->parent_dmat, 0, ch->bufsz) != 0) + return (NULL); ES_LOCK(es); if (dir == PCMDIR_PLAY) { if (ch->index == ES_DAC1) { - es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC1_FRAMEADR >> 8, 1); - es_wr(es, ES1370_REG_DAC1_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4); - es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + es_wr(es, ES1370_REG_MEMPAGE, + ES1370_REG_DAC1_FRAMEADR >> 8, 1); + es_wr(es, ES1370_REG_DAC1_FRAMEADR & 0xff, + sndbuf_getbufaddr(ch->buffer), 4); + es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, + (ch->bufsz >> 2) - 1, 4); } else { - es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8, 1); - es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4); - es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + es_wr(es, ES1370_REG_MEMPAGE, + ES1370_REG_DAC2_FRAMEADR >> 8, 1); + es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff, + sndbuf_getbufaddr(ch->buffer), 4); + es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, + (ch->bufsz >> 2) - 1, 4); } } else { es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8, 1); - es_wr(es, ES1370_REG_ADC_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4); - es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + es_wr(es, ES1370_REG_ADC_FRAMEADR & 0xff, + sndbuf_getbufaddr(ch->buffer), 4); + es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, + (ch->bufsz >> 2) - 1, 4); } ES_UNLOCK(es); - return ch; + return (ch); } static int -eschan_setformat(kobj_t obj, void *data, u_int32_t format) +eschan_setformat(kobj_t obj, void *data, uint32_t format) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; @@ -488,38 +511,46 @@ if (ch->dir == PCMDIR_PLAY) { if (ch->index == ES_DAC1) { es->sctrl &= ~SCTRL_P1FMT; - if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P1SEB; - if (format & AFMT_STEREO) es->sctrl |= SCTRL_P1SMB; + if (format & AFMT_S16_LE) + es->sctrl |= SCTRL_P1SEB; + if (format & AFMT_STEREO) + es->sctrl |= SCTRL_P1SMB; } else { es->sctrl &= ~SCTRL_P2FMT; - if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB; - if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB; + if (format & AFMT_S16_LE) + es->sctrl |= SCTRL_P2SEB; + if (format & AFMT_STEREO) + es->sctrl |= SCTRL_P2SMB; } } else { es->sctrl &= ~SCTRL_R1FMT; - if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB; - if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB; + if (format & AFMT_S16_LE) + es->sctrl |= SCTRL_R1SEB; + if (format & AFMT_STEREO) + es->sctrl |= SCTRL_R1SMB; } es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); ES_UNLOCK(es); ch->fmt = format; - return 0; + return (0); } static int -eschan1370_setspeed(kobj_t obj, void *data, u_int32_t speed) +eschan1370_setspeed(kobj_t obj, void *data, uint32_t speed) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; + ES_LOCK(es); /* Fixed rate , do nothing. */ - if (ch->caps.minspeed == ch->caps.maxspeed) - return ch->caps.maxspeed; + if (ch->caps.minspeed == ch->caps.maxspeed) { + ES_UNLOCK(es); + return (ch->caps.maxspeed); + } if (speed < ch->caps.minspeed) speed = ch->caps.minspeed; if (speed > ch->caps.maxspeed) speed = ch->caps.maxspeed; - ES_LOCK(es); if (ch->index == ES_DAC1) { /* * DAC1 does not support continuous rate settings. @@ -546,11 +577,11 @@ } es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); ES_UNLOCK(es); - return speed; + return (speed); } static int -eschan1371_setspeed(kobj_t obj, void *data, u_int32_t speed) +eschan1371_setspeed(kobj_t obj, void *data, uint32_t speed) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; @@ -563,33 +594,132 @@ else i = es1371_adc_rate(es, speed, ch->index); /* record */ ES_UNLOCK(es); - delta = (speed > i) ? speed - i : i - speed; + delta = (speed > i) ? (speed - i) : (i - speed); if (delta < 2) - return speed; - return i; + return (speed); + return (i); } static int -eschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +eschan_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt) { - struct es_info *es; struct es_chinfo *ch = data; - uint32_t oblksz, obufsz; - int error; + struct es_info *es = ch->parent; + + blksz &= ES_BLK_ALIGN; + + if (blksz > (sndbuf_getmaxsize(ch->buffer) / ES_DMA_SEGS_MIN)) + blksz = sndbuf_getmaxsize(ch->buffer) / ES_DMA_SEGS_MIN; + if (blksz < ES_BLK_MIN) + blksz = ES_BLK_MIN; + if (blkcnt > ES_DMA_SEGS_MAX) + blkcnt = ES_DMA_SEGS_MAX; + if (blkcnt < ES_DMA_SEGS_MIN) + blkcnt = ES_DMA_SEGS_MIN; + + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) { + if ((blkcnt >> 1) >= ES_DMA_SEGS_MIN) + blkcnt >>= 1; + else if ((blksz >> 1) >= ES_BLK_MIN) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->buffer) != blksz || + sndbuf_getblkcnt(ch->buffer) != blkcnt) && + sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) + device_printf(es->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + ch->bufsz = sndbuf_getsize(ch->buffer); + ch->blksz = sndbuf_getblksz(ch->buffer); + ch->blkcnt = sndbuf_getblkcnt(ch->buffer); + + return (1); +} + +static int +eschan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct es_chinfo *ch = data; + struct es_info *es = ch->parent; + + eschan_setfragments(obj, data, blksz, es->blkcnt); + + return (ch->blksz); +} + +#define es_chan_active(es) ((es)->ch[ES_DAC1].active + \ + (es)->ch[ES_DAC2].active + \ + (es)->ch[ES_ADC].active) + +static __inline int +es_poll_channel(struct es_chinfo *ch) +{ + struct es_info *es; + uint32_t sz, delta; + uint32_t reg, ptr; + + if (ch == NULL || ch->channel == NULL || ch->active == 0) + return (0); + + es = ch->parent; + if (ch->dir == PCMDIR_PLAY) { + if (ch->index == ES_DAC1) + reg = ES1370_REG_DAC1_FRAMECNT; + else + reg = ES1370_REG_DAC2_FRAMECNT; + } else + reg = ES1370_REG_ADC_FRAMECNT; + sz = ch->blksz * ch->blkcnt; + es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4); + ptr = es_rd(es, reg & 0x000000ff, 4) >> 16; + ptr <<= 2; + ch->ptr = ptr; + ptr %= sz; + ptr &= ~(ch->blksz - 1); + delta = (sz + ptr - ch->prevptr) % sz; - oblksz = ch->blksz; - obufsz = ch->bufsz; - ch->blksz = blocksize; - ch->bufsz = ch->blksz * 2; - error = sndbuf_resize(ch->buffer, 2, ch->blksz); - if (error != 0) { - ch->blksz = oblksz; - ch->bufsz = obufsz; - es = ch->parent; - device_printf(es->dev, "unable to set block size, blksz = %d, " - "error = %d", blocksize, error); + if (delta < ch->blksz) + return (0); + + ch->prevptr = ptr; + + return (1); +} + +static void +es_poll_callback(void *arg) +{ + struct es_info *es = arg; + uint32_t trigger = 0; + int i; + + if (es == NULL) + return; + + ES_LOCK(es); + if (es->polling == 0 || es_chan_active(es) == 0) { + ES_UNLOCK(es); + return; + } + + for (i = 0; i < ES_NCHANS; i++) { + if (es_poll_channel(&es->ch[i]) != 0) + trigger |= 1 << i; + } + + /* XXX */ + callout_reset(&es->poll_timer, 1/*es->poll_ticks*/, + es_poll_callback, es); + + ES_UNLOCK(es); + + for (i = 0; i < ES_NCHANS; i++) { + if (trigger & (1 << i)) + chn_intr(es->ch[i].channel); } - return ch->blksz; } static int @@ -599,51 +729,95 @@ struct es_info *es = ch->parent; uint32_t cnt, b = 0; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + if (!PCMTRIG_COMMON(go)) return 0; + ES_LOCK(es); cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1; if (ch->fmt & AFMT_16BIT) b |= 0x02; if (ch->fmt & AFMT_STEREO) b |= 0x01; - ES_LOCK(es); if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { if (ch->index == ES_DAC1) { es->ctrl |= CTRL_DAC1_EN; - es->sctrl &= ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD); - es->sctrl |= SCTRL_P1INTEN | b; + es->sctrl &= ~(SCTRL_P1LOOPSEL | + SCTRL_P1PAUSE | SCTRL_P1SCTRLD); + if (es->polling == 0) + es->sctrl |= SCTRL_P1INTEN; + else + es->sctrl &= ~SCTRL_P1INTEN; + es->sctrl |= b; es_wr(es, ES1370_REG_DAC1_SCOUNT, cnt, 4); /* start at beginning of buffer */ - es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC1_FRAMECNT >> 8, 4); - es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + es_wr(es, ES1370_REG_MEMPAGE, + ES1370_REG_DAC1_FRAMECNT >> 8, 4); + es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, + (ch->bufsz >> 2) - 1, 4); } else { es->ctrl |= CTRL_DAC2_EN; - es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN); - es->sctrl |= SCTRL_P2INTEN | (b << 2) | - (((b & 2) ? : 1) << SCTRL_SH_P2ENDINC); + es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | + SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | + SCTRL_P2DACSEN); + if (es->polling == 0) + es->sctrl |= SCTRL_P2INTEN; + else + es->sctrl &= ~SCTRL_P2INTEN; + es->sctrl |= (b << 2) | + ((((b >> 1) & 1) + 1) << SCTRL_SH_P2ENDINC); es_wr(es, ES1370_REG_DAC2_SCOUNT, cnt, 4); /* start at beginning of buffer */ - es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMECNT >> 8, 4); - es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + es_wr(es, ES1370_REG_MEMPAGE, + ES1370_REG_DAC2_FRAMECNT >> 8, 4); + es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, + (ch->bufsz >> 2) - 1, 4); } - } else es->ctrl &= ~(ch->index == ES_DAC1 ? CTRL_DAC1_EN : CTRL_DAC2_EN); + } else + es->ctrl &= ~((ch->index == ES_DAC1) ? + CTRL_DAC1_EN : CTRL_DAC2_EN); } else { if (go == PCMTRIG_START) { es->ctrl |= CTRL_ADC_EN; es->sctrl &= ~SCTRL_R1LOOPSEL; - es->sctrl |= SCTRL_R1INTEN | (b << 4); + if (es->polling == 0) + es->sctrl |= SCTRL_R1INTEN; + else + es->sctrl &= ~SCTRL_R1INTEN; + es->sctrl |= b << 4; es_wr(es, ES1370_REG_ADC_SCOUNT, cnt, 4); /* start at beginning of buffer */ - es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMECNT >> 8, 4); - es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); - } else es->ctrl &= ~CTRL_ADC_EN; + es_wr(es, ES1370_REG_MEMPAGE, + ES1370_REG_ADC_FRAMECNT >> 8, 4); + es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, + (ch->bufsz >> 2) - 1, 4); + } else + es->ctrl &= ~CTRL_ADC_EN; } es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + if (go == PCMTRIG_START) { + if (es->polling != 0) { + ch->ptr = 0; + ch->prevptr = 0; + if (es_chan_active(es) == 0) { + es->poll_ticks = 1; + callout_reset(&es->poll_timer, 1, + es_poll_callback, es); + } + } + ch->active = 1; + } else { + ch->active = 0; + if (es->polling != 0) { + if (es_chan_active(es) == 0) { + callout_stop(&es->poll_timer); + es->poll_ticks = 1; + } + } + } ES_UNLOCK(es); - return 0; + return (0); } static int @@ -651,21 +825,29 @@ { struct es_chinfo *ch = data; struct es_info *es = ch->parent; - u_int32_t reg, cnt; + uint32_t reg, cnt; - if (ch->dir == PCMDIR_PLAY) { - if (ch->index == ES_DAC1) - reg = ES1370_REG_DAC1_FRAMECNT; - else - reg = ES1370_REG_DAC2_FRAMECNT; - } else - reg = ES1370_REG_ADC_FRAMECNT; ES_LOCK(es); - es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4); - cnt = es_rd(es, reg & 0x000000ff, 4) >> 16; + if (es->polling != 0) + cnt = ch->ptr; + else { + if (ch->dir == PCMDIR_PLAY) { + if (ch->index == ES_DAC1) + reg = ES1370_REG_DAC1_FRAMECNT; + else + reg = ES1370_REG_DAC2_FRAMECNT; + } else + reg = ES1370_REG_ADC_FRAMECNT; + es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4); + cnt = es_rd(es, reg & 0x000000ff, 4) >> 16; + /* cnt is longwords */ + cnt <<= 2; + } ES_UNLOCK(es); - /* cnt is longwords */ - return cnt << 2; + + cnt &= ES_BLK_ALIGN; + + return (cnt); } static struct pcmchan_caps * @@ -673,29 +855,31 @@ { struct es_chinfo *ch = data; - return &ch->caps; + return (&ch->caps); } static kobj_method_t eschan1370_methods[] = { - KOBJMETHOD(channel_init, eschan_init), - KOBJMETHOD(channel_setformat, eschan_setformat), - KOBJMETHOD(channel_setspeed, eschan1370_setspeed), - KOBJMETHOD(channel_setblocksize, eschan_setblocksize), - KOBJMETHOD(channel_trigger, eschan_trigger), - KOBJMETHOD(channel_getptr, eschan_getptr), - KOBJMETHOD(channel_getcaps, eschan_getcaps), + KOBJMETHOD(channel_init, eschan_init), + KOBJMETHOD(channel_setformat, eschan_setformat), + KOBJMETHOD(channel_setspeed, eschan1370_setspeed), + KOBJMETHOD(channel_setblocksize, eschan_setblocksize), + KOBJMETHOD(channel_setfragments, eschan_setfragments), + KOBJMETHOD(channel_trigger, eschan_trigger), + KOBJMETHOD(channel_getptr, eschan_getptr), + KOBJMETHOD(channel_getcaps, eschan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(eschan1370); static kobj_method_t eschan1371_methods[] = { - KOBJMETHOD(channel_init, eschan_init), - KOBJMETHOD(channel_setformat, eschan_setformat), - KOBJMETHOD(channel_setspeed, eschan1371_setspeed), - KOBJMETHOD(channel_setblocksize, eschan_setblocksize), - KOBJMETHOD(channel_trigger, eschan_trigger), - KOBJMETHOD(channel_getptr, eschan_getptr), - KOBJMETHOD(channel_getcaps, eschan_getcaps), + KOBJMETHOD(channel_init, eschan_init), + KOBJMETHOD(channel_setformat, eschan_setformat), + KOBJMETHOD(channel_setspeed, eschan1371_setspeed), + KOBJMETHOD(channel_setblocksize, eschan_setblocksize), + KOBJMETHOD(channel_setfragments, eschan_setfragments), + KOBJMETHOD(channel_trigger, eschan_trigger), + KOBJMETHOD(channel_getptr, eschan_getptr), + KOBJMETHOD(channel_getcaps, eschan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(eschan1371); @@ -709,6 +893,10 @@ uint32_t intsrc, sctrl; ES_LOCK(es); + if (es->polling != 0) { + ES_UNLOCK(es); + return; + } intsrc = es_rd(es, ES1370_REG_STATUS, 4); if ((intsrc & STAT_INTR) == 0) { ES_UNLOCK(es); @@ -716,17 +904,23 @@ } sctrl = es->sctrl; - if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN; - if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN; - if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN; + if (intsrc & STAT_ADC) + sctrl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctrl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctrl &= ~SCTRL_P2INTEN; es_wr(es, ES1370_REG_SERIAL_CONTROL, sctrl, 4); es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); ES_UNLOCK(es); - if (intsrc & STAT_ADC) chn_intr(es->ch[ES_ADC].channel); - if (intsrc & STAT_DAC1) chn_intr(es->ch[ES_DAC1].channel); - if (intsrc & STAT_DAC2) chn_intr(es->ch[ES_DAC2].channel); + if (intsrc & STAT_ADC) + chn_intr(es->ch[ES_ADC].channel); + if (intsrc & STAT_DAC1) + chn_intr(es->ch[ES_DAC1].channel); + if (intsrc & STAT_DAC2) + chn_intr(es->ch[ES_DAC2].channel); } /* ES1370 specific */ @@ -738,7 +932,7 @@ /* ES1370 default to fixed rate operation */ if (resource_int_value(device_get_name(es->dev), - device_get_unit(es->dev), "fixed_rate", &r) == 0) { + device_get_unit(es->dev), "fixed_rate", &r) == 0) { fixed_rate = r; if (fixed_rate) { if (fixed_rate < es_caps.minspeed) @@ -750,8 +944,8 @@ fixed_rate = es_caps.maxspeed; if (resource_int_value(device_get_name(es->dev), - device_get_unit(es->dev), "single_pcm_mixer", &r) == 0) - single_pcm = (r) ? 1 : 0; + device_get_unit(es->dev), "single_pcm_mixer", &r) == 0) + single_pcm = (r != 0) ? 1 : 0; else single_pcm = 1; @@ -760,34 +954,38 @@ single_pcm = 1; /* This is ES1370 */ es->escfg = ES_SET_IS_ES1370(es->escfg, 1); - if (fixed_rate) { + if (fixed_rate) es->escfg = ES_SET_FIXED_RATE(es->escfg, fixed_rate); - } else { + else { es->escfg = ES_SET_FIXED_RATE(es->escfg, 0); fixed_rate = DSP_DEFAULT_SPEED; } - if (single_pcm) { + if (single_pcm) es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1); - } else { + else es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0); - } es->ctrl = CTRL_CDC_EN | CTRL_JYSTK_EN | CTRL_SERR_DIS | - (DAC2_SRTODIV(fixed_rate) << CTRL_SH_PCLKDIV); + (DAC2_SRTODIV(fixed_rate) << CTRL_SH_PCLKDIV); es->ctrl |= 3 << CTRL_SH_WTSRSEL; es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); es->sctrl = 0; es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); - es1370_wrcodec(es, CODEC_RES_PD, 3);/* No RST, PD */ - es1370_wrcodec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use - * {LR,B}CLK2 and run off the LRCLK2 - * PLL; program DAC_SYNC=0! */ - es1370_wrcodec(es, CODEC_ADSEL, 0);/* Recording source is mixer */ - es1370_wrcodec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */ + /* No RST, PD */ + es1370_wrcodec(es, CODEC_RES_PD, 3); + /* + * CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; + * program DAC_SYNC=0! + */ + es1370_wrcodec(es, CODEC_CSEL, 0); + /* Recording source is mixer */ + es1370_wrcodec(es, CODEC_ADSEL, 0); + /* MIC amp is 0db */ + es1370_wrcodec(es, CODEC_MGAIN, 0); ES_UNLOCK(es); - return 0; + return (0); } /* ES1371 specific */ @@ -805,7 +1003,8 @@ cssr = 0; devid = pci_get_devid(es->dev); revid = pci_get_revid(es->dev); - subdev = (pci_get_subdevice(es->dev) << 16) | pci_get_subvendor(es->dev); + subdev = (pci_get_subdevice(es->dev) << 16) | + pci_get_subvendor(es->dev); /* * Joyport blacklist. Either we're facing with broken hardware * or because this hardware need special (unknown) initialization @@ -846,20 +1045,21 @@ es_wr(es, ES1371_REG_SMPRATE, ES1371_DIS_SRC, 4); for (idx = 0; idx < 0x80; idx++) es1371_src_write(es, idx, 0); - es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); + es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10); - es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); + es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10); - es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12); - es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12); - es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12); - es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); - es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12); - es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); - es1371_adc_rate(es, 22050, ES_ADC); - es1371_dac_rate(es, 22050, ES_DAC1); - es1371_dac_rate(es, 22050, ES_DAC2); - /* WARNING: + es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12); + es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12); + es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12); + es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); + es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12); + es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); + es1371_adc_rate(es, 22050, ES_ADC); + es1371_dac_rate(es, 22050, ES_DAC1); + es1371_dac_rate(es, 22050, ES_DAC2); + /* + * WARNING: * enabling the sample rate converter without properly programming * its parameters causes the chip to lock up (the SRC busy bit will * be stuck high, and I've found no way to rectify this other than @@ -878,38 +1078,41 @@ /* -------------------------------------------------------------------- */ static int -es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data) +es1371_wrcd(kobj_t obj, void *s, int addr, uint32_t data) { uint32_t t, x, orig; struct es_info *es = (struct es_info*)s; - for (t = 0; t < 0x1000; t++) + for (t = 0; t < 0x1000; t++) { if (!es_rd(es, ES1371_REG_CODEC & CODEC_WIP, 4)) break; + } /* save the current state for later */ x = orig = es_rd(es, ES1371_REG_SMPRATE, 4); /* enable SRC state data in SRC mux */ - es_wr(es, ES1371_REG_SMPRATE, - (x & - (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) | - 0x00010000, 4); + es_wr(es, ES1371_REG_SMPRATE, (x & (ES1371_DIS_SRC | ES1371_DIS_P1 | + ES1371_DIS_P2 | ES1371_DIS_R1)) | 0x00010000, 4); /* busy wait */ - for (t = 0; t < 0x1000; t++) - if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000) + for (t = 0; t < 0x1000; t++) { + if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == + 0x00000000) break; + } /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t = 0; t < 0x1000; t++) - if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000) + for (t = 0; t < 0x1000; t++) { + if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == + 0x00010000) break; + } - es_wr(es, ES1371_REG_CODEC, - ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | - ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), 4); + es_wr(es, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) & + CODEC_POADD_MASK) | ((data << CODEC_PODAT_SHIFT) & + CODEC_PODAT_MASK), 4); /* restore SRC reg */ es1371_wait_src_ready(s); es_wr(es, ES1371_REG_SMPRATE, orig, 4); - return 0; + return (0); } static int @@ -918,83 +1121,88 @@ uint32_t t, x, orig; struct es_info *es = (struct es_info *)s; - for (t = 0; t < 0x1000; t++) + for (t = 0; t < 0x1000; t++) { if (!(x = es_rd(es, ES1371_REG_CODEC, 4) & CODEC_WIP)) break; + } /* save the current state for later */ x = orig = es_rd(es, ES1371_REG_SMPRATE, 4); /* enable SRC state data in SRC mux */ - es_wr(es, ES1371_REG_SMPRATE, - (x & - (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) | - 0x00010000, 4); + es_wr(es, ES1371_REG_SMPRATE, (x & (ES1371_DIS_SRC | ES1371_DIS_P1 | + ES1371_DIS_P2 | ES1371_DIS_R1)) | 0x00010000, 4); /* busy wait */ - for (t = 0; t < 0x1000; t++) - if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000) + for (t = 0; t < 0x1000; t++) { + if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == + 0x00000000) break; + } /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t = 0; t < 0x1000; t++) - if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000) + for (t = 0; t < 0x1000; t++) { + if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == + 0x00010000) break; + } - es_wr(es, ES1371_REG_CODEC, - ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | - CODEC_PORD, 4); + es_wr(es, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) & + CODEC_POADD_MASK) | CODEC_PORD, 4); /* restore SRC reg */ es1371_wait_src_ready(s); es_wr(es, ES1371_REG_SMPRATE, orig, 4); /* now wait for the stinkin' data (RDY) */ - for (t = 0; t < 0x1000; t++) + for (t = 0; t < 0x1000; t++) { if ((x = es_rd(es, ES1371_REG_CODEC, 4)) & CODEC_RDY) break; + } return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } static kobj_method_t es1371_ac97_methods[] = { - KOBJMETHOD(ac97_read, es1371_rdcd), - KOBJMETHOD(ac97_write, es1371_wrcd), + KOBJMETHOD(ac97_read, es1371_rdcd), + KOBJMETHOD(ac97_write, es1371_wrcd), { 0, 0 } }; AC97_DECLARE(es1371_ac97); /* -------------------------------------------------------------------- */ -static u_int -es1371_src_read(struct es_info *es, u_short reg) +static unsigned int +es1371_src_read(struct es_info *es, unsigned short reg) { uint32_t r; - r = es1371_wait_src_ready(es) & - (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1); + r = es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | + ES1371_DIS_P2 | ES1371_DIS_R1); r |= ES1371_SRC_RAM_ADDRO(reg); es_wr(es, ES1371_REG_SMPRATE, r, 4); - return ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es)); + return (ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es))); } static void -es1371_src_write(struct es_info *es, u_short reg, u_short data) +es1371_src_write(struct es_info *es, unsigned short reg, unsigned short data) { uint32_t r; - r = es1371_wait_src_ready(es) & - (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1); + r = es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | + ES1371_DIS_P2 | ES1371_DIS_R1); r |= ES1371_SRC_RAM_ADDRO(reg) | ES1371_SRC_RAM_DATAO(data); es_wr(es, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE, 4); } -static u_int -es1371_adc_rate(struct es_info *es, u_int rate, int set) +static unsigned int +es1371_adc_rate(struct es_info *es, unsigned int rate, int set) { - u_int n, truncm, freq, result; + unsigned int n, truncm, freq, result; ES_LOCK_ASSERT(es); - if (rate > 48000) rate = 48000; - if (rate < 4000) rate = 4000; + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; n = rate / 3000; if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) n--; @@ -1003,46 +1211,54 @@ result = (48000UL << 15) / (freq / n); if (set) { if (rate >= 24000) { - if (truncm > 239) truncm = 239; + if (truncm > 239) + truncm = 239; es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, - (((239 - truncm) >> 1) << 9) | (n << 4)); + (((239 - truncm) >> 1) << 9) | (n << 4)); } else { - if (truncm > 119) truncm = 119; + if (truncm > 119) + truncm = 119; es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, - 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); } es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS, - (es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & - 0x00ff) | ((freq >> 5) & 0xfc00)); - es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + (es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & + 0x00ff) | ((freq >> 5) & 0xfc00)); + es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, + freq & 0x7fff); es1371_src_write(es, ES_SMPREG_VOL_ADC, n << 8); es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, n << 8); } - return result; + return (result); } -static u_int -es1371_dac_rate(struct es_info *es, u_int rate, int set) +static unsigned int +es1371_dac_rate(struct es_info *es, unsigned int rate, int set) { - u_int freq, r, result, dac, dis; + unsigned int freq, r, result, dac, dis; ES_LOCK_ASSERT(es); - if (rate > 48000) rate = 48000; - if (rate < 4000) rate = 4000; + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; freq = ((rate << 15) + 1500) / 3000; result = (freq * 3000) >> 15; - + dac = (set == ES_DAC1) ? ES_SMPREG_DAC1 : ES_SMPREG_DAC2; dis = (set == ES_DAC1) ? ES1371_DIS_P2 : ES1371_DIS_P1; - r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)); + r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | + ES1371_DIS_P2 | ES1371_DIS_R1)); es_wr(es, ES1371_REG_SMPRATE, r, 4); es1371_src_write(es, dac + ES_SMPREG_INT_REGS, - (es1371_src_read(es, dac + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00)); + (es1371_src_read(es, dac + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); es1371_src_write(es, dac + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); - r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | dis | ES1371_DIS_R1)); + r = (es1371_wait_src_ready(es) & + (ES1371_DIS_SRC | dis | ES1371_DIS_R1)); es_wr(es, ES1371_REG_SMPRATE, r, 4); - return result; + return (result); } static uint32_t @@ -1051,13 +1267,14 @@ uint32_t t, r; for (t = 0; t < 0x1000; t++) { - if (!((r = es_rd(es, ES1371_REG_SMPRATE, 4)) & ES1371_SRC_RAM_BUSY)) - return r; + if (!((r = es_rd(es, ES1371_REG_SMPRATE, 4)) & + ES1371_SRC_RAM_BUSY)) + return (r); DELAY(1); } device_printf(es->dev, "%s: timed out 0x%x [0x%x]\n", __func__, ES1371_REG_SMPRATE, r); - return 0; + return (0); } /* -------------------------------------------------------------------- */ @@ -1072,78 +1289,76 @@ switch(pci_get_devid(dev)) { case ES1370_PCI_ID: device_set_desc(dev, "AudioPCI ES1370"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case ES1371_PCI_ID: switch(pci_get_revid(dev)) { case ES1371REV_ES1371_A: device_set_desc(dev, "AudioPCI ES1371-A"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case ES1371REV_ES1371_B: device_set_desc(dev, "AudioPCI ES1371-B"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case ES1371REV_ES1373_A: device_set_desc(dev, "AudioPCI ES1373-A"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case ES1371REV_ES1373_B: device_set_desc(dev, "AudioPCI ES1373-B"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case ES1371REV_ES1373_8: device_set_desc(dev, "AudioPCI ES1373-8"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case ES1371REV_CT5880_A: device_set_desc(dev, "Creative CT5880-A"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); default: device_set_desc(dev, "AudioPCI ES1371-?"); - device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return BUS_PROBE_DEFAULT; + device_printf(dev, + "unknown revision %d -- please report to " + "freebsd-multimedia@freebsd.org\n", + pci_get_revid(dev)); + return (BUS_PROBE_DEFAULT); } - case ES1371_PCI_ID2: device_set_desc(dev, "Strange AudioPCI ES1371-? (vid=3274)"); - device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return BUS_PROBE_DEFAULT; - + device_printf(dev, + "unknown revision %d -- please report to " + "freebsd-multimedia@freebsd.org\n", pci_get_revid(dev)); + return (BUS_PROBE_DEFAULT); case CT4730_PCI_ID: switch(pci_get_revid(dev)) { case CT4730REV_CT4730_A: - device_set_desc(dev, "Creative SB AudioPCI CT4730/EV1938"); - return BUS_PROBE_DEFAULT; + device_set_desc(dev, + "Creative SB AudioPCI CT4730/EV1938"); + return (BUS_PROBE_DEFAULT); default: device_set_desc(dev, "Creative SB AudioPCI CT4730-?"); - device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return BUS_PROBE_DEFAULT; + device_printf(dev, + "unknown revision %d -- please report to " + "freebsd-multimedia@freebsd.org\n", + pci_get_revid(dev)); + return (BUS_PROBE_DEFAULT); } - case CT5880_PCI_ID: switch(pci_get_revid(dev)) { case CT5880REV_CT5880_C: device_set_desc(dev, "Creative CT5880-C"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case CT5880REV_CT5880_D: device_set_desc(dev, "Creative CT5880-D"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); case CT5880REV_CT5880_E: device_set_desc(dev, "Creative CT5880-E"); - return BUS_PROBE_DEFAULT; - + return (BUS_PROBE_DEFAULT); default: device_set_desc(dev, "Creative CT5880-?"); - device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return BUS_PROBE_DEFAULT; + device_printf(dev, + "unknown revision %d -- please report to " + "freebsd-multimedia@freebsd.org\n", + pci_get_revid(dev)); + return (BUS_PROBE_DEFAULT); } - default: - return ENXIO; + return (ENXIO); } } @@ -1162,7 +1377,7 @@ r = es_rd(es, ES1370_REG_STATUS, 4); ES_UNLOCK(es); new_en = (r & ENABLE_SPDIF) ? 1 : 0; - err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req); + err = sysctl_handle_int(oidp, &new_en, 0, req); if (err || req->newptr == NULL) return (err); @@ -1186,7 +1401,6 @@ return (0); } -#if 0 static int sysctl_es137x_latency_timer(SYSCTL_HANDLER_ARGS) { @@ -1200,8 +1414,8 @@ ES_LOCK(es); val = pci_read_config(dev, PCIR_LATTIMER, 1); ES_UNLOCK(es); - err = sysctl_handle_int(oidp, &val, sizeof(val), req); - + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || req->newptr == NULL) return (err); if (val > 255) @@ -1229,8 +1443,8 @@ if (val < es_caps.minspeed) val = 0; ES_UNLOCK(es); - err = sysctl_handle_int(oidp, &val, sizeof(val), req); - + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || req->newptr == NULL) return (err); if (val != 0 && (val < es_caps.minspeed || val > es_caps.maxspeed)) @@ -1268,14 +1482,14 @@ struct es_info *es; struct snddev_info *d; struct snd_mixer *m; - struct cdev *i_dev; device_t dev; uint32_t val, set; int recsrc, level, err; dev = oidp->oid_arg1; d = device_get_softc(dev); - if (d == NULL || d->mixer_dev == NULL || d->mixer_dev->si_drv1 == NULL) + if (!PCM_REGISTERED(d) || d->mixer_dev == NULL || + d->mixer_dev->si_drv1 == NULL) return (EINVAL); es = d->devinfo; if (es == NULL) @@ -1284,53 +1498,53 @@ set = ES_SINGLE_PCM_MIX(es->escfg); val = set; ES_UNLOCK(es); - err = sysctl_handle_int(oidp, &val, sizeof(val), req); - + err = sysctl_handle_int(oidp, &val, 0, req); + if (err || req->newptr == NULL) return (err); if (!(val == 0 || val == 1)) return (EINVAL); if (val == set) return (0); - i_dev = d->mixer_dev; - if (mixer_ioctl(i_dev, 0, (caddr_t)&recsrc, 0, NULL) != EBADF) + PCM_ACQUIRE_QUICK(d); + m = (d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : NULL; + if (m == NULL) { + PCM_RELEASE_QUICK(d); + return (ENODEV); + } + if (mixer_busy(m) != 0) { + PCM_RELEASE_QUICK(d); return (EBUSY); - err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_PCM), - (caddr_t)&level, -1, NULL); - if (!err) - err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_RECSRC), - (caddr_t)&recsrc, -1, NULL); - if (err) - return (err); - if (level < 0) - return (EINVAL); + } + level = mix_get(m, SOUND_MIXER_PCM); + recsrc = mix_getrecsrc(m); + if (level < 0 || recsrc < 0) { + PCM_RELEASE_QUICK(d); + return (ENXIO); + } ES_LOCK(es); if (es->ctrl & (CTRL_ADC_EN | CTRL_DAC1_EN | CTRL_DAC2_EN)) { ES_UNLOCK(es); + PCM_RELEASE_QUICK(d); return (EBUSY); } - if (val) { + if (val) es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1); - } else { + else es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0); - } ES_UNLOCK(es); - m = i_dev->si_drv1; if (!val) { - mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) | - (1 << SOUND_MIXER_SYNTH)); - mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) | - (1 << SOUND_MIXER_SYNTH)); - err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_S