/* $NetBSD: bktr_os.c,v 1.69 2022/05/23 13:53:37 rin Exp $ */
/* $FreeBSD: src/sys/dev/bktr/bktr_os.c,v 1.20 2000/10/20 08:16:53 roger Exp$ */
/*
* This is part of the Driver for Video Capture Cards (Frame grabbers)
* and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879
* chipset.
* Copyright Roger Hardiman and Amancio Hasty.
*
* bktr_os : This has all the Operating System dependent code,
* probe/attach and open/close/ioctl/read/mmap
* memory allocation
* PCI bus interfacing
*
*
*/
/*
* 1. Redistributions of source code must retain the
* Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Amancio Hasty and
* Roger Hardiman
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#if (__FreeBSD_version >=400000) || (NSMBUS > 0)
#include <sys/bus.h> /* used by smbus and newbus */
#endif
#if (__FreeBSD_version >=300000)
#include <machine/bus_memio.h> /* used by bus space */
#include <sys/bus.h> /* used by bus space and newbus */
#include <sys/bus.h>
#endif
#if (__FreeBSD_version >=400000)
#include <sys/rman.h> /* used by newbus */
#include <machine/resource.h> /* used by newbus */
#endif
/*
* the boot time probe routine.
*/
static int
bktr_probe(device_t dev)
{
unsigned int type = pci_get_devid(dev);
unsigned int rev = pci_get_revid(dev);
if (PCI_VENDOR(type) == PCI_VENDOR_BROOKTREE)
{
switch (PCI_PRODUCT(type)) {
case PCI_PRODUCT_BROOKTREE_BT848:
if (rev == 0x12)
device_set_desc(dev, "BrookTree 848A");
else
device_set_desc(dev, "BrookTree 848");
return 0;
case PCI_PRODUCT_BROOKTREE_BT849:
device_set_desc(dev, "BrookTree 849A");
return 0;
case PCI_PRODUCT_BROOKTREE_BT878:
device_set_desc(dev, "BrookTree 878");
return 0;
case PCI_PRODUCT_BROOKTREE_BT879:
device_set_desc(dev, "BrookTree 879");
return 0;
}
}
return ENXIO;
}
/*
* the attach routine.
*/
static int
bktr_attach(device_t dev)
{
u_int latency;
u_int fun;
u_int val;
unsigned int rev;
unsigned int unit;
int error = 0;
#ifdef BROOKTREE_IRQ
u_int old_irq, new_irq;
#endif
struct bktr_softc *bktr = device_get_softc(dev);
unit = device_get_unit(dev);
/* build the device name for bktr_name() */
snprintf(bktr->bktr_xname, sizeof(bktr->bktr_xname), "bktr%d",unit);
/*
* Enable bus mastering and Memory Mapped device
*/
val = pci_read_config(dev, PCIR_COMMAND, 4);
val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
pci_write_config(dev, PCIR_COMMAND, val, 4);
error = bus_setup_intr(dev, bktr->res_irq, INTR_TYPE_TTY,
bktr_intr, bktr, &bktr->res_ih);
if (error) {
device_printf(dev, "could not setup irq\n");
goto fail;
}
/* Update the Device Control Register */
/* on Bt878 and Bt879 cards */
fun = pci_read_config(dev, 0x40, 2);
fun = fun | 1; /* Enable writes to the sub-system vendor ID */
#if defined(BKTR_430_FX_MODE)
if (bootverbose) printf("Using 430 FX chipset compatibility mode\n");
fun = fun | 2; /* Enable Intel 430 FX compatibility mode */
#endif
#if defined(BKTR_SIS_VIA_MODE)
if (bootverbose) printf("Using SiS/VIA chipset compatibility mode\n");
fun = fun | 4; /* Enable SiS/VIA compatibility mode (useful for
OPTi chipset motherboards too */
#endif
pci_write_config(dev, 0x40, fun, 2);
/*
* PCI latency timer. 32 is a good value for 4 bus mastering slots, if
* you have more than four, then 16 would probably be a better value.
*/
#ifndef BROOKTREE_DEF_LATENCY_VALUE
#define BROOKTREE_DEF_LATENCY_VALUE 10
#endif
latency = pci_read_config(dev, PCI_LATENCY_TIMER, 4);
latency = (latency >> 8) & 0xff;
if (bootverbose) {
if (latency)
printf("brooktree%d: PCI bus latency is", unit);
else
printf("brooktree%d: PCI bus latency was 0 changing to",
unit);
}
if (!latency) {
latency = BROOKTREE_DEF_LATENCY_VALUE;
pci_write_config(dev, PCI_LATENCY_TIMER, latency<<8, 4);
}
if (bootverbose) {
printf(" %d.\n", (int) latency);
}
/* read the pci device id and revision id */
fun = pci_get_devid(dev);
rev = pci_get_revid(dev);
/* call the common attach code */
if (common_bktr_attach(bktr, unit, fun, rev) == 0)
return;
/* if this is unit 0 (/dev/bktr0, /dev/tuner0, /dev/vbi0) then make */
/* alias entries to /dev/bktr /dev/tuner and /dev/vbi */
#if (__FreeBSD_version >=500000)
if (unit == 0) {
bktr->bktrdev_alias = make_dev_alias(bktr->bktrdev, "bktr");
bktr->tunerdev_alias= make_dev_alias(bktr->tunerdev, "tuner");
bktr->vbidev_alias = make_dev_alias(bktr->vbidev, "vbi");
}
#endif
return 0;
fail:
if (bktr->res_irq)
bus_release_resource(dev, SYS_RES_IRQ, bktr->irq_rid, bktr->res_irq);
if (bktr->res_mem)
bus_release_resource(dev, SYS_RES_IRQ, bktr->mem_rid, bktr->res_mem);
return error;
}
/*
* the detach routine.
*/
static int
bktr_detach(device_t dev)
{
unsigned int unit;
/* Note: We do not free memory for RISC programs, grab buffer, vbi buffers */
/* The memory is retained by the bktr_mem module so we can unload and */
/* then reload the main bktr driver module */
/* Unregister the /dev/bktrN, tunerN and vbiN devices */
destroy_dev(bktr->vbidev);
destroy_dev(bktr->tunerdev);
destroy_dev(bktr->bktrdev);
/* If this is unit 0, then destroy the alias entries too */
#if (__FreeBSD_version >=500000)
if (unit == 0) {
destroy_dev(bktr->vbidev_alias);
destroy_dev(bktr->tunerdev_alias);
destroy_dev(bktr->bktrdev_alias);
}
#endif
seldestroy(&bktr->vbi_select);
/*
*
*/
int
bktr_open(dev_t dev, int flags, int fmt, struct lwp *l)
{
bktr_ptr_t bktr;
int unit;
int result;
unit = UNIT(minor(dev));
/* Get the device data */
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
/* the device is no longer valid/functioning */
return (ENXIO);
}
if (!(bktr->flags & METEOR_INITIALIZED)) /* device not found */
return(ENXIO);
/* Record that the device is now busy */
device_busy(devclass_get_device(bktr_devclass, unit));
if (bt848_reverse_mute != -1) {
if ((bt848_reverse_mute >> 8) == unit) {
bktr->reverse_mute = bt848_reverse_mute & 0xff;
}
}
if (bt848_slow_msp_audio != -1) {
if ((bt848_slow_msp_audio >> 8) == unit) {
bktr->slow_msp_audio = (bt848_slow_msp_audio & 0xff);
}
}
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
result = video_open(bktr);
break;
case TUNER_DEV:
result = tuner_open(bktr);
break;
case VBI_DEV:
result = vbi_open(bktr);
break;
default:
result = ENXIO;
break;
}
/* If there was an error opening the device, undo the busy status */
if (result != 0)
device_unbusy(devclass_get_device(bktr_devclass, unit));
return(result);
}
/*
*
*/
int
bktr_close(dev_t dev, int flags, int fmt, struct lwp *l)
{
bktr_ptr_t bktr;
int unit;
int result;
unit = UNIT(minor(dev));
/* Get the device data */
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
/* the device is no longer valid/functioning */
return (ENXIO);
}
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
result = video_close(bktr);
break;
case TUNER_DEV:
result = tuner_close(bktr);
break;
case VBI_DEV:
result = vbi_close(bktr);
break;
default:
return (ENXIO);
break;
}
/*
*
*/
int
bktr_read(dev_t dev, struct uio *uio, int ioflag)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
/* Get the device data */
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
/* the device is no longer valid/functioning */
return (ENXIO);
}
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
return(video_read(bktr, unit, dev, uio));
case VBI_DEV:
return(vbi_read(bktr, uio, ioflag));
}
return(ENXIO);
}
/*
*
*/
int
bktr_write(dev_t dev, struct uio *uio, int ioflag)
{
return(EINVAL); /* XXX or ENXIO ? */
}
/*
*
*/
int
bktr_ioctl(dev_t dev, ioctl_cmd_t cmd, void *arg, int flag, struct proc* pr)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
/* Get the device data */
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
/* the device is no longer valid/functioning */
return (ENXIO);
}
if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */
return(ENOMEM);
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
return(video_ioctl(bktr, unit, cmd, arg, pr));
case TUNER_DEV:
return(tuner_ioctl(bktr, unit, cmd, arg, pr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_mmap(dev_t dev, vm_offset_t offset, int nprot)
{
int unit;
bktr_ptr_t bktr;
unit = UNIT(minor(dev));
if (FUNCTION(minor(dev)) > 0) /* only allow mmap on /dev/bktr[n] */
return(-1);
/* Get the device data */
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
/* the device is no longer valid/functioning */
return (-1);
}
if (nprot & PROT_EXEC)
return(-1);
if (offset < 0)
return(-1);
if (offset >= bktr->alloc_pages * PAGE_SIZE)
return(-1);
return(atop(vtophys(bktr->bigbuf) + offset));
}
int bktr_poll(dev_t dev, int events, struct lwp *l)
{
int unit;
bktr_ptr_t bktr;
int revents = 0;
DECLARE_INTR_MASK(s);
unit = UNIT(minor(dev));
/* Get the device data */
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
/* the device is no longer valid/functioning */
return (ENXIO);
}
/*
* the boot time probe routine.
*/
static const char*
bktr_probe(pcici_t tag, pcidi_t type)
{
unsigned int rev = pci_conf_read(tag, PCIR_REVID) & 0x000000ff;
if (PCI_VENDOR(type) == PCI_VENDOR_BROOKTREE)
{
switch (PCI_PRODUCT(type)) {
case PCI_PRODUCT_BROOKTREE_BT848:
if (rev == 0x12) return("BrookTree 848A");
else return("BrookTree 848");
case PCI_PRODUCT_BROOKTREE_BT849:
return("BrookTree 849A");
case PCI_PRODUCT_BROOKTREE_BT878:
return("BrookTree 878");
case PCI_PRODUCT_BROOKTREE_BT879:
return("BrookTree 879");
}
};
return ((char *)0);
}
/*
* the attach routine.
*/
static void
bktr_attach(pcici_t tag, int unit)
{
bktr_ptr_t bktr;
u_int latency;
u_int fun;
unsigned int rev;
unsigned long base;
#ifdef BROOKTREE_IRQ
u_int old_irq, new_irq;
#endif
bktr = &brooktree[unit];
if (unit >= NBKTR) {
printf("brooktree%d: attach: only %d units configured.\n",
unit, NBKTR);
printf("brooktree%d: attach: invalid unit number.\n", unit);
return;
}
/* build the device name for bktr_name() */
snprintf(bktr->bktr_xname, sizeof(bktr->bktr_xname), "bktr%d",unit);
/* Enable Memory Mapping */
fun = pci_conf_read(tag, PCI_COMMAND_STATUS_REG);
pci_conf_write(tag, PCI_COMMAND_STATUS_REG, fun | 2);
/* Enable Bus Mastering */
fun = pci_conf_read(tag, PCI_COMMAND_STATUS_REG);
pci_conf_write(tag, PCI_COMMAND_STATUS_REG, fun | 4);
bktr->tag = tag;
/*
* Map control/status registers
*/
pci_map_mem(tag, PCI_MAP_REG_START, (vm_offset_t *) &base,
&bktr->phys_base);
#if (__FreeBSD_version >= 300000)
bktr->memt = I386_BUS_SPACE_MEM; /* XXX should use proper bus space */
bktr->memh = (bus_space_handle_t)base; /* XXX functions here */
#endif
/* Update the Device Control Register */
/* on Bt878 and Bt879 cards */
fun = pci_conf_read(tag, 0x40);
fun = fun | 1; /* Enable writes to the sub-system vendor ID */
#if defined(BKTR_430_FX_MODE)
if (bootverbose) printf("Using 430 FX chipset compatibility mode\n");
fun = fun | 2; /* Enable Intel 430 FX compatibility mode */
#endif
#if defined(BKTR_SIS_VIA_MODE)
if (bootverbose) printf("Using SiS/VIA chipset compatibility mode\n");
fun = fun | 4; /* Enable SiS/VIA compatibility mode (useful for
OPTi chipset motherboards too */
#endif
pci_conf_write(tag, 0x40, fun);
/*
* PCI latency timer. 32 is a good value for 4 bus mastering slots, if
* you have more than four, then 16 would probably be a better value.
*/
#ifndef BROOKTREE_DEF_LATENCY_VALUE
#define BROOKTREE_DEF_LATENCY_VALUE 10
#endif
latency = pci_conf_read(tag, PCI_LATENCY_TIMER);
latency = (latency >> 8) & 0xff;
if (bootverbose) {
if (latency)
printf("brooktree%d: PCI bus latency is", unit);
else
printf("brooktree%d: PCI bus latency was 0 changing to",
unit);
}
if (!latency) {
latency = BROOKTREE_DEF_LATENCY_VALUE;
pci_conf_write(tag, PCI_LATENCY_TIMER, latency<<8);
}
if (bootverbose) {
printf(" %d.\n", (int) latency);
}
/* read the pci device id and revision id */
fun = pci_conf_read(tag, PCI_ID_REG);
rev = pci_conf_read(tag, PCIR_REVID) & 0x000000ff;
/* call the common attach code */
common_bktr_attach(bktr, unit, fun, rev);
}
/*
* Special Memory Allocation
*/
vm_offset_t
get_bktr_mem(int unit, unsigned size)
{
vm_offset_t addr = 0;
addr = vm_page_alloc_contig(size, 0x100000, 0xffffffff, 1<<24);
if (addr == 0)
addr = vm_page_alloc_contig(size, 0x100000, 0xffffffff,
PAGE_SIZE);
if (addr == 0) {
printf("bktr%d: Unable to allocate %d bytes of memory.\n",
unit, size);
}
if (bt848_reverse_mute != -1) {
if ((bt848_reverse_mute >> 8) == unit) {
bktr->reverse_mute = bt848_reverse_mute & 0xff;
}
}
if (bt848_slow_msp_audio != -1) {
if ((bt848_slow_msp_audio >> 8) == unit) {
bktr->slow_msp_audio = (bt848_slow_msp_audio & 0xff);
}
}
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
return(video_open(bktr));
case TUNER_DEV:
return(tuner_open(bktr));
case VBI_DEV:
return(vbi_open(bktr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_close(dev_t dev, int flags, int fmt, struct lwp *l)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
if (unit >= NBKTR) /* unit out of range */
return(ENXIO);
bktr = &(brooktree[unit]);
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
return(video_close(bktr));
case TUNER_DEV:
return(tuner_close(bktr));
case VBI_DEV:
return(vbi_close(bktr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_read(dev_t dev, struct uio *uio, int ioflag)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
if (unit >= NBKTR) /* unit out of range */
return(ENXIO);
bktr = &(brooktree[unit]);
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
return(video_read(bktr, unit, dev, uio));
case VBI_DEV:
return(vbi_read(bktr, uio, ioflag));
}
return(ENXIO);
}
/*
*
*/
int
bktr_write(dev_t dev, struct uio *uio, int ioflag)
{
return(EINVAL); /* XXX or ENXIO ? */
}
/*
*
*/
int
bktr_ioctl(dev_t dev, ioctl_cmd_t cmd, void *arg, int flag, struct proc* pr)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
if (unit >= NBKTR) /* unit out of range */
return(ENXIO);
bktr = &(brooktree[unit]);
if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */
return(ENOMEM);
switch (FUNCTION(minor(dev))) {
case VIDEO_DEV:
return(video_ioctl(bktr, unit, cmd, arg, pr));
case TUNER_DEV:
return(tuner_ioctl(bktr, unit, cmd, arg, pr));
}
return(ENXIO);
}
/*
* bktr_mmap.
* Note: 2.2.5/2.2.6/2.2.7/3.0 users must manually
* edit the line below and change "vm_offset_t" to "int"
*/
int bktr_mmap(dev_t dev, vm_offset_t offset, int nprot)
{
int unit;
bktr_ptr_t bktr;
unit = UNIT(minor(dev));
if (unit >= NBKTR || FUNCTION(minor(dev)) > 0)
return(-1);
bktr = &(brooktree[unit]);
if (nprot & PROT_EXEC)
return(-1);
if (offset < 0)
return(-1);
if (offset >= bktr->alloc_pages * PAGE_SIZE)
return(-1);
/* Enable Bus Master
XXX: check if all old DMA is stopped first (e.g. after warm
boot) */
command = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
command |= PCI_COMMAND_MASTER_ENABLE;
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command);
/*
* PCI latency timer. 32 is a good value for 4 bus mastering slots, if
* you have more than four, then 16 would probably be a better value.
*/
#ifndef BROOKTREE_DEF_LATENCY_VALUE
#define BROOKTREE_DEF_LATENCY_VALUE 0x10
#endif
latency = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_LATENCY_TIMER);
latency = (latency >> 8) & 0xff;
if (!latency) {
if (bootverbose) {
printf("%s: PCI bus latency was 0 changing to %d",
bktr_name(bktr), BROOKTREE_DEF_LATENCY_VALUE);
}
latency = BROOKTREE_DEF_LATENCY_VALUE;
pci_conf_write(pa->pa_pc, pa->pa_tag,
PCI_LATENCY_TIMER, latency<<8);
}
if (common_bktr_attach(bktr, unit, pa->pa_id,
PCI_REVISION(pa->pa_class)) == 0)
return;
#if NRADIO > 0
/* attach to radio(4) */
if (bktr->card.tuner->pllControl[3] != 0x00)
radio_attach_mi(&bktr_hw_if, bktr, bktr->bktr_dev);
#endif
}
/*
* Special Memory Allocation
*/
#if defined (__NetBSD__)
vaddr_t
#else
vm_offset_t
#endif
get_bktr_mem(bktr_ptr_t bktr, bus_dmamap_t *dmapp, unsigned int size)
{
bus_dma_tag_t dmat = bktr->dmat;
bus_dma_segment_t seg;
bus_size_t align;
int rseg;
void *kva;
/*
* Allocate a DMA area
*/
align = 1 << 24;
if (bus_dmamem_alloc(dmat, size, align, 0, &seg, 1,
&rseg, BUS_DMA_NOWAIT)) {
align = PAGE_SIZE;
if (bus_dmamem_alloc(dmat, size, align, 0, &seg, 1,
&rseg, BUS_DMA_NOWAIT)) {
printf("%s: Unable to dmamem_alloc of %d bytes\n",
bktr_name(bktr), size);
return 0;
}
}
if (bus_dmamem_map(dmat, &seg, rseg, size,
&kva, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) {
printf("%s: Unable to dmamem_map of %d bytes\n",
bktr_name(bktr), size);
bus_dmamem_free(dmat, &seg, rseg);
return 0;
}
#ifdef __OpenBSD__
bktr->dm_mapsize = size;
#endif
/*
* Create and locd the DMA map for the DMA area
*/
if (bus_dmamap_create(dmat, size, 1, size, 0, BUS_DMA_NOWAIT, dmapp)) {
printf("%s: Unable to dmamap_create of %d bytes\n",
bktr_name(bktr), size);
bus_dmamem_unmap(dmat, kva, size);
bus_dmamem_free(dmat, &seg, rseg);
return 0;
}
if (bus_dmamap_load(dmat, *dmapp, kva, size, NULL, BUS_DMA_NOWAIT)) {
printf("%s: Unable to dmamap_load of %d bytes\n",
bktr_name(bktr), size);
bus_dmamem_unmap(dmat, kva, size);
bus_dmamem_free(dmat, &seg, rseg);
bus_dmamap_destroy(dmat, *dmapp);
return 0;
}
#if defined(__NetBSD__)
return (vaddr_t)kva;
#else
return (vm_offset_t)kva;
#endif
}
/*
*
*/
int
bktr_open(dev_t dev, int flags, int fmt,
struct lwp *l)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
/* unit out of range */
bktr = device_lookup_private(&bktr_cd, unit);
if (bktr == NULL)
return(ENXIO);
if (!(bktr->flags & METEOR_INITIALIZED)) /* device not found */
return(ENXIO);
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_open(bktr));
case TUNER_DEV:
return(tuner_open(bktr));
case VBI_DEV:
return(vbi_open(bktr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_close(dev_t dev, int flags, int fmt,
struct lwp *l)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
bktr = device_lookup_private(&bktr_cd, unit);
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_close(bktr));
case TUNER_DEV:
return(tuner_close(bktr));
case VBI_DEV:
return(vbi_close(bktr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_read(dev_t dev, struct uio *uio, int ioflag)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
bktr = device_lookup_private(&bktr_cd, unit);
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_read(bktr, unit, dev, uio));
case VBI_DEV:
return(vbi_read(bktr, uio, ioflag));
}
return(ENXIO);
}
/*
*
*/
int
bktr_write(dev_t dev, struct uio *uio, int ioflag)
{
/* operation not supported */
return(EOPNOTSUPP);
}
/*
*
*/
int
bktr_ioctl(dev_t dev, ioctl_cmd_t cmd, void *arg, int flag,
struct lwp *l)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
bktr = device_lookup_private(&bktr_cd, unit);
if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */
return(ENOMEM);
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_ioctl(bktr, unit, cmd, arg, l));
case TUNER_DEV:
return(tuner_ioctl(bktr, unit, cmd, arg, l));
}
return(ENXIO);
}
/*
*
*/
paddr_t
bktr_mmap(dev_t dev, off_t offset, int nprot)
{
int unit;
bktr_ptr_t bktr;
unit = UNIT(dev);
if (FUNCTION(dev) > 0) /* only allow mmap on /dev/bktr[n] */
return(-1);
bktr = device_lookup_private(&bktr_cd, unit);
if ((vaddr_t)offset >= bktr->alloc_pages * PAGE_SIZE)
return(-1);
if (ri->mute) {
/* mute the audio stream by switching the mux */
set_audio(sc, AUDIO_MUTE);
/* disable drivers on the GPIO port that controls the MUXes */
OUTL(sc, BKTR_GPIO_OUT_EN, INL(sc, BKTR_GPIO_OUT_EN) &
~sc->card.gpio_mux_bits);
} else {
/* enable drivers on the GPIO port that controls the MUXes */
OUTL(sc, BKTR_GPIO_OUT_EN, INL(sc, BKTR_GPIO_OUT_EN) |
sc->card.gpio_mux_bits);
/* unmute the audio stream */
set_audio(sc, AUDIO_UNMUTE);
init_audio_devices(sc);
}