/* * FreeBSD device driver for B004-compatible Transputer boards. * * based on Linux version Copyright (C) 1993 by Christoph Niemann * * Rewritten for FreeBSD by * Luigi Rizzo (luigi@iet.unipi.it) and * Lorenzo Vicisano (l.vicisano@iet.unipi.it) * Dipartimento di Ingegneria dell'Informazione * Universita` di Pisa * via Diotisalvi 2, 56126 Pisa, ITALY * 14 september 1994 * * 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 Christoph Niemann, * Luigi Rizzo and Lorenzo Vicisano - Dipartimento di Ingegneria * dell'Informazione * 4. The names of these contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE 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 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. * * NOTE NOTE NOTE * The assembler version is still under development. */ /* #define USE_ASM */ #include "bqu.h" #if NBQU > 0 #include #include #include #include #include #include "opt_devfs.h" #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #define IOCTL_OUT(arg, ret) *(int*)arg = ret #define B004PRI (PZERO+8) #define B004_CHANCE 8 /* * Define these symbols if you want to debug the code. */ #undef B004_DEBUG #undef B004_DEBUG_2 #ifdef B004_DEBUG static u_char d_inb(u_int port); static void d_outb(u_int port, u_char data); #define out(port,data) d_outb(port, data) #define in(a) d_inb(((u_int)a)) #else #define out(port, data) outb(port,data) #define in(port) inb(((u_int)port)) #endif B004_DEBUG #ifdef B004_DEBUG #define DEB(x) x #define NO_DEB(x) /* */ #else #define DEB(x) /* */ #define NO_DEB(x) x #endif #ifdef B004_DEBUG_2 #define DEB2(x) x #else #define DEB2(x) #endif static int bquprobe(struct isa_device *idp); static int bquattach(struct isa_device *idp); struct isa_driver bqudriver = { bquprobe, bquattach, "bqu" }; static d_open_t bquopen; static d_close_t bquclose; static d_read_t bquread; static d_write_t bquwrite; static d_ioctl_t bquioctl; static d_poll_t bqupoll; #define CDEV_MAJOR 8 static struct cdevsw bqu_cdevsw = { bquopen, bquclose, bquread, bquwrite, /*8*/ bquioctl, nostop, nullreset, nodevtotty,/* tputer */ bqupoll, nommap, NULL, "bqu", NULL, -1 }; static int b004_sleep; /* wait address */ static struct b004_struct b004_table[NBQU]; static int first_time=1; /* * At these addresses the driver will search for B004-compatible boards */ static int b004_base_addresses[B004_CHANCE] = { /* 0x150, 0x170, 0x190, 0x200, 0x300, 0x320, 0x340, 0x360 */ 0x150, 0x190, 0, 0, 0, 0, 0, 0 }; #ifdef B004_DEBUG static void d_outb(u_int port, u_char data) { printf("OUT 0x%x TO 0x%x\n",data,port); outb(port,data); } static u_char d_inb(u_int port) { u_char ap; ap=inb(port); printf("INPUT 0x%x FROM 0x%x\n",ap,port); return(ap); } #endif static int detected(int base) { int i; for(i=0;iuio_resid < 0) { DEB(printf("B004: invalid count for reading = %d.\n", uio->uio_resid);) return EINVAL; } while ( uio->uio_resid ) { int sleep_ticks=0; char *p, *last, *lim; int i, end = min(B004_MAX_BYTES,uio->uio_resid); lim= &buffer[end]; for (p= buffer; puio_resid < 0) { DEB(printf("B004 invalid argument for writing: count = %d.\n", uio->uio_resid);) return EINVAL; } while ( uio->uio_resid ) { int sleep_ticks=0; char *p, *last, *lim; end = min(B004_MAX_BYTES,uio->uio_resid); uiomove((caddr_t)buffer, end, uio); lim= &buffer[end]; for (p= &buffer[0]; puio_resid += (lim - p); break; } if (timeout < sleep_ticks) sleep_ticks=timeout; timeout -= sleep_ticks; } DEB2(printf("Write: SLEEPING FOR %d TICKS XXXXXXX\n",sleep_ticks);) if (tsleep((caddr_t)&b004_sleep, B004PRI | PCATCH, "b004_rd", sleep_ticks)!=EWOULDBLOCK) return 1; } } if( (Timeout) && (timeout <= 0) ) break; } return 0; } /* bquwrite() */ /* * int bquopen() -- open the link-device. * */ static int bquopen(dev_t dev, int flags, int fmt, struct proc *p) { unsigned int dev_min = minor(dev) & 7; if (dev_min >= NBQU) { DEB(printf("B004 not opened, minor number >= %d.\n", NBQU);) return ENXIO; } if ((B004_F(dev_min) & B004_EXIST) == 0) { DEB(printf("B004 not opened, board %d does not exist.\n", dev_min);) return ENXIO; } if (B004_F(dev_min) & B004_BUSY) { DEB(printf("B004 not opened, board busy (minor = %d).\n", dev_min);) return EBUSY; } B004_F(dev_min) |= B004_BUSY; B004_TIMEOUT(dev_min) = 0; DEB(printf( "B004 opened, minor = %d.\n", dev_min );) return 0; } /* bquopen() */ /* * int b004close() -- close the link device. */ static int bquclose(dev_t dev, int flags, int fmt, struct proc *p) { unsigned int dev_min = minor(dev) & 7; if (dev_min >= NBQU) { DEB(printf("B004 not released, minor number >= %d.\n", NBQU);) return ENXIO; } B004_F(dev_min) &= ~B004_BUSY; DEB(printf("B004(%d) released.\n", dev_min );) return 0; } static int bqupoll(dev_t dev, int events, struct proc *p) { /* still unimplemented */ return(seltrue(dev, events, p)); } /* * int bquioctl() * * Supported functions: * - reset * - analyse * - test error flag * - set timeout */ static int bquioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) { unsigned int dev_min = minor(dev) & 7; int result = 0; if (dev_min >= NBQU) { DEB(printf("B004 ioctl exit, minor >= %d.\n", NBQU );) return ENODEV; } if ((B004_F(dev_min) & B004_EXIST) == 0) { DEB(printf("B004 ioctl exit, (B004_F & B004_EXIST) == 0.\n" );) return ENODEV; } switch ( cmd ) { case B004RESET: /* reset transputer */ bqureset(dev_min); DEB(printf("B004 ioctl B004RESET, done\n" );) break; case B004WRITEABLE: /* can we write a byte to the C012 ? */ IOCTL_OUT (addr, ((in(B004_OSR(dev_min))&B004_WRITEBYTE) != 0 )); break; case B004READABLE: /* can we read a byte from C012 ? */ IOCTL_OUT (addr, ((in(B004_ISR(dev_min)) & B004_READBYTE) != 0 )); break; case B004ANALYSE: /* switch transputer to analyse mode */ bquanalyse(dev_min); break; case B004ERROR: /* test error-flag */ IOCTL_OUT (addr, ((inb(B004_BASE(dev_min)+B004_ERROR_OFFSET) & B004_TEST_ERROR) ? 0 : 1)); break; case B004TIMEOUT: /* set, retrieve timeout for writing & reading*/ B004_TIMEOUT(dev_min) = *((int *)addr); break; default: result = EINVAL; } return result; } /* bquioctl() */ static int bquattach(struct isa_device *idp) { int unit = idp->id_unit; struct b004_struct *bp; int i; #ifdef DEVFS #define BQU_UID 66 #define BQU_GID 66 #define BQU_PERM 0600 bp = &b004_table[unit]; for ( i = 0; i < 8; i++) { #ifdef NOTYET /* if (we've done all the ports found) break; */ #endif bp->devfs_token[i][0]= devfs_add_devswf(&bqu_cdevsw, i, DV_CHR, BQU_UID, BQU_GID, BQU_PERM, "ttyba%d", i); bp->devfs_token[i][0]= devfs_add_devswf(&bqu_cdevsw, i+64, DV_CHR, BQU_UID, BQU_GID, BQU_PERM, "ttybb%d", i); bp->devfs_token[i][0]= devfs_add_devswf(&bqu_cdevsw, i+128, DV_CHR, BQU_UID, BQU_GID, BQU_PERM, "ttybc%d", i); bp->devfs_token[i][0]= devfs_add_devswf(&bqu_cdevsw, i+192, DV_CHR, BQU_UID, BQU_GID, BQU_PERM, "ttybd%d", unit); } #endif return 1; } /* * int bquprobe * * Initializes the driver. It tries to detect the hardware * and sets up all relevant data-structures. */ static int bquprobe(struct isa_device *idp) { unsigned int test; unsigned int dev_min = idp->id_unit; int i,found = 0; /* After a reset it should be possible to write a byte to the B004. So let'S do a reset and then test the output status register */ #ifdef undef printf( "bquprobe::\nIOBASE 0x%x\nIRQ %d\nDRQ %d\nMSIZE %d\nUNIT %d\nFLAGS" "x0%x\nALIVE %d\n",idp->id_iobase,idp->id_irq, idp->id_drq,idp->id_msize,idp->id_unit,idp->id_flags,idp->id_alive); #endif if(first_time){ for(i=0;i= NBQU) return (0); /* No more descriptors */ if ((idp->id_iobase < 0x100) || (idp->id_iobase >= 0x1000)) idp->id_iobase=0; /* Dangerous isa addres ) */ for (test = 0; (test < B004_CHANCE); test++) { if((idp->id_iobase==0)&&((!b004_base_addresses[test])|| detected(b004_base_addresses[test]))) continue; idp->id_iobase=b004_base_addresses[test]; DEB(printf("Probing device %d at address 0x%x\n",dev_min, idp->id_iobase); ) b004_delay(test); B004_F(dev_min) = 0; B004_TIMEOUT(dev_min) = B004_INIT_TIMEOUT; B004_BASE(dev_min) = idp->id_iobase; B004_ODR(dev_min) = B004_BASE(dev_min) + B004_ODR_OFFSET; B004_ISR(dev_min) = B004_BASE(dev_min) + B004_ISR_OFFSET; B004_OSR(dev_min) = B004_BASE(dev_min) + B004_OSR_OFFSET; bqureset(dev_min); for (i = 0; i < B004_MAXTRY; i++) if ( in(B004_OSR(dev_min)) == B004_WRITEBYTE) { B004_F(dev_min) |= B004_EXIST; out(B004_BASE(dev_min) + B008_INT_OFFSET, 0); b004_delay(test); if (in(B004_BASE(dev_min) + B008_INT_OFFSET) & 0x0f == 0) B004_BOARDTYPE(dev_min) = B008; else B004_BOARDTYPE(dev_min) = B004; printf("bqu%d at 0x0%x (polling) is a B00%s\n", dev_min,B004_IDR(dev_min), (B004_BOARDTYPE(dev_min) == B004) ? "4" : "8"); found = 1; break; } if(!found) { idp->id_iobase=0; } else break; } if (!found){ DEB(printf("b004probe(): no B004-board found.\n")); return (0); } idp->id_maddr=NULL; idp->id_irq=0; if(B004_BOARDTYPE(dev_min) == B004) return(18); else return(20); } /* bquprobe() */ static bqu_devsw_installed = 0; static void bqu_drvinit(void *unused) { dev_t dev; if( ! bqu_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&bqu_cdevsw, NULL); bqu_devsw_installed = 1; } } SYSINIT(bqudev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,bqu_drvinit,NULL) #endif /* NBQU */