/* $ZEL: sis1100_ioctl.c,v 1.5 2004/05/27 23:10:22 wuestner Exp $ */

/*
 * Copyright (c) 2001-2004
 * 	Matthias Drochner, Peter Wuestner.  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 AUTHORS 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 "sis1100_sc.h"

#if !defined(__NetBSD__) && ! defined(__linux__)
#error Invalid or Unknown Operating System
#endif

static int
test_super(struct sis1100_softc* sc, struct sis1100_fdata* fd)
{
return 0;
#ifdef __NetBSD__
    if (suser(fd->p->p_ucred, &fd->p->p_acflag)) return EPERM;
#elif __linux__
    if (!capable(CAP_SYS_RAWIO)) return EPERM;
#endif
    return 0;
}

#ifdef SIS1100_NEW_CTRL
static int
ioctl_ctrl_read(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_ctrl_reg* d)
{
    if (fd->subdev==sis1100_subdev_ctrl) {
        SEM_LOCK(sc->sem_hw);
        d->val=plxreadlocal0(sc, d->offset&0x7ff);
        SEM_UNLOCK(sc->sem_hw);
        d->error=0;
    } else {
        if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
        d->error=sis1100_remote_reg_read(sc, d->offset, &d->val, 1);
    }
    return 0;
}

static int
ioctl_ctrl_write(struct sis1100_softc* sc, struct sis1100_fdata* fd, 
    struct sis1100_ctrl_reg* d)
{
    if (fd->subdev==sis1100_subdev_ctrl) {
        SEM_LOCK(sc->sem_hw);
        plxwritelocal0(sc, d->offset&0x7ff, d->val);
        SEM_UNLOCK(sc->sem_hw);
        d->error=0;
    } else {
        if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
        d->error=sis1100_remote_reg_write(sc, d->offset, d->val, 0);
    }
    return 0;
}

#else

static int
ioctl_local_ctrl_read(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_ctrl_reg* d)
{
    SEM_LOCK(sc->sem_hw);
    d->val=plxreadlocal0(sc, d->offset&0x7ff);
    SEM_UNLOCK(sc->sem_hw);
    d->error=0;
    return 0;
}

static int
ioctl_local_ctrl_write(struct sis1100_softc* sc, struct sis1100_fdata* fd, 
    struct sis1100_ctrl_reg* d)
{
    SEM_LOCK(sc->sem_hw);
    plxwritelocal0(sc, d->offset&0x7ff, d->val);
    SEM_UNLOCK(sc->sem_hw);
    d->error=0;
    return 0;
}

static int
ioctl_remote_ctrl_read(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_ctrl_reg* d)
{
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
    d->error=sis1100_remote_reg_read(sc, d->offset, &d->val, 0);
    return 0;
}

static int
ioctl_remote_ctrl_write(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_ctrl_reg* d)
{
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
    d->error=sis1100_remote_reg_write(sc, d->offset, d->val, 0);
    return 0;
}
#endif

static int
ioctl_ident(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_ident* d)
{
    d->local.hw_type=sc->local_ident&0xff;
    d->local.hw_version=(sc->local_ident>>8)&0xff;
    d->local.fw_type=(sc->local_ident>>16)&0xff;
    d->local.fw_version=(sc->local_ident>>24)&0xff;

    d->remote.hw_type=sc->remote_ident&0xff;
    d->remote.hw_version=(sc->remote_ident>>8)&0xff;
    d->remote.fw_type=(sc->remote_ident>>16)&0xff;
    d->remote.fw_version=(sc->remote_ident>>24)&0xff;

    d->remote_ok=sc->remote_hw!=sis1100_hw_invalid;
    d->remote_online=(sis1100readreg(sc, sr)&sr_synch)==sr_synch;
    return 0;
}

static int
ioctl_remote_reset(struct sis1100_softc* sc, struct sis1100_fdata* fd)
{
#if 0
    SEM_LOCK(sc->sem_hw);
    sis1100writereg(sc, cr, cr_rem_reset);
    switch (sc->remote_hw) {
    case sis1100_hw_invalid:
        SEM_UNLOCK(sc->sem_hw);
        return ENXIO;
    case sis1100_hw_pci:
        /* do nothing */
        break;
    case sis1100_hw_vme:
        sis3100writeremreg(sc, vme_master_sc, 8, 1);
        break;
    case sis1100_hw_camac:
        break;
    }
    SEM_UNLOCK(sc->sem_hw);
    mdelay(500);
    sis1100_init_remote(sc);
#endif
    return ENOTTY;
}

static int
ioctl_devtype(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
    *d=fd->subdev;
    return 0;
}

static int
ioctl_driverversion(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
    *d=SIS1100_Version;
    return 0;
}

static int
ioctl_mindmalen(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    int* d)
{
/*
 *   0: never use DMA
 *   1: always use DMA (if size>4)
 *  >1: use DMA if transfersize (in Bytes) is >= mindmalen
 *  -1: don't change old value
 */
    int tmp[2];

    tmp[0]=fd->mindmalen_r;
    tmp[1]=fd->mindmalen_w;
    if (d[0]>=0) fd->mindmalen_r=d[0];
    if (d[1]>=0) fd->mindmalen_w=d[1];
    d[0]=tmp[0];
    d[1]=tmp[1];
    return 0;
}

static int
ioctl_setvmespace(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct vmespace* d)
{
    if ((d->datasize!=1) && (d->datasize!=2) && (d->datasize!=4))
        return EINVAL;
    fd->vmespace_am=d->am;
    fd->vmespace_datasize=d->datasize;
    if (d->swap>=0) {
        sc->user_wants_swap=d->swap;
        sis1100_update_swapping(sc, "ioctl_setvmespace");
    }
    if (d->mindmalen>=0) {
        fd->mindmalen_r=d->mindmalen;
        fd->mindmalen_w=d->mindmalen;
    }
    return 0;
}

static int
ioctl_swap(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
    int old;

    old=sc->user_wants_swap;
    sc->user_wants_swap=*d;
    sis1100_update_swapping(sc, "ioctl_swap");
    *d=old;
    return 0;
}

static int
ioctl_3100_timeouts(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
/*
 *  d[0]: bus error terms of 10**-9 s
 *  d[1]: arbitration timeout in terms of 10**-3 s
 */
    int tmp[2];
    if ((fd->subdev!=sis1100_subdev_remote)
        || (sc->remote_hw!=sis1100_hw_vme))
                return ENXIO;
    if (sis3100_get_timeouts(sc, tmp+0, tmp+1)) return EIO;
    if (sis3100_set_timeouts(sc, d[0], d[1])) return EIO;
    d[0]=tmp[0];
    d[1]=tmp[1];
    return 0;
}

static int
ioctl_front_io(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    u_int32_t* d)
{
    return sis1100_front_io(sc, d, 0);
}

static int
ioctl_front_pulse(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    u_int32_t* d)
{
    return sis1100_front_pulse(sc, d, 0);
}

static int
ioctl_front_latch(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    u_int32_t* d)
{
    return sis1100_front_latch(sc, d, 0);
}

static int
ioctl_last_error(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    u_int32_t* d)
{
    *d=fd->last_prot_err;
    return 0;
}

static int
ioctl_mapsize(struct sis1100_softc* sc, struct sis1100_fdata* fd, u_int32_t* d)
{
    switch (fd->subdev) {
        case sis1100_subdev_remote:
	    *d=sc->rem_size;
	    break;
	case sis1100_subdev_ram:
	    *d=sc->ram_size;
	    break;
	case sis1100_subdev_ctrl:
	    *d=sc->reg_size;
	    break;
	case sis1100_subdev_dsp:
	    *d=0;
	    break;
	default:
	    return EINVAL;
    }
    return 0;
}

static int
ioctl_pipe(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_pipe* d)
{
    int res;
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
    res=sis1100_read_pipe(sc, d);
    return res;
}

static int
ioctl_write_pipe(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_writepipe* d)
{
    u_int32_t* list;
    int res;

    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;

#ifdef __NetBSD__
    list=malloc(d->num*2*sizeof(u_int32_t), M_IOCTLOPS,
            M_WAITOK/*|M_CANFAIL*/);
#elif __linux__
    list=kmalloc(d->num*2*sizeof(u_int32_t), GFP_KERNEL);
#endif
    if (!list) return ENOMEM;

    if (
#ifdef __NetBSD__
        copyin(d->data, list, d->num*2*sizeof(u_int32_t))
#elif __linux__
        copy_from_user(list, d->data, d->num*2*sizeof(u_int32_t))
#endif
        ) {
	res=EFAULT;
        goto raus;
    }

    res=0;
    d->error=sis1100_write_pipe(sc, d->am, 1/*space*/, d->num, list);

    raus:
#ifdef __NetBSD__
    free(list, M_IOCTLOPS);
#elif __linux__
    kfree(list);
#endif
    return res;
}

static int
ioctl_vme_probe(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
    int dummy;
    if (sc->remote_hw!=sis1100_hw_vme) return ENXIO;
    if (sis1100_tmp_read(sc, *d, fd->vmespace_am,
	    fd->vmespace_datasize, 1/*space*/, &dummy))
	return EIO;
    return 0;
}

static int
ioctl_vme_read(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        struct sis1100_vme_req* d)
{
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
    d->error=sis1100_tmp_read(sc, d->addr, d->am, d->size,
	    1/*space*/, &d->data);
    return 0;
}

static int
ioctl_vme_write(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_vme_req* d)
{
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
    d->error=sis1100_tmp_write(sc, d->addr, d->am, d->size,
	    1/*space*/, d->data);
    return 0;
}

static int
ioctl_vme_block_read(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_vme_block_req* d)
{
    int res;
    int space=1;
#if 0
    switch (sc->remote_hw) {
    case sis1100_hw_invalid: return ENXIO;
    case sis1100_hw_pci: space=1; break;
    case sis1100_hw_vme: space=1; break;
    case sis1100_hw_camac: space=1; break;
    case sis1100_hw_f1: space=1; break;
    case sis1100_hw_vertex: space=1; break;
    }
#else
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
#endif
    res=sis1100_read_block(sc, fd, d->size, d->fifo, d->num, &d->num,
        space, d->am, d->addr, d->data, &d->error);
    return res;
}

static int
ioctl_vme_super_block_read(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_vme_super_block_req* d)
{
    struct sis1100_vme_block_req* reqs;
    int res, i;

    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
#ifdef __NetBSD__
    reqs=malloc(d->n*sizeof(struct sis1100_vme_block_req), M_IOCTLOPS,
            M_WAITOK/*|M_CANFAIL*/);
#elif __linux__
    reqs=kmalloc(d->n*sizeof(struct sis1100_vme_block_req), GFP_KERNEL);
#endif
    if (!reqs) return ENOMEM;

    if (
#ifdef __NetBSD__
        copyin(d->reqs, reqs, d->n*sizeof(struct sis1100_vme_block_req))
#elif __linux__
        copy_from_user(reqs, d->reqs,
            d->n*sizeof(struct sis1100_vme_block_req))
#endif
        ) {
	res=EFAULT;
        goto raus;
    }

    d->error=0;
    for (i=0; i<d->n; i++) {
        struct sis1100_vme_block_req* r=reqs+i;
	res=sis1100_read_block(sc, fd, r->size, r->fifo, r->num,
        &r->num, 1/*space*/, r->am, r->addr, r->data, &r->error);
	if (res) {
            d->n=i;
            d->error=res;
            break;
        }
    }
    res=0;
    if (
#ifdef __NetBSD__
        copyout(reqs, d->reqs, d->n*sizeof(struct sis1100_vme_block_req))
#elif __linux__
        copy_to_user(d->reqs, reqs,
            d->n*sizeof(struct sis1100_vme_block_req))
#endif
        ) res=EFAULT;

    raus:
#ifdef __NetBSD__
    free(reqs, M_IOCTLOPS);
#elif __linux__
    kfree(reqs);
#endif
    return res;
}

static int
ioctl_vme_block_write(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_vme_block_req* d)
{
    int res;
 
    if (sc->remote_hw==sis1100_hw_invalid) return ENXIO;
    res=sis1100_write_block(sc, fd, d->size, d->fifo, d->num,
        &d->num, 1/*space*/, d->am, d->addr, d->data, &d->error);
    return res;
}

static int
ioctl_fifomode(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
    int tmp;
    tmp=fd->fifo_mode;
    if (*d>=0) fd->fifo_mode=!!*d;
    *d=tmp;
    return 0;
}

static int
ioctl_irq_ctl(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_irq_ctl* d)
{
    return sis1100_irq_ctl(sc, fd, d);
}

static int
ioctl_irq_get(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_irq_get* d)
{
    return sis1100_irq_get(sc, fd, d);
}

static int
ioctl_irq_ack(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_irq_ack* d)
{
    return sis1100_irq_ack(sc, fd, d);
}

static int
ioctl_irq_wait(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_irq_get* d)
{
    return sis1100_irq_wait(sc, fd, d);
}

static int
ioctl_dma_alloc(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_dma_alloc* d)
{
#ifdef __NetBSD__
    return sis1100_dma_alloc(sc, fd, d);
#elif __linux__
    return ENOTTY;
#endif
}

static int
ioctl_dma_free(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_dma_alloc* d)
{
    /*return sis1100_dma_free(sc, fd, d);*/
    return ENOTTY;
}
#if 0
static int
ioctl_reset(struct sis1100_softc* sc, struct sis1100_fdata* fd)
{
    return sis1100_reset(sc);
}
#endif

static int
ioctl_cccz(struct sis1100_softc* sc, struct sis1100_fdata* fd)
{
    u_int32_t data;
    int res;
    return 0;

    if (sc->remote_hw!=sis1100_hw_camac) return ENXIO;
    res=sis1100_tmp_camacread(sc, SIS5100_CAMACaddr(28, 8, 26), &data);
pINFO(sc, "CCCZ: data=0x%x res=0x%x", data, res);
    return 0;
}

static int
ioctl_cccc(struct sis1100_softc* sc, struct sis1100_fdata* fd)
{
    u_int32_t data;
    int res;
    return 0;

    if (sc->remote_hw!=sis1100_hw_camac) return ENXIO;
    res=sis1100_tmp_camacread(sc, SIS5100_CAMACaddr(28, 9, 26), &data);
pINFO(sc, "CCCC: data=0x%x res=0x%x", data, res);
    return 0;
}

static int
ioctl_ccci(struct sis1100_softc* sc, struct sis1100_fdata* fd, int* d)
{
    u_int32_t data;
    int res;

    if (sc->remote_hw!=sis1100_hw_camac) return ENXIO;
    res=sis5100writeremreg(sc, camac_sc, *d?1:0x1000, 0);

    /*res=sis1100_tmp_camacread(sc, SIS5100_CAMACaddr(30, 9, *d?26:24), &data);*/
pINFO(sc, "CCCI(%d): res=0x%x", *d, res);
    res=sis5100readremreg(sc, camac_sc, &data, 0);
pINFO(sc, "CCCI    : data=0x%x, res=0x%x", data, res);
    return 0;
}

static int
ioctl_cnaf(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    struct sis1100_camac_req* d)
{
    u_int32_t addr;
    int res;

    addr=SIS5100_CAMACaddr(d->N, d->A, d->F);
    if (d->F>15) { /* write */
        res=sis1100_tmp_camacwrite(sc, addr, d->data);
        d->data=(~res<<24)&0xc0000000;
        if ((res&~0x2c0)==0) { /* X or Q missing; not an error */
	  /* res=0; */ /* mki 29.06.04 */
        }
    } else { /* read or control */
        res=sis1100_tmp_camacread(sc, addr, &d->data);
        d->data^=0xc0000000;
    }
    d->error=res;
    return 0;
}

static int
ioctl_read_eeprom(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        struct sis1100_eeprom_req* d)
{
    return sis1100_read_eeprom(sc, d->num, d->addr, d->data);
}

static int
ioctl_write_eeprom(struct sis1100_softc* sc, struct sis1100_fdata* fd, 
        struct sis1100_eeprom_req* d)
{
    if (test_super(sc, fd)) return EPERM;
    return sis1100_write_eeprom(sc, d->num, d->addr, d->data);
}

static int
ioctl_jtag_enable(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        u_int32_t* d)
{
    if (test_super(sc, fd)) return EPERM;
    *d<<=8;
    sis1100writereg(sc, jtag_csr.jtag_csrl, *d);
    return 0;
}

static int
ioctl_jtag_ctrl(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        u_int32_t* d)
{
    if (test_super(sc, fd)) return EPERM;
    sis1100writereg(sc, jtag_csr.jtag_csrb[1], *d);
    *d=sis1100readreg(sc, jtag_csr.jtag_csrl);
    return 0;
}

static int
ioctl_jtag_data(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        u_int32_t* d)
{
    *d=sis1100readreg(sc, jtag_data);
    return 0;
}

static int
ioctl_jtag_put(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        u_int32_t* d)
{
    if (test_super(sc, fd)) return EPERM;
    sis1100writereg(sc, jtag_csr.jtag_csrl, *d);
    return 0;
}

static int
ioctl_jtag_get(struct sis1100_softc* sc, struct sis1100_fdata* fd,
        u_int32_t* d)
{
    *d=sis1100readreg(sc, jtag_csr.jtag_csrl);
    return 0;
}

static int
_sis1100_ioctl(struct sis1100_softc* sc, struct sis1100_fdata* fd,
    unsigned int cmd, void* data)
{
    int res=0;

    switch (cmd) {
    case SIS1100_SETVMESPACE:
        res=ioctl_setvmespace(sc, fd, (struct vmespace*)data); break;
    case SIS3100_VME_PROBE:
        res=ioctl_vme_probe(sc, fd, (int*)data); break;
    case SIS3100_VME_READ:
        res=ioctl_vme_read(sc, fd, (struct sis1100_vme_req*)data); break;
    case SIS3100_VME_WRITE:
        res=ioctl_vme_write(sc, fd, (struct sis1100_vme_req*)data); break;
    case SIS3100_VME_BLOCK_READ:
        res=ioctl_vme_block_read(sc, fd, (struct sis1100_vme_block_req*)data);
        break;
    case SIS3100_VME_BLOCK_WRITE:
        res=ioctl_vme_block_write(sc, fd, (struct sis1100_vme_block_req*)data);
        break;
#ifdef SIS1100_NEW_CTRL
    case SIS1100_CTRL_READ:
        res=ioctl_ctrl_read(sc, fd, (struct sis1100_ctrl_reg*)data);
        break;
    case SIS1100_CTRL_WRITE:
        res=ioctl_ctrl_write(sc, fd, (struct sis1100_ctrl_reg*)data);
        break;
#else
    case SIS1100_LOCAL_CTRL_READ:
        res=ioctl_local_ctrl_read(sc, fd, (struct sis1100_ctrl_reg*)data);
        break;
    case SIS1100_LOCAL_CTRL_WRITE:
        res=ioctl_local_ctrl_write(sc, fd, (struct sis1100_ctrl_reg*)data);
        break;
    case SIS1100_REMOTE_CTRL_READ:
        res=ioctl_remote_ctrl_read(sc, fd, (struct sis1100_ctrl_reg*)data);
        break;
    case SIS1100_REMOTE_CTRL_WRITE:
        res=ioctl_remote_ctrl_write(sc, fd, (struct sis1100_ctrl_reg*)data);
        break;
#endif
    case SIS1100_PIPE:
        res=ioctl_pipe(sc, fd, (struct sis1100_pipe*)data); break;
    case SIS1100_MAPSIZE:
        res=ioctl_mapsize(sc, fd, (u_int32_t*)data); break;
    case SIS1100_LAST_ERROR:
        res=ioctl_last_error(sc, fd, (u_int32_t*)data); break;
    case SIS1100_IDENT:
        res=ioctl_ident(sc, fd, (struct sis1100_ident*)data); break;
    case SIS1100_FIFOMODE:
        res=ioctl_fifomode(sc, fd, (int*)data); break;

    case SIS1100_IRQ_CTL:
        res=ioctl_irq_ctl(sc, fd, (struct sis1100_irq_ctl*)data); break;
    case SIS1100_IRQ_GET:
        res=ioctl_irq_get(sc, fd, (struct sis1100_irq_get*)data); break;
    case SIS1100_IRQ_ACK:
        res=ioctl_irq_ack(sc, fd, (struct sis1100_irq_ack*)data); break;
    case SIS1100_IRQ_WAIT:
        res=ioctl_irq_wait(sc, fd, (struct sis1100_irq_get*)data); break;

    case SIS1100_MINDMALEN:
        res=ioctl_mindmalen(sc, fd, (int*)data); break;

    case SIS1100_FRONT_IO:
        res=ioctl_front_io(sc, fd, (u_int32_t*)data); break;
    case SIS1100_FRONT_PULSE:
        res=ioctl_front_pulse(sc, fd, (u_int32_t*)data); break;
    case SIS1100_FRONT_LATCH:
        res=ioctl_front_latch(sc, fd, (u_int32_t*)data); break;

    case SIS3100_VME_SUPER_BLOCK_READ:
        res=ioctl_vme_super_block_read(sc, fd,
                (struct sis1100_vme_super_block_req*)data);
        break;
    case SIS1100_WRITE_PIPE:
        res=ioctl_write_pipe(sc, fd, (struct sis1100_writepipe*)data); break;

    case SIS1100_DMA_ALLOC:
        res=ioctl_dma_alloc(sc, fd, (struct sis1100_dma_alloc*)data); break;
    case SIS1100_DMA_FREE:
        res=ioctl_dma_free(sc, fd, (struct sis1100_dma_alloc*)data); break;

    case SIS5100_CCCZ:
        res=ioctl_cccz(sc, fd); break;
    case SIS5100_CCCC:
        res=ioctl_cccc(sc, fd); break;
    case SIS5100_CCCI:
        res=ioctl_ccci(sc, fd, (int*)data); break;
    case SIS5100_CNAF:
        res=ioctl_cnaf(sc, fd, (struct sis1100_camac_req*)data); break;
    case SIS1100_SWAP:
        res=ioctl_swap(sc, fd, (int*)data); break;
    case SIS3100_TIMEOUTS:
        res=ioctl_3100_timeouts(sc, fd, (int*)data); break;

    case SIS1100_DSP_LOAD:
        res=sis1100_dsp_load(sc, fd, (struct sis1100_dsp_code*)data); break;
    case SIS1100_DSP_RESET:
        res=sis1100_dsp_reset(sc, fd); break;
    case SIS1100_DSP_START:
        res=sis1100_dsp_start(sc, fd); break;

#if 0
    case SIS1100_RESET:
        res=ioctl_reset(sc, fd); break;
#endif
    case SIS1100_REMOTE_RESET:
        res=ioctl_remote_reset(sc, fd); break;
    case SIS1100_DEVTYPE:
        res=ioctl_devtype(sc, fd, (int*)data); break;
    case SIS1100_DRIVERVERSION:
        res=ioctl_driverversion(sc, fd, (int*)data); break;
    case SIS1100_READ_EEPROM:
        res=ioctl_read_eeprom(sc, fd, (struct sis1100_eeprom_req*)data); break;
    case SIS1100_WRITE_EEPROM:
        res=ioctl_write_eeprom(sc, fd, (struct sis1100_eeprom_req*)data); break;

    case SIS1100_JTAG_ENABLE:
        res=ioctl_jtag_enable(sc, fd, (u_int32_t*)data); break;
    case SIS1100_JTAG_CTRL:
        res=ioctl_jtag_ctrl(sc, fd, (u_int32_t*)data); break;
    case SIS1100_JTAG_DATA:
        res=ioctl_jtag_data(sc, fd, (u_int32_t*)data); break;
    case SIS1100_JTAG_PUT:
        res=ioctl_jtag_put(sc, fd, (u_int32_t*)data); break;
    case SIS1100_JTAG_GET:
        res=ioctl_jtag_get(sc, fd, (u_int32_t*)data); break;

    default:
        res=ENOTTY; break;
    }
    return res;
}

#ifdef __NetBSD__
int
sis1100_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
    struct sis1100_softc* sc=SIS1100SC(dev);
    struct sis1100_fdata* fd=SIS1100FD(dev);
    fd->p=p;
    return _sis1100_ioctl(sc, fd, cmd, data);
}
#elif __linux__

union alles {
    struct vmespace                               vmespace;
    struct sis1100_vme_req                         vme_req;
    struct sis1100_vme_block_req             vme_block_req;
    struct sis1100_ctrl_reg                       ctrl_reg;
    struct sis1100_pipe                               pipe;
    struct sis1100_ident                             ident;
    struct sis1100_irq_ctl                         irq_ctl;
    struct sis1100_irq_get                         irq_get;
    struct sis1100_irq_ack                  irq_ackirq_ack;
    struct sis1100_vme_super_block_req vme_super_block_req;
    struct sis1100_writepipe                     writepipe;
    struct sis1100_dma_alloc                     dma_alloc;
    struct sis1100_camac_req                     camac_req;
    struct sis1100_dsp_code                       dsp_code;
    struct sis1100_eeprom_req                   eeprom_req;
};

#define MAX_DATA (sizeof(union alles))

int
sis1100_ioctl(struct inode *inode, struct file *file,
	      unsigned int cmd, unsigned long arg)
{
    struct sis1100_softc* sc=SIS1100SC(file);
    struct sis1100_fdata* fd=SIS1100FD(file);
    u_int8_t data[MAX_DATA];
    int res;

    if ((cmd&IOC_INOUT) && (_IOC_SIZE(cmd)>MAX_DATA)) {
        pINFO(sc, "sis1100_ioctl: cmd=0x%08x _IOC_SIZE(cmd)=%d",
            cmd, _IOC_SIZE(cmd));
        return -EINVAL;
    }

    if (cmd&IOC_IN) {
        if (copy_from_user(&data, (void *)arg, _IOC_SIZE(cmd)))
                return -EFAULT;
    }
    if ((res=_sis1100_ioctl(sc, fd, cmd, &data)))
            return -res;

    if (cmd&IOC_OUT) {
        if (copy_to_user((void *)arg, &data, _IOC_SIZE(cmd)))
                return -EFAULT;
    }
    return 0;
}
#endif
