Index: head/sys/compat/linux/linux_ioctl.c =================================================================== --- head/sys/compat/linux/linux_ioctl.c (revision 113580) +++ head/sys/compat/linux/linux_ioctl.c (revision 113581) @@ -1,2463 +1,2462 @@ /* * Copyright (c) 1994-1995 Søren Schmidt * 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 * in this position and unchanged. * 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. 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. * * $FreeBSD$ */ #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 #include #include #include static linux_ioctl_function_t linux_ioctl_cdrom; static linux_ioctl_function_t linux_ioctl_vfat; static linux_ioctl_function_t linux_ioctl_console; static linux_ioctl_function_t linux_ioctl_disk; static linux_ioctl_function_t linux_ioctl_socket; static linux_ioctl_function_t linux_ioctl_sound; static linux_ioctl_function_t linux_ioctl_termio; static linux_ioctl_function_t linux_ioctl_private; static linux_ioctl_function_t linux_ioctl_special; static struct linux_ioctl_handler cdrom_handler = { linux_ioctl_cdrom, LINUX_IOCTL_CDROM_MIN, LINUX_IOCTL_CDROM_MAX }; static struct linux_ioctl_handler vfat_handler = { linux_ioctl_vfat, LINUX_IOCTL_VFAT_MIN, LINUX_IOCTL_VFAT_MAX }; static struct linux_ioctl_handler console_handler = { linux_ioctl_console, LINUX_IOCTL_CONSOLE_MIN, LINUX_IOCTL_CONSOLE_MAX }; static struct linux_ioctl_handler disk_handler = { linux_ioctl_disk, LINUX_IOCTL_DISK_MIN, LINUX_IOCTL_DISK_MAX }; static struct linux_ioctl_handler socket_handler = { linux_ioctl_socket, LINUX_IOCTL_SOCKET_MIN, LINUX_IOCTL_SOCKET_MAX }; static struct linux_ioctl_handler sound_handler = { linux_ioctl_sound, LINUX_IOCTL_SOUND_MIN, LINUX_IOCTL_SOUND_MAX }; static struct linux_ioctl_handler termio_handler = { linux_ioctl_termio, LINUX_IOCTL_TERMIO_MIN, LINUX_IOCTL_TERMIO_MAX }; static struct linux_ioctl_handler private_handler = { linux_ioctl_private, LINUX_IOCTL_PRIVATE_MIN, LINUX_IOCTL_PRIVATE_MAX }; DATA_SET(linux_ioctl_handler_set, cdrom_handler); DATA_SET(linux_ioctl_handler_set, vfat_handler); DATA_SET(linux_ioctl_handler_set, console_handler); DATA_SET(linux_ioctl_handler_set, disk_handler); DATA_SET(linux_ioctl_handler_set, socket_handler); DATA_SET(linux_ioctl_handler_set, sound_handler); DATA_SET(linux_ioctl_handler_set, termio_handler); DATA_SET(linux_ioctl_handler_set, private_handler); struct handler_element { TAILQ_ENTRY(handler_element) list; int (*func)(struct thread *, struct linux_ioctl_args *); int low, high, span; }; static TAILQ_HEAD(, handler_element) handlers = TAILQ_HEAD_INITIALIZER(handlers); static int linux_ioctl_disk(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; u_int sectorsize; off_t mediasize; if ((error = fget(td, args->fd, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_BLKGETSIZE: error = fo_ioctl(fp, DIOCGSECTORSIZE, (caddr_t)§orsize, td->td_ucred, td); if (!error) error = fo_ioctl(fp, DIOCGMEDIASIZE, (caddr_t)&mediasize, td->td_ucred, td); fdrop(fp, td); if (error) return (error); sectorsize = mediasize / sectorsize; /* * XXX: How do we know we return the right size of integer ? */ return (copyout(§orsize, (void *)args->arg, sizeof(sectorsize))); } fdrop(fp, td); return (ENOIOCTL); } /* * termio related ioctls */ struct linux_termio { unsigned short c_iflag; unsigned short c_oflag; unsigned short c_cflag; unsigned short c_lflag; unsigned char c_line; unsigned char c_cc[LINUX_NCC]; }; struct linux_termios { unsigned int c_iflag; unsigned int c_oflag; unsigned int c_cflag; unsigned int c_lflag; #ifdef __alpha__ unsigned char c_cc[LINUX_NCCS]; unsigned char c_line; unsigned int c_ispeed; unsigned int c_ospeed; #else unsigned char c_line; unsigned char c_cc[LINUX_NCCS]; #endif }; struct linux_winsize { unsigned short ws_row, ws_col; unsigned short ws_xpixel, ws_ypixel; }; static struct speedtab sptab[] = { { B0, LINUX_B0 }, { B50, LINUX_B50 }, { B75, LINUX_B75 }, { B110, LINUX_B110 }, { B134, LINUX_B134 }, { B150, LINUX_B150 }, { B200, LINUX_B200 }, { B300, LINUX_B300 }, { B600, LINUX_B600 }, { B1200, LINUX_B1200 }, { B1800, LINUX_B1800 }, { B2400, LINUX_B2400 }, { B4800, LINUX_B4800 }, { B9600, LINUX_B9600 }, { B19200, LINUX_B19200 }, { B38400, LINUX_B38400 }, { B57600, LINUX_B57600 }, { B115200, LINUX_B115200 }, {-1, -1 } }; struct linux_serial_struct { int type; int line; int port; int irq; int flags; int xmit_fifo_size; int custom_divisor; int baud_base; unsigned short close_delay; char reserved_char[2]; int hub6; unsigned short closing_wait; unsigned short closing_wait2; int reserved[4]; }; static int linux_to_bsd_speed(int code, struct speedtab *table) { for ( ; table->sp_code != -1; table++) if (table->sp_code == code) return (table->sp_speed); return -1; } static int bsd_to_linux_speed(int speed, struct speedtab *table) { for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return -1; } static void bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios) { int i; #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: BSD termios structure (input):\n"); printf("i=%08x o=%08x c=%08x l=%08x ispeed=%d ospeed=%d\n", bios->c_iflag, bios->c_oflag, bios->c_cflag, bios->c_lflag, bios->c_ispeed, bios->c_ospeed); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif lios->c_iflag = 0; if (bios->c_iflag & IGNBRK) lios->c_iflag |= LINUX_IGNBRK; if (bios->c_iflag & BRKINT) lios->c_iflag |= LINUX_BRKINT; if (bios->c_iflag & IGNPAR) lios->c_iflag |= LINUX_IGNPAR; if (bios->c_iflag & PARMRK) lios->c_iflag |= LINUX_PARMRK; if (bios->c_iflag & INPCK) lios->c_iflag |= LINUX_INPCK; if (bios->c_iflag & ISTRIP) lios->c_iflag |= LINUX_ISTRIP; if (bios->c_iflag & INLCR) lios->c_iflag |= LINUX_INLCR; if (bios->c_iflag & IGNCR) lios->c_iflag |= LINUX_IGNCR; if (bios->c_iflag & ICRNL) lios->c_iflag |= LINUX_ICRNL; if (bios->c_iflag & IXON) lios->c_iflag |= LINUX_IXON; if (bios->c_iflag & IXANY) lios->c_iflag |= LINUX_IXANY; if (bios->c_iflag & IXOFF) lios->c_iflag |= LINUX_IXOFF; if (bios->c_iflag & IMAXBEL) lios->c_iflag |= LINUX_IMAXBEL; lios->c_oflag = 0; if (bios->c_oflag & OPOST) lios->c_oflag |= LINUX_OPOST; if (bios->c_oflag & ONLCR) lios->c_oflag |= LINUX_ONLCR; if (bios->c_oflag & OXTABS) lios->c_oflag |= LINUX_XTABS; lios->c_cflag = bsd_to_linux_speed(bios->c_ispeed, sptab); lios->c_cflag |= (bios->c_cflag & CSIZE) >> 4; if (bios->c_cflag & CSTOPB) lios->c_cflag |= LINUX_CSTOPB; if (bios->c_cflag & CREAD) lios->c_cflag |= LINUX_CREAD; if (bios->c_cflag & PARENB) lios->c_cflag |= LINUX_PARENB; if (bios->c_cflag & PARODD) lios->c_cflag |= LINUX_PARODD; if (bios->c_cflag & HUPCL) lios->c_cflag |= LINUX_HUPCL; if (bios->c_cflag & CLOCAL) lios->c_cflag |= LINUX_CLOCAL; if (bios->c_cflag & CRTSCTS) lios->c_cflag |= LINUX_CRTSCTS; lios->c_lflag = 0; if (bios->c_lflag & ISIG) lios->c_lflag |= LINUX_ISIG; if (bios->c_lflag & ICANON) lios->c_lflag |= LINUX_ICANON; if (bios->c_lflag & ECHO) lios->c_lflag |= LINUX_ECHO; if (bios->c_lflag & ECHOE) lios->c_lflag |= LINUX_ECHOE; if (bios->c_lflag & ECHOK) lios->c_lflag |= LINUX_ECHOK; if (bios->c_lflag & ECHONL) lios->c_lflag |= LINUX_ECHONL; if (bios->c_lflag & NOFLSH) lios->c_lflag |= LINUX_NOFLSH; if (bios->c_lflag & TOSTOP) lios->c_lflag |= LINUX_TOSTOP; if (bios->c_lflag & ECHOCTL) lios->c_lflag |= LINUX_ECHOCTL; if (bios->c_lflag & ECHOPRT) lios->c_lflag |= LINUX_ECHOPRT; if (bios->c_lflag & ECHOKE) lios->c_lflag |= LINUX_ECHOKE; if (bios->c_lflag & FLUSHO) lios->c_lflag |= LINUX_FLUSHO; if (bios->c_lflag & PENDIN) lios->c_lflag |= LINUX_PENDIN; if (bios->c_lflag & IEXTEN) lios->c_lflag |= LINUX_IEXTEN; for (i=0; ic_cc[i] = LINUX_POSIX_VDISABLE; lios->c_cc[LINUX_VINTR] = bios->c_cc[VINTR]; lios->c_cc[LINUX_VQUIT] = bios->c_cc[VQUIT]; lios->c_cc[LINUX_VERASE] = bios->c_cc[VERASE]; lios->c_cc[LINUX_VKILL] = bios->c_cc[VKILL]; lios->c_cc[LINUX_VEOF] = bios->c_cc[VEOF]; lios->c_cc[LINUX_VEOL] = bios->c_cc[VEOL]; lios->c_cc[LINUX_VMIN] = bios->c_cc[VMIN]; lios->c_cc[LINUX_VTIME] = bios->c_cc[VTIME]; lios->c_cc[LINUX_VEOL2] = bios->c_cc[VEOL2]; lios->c_cc[LINUX_VSUSP] = bios->c_cc[VSUSP]; lios->c_cc[LINUX_VSTART] = bios->c_cc[VSTART]; lios->c_cc[LINUX_VSTOP] = bios->c_cc[VSTOP]; lios->c_cc[LINUX_VREPRINT] = bios->c_cc[VREPRINT]; lios->c_cc[LINUX_VDISCARD] = bios->c_cc[VDISCARD]; lios->c_cc[LINUX_VWERASE] = bios->c_cc[VWERASE]; lios->c_cc[LINUX_VLNEXT] = bios->c_cc[VLNEXT]; for (i=0; ic_cc[i] == _POSIX_VDISABLE) lios->c_cc[i] = LINUX_POSIX_VDISABLE; } lios->c_line = 0; #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: LINUX termios structure (output):\n"); printf("i=%08x o=%08x c=%08x l=%08x line=%d\n", lios->c_iflag, lios->c_oflag, lios->c_cflag, lios->c_lflag, (int)lios->c_line); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif } static void linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios) { int i; #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: LINUX termios structure (input):\n"); printf("i=%08x o=%08x c=%08x l=%08x line=%d\n", lios->c_iflag, lios->c_oflag, lios->c_cflag, lios->c_lflag, (int)lios->c_line); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif bios->c_iflag = 0; if (lios->c_iflag & LINUX_IGNBRK) bios->c_iflag |= IGNBRK; if (lios->c_iflag & LINUX_BRKINT) bios->c_iflag |= BRKINT; if (lios->c_iflag & LINUX_IGNPAR) bios->c_iflag |= IGNPAR; if (lios->c_iflag & LINUX_PARMRK) bios->c_iflag |= PARMRK; if (lios->c_iflag & LINUX_INPCK) bios->c_iflag |= INPCK; if (lios->c_iflag & LINUX_ISTRIP) bios->c_iflag |= ISTRIP; if (lios->c_iflag & LINUX_INLCR) bios->c_iflag |= INLCR; if (lios->c_iflag & LINUX_IGNCR) bios->c_iflag |= IGNCR; if (lios->c_iflag & LINUX_ICRNL) bios->c_iflag |= ICRNL; if (lios->c_iflag & LINUX_IXON) bios->c_iflag |= IXON; if (lios->c_iflag & LINUX_IXANY) bios->c_iflag |= IXANY; if (lios->c_iflag & LINUX_IXOFF) bios->c_iflag |= IXOFF; if (lios->c_iflag & LINUX_IMAXBEL) bios->c_iflag |= IMAXBEL; bios->c_oflag = 0; if (lios->c_oflag & LINUX_OPOST) bios->c_oflag |= OPOST; if (lios->c_oflag & LINUX_ONLCR) bios->c_oflag |= ONLCR; if (lios->c_oflag & LINUX_XTABS) bios->c_oflag |= OXTABS; bios->c_cflag = (lios->c_cflag & LINUX_CSIZE) << 4; if (lios->c_cflag & LINUX_CSTOPB) bios->c_cflag |= CSTOPB; if (lios->c_cflag & LINUX_CREAD) bios->c_cflag |= CREAD; if (lios->c_cflag & LINUX_PARENB) bios->c_cflag |= PARENB; if (lios->c_cflag & LINUX_PARODD) bios->c_cflag |= PARODD; if (lios->c_cflag & LINUX_HUPCL) bios->c_cflag |= HUPCL; if (lios->c_cflag & LINUX_CLOCAL) bios->c_cflag |= CLOCAL; if (lios->c_cflag & LINUX_CRTSCTS) bios->c_cflag |= CRTSCTS; bios->c_lflag = 0; if (lios->c_lflag & LINUX_ISIG) bios->c_lflag |= ISIG; if (lios->c_lflag & LINUX_ICANON) bios->c_lflag |= ICANON; if (lios->c_lflag & LINUX_ECHO) bios->c_lflag |= ECHO; if (lios->c_lflag & LINUX_ECHOE) bios->c_lflag |= ECHOE; if (lios->c_lflag & LINUX_ECHOK) bios->c_lflag |= ECHOK; if (lios->c_lflag & LINUX_ECHONL) bios->c_lflag |= ECHONL; if (lios->c_lflag & LINUX_NOFLSH) bios->c_lflag |= NOFLSH; if (lios->c_lflag & LINUX_TOSTOP) bios->c_lflag |= TOSTOP; if (lios->c_lflag & LINUX_ECHOCTL) bios->c_lflag |= ECHOCTL; if (lios->c_lflag & LINUX_ECHOPRT) bios->c_lflag |= ECHOPRT; if (lios->c_lflag & LINUX_ECHOKE) bios->c_lflag |= ECHOKE; if (lios->c_lflag & LINUX_FLUSHO) bios->c_lflag |= FLUSHO; if (lios->c_lflag & LINUX_PENDIN) bios->c_lflag |= PENDIN; if (lios->c_lflag & LINUX_IEXTEN) bios->c_lflag |= IEXTEN; for (i=0; ic_cc[i] = _POSIX_VDISABLE; bios->c_cc[VINTR] = lios->c_cc[LINUX_VINTR]; bios->c_cc[VQUIT] = lios->c_cc[LINUX_VQUIT]; bios->c_cc[VERASE] = lios->c_cc[LINUX_VERASE]; bios->c_cc[VKILL] = lios->c_cc[LINUX_VKILL]; bios->c_cc[VEOF] = lios->c_cc[LINUX_VEOF]; bios->c_cc[VEOL] = lios->c_cc[LINUX_VEOL]; bios->c_cc[VMIN] = lios->c_cc[LINUX_VMIN]; bios->c_cc[VTIME] = lios->c_cc[LINUX_VTIME]; bios->c_cc[VEOL2] = lios->c_cc[LINUX_VEOL2]; bios->c_cc[VSUSP] = lios->c_cc[LINUX_VSUSP]; bios->c_cc[VSTART] = lios->c_cc[LINUX_VSTART]; bios->c_cc[VSTOP] = lios->c_cc[LINUX_VSTOP]; bios->c_cc[VREPRINT] = lios->c_cc[LINUX_VREPRINT]; bios->c_cc[VDISCARD] = lios->c_cc[LINUX_VDISCARD]; bios->c_cc[VWERASE] = lios->c_cc[LINUX_VWERASE]; bios->c_cc[VLNEXT] = lios->c_cc[LINUX_VLNEXT]; for (i=0; ic_cc[i] == LINUX_POSIX_VDISABLE) bios->c_cc[i] = _POSIX_VDISABLE; } bios->c_ispeed = bios->c_ospeed = linux_to_bsd_speed(lios->c_cflag & LINUX_CBAUD, sptab); #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: BSD termios structure (output):\n"); printf("i=%08x o=%08x c=%08x l=%08x ispeed=%d ospeed=%d\n", bios->c_iflag, bios->c_oflag, bios->c_cflag, bios->c_lflag, bios->c_ispeed, bios->c_ospeed); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif } static void bsd_to_linux_termio(struct termios *bios, struct linux_termio *lio) { struct linux_termios lios; bsd_to_linux_termios(bios, &lios); lio->c_iflag = lios.c_iflag; lio->c_oflag = lios.c_oflag; lio->c_cflag = lios.c_cflag; lio->c_lflag = lios.c_lflag; lio->c_line = lios.c_line; #ifdef __alpha__ lio->c_cc[LINUX__VINTR] = lios.c_cc[LINUX_VINTR]; lio->c_cc[LINUX__VQUIT] = lios.c_cc[LINUX_VQUIT]; lio->c_cc[LINUX__VERASE] = lios.c_cc[LINUX_VERASE]; lio->c_cc[LINUX__VKILL] = lios.c_cc[LINUX_VKILL]; lio->c_cc[LINUX__VEOF] = lios.c_cc[(lios.c_lflag & ICANON) ? LINUX_VEOF : LINUX_VMIN]; lio->c_cc[LINUX__VEOL] = lios.c_cc[(lios.c_lflag & ICANON) ? LINUX_VEOL : LINUX_VTIME]; lio->c_cc[LINUX__VEOL2] = lios.c_cc[LINUX_VEOL2]; lio->c_cc[LINUX__VSWTC] = lios.c_cc[LINUX_VSWTC]; #else memcpy(lio->c_cc, lios.c_cc, LINUX_NCC); #endif } static void linux_to_bsd_termio(struct linux_termio *lio, struct termios *bios) { struct linux_termios lios; int i; lios.c_iflag = lio->c_iflag; lios.c_oflag = lio->c_oflag; lios.c_cflag = lio->c_cflag; lios.c_lflag = lio->c_lflag; #ifdef __alpha__ for (i=0; ic_cc[LINUX__VINTR]; lios.c_cc[LINUX_VQUIT] = lio->c_cc[LINUX__VQUIT]; lios.c_cc[LINUX_VERASE] = lio->c_cc[LINUX__VERASE]; lios.c_cc[LINUX_VKILL] = lio->c_cc[LINUX__VKILL]; lios.c_cc[LINUX_VEOL2] = lio->c_cc[LINUX__VEOL2]; lios.c_cc[LINUX_VSWTC] = lio->c_cc[LINUX__VSWTC]; lios.c_cc[(lio->c_lflag & ICANON) ? LINUX_VEOF : LINUX_VMIN] = lio->c_cc[LINUX__VEOF]; lios.c_cc[(lio->c_lflag & ICANON) ? LINUX_VEOL : LINUX_VTIME] = lio->c_cc[LINUX__VEOL]; #else for (i=LINUX_NCC; ic_cc, LINUX_NCC); #endif linux_to_bsd_termios(&lios, bios); } static int linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args) { struct termios bios; struct linux_termios lios; struct linux_termio lio; struct file *fp; int error; if ((error = fget(td, args->fd, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_TCGETS: error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, td); if (error) break; bsd_to_linux_termios(&bios, &lios); error = copyout(&lios, (void *)args->arg, sizeof(lios)); break; case LINUX_TCSETS: error = copyin((void *)args->arg, &lios, sizeof(lios)); if (error) break; linux_to_bsd_termios(&lios, &bios); error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETSW: error = copyin((void *)args->arg, &lios, sizeof(lios)); if (error) break; linux_to_bsd_termios(&lios, &bios); error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETSF: error = copyin((void *)args->arg, &lios, sizeof(lios)); if (error) break; linux_to_bsd_termios(&lios, &bios); error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCGETA: error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, td); if (error) break; bsd_to_linux_termio(&bios, &lio); error = (copyout(&lio, (void *)args->arg, sizeof(lio))); break; case LINUX_TCSETA: error = copyin((void *)args->arg, &lio, sizeof(lio)); if (error) break; linux_to_bsd_termio(&lio, &bios); error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETAW: error = copyin((void *)args->arg, &lio, sizeof(lio)); if (error) break; linux_to_bsd_termio(&lio, &bios); error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETAF: error = copyin((void *)args->arg, &lio, sizeof(lio)); if (error) break; linux_to_bsd_termio(&lio, &bios); error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred, td)); break; /* LINUX_TCSBRK */ case LINUX_TCXONC: { switch (args->arg) { case LINUX_TCOOFF: args->cmd = TIOCSTOP; break; case LINUX_TCOON: args->cmd = TIOCSTART; break; case LINUX_TCIOFF: case LINUX_TCION: { int c; struct write_args wr; error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, td); if (error) break; fdrop(fp, td); c = (args->arg == LINUX_TCIOFF) ? VSTOP : VSTART; c = bios.c_cc[c]; if (c != _POSIX_VDISABLE) { wr.fd = args->fd; wr.buf = &c; wr.nbyte = sizeof(c); return (write(td, &wr)); } else return (0); } default: fdrop(fp, td); return (EINVAL); } args->arg = 0; error = (ioctl(td, (struct ioctl_args *)args)); break; } case LINUX_TCFLSH: { args->cmd = TIOCFLUSH; switch (args->arg) { case LINUX_TCIFLUSH: args->arg = FREAD; break; case LINUX_TCOFLUSH: args->arg = FWRITE; break; case LINUX_TCIOFLUSH: args->arg = FREAD | FWRITE; break; default: fdrop(fp, td); return (EINVAL); } error = (ioctl(td, (struct ioctl_args *)args)); break; } case LINUX_TIOCEXCL: args->cmd = TIOCEXCL; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCNXCL: args->cmd = TIOCNXCL; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSCTTY: args->cmd = TIOCSCTTY; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCGPGRP: args->cmd = TIOCGPGRP; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSPGRP: args->cmd = TIOCSPGRP; error = (ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_TIOCOUTQ */ /* LINUX_TIOCSTI */ case LINUX_TIOCGWINSZ: args->cmd = TIOCGWINSZ; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSWINSZ: args->cmd = TIOCSWINSZ; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMGET: args->cmd = TIOCMGET; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMBIS: args->cmd = TIOCMBIS; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMBIC: args->cmd = TIOCMBIC; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMSET: args->cmd = TIOCMSET; error = (ioctl(td, (struct ioctl_args *)args)); break; /* TIOCGSOFTCAR */ /* TIOCSSOFTCAR */ case LINUX_FIONREAD: /* LINUX_TIOCINQ */ args->cmd = FIONREAD; error = (ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_TIOCLINUX */ case LINUX_TIOCCONS: args->cmd = TIOCCONS; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCGSERIAL: { struct linux_serial_struct lss; lss.type = LINUX_PORT_16550A; lss.flags = 0; lss.close_delay = 0; error = copyout(&lss, (void *)args->arg, sizeof(lss)); break; } case LINUX_TIOCSSERIAL: { struct linux_serial_struct lss; error = copyin((void *)args->arg, &lss, sizeof(lss)); if (error) break; /* XXX - It really helps to have an implementation that * does nothing. NOT! */ error = 0; break; } /* LINUX_TIOCPKT */ case LINUX_FIONBIO: args->cmd = FIONBIO; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCNOTTY: args->cmd = TIOCNOTTY; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSETD: { int line; switch (args->arg) { case LINUX_N_TTY: line = TTYDISC; break; case LINUX_N_SLIP: line = SLIPDISC; break; case LINUX_N_PPP: line = PPPDISC; break; default: fdrop(fp, td); return (EINVAL); } error = (fo_ioctl(fp, TIOCSETD, (caddr_t)&line, td->td_ucred, td)); break; } case LINUX_TIOCGETD: { int linux_line; int bsd_line = TTYDISC; error = fo_ioctl(fp, TIOCGETD, (caddr_t)&bsd_line, td->td_ucred, td); if (error) return (error); switch (bsd_line) { case TTYDISC: linux_line = LINUX_N_TTY; break; case SLIPDISC: linux_line = LINUX_N_SLIP; break; case PPPDISC: linux_line = LINUX_N_PPP; break; default: fdrop(fp, td); return (EINVAL); } error = (copyout(&linux_line, (void *)args->arg, sizeof(int))); break; } /* LINUX_TCSBRKP */ /* LINUX_TIOCTTYGSTRUCT */ case LINUX_FIONCLEX: args->cmd = FIONCLEX; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_FIOCLEX: args->cmd = FIOCLEX; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_FIOASYNC: args->cmd = FIOASYNC; error = (ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_TIOCSERCONFIG */ /* LINUX_TIOCSERGWILD */ /* LINUX_TIOCSERSWILD */ /* LINUX_TIOCGLCKTRMIOS */ /* LINUX_TIOCSLCKTRMIOS */ default: error = ENOIOCTL; break; } fdrop(fp, td); return (error); } /* * CDROM related ioctls */ struct linux_cdrom_msf { u_char cdmsf_min0; u_char cdmsf_sec0; u_char cdmsf_frame0; u_char cdmsf_min1; u_char cdmsf_sec1; u_char cdmsf_frame1; }; struct linux_cdrom_tochdr { u_char cdth_trk0; u_char cdth_trk1; }; union linux_cdrom_addr { struct { u_char minute; u_char second; u_char frame; } msf; int lba; }; struct linux_cdrom_tocentry { u_char cdte_track; u_char cdte_adr:4; u_char cdte_ctrl:4; u_char cdte_format; union linux_cdrom_addr cdte_addr; u_char cdte_datamode; }; struct linux_cdrom_subchnl { u_char cdsc_format; u_char cdsc_audiostatus; u_char cdsc_adr:4; u_char cdsc_ctrl:4; u_char cdsc_trk; u_char cdsc_ind; union linux_cdrom_addr cdsc_absaddr; union linux_cdrom_addr cdsc_reladdr; }; struct l_cdrom_read_audio { union linux_cdrom_addr addr; u_char addr_format; l_int nframes; u_char *buf; }; struct l_dvd_layer { u_char book_version:4; u_char book_type:4; u_char min_rate:4; u_char disc_size:4; u_char layer_type:4; u_char track_path:1; u_char nlayers:2; u_char track_density:4; u_char linear_density:4; u_char bca:1; u_int32_t start_sector; u_int32_t end_sector; u_int32_t end_sector_l0; }; struct l_dvd_physical { u_char type; u_char layer_num; struct l_dvd_layer layer[4]; }; struct l_dvd_copyright { u_char type; u_char layer_num; u_char cpst; u_char rmi; }; struct l_dvd_disckey { u_char type; l_uint agid:2; u_char value[2048]; }; struct l_dvd_bca { u_char type; l_int len; u_char value[188]; }; struct l_dvd_manufact { u_char type; u_char layer_num; l_int len; u_char value[2048]; }; typedef union { u_char type; struct l_dvd_physical physical; struct l_dvd_copyright copyright; struct l_dvd_disckey disckey; struct l_dvd_bca bca; struct l_dvd_manufact manufact; } l_dvd_struct; typedef u_char l_dvd_key[5]; typedef u_char l_dvd_challenge[10]; struct l_dvd_lu_send_agid { u_char type; l_uint agid:2; }; struct l_dvd_host_send_challenge { u_char type; l_uint agid:2; l_dvd_challenge chal; }; struct l_dvd_send_key { u_char type; l_uint agid:2; l_dvd_key key; }; struct l_dvd_lu_send_challenge { u_char type; l_uint agid:2; l_dvd_challenge chal; }; struct l_dvd_lu_send_title_key { u_char type; l_uint agid:2; l_dvd_key title_key; l_int lba; l_uint cpm:1; l_uint cp_sec:1; l_uint cgms:2; }; struct l_dvd_lu_send_asf { u_char type; l_uint agid:2; l_uint asf:1; }; struct l_dvd_host_send_rpcstate { u_char type; u_char pdrc; }; struct l_dvd_lu_send_rpcstate { u_char type:2; u_char vra:3; u_char ucca:3; u_char region_mask; u_char rpc_scheme; }; typedef union { u_char type; struct l_dvd_lu_send_agid lsa; struct l_dvd_host_send_challenge hsc; struct l_dvd_send_key lsk; struct l_dvd_lu_send_challenge lsc; struct l_dvd_send_key hsk; struct l_dvd_lu_send_title_key lstk; struct l_dvd_lu_send_asf lsasf; struct l_dvd_host_send_rpcstate hrpcs; struct l_dvd_lu_send_rpcstate lrpcs; } l_dvd_authinfo; static void bsd_to_linux_msf_lba(u_char af, union msf_lba *bp, union linux_cdrom_addr *lp) { if (af == CD_LBA_FORMAT) lp->lba = bp->lba; else { lp->msf.minute = bp->msf.minute; lp->msf.second = bp->msf.second; lp->msf.frame = bp->msf.frame; } } static void linux_to_bsd_msf_lba(u_char af, union linux_cdrom_addr *lp, union msf_lba *bp) { if (af == CD_LBA_FORMAT) bp->lba = lp->lba; else { bp->msf.minute = lp->msf.minute; bp->msf.second = lp->msf.second; bp->msf.frame = lp->msf.frame; } } static void set_linux_cdrom_addr(union linux_cdrom_addr *addr, int format, int lba) { if (format == LINUX_CDROM_MSF) { addr->msf.frame = lba % 75; lba /= 75; lba += 2; addr->msf.second = lba % 60; addr->msf.minute = lba / 60; } else addr->lba = lba; } static int linux_to_bsd_dvd_struct(l_dvd_struct *lp, struct dvd_struct *bp) { bp->format = lp->type; switch (bp->format) { case DVD_STRUCT_PHYSICAL: if (bp->layer_num >= 4) return (EINVAL); bp->layer_num = lp->physical.layer_num; break; case DVD_STRUCT_COPYRIGHT: bp->layer_num = lp->copyright.layer_num; break; case DVD_STRUCT_DISCKEY: bp->agid = lp->disckey.agid; break; case DVD_STRUCT_BCA: case DVD_STRUCT_MANUFACT: break; default: return (EINVAL); } return (0); } static int bsd_to_linux_dvd_struct(struct dvd_struct *bp, l_dvd_struct *lp) { switch (bp->format) { case DVD_STRUCT_PHYSICAL: { struct dvd_layer *blp = (struct dvd_layer *)bp->data; struct l_dvd_layer *llp = &lp->physical.layer[bp->layer_num]; memset(llp, 0, sizeof(*llp)); llp->book_version = blp->book_version; llp->book_type = blp->book_type; llp->min_rate = blp->max_rate; llp->disc_size = blp->disc_size; llp->layer_type = blp->layer_type; llp->track_path = blp->track_path; llp->nlayers = blp->nlayers; llp->track_density = blp->track_density; llp->linear_density = blp->linear_density; llp->bca = blp->bca; llp->start_sector = blp->start_sector; llp->end_sector = blp->end_sector; llp->end_sector_l0 = blp->end_sector_l0; break; } case DVD_STRUCT_COPYRIGHT: lp->copyright.cpst = bp->cpst; lp->copyright.rmi = bp->rmi; break; case DVD_STRUCT_DISCKEY: memcpy(lp->disckey.value, bp->data, sizeof(lp->disckey.value)); break; case DVD_STRUCT_BCA: lp->bca.len = bp->length; memcpy(lp->bca.value, bp->data, sizeof(lp->bca.value)); break; case DVD_STRUCT_MANUFACT: lp->manufact.len = bp->length; memcpy(lp->manufact.value, bp->data, sizeof(lp->manufact.value)); /* lp->manufact.layer_num is unused in linux (redhat 7.0) */ break; default: return (EINVAL); } return (0); } static int linux_to_bsd_dvd_authinfo(l_dvd_authinfo *lp, int *bcode, struct dvd_authinfo *bp) { switch (lp->type) { case LINUX_DVD_LU_SEND_AGID: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_AGID; bp->agid = lp->lsa.agid; break; case LINUX_DVD_HOST_SEND_CHALLENGE: *bcode = DVDIOCSENDKEY; bp->format = DVD_SEND_CHALLENGE; bp->agid = lp->hsc.agid; memcpy(bp->keychal, lp->hsc.chal, 10); break; case LINUX_DVD_LU_SEND_KEY1: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_KEY1; bp->agid = lp->lsk.agid; break; case LINUX_DVD_LU_SEND_CHALLENGE: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_CHALLENGE; bp->agid = lp->lsc.agid; break; case LINUX_DVD_HOST_SEND_KEY2: *bcode = DVDIOCSENDKEY; bp->format = DVD_SEND_KEY2; bp->agid = lp->hsk.agid; memcpy(bp->keychal, lp->hsk.key, 5); break; case LINUX_DVD_LU_SEND_TITLE_KEY: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_TITLE_KEY; bp->agid = lp->lstk.agid; bp->lba = lp->lstk.lba; break; case LINUX_DVD_LU_SEND_ASF: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_ASF; bp->agid = lp->lsasf.agid; break; case LINUX_DVD_INVALIDATE_AGID: *bcode = DVDIOCREPORTKEY; bp->format = DVD_INVALIDATE_AGID; bp->agid = lp->lsa.agid; break; case LINUX_DVD_LU_SEND_RPC_STATE: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_RPC; break; case LINUX_DVD_HOST_SEND_RPC_STATE: *bcode = DVDIOCSENDKEY; bp->format = DVD_SEND_RPC; bp->region = lp->hrpcs.pdrc; break; default: return (EINVAL); } return (0); } static int bsd_to_linux_dvd_authinfo(struct dvd_authinfo *bp, l_dvd_authinfo *lp) { switch (lp->type) { case LINUX_DVD_LU_SEND_AGID: lp->lsa.agid = bp->agid; break; case LINUX_DVD_HOST_SEND_CHALLENGE: lp->type = LINUX_DVD_LU_SEND_KEY1; break; case LINUX_DVD_LU_SEND_KEY1: memcpy(lp->lsk.key, bp->keychal, sizeof(lp->lsk.key)); break; case LINUX_DVD_LU_SEND_CHALLENGE: memcpy(lp->lsc.chal, bp->keychal, sizeof(lp->lsc.chal)); break; case LINUX_DVD_HOST_SEND_KEY2: lp->type = LINUX_DVD_AUTH_ESTABLISHED; break; case LINUX_DVD_LU_SEND_TITLE_KEY: memcpy(lp->lstk.title_key, bp->keychal, sizeof(lp->lstk.title_key)); lp->lstk.cpm = bp->cpm; lp->lstk.cp_sec = bp->cp_sec; lp->lstk.cgms = bp->cgms; break; case LINUX_DVD_LU_SEND_ASF: lp->lsasf.asf = bp->asf; break; case LINUX_DVD_INVALIDATE_AGID: break; case LINUX_DVD_LU_SEND_RPC_STATE: lp->lrpcs.type = bp->reg_type; lp->lrpcs.vra = bp->vend_rsts; lp->lrpcs.ucca = bp->user_rsts; lp->lrpcs.region_mask = bp->region; lp->lrpcs.rpc_scheme = bp->rpc_scheme; break; case LINUX_DVD_HOST_SEND_RPC_STATE: break; default: return (EINVAL); } return (0); } static int linux_ioctl_cdrom(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; if ((error = fget(td, args->fd, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_CDROMPAUSE: args->cmd = CDIOCPAUSE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMRESUME: args->cmd = CDIOCRESUME; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMPLAYMSF: args->cmd = CDIOCPLAYMSF; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMPLAYTRKIND: args->cmd = CDIOCPLAYTRACKS; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMREADTOCHDR: { struct ioc_toc_header th; struct linux_cdrom_tochdr lth; error = fo_ioctl(fp, CDIOREADTOCHEADER, (caddr_t)&th, td->td_ucred, td); if (!error) { lth.cdth_trk0 = th.starting_track; lth.cdth_trk1 = th.ending_track; copyout(<h, (void *)args->arg, sizeof(lth)); } break; } case LINUX_CDROMREADTOCENTRY: { struct linux_cdrom_tocentry lte, *ltep = (struct linux_cdrom_tocentry *)args->arg; struct ioc_read_toc_single_entry irtse; irtse.address_format = ltep->cdte_format; irtse.track = ltep->cdte_track; error = fo_ioctl(fp, CDIOREADTOCENTRY, (caddr_t)&irtse, td->td_ucred, td); if (!error) { lte = *ltep; lte.cdte_ctrl = irtse.entry.control; lte.cdte_adr = irtse.entry.addr_type; bsd_to_linux_msf_lba(irtse.address_format, &irtse.entry.addr, <e.cdte_addr); copyout(<e, (void *)args->arg, sizeof(lte)); } break; } case LINUX_CDROMSTOP: args->cmd = CDIOCSTOP; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMSTART: args->cmd = CDIOCSTART; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMEJECT: args->cmd = CDIOCEJECT; error = (ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_CDROMVOLCTRL */ case LINUX_CDROMSUBCHNL: { struct linux_cdrom_subchnl sc; struct ioc_read_subchannel bsdsc; struct cd_sub_channel_info *bsdinfo; caddr_t sg = stackgap_init(); bsdinfo = stackgap_alloc(&sg, sizeof(*bsdinfo)); bsdsc.address_format = CD_LBA_FORMAT; bsdsc.data_format = CD_CURRENT_POSITION; bsdsc.track = 0; bsdsc.data_len = sizeof(*bsdinfo); bsdsc.data = bsdinfo; error = fo_ioctl(fp, CDIOCREADSUBCHANNEL, (caddr_t)&bsdsc, td->td_ucred, td); if (error) break; error = copyin((void *)args->arg, &sc, sizeof(sc)); if (error) break; sc.cdsc_audiostatus = bsdinfo->header.audio_status; sc.cdsc_adr = bsdinfo->what.position.addr_type; sc.cdsc_ctrl = bsdinfo->what.position.control; sc.cdsc_trk = bsdinfo->what.position.track_number; sc.cdsc_ind = bsdinfo->what.position.index_number; set_linux_cdrom_addr(&sc.cdsc_absaddr, sc.cdsc_format, bsdinfo->what.position.absaddr.lba); set_linux_cdrom_addr(&sc.cdsc_reladdr, sc.cdsc_format, bsdinfo->what.position.reladdr.lba); error = copyout(&sc, (void *)args->arg, sizeof(sc)); break; } /* LINUX_CDROMREADMODE2 */ /* LINUX_CDROMREADMODE1 */ case LINUX_CDROMREADAUDIO: { struct l_cdrom_read_audio lra; struct ioc_read_audio bra; error = copyin((void *)args->arg, &lra, sizeof(lra)); if (error) break; bra.address_format = lra.addr_format; linux_to_bsd_msf_lba(bra.address_format, &lra.addr, &bra.address); bra.nframes = lra.nframes; bra.buffer = lra.buf; error = fo_ioctl(fp, CDIOCREADAUDIO, (caddr_t)&bra, td->td_ucred, td); break; } /* LINUX_CDROMEJECT_SW */ /* LINUX_CDROMMULTISESSION */ /* LINUX_CDROM_GET_UPC */ case LINUX_CDROMRESET: args->cmd = CDIOCRESET; error = (ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_CDROMVOLREAD */ /* LINUX_CDROMREADRAW */ /* LINUX_CDROMREADCOOKED */ /* LINUX_CDROMSEEK */ /* LINUX_CDROMPLAYBLK */ /* LINUX_CDROMREADALL */ /* LINUX_CDROMCLOSETRAY */ /* LINUX_CDROMLOADFROMSLOT */ /* LINUX_CDROMGETSPINDOWN */ /* LINUX_CDROMSETSPINDOWN */ /* LINUX_CDROM_SET_OPTIONS */ /* LINUX_CDROM_CLEAR_OPTIONS */ /* LINUX_CDROM_SELECT_SPEED */ /* LINUX_CDROM_SELECT_DISC */ /* LINUX_CDROM_MEDIA_CHANGED */ /* LINUX_CDROM_DRIVE_STATUS */ /* LINUX_CDROM_DISC_STATUS */ /* LINUX_CDROM_CHANGER_NSLOTS */ /* LINUX_CDROM_LOCKDOOR */ /* LINUX_CDROM_DEBUG */ /* LINUX_CDROM_GET_CAPABILITY */ /* LINUX_CDROMAUDIOBUFSIZ */ case LINUX_DVD_READ_STRUCT: { l_dvd_struct lds; struct dvd_struct bds; error = copyin((void *)args->arg, &lds, sizeof(lds)); if (error) break; error = linux_to_bsd_dvd_struct(&lds, &bds); if (error) break; error = fo_ioctl(fp, DVDIOCREADSTRUCTURE, (caddr_t)&bds, td->td_ucred, td); if (error) break; error = bsd_to_linux_dvd_struct(&bds, &lds); if (error) break; error = copyout(&lds, (void *)args->arg, sizeof(lds)); break; } /* LINUX_DVD_WRITE_STRUCT */ case LINUX_DVD_AUTH: { l_dvd_authinfo lda; struct dvd_authinfo bda; int bcode; error = copyin((void *)args->arg, &lda, sizeof(lda)); if (error) break; error = linux_to_bsd_dvd_authinfo(&lda, &bcode, &bda); if (error) break; error = fo_ioctl(fp, bcode, (caddr_t)&bda, td->td_ucred, td); if (error) { if (lda.type == LINUX_DVD_HOST_SEND_KEY2) { lda.type = LINUX_DVD_AUTH_FAILURE; copyout(&lda, (void *)args->arg, sizeof(lda)); } break; } error = bsd_to_linux_dvd_authinfo(&bda, &lda); if (error) break; error = copyout(&lda, (void *)args->arg, sizeof(lda)); break; } /* LINUX_CDROM_SEND_PACKET */ /* LINUX_CDROM_NEXT_WRITABLE */ /* LINUX_CDROM_LAST_WRITTEN */ default: error = ENOIOCTL; break; } fdrop(fp, td); return (error); } static int linux_ioctl_vfat(struct thread *td, struct linux_ioctl_args *args) { return (ENOTTY); } /* * Sound related ioctls */ static u_int32_t dirbits[4] = { IOC_VOID, IOC_IN, IOC_OUT, IOC_INOUT }; #define SETDIR(c) (((c) & ~IOC_DIRMASK) | dirbits[args->cmd >> 30]) static int linux_ioctl_sound(struct thread *td, struct linux_ioctl_args *args) { switch (args->cmd & 0xffff) { case LINUX_SOUND_MIXER_WRITE_VOLUME: args->cmd = SETDIR(SOUND_MIXER_WRITE_VOLUME); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_BASS: args->cmd = SETDIR(SOUND_MIXER_WRITE_BASS); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_TREBLE: args->cmd = SETDIR(SOUND_MIXER_WRITE_TREBLE); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_SYNTH: args->cmd = SETDIR(SOUND_MIXER_WRITE_SYNTH); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_PCM: args->cmd = SETDIR(SOUND_MIXER_WRITE_PCM); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_SPEAKER: args->cmd = SETDIR(SOUND_MIXER_WRITE_SPEAKER); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_MIC: args->cmd = SETDIR(SOUND_MIXER_WRITE_MIC); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_CD: args->cmd = SETDIR(SOUND_MIXER_WRITE_CD); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_IMIX: args->cmd = SETDIR(SOUND_MIXER_WRITE_IMIX); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_ALTPCM: args->cmd = SETDIR(SOUND_MIXER_WRITE_ALTPCM); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_RECLEV: args->cmd = SETDIR(SOUND_MIXER_WRITE_RECLEV); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_IGAIN: args->cmd = SETDIR(SOUND_MIXER_WRITE_IGAIN); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_OGAIN: args->cmd = SETDIR(SOUND_MIXER_WRITE_OGAIN); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE1: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE1); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE2: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE2); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE3: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE3); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_OSS_GETVERSION: { int version = linux_get_oss_version(td); return (copyout(&version, (void *)args->arg, sizeof(int))); } case LINUX_SOUND_MIXER_READ_STEREODEVS: args->cmd = SOUND_MIXER_READ_STEREODEVS; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_READ_DEVMASK: args->cmd = SOUND_MIXER_READ_DEVMASK; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_RECSRC: args->cmd = SETDIR(SOUND_MIXER_WRITE_RECSRC); return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_RESET: args->cmd = SNDCTL_DSP_RESET; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SYNC: args->cmd = SNDCTL_DSP_SYNC; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SPEED: args->cmd = SNDCTL_DSP_SPEED; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_STEREO: args->cmd = SNDCTL_DSP_STEREO; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETBLKSIZE: /* LINUX_SNDCTL_DSP_SETBLKSIZE */ args->cmd = SNDCTL_DSP_GETBLKSIZE; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETFMT: args->cmd = SNDCTL_DSP_SETFMT; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_PCM_WRITE_CHANNELS: args->cmd = SOUND_PCM_WRITE_CHANNELS; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_PCM_WRITE_FILTER: args->cmd = SOUND_PCM_WRITE_FILTER; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_POST: args->cmd = SNDCTL_DSP_POST; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SUBDIVIDE: args->cmd = SNDCTL_DSP_SUBDIVIDE; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETFRAGMENT: args->cmd = SNDCTL_DSP_SETFRAGMENT; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETFMTS: args->cmd = SNDCTL_DSP_GETFMTS; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETOSPACE: args->cmd = SNDCTL_DSP_GETOSPACE; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETISPACE: args->cmd = SNDCTL_DSP_GETISPACE; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_NONBLOCK: args->cmd = SNDCTL_DSP_NONBLOCK; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETCAPS: args->cmd = SNDCTL_DSP_GETCAPS; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETTRIGGER: /* LINUX_SNDCTL_GETTRIGGER */ args->cmd = SNDCTL_DSP_SETTRIGGER; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETIPTR: args->cmd = SNDCTL_DSP_GETIPTR; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETOPTR: args->cmd = SNDCTL_DSP_GETOPTR; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETODELAY: args->cmd = SNDCTL_DSP_GETODELAY; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_RESET: args->cmd = SNDCTL_SEQ_RESET; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_SYNC: args->cmd = SNDCTL_SEQ_SYNC; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SYNTH_INFO: args->cmd = SNDCTL_SYNTH_INFO; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_CTRLRATE: args->cmd = SNDCTL_SEQ_CTRLRATE; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_GETOUTCOUNT: args->cmd = SNDCTL_SEQ_GETOUTCOUNT; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_GETINCOUNT: args->cmd = SNDCTL_SEQ_GETINCOUNT; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_PERCMODE: args->cmd = SNDCTL_SEQ_PERCMODE; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_FM_LOAD_INSTR: args->cmd = SNDCTL_FM_LOAD_INSTR; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_TESTMIDI: args->cmd = SNDCTL_SEQ_TESTMIDI; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_RESETSAMPLES: args->cmd = SNDCTL_SEQ_RESETSAMPLES; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_NRSYNTHS: args->cmd = SNDCTL_SEQ_NRSYNTHS; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_NRMIDIS: args->cmd = SNDCTL_SEQ_NRMIDIS; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_MIDI_INFO: args->cmd = SNDCTL_MIDI_INFO; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_TRESHOLD: args->cmd = SNDCTL_SEQ_TRESHOLD; return (ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SYNTH_MEMAVL: args->cmd = SNDCTL_SYNTH_MEMAVL; return (ioctl(td, (struct ioctl_args *)args)); } return (ENOIOCTL); } /* * Console related ioctls */ #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) static int linux_ioctl_console(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; if ((error = fget(td, args->fd, &fp)) != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_KIOCSOUND: args->cmd = KIOCSOUND; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDMKTONE: args->cmd = KDMKTONE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDGETLED: args->cmd = KDGETLED; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDSETLED: args->cmd = KDSETLED; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDSETMODE: args->cmd = KDSETMODE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDGETMODE: args->cmd = KDGETMODE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDGKBMODE: args->cmd = KDGKBMODE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDSKBMODE: { int kbdmode; switch (args->arg) { case LINUX_KBD_RAW: kbdmode = K_RAW; break; case LINUX_KBD_XLATE: kbdmode = K_XLATE; break; case LINUX_KBD_MEDIUMRAW: kbdmode = K_RAW; break; default: fdrop(fp, td); return (EINVAL); } error = (fo_ioctl(fp, KDSKBMODE, (caddr_t)&kbdmode, td->td_ucred, td)); break; } case LINUX_VT_OPENQRY: args->cmd = VT_OPENQRY; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_GETMODE: args->cmd = VT_GETMODE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_SETMODE: { struct vt_mode *mode; args->cmd = VT_SETMODE; mode = (struct vt_mode *)args->arg; if (!ISSIGVALID(mode->frsig) && ISSIGVALID(mode->acqsig)) mode->frsig = mode->acqsig; error = (ioctl(td, (struct ioctl_args *)args)); break; } case LINUX_VT_GETSTATE: args->cmd = VT_GETACTIVE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_RELDISP: args->cmd = VT_RELDISP; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_ACTIVATE: args->cmd = VT_ACTIVATE; error = (ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_WAITACTIVE: args->cmd = VT_WAITACTIVE; error = (ioctl(td, (struct ioctl_args *)args)); break; default: error = ENOIOCTL; break; } fdrop(fp, td); return (error); } /* * Criteria for interface name translation */ #define IFP_IS_ETH(ifp) (ifp->if_type == IFT_ETHER) /* * Interface function used by linprocfs (at the time of writing). It's not * used by the Linuxulator itself. */ int linux_ifname(struct ifnet *ifp, char *buffer, size_t buflen) { struct ifnet *ifscan; int ethno; /* Short-circuit non ethernet interfaces */ if (!IFP_IS_ETH(ifp)) return (snprintf(buffer, buflen, "%s%d", ifp->if_name, ifp->if_unit)); /* Determine the (relative) unit number for ethernet interfaces */ ethno = 0; IFNET_RLOCK(); TAILQ_FOREACH(ifscan, &ifnet, if_link) { if (ifscan == ifp) { IFNET_RUNLOCK(); return (snprintf(buffer, buflen, "eth%d", ethno)); } if (IFP_IS_ETH(ifscan)) ethno++; } IFNET_RUNLOCK(); return (0); } /* * Translate a Linux interface name to a FreeBSD interface name, * and return the associated ifnet structure * bsdname and lxname need to be least IFNAMSIZ bytes long, but * can point to the same buffer. */ static struct ifnet * ifname_linux_to_bsd(const char *lxname, char *bsdname) { struct ifnet *ifp; int len, unit; char *ep; int is_eth, index; for (len = 0; len < LINUX_IFNAMSIZ; ++len) if (!isalpha(lxname[len])) break; if (len == 0 || len == LINUX_IFNAMSIZ) return (NULL); unit = (int)strtoul(lxname + len, &ep, 10); if (ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) return (NULL); index = 0; is_eth = (len == 3 && !strncmp(lxname, "eth", len)) ? 1 : 0; IFNET_RLOCK(); TAILQ_FOREACH(ifp, &ifnet, if_link) { /* * Allow Linux programs to use FreeBSD names. Don't presume * we never have an interface named "eth", so don't make * the test optional based on is_eth. */ if (ifp->if_unit == unit && ifp->if_name[len] == '\0' && strncmp(ifp->if_name, lxname, len) == 0) break; if (is_eth && IFP_IS_ETH(ifp) && unit == index++) break; } IFNET_RUNLOCK(); if (ifp != NULL) snprintf(bsdname, IFNAMSIZ, "%s%d", ifp->if_name, ifp->if_unit); return (ifp); } /* * Implement the SIOCGIFCONF ioctl */ static int linux_ifconf(struct thread *td, struct ifconf *uifc) { struct ifconf ifc; struct l_ifreq ifr; struct ifnet *ifp; struct ifaddr *ifa; struct iovec iov; struct uio uio; int error, ethno; error = copyin(uifc, &ifc, sizeof(ifc)); if (error != 0) return (error); /* much easier to use uiomove than keep track ourselves */ iov.iov_base = ifc.ifc_buf; iov.iov_len = ifc.ifc_len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = ifc.ifc_len; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = UIO_READ; uio.uio_td = td; /* Keep track of eth interfaces */ ethno = 0; /* Return all AF_INET addresses of all interfaces */ IFNET_RLOCK(); /* could sleep XXX */ TAILQ_FOREACH(ifp, &ifnet, if_link) { if (uio.uio_resid <= 0) break; bzero(&ifr, sizeof(ifr)); if (IFP_IS_ETH(ifp)) snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d", ethno++); else snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "%s%d", ifp->if_name, ifp->if_unit); /* Walk the address list */ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct sockaddr *sa = ifa->ifa_addr; if (uio.uio_resid <= 0) break; if (sa->sa_family == AF_INET) { ifr.ifr_addr.sa_family = LINUX_AF_INET; memcpy(ifr.ifr_addr.sa_data, sa->sa_data, sizeof(ifr.ifr_addr.sa_data)); error = uiomove(&ifr, sizeof(ifr), &uio); if (error != 0) { IFNET_RUNLOCK(); return (error); } } } } IFNET_RUNLOCK(); ifc.ifc_len -= uio.uio_resid; error = copyout(&ifc, uifc, sizeof(ifc)); return (error); } static int linux_gifflags(struct thread *td, struct ifnet *ifp, struct l_ifreq *ifr) { l_short flags; flags = ifp->if_flags & 0xffff; /* these flags have no Linux equivalent */ flags &= ~(IFF_SMART|IFF_OACTIVE|IFF_SIMPLEX| IFF_LINK0|IFF_LINK1|IFF_LINK2); /* Linux' multicast flag is in a different bit */ if (flags & IFF_MULTICAST) { flags &= ~IFF_MULTICAST; flags |= 0x1000; } return (copyout(&flags, &ifr->ifr_flags, sizeof(flags))); } #define ARPHRD_ETHER 1 #define ARPHRD_LOOPBACK 772 static int linux_gifhwaddr(struct ifnet *ifp, struct l_ifreq *ifr) { struct ifaddr *ifa; struct sockaddr_dl *sdl; struct l_sockaddr lsa; if (ifp->if_type == IFT_LOOP) { bzero(&lsa, sizeof(lsa)); lsa.sa_family = ARPHRD_LOOPBACK; return (copyout(&lsa, &ifr->ifr_hwaddr, sizeof(lsa))); } if (ifp->if_type != IFT_ETHER) return (ENOENT); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { sdl = (struct sockaddr_dl*)ifa->ifa_addr; if (sdl != NULL && (sdl->sdl_family == AF_LINK) && (sdl->sdl_type == IFT_ETHER)) { bzero(&lsa, sizeof(lsa)); lsa.sa_family = ARPHRD_ETHER; bcopy(LLADDR(sdl), lsa.sa_data, LINUX_IFHWADDRLEN); return (copyout(&lsa, &ifr->ifr_hwaddr, sizeof(lsa))); } } return (ENOENT); } /* * Socket related ioctls */ static int linux_ioctl_socket(struct thread *td, struct linux_ioctl_args *args) { char lifname[LINUX_IFNAMSIZ], ifname[IFNAMSIZ]; struct ifnet *ifp; struct file *fp; int error, type; KASSERT(LINUX_IFNAMSIZ == IFNAMSIZ, ("%s(): LINUX_IFNAMSIZ != IFNAMSIZ", __func__)); ifp = NULL; error = 0; if ((error = fget(td, args->fd, &fp)) != 0) return (error); type = fp->f_type; fdrop(fp, td); if (type != DTYPE_SOCKET) { /* not a socket - probably a tap / vmnet device */ switch (args->cmd) { case LINUX_SIOCGIFADDR: case LINUX_SIOCSIFADDR: case LINUX_SIOCGIFFLAGS: return (linux_ioctl_special(td, args)); default: return (ENOIOCTL); } } switch (args->cmd & 0xffff) { case LINUX_FIOGETOWN: case LINUX_FIOSETOWN: case LINUX_SIOCADDMULTI: case LINUX_SIOCATMARK: case LINUX_SIOCDELMULTI: case LINUX_SIOCGIFCONF: case LINUX_SIOCGPGRP: case LINUX_SIOCSPGRP: /* these ioctls don't take an interface name */ #ifdef DEBUG printf("%s(): ioctl %d\n", __func__, args->cmd & 0xffff); #endif break; case LINUX_SIOCGIFFLAGS: case LINUX_SIOCGIFADDR: case LINUX_SIOCSIFADDR: case LINUX_SIOCGIFDSTADDR: case LINUX_SIOCGIFBRDADDR: case LINUX_SIOCGIFNETMASK: case LINUX_SIOCSIFNETMASK: case LINUX_SIOCGIFMTU: case LINUX_SIOCSIFMTU: case LINUX_SIOCSIFNAME: case LINUX_SIOCGIFHWADDR: case LINUX_SIOCSIFHWADDR: case LINUX_SIOCDEVPRIVATE: case LINUX_SIOCDEVPRIVATE+1: /* copy in the interface name and translate it. */ error = copyin((void *)args->arg, lifname, LINUX_IFNAMSIZ); if (error != 0) return (error); #ifdef DEBUG printf("%s(): ioctl %d on %.*s\n", __func__, args->cmd & 0xffff, LINUX_IFNAMSIZ, lifname); #endif ifp = ifname_linux_to_bsd(lifname, ifname); if (ifp == NULL) return (EINVAL); /* * We need to copy it back out in case we pass the * request on to our native ioctl(), which will expect * the ifreq to be in user space and have the correct * interface name. */ error = copyout(ifname, (void *)args->arg, IFNAMSIZ); if (error != 0) return (error); #ifdef DEBUG printf("%s(): %s translated to %s\n", __func__, lifname, ifname); #endif break; default: return (ENOIOCTL); } switch (args->cmd & 0xffff) { case LINUX_FIOSETOWN: args->cmd = FIOSETOWN; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSPGRP: args->cmd = SIOCSPGRP; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_FIOGETOWN: args->cmd = FIOGETOWN; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGPGRP: args->cmd = SIOCGPGRP; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCATMARK: args->cmd = SIOCATMARK; error = ioctl(td, (struct ioctl_args *)args); break; /* LINUX_SIOCGSTAMP */ case LINUX_SIOCGIFCONF: error = linux_ifconf(td, (struct ifconf *)args->arg); break; case LINUX_SIOCGIFFLAGS: args->cmd = SIOCGIFFLAGS; error = linux_gifflags(td, ifp, (struct l_ifreq *)args->arg); break; case LINUX_SIOCGIFADDR: args->cmd = OSIOCGIFADDR; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFADDR: /* XXX probably doesn't work, included for completeness */ args->cmd = SIOCSIFADDR; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFDSTADDR: args->cmd = OSIOCGIFDSTADDR; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFBRDADDR: args->cmd = OSIOCGIFBRDADDR; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFNETMASK: args->cmd = OSIOCGIFNETMASK; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFNETMASK: error = ENOIOCTL; break; case LINUX_SIOCGIFMTU: args->cmd = SIOCGIFMTU; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFMTU: args->cmd = SIOCSIFMTU; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFNAME: error = ENOIOCTL; break; case LINUX_SIOCGIFHWADDR: error = linux_gifhwaddr(ifp, (struct l_ifreq *)args->arg); break; case LINUX_SIOCSIFHWADDR: error = ENOIOCTL; break; case LINUX_SIOCADDMULTI: args->cmd = SIOCADDMULTI; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCDELMULTI: args->cmd = SIOCDELMULTI; error = ioctl(td, (struct ioctl_args *)args); break; /* * XXX This is slightly bogus, but these ioctls are currently * XXX only used by the aironet (if_an) network driver. */ case LINUX_SIOCDEVPRIVATE: args->cmd = SIOCGPRIVATE_0; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCDEVPRIVATE+1: args->cmd = SIOCGPRIVATE_1; error = ioctl(td, (struct ioctl_args *)args); break; } if (ifp != NULL) /* restore the original interface name */ copyout(lifname, (void *)args->arg, LINUX_IFNAMSIZ); #ifdef DEBUG printf("%s(): returning %d\n", __func__, error); #endif return (error); } /* * Device private ioctl handler */ static int linux_ioctl_private(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error, type; if ((error = fget(td, args->fd, &fp)) != 0) return (error); type = fp->f_type; fdrop(fp, td); if (type == DTYPE_SOCKET) return (linux_ioctl_socket(td, args)); return (ENOIOCTL); } /* * Special ioctl handler */ static int linux_ioctl_special(struct thread *td, struct linux_ioctl_args *args) { int error; switch (args->cmd) { case LINUX_SIOCGIFADDR: args->cmd = SIOCGIFADDR; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFADDR: args->cmd = SIOCSIFADDR; error = ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFFLAGS: args->cmd = SIOCGIFFLAGS; error = ioctl(td, (struct ioctl_args *)args); break; default: error = ENOIOCTL; } return (error); } /* * main ioctl syscall function */ int linux_ioctl(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; struct handler_element *he; int error, cmd; #ifdef DEBUG if (ldebug(ioctl)) printf(ARGS(ioctl, "%d, %04lx, *"), args->fd, (unsigned long)args->cmd); #endif if ((error = fget(td, args->fd, &fp)) != 0) return (error); if ((fp->f_flag & (FREAD|FWRITE)) == 0) { fdrop(fp, td); return (EBADF); } /* Iterate over the ioctl handlers */ cmd = args->cmd & 0xffff; TAILQ_FOREACH(he, &handlers, list) { if (cmd >= he->low && cmd <= he->high) { error = (*he->func)(td, args); if (error != ENOIOCTL) { fdrop(fp, td); return (error); } } } fdrop(fp, td); linux_msg(td, "ioctl fd=%d, cmd=0x%x ('%c',%d) is not implemented", args->fd, (int)(args->cmd & 0xffff), (int)(args->cmd & 0xff00) >> 8, (int)(args->cmd & 0xff)); return (EINVAL); } int linux_ioctl_register_handler(struct linux_ioctl_handler *h) { struct handler_element *he, *cur; if (h == NULL || h->func == NULL) return (EINVAL); /* * Reuse the element if the handler is already on the list, otherwise * create a new element. */ TAILQ_FOREACH(he, &handlers, list) { if (he->func == h->func) break; } if (he == NULL) { MALLOC(he, struct handler_element *, sizeof(*he), M_LINUX, M_WAITOK); he->func = h->func; } else TAILQ_REMOVE(&handlers, he, list); /* Initialize range information. */ he->low = h->low; he->high = h->high; he->span = h->high - h->low + 1; /* Add the element to the list, sorted on span. */ TAILQ_FOREACH(cur, &handlers, list) { if (cur->span > he->span) { TAILQ_INSERT_BEFORE(cur, he, list); return (0); } } TAILQ_INSERT_TAIL(&handlers, he, list); return (0); } int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h) { struct handler_element *he; if (h == NULL || h->func == NULL) return (EINVAL); TAILQ_FOREACH(he, &handlers, list) { if (he->func == h->func) { TAILQ_REMOVE(&handlers, he, list); FREE(he, M_LINUX); return (0); } } return (EINVAL); } Index: head/sys/dev/mcd/mcd_isa.c =================================================================== --- head/sys/dev/mcd/mcd_isa.c (revision 113580) +++ head/sys/dev/mcd/mcd_isa.c (revision 113581) @@ -1,210 +1,209 @@ /* * $FreeBSD$ */ #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include static int mcd_isa_probe (device_t); static int mcd_isa_attach (device_t); static int mcd_isa_detach (device_t); static int mcd_alloc_resources (device_t); static void mcd_release_resources (device_t); static int mcd_isa_probe (device_t dev) { struct mcd_softc * sc; int error; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); /* IO port must be configured. */ if (bus_get_resource_start(dev, SYS_RES_IOPORT, 0) == 0) return (ENXIO); sc = device_get_softc(dev); sc->dev = dev; sc->port_rid = 0; sc->port_type = SYS_RES_IOPORT; error = mcd_alloc_resources(dev); if (error) goto fail; error = mcd_probe(sc); if (error) { device_printf(dev, "Probe failed.\n"); goto fail; } device_set_desc(dev, sc->data.name); fail: mcd_release_resources(dev); return (error); } static int mcd_isa_attach (device_t dev) { struct mcd_softc * sc; int error; sc = device_get_softc(dev); error = 0; sc->dev = dev; sc->port_rid = 0; sc->port_type = SYS_RES_IOPORT; error = mcd_alloc_resources(dev); if (error) goto fail; error = mcd_probe(sc); if (error) { device_printf(dev, "Re-Probe failed.\n"); goto fail; } error = mcd_attach(sc); if (error) { device_printf(dev, "Attach failed.\n"); goto fail; } return (0); fail: mcd_release_resources(dev); return (error); } static int mcd_isa_detach (device_t dev) { struct mcd_softc * sc; int error; sc = device_get_softc(dev); error = 0; destroy_dev(sc->mcd_dev_t); mcd_release_resources(dev); return (error); } static int mcd_alloc_resources (device_t dev) { struct mcd_softc * sc; int error; sc = device_get_softc(dev); error = 0; if (sc->port_type) { sc->port = bus_alloc_resource(dev, sc->port_type, &sc->port_rid, 0, ~0, 1, RF_ACTIVE); if (sc->port == NULL) { device_printf(dev, "Unable to allocate PORT resource.\n"); error = ENOMEM; goto bad; } sc->port_bst = rman_get_bustag(sc->port); sc->port_bsh = rman_get_bushandle(sc->port); } if (sc->irq_type) { sc->irq = bus_alloc_resource(dev, sc->irq_type, &sc->irq_rid, 0, ~0, 1, RF_ACTIVE); if (sc->irq == NULL) { device_printf(dev, "Unable to allocate IRQ resource.\n"); error = ENOMEM; goto bad; } } if (sc->drq_type) { sc->drq = bus_alloc_resource(dev, sc->drq_type, &sc->drq_rid, 0, ~0, 1, RF_ACTIVE); if (sc->drq == NULL) { device_printf(dev, "Unable to allocate DRQ resource.\n"); error = ENOMEM; goto bad; } } mtx_init(&sc->mtx, device_get_nameunit(dev), "Interrupt lock", MTX_DEF | MTX_RECURSE); bad: return (error); } static void mcd_release_resources (device_t dev) { struct mcd_softc * sc; sc = device_get_softc(dev); if (sc->irq_ih) bus_teardown_intr(dev, sc->irq, sc->irq_ih); if (sc->port) { bus_release_resource(dev, sc->port_type, sc->port_rid, sc->port); sc->port_bst = 0; sc->port_bsh = 0; } if (sc->irq) bus_release_resource(dev, sc->irq_type, sc->irq_rid, sc->irq); if (sc->drq) bus_release_resource(dev, sc->drq_type, sc->drq_rid, sc->drq); if (mtx_initialized(&sc->mtx) != 0) mtx_destroy(&sc->mtx); return; } static device_method_t mcd_isa_methods[] = { DEVMETHOD(device_probe, mcd_isa_probe), DEVMETHOD(device_attach, mcd_isa_attach), DEVMETHOD(device_detach, mcd_isa_detach), { 0, 0 } }; static driver_t mcd_isa_driver = { "mcd", mcd_isa_methods, sizeof(struct mcd_softc) }; static devclass_t mcd_devclass; DRIVER_MODULE(mcd, isa, mcd_isa_driver, mcd_devclass, NULL, 0); Index: head/sys/dev/scd/scd_isa.c =================================================================== --- head/sys/dev/scd/scd_isa.c (revision 113580) +++ head/sys/dev/scd/scd_isa.c (revision 113581) @@ -1,183 +1,182 @@ /* * $FreeBSD$ */ #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include static int scd_isa_probe (device_t); static int scd_isa_attach (device_t); static int scd_isa_detach (device_t); static int scd_alloc_resources (device_t); static void scd_release_resources (device_t); static int scd_isa_probe (device_t dev) { struct scd_softc * sc; int error; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); /* IO port must be configured. */ if (bus_get_resource_start(dev, SYS_RES_IOPORT, 0) == 0) return (ENXIO); sc = device_get_softc(dev); sc->dev = dev; sc->port_rid = 0; sc->port_type = SYS_RES_IOPORT; error = scd_alloc_resources(dev); if (error) goto fail; error = scd_probe(sc); if (error) { device_printf(dev, "Probe failed.\n"); goto fail; } device_set_desc(dev, sc->data.name); fail: scd_release_resources(dev); return (error); } static int scd_isa_attach (device_t dev) { struct scd_softc * sc; int error; sc = device_get_softc(dev); error = 0; sc->dev = dev; sc->port_rid = 0; sc->port_type = SYS_RES_IOPORT; error = scd_alloc_resources(dev); if (error) goto fail; error = scd_probe(sc); if (error) { device_printf(dev, "Re-Probe failed.\n"); goto fail; } error = scd_attach(sc); if (error) { device_printf(dev, "Attach failed.\n"); goto fail; } return (0); fail: scd_release_resources(dev); return (error); } static int scd_isa_detach (device_t dev) { struct scd_softc * sc; int error; sc = device_get_softc(dev); error = 0; destroy_dev(sc->scd_dev_t); scd_release_resources(dev); return (error); } static int scd_alloc_resources (device_t dev) { struct scd_softc * sc; int error; sc = device_get_softc(dev); error = 0; if (sc->port_type) { sc->port = bus_alloc_resource(dev, sc->port_type, &sc->port_rid, 0, ~0, 1, RF_ACTIVE); if (sc->port == NULL) { device_printf(dev, "Unable to allocate PORT resource.\n"); error = ENOMEM; goto bad; } sc->port_bst = rman_get_bustag(sc->port); sc->port_bsh = rman_get_bushandle(sc->port); } mtx_init(&sc->mtx, device_get_nameunit(dev), "Interrupt lock", MTX_DEF | MTX_RECURSE); bad: return (error); } void scd_release_resources (device_t dev) { struct scd_softc * sc; sc = device_get_softc(dev); if (sc->port) { bus_release_resource(dev, sc->port_type, sc->port_rid, sc->port); sc->port_bst = 0; sc->port_bsh = 0; } if (mtx_initialized(&sc->mtx) != 0) mtx_destroy(&sc->mtx); return; } static device_method_t scd_isa_methods[] = { DEVMETHOD(device_probe, scd_isa_probe), DEVMETHOD(device_attach, scd_isa_attach), DEVMETHOD(device_detach, scd_isa_detach), { 0, 0 } }; static driver_t scd_isa_driver = { "scd", scd_isa_methods, sizeof(struct scd_softc) }; static devclass_t scd_devclass; DRIVER_MODULE(scd, isa, scd_isa_driver, scd_devclass, NULL, 0); Index: head/sys/dev/wds/wd7000.c =================================================================== --- head/sys/dev/wds/wd7000.c (revision 113580) +++ head/sys/dev/wds/wd7000.c (revision 113581) @@ -1,1438 +1,1437 @@ /* * Copyright (c) 1994 Ludd, University of Lule}, Sweden. * Copyright (c) 2000 Sergey A. Babkin * All rights reserved. * * Written by Olof Johansson (offe@ludd.luth.se) 1995. * Based on code written by Theo de Raadt (deraadt@fsa.ca). * Resurrected, ported to CAM and generally cleaned up by Sergey Babkin * or . * * 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 at Ludd, University of Lule} * and by the FreeBSD project. * 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. * * $FreeBSD$ */ /* All bugs are subject to removal without further notice */ /* * offe 01/07/95 * * This version of the driver _still_ doesn't implement scatter/gather for the * WD7000-FASST2. This is due to the fact that my controller doesn't seem to * support it. That, and the lack of documentation makes it impossible for me * to implement it. What I've done instead is allocated a local buffer, * contiguous buffer big enough to handle the requests. I haven't seen any * read/write bigger than 64k, so I allocate a buffer of 64+16k. The data * that needs to be DMA'd to/from the controller is copied to/from that * buffer before/after the command is sent to the card. * * SB 03/30/00 * * An intermediate buffer is needed anyway to make sure that the buffer is * located under 16MB, otherwise it's out of reach of ISA cards. I've added * optimizations to allocate space in buffer in fragments. */ /* * Jumpers: (see The Ref(TM) for more info) * W1/W2 - interrupt selection: * W1 (1-2) IRQ3, (3-4) IRQ4, (5-6) IRQ5, (7-8) IRQ7, (9-10) IRQ9 * W2 (21-22) IRQ10, (19-20) IRQ11, (17-18) IRQ12, (15-16) IRQ14, (13-14) IRQ15 * * W2 - DRQ/DACK selection, DRQ and DACK must be the same: * (5-6) DRQ5 (11-12) DACK5 * (3-4) DRQ6 (9-10) DACK6 * (1-2) DRQ7 (7-8) DACK7 * * W3 - I/O address selection: open pair of pins (OFF) means 1, jumpered (ON) means 0 * pair (1-2) is bit 3, ..., pair (9-10) is bit 7. All the other bits are equal * to the value 0x300. In bitwise representation that would be: * 0 0 1 1 (9-10) (7-8) (5-6) (3-4) (1-2) 0 0 0 * For example, address 0x3C0, bitwise 1111000000 will be represented as: * (9-10) OFF, (7-8) OFF, (5-6) ON, (3-4) ON, (1-2) ON * * W4 - BIOS address: open pair of pins (OFF) means 1, jumpered (ON) means 0 * pair (1-2) is bit 13, ..., pair (7-8) is bit 16. All the other bits are * equal to the value 0xC0000. In bitwise representation that would be: * 1 1 0 (7-8) (5-6) (3-4) (1-2) 0 0000 0000 0000 * For example, address 0xD8000 will be represented as: * (7-8) OFF, (5-6) OFF, (3-4) ON, (1-2) ON * * W98 (on newer cards) - BIOS enabled; on older cards just remove the BIOS * chip to disable it * W99 (on newer cards) - ROM size (1-2) OFF, (3-4) ON * * W5 - terminator power * ON - host supplies term. power * OFF - target supplies term. power * * W6, W9 - floppy support (a bit cryptic): * W6 ON, W9 ON - disabled * W6 OFF, W9 ON - enabled with HardCard only * W6 OFF, W9 OFF - enabled with no hardCard or Combo * * Default: I/O 0x350, IRQ15, DMA6 */ /* * debugging levels: * 0 - disabled * 1 - print debugging messages * 2 - collect debugging messages in an internal log buffer which can be * printed later by calling wds_printlog from DDB * * Both kind of logs are heavy and interact significantly with the timing * of commands, so the observed problems may become invisible if debug * logging is enabled. * * The light-weight logging facility may be enabled by defining * WDS_ENABLE_SMALLOG as 1. It has very little overhead and allows observing * the traces of various race conditions without affectiong them but the log is * quite terse. The small log can be printer from DDB by calling * wds_printsmallog. */ #ifndef WDS_DEBUG #define WDS_DEBUG 0 #endif #ifndef WDS_ENABLE_SMALLOG #define WDS_ENABLE_SMALLOG 0 #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 #define WDSTOPHYS(wp, a) ( ((u_long)a) - ((u_long)wp->dx) + ((u_long)wp->dx_p) ) #define WDSTOVIRT(wp, a) ( ((char *)a) - ((char*)wp->dx_p) + ((char *)wp->dx) ) /* 0x10000 (64k) should be enough. But just to be sure... */ #define BUFSIZ 0x12000 /* buffer fragment size, no more than 32 frags per buffer */ #define FRAGSIZ 0x1000 /* WD7000 registers */ #define WDS_STAT 0 /* read */ #define WDS_IRQSTAT 1 /* read */ #define WDS_CMD 0 /* write */ #define WDS_IRQACK 1 /* write */ #define WDS_HCR 2 /* write */ #define WDS_NPORTS 4 /* number of ports used */ /* WDS_STAT (read) defs */ #define WDS_IRQ 0x80 #define WDS_RDY 0x40 #define WDS_REJ 0x20 #define WDS_INIT 0x10 /* WDS_IRQSTAT (read) defs */ #define WDSI_MASK 0xc0 #define WDSI_ERR 0x00 #define WDSI_MFREE 0x80 #define WDSI_MSVC 0xc0 /* WDS_CMD (write) defs */ #define WDSC_NOOP 0x00 #define WDSC_INIT 0x01 #define WDSC_DISUNSOL 0x02 /* disable unsolicited ints */ #define WDSC_ENAUNSOL 0x03 /* enable unsolicited ints */ #define WDSC_IRQMFREE 0x04 /* interrupt on free RQM */ #define WDSC_SCSIRESETSOFT 0x05 /* soft reset */ #define WDSC_SCSIRESETHARD 0x06 /* hard reset ack */ #define WDSC_MSTART(m) (0x80 + (m)) /* start mailbox */ #define WDSC_MMSTART(m) (0xc0 + (m)) /* start all mailboxes */ /* WDS_HCR (write) defs */ #define WDSH_IRQEN 0x08 #define WDSH_DRQEN 0x04 #define WDSH_SCSIRESET 0x02 #define WDSH_ASCRESET 0x01 struct wds_cmd { u_int8_t cmd; u_int8_t targ; u_int8_t scb[12]; u_int8_t stat; u_int8_t venderr; u_int8_t len[3]; u_int8_t data[3]; u_int8_t next[3]; u_int8_t write; u_int8_t xx[6]; }; struct wds_req { struct wds_cmd cmd; union ccb *ccb; enum { WR_DONE = 0x01, WR_SENSE = 0x02 } flags; u_int8_t *buf; /* address of linear data buffer */ u_int32_t mask; /* mask of allocated fragments */ u_int8_t ombn; u_int8_t id; /* number of request */ }; #define WDSX_SCSICMD 0x00 #define WDSX_OPEN_RCVBUF 0x80 #define WDSX_RCV_CMD 0x81 #define WDSX_RCV_DATA 0x82 #define WDSX_RCV_DATASTAT 0x83 #define WDSX_SND_DATA 0x84 #define WDSX_SND_DATASTAT 0x85 #define WDSX_SND_CMDSTAT 0x86 #define WDSX_READINIT 0x88 #define WDSX_READSCSIID 0x89 #define WDSX_SETUNSOLIRQMASK 0x8a #define WDSX_GETUNSOLIRQMASK 0x8b #define WDSX_GETFIRMREV 0x8c #define WDSX_EXECDIAG 0x8d #define WDSX_SETEXECPARM 0x8e #define WDSX_GETEXECPARM 0x8f struct wds_mb { u_int8_t stat; u_int8_t addr[3]; }; /* ICMB status value */ #define ICMB_OK 0x01 #define ICMB_OKERR 0x02 #define ICMB_ETIME 0x04 #define ICMB_ERESET 0x05 #define ICMB_ETARCMD 0x06 #define ICMB_ERESEL 0x80 #define ICMB_ESEL 0x81 #define ICMB_EABORT 0x82 #define ICMB_ESRESET 0x83 #define ICMB_EHRESET 0x84 struct wds_setup { u_int8_t cmd; u_int8_t scsi_id; u_int8_t buson_t; u_int8_t busoff_t; u_int8_t xx; u_int8_t mbaddr[3]; u_int8_t nomb; u_int8_t nimb; }; /* the code depends on equality of these parameters */ #define MAXSIMUL 8 #define WDS_NOMB MAXSIMUL #define WDS_NIMB MAXSIMUL static int fragsiz; static int nfrags; /* structure for data exchange with controller */ struct wdsdx { struct wds_req req[MAXSIMUL]; struct wds_mb ombs[MAXSIMUL]; struct wds_mb imbs[MAXSIMUL]; u_int8_t data[BUFSIZ]; }; /* structure softc */ struct wds { device_t dev; int unit; int addr; int drq; struct cam_sim *sim; /* SIM descriptor for this card */ struct cam_path *path; /* wildcard path for this card */ char want_wdsr; /* resource shortage flag */ u_int32_t data_free; u_int32_t wdsr_free; struct wdsdx *dx; struct wdsdx *dx_p; /* physical address */ struct resource *port_r; int port_rid; struct resource *drq_r; int drq_rid; struct resource *intr_r; int intr_rid; void *intr_cookie; bus_dma_tag_t bustag; bus_dmamap_t busmap; }; #define ccb_wdsr spriv_ptr1 /* for wds request */ static int wds_probe(device_t dev); static int wds_attach(device_t dev); static void wds_intr(struct wds *wp); static void wds_action(struct cam_sim * sim, union ccb * ccb); static void wds_poll(struct cam_sim * sim); static int wds_preinit(struct wds *wp); static int wds_init(struct wds *wp); static void wds_alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error); static void wds_free_resources(struct wds *wp); static struct wds_req *wdsr_alloc(struct wds *wp); static void wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio); static void wdsr_ccb_done(struct wds *wp, struct wds_req *r, union ccb *ccb, u_int32_t status); static void wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat); static int wds_runsense(struct wds *wp, struct wds_req *r); static int wds_getvers(struct wds *wp); static int wds_cmd(int base, u_int8_t * p, int l); static void wds_wait(int reg, int mask, int val); static struct wds_req *cmdtovirt(struct wds *wp, u_int32_t phys); static u_int32_t frag_alloc(struct wds *wp, int size, u_int8_t **res, u_int32_t *maskp); static void frag_free(struct wds *wp, u_int32_t mask); void wds_print(void); #if WDS_ENABLE_SMALLOG==1 static __inline void smallog(char c); void wds_printsmallog(void); #endif /* SMALLOG */ /* SCSI ID of the adapter itself */ #ifndef WDS_HBA_ID #define WDS_HBA_ID 7 #endif #if WDS_DEBUG == 2 #define LOGLINESIZ 81 #define NLOGLINES 300 #define DBX wds_nextlog(), LOGLINESIZ, #define DBG snprintf static char wds_log[NLOGLINES][LOGLINESIZ]; static int logwrite = 0, logread = 0; static char *wds_nextlog(void); void wds_printlog(void); #elif WDS_DEBUG != 0 #define DBX #define DBG printf #else #define DBX #define DBG if(0) printf #endif /* the table of supported bus methods */ static device_method_t wds_isa_methods[] = { DEVMETHOD(device_probe, wds_probe), DEVMETHOD(device_attach, wds_attach), { 0, 0 } }; static driver_t wds_isa_driver = { "wds", wds_isa_methods, sizeof(struct wds), }; static devclass_t wds_devclass; DRIVER_MODULE(wds, isa, wds_isa_driver, wds_devclass, 0, 0); #if WDS_ENABLE_SMALLOG==1 #define SMALLOGSIZ 512 static char wds_smallog[SMALLOGSIZ]; static char *wds_smallogp = wds_smallog; static char wds_smallogover = 0; static __inline void smallog(char c) { *wds_smallogp = c; if (++wds_smallogp == &wds_smallog[SMALLOGSIZ]) { wds_smallogp = wds_smallog; wds_smallogover = 1; } } #define smallog2(a, b) (smallog(a), smallog(b)) #define smallog3(a, b, c) (smallog(a), smallog(b), smallog(c)) #define smallog4(a, b, c, d) (smallog(a),smallog(b),smallog(c),smallog(d)) void wds_printsmallog(void) { int i; char *p; printf("wds: "); p = wds_smallogover ? wds_smallogp : wds_smallog; i = 0; do { printf("%c", *p); if (++p == &wds_smallog[SMALLOGSIZ]) p = wds_smallog; if (++i == 70) { i = 0; printf("\nwds: "); } } while (p != wds_smallogp); printf("\n"); } #else #define smallog(a) #define smallog2(a, b) #define smallog3(a, b, c) #define smallog4(a, b, c, d) #endif /* SMALLOG */ static int wds_probe(device_t dev) { struct wds *wp; int error = 0; int irq; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); wp = (struct wds *) device_get_softc(dev); wp->unit = device_get_unit(dev); wp->dev = dev; wp->addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/); if (wp->addr == 0 || wp->addr <0x300 || wp->addr > 0x3f8 || wp->addr & 0x7) { device_printf(dev, "invalid port address 0x%x\n", wp->addr); return (ENXIO); } if (bus_set_resource(dev, SYS_RES_IOPORT, 0, wp->addr, WDS_NPORTS) < 0) return (ENXIO); /* get the DRQ */ wp->drq = bus_get_resource_start(dev, SYS_RES_DRQ, 0 /*rid*/); if (wp->drq < 5 || wp->drq > 7) { device_printf(dev, "invalid DRQ %d\n", wp->drq); return (ENXIO); } /* get the IRQ */ irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0 /*rid*/); if (irq < 3) { device_printf(dev, "invalid IRQ %d\n", irq); return (ENXIO); } wp->port_rid = 0; wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &wp->port_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if (wp->port_r == NULL) return (ENXIO); error = wds_preinit(wp); /* * We cannot hold resources between probe and * attach as we may never be attached. */ wds_free_resources(wp); return (error); } static int wds_attach(device_t dev) { struct wds *wp; struct cam_devq *devq; struct cam_sim *sim; struct cam_path *pathp; int i; int error = 0; wp = (struct wds *)device_get_softc(dev); wp->port_rid = 0; wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &wp->port_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if (wp->port_r == NULL) return (ENXIO); /* We must now release resources on error. */ wp->drq_rid = 0; wp->drq_r = bus_alloc_resource(dev, SYS_RES_DRQ, &wp->drq_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if (wp->drq_r == NULL) goto bad; wp->intr_rid = 0; wp->intr_r = bus_alloc_resource(dev, SYS_RES_IRQ, &wp->intr_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if (wp->intr_r == NULL) goto bad; error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM | INTR_ENTROPY, (driver_intr_t *)wds_intr, (void *)wp, &wp->intr_cookie); if (error) goto bad; /* now create the memory buffer */ error = bus_dma_tag_create(NULL, /*alignment*/4, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ sizeof(* wp->dx), /*nsegments*/ 1, /*maxsegsz*/ sizeof(* wp->dx), /*flags*/ 0, &wp->bustag); if (error) goto bad; error = bus_dmamem_alloc(wp->bustag, (void **)&wp->dx, /*flags*/ 0, &wp->busmap); if (error) goto bad; bus_dmamap_load(wp->bustag, wp->busmap, (void *)wp->dx, sizeof(* wp->dx), wds_alloc_callback, (void *)&wp->dx_p, /*flags*/0); /* initialize the wds_req structures on this unit */ for(i=0; idx->req[i].id = i; wp->wdsr_free |= 1< 32) { fragsiz = (BUFSIZ / 32) & ~0x01; /* keep it word-aligned */ device_printf(dev, "data buffer fragment size too small. " "BUFSIZE / FRAGSIZE must be <= 32\n"); } else fragsiz = FRAGSIZ & ~0x01; /* keep it word-aligned */ wp->data_free = 0; nfrags = 0; for (i = fragsiz; i <= BUFSIZ; i += fragsiz) { nfrags++; wp->data_free = (wp->data_free << 1) | 1; } /* complete the hardware initialization */ if (wds_init(wp) != 0) goto bad; if (wds_getvers(wp) == -1) device_printf(dev, "getvers failed\n"); device_printf(dev, "using %d bytes / %d frags for dma buffer\n", BUFSIZ, nfrags); devq = cam_simq_alloc(MAXSIMUL); if (devq == NULL) goto bad; sim = cam_sim_alloc(wds_action, wds_poll, "wds", (void *) wp, wp->unit, 1, 1, devq); if (sim == NULL) { cam_simq_free(devq); goto bad; } wp->sim = sim; if (xpt_bus_register(sim, 0) != CAM_SUCCESS) { cam_sim_free(sim, /* free_devq */ TRUE); goto bad; } if (xpt_create_path(&pathp, /* periph */ NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, /* free_devq */ TRUE); goto bad; } wp->path = pathp; return (0); bad: wds_free_resources(wp); if (error) return (error); else /* exact error is unknown */ return (ENXIO); } /* callback to save the physical address */ static void wds_alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error) { *(bus_addr_t *)arg = seg[0].ds_addr; } static void wds_free_resources(struct wds *wp) { /* check every resource and free if not zero */ /* interrupt handler */ if (wp->intr_r) { bus_teardown_intr(wp->dev, wp->intr_r, wp->intr_cookie); bus_release_resource(wp->dev, SYS_RES_IRQ, wp->intr_rid, wp->intr_r); wp->intr_r = 0; } /* all kinds of memory maps we could have allocated */ if (wp->dx_p) { bus_dmamap_unload(wp->bustag, wp->busmap); wp->dx_p = 0; } if (wp->dx) { /* wp->busmap may be legitimately equal to 0 */ /* the map will also be freed */ bus_dmamem_free(wp->bustag, wp->dx, wp->busmap); wp->dx = 0; } if (wp->bustag) { bus_dma_tag_destroy(wp->bustag); wp->bustag = 0; } /* release all the bus resources */ if (wp->drq_r) { bus_release_resource(wp->dev, SYS_RES_DRQ, wp->drq_rid, wp->drq_r); wp->drq_r = 0; } if (wp->port_r) { bus_release_resource(wp->dev, SYS_RES_IOPORT, wp->port_rid, wp->port_r); wp->port_r = 0; } } /* allocate contiguous fragments from the buffer */ static u_int32_t frag_alloc(struct wds *wp, int size, u_int8_t **res, u_int32_t *maskp) { int i; u_int32_t mask; u_int32_t free; if (size > fragsiz * nfrags) return (CAM_REQ_TOO_BIG); mask = 1; /* always allocate at least 1 fragment */ for (i = fragsiz; i < size; i += fragsiz) mask = (mask << 1) | 1; free = wp->data_free; if(free != 0) { i = ffs(free)-1; /* ffs counts bits from 1 */ for (mask <<= i; i < nfrags; i++) { if ((free & mask) == mask) { wp->data_free &= ~mask; /* mark frags as busy */ *maskp = mask; *res = &wp->dx->data[fragsiz * i]; DBG(DBX "wds%d: allocated buffer mask=0x%x\n", wp->unit, mask); return (CAM_REQ_CMP); } if (mask & 0x80000000) break; mask <<= 1; } } return (CAM_REQUEUE_REQ); /* no free memory now, try later */ } static void frag_free(struct wds *wp, u_int32_t mask) { wp->data_free |= mask; /* mark frags as free */ DBG(DBX "wds%d: freed buffer mask=0x%x\n", wp->unit, mask); } static struct wds_req * wdsr_alloc(struct wds *wp) { struct wds_req *r; int x; int i; r = NULL; x = splcam(); /* anyway most of the time only 1 or 2 commands will * be active because SCSI disconnect is not supported * by hardware, so the search should be fast enough */ i = ffs(wp->wdsr_free) - 1; if(i < 0) { splx(x); return (NULL); } wp->wdsr_free &= ~ (1<dx->req[i]; r->flags = 0; /* reset all flags */ r->ombn = i; /* luckily we have one omb per wdsr */ wp->dx->ombs[i].stat = 1; r->mask = 0; splx(x); smallog3('r', i + '0', r->ombn + '0'); return (r); } static void wds_intr(struct wds *wp) { struct wds_req *rp; struct wds_mb *in; u_int8_t stat; u_int8_t c; int addr = wp->addr; DBG(DBX "wds%d: interrupt [\n", wp->unit); smallog('['); if (inb(addr + WDS_STAT) & WDS_IRQ) { c = inb(addr + WDS_IRQSTAT); if ((c & WDSI_MASK) == WDSI_MSVC) { c = c & ~WDSI_MASK; in = &wp->dx->imbs[c]; rp = cmdtovirt(wp, scsi_3btoul(in->addr)); stat = in->stat; if (rp != NULL) wds_done(wp, rp, stat); else device_printf(wp->dev, "got weird command address %p" "from controller\n", rp); in->stat = 0; } else device_printf(wp->dev, "weird interrupt, irqstat=0x%x\n", c); outb(addr + WDS_IRQACK, 0); } else { smallog('?'); } smallog(']'); DBG(DBX "wds%d: ]\n", wp->unit); } static void wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat) { struct ccb_hdr *ccb_h; struct ccb_scsiio *csio; int status; smallog('d'); if (r->flags & WR_DONE) { device_printf(wp->dev, "request %d reported done twice\n", r->id); smallog2('x', r->id + '0'); return; } smallog(r->id + '0'); ccb_h = &r->ccb->ccb_h; csio = &r->ccb->csio; status = CAM_REQ_CMP_ERR; DBG(DBX "wds%d: %s stat=0x%x c->stat=0x%x c->venderr=0x%x\n", wp->unit, r->flags & WR_SENSE ? "(sense)" : "", stat, r->cmd.stat, r->cmd.venderr); if (r->flags & WR_SENSE) { if (stat == ICMB_OK || (stat == ICMB_OKERR && r->cmd.stat == 0)) { DBG(DBX "wds%d: sense 0x%x\n", wp->unit, r->buf[0]); /* it has the same size now but for future */ bcopy(r->buf, &csio->sense_data, sizeof(struct scsi_sense_data) > csio->sense_len ? csio->sense_len : sizeof(struct scsi_sense_data)); if (sizeof(struct scsi_sense_data) >= csio->sense_len) csio->sense_resid = 0; else csio->sense_resid = csio->sense_len - sizeof(struct scsi_sense_data); status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR; } else { status = CAM_AUTOSENSE_FAIL; } } else { switch (stat) { case ICMB_OK: if (ccb_h) { csio->resid = 0; csio->scsi_status = r->cmd.stat; status = CAM_REQ_CMP; } break; case ICMB_OKERR: if (ccb_h) { csio->scsi_status = r->cmd.stat; if (r->cmd.stat) { if (ccb_h->flags & CAM_DIS_AUTOSENSE) status = CAM_SCSI_STATUS_ERROR; else { if ( wds_runsense(wp, r) == CAM_REQ_CMP ) return; /* in case of error continue with freeing of CCB */ } } else { csio->resid = 0; status = CAM_REQ_CMP; } } break; case ICMB_ETIME: if (ccb_h) status = CAM_SEL_TIMEOUT; break; case ICMB_ERESET: case ICMB_ETARCMD: case ICMB_ERESEL: case ICMB_ESEL: case ICMB_EABORT: case ICMB_ESRESET: case ICMB_EHRESET: if (ccb_h) status = CAM_REQ_CMP_ERR; break; } if (ccb_h && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) { /* we accept only virtual addresses in wds_action() */ bcopy(r->buf, csio->data_ptr, csio->dxfer_len); } } r->flags |= WR_DONE; wp->dx->ombs[r->ombn].stat = 0; if (ccb_h) { wdsr_ccb_done(wp, r, r->ccb, status); smallog3('-', ccb_h->target_id + '0', ccb_h->target_lun + '0'); } else { frag_free(wp, r->mask); if (wp->want_wdsr) { wp->want_wdsr = 0; xpt_release_simq(wp->sim, /* run queue */ 1); } wp->wdsr_free |= (1 << r->id); } DBG(DBX "wds%d: request %p done\n", wp->unit, r); } /* command returned bad status, request sense */ static int wds_runsense(struct wds *wp, struct wds_req *r) { u_int8_t c; struct ccb_hdr *ccb_h; ccb_h = &r->ccb->ccb_h; r->flags |= WR_SENSE; scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr); bzero(&r->cmd, sizeof r->cmd); r->cmd.cmd = WDSX_SCSICMD; r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun; scsi_ulto3b(0, r->cmd.next); r->cmd.scb[0] = REQUEST_SENSE; r->cmd.scb[1] = ccb_h->target_lun << 5; r->cmd.scb[4] = sizeof(struct scsi_sense_data); r->cmd.scb[5] = 0; scsi_ulto3b(WDSTOPHYS(wp, r->buf), r->cmd.data); scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len); r->cmd.write = 0x80; outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN); wp->dx->ombs[r->ombn].stat = 1; c = WDSC_MSTART(r->ombn); if (wds_cmd(wp->addr, &c, sizeof c) != 0) { device_printf(wp->dev, "unable to start outgoing sense mbox\n"); wp->dx->ombs[r->ombn].stat = 0; wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL); return CAM_AUTOSENSE_FAIL; } else { DBG(DBX "wds%d: enqueued status cmd 0x%x, r=%p\n", wp->unit, r->cmd.scb[0] & 0xFF, r); /* don't free CCB yet */ smallog3('*', ccb_h->target_id + '0', ccb_h->target_lun + '0'); return CAM_REQ_CMP; } } static int wds_getvers(struct wds *wp) { struct wds_req *r; int base; u_int8_t c; int i; base = wp->addr; r = wdsr_alloc(wp); if (!r) { device_printf(wp->dev, "no request slot available!\n"); return (-1); } r->flags &= ~WR_DONE; r->ccb = NULL; scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr); bzero(&r->cmd, sizeof r->cmd); r->cmd.cmd = WDSX_GETFIRMREV; outb(base + WDS_HCR, WDSH_DRQEN); c = WDSC_MSTART(r->ombn); if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) { device_printf(wp->dev, "version request failed\n"); wp->wdsr_free |= (1 << r->id); wp->dx->ombs[r->ombn].stat = 0; return (-1); } while (1) { i = 0; while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) { DELAY(9000); if (++i == 100) { device_printf(wp->dev, "getvers timeout\n"); return (-1); } } wds_intr(wp); if (r->flags & WR_DONE) { device_printf(wp->dev, "firmware version %d.%02d\n", r->cmd.targ, r->cmd.scb[0]); wp->wdsr_free |= (1 << r->id); return (0); } } } static void wdsr_ccb_done(struct wds *wp, struct wds_req *r, union ccb *ccb, u_int32_t status) { ccb->ccb_h.ccb_wdsr = 0; if (r != NULL) { /* To implement timeouts we would need to know how to abort the * command on controller, and this is a great mystery. * So for now we just pass the responsibility for timeouts * to the controlles itself, it does that reasonably good. */ /* untimeout(_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch); */ /* we're about to free a hcb, so the shortage has ended */ frag_free(wp, r->mask); if (wp->want_wdsr && status != CAM_REQUEUE_REQ) { wp->want_wdsr = 0; status |= CAM_RELEASE_SIMQ; smallog('R'); } wp->wdsr_free |= (1 << r->id); } ccb->ccb_h.status = status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED)); xpt_done(ccb); } static void wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio) { int unit = cam_sim_unit(sim); struct wds *wp; struct ccb_hdr *ccb_h; struct wds_req *r; int base; u_int8_t c; int error; int n; wp = (struct wds *)cam_sim_softc(sim); ccb_h = &csio->ccb_h; DBG(DBX "wds%d: cmd TARG=%d LUN=%d\n", unit, ccb_h->target_id, ccb_h->target_lun); if (ccb_h->target_id > 7 || ccb_h->target_id == WDS_HBA_ID) { ccb_h->status = CAM_TID_INVALID; xpt_done((union ccb *) csio); return; } if (ccb_h->target_lun > 7) { ccb_h->status = CAM_LUN_INVALID; xpt_done((union ccb *) csio); return; } if (csio->dxfer_len > BUFSIZ) { ccb_h->status = CAM_REQ_TOO_BIG; xpt_done((union ccb *) csio); return; } if (ccb_h->flags & (CAM_CDB_PHYS | CAM_SCATTER_VALID | CAM_DATA_PHYS)) { /* don't support these */ ccb_h->status = CAM_REQ_INVALID; xpt_done((union ccb *) csio); return; } base = wp->addr; /* * this check is mostly for debugging purposes, * "can't happen" normally. */ if(wp->want_wdsr) { DBG(DBX "wds%d: someone already waits for buffer\n", unit); smallog('b'); n = xpt_freeze_simq(sim, /* count */ 1); smallog('0'+n); ccb_h->status = CAM_REQUEUE_REQ; xpt_done((union ccb *) csio); return; } r = wdsr_alloc(wp); if (r == NULL) { device_printf(wp->dev, "no request slot available!\n"); wp->want_wdsr = 1; n = xpt_freeze_simq(sim, /* count */ 1); smallog2('f', '0'+n); ccb_h->status = CAM_REQUEUE_REQ; xpt_done((union ccb *) csio); return; } ccb_h->ccb_wdsr = (void *) r; r->ccb = (union ccb *) csio; switch (error = frag_alloc(wp, csio->dxfer_len, &r->buf, &r->mask)) { case CAM_REQ_CMP: break; case CAM_REQUEUE_REQ: DBG(DBX "wds%d: no data buffer available\n", unit); wp->want_wdsr = 1; n = xpt_freeze_simq(sim, /* count */ 1); smallog2('f', '0'+n); wdsr_ccb_done(wp, r, r->ccb, CAM_REQUEUE_REQ); return; default: DBG(DBX "wds%d: request is too big\n", unit); wdsr_ccb_done(wp, r, r->ccb, error); break; } ccb_h->status |= CAM_SIM_QUEUED; r->flags &= ~WR_DONE; scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr); bzero(&r->cmd, sizeof r->cmd); r->cmd.cmd = WDSX_SCSICMD; r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun; if (ccb_h->flags & CAM_CDB_POINTER) bcopy(csio->cdb_io.cdb_ptr, &r->cmd.scb, csio->cdb_len < 12 ? csio->cdb_len : 12); else bcopy(csio->cdb_io.cdb_bytes, &r->cmd.scb, csio->cdb_len < 12 ? csio->cdb_len : 12); scsi_ulto3b(csio->dxfer_len, r->cmd.len); if (csio->dxfer_len > 0 && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_OUT) { /* we already rejected physical or scattered addresses */ bcopy(csio->data_ptr, r->buf, csio->dxfer_len); } scsi_ulto3b(csio->dxfer_len ? WDSTOPHYS(wp, r->buf) : 0, r->cmd.data); if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) r->cmd.write = 0x80; else r->cmd.write = 0x00; scsi_ulto3b(0, r->cmd.next); outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN); c = WDSC_MSTART(r->ombn); if (wds_cmd(base, &c, sizeof c) != 0) { device_printf(wp->dev, "unable to start outgoing mbox\n"); wp->dx->ombs[r->ombn].stat = 0; wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL); return; } DBG(DBX "wds%d: enqueued cmd 0x%x, r=%p\n", unit, r->cmd.scb[0] & 0xFF, r); smallog3('+', ccb_h->target_id + '0', ccb_h->target_lun + '0'); } static void wds_action(struct cam_sim * sim, union ccb * ccb) { int unit = cam_sim_unit(sim); int s; DBG(DBX "wds%d: action 0x%x\n", unit, ccb->ccb_h.func_code); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: s = splcam(); DBG(DBX "wds%d: SCSI IO entered\n", unit); wds_scsi_io(sim, &ccb->csio); DBG(DBX "wds%d: SCSI IO returned\n", unit); splx(s); break; case XPT_RESET_BUS: /* how to do it right ? */ printf("wds%d: reset\n", unit); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_ABORT: ccb->ccb_h.status = CAM_UA_ABORT; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg = &ccb->ccg; size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); ccg->heads = 64; ccg->secs_per_track = 16; secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = 0; /* nothing fancy */ cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 7; cpi->max_lun = 7; cpi->initiator_id = WDS_HBA_ID; cpi->hba_misc = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "WD/FDC", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static void wds_poll(struct cam_sim * sim) { wds_intr((struct wds *)cam_sim_softc(sim)); } /* part of initialization done in probe() */ /* returns 0 if OK, ENXIO if bad */ static int wds_preinit(struct wds *wp) { int base; int i; base = wp->addr; /* * Sending a command causes the CMDRDY bit to clear. */ outb(base + WDS_CMD, WDSC_NOOP); if (inb(base + WDS_STAT) & WDS_RDY) return (ENXIO); /* * the controller exists. reset and init. */ outb(base + WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET); DELAY(30); outb(base + WDS_HCR, 0); if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) { for (i = 0; i < 10; i++) { if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY) break; DELAY(40000); } if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) /* probe timeout */ return (ENXIO); } return (0); } /* part of initialization done in attach() */ /* returns 0 if OK, 1 if bad */ static int wds_init(struct wds *wp) { struct wds_setup init; int base; int i; struct wds_cmd wc; base = wp->addr; outb(base + WDS_HCR, WDSH_DRQEN); isa_dmacascade(wp->drq); if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) { for (i = 0; i < 10; i++) { if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY) break; DELAY(40000); } if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) /* probe timeout */ return (1); } bzero(&init, sizeof init); init.cmd = WDSC_INIT; init.scsi_id = WDS_HBA_ID; init.buson_t = 24; init.busoff_t = 48; scsi_ulto3b(WDSTOPHYS(wp, &wp->dx->ombs), init.mbaddr); init.xx = 0; init.nomb = WDS_NOMB; init.nimb = WDS_NIMB; wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY); if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) { device_printf(wp->dev, "wds_cmd init failed\n"); return (1); } wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT); wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY); bzero(&wc, sizeof wc); wc.cmd = WDSC_DISUNSOL; if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) { device_printf(wp->dev, "wds_cmd init2 failed\n"); return (1); } return (0); } static int wds_cmd(int base, u_int8_t * p, int l) { int s = splcam(); while (l--) { do { outb(base + WDS_CMD, *p); wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY); } while (inb(base + WDS_STAT) & WDS_REJ); p++; } wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY); splx(s); return (0); } static void wds_wait(int reg, int mask, int val) { while ((inb(reg) & mask) != val) ; } static struct wds_req * cmdtovirt(struct wds *wp, u_int32_t phys) { char *a; a = WDSTOVIRT(wp, (uintptr_t)phys); if( a < (char *)&wp->dx->req[0] || a>= (char *)&wp->dx->req[MAXSIMUL]) { device_printf(wp->dev, "weird phys address 0x%x\n", phys); return (NULL); } a -= (int)offsetof(struct wds_req, cmd); /* convert cmd to request */ return ((struct wds_req *)a); } /* for debugging, print out all the data about the status of devices */ void wds_print(void) { int unit; int i; struct wds_req *r; struct wds *wp; for (unit = 0; unit < devclass_get_maxunit(wds_devclass); unit++) { wp = (struct wds *) devclass_get_device(wds_devclass, unit); if (wp == NULL) continue; printf("wds%d: want_wdsr=0x%x stat=0x%x irq=%s irqstat=0x%x\n", unit, wp->want_wdsr, inb(wp->addr + WDS_STAT) & 0xff, (inb(wp->addr + WDS_STAT) & WDS_IRQ) ? "ready" : "no", inb(wp->addr + WDS_IRQSTAT) & 0xff); for (i = 0; i < MAXSIMUL; i++) { r = &wp->dx->req[i]; if( wp->wdsr_free & (1 << r->id) ) { printf("req=%d flg=0x%x ombn=%d ombstat=%d " "mask=0x%x targ=%d lun=%d cmd=0x%x\n", i, r->flags, r->ombn, wp->dx->ombs[r->ombn].stat, r->mask, r->cmd.targ >> 5, r->cmd.targ & 7, r->cmd.scb[0]); } } } } #if WDS_DEBUG == 2 /* create circular log buffer */ static char * wds_nextlog(void) { int n = logwrite; if (++logwrite >= NLOGLINES) logwrite = 0; if (logread == logwrite) if (++logread >= NLOGLINES) logread = 0; return (wds_log[n]); } void wds_printlog(void) { /* print the circular buffer */ int i; for (i = logread; i != logwrite;) { printf("%s", wds_log[i]); if (i == NLOGLINES) i = 0; else i++; } } #endif /* WDS_DEBUG */ Index: head/sys/i386/isa/bs/bsif.h =================================================================== --- head/sys/i386/isa/bs/bsif.h (revision 113580) +++ head/sys/i386/isa/bs/bsif.h (revision 113581) @@ -1,228 +1,227 @@ /* $NecBSD: bsif.h,v 1.5 1997/10/23 20:52:34 honda Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) HONDA Naofumi, KATO Takenori, 1996. 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 as * the first lines of this file unmodified. * 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. * 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. */ /*************************************************** * misc device header in bs_softc ***************************************************/ #ifdef __NetBSD__ #define OS_DEPEND_DEVICE_HEADER \ struct device sc_dev; \ void *sc_ih; #define OS_DEPEND_SCSI_HEADER \ struct scsi_link sc_link; #define OS_DEPEND_MISC_HEADER \ pisa_device_handle_t sc_dh; \ bus_space_tag_t sc_iot; \ bus_space_tag_t sc_memt; \ bus_space_handle_t sc_ioh; \ bus_space_handle_t sc_delaybah; \ bus_space_handle_t sc_memh; \ bus_dma_tag_t sc_dmat; #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #define OS_DEPEND_DEVICE_HEADER \ int unit; #define OS_DEPEND_SCSI_HEADER \ struct scsi_link sc_link; #define OS_DEPEND_MISC_HEADER \ struct callout_handle timeout_ch; #endif /* __FreeBSD__ */ #if defined(__NetBSD__) #define BSHW_NBPG NBPG #endif #if defined(__FreeBSD__) #define BSHW_NBPG PAGE_SIZE #endif /*************************************************** * include ***************************************************/ /* (I) common include */ #include #include #include -#include #include #include #include #include #include /* (II) os depend include */ #ifdef __NetBSD__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __FreeBSD__ */ /*************************************************** * BUS IO MAPPINGS & BS specific inclusion ***************************************************/ #ifdef __NetBSD__ #define BUS_IO_DELAY ((void) bus_space_read_1(bsc->sc_iot, bsc->sc_delaybah, 0)) #define BUS_IO_WEIGHT (bus_space_write_1(bsc->sc_iot, bsc->sc_delaybah, 0, 0)) #define BUS_IOR(offs) (bus_space_read_1(bsc->sc_iot, bsc->sc_ioh, (offs))) #define BUS_IOW(offs, val) (bus_space_write_1(bsc->sc_iot, bsc->sc_ioh, (offs), (val))) #include #include #include #include #include #include #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #define BUS_IO_DELAY ((void) inb(0x5f)) #define BUS_IO_WEIGHT (outb(0x5f, 0)) #define BUS_IOR(offs) (BUS_IO_DELAY, inb(bsc->sc_iobase + (offs))) #define BUS_IOW(offs, val) (BUS_IO_DELAY, outb(bsc->sc_iobase + (offs), (val))) #include #include #include #include #include #include #endif /* __FreeBSD__ */ /*************************************************** * XS return type ***************************************************/ #ifdef __NetBSD__ #define XSBS_INT32T int #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #define XSBS_INT32T int32_t #endif /* __FreeBSD__ */ /*************************************************** * xs flags's abstraction (all currently used) ***************************************************/ #define XSBS_ITSDONE ITSDONE #ifdef __NetBSD__ #define XSBS_SCSI_NOSLEEP SCSI_NOSLEEP #define XSBS_SCSI_POLL SCSI_POLL #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #define XSBS_SCSI_POLL SCSI_NOMASK #endif /* __FreeBSD__ */ /*************************************************** * declare ***************************************************/ /* (I) common declare */ void bs_alloc_buf(struct targ_info *); #ifdef __NetBSD__ XSBS_INT32T bs_target_open(struct scsi_link *, struct cfdata *); XSBS_INT32T bs_scsi_cmd(struct scsi_xfer *); #endif #ifdef __FreeBSD__ void bs_scsi_cmd(struct cam_sim *sim, union ccb *ccb); #endif extern int delaycount; /* (II) os depend declare */ #ifdef __NetBSD__ int bsintr(void *); int bsprint(void *, const char *); #endif /* __NetBSD__ */ #ifdef __FreeBSD__ static BS_INLINE void memcopy(void *from, void *to, register size_t len); u_int32_t bs_adapter_info(int); #define delay(y) DELAY(y) extern int dma_init_flag; #define softintr(y) static BS_INLINE void memcopy(from, to, len) void *from, *to; register size_t len; { len >>= 2; __asm __volatile(" \n\ cld \n\ rep \n\ movsl" : "=D" (to), "=c" (len), "=S" (from) : "0" (to), "1" (len), "2" (from) : "memory", "cc"); } #endif /* __FreeBSD__ */ Index: head/sys/kern/kern_shutdown.c =================================================================== --- head/sys/kern/kern_shutdown.c (revision 113580) +++ head/sys/kern/kern_shutdown.c (revision 113581) @@ -1,618 +1,617 @@ /*- * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, 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. * * @(#)kern_shutdown.c 8.3 (Berkeley) 1/21/94 * $FreeBSD$ */ #include "opt_ddb.h" #include "opt_ddb_trace.h" #include "opt_ddb_unattended.h" #include "opt_hw_wdog.h" #include "opt_mac.h" #include "opt_panic.h" #include "opt_show_busybufs.h" #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include /* smp_active */ #include #include #include #include #include #include #include #ifdef DDB #include #endif #ifndef PANIC_REBOOT_WAIT_TIME #define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */ #endif /* * Note that stdarg.h and the ANSI style va_start macro is used for both * ANSI and traditional C compilers. */ #include #ifdef DDB #ifdef DDB_UNATTENDED int debugger_on_panic = 0; #else int debugger_on_panic = 1; #endif SYSCTL_INT(_debug, OID_AUTO, debugger_on_panic, CTLFLAG_RW, &debugger_on_panic, 0, "Run debugger on kernel panic"); #ifdef DDB_TRACE int trace_on_panic = 1; #else int trace_on_panic = 0; #endif SYSCTL_INT(_debug, OID_AUTO, trace_on_panic, CTLFLAG_RW, &trace_on_panic, 0, "Print stack trace on kernel panic"); #endif int sync_on_panic = 1; SYSCTL_INT(_kern, OID_AUTO, sync_on_panic, CTLFLAG_RW, &sync_on_panic, 0, "Do a sync before rebooting from a panic"); SYSCTL_NODE(_kern, OID_AUTO, shutdown, CTLFLAG_RW, 0, "Shutdown environment"); #ifdef HW_WDOG /* * If there is a hardware watchdog, point this at the function needed to * hold it off. * It's needed when the kernel needs to do some lengthy operations. * e.g. in wd.c when dumping core.. It's most annoying to have * your precious core-dump only half written because the wdog kicked in. */ watchdog_tickle_fn wdog_tickler = NULL; #endif /* HW_WDOG */ /* * Variable panicstr contains argument to first call to panic; used as flag * to indicate that the kernel has already called panic. */ const char *panicstr; int dumping; /* system is dumping */ static struct dumperinfo dumper; /* our selected dumper */ static struct pcb dumppcb; /* "You Are Here" sign for dump-debuggers */ static void boot(int) __dead2; static void poweroff_wait(void *, int); static void shutdown_halt(void *junk, int howto); static void shutdown_panic(void *junk, int howto); static void shutdown_reset(void *junk, int howto); /* register various local shutdown events */ static void shutdown_conf(void *unused) { EVENTHANDLER_REGISTER(shutdown_final, poweroff_wait, NULL, SHUTDOWN_PRI_FIRST); EVENTHANDLER_REGISTER(shutdown_final, shutdown_halt, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_panic, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_reset, NULL, SHUTDOWN_PRI_LAST + 200); } SYSINIT(shutdown_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, shutdown_conf, NULL) /* * The system call that results in a reboot * * MPSAFE */ /* ARGSUSED */ int reboot(struct thread *td, struct reboot_args *uap) { int error; error = 0; #ifdef MAC error = mac_check_system_reboot(td->td_ucred, uap->opt); #endif if (error == 0) error = suser(td); if (error == 0) { mtx_lock(&Giant); boot(uap->opt); mtx_unlock(&Giant); } return (error); } /* * Called by events that want to shut down.. e.g on a PC */ static int shutdown_howto = 0; void shutdown_nice(int howto) { shutdown_howto = howto; /* Send a signal to init(8) and have it shutdown the world */ if (initproc != NULL) { PROC_LOCK(initproc); psignal(initproc, SIGINT); PROC_UNLOCK(initproc); } else { /* No init(8) running, so simply reboot */ boot(RB_NOSYNC); } return; } static int waittime = -1; static void print_uptime(void) { int f; struct timespec ts; getnanouptime(&ts); printf("Uptime: "); f = 0; if (ts.tv_sec >= 86400) { printf("%ldd", (long)ts.tv_sec / 86400); ts.tv_sec %= 86400; f = 1; } if (f || ts.tv_sec >= 3600) { printf("%ldh", (long)ts.tv_sec / 3600); ts.tv_sec %= 3600; f = 1; } if (f || ts.tv_sec >= 60) { printf("%ldm", (long)ts.tv_sec / 60); ts.tv_sec %= 60; f = 1; } printf("%lds\n", (long)ts.tv_sec); } static void doadump(void) { savectx(&dumppcb); dumping++; dumpsys(&dumper); } /* * Go through the rigmarole of shutting down.. * this used to be in machdep.c but I'll be dammned if I could see * anything machine dependant in it. */ static void boot(int howto) { /* collect extra flags that shutdown_nice might have set */ howto |= shutdown_howto; #ifdef DDB /* We are out of the debugger now. */ db_active = 0; #endif #ifdef SMP if (smp_active) printf("boot() called on cpu#%d\n", PCPU_GET(cpuid)); #endif /* * Do any callouts that should be done BEFORE syncing the filesystems. */ EVENTHANDLER_INVOKE(shutdown_pre_sync, howto); /* * Now sync filesystems */ if (!cold && (howto & RB_NOSYNC) == 0 && waittime < 0) { register struct buf *bp; int iter, nbusy, pbusy; int subiter; waittime = 0; printf("\nsyncing disks, buffers remaining... "); sync(&thread0, NULL); /* * With soft updates, some buffers that are * written will be remarked as dirty until other * buffers are written. */ for (iter = pbusy = 0; iter < 20; iter++) { nbusy = 0; for (bp = &buf[nbuf]; --bp >= buf; ) { if ((bp->b_flags & B_INVAL) == 0 && BUF_REFCNT(bp) > 0) { nbusy++; } else if ((bp->b_flags & (B_DELWRI | B_INVAL)) == B_DELWRI) { /* bawrite(bp);*/ nbusy++; } } if (nbusy == 0) break; printf("%d ", nbusy); if (nbusy < pbusy) iter = 0; pbusy = nbusy; sync(&thread0, NULL); if (curthread != NULL) { DROP_GIANT(); for (subiter = 0; subiter < 50 * iter; subiter++) { mtx_lock_spin(&sched_lock); curthread->td_proc->p_stats->p_ru.ru_nvcsw++; mi_switch(); /* Allow interrupt threads to run */ mtx_unlock_spin(&sched_lock); DELAY(1000); } PICKUP_GIANT(); } else DELAY(50000 * iter); } printf("\n"); /* * Count only busy local buffers to prevent forcing * a fsck if we're just a client of a wedged NFS server */ nbusy = 0; for (bp = &buf[nbuf]; --bp >= buf; ) { if (((bp->b_flags&B_INVAL) == 0 && BUF_REFCNT(bp)) || ((bp->b_flags & (B_DELWRI|B_INVAL)) == B_DELWRI)) { if (bp->b_dev == NODEV) { TAILQ_REMOVE(&mountlist, bp->b_vp->v_mount, mnt_list); continue; } nbusy++; #if defined(SHOW_BUSYBUFS) || defined(DIAGNOSTIC) printf( "%d: dev:%s, flags:%0x, blkno:%ld, lblkno:%ld\n", nbusy, devtoname(bp->b_dev), bp->b_flags, (long)bp->b_blkno, (long)bp->b_lblkno); #endif } } if (nbusy) { /* * Failed to sync all blocks. Indicate this and don't * unmount filesystems (thus forcing an fsck on reboot). */ printf("giving up on %d buffers\n", nbusy); DELAY(5000000); /* 5 seconds */ } else { printf("done\n"); /* * Unmount filesystems */ if (panicstr == 0) vfs_unmountall(); } DELAY(100000); /* wait for console output to finish */ } print_uptime(); /* * Ok, now do things that assume all filesystem activity has * been completed. */ EVENTHANDLER_INVOKE(shutdown_post_sync, howto); splhigh(); if ((howto & (RB_HALT|RB_DUMP)) == RB_DUMP && !cold && dumper.dumper != NULL && !dumping) doadump(); /* Now that we're going to really halt the system... */ EVENTHANDLER_INVOKE(shutdown_final, howto); for(;;) ; /* safety against shutdown_reset not working */ /* NOTREACHED */ } /* * If the shutdown was a clean halt, behave accordingly. */ static void shutdown_halt(void *junk, int howto) { if (howto & RB_HALT) { printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); switch (cngetc()) { case -1: /* No console, just die */ cpu_halt(); /* NOTREACHED */ default: howto &= ~RB_HALT; break; } } } /* * Check to see if the system paniced, pause and then reboot * according to the specified delay. */ static void shutdown_panic(void *junk, int howto) { int loop; if (howto & RB_DUMP) { if (PANIC_REBOOT_WAIT_TIME != 0) { if (PANIC_REBOOT_WAIT_TIME != -1) { printf("Automatic reboot in %d seconds - " "press a key on the console to abort\n", PANIC_REBOOT_WAIT_TIME); for (loop = PANIC_REBOOT_WAIT_TIME * 10; loop > 0; --loop) { DELAY(1000 * 100); /* 1/10th second */ /* Did user type a key? */ if (cncheckc() != -1) break; } if (!loop) return; } } else { /* zero time specified - reboot NOW */ return; } printf("--> Press a key on the console to reboot,\n"); printf("--> or switch off the system now.\n"); cngetc(); } } /* * Everything done, now reset */ static void shutdown_reset(void *junk, int howto) { printf("Rebooting...\n"); DELAY(1000000); /* wait 1 sec for printf's to complete and be read */ /* cpu_boot(howto); */ /* doesn't do anything at the moment */ cpu_reset(); /* NOTREACHED */ /* assuming reset worked */ } /* * Print a backtrace if we can. */ void backtrace(void) { #ifdef DDB printf("Stack backtrace:\n"); db_print_backtrace(); #else printf("Sorry, need DDB option to print backtrace"); #endif } #ifdef SMP static u_int panic_cpu = NOCPU; #endif /* * Panic is called on unresolvable fatal errors. It prints "panic: mesg", * and then reboots. If we are called twice, then we avoid trying to sync * the disks as this often leads to recursive panics. * * MPSAFE */ void panic(const char *fmt, ...) { struct thread *td = curthread; int bootopt, newpanic; va_list ap; static char buf[256]; #ifdef SMP /* * We don't want multiple CPU's to panic at the same time, so we * use panic_cpu as a simple spinlock. We have to keep checking * panic_cpu if we are spinning in case the panic on the first * CPU is canceled. */ if (panic_cpu != PCPU_GET(cpuid)) while (atomic_cmpset_int(&panic_cpu, NOCPU, PCPU_GET(cpuid)) == 0) while (panic_cpu != NOCPU) ; /* nothing */ #endif bootopt = RB_AUTOBOOT | RB_DUMP; newpanic = 0; if (panicstr) bootopt |= RB_NOSYNC; else { panicstr = fmt; newpanic = 1; } va_start(ap, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, ap); if (panicstr == fmt) panicstr = buf; va_end(ap); printf("panic: %s\n", buf); #ifdef SMP /* two separate prints in case of an unmapped page and trap */ printf("cpuid = %d; ", PCPU_GET(cpuid)); #ifdef APIC_IO printf("lapic.id = %08x\n", lapic.id); #else printf("\n"); #endif #endif #if defined(DDB) if (newpanic && trace_on_panic) backtrace(); if (debugger_on_panic) Debugger ("panic"); #ifdef RESTARTABLE_PANICS /* See if the user aborted the panic, in which case we continue. */ if (panicstr == NULL) { #ifdef SMP atomic_store_rel_int(&panic_cpu, NOCPU); #endif return; } #endif #endif td->td_flags |= TDF_INPANIC; if (!sync_on_panic) bootopt |= RB_NOSYNC; boot(bootopt); } /* * Support for poweroff delay. */ #ifndef POWEROFF_DELAY # define POWEROFF_DELAY 5000 #endif static int poweroff_delay = POWEROFF_DELAY; SYSCTL_INT(_kern_shutdown, OID_AUTO, poweroff_delay, CTLFLAG_RW, &poweroff_delay, 0, ""); static void poweroff_wait(void *junk, int howto) { if (!(howto & RB_POWEROFF) || poweroff_delay <= 0) return; DELAY(poweroff_delay * 1000); } /* * Some system processes (e.g. syncer) need to be stopped at appropriate * points in their main loops prior to a system shutdown, so that they * won't interfere with the shutdown process (e.g. by holding a disk buf * to cause sync to fail). For each of these system processes, register * shutdown_kproc() as a handler for one of shutdown events. */ static int kproc_shutdown_wait = 60; SYSCTL_INT(_kern_shutdown, OID_AUTO, kproc_shutdown_wait, CTLFLAG_RW, &kproc_shutdown_wait, 0, ""); void kproc_shutdown(void *arg, int howto) { struct proc *p; int error; if (panicstr) return; p = (struct proc *)arg; printf("Waiting (max %d seconds) for system process `%s' to stop...", kproc_shutdown_wait, p->p_comm); error = kthread_suspend(p, kproc_shutdown_wait * hz); if (error == EWOULDBLOCK) printf("timed out\n"); else printf("stopped\n"); } /* Registration of dumpers */ int set_dumper(struct dumperinfo *di) { if (di == NULL) { bzero(&dumper, sizeof dumper); return (0); } if (dumper.dumper != NULL) return (EBUSY); dumper = *di; return (0); } #if defined(__powerpc__) void dumpsys(struct dumperinfo *di __unused) { printf("Kernel dumps not implemented on this architecture\n"); } #endif Index: head/sys/kern/subr_disk.c =================================================================== --- head/sys/kern/subr_disk.c (revision 113580) +++ head/sys/kern/subr_disk.c (revision 113581) @@ -1,233 +1,232 @@ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "opt_geom.h" #include #include #include #include #include -#include #include /*- * Disk error is the preface to plaintive error messages * about failing disk transfers. It prints messages of the form * "hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347" * blkdone should be -1 if the position of the error is unknown. * The message is printed with printf. */ void disk_err(struct bio *bp, const char *what, int blkdone, int nl) { daddr_t sn; if (bp->bio_dev != NULL) printf("%s: %s ", devtoname(bp->bio_dev), what); else if (bp->bio_disk != NULL) printf("%s%d: %s ", bp->bio_disk->d_name, bp->bio_disk->d_unit, what); else printf("disk??: %s ", what); switch(bp->bio_cmd) { case BIO_READ: printf("cmd=read "); break; case BIO_WRITE: printf("cmd=write "); break; case BIO_DELETE: printf("cmd=delete "); break; case BIO_GETATTR: printf("cmd=getattr "); break; default: printf("cmd=%x ", bp->bio_cmd); break; } sn = bp->bio_blkno; if (bp->bio_bcount <= DEV_BSIZE) { printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); return; } if (blkdone >= 0) { sn += blkdone; printf("fsbn %jd of ", (intmax_t)sn); } printf("%jd-%jd", (intmax_t)bp->bio_blkno, (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE)); if (nl) printf("\n"); } /* * BIO queue implementation */ void bioq_init(struct bio_queue_head *head) { TAILQ_INIT(&head->queue); head->last_pblkno = 0; head->insert_point = NULL; head->switch_point = NULL; } void bioq_remove(struct bio_queue_head *head, struct bio *bp) { if (bp == head->switch_point) head->switch_point = TAILQ_NEXT(bp, bio_queue); if (bp == head->insert_point) { head->insert_point = TAILQ_PREV(bp, bio_queue, bio_queue); if (head->insert_point == NULL) head->last_pblkno = 0; } else if (bp == TAILQ_FIRST(&head->queue)) head->last_pblkno = bp->bio_pblkno; TAILQ_REMOVE(&head->queue, bp, bio_queue); if (TAILQ_FIRST(&head->queue) == head->switch_point) head->switch_point = NULL; } void bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error) { struct bio *bp; for (;;) { bp = bioq_first(head); if (bp == NULL) break; bioq_remove(head, bp); biofinish(bp, stp, ENXIO); } } void bioq_insert_tail(struct bio_queue_head *head, struct bio *bp) { TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue); } struct bio * bioq_first(struct bio_queue_head *head) { return (TAILQ_FIRST(&head->queue)); } /* * Seek sort for disks. * * The buf_queue keep two queues, sorted in ascending block order. The first * queue holds those requests which are positioned after the current block * (in the first request); the second, which starts at queue->switch_point, * holds requests which came in after their block number was passed. Thus * we implement a one way scan, retracting after reaching the end of the drive * to the first request on the second queue, at which time it becomes the * first queue. * * A one-way scan is natural because of the way UNIX read-ahead blocks are * allocated. */ void bioq_disksort(bioq, bp) struct bio_queue_head *bioq; struct bio *bp; { struct bio *bq; struct bio *bn; struct bio *be; be = TAILQ_LAST(&bioq->queue, bio_queue); /* * If the queue is empty or we are an * ordered transaction, then it's easy. */ if ((bq = bioq_first(bioq)) == NULL) { bioq_insert_tail(bioq, bp); return; } else if (bioq->insert_point != NULL) { /* * A certain portion of the list is * "locked" to preserve ordering, so * we can only insert after the insert * point. */ bq = bioq->insert_point; } else { /* * If we lie before the last removed (currently active) * request, and are not inserting ourselves into the * "locked" portion of the list, then we must add ourselves * to the second request list. */ if (bp->bio_pblkno < bioq->last_pblkno) { bq = bioq->switch_point; /* * If we are starting a new secondary list, * then it's easy. */ if (bq == NULL) { bioq->switch_point = bp; bioq_insert_tail(bioq, bp); return; } /* * If we lie ahead of the current switch point, * insert us before the switch point and move * the switch point. */ if (bp->bio_pblkno < bq->bio_pblkno) { bioq->switch_point = bp; TAILQ_INSERT_BEFORE(bq, bp, bio_queue); return; } } else { if (bioq->switch_point != NULL) be = TAILQ_PREV(bioq->switch_point, bio_queue, bio_queue); /* * If we lie between last_pblkno and bq, * insert before bq. */ if (bp->bio_pblkno < bq->bio_pblkno) { TAILQ_INSERT_BEFORE(bq, bp, bio_queue); return; } } } /* * Request is at/after our current position in the list. * Optimize for sequential I/O by seeing if we go at the tail. */ if (bp->bio_pblkno > be->bio_pblkno) { TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue); return; } /* Otherwise, insertion sort */ while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) { /* * We want to go after the current request if it is the end * of the first request list, or if the next request is a * larger cylinder than our request. */ if (bn == bioq->switch_point || bp->bio_pblkno < bn->bio_pblkno) break; bq = bn; } TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); } Index: head/sys/pc98/pc98/wd.c =================================================================== --- head/sys/pc98/pc98/wd.c (revision 113580) +++ head/sys/pc98/pc98/wd.c (revision 113581) @@ -1,2075 +1,2074 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 * $FreeBSD$ */ /* TODO: * o Bump error count after timeout. * o Satisfy ATA timing in all cases. * o Finish merging berry/sos timeout code (bump error count...). * o Don't use polling except for initialization. Need to * reorganize the state machine. Then "extra" interrupts * shouldn't happen (except maybe one for initialization). * o Support extended DOS partitions. * o Support swapping to DOS partitions. * o Handle bad sectors, clustering, disklabelling, DOS * partitions and swapping driver-independently. Use * i386/dkbad.c for bad sectors. Swapping will need new * driver entries for polled reinit and polled write). */ #include "wdc.h" #undef NWD #define NWD (NWDC * 4) /* 4 drives per wdc on PC98 */ #if NWDC > 0 #include "opt_hw_wdog.h" #include #include #include #include #include #include -#include #include #include #include #include #include #ifdef PC98 #include #include #include #else #include #endif #include #include #include #include #include #include #ifndef COMPAT_OLDISA #error "The wdc device requires the old isa compatibility shims" #endif extern void wdstart(int ctrlr); #ifdef IDE_DELAY #define TIMEOUT IDE_DELAY #else #define TIMEOUT 10000 #endif #define RETRIES 5 /* number of retries before giving up */ #define RECOVERYTIME 500000 /* usec for controller to recover after err */ #define MAXTRANSFER 255 /* max size of transfer in sectors */ /* correct max is 256 but some controllers */ /* can't handle that in all cases */ #define WDOPT_32BIT 0x8000 #define WDOPT_SLEEPHACK 0x4000 #define WDOPT_DMA 0x2000 #define WDOPT_LBA 0x1000 #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff #ifdef PC98 static __inline u_char epson_errorf(int wdc) { u_char wdc_error; outb(wdc, inb(0x82) | 0x40); wdc_error = (u_char)epson_inb(wdc); outb(wdc, inb(0x82) & ~0x40); return ((u_char)wdc_error); } #endif /* * Drive states. Used to initialize drive. */ #define CLOSED 0 /* disk is closed. */ #define WANTOPEN 1 /* open requested, not started */ #define RECAL 2 /* doing restore */ #define OPEN 3 /* done with open */ #define PRIMARY 0 /* * Disk geometry. A small part of struct disklabel. * XXX disklabel.5 contains an old clone of disklabel.h. */ struct diskgeom { u_long d_secsize; /* # of bytes per sector */ u_long d_nsectors; /* # of data sectors per track */ u_long d_ntracks; /* # of tracks per cylinder */ u_long d_ncylinders; /* # of data cylinders per unit */ u_long d_secpercyl; /* # of data sectors per cylinder */ u_long d_secperunit; /* # of data sectors per unit */ u_long d_precompcyl; /* XXX always 0 */ }; /* * The structure of a disk drive. */ struct softc { u_int dk_bc; /* byte count left */ short dk_skip; /* blocks already transferred */ int dk_ctrlr; /* physical controller number */ int dk_ctrlr_cmd640;/* controller number for CMD640 quirk */ u_int32_t dk_unit; /* physical unit number */ u_int32_t dk_lunit; /* logical unit number */ u_int32_t dk_interface; /* interface (two ctrlrs per interface) */ char dk_state; /* control state */ u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ u_char dk_timeout; /* countdown to next timeout */ u_int32_t dk_port; /* i/o port base */ u_int32_t dk_altport; /* altstatus port base */ u_long cfg_flags; /* configured characteristics */ short dk_flags; /* drive characteristics found */ #define DKFL_SINGLE 0x00004 /* sector at a time mode */ #define DKFL_ERROR 0x00008 /* processing a disk error */ #define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */ #define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */ #define DKFL_MULTI 0x00200 /* use multi-i/o mode */ #define DKFL_BADSCAN 0x00400 /* report all errors */ #define DKFL_USEDMA 0x00800 /* use DMA for data transfers */ #define DKFL_DMA 0x01000 /* using DMA on this transfer-- DKFL_SINGLE * overrides this */ #define DKFL_LBA 0x02000 /* use LBA for data transfers */ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ unsigned int dk_multi; /* multi transfers */ int dk_currentiosize; /* current io size */ struct diskgeom dk_dd; /* device configuration data */ struct diskslices *dk_slices; /* virtual drives */ void *dk_dmacookie; /* handle for DMA services */ struct disk disk; }; #define WD_COUNT_RETRIES static int wdtest = 0; static struct softc *wddrives[NWD]; /* table of units */ static struct bio_queue_head drive_queue[NWD]; /* head of queue per drive */ static struct { int b_active; } wdutab[NWD]; /* static struct bio wdtab[NWDC]; */ static struct { struct bio_queue_head controller_queue; int b_errcnt; int b_active; } wdtab[NWDC]; struct wddma wddma[NWDC]; #ifdef notyet static struct bio rwdbuf[NWD]; /* buffers for raw IO */ #endif #ifdef PC98 static short wd_ctlr; static int old_epson_note; #endif static int wdprobe(struct isa_device *dvp); static int wdattach(struct isa_device *dvp); static void wdustart(struct softc *du); static int wdcontrol(struct bio *bp); static int wdcommand(struct softc *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command); static int wdsetctlr(struct softc *du); #if 0 static int wdwsetctlr(struct softc *du); #endif static int wdsetmode(int mode, void *wdinfo); static int wdgetctlr(struct softc *du); static void wderror(struct bio *bp, struct softc *du, char *mesg); static void wdflushirq(struct softc *du, int old_ipl); static int wdreset(struct softc *du); static void wdsleep(int ctrlr, char *wmesg); static disk_open_t wdopen; static disk_strategy_t wdstrategy; static timeout_t wdtimeout; static int wdunwedge(struct softc *du); static int wdwait(struct softc *du, u_char bits_wanted, int timeout); struct isa_driver wdcdriver = { INTR_TYPE_BIO, wdprobe, wdattach, "wdc", }; COMPAT_ISA_DRIVER(wdc, wdcdriver); static int atapictrlr; static int eide_quirks; /* * Here we use the pci-subsystem to find out, whether there is * a cmd640b-chip attached on this pci-bus. This public routine * will be called by ide_pci.c */ void wdc_pci(int quirks) { eide_quirks = quirks; } /* * Probe for controller. */ static int wdprobe(struct isa_device *dvp) { int unit = dvp->id_unit; int interface; struct softc *du; if (unit >= NWDC) return (0); du = malloc(sizeof *du, M_TEMP, M_NOWAIT | M_ZERO); if (du == NULL) return (0); du->dk_ctrlr = dvp->id_unit; interface = du->dk_ctrlr / 2; du->dk_interface = interface; du->dk_port = dvp->id_iobase; if (wddma[interface].wdd_candma != NULL) { du->dk_dmacookie = wddma[interface].wdd_candma(dvp->id_iobase, du->dk_ctrlr, du->dk_unit); du->dk_altport = wddma[interface].wdd_altiobase(du->dk_dmacookie); } if (du->dk_altport == 0) du->dk_altport = du->dk_port + wd_ctlr; /* check if we have registers that work */ #ifdef PC98 /* XXX ATAPI support isn't imported */ wd_ctlr = wd_ctlr_nec; /* wdreg.h */ old_epson_note=0; if (pc98_machine_type & M_EPSON_PC98 ) { switch (epson_machine_id) { case 0x20: case 0x22: case 0x2a: /* note A/W/WR */ du->dk_port = IO_WD1_EPSON; /* pc98.h */ dvp->id_iobase = IO_WD1_EPSON; /* pc98.h */ wd_ctlr = wd_ctlr_epson; /* wdreg.h */ old_epson_note = 1; /* for OLD EPSON NOTE */ break; default: break; } } du->dk_altport = du->dk_port + wd_ctlr; #if 0 if ((PC98_SYSTEM_PARAMETER(0x55d) & 3) == 0) { goto nodevice; } #endif outb(0x432,(du->dk_unit)%2); #else /* IBM-PC */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */ outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */ if (inb(du->dk_port + wd_cyl_lo) == 0xff) { /* XXX too weak */ /* There is no master, try the ATAPI slave. */ du->dk_unit = 1; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); outb(du->dk_port + wd_cyl_lo, 0xa5); if (inb(du->dk_port + wd_cyl_lo) == 0xff) goto nodevice; } #endif /* PC98 */ if (wdreset(du) == 0) goto reset_ok; /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #ifdef PC98 du->dk_unit = 2; #else du->dk_unit = 1; #endif outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #ifdef PC98 du->dk_unit = 1; outb(0x432,(du->dk_unit)%2); if (wdreset(du) == 0) goto reset_ok; /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; du->dk_unit = 3; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #endif DELAY(RECOVERYTIME); if (wdreset(du) != 0) { goto nodevice; } reset_ok: /* execute a controller only command */ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 || wdwait(du, 0, TIMEOUT) < 0) { goto nodevice; } /* * drive(s) did not time out during diagnostic : * Get error status and check that both drives are OK. * Table 9-2 of ATA specs suggests that we must check for * a value of 0x01 * * Strangely, some controllers will return a status of * 0x81 (drive 0 OK, drive 1 failure), and then when * the DRV bit is set, return status of 0x01 (OK) for * drive 2. (This seems to contradict the ATA spec.) */ if (old_epson_note) du->dk_error = epson_errorf(du->dk_port + wd_error); else du->dk_error = inb(du->dk_port + wd_error); if(du->dk_error != 0x01 && du->dk_error != 0) { if(du->dk_error & 0x80) { /* drive 1 failure */ /* first set the DRV bit */ u_int sdh; if (old_epson_note) sdh = epson_inb(du->dk_port+ wd_sdh); else sdh = inb(du->dk_port+ wd_sdh); sdh = sdh | 0x10; if (old_epson_note) epson_outb(du->dk_port+ wd_sdh, sdh); else outb(du->dk_port+ wd_sdh, sdh); /* Wait, to make sure drv 1 has completed diags */ if ( wdwait(du, 0, TIMEOUT) < 0) goto nodevice; /* Get status for drive 1 */ if (old_epson_note) du->dk_error = epson_errorf(du->dk_port + wd_error); else du->dk_error = inb(du->dk_port + wd_error); /* printf("Error (drv 1) : %x\n", du->dk_error); */ /* * Sometimes (apparently mostly with ATAPI * drives involved) 0x81 really means 0x81 * (drive 0 OK, drive 1 failed). */ if(du->dk_error != 0x01 && du->dk_error != 0x81) goto nodevice; } else /* drive 0 fail */ goto nodevice; } free(du, M_TEMP); return (IO_WDCSIZE); nodevice: free(du, M_TEMP); return (0); } /* * Attach each drive if possible. */ static int wdattach(struct isa_device *dvp) { int unit, lunit, flags, i; struct softc *du; struct wdparams *wp; static char buf[] = "wdcXXX"; const char *dname; dvp->id_intr = wdintr; if (dvp->id_unit >= NWDC) return (0); if (eide_quirks & Q_CMD640B) { if (dvp->id_unit == PRIMARY) { printf("wdc0: CMD640B workaround enabled\n"); bioq_init(&wdtab[PRIMARY].controller_queue); } } else bioq_init(&wdtab[dvp->id_unit].controller_queue); sprintf(buf, "wdc%d", dvp->id_unit); i = 0; while ((resource_find_match(&i, &dname, &lunit, "at", buf)) == 0) { if (strcmp(dname, "wd")) /* Avoid a bit of foot shooting. */ continue; if (lunit >= NWD) continue; #ifdef PC98 if ((lunit%2)!=0) { if ((PC98_SYSTEM_PARAMETER(0x457) & 0x40)==0) { continue; } } #endif if (resource_int_value("wd", lunit, "drive", &unit) != 0) continue; if (resource_int_value("wd", lunit, "flags", &flags) != 0) flags = 0; du = malloc(sizeof *du, M_TEMP, M_NOWAIT | M_ZERO); if (du == NULL) continue; if (wddrives[lunit] != NULL) panic("drive attached twice"); wddrives[lunit] = du; bioq_init(&drive_queue[lunit]); du->dk_ctrlr = dvp->id_unit; if (eide_quirks & Q_CMD640B) { du->dk_ctrlr_cmd640 = PRIMARY; } else { du->dk_ctrlr_cmd640 = du->dk_ctrlr; } du->dk_unit = unit; du->dk_lunit = lunit; du->dk_port = dvp->id_iobase; du->dk_altport = du->dk_port + wd_ctlr; /* * Use the individual device flags or the controller * flags. */ du->cfg_flags = flags | ((dvp->id_flags) >> (16 * unit)); if (wdgetctlr(du) == 0) { /* * Print out description of drive. * wdp_model may not be null terminated. */ printf("wdc%d: unit %d (wd%d): <%.*s>", dvp->id_unit, unit, lunit, (int)sizeof(du->dk_params.wdp_model), du->dk_params.wdp_model); if (du->dk_flags & DKFL_LBA) printf(", LBA"); if (du->dk_flags & DKFL_USEDMA) printf(", DMA"); if (du->dk_flags & DKFL_32BIT) printf(", 32-bit"); if (du->dk_multi > 1) printf(", multi-block-%d", du->dk_multi); if (du->cfg_flags & WDOPT_SLEEPHACK) printf(", sleep-hack"); printf("\n"); if (du->dk_params.wdp_heads == 0) printf("wd%d: size unknown, using %s values\n", lunit, du->dk_dd.d_secperunit > 17 ? "BIOS" : "fake"); printf( "wd%d: %luMB (%lu sectors), " "%lu cyls, %lu heads, %lu S/T, %lu B/S\n", lunit, du->dk_dd.d_secperunit / ((1024L * 1024L) / du->dk_dd.d_secsize), du->dk_dd.d_secperunit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors, du->dk_dd.d_secsize); if (bootverbose) { wp = &du->dk_params; printf( "wd%d: ATA INQUIRE valid = %04x, " "dmamword = %04x, apio = %04x, " "udma = %04x\n", du->dk_lunit, wp->wdp_atavalid, wp->wdp_dmamword, wp->wdp_eidepiomodes, wp->wdp_udmamode); } /* * Start timeout routine for this drive. * XXX timeout should be per controller. */ wdtimeout(du); /* * Register this media as a disk */ du->disk.d_open = wdopen; du->disk.d_strategy = wdstrategy; du->disk.d_drv1 = du; du->disk.d_maxsize = 248 * 512; du->disk.d_name = "wd"; disk_create(lunit, &du->disk, 0, NULL, NULL); } else { free(du, M_TEMP); wddrives[lunit] = NULL; } } /* * Probe all free IDE units, searching for ATAPI drives. */ #ifdef PC98 for (unit=0; unit<4; ++unit) { outb(0x432,unit%2); #else for (unit=0; unit<2; ++unit) { #endif /* PC98 */ for (lunit=0; lunitdk_ctrlr == dvp->id_unit && wddrives[lunit]->dk_unit == unit) goto next; if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) atapictrlr = dvp->id_unit; next: ; } /* * Discard any interrupts generated by wdgetctlr(). wdflushirq() * doesn't work now because the ambient ipl is too high. */ if (eide_quirks & Q_CMD640B) { wdtab[PRIMARY].b_active = 2; } else { wdtab[dvp->id_unit].b_active = 2; } return (1); } /* Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ void wdstrategy(struct bio *bp) { struct softc *du; int lunit; int s; du = bp->bio_disk->d_drv1; if (du == NULL || bp->bio_blkno < 0 || bp->bio_bcount % DEV_BSIZE != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto done; } lunit = du->dk_lunit; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* queue transfer on drive, activate drive and controller if idle */ s = splbio(); /* Pick up changes made by readdisklabel(). */ if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) { wdsleep(du->dk_ctrlr, "wdlab"); du->dk_state = WANTOPEN; } bioq_disksort(&drive_queue[lunit], bp); if (wdutab[lunit].b_active == 0) wdustart(du); /* start drive */ if (wdtab[du->dk_ctrlr_cmd640].b_active == 0) wdstart(du->dk_ctrlr); /* start controller */ splx(s); return; done: /* toss transfer, we're done early */ biodone(bp); } /* * Routine to queue a command to the controller. The unit's * request is linked into the active list for the controller. * If the controller is idle, the transfer is started. */ static void wdustart(register struct softc *du) { register struct bio *bp; int ctrlr = du->dk_ctrlr_cmd640; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* unit already active? */ if (wdutab[du->dk_lunit].b_active) return; bp = bioq_first(&drive_queue[du->dk_lunit]); if (bp == NULL) { /* yes, an assign */ return; } /* * store away which device we came from. */ bp->bio_driver1 = du; bioq_remove(&drive_queue[du->dk_lunit], bp); /* link onto controller queue */ bioq_insert_tail(&wdtab[ctrlr].controller_queue, bp); /* mark the drive unit as busy */ wdutab[du->dk_lunit].b_active = 1; } /* * Controller startup routine. This does the calculation, and starts * a single-sector read or write operation. Called to start a transfer, * or from the interrupt routine to continue a multi-sector transfer. * RESTRICTIONS: * 1. The transfer length must be an exact multiple of the sector size. */ void wdstart(int ctrlr) { register struct softc *du; register struct bio *bp; struct diskgeom *lp; /* XXX sic */ long blknum; long secpertrk, secpercyl; u_int lunit; u_int count; int ctrlr_atapi; if (eide_quirks & Q_CMD640B) { ctrlr = PRIMARY; ctrlr_atapi = atapictrlr; } else { ctrlr_atapi = ctrlr; } if (wdtab[ctrlr].b_active == 2) wdtab[ctrlr].b_active = 0; if (wdtab[ctrlr].b_active) return; /* is there a drive for the controller to do a transfer with? */ bp = bioq_first(&wdtab[ctrlr].controller_queue); if (bp == NULL) { if (atapi_start && atapi_start (ctrlr_atapi)) /* mark controller active in ATAPI mode */ wdtab[ctrlr].b_active = 3; return; } /* obtain controller and drive information */ du = bp->bio_dev->si_drv1; lunit = du->dk_lunit; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* if not really a transfer, do control operations specially */ if (du->dk_state < OPEN) { if (du->dk_state != WANTOPEN) printf("wd%d: wdstart: weird dk_state %d\n", du->dk_lunit, du->dk_state); if (wdcontrol(bp) != 0) printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n", du->dk_lunit, du->dk_state); return; } /* calculate transfer details */ blknum = bp->bio_pblkno + du->dk_skip; #ifdef WDDEBUG if (du->dk_skip == 0) printf("wd%d: wdstart: %s %d@%d; map ", lunit, (bp->bio_cmd == BIO_READ) ? "read" : "write", bp->bio_bcount, blknum); else { if (old_epson_note) printf(" %d)%x", du->dk_skip, epson_inb(du->dk_altport); else printf(" %d)%x", du->dk_skip, inb(du->dk_altport); } #endif lp = &du->dk_dd; secpertrk = lp->d_nsectors; secpercyl = lp->d_secpercyl; if (du->dk_skip == 0) du->dk_bc = bp->bio_bcount; wdtab[ctrlr].b_active = 1; /* mark controller active */ /* if starting a multisector transfer, or doing single transfers */ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) { u_int command; u_int count1; long cylin, head, sector; if (du->dk_flags & DKFL_LBA) { sector = (blknum >> 0) & 0xff; cylin = (blknum >> 8) & 0xffff; head = ((blknum >> 24) & 0xf) | WDSD_LBA; } else { cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; } /* * XXX this looks like an attempt to skip bad sectors * on write. */ if (wdtab[ctrlr].b_errcnt && (bp->bio_cmd == BIO_WRITE)) du->dk_bc += DEV_BSIZE; count1 = howmany( du->dk_bc, DEV_BSIZE); du->dk_flags &= ~DKFL_MULTI; if (du->dk_flags & DKFL_SINGLE) { command = (bp->bio_cmd == BIO_READ) ? WDCC_READ : WDCC_WRITE; count1 = 1; du->dk_currentiosize = 1; } else { if((du->dk_flags & DKFL_USEDMA) && wddma[du->dk_interface].wdd_dmaverify(du->dk_dmacookie, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), du->dk_bc, bp->bio_cmd == BIO_READ)) { du->dk_flags |= DKFL_DMA; if(bp->bio_cmd == BIO_READ) command = WDCC_READ_DMA; else command = WDCC_WRITE_DMA; du->dk_currentiosize = count1; } else if( (count1 > 1) && (du->dk_multi > 1)) { du->dk_flags |= DKFL_MULTI; if(bp->bio_cmd == BIO_READ) { command = WDCC_READ_MULTI; } else { command = WDCC_WRITE_MULTI; } du->dk_currentiosize = du->dk_multi; if( du->dk_currentiosize > count1) du->dk_currentiosize = count1; } else { if(bp->bio_cmd == BIO_READ) { command = WDCC_READ; } else { command = WDCC_WRITE; } du->dk_currentiosize = 1; } } /* * XXX this loop may never terminate. The code to handle * counting down of retries and eventually failing the i/o * is in wdintr() and we can't get there from here. */ if (wdtest != 0) { if (--wdtest == 0) { wdtest = 100; printf("dummy wdunwedge\n"); wdunwedge(du); } } if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { wddma[du->dk_interface].wdd_dmaprep(du->dk_dmacookie, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), du->dk_bc, bp->bio_cmd == BIO_READ); } while (wdcommand(du, cylin, head, sector, count1, command) != 0) { wderror(bp, du, "wdstart: timeout waiting to give command"); wdunwedge(du); } #ifdef WDDEBUG printf("cylin %ld head %ld sector %ld addr %x sts ", cylin, head, sector, (int)bp->bio_data + du->dk_skip * DEV_BSIZE); if (old_epson_note) printf("%x\n", epson_inb(du->dk_altport)); else printf("%x\n", inb(du->dk_altport)); #endif } /* * Schedule wdtimeout() to wake up after a few seconds. Retrying * unmarked bad blocks can take 3 seconds! Then it is not good that * we retry 5 times. * * On the first try, we give it 10 seconds, for drives that may need * to spin up. * * XXX wdtimeout() doesn't increment the error count so we may loop * forever. More seriously, the loop isn't forever but causes a * crash. * * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive * counts being discarded after there is an error (in physio I * think). Discarding them would be OK if the (special) file offset * was not advanced. */ if (wdtab[ctrlr].b_errcnt == 0) du->dk_timeout = 1 + 10; else du->dk_timeout = 1 + 3; /* if this is a DMA op, start DMA and go away until it's done. */ if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { wddma[du->dk_interface].wdd_dmastart(du->dk_dmacookie); return; } /* If this is a read operation, just go away until it's done. */ if (bp->bio_cmd == BIO_READ) return; /* Ready to send data? */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) { wderror(bp, du, "wdstart: timeout waiting for DRQ"); /* * XXX what do we do now? If we've just issued the command, * then we can treat this failure the same as a command * failure. But if we are continuing a multi-sector write, * the command was issued ages ago, so we can't simply * restart it. * * XXX we waste a lot of time unnecessarily translating block * numbers to cylin/head/sector for continued i/o's. */ } count = 1; if( du->dk_flags & DKFL_MULTI) { count = howmany(du->dk_bc, DEV_BSIZE); if( count > du->dk_multi) count = du->dk_multi; if( du->dk_currentiosize > count) du->dk_currentiosize = count; } if (!old_epson_note) { if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(long)); else outsw(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); } else epson_outsw(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); du->dk_bc -= DEV_BSIZE * count; } /* Interrupt routine for the controller. Acknowledge the interrupt, check for * errors on the current operation, mark it done if necessary, and start * the next request. Also check for a partially done transfer, and * continue with the next chunk if so. */ void wdintr(void *unitnum) { register struct softc *du; register struct bio *bp; int dmastat = 0; /* Shut up GCC */ int unit = (int)unitnum; int ctrlr_atapi; if (eide_quirks & Q_CMD640B) { unit = PRIMARY; ctrlr_atapi = atapictrlr; } else { ctrlr_atapi = unit; } if (wdtab[unit].b_active == 2) return; /* intr in wdflushirq() */ if (!wdtab[unit].b_active) { #ifdef WDDEBUG /* * These happen mostly because the power-mgt part of the * bios shuts us down, and we just manage to see the * interrupt from the "SLEEP" command. */ printf("wdc%d: extra interrupt\n", unit); #endif return; } if (wdtab[unit].b_active == 3) { /* process an ATAPI interrupt */ if (atapi_intr && atapi_intr (ctrlr_atapi)) /* ATAPI op continues */ return; /* controller is free, start new op */ wdtab[unit].b_active = 0; wdstart (unit); return; } bp = bioq_first(&wdtab[unit].controller_queue); du = bp->bio_dev->si_drv1; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* finish off DMA */ if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { /* XXX SMP boxes sometimes generate an early intr. Why? */ if ((wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie) & WDDS_INTERRUPT) == 0) return; dmastat = wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); } du->dk_timeout = 0; /* check drive status/failure */ if (wdwait(du, 0, TIMEOUT) < 0) { wderror(bp, du, "wdintr: timeout waiting for status"); du->dk_status |= WDCS_ERR; /* XXX */ } /* is it not a transfer, but a control operation? */ if (du->dk_state < OPEN) { wdtab[unit].b_active = 0; switch (wdcontrol(bp)) { case 0: return; case 1: wdstart(unit); return; case 2: goto done; } } /* have we an error? */ if ((du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) || (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && dmastat != WDDS_INTERRUPT)) { unsigned int errstat; oops: /* * XXX bogus inb() here */ errstat = inb(du->dk_port + wd_error); if(((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && (errstat & WDERR_ABORT)) { wderror(bp, du, "reverting to PIO mode"); du->dk_flags &= ~DKFL_USEDMA; } else if((du->dk_flags & DKFL_MULTI) && (errstat & WDERR_ABORT)) { wderror(bp, du, "reverting to non-multi sector mode"); du->dk_multi = 1; } if (!(du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) && (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && (dmastat != WDDS_INTERRUPT))) printf("wd%d: DMA failure, DMA status %b\n", du->dk_lunit, dmastat, WDDS_BITS); #ifdef WDDEBUG wderror(bp, du, "wdintr"); #endif if ((du->dk_flags & DKFL_SINGLE) == 0) { du->dk_flags |= DKFL_ERROR; goto outt; } if (du->dk_status & WDCS_ERR) { if (++wdtab[unit].b_errcnt < RETRIES) { wdtab[unit].b_active = 0; } else { wderror(bp, du, "hard error"); bp->bio_error = EIO; bp->bio_flags |= BIO_ERROR; /* flag the error */ } } else if (du->dk_status & WDCS_ECCCOR) wderror(bp, du, "soft ecc"); } /* * If this was a successful read operation, fetch the data. */ if (bp->bio_cmd == BIO_READ && !(bp->bio_flags & BIO_ERROR) && !((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { u_int chk, dummy, multisize; multisize = chk = du->dk_currentiosize * DEV_BSIZE; if( du->dk_bc < chk) { chk = du->dk_bc; if( ((chk + DEV_BSIZE - 1) / DEV_BSIZE) < du->dk_currentiosize) { du->dk_currentiosize = (chk + DEV_BSIZE - 1) / DEV_BSIZE; multisize = du->dk_currentiosize * DEV_BSIZE; } } /* ready to receive data? */ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) wderror(bp, du, "wdintr: read intr arrived early"); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { wderror(bp, du, "wdintr: read error detected late"); goto oops; } /* suck in data */ if( du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), chk / sizeof(long)); else insw(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), chk / sizeof(short)); du->dk_bc -= chk; /* XXX for obsolete fractional sector reads. */ while (chk < multisize) { insw(du->dk_port + wd_data, &dummy, 1); chk += sizeof(short); } } /* final cleanup on DMA */ if (((bp->bio_flags & BIO_ERROR) == 0) && ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { int iosize; iosize = du->dk_currentiosize * DEV_BSIZE; du->dk_bc -= iosize; } outt: if (wdtab[unit].b_active) { if ((bp->bio_flags & BIO_ERROR) == 0) { du->dk_skip += du->dk_currentiosize;/* add to successful sectors */ if (wdtab[unit].b_errcnt) wderror(bp, du, "soft error"); wdtab[unit].b_errcnt = 0; /* see if more to transfer */ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { if( (du->dk_flags & DKFL_SINGLE) || (bp->bio_cmd == BIO_WRITE)) { wdtab[unit].b_active = 0; wdstart(unit); } else { du->dk_timeout = 1 + 3; } return; /* next chunk is started */ } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR)) == DKFL_ERROR) { du->dk_skip = 0; du->dk_flags &= ~DKFL_ERROR; du->dk_flags |= DKFL_SINGLE; wdtab[unit].b_active = 0; wdstart(unit); return; /* redo xfer sector by sector */ } } done: ; /* done with this transfer, with or without error */ du->dk_flags &= ~(DKFL_SINGLE|DKFL_DMA); bioq_remove( &wdtab[unit].controller_queue, bp); wdtab[unit].b_errcnt = 0; bp->bio_resid = bp->bio_bcount - du->dk_skip * DEV_BSIZE; wdutab[du->dk_lunit].b_active = 0; du->dk_skip = 0; biodone(bp); } /* controller idle */ wdtab[unit].b_active = 0; /* anything more on drive queue? */ wdustart(du); /* anything more for controller to do? */ wdstart(unit); } /* * Initialize a drive. */ int wdopen(struct disk *dp) { register struct softc *du; du = dp->d_drv1; if (du == NULL) return (ENXIO); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Finish flushing IRQs left over from wdattach(). */ if (wdtab[du->dk_ctrlr_cmd640].b_active == 2) wdtab[du->dk_ctrlr_cmd640].b_active = 0; du->dk_flags &= ~DKFL_BADSCAN; /* spin waiting for anybody else reading the disk label */ while (du->dk_flags & DKFL_LABELLING) tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1); wdsleep(du->dk_ctrlr, "wdopn1"); du->dk_flags |= DKFL_LABELLING; du->dk_state = WANTOPEN; du->disk.d_sectorsize = du->dk_dd.d_secsize; du->disk.d_mediasize = du->dk_dd.d_secperunit * du->dk_dd.d_secsize; du->disk.d_fwsectors = du->dk_dd.d_nsectors; du->disk.d_fwheads = du->dk_dd.d_ntracks; du->dk_flags &= ~DKFL_LABELLING; wdsleep(du->dk_ctrlr, "wdopn2"); return 0; } /* * Implement operations other than read/write. * Called from wdstart or wdintr during opens. * Uses finite-state-machine to track progress of operation in progress. * Returns 0 if operation still in progress, 1 if completed, 2 if error. */ static int wdcontrol(register struct bio *bp) { register struct softc *du; int ctrlr; du = bp->bio_dev->si_drv1; ctrlr = du->dk_ctrlr_cmd640; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif switch (du->dk_state) { case WANTOPEN: tryagainrecal: wdtab[ctrlr].b_active = 1; if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) { wderror(bp, du, "wdcontrol: wdcommand failed"); goto maybe_retry; } du->dk_state = RECAL; return (0); case RECAL: if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) { wderror(bp, du, "wdcontrol: recal failed"); maybe_retry: if (du->dk_status & WDCS_ERR) wdunwedge(du); du->dk_state = WANTOPEN; if (++wdtab[ctrlr].b_errcnt < RETRIES) goto tryagainrecal; bp->bio_error = ENXIO; /* XXX needs translation */ bp->bio_flags |= BIO_ERROR; return (2); } wdtab[ctrlr].b_errcnt = 0; du->dk_state = OPEN; /* * The rest of the initialization can be done by normal * means. */ return (1); } panic("wdcontrol"); return (2); } /* * Wait uninterruptibly until controller is not busy, then send it a command. * The wait usually terminates immediately because we waited for the previous * command to terminate. */ static int wdcommand(struct softc *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command) { u_int wdc; #ifdef PC98 unsigned char u_addr; #endif wdc = du->dk_port; if (du->cfg_flags & WDOPT_SLEEPHACK) { /* OK, so the APM bios has put the disk into SLEEP mode, * how can we tell ? Uhm, we can't. There is no * standardized way of finding out, and the only way to * wake it up is to reset it. Bummer. * * All the many and varied versions of the IDE/ATA standard * explicitly tells us not to look at these registers if * the disk is in SLEEP mode. Well, too bad really, we * have to find out if it's in sleep mode before we can * avoid reading the registers. * * I have reason to belive that most disks will return * either 0xff or 0x00 in all but the status register * when in SLEEP mode, but I have yet to see one return * 0x00, so we don't check for that yet. * * The check for WDCS_BUSY is for the case where the * bios spins up the disk for us, but doesn't initialize * it correctly /phk */ if (old_epson_note) { if(epson_inb(wdc + wd_precomp) + epson_inb(wdc + wd_cyl_lo) + epson_inb(wdc + wd_cyl_hi) + epson_inb(wdc + wd_sdh) + epson_inb(wdc + wd_sector) + epson_inb(wdc + wd_seccnt) == 6 * 0xff) { if (bootverbose) printf("wd(%d,%d): disk aSLEEP\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } else if(epson_inb(wdc + wd_status) == WDCS_BUSY) { if (bootverbose) printf("wd(%d,%d): disk is BUSY\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } } else { if(inb(wdc + wd_precomp) + inb(wdc + wd_cyl_lo) + inb(wdc + wd_cyl_hi) + inb(wdc + wd_sdh) + inb(wdc + wd_sector) + inb(wdc + wd_seccnt) == 6 * 0xff) { if (bootverbose) printf("wd(%d,%d): disk aSLEEP\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } else if(inb(wdc + wd_status) == WDCS_BUSY) { if (bootverbose) printf("wd(%d,%d): disk is BUSY\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } } } if (wdwait(du, 0, TIMEOUT) < 0) return (1); #ifdef PC98 /* u_addr = (du->dk_unit & 0xfe); */ u_addr = ((du->dk_unit)/2)<<4; #endif /* PC98 */ if( command == WDCC_FEATURES) { if (old_epson_note) epson_outb(wdc + wd_features, count); else { outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head); outb(wdc + wd_features, count); if ( count == WDFEA_SETXFER ) outb(wdc + wd_seccnt, sector); } } else { if (old_epson_note) { epson_outb(wdc + wd_precomp, du->dk_dd.d_precompcyl/4); epson_outb(wdc + wd_cyl_lo, cylinder); epson_outb(wdc + wd_cyl_hi, cylinder >> 8); epson_outb(wdc + wd_sdh, WDSD_IBM | u_addr | head); epson_outb(wdc + wd_sector, sector + 1); epson_outb(wdc + wd_seccnt, count); } else { outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4); outb(wdc + wd_cyl_lo, cylinder); outb(wdc + wd_cyl_hi, cylinder >> 8); #ifdef PC98 outb(wdc + wd_sdh, WDSD_IBM | u_addr | head); #else outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit<<4) | head); #endif if (head & WDSD_LBA) outb(wdc + wd_sector, sector); else outb(wdc + wd_sector, sector + 1); outb(wdc + wd_seccnt, count); } } if (wdwait(du, (command == WDCC_DIAGNOSE || command == WDCC_IDC) ? 0 : WDCS_READY, TIMEOUT) < 0) return (1); if (old_epson_note) epson_outb(wdc + wd_command, command); else outb(wdc + wd_command, command); return (0); } static void wdsetmulti(struct softc *du) { /* * The config option flags low 8 bits define the maximum multi-block * transfer size. If the user wants the maximum that the drive * is capable of, just set the low bits of the config option to * 0x00ff. */ if ((du->cfg_flags & WDOPT_MULTIMASK) != 0 && (du->dk_multi > 1)) { int configval = du->cfg_flags & WDOPT_MULTIMASK; du->dk_multi = min(du->dk_multi, configval); if (wdcommand(du, 0, 0, 0, du->dk_multi, WDCC_SET_MULTI)) { du->dk_multi = 1; } else { if (wdwait(du, WDCS_READY, TIMEOUT) < 0) { du->dk_multi = 1; } } } else { du->dk_multi = 1; } } /* * issue IDC to drive to tell it just what geometry it is to be. */ static int wdsetctlr(struct softc *du) { int error = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif #ifdef WDDEBUG printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n", du->dk_ctrlr, du->dk_unit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors); #endif if (!(du->dk_flags & DKFL_LBA)) { if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) { struct wdparams *wp; printf("wd%d: can't handle %lu heads from partition table ", du->dk_lunit, du->dk_dd.d_ntracks); /* obtain parameters */ wp = &du->dk_params; if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) { printf("(controller value %u restored)\n", wp->wdp_heads); du->dk_dd.d_ntracks = wp->wdp_heads; } else { printf("(truncating to 16)\n"); du->dk_dd.d_ntracks = 16; } } if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { printf("wd%d: cannot handle %lu sectors (max 255)\n", du->dk_lunit, du->dk_dd.d_nsectors); error = 1; } if (error) { wdtab[du->dk_ctrlr_cmd640].b_errcnt += RETRIES; return (1); } if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, du->dk_dd.d_nsectors, WDCC_IDC) != 0 || wdwait(du, WDCS_READY, TIMEOUT) < 0) { wderror((struct bio *)NULL, du, "wdsetctlr failed"); return (1); } } wdsetmulti(du); #ifdef NOTYET /* set read caching and write caching */ wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES); wdwait(du, WDCS_READY, TIMEOUT); wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES); wdwait(du, WDCS_READY, TIMEOUT); #endif return (0); } #if 0 /* * Wait until driver is inactive, then set up controller. */ static int wdwsetctlr(struct softc *du) { int stat; int x; wdsleep(du->dk_ctrlr, "wdwset"); x = splbio(); stat = wdsetctlr(du); wdflushirq(du, x); splx(x); return (stat); } #endif /* * gross little callback function for wdddma interface. returns 1 for * success, 0 for failure. */ static int wdsetmode(int mode, void *wdinfo) { int i; struct softc *du; du = wdinfo; if (bootverbose) printf("wd%d: wdsetmode() setting transfer mode to %02x\n", du->dk_lunit, mode); i = wdcommand(du, 0, 0, mode, WDFEA_SETXFER, WDCC_FEATURES) == 0 && wdwait(du, WDCS_READY, TIMEOUT) == 0; return i; } /* * issue READP to drive to ask it what it is. */ static int wdgetctlr(struct softc *du) { int i; char tb[DEV_BSIZE], tb2[DEV_BSIZE]; struct wdparams *wp = NULL; u_long flags = du->cfg_flags; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif again: if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { #ifdef PC98 if ( du->dk_unit > 1 ) return(1); #endif /* * if we failed on the second try, assume non-32bit */ if( du->dk_flags & DKFL_32BIT) goto failed; /* XXX need to check error status after final transfer. */ /* * Old drives don't support WDCC_READP. Try a seek to 0. * Some IDE controllers return trash if there is no drive * attached, so first test that the drive can be selected. * This also avoids long waits for nonexistent drives. */ if (wdwait(du, 0, TIMEOUT) < 0) return (1); if (old_epson_note) { epson_outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ if ((epson_inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); } else { outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ /* * Do this twice: may get a false WDCS_READY the first time. */ inb(du->dk_port + wd_status); if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); } if (du->dk_unit == bootinfo.bi_n_bios_used) { du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; du->dk_dd.d_ntracks = ((bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff) + 1; /* XXX Why 2 ? */ du->dk_dd.d_ncylinders = (bootinfo.bi_bios_geom[du->dk_unit] >> 16) + 2; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; #if 0 du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit; du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Bios geometry", sizeof du->dk_dd.d_typename); strncpy(du->dk_params.wdp_model, "ST506", sizeof du->dk_params.wdp_model); #endif bootinfo.bi_n_bios_used ++; return 0; } /* * Fake minimal drive geometry for reading the MBR. * readdisklabel() may enlarge it to read the label and the * bad sector table. */ du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = 17; du->dk_dd.d_ntracks = 1; du->dk_dd.d_ncylinders = 1; du->dk_dd.d_secpercyl = 17; du->dk_dd.d_secperunit = 17; #if 0 /* * Fake maximal drive size for writing the label. */ du->dk_dd.d_partitions[RAW_PART].p_size = 64 * 16 * 1024; /* * Fake some more of the label for printing by disklabel(1) * in case there is no real label. */ du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Fake geometry", sizeof du->dk_dd.d_typename); #endif /* Fake the model name for printing by wdattach(). */ strncpy(du->dk_params.wdp_model, "unknown", sizeof du->dk_params.wdp_model); return (0); } /* obtain parameters */ wp = &du->dk_params; if (!old_epson_note) { if (du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(long)); else insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); } else epson_insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); /* try 32-bit data path (VLB IDE controller) */ if (flags & WDOPT_32BIT) { if (! (du->dk_flags & DKFL_32BIT)) { bcopy(tb, tb2, sizeof(struct wdparams)); du->dk_flags |= DKFL_32BIT; goto again; } /* check that we really have 32-bit controller */ if (bcmp (tb, tb2, sizeof(struct wdparams)) != 0) { failed: /* test failed, use 16-bit i/o mode */ bcopy(tb2, tb, sizeof(struct wdparams)); du->dk_flags &= ~DKFL_32BIT; } } bcopy(tb, wp, sizeof(struct wdparams)); /* shuffle string byte order */ for (i = 0; (unsigned)i < sizeof(wp->wdp_model); i += 2) { u_short *p; p = (u_short *) (wp->wdp_model + i); *p = ntohs(*p); } /* * Clean up the wdp_model by converting nulls to spaces, and * then removing the trailing spaces. */ for (i = 0; (unsigned)i < sizeof(wp->wdp_model); i++) { if (wp->wdp_model[i] == '\0') { wp->wdp_model[i] = ' '; } } for (i = sizeof(wp->wdp_model) - 1; (i >= 0 && wp->wdp_model[i] == ' '); i--) { wp->wdp_model[i] = '\0'; } /* * find out the drives maximum multi-block transfer capability */ du->dk_multi = wp->wdp_nsecperint & 0xff; wdsetmulti(du); /* * check drive's DMA capability */ if (wddma[du->dk_interface].wdd_candma) { du->dk_dmacookie = wddma[du->dk_interface].wdd_candma( du->dk_port, du->dk_ctrlr, du->dk_unit); /* does user want this? */ if ((du->cfg_flags & WDOPT_DMA) && /* have we got a DMA controller? */ du->dk_dmacookie && /* can said drive do DMA? */ wddma[du->dk_interface].wdd_dmainit(du->dk_dmacookie, wp, wdsetmode, du)) { du->dk_flags |= DKFL_USEDMA; } } else { du->dk_dmacookie = NULL; } #ifdef WDDEBUG printf( "\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", du->dk_ctrlr, du->dk_unit, wp->wdp_config, wp->wdp_cylinders, wp->wdp_heads, wp->wdp_sectors, wp->wdp_buffertype, wp->wdp_buffersize, wp->wdp_model); #endif #ifdef PC98 /* for larger than 40MB */ { long cyl = wp->wdp_cylinders * wp->wdp_heads * wp->wdp_sectors; if ( du->dk_unit > 1 ) { wp->wdp_sectors = 17; wp->wdp_heads = 8; } else { wp->wdp_sectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; wp->wdp_heads = (bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff; } wp->wdp_cylinders = cyl / (wp->wdp_heads * wp->wdp_sectors); } #endif /* update disklabel given drive information */ du->dk_dd.d_secsize = DEV_BSIZE; if ((du->cfg_flags & WDOPT_LBA) && wp->wdp_lbasize) { du->dk_dd.d_nsectors = 63; if (wp->wdp_lbasize < 16*63*1024) { /* <=528.4 MB */ du->dk_dd.d_ntracks = 16; } else if (wp->wdp_lbasize < 32*63*1024) { /* <=1.057 GB */ du->dk_dd.d_ntracks = 32; } else if (wp->wdp_lbasize < 64*63*1024) { /* <=2.114 GB */ du->dk_dd.d_ntracks = 64; } else if (wp->wdp_lbasize < 128*63*1024) { /* <=4.228 GB */ du->dk_dd.d_ntracks = 128; } else if (wp->wdp_lbasize < 255*63*1024) { /* <=8.422 GB */ du->dk_dd.d_ntracks = 255; } else { /* >8.422 GB */ du->dk_dd.d_ntracks = 255; /* XXX */ } du->dk_dd.d_secpercyl= du->dk_dd.d_ntracks*du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = wp->wdp_lbasize/du->dk_dd.d_secpercyl; du->dk_dd.d_secperunit = wp->wdp_lbasize; du->dk_flags |= DKFL_LBA; } else { du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */ du->dk_dd.d_ntracks = wp->wdp_heads; du->dk_dd.d_nsectors = wp->wdp_sectors; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; if (wp->wdp_cylinders == 16383 && du->dk_dd.d_secperunit < wp->wdp_lbasize) { du->dk_dd.d_secperunit = wp->wdp_lbasize; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } } if (WDOPT_FORCEHD(du->cfg_flags)) { du->dk_dd.d_ntracks = WDOPT_FORCEHD(du->cfg_flags); du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } if (du->dk_dd.d_ncylinders > 0x10000 && !(du->cfg_flags & WDOPT_LBA)) { du->dk_dd.d_ncylinders = 0x10000; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; printf( "wd%d: cannot handle %d total sectors; truncating to %lu\n", du->dk_lunit, wp->wdp_lbasize, du->dk_dd.d_secperunit); } #if 0 du->dk_dd.d_partitions[RAW_PART].p_size = du->dk_dd.d_secperunit; /* dubious ... */ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9); bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1); /* better ... */ du->dk_dd.d_type = DTYPE_ESDI; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; #endif return (0); } static void wderror(struct bio *bp, struct softc *du, char *mesg) { if (bp == NULL) printf("wd%d: %s", du->dk_lunit, mesg); else disk_err(bp, mesg, du->dk_skip, 0); printf(" (status %b error %b)\n", du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS); } /* * Discard any interrupts that were latched by the interrupt system while * we were doing polled i/o. */ static void wdflushirq(struct softc *du, int old_ipl) { wdtab[du->dk_ctrlr_cmd640].b_active = 2; splx(old_ipl); (void)splbio(); wdtab[du->dk_ctrlr_cmd640].b_active = 0; } /* * Reset the controller. */ static int wdreset(struct softc *du) { int err = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); (void)wdwait(du, 0, TIMEOUT); #ifdef PC98 if (old_epson_note) { epson_outb(du->dk_altport, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); epson_outb(du->dk_altport, WDCTL_IDS); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 || (du->dk_error = epson_errorf(du->dk_port + wd_error)) != 0x01) return (1); epson_outb(du->dk_altport, WDCTL_4BIT); err = 0; } else { #endif outb(du->dk_altport, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); outb(du->dk_altport, WDCTL_IDS); outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); if (wdwait(du, 0, TIMEOUT) != 0) err = 1; /* no IDE drive found */ du->dk_error = inb(du->dk_port + wd_error); if (du->dk_error != 0x01) err = 1; /* the drive is incompatible */ outb(du->dk_altport, WDCTL_4BIT); #ifdef PC98 } #endif return (err); } /* * Sleep until driver is inactive. * This is used only for avoiding rare race conditions, so it is unimportant * that the sleep may be far too short or too long. */ static void wdsleep(int ctrlr, char *wmesg) { int s = splbio(); if (eide_quirks & Q_CMD640B) ctrlr = PRIMARY; while (wdtab[ctrlr].b_active) tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1); splx(s); } static void wdtimeout(void *cdu) { struct softc *du; int x; static int timeouts; du = (struct softc *)cdu; x = splbio(); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif if (du->dk_timeout != 0 && --du->dk_timeout == 0) { if(timeouts++ <= 5) { char *msg; msg = (timeouts > 5) ? "Last time I say: interrupt timeout. Probably a portable PC." : "interrupt timeout"; wderror((struct bio *)NULL, du, msg); if (du->dk_dmacookie) printf("wd%d: wdtimeout() DMA status %b\n", du->dk_lunit, wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie), WDDS_BITS); } wdunwedge(du); wdflushirq(du, x); du->dk_skip = 0; du->dk_flags |= DKFL_SINGLE; wdstart(du->dk_ctrlr); } timeout(wdtimeout, cdu, hz); splx(x); } /* * Reset the controller after it has become wedged. This is different from * wdreset() so that wdreset() can be used in the probe and so that this * can restore the geometry . */ static int wdunwedge(struct softc *du) { struct softc *du1; int lunit; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Schedule other drives for recalibration. */ for (lunit = 0; lunit < NWD; lunit++) if ((du1 = wddrives[lunit]) != NULL && du1 != du && du1->dk_ctrlr == du->dk_ctrlr && du1->dk_state > WANTOPEN) du1->dk_state = WANTOPEN; DELAY(RECOVERYTIME); if (wdreset(du) == 0) { /* * XXX - recalibrate current drive now because some callers * aren't prepared to have its state change. */ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0 && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0 && wdsetctlr(du) == 0) return (0); } wderror((struct bio *)NULL, du, "wdunwedge failed"); return (1); } /* * Wait uninterruptibly until controller is not busy and either certain * status bits are set or an error has occurred. * The wait is usually short unless it is for the controller to process * an entire critical command. * Return 1 for (possibly stale) controller errors, -1 for timeout errors, * or 0 for no errors. * Return controller status in du->dk_status and, if there was a controller * error, return the error code in du->dk_error. */ #ifdef WD_COUNT_RETRIES static int min_retries[NWDC]; #endif static int wdwait(struct softc *du, u_char bits_wanted, int timeout) { int wdc; u_char status; #define POLLING 1000 wdc = du->dk_port; timeout += POLLING; /* * This delay is really too long, but does not impact the performance * as much when using the multi-sector option. Shorter delays have * caused I/O errors on some drives and system configs. This should * probably be fixed if we develop a better short term delay mechanism. */ DELAY(1); do { #ifdef WD_COUNT_RETRIES if (min_retries[du->dk_ctrlr] > timeout || min_retries[du->dk_ctrlr] == 0) min_retries[du->dk_ctrlr] = timeout; #endif #ifdef PC98 if (old_epson_note) du->dk_status = status = epson_inb(wdc + wd_status); else du->dk_status = status = inb(wdc + wd_status); #else du->dk_status = status = inb(wdc + wd_status); #endif /* * Atapi drives have a very interesting feature, when attached * as a slave on the IDE bus, and there is no master. * They release the bus after getting the command. * We should reselect the drive here to get the status. */ if (status == 0xff) { outb(wdc + wd_sdh, WDSD_IBM | du->dk_unit << 4); du->dk_status = status = inb(wdc + wd_status); } if (!(status & WDCS_BUSY)) { if (status & WDCS_ERR) { if (old_epson_note) du->dk_error = epson_errorf(wdc + wd_error); else du->dk_error = inb(wdc + wd_error); /* * We once returned here. This is wrong * because the error bit is apparently only * valid after the controller has interrupted * (e.g., the error bit is stale when we wait * for DRQ for writes). So we can't depend * on the error bit at all when polling for * command completion. */ } if ((status & bits_wanted) == bits_wanted) { return (status & WDCS_ERR); } } if (timeout < TIMEOUT) /* * Switch to a polling rate of about 1 KHz so that * the timeout is almost machine-independent. The * controller is taking a long time to respond, so * an extra msec won't matter. */ DELAY(1000); else DELAY(1); } while (--timeout != 0); return (-1); } #endif /* NWDC > 0 */