📄 dr.c
字号:
/* * Copyright (c) 1988 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Computer Consoles Inc. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)dr.c 7.9 (Berkeley) 12/16/90 */#include "dr.h"#if NDR > 0/* * DRV11-W DMA interface driver. * * UNTESTED WITH 4.3 */#include "../include/mtpr.h"#include "../include/pte.h"#include "sys/param.h"#include "sys/conf.h"#include "sys/user.h"#include "sys/proc.h"#include "sys/map.h"#include "sys/ioctl.h"#include "sys/buf.h"#include "sys/vm.h"#include "sys/kernel.h"#include "../vba/vbavar.h"#include "../vba/drreg.h"#define YES 1#define NO 0struct vba_device *drinfo[NDR];struct dr_aux dr_aux[NDR];unsigned drminphys();int drprobe(), drintr(), drattach(), drtimo(), drrwtimo();int drstrategy();extern struct vba_device *drinfo[];static long drstd[] = { 0 };struct vba_driver drdriver = { drprobe, 0, drattach, 0, drstd, "rs", drinfo };#define RSUNIT(dev) (minor(dev) & 7)#define SPL_UP spl5/* -------- Per-unit data -------- */extern struct dr_aux dr_aux[];#ifdef DR_DEBUGlong DR11 = 0;#endifdrprobe(reg, vi) caddr_t reg; struct vba_device *vi;{ register int br, cvec; /* must be r12, r11 */ struct rsdevice *dr;#ifdef lint br = 0; cvec = br; br = cvec; drintr(0);#endif if (badaddr(reg, 2)) return (0); dr = (struct rsdevice *)reg; dr->dr_intvect = --vi->ui_hd->vh_lastiv;#ifdef DR_DEBUG printf("dprobe: Set interrupt vector %lx and init\n",dr->dr_intvec);#endif /* generate interrupt here for autoconfig */ dr->dr_cstat = MCLR; /* init board and device */#ifdef DR_DEBUG printf("drprobe: Initial status %lx\n", dr->dr_cstat);#endif br = 0x18, cvec = dr->dr_intvect; /* XXX */ return (sizeof (struct rsdevice)); /* DR11 exist */}/* ARGSUSED */drattach(ui) struct vba_device *ui;{ register struct dr_aux *rsd; rsd = &dr_aux[ui->ui_unit]; rsd->dr_flags = DR_PRES; /* This dr11 is present */ rsd->dr_addr = (struct rsdevice *)ui->ui_addr; /* Save addr of this dr11 */ rsd->dr_istat = 0; rsd->dr_bycnt = 0; rsd->dr_cmd = 0; rsd->currenttimo = 0;}/*ARGSUSED*/dropen(dev, flag) dev_t dev; int flag;{ register int unit = RSUNIT(dev); register struct rsdevice *dr; register struct dr_aux *rsd; if (drinfo[unit] == 0 || !drinfo[unit]->ui_alive) return (ENXIO); dr = RSADDR(unit); rsd = &dr_aux[unit]; if (rsd->dr_flags & DR_OPEN) {#ifdef DR_DEBUG printf("\ndropen: dr11 unit %ld already open",unit);#endif return (ENXIO); /* DR11 already open */ } rsd->dr_flags |= DR_OPEN; /* Mark it OPEN */ rsd->dr_istat = 0; /* Clear status of previous interrupt */ rsd->rtimoticks = hz; /* Set read no stall timout to 1 sec */ rsd->wtimoticks = hz*60; /* Set write no stall timout to 1 min */ dr->dr_cstat = DR_ZERO; /* Clear function & latches */ dr->dr_pulse = (RDMA | RATN); /* clear leftover attn & e-o-r flags */ drtimo(dev); /* start the self kicker */ return (0);}drclose (dev) dev_t dev;{ register int unit = RSUNIT(dev); register struct dr_aux *dra; register struct rsdevice *rs; register short s; dra = &dr_aux[unit]; if ((dra->dr_flags & DR_OPEN) == 0) {#ifdef DR_DEBUG printf("\ndrclose: DR11 device %ld not open",unit);#endif return; } dra->dr_flags &= ~(DR_OPEN|DR_ACTV); rs = dra->dr_addr; s = SPL_UP(); rs->dr_cstat = DR_ZERO; if (dra->dr_buf.b_flags & B_BUSY) { dra->dr_buf.b_flags &= ~B_BUSY; wakeup((caddr_t)&dra->dr_buf.b_flags); } splx(s); return (0);}/* drread() works exactly like drwrite() except that the B_READ flag is used when physio() is called*/drread (dev, uio) dev_t dev; struct uio *uio;{ register struct dr_aux *dra; register struct buf *bp; register int spl, err; register int unit = RSUNIT(dev); if (uio->uio_iov->iov_len <= 0 || /* Negative count */ uio->uio_iov->iov_len & 1 || /* odd count */ (int)uio->uio_iov->iov_base & 1) /* odd destination address */ return (EINVAL);#ifdef DR_DEBUG if (DR11 & 8) printf("\ndrread: (len:%ld)(base:%lx)", uio->uio_iov->iov_len,(int)uio->uio_iov->iov_base); #endif dra = &dr_aux[RSUNIT(dev)]; dra->dr_op = DR_READ; bp = &dra->dr_buf; bp->b_resid = 0; if (dra->dr_flags & DR_NORSTALL) { /* * We are in no stall mode, start the timer, * raise IPL so nothing can stop us once the * timer's running */ spl = SPL_UP(); timeout(drrwtimo, (caddr_t)((dra->currenttimo<<8) | unit), (int)dra->rtimoticks); err = physio(drstrategy, bp, dev,B_READ, drminphys, uio); splx(spl); if (err) return (err); dra->currenttimo++; /* Update current timeout number */ /* Did we timeout */ if (dra->dr_flags & DR_TMDM) dra->dr_flags &= ~DR_TMDM; /* Clear timeout flag */ return (err); } return (physio(drstrategy, bp, dev,B_READ, drminphys, uio));}drwrite(dev, uio) dev_t dev; struct uio *uio;{ register struct dr_aux *dra; register struct buf *bp; register int unit = RSUNIT(dev); int spl, err; if (uio->uio_iov->iov_len <= 0 || uio->uio_iov->iov_len & 1 || (int)uio->uio_iov->iov_base & 1) return (EINVAL);#ifdef DR_DEBUG if (DR11 & 4) printf("\ndrwrite: (len:%ld)(base:%lx)", uio->uio_iov->iov_len,(int)uio->uio_iov->iov_base); #endif dra = &dr_aux[RSUNIT(dev)]; dra->dr_op = DR_WRITE; bp = &dra->dr_buf; bp->b_resid = 0; if (dra->dr_flags & DR_NOWSTALL) { /* * We are in no stall mode, start the timer, * raise IPL so nothing can stop us once the * timer's running */ spl = SPL_UP(); timeout(drrwtimo,(caddr_t)((dra->currenttimo<<8) | unit), (int)dra->wtimoticks); err = physio (drstrategy, bp, dev,B_WRITE, drminphys, uio); splx(spl); if (err) return (err); dra->currenttimo++; /* Update current timeout number */ /* Did we timeout */ if (dra->dr_flags & DR_TMDM) dra->dr_flags &= ~DR_TMDM; /* Clear timeout flag */ return (err); } return (physio(drstrategy, bp, dev,B_WRITE, drminphys, uio));}/* * Routine used by calling program to issue commands to dr11 driver and * through it to the device. * It is also used to read status from the device and driver and to wait * for attention interrupts. * Status is returned in an 8 elements unsigned short integer array, the * first two elements of the array are also used to pass arguments to * drioctl() if required. * The function bits to be written to the dr11 are included in the cmd * argument. Even if they are not being written to the dr11 in a particular * drioctl() call, they will update the copy of cmd that is stored in the * driver. When drstrategy() is called, this updated copy is used if a * deferred function bit write has been specified. The "side effect" of * calls to the drioctl() requires that the last call prior to a read or * write has an appropriate copy of the function bits in cmd if they are * to be used in drstrategy(). * When used as command value, the contents of data[0] is the command * parameter. */drioctl(dev, cmd, data) dev_t dev; int cmd; long *data;{ register int unit = RSUNIT(dev); register struct dr_aux *dra; register struct rsdevice *rsaddr = RSADDR(unit); int s, error = 0; u_short status; long temp;#ifdef DR_DEBUG if (DR11 & 0x10) printf("\ndrioctl: (dev:%lx)(cmd:%lx)(data:%lx)(data[0]:%lx)", dev,cmd,data,data[0]);#endif dra = &dr_aux[unit]; dra->dr_cmd = 0; /* Fresh copy; clear all previous flags */ switch (cmd) { case DRWAIT: /* Wait for attention interrupt */#ifdef DR_DEBUG printf("\ndrioctl: wait for attention interrupt");#endif s = SPL_UP(); /* * If the attention flag in dr_flags is set, it probably * means that an attention has arrived by the time a * previous DMA end-of-range interrupt was serviced. If * ATRX is set, we will return with out sleeping, since * we have received an attention since the last call to * wait on attention. This may not be appropriate for * some applications. */ if ((dra->dr_flags & DR_ATRX) == 0) { dra->dr_flags |= DR_ATWT; /* Set waiting flag */ /* * Enable interrupt; use pulse reg. * so function bits are not changed */ rsaddr->dr_pulse = IENB; error = tsleep((caddr_t)&dra->dr_cmd, DRPRI | PCATCH, devio, 0); } splx(s); break; case DRPIOW: /* Write to p-i/o register */ rsaddr->dr_data = data[0]; break; case DRPACL: /* Send pulse to device */ rsaddr->dr_pulse = FCN2; break; case DRDACL: /* Defer alco pulse until go */ dra->dr_cmd |= DR_DACL; break; case DRPCYL: /* Set cycle with next go */ dra->dr_cmd |= DR_PCYL; break; case DRDFCN: /* Update function with next go */ dra->dr_cmd |= DR_DFCN; break; case DRRATN: /* Reset attention flag */ rsaddr->dr_pulse = RATN; break; case DRRDMA: /* Reset DMA e-o-r flag */ rsaddr->dr_pulse = RDMA; break; case DRSFCN: /* Set function bits */ temp = data[0] & DR_FMSK; /* * This has a very important side effect -- It clears * the interrupt enable flag. That is fine for this driver, * but if it is desired to leave interrupt enable at all * times, it will be necessary to read the status register * first to get IENB, or carry a software flag that indicates * whether interrupts are set, and or this into the control * register value being written. */ rsaddr->dr_cstat = temp; break; case DRRPER: /* Clear parity flag */ rsaddr->dr_pulse = RPER; break; case DRSETRSTALL: /* Set read stall mode. */ dra->dr_flags &= (~DR_NORSTALL); break; case DRSETNORSTALL: /* Set no stall read mode. */ dra->dr_flags |= DR_NORSTALL; break; case DRGETRSTALL: /* Returns true if in read stall mode */ data[0] = (dra->dr_flags & DR_NORSTALL)? 0 : 1; break; case DRSETRTIMEOUT: /* Set read stall timeout (1/10 secs) */ if (data[0] < 1) error = EINVAL; dra->rtimoticks = (data[0] * hz )/10; break; case DRGETRTIMEOUT: /* Return read stall timeout */ data[0] = ((dra->rtimoticks)*10)/hz; break; case DRSETWSTALL: /* Set write stall mode. */ dra->dr_flags &= (~DR_NOWSTALL); break; case DRSETNOWSTALL: /* Set write stall mode. */ dra->dr_flags |= DR_NOWSTALL; break; case DRGETWSTALL: /* Return true if in write stall mode */ data[0] = (dra->dr_flags & DR_NOWSTALL)? 0 : 1; break; case DRSETWTIMEOUT: /* Set write stall timeout (1/10's) */ if (data[0] < 1) error = EINVAL; dra->wtimoticks = (data[0] * hz )/10; break; case DRGETWTIMEOUT: /* Return write stall timeout */ data[0] = ((dra->wtimoticks)*10)/hz; break; case DRWRITEREADY: /* Return true if can write data */ data[0] = (rsaddr->dr_cstat & STTA)? 1 : 0; break; case DRREADREADY: /* Return true if data to be read */ data[0] = (rsaddr->dr_cstat & STTB)? 1 : 0; break; case DRBUSY: /* Return true if device busy */ /* * Internally this is the DR11-W * STAT C bit, but there is a bug in the Omega 500/FIFO * interface board that it cannot drive this signal low * for certain DR11-W ctlr such as the Ikon. We use the * REDY signal of the CSR on the Ikon DR11-W instead. */#ifdef notdef data[0] = (rsaddr->dr_cstat & STTC)? 1 : 0;#else data[0] = ((rsaddr->dr_cstat & REDY)? 0 : 1);#endif break; case DRRESET: /* Reset device */ /* Reset DMA ATN RPER flag */ rsaddr->dr_pulse = (MCLR|RDMA|RATN|RPER); DELAY(0x1f000); while ((rsaddr->dr_cstat & REDY) == 0 && error == 0) /* Wakeup by drtimo() */ error = tsleep((caddr_t)dra, DRPRI | PCATCH, devio, 0); dra->dr_istat = 0; dra->dr_cmd = 0; dra->currenttimo = 0; break; case DR11STAT: { /* Copy back dr11 status to user */ register struct dr11io *dr = (struct dr11io *)data; dr->arg[0] = dra->dr_flags; dr->arg[1] = rsaddr->dr_cstat; dr->arg[2] = dra->dr_istat; /* Status at last interrupt */ dr->arg[3] = rsaddr->dr_data; /* P-i/o input data */ status = (u_short)((rsaddr->dr_addmod << 8) & 0xff00); dr->arg[4] = status | (u_short)(rsaddr->dr_intvect & 0xff); dr->arg[5] = rsaddr->dr_range; dr->arg[6] = rsaddr->dr_rahi; dr->arg[7] = rsaddr->dr_ralo; break; } case DR11LOOP: /* Perform loopback test */ /* * NB: MUST HAVE LOOPBACK CABLE ATTACHED -- * Test results are printed on system console */ if (error = suser(u.u_cred, &u.u_acflag)) break; dr11loop(rsaddr, dra, unit); break; default: return (EINVAL); }#ifdef DR_DEBUG if (DR11 & 0x10) printf("**** (data[0]:%lx)",data[0]);#endif return (error);}#define NPAT 2#define DMATBL 20u_short tstpat[DMATBL] = { 0xAAAA, 0x5555};long DMAin = 0;/* * Perform loopback test -- MUST HAVE LOOPBACK CABLE ATTACHED * Test results are printed on system console */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -